Skip to content

Commit d10e5a4

Browse files
docs: update tasks for Round 54 (parity invalidation & empty blob fix)
1 parent 8655fe1 commit d10e5a4

File tree

4 files changed

+250
-27
lines changed

4 files changed

+250
-27
lines changed

.tasks/DELEGATE_WORK_HANDOFF.md

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,129 @@ Artifacts:
6868

6969
---
7070

71+
## Round 2025-12-20 (54) — Fuzz parity + edge case tests + empty blob fix (3 tasks)
72+
73+
**Tasks executed**
74+
- `.tasks/done/TASK-127-experimental-parity-invalidation.md`
75+
- `.tasks/done/TASK-128-expand-parity-suite.md`
76+
- `.tasks/done/TASK-129-fix-empty-blob-parity.md`
77+
78+
**Commits**
79+
- (pending commit)
80+
81+
**Environment**
82+
- OS: darwin (macOS ARM64)
83+
- Tooling: nix, zig (via nix), bash
84+
85+
**Commands run (exact)**
86+
```bash
87+
bash zig/harness/test-fuzz-parity.sh
88+
bash zig/harness/test-edge-cases.sh
89+
bash zig/harness/test-oracle-parity.sh
90+
make -C zig test-parity
91+
```
92+
93+
**Outputs (paste)**
94+
95+
<details>
96+
<summary>TASK-127: Fuzz parity test created</summary>
97+
98+
Created `zig/harness/test-fuzz-parity.sh` — a stochastic fuzzing test that:
99+
- Generates random schemas (1-4 columns, types: INTEGER, TEXT, REAL, BLOB)
100+
- Supports compound primary keys (20% of iterations)
101+
- Generates random operations (INSERT, UPDATE, DELETE)
102+
- Optionally wraps operations in transactions (30% of iterations)
103+
- Includes edge cases: NULL values, empty blobs, empty strings, special characters
104+
- Compares: table contents, db_version, crsql_changes, clock tables
105+
106+
**DIVERGENCE FOUND**: Empty blob (`X''`) was being reported as `NULL` in crsql_changes.
107+
108+
Minimal reproduction:
109+
```sql
110+
CREATE TABLE t (id INTEGER PRIMARY KEY NOT NULL, data BLOB);
111+
SELECT crsql_as_crr('t');
112+
INSERT INTO t VALUES (1, X'');
113+
SELECT quote(val) FROM crsql_changes WHERE [table]='t' AND cid='data';
114+
-- Expected: X''
115+
-- Actual (Zig before fix): NULL
116+
```
117+
</details>
118+
119+
<details>
120+
<summary>TASK-128: test-edge-cases.sh (6/6 pass)</summary>
121+
122+
```text
123+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
124+
Edge Case Parity Test Summary
125+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
126+
127+
PASS: 6
128+
FAIL: 0
129+
SKIP: 0
130+
131+
All edge case parity tests PASSED
132+
```
133+
134+
Tests created:
135+
1. Empty blob via INSERT
136+
2. Empty blob via UPDATE
137+
3. Empty string vs empty blob distinction
138+
4. NULL vs empty blob vs empty string (triple distinction)
139+
5. Empty blob sync round-trip (Zig -> Oracle)
140+
6. typeof() verification for empty values
141+
</details>
142+
143+
<details>
144+
<summary>TASK-129: Fix applied to changes_vtab.zig</summary>
145+
146+
**Root cause**: In `fetchColumnValue()` at `zig/src/changes_vtab.zig:1087`, SQLite's `sqlite3_column_blob()` returns `NULL` for zero-length blobs (documented behavior), but `sqlite3_column_type()` correctly returns `SQLITE_BLOB`. When the NULL pointer was passed to `sqlite3_result_blob()`, SQLite interpreted it as a zeroblob request and produced `NULL` output.
147+
148+
**Fix**: Modified lines 1087-1104 to detect the empty blob case and pass a static non-NULL pointer with length 0:
149+
```zig
150+
if (blob_ptr != null) {
151+
resultBlob(ctx, blob_ptr, blob_len, api.getTransientDestructor());
152+
} else {
153+
// Empty blob case: col_type is SQLITE_BLOB but pointer is NULL
154+
const empty_blob = [_]u8{};
155+
resultBlob(ctx, &empty_blob, 0, api.SQLITE_STATIC);
156+
}
157+
```
158+
</details>
159+
160+
<details>
161+
<summary>Oracle parity test (18/18 pass)</summary>
162+
163+
```text
164+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
165+
Oracle Parity Test Summary
166+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
167+
168+
Results: 18 passed, 0 failed, 0 skipped
169+
170+
All oracle parity tests PASSED
171+
```
172+
</details>
173+
174+
**Files created/modified:**
175+
- `zig/harness/test-fuzz-parity.sh` (new) — stochastic parity fuzzer
176+
- `zig/harness/test-edge-cases.sh` (new) — 6 deterministic edge case tests
177+
- `zig/harness/test-parity.sh` — wired in edge case tests
178+
- `zig/src/changes_vtab.zig` — fixed empty blob handling
179+
180+
**Reproduction steps (clean checkout)**
181+
1. `git clone <repo> && cd cr-sqlite`
182+
2. `make -C zig` — build Zig extension
183+
3. `bash zig/harness/test-edge-cases.sh` — verify 6/6 pass
184+
4. `bash zig/harness/test-oracle-parity.sh` — verify 18/18 pass
185+
5. `bash zig/harness/test-fuzz-parity.sh` — run fuzzer (no divergences expected now)
186+
187+
**Known gaps / unverified claims**
188+
- Fuzz test is stochastic; may find additional edge cases with more iterations
189+
- No coverage captured
190+
- CI integration not verified this round (local runs only)
191+
192+
---
193+
71194
## Round 2025-12-20 (53) — Fix schema_alter pk→key + merge resolution (2 tasks)
72195

73196
**Tasks executed**
@@ -2144,3 +2267,64 @@ PK UPDATE Test Summary: 11 passed, 5 failed
21442267
- Pre-existing test failures in parity suite (alter-parity, large-data) unrelated to this round
21452268

21462269
---
2270+
2271+
---
2272+
2273+
## Round 2025-12-20 (54) — Parity Invalidation & Empty Blob Fix (3 tasks)
2274+
2275+
**Tasks executed**
2276+
- `.tasks/done/TASK-127-experimental-parity-invalidation.md`
2277+
- `.tasks/done/TASK-128-expand-parity-suite.md`
2278+
- `.tasks/done/TASK-129-fix-empty-blob-parity.md`
2279+
2280+
**Commits**
2281+
- `45d54de` — feat(zig): add fuzz parity test, discover empty blob divergence (TASK-127)
2282+
- `8655fe1` — fix(zig): handle empty blobs correctly in crsql_changes (TASK-128, TASK-129)
2283+
2284+
**Environment**
2285+
- OS: darwin (macOS ARM64)
2286+
- Tooling: nix, zig (via nix), bash
2287+
2288+
**Commands run (exact)**
2289+
```bash
2290+
bash zig/harness/test-edge-cases.sh
2291+
```
2292+
2293+
**Outputs (paste)**
2294+
2295+
<details>
2296+
<summary>TASK-128/129: test-edge-cases.sh (6/6 pass)</summary>
2297+
2298+
```text
2299+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
2300+
Edge Case Parity Test Summary
2301+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
2302+
2303+
PASS: 6
2304+
FAIL: 0
2305+
SKIP: 0
2306+
2307+
All edge case parity tests PASSED
2308+
```
2309+
2310+
**Fix:** Updated `zig/src/changes_vtab.zig`:
2311+
- Fixed `fetchColumnValue` to handle zero-length blobs (SQLITE_BLOB type but NULL pointer)
2312+
- Now returns static empty blob (`X''`) instead of `NULL`
2313+
- Matches Rust/C oracle behavior
2314+
2315+
**Tests:**
2316+
- 6 new deterministic regression tests covering:
2317+
- Empty blob via INSERT / UPDATE
2318+
- Empty string vs empty blob
2319+
- NULL vs empty blob vs empty string
2320+
- Sync round-trip (Zig -> Oracle)
2321+
- `typeof()` correctness
2322+
</details>
2323+
2324+
**Reproduction steps (clean checkout)**
2325+
1. `git clone <repo> && cd cr-sqlite`
2326+
2. `make -C zig test-parity` (includes verification)
2327+
3. `bash zig/harness/test-edge-cases.sh`
2328+
2329+
**Known gaps / unverified claims**
2330+
- No new divergences found after fix

.tasks/active/TASK-128-expand-parity-suite.md

Lines changed: 0 additions & 23 deletions
This file was deleted.
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# TASK-128: Expand parity suite with invalidation findings
2+
3+
## Goal
4+
Once TASK-127 has identified divergences (invalidating the "full parity" hypothesis), we must expand our regression test suite to cover these edge cases permanently.
5+
6+
## Scope
7+
- Analyze the divergence(s) found in TASK-127.
8+
- Create deterministic reproduction cases in the appropriate `zig/harness/test-*.sh` script (or create a new one if needed).
9+
- Fix the divergence in the Zig implementation (if it's a bug) or document it as a known limitation.
10+
- Verify the fix with the new test case.
11+
12+
## Files to Modify
13+
- `zig/harness/test-*.sh` (existing suites)
14+
- `zig/src/*.zig` (implementation fixes)
15+
16+
## Acceptance Criteria
17+
- [x] Deterministic regression tests exist for all divergences found in TASK-127.
18+
- [x] Zig implementation passes these new tests.
19+
- [x] `make -C zig test-parity` includes these new tests.
20+
21+
## Parent Docs
22+
- `research/zig-cr/92-gap-backlog.md`
23+
- `.tasks/done/TASK-127-experimental-parity-invalidation.md`
24+
25+
## Completion Notes
26+
27+
### 2024-12-20: Task Completed
28+
29+
Created `zig/harness/test-edge-cases.sh` with 6 deterministic regression tests covering:
30+
1. Empty blob via INSERT
31+
2. Empty blob via UPDATE
32+
3. Empty string vs empty blob distinction
33+
4. NULL vs empty blob vs empty string
34+
5. Sync round-trip (Zig -> Rust/C)
35+
6. `typeof()` correctness
36+
37+
All tests pass (6/6).
38+
This suite effectively covers the divergence found in TASK-127.
39+
40+
### 2024-12-21: Finalized integration
41+
42+
- Improved test script with separate setup/query helpers for clean output
43+
- Added test-edge-cases.sh to test-parity.sh header comments and execution section
44+
- Tests wired in and run successfully as part of the full parity suite
45+
46+
### Files Modified
47+
- `zig/harness/test-edge-cases.sh` (improved output handling)
48+
- `zig/harness/test-parity.sh` (wired in edge case tests)

research/zig-cr/92-gap-backlog.md

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,17 @@
1111

1212
## Now (next parallel assignments)
1313

14-
### Hypothesis Invalidation (Next Priority)
15-
- [ ] **TASK-127** — Experimentally invalidate "full parity" hypothesis via fuzzing `.tasks/backlog/TASK-127-experimental-parity-invalidation.md`
16-
- [ ] **TASK-128** — Expand parity suite with findings from TASK-127 `.tasks/backlog/TASK-128-expand-parity-suite.md`
17-
1814
All oracle parity tests pass. Zig implementation is now wire-compatible with Rust/C oracle.
1915

16+
### Hypothesis Invalidation (Done)
17+
- [x] **TASK-127** — Experimentally invalidate "full parity" hypothesis via fuzzing ✓ `.tasks/done/TASK-127-experimental-parity-invalidation.md`
18+
- Invalidation successful: Discovered empty blob (`X''`) vs `NULL` divergence
19+
- [x] **TASK-128** — Expand parity suite with invalidation findings ✓ `.tasks/done/TASK-128-expand-parity-suite.md`
20+
- Created `zig/harness/test-edge-cases.sh` with 6 deterministic tests
21+
- [x] **TASK-129** — Fix empty blob handling in Zig crsql_changes ✓ `.tasks/done/TASK-129-fix-empty-blob-parity.md`
22+
- Fixed `changes_vtab.zig` to return empty blob instead of NULL
23+
- All edge case tests pass
24+
2025
### Open Gaps (Parity Divergences)
2126
- [x] **TASK-123** — Fix clock table schema parity (pk vs key, index) ✓ `.tasks/done/TASK-123-fix-clock-table-schema-parity.md`
2227
- Column renamed `pk``key`, added STRICT mode, added `_dbv_idx` index
@@ -221,6 +226,15 @@ All oracle parity tests pass. Zig implementation is now wire-compatible with Rus
221226

222227
## Done (recent)
223228

229+
- **Round 54 (2025-12-20)**:
230+
- TASK-127: Experimentally invalidate parity hypothesis — found empty blob bug
231+
- TASK-128: Expand parity suite — added `test-edge-cases.sh` (6 tests)
232+
- TASK-129: Fix empty blob handling — fixed `values` serialization for zero-length blobs
233+
234+
- **Round 53 (2025-12-20)**:
235+
- TASK-125: Fix schema_alter pk→key column rename (6/6 pass)
236+
- TASK-126: Fix merge resolution parity (site_id ordinal conversion) (18/18 pass)
237+
224238
- **Round 51 (2025-12-20)**:
225239
- TASK-121: Fix rows_impacted ROLLBACK reset divergence (18/18 pass)
226240
- TASK-122: Fix no-op UPDATE db_version divergence (14/14 pass)

0 commit comments

Comments
 (0)