Skip to content

Commit 45ad4c0

Browse files
delegate round 41: oracle parity tests complete (TASK-089, 090, 091, 092)
1 parent 5fda98a commit 45ad4c0

File tree

6 files changed

+308
-61
lines changed

6 files changed

+308
-61
lines changed

.tasks/DELEGATE_WORK_HANDOFF.md

Lines changed: 122 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,128 @@ Artifacts:
6868

6969
---
7070

71-
## Round 2025-12-15 (32) — Phase 4 Mesh implementation complete
71+
## Round 2025-12-17 (41) — Oracle parity tests (4 tasks)
72+
73+
**Tasks executed**
74+
- `.tasks/done/TASK-089-api-surface-completeness.md`
75+
- `.tasks/done/TASK-090-trigger-clock-logic-equivalence.md`
76+
- `.tasks/done/TASK-091-fract-index-algorithm-parity.md`
77+
- `.tasks/done/TASK-092-db-version-advancement-parity.md`
78+
79+
**Commits**
80+
- `5fda98a2``++` (batched oracle parity tests)
81+
- (and 5 earlier `++` commits from subagents)
82+
83+
**Modified files (root repo)**
84+
- `zig/harness/test-api-surface.sh` (new, 205 lines)
85+
- `zig/harness/test-trigger-parity.sh` (new, 456 lines)
86+
- `zig/harness/test-fract-parity.sh` (new, 277 lines)
87+
- `zig/harness/test-db-version-parity.sh` (new, 442 lines)
88+
- `zig/harness/test-parity.sh` (updated, wired in all new tests)
89+
- Task cards in `.tasks/done/`
90+
91+
**Environment**
92+
- OS: darwin (macOS ARM64)
93+
- Tooling: nix, sqlite3, bash
94+
95+
**Commands run (exact)**
96+
```bash
97+
make -C zig test-parity
98+
bash zig/harness/test-api-surface.sh
99+
bash zig/harness/test-trigger-parity.sh
100+
bash zig/harness/test-fract-parity.sh
101+
bash zig/harness/test-db-version-parity.sh
102+
```
103+
104+
**Outputs (paste)**
105+
106+
<details>
107+
<summary>TASK-089 API Surface (10 gaps found)</summary>
108+
109+
```text
110+
Functions Missing from Zig (4 actionable):
111+
- crsql_automigrate
112+
- crsql_config_get
113+
- crsql_config_set
114+
- crsql_get_seq
115+
116+
Functions Intentionally Excluded (4 internal):
117+
- crsql_after_delete, crsql_after_insert, crsql_after_update (trigger functions)
118+
- crsql_sha (debug utility)
119+
120+
Modules Missing from Zig (2):
121+
- clset
122+
- crsql_unpack_columns
123+
124+
Result: FAIL (10 gaps documented)
125+
```
126+
</details>
127+
128+
<details>
129+
<summary>TASK-090 Trigger/Clock Parity (13 divergences)</summary>
130+
131+
```text
132+
Key Divergences:
133+
1. Sentinel row timing: Zig creates sentinel on every INSERT, Rust only on resurrection
134+
2. Resurrection col_version: Zig resets to 1, Rust increments through cycle (col_version=3)
135+
3. Seq ordering: Different strategies for ordering changes within a transaction
136+
137+
Result: 2 passed, 13 failed (divergences documented)
138+
```
139+
</details>
140+
141+
<details>
142+
<summary>TASK-091 Fract Index Parity (byte-identical)</summary>
143+
144+
```text
145+
12 test cases comparing crsql_fract_key_between(a, b):
146+
- (NULL, NULL) → 'a ' ✓
147+
- ('a ', NULL) → 'a!' ✓
148+
- (NULL, 'a ') → 'Z~' ✓
149+
- Between values → byte-identical ✓
150+
- Long strings → byte-identical ✓
151+
- Error cases → both reject invalid input ✓
152+
153+
Result: 12/12 passed — Zig and Rust/C are byte-identical
154+
```
155+
</details>
156+
157+
<details>
158+
<summary>TASK-092 db_version Parity (1 divergence)</summary>
159+
160+
```text
161+
Test 1-5, 7-8: PASS (initial, INSERT, UPDATE, TX, DELETE, merge, no-op merge)
162+
Test 6: No-op UPDATE (same value)
163+
- Rust/C: db_version advances (1 → 2)
164+
- Zig: db_version unchanged (1 → 1)
165+
166+
Result: 12 passed, 1 failed
167+
Critical divergence: No-op UPDATE handling differs
168+
```
169+
</details>
170+
171+
**Reproduction steps (clean checkout)**
172+
1. `git clone <repo> && cd cr-sqlite`
173+
2. `make -C zig test-parity`
174+
3. Or run individual tests: `bash zig/harness/test-api-surface.sh`
175+
176+
**Known gaps / unverified claims**
177+
- Trigger/clock divergences are significant (sentinel row, resurrection col_version, seq ordering)
178+
- API surface has 10 gaps (4 actionable functions + 2 modules)
179+
- db_version no-op UPDATE divergence may or may not be intentional
180+
- Tests run against local extension builds — CI integration not verified this round
181+
182+
**Summary**
183+
This round created 4 oracle-based parity test scripts that use Rust/C as the golden master:
184+
1. **API surface** — enumerated all functions/modules, found 10 gaps
185+
2. **Trigger/clock** — compared clock table outputs, found 13 divergences in sentinel/resurrection behavior
186+
3. **Fract index** — verified byte-identical output across 12 test cases
187+
4. **db_version** — found 1 critical divergence in no-op UPDATE handling
188+
189+
These tests now run as part of `make -C zig test-parity` and will catch regressions.
190+
191+
---
192+
72193

73194
**Tasks executed**
74195
- `.tasks/done/TASK-048-crsql-mesh-protocol-schema-reuse.md`

.tasks/active/TASK-091-fract-index-algorithm-parity.md

Lines changed: 0 additions & 50 deletions
This file was deleted.
File renamed without changes.
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
# TASK-091: Oracle Parity — Fractional index algorithm
2+
3+
## Status
4+
- [ ] Planned
5+
- [ ] Assigned
6+
- [ ] In Progress
7+
- [ ] Blocked (reason: ...)
8+
- [x] Complete
9+
10+
## Priority
11+
medium
12+
13+
## Assigned To
14+
(unassigned)
15+
16+
## Parent Docs / Cross-links
17+
- Rust fract implementation: `core/rs/fractindex-core/src/fractindex.rs`
18+
- Zig fract implementation: `zig/src/fract_index.zig`
19+
- Existing fract tests: `zig/harness/test-fract.sh`
20+
- Gap backlog: `research/zig-cr/92-gap-backlog.md`
21+
22+
## Description
23+
Verify that `crsql_fract_key_between(a, b)` produces identical output in both Rust/C and Zig for the same inputs.
24+
25+
This is an **oracle test**: The fractional index algorithm must be deterministic and produce the same lexicographically-sortable string in both implementations.
26+
27+
## Files to Modify
28+
- `zig/harness/test-fract-parity.sh` (new or extend `test-fract.sh`)
29+
- `zig/harness/test-parity.sh` (wire into suite)
30+
- `research/zig-cr/92-gap-backlog.md`
31+
32+
## Acceptance Criteria
33+
- [x] Test calls `crsql_fract_key_between(a, b)` with identical inputs on both implementations.
34+
- [x] Test cases include:
35+
- `(NULL, NULL)` — first key: returns `'a '` (hex: 6120)
36+
- `('a ', NULL)` — key after 'a ': returns `'a!'` (hex: 6121)
37+
- `(NULL, 'a ')` — key before 'a ': returns `'Z~'` (hex: 5A7E)
38+
- `('a0', 'a1')` — key between: returns `'a0P'` (hex: 613050)
39+
- `('aaa', 'aab')` — key between close values: returns `'aaaP'` (hex: 61616150)
40+
- `('', 'a ')` — edge case with empty string: both reject as invalid
41+
- Long strings (101 chars) to test: returns `'aQ'` (hex: 6151)
42+
- [x] Outputs are **byte-identical** (not just semantically equivalent).
43+
- [x] Test fails if any output differs.
44+
- [x] Results maintain lexicographic ordering: `a < result < b` when both are non-NULL.
45+
46+
## Progress Log
47+
### 2025-12-18
48+
- Task created from oracle-based parity test suite.
49+
50+
### 2025-12-18
51+
- Created `zig/harness/test-fract-parity.sh` with 12 test cases
52+
- Wired into `zig/harness/test-parity.sh`
53+
- All 12 parity tests pass - Zig and Rust/C produce byte-identical output
54+
55+
## Completion Notes
56+
57+
### Commands Run
58+
```bash
59+
# Direct test run
60+
./zig/harness/test-fract-parity.sh
61+
62+
# Via make target
63+
make -C zig test-parity
64+
```
65+
66+
### Full Test Output
67+
```
68+
╔═══════════════════════════════════════════════════════════════════════╗
69+
║ Fractional Index Oracle Parity Test ║
70+
║ Compares Rust/C vs Zig implementation of crsql_fract_key_between ║
71+
╚═══════════════════════════════════════════════════════════════════════╝
72+
73+
Rust/C extension: lib/crsqlite.dylib
74+
Zig extension: lib/crsqlite-zig-darwin-aarch64.dylib
75+
76+
Test: (NULL, NULL) — first key
77+
Rust/C: 'a ' (hex: 6120)
78+
Zig: 'a ' (hex: 6120)
79+
✓ Byte-identical: YES
80+
PASS
81+
82+
Test: ('a ', NULL) — key after 'a '
83+
Rust/C: 'a!' (hex: 6121)
84+
Zig: 'a!' (hex: 6121)
85+
✓ Byte-identical: YES
86+
PASS
87+
88+
Test: (NULL, 'a ') — key before 'a '
89+
Rust/C: 'Z~' (hex: 5A7E)
90+
Zig: 'Z~' (hex: 5A7E)
91+
✓ Byte-identical: YES
92+
PASS
93+
94+
Test: ('a0', 'a1') — key between
95+
Rust/C: 'a0P' (hex: 613050)
96+
Zig: 'a0P' (hex: 613050)
97+
✓ Byte-identical: YES
98+
✓ Ordering: a < result < b
99+
PASS
100+
101+
Test: ('aaa', 'aab') — close values
102+
Rust/C: 'aaaP' (hex: 61616150)
103+
Zig: 'aaaP' (hex: 61616150)
104+
✓ Byte-identical: YES
105+
✓ Ordering: a < result < b
106+
PASS
107+
108+
Test: ('a0P', 'a0Q') — very close values
109+
Rust/C: 'a0PP' (hex: 61305050)
110+
Zig: 'a0PP' (hex: 61305050)
111+
✓ Byte-identical: YES
112+
✓ Ordering: a < result < b
113+
PASS
114+
115+
Test: Long string (101 chars)
116+
Rust/C: 'aQ' (hex: 6151)
117+
Zig: 'aQ' (hex: 6151)
118+
✓ Byte-identical: YES
119+
PASS
120+
121+
Test: (NULL, 'Z~') — negative integer
122+
Rust/C: 'Z}' (hex: 5A7D)
123+
Zig: 'Z}' (hex: 5A7D)
124+
✓ Byte-identical: YES
125+
PASS
126+
127+
Test: ('Z}', 'Z~') — between negative integers
128+
Rust/C: 'Z}P' (hex: 5A7D50)
129+
Zig: 'Z}P' (hex: 5A7D50)
130+
✓ Byte-identical: YES
131+
✓ Ordering: a < result < b
132+
PASS
133+
134+
Test: Sequential key generation maintains ordering
135+
Rust/C sequence (hex): 6120,6121,6122,6123,6124
136+
Zig sequence (hex): 6120,6121,6122,6123,6124
137+
✓ Byte-identical: YES
138+
PASS
139+
140+
Test: Error case parity - empty string
141+
Rust/C value: ERROR (rejects invalid input)
142+
Zig value: ERROR (rejects invalid input)
143+
✓ Both reject invalid input (empty string)
144+
PASS
145+
146+
Test: Error case parity - a > b (invalid order)
147+
Rust/C: Error (must be before)
148+
Zig: Error (left key must be before right key)
149+
✓ Both produce error on invalid order
150+
PASS
151+
152+
╔═══════════════════════════════════════════════════════════════════════╗
153+
║ PARITY TEST SUMMARY ║
154+
╠═══════════════════════════════════════════════════════════════════════╣
155+
║ PASSED: 12 ║
156+
║ FAILED: 0 ║
157+
╚═══════════════════════════════════════════════════════════════════════╝
158+
159+
✓ All parity tests PASSED - Zig and Rust/C are byte-identical
160+
```
161+
162+
### Divergences Discovered
163+
None - all outputs are byte-identical between Zig and Rust/C implementations.
164+
165+
### Notes on Test Input Format
166+
The fractional index algorithm uses a specific key format with an "integer part" prefix. Valid keys must:
167+
- Start with a letter A-Z (negative integers) or a-z (positive integers)
168+
- Have a fractional part using base-95 digits (space to tilde)
169+
170+
Invalid inputs like `'a'` (missing fractional part) or `''` (empty string) are rejected by both implementations.

.tasks/active/TASK-092-db-version-advancement-parity.md renamed to .tasks/done/TASK-092-db-version-advancement-parity.md

File renamed without changes.

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

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# 92-gap-backlog
22

3-
> Last updated: 2025-12-18 (added oracle-based parity tests TASK-089 through TASK-094)
3+
> Last updated: 2025-12-17 (Round 41: completed TASK-089/090/091/092 oracle parity tests)
44
55
## Status
66

@@ -35,15 +35,21 @@ Missing-feature RGRTDD tracks (spec then impl):
3535
- [ ] **TASK-088** — Implement savepoint-backed merge atomicity → `.tasks/backlog/TASK-088-impl-merge-atomicity.md`
3636

3737
Oracle-based parity tests (Rust/C as golden master):
38-
- [ ] **TASK-089** — API surface completeness: pragma_function_list/module_list comparison → `.tasks/active/TASK-089-api-surface-completeness.md`
39-
- [ ] **TASK-090** — Trigger/clock logic equivalence: col_version/db_version/seq match → `.tasks/backlog/TASK-090-trigger-clock-logic-equivalence.md`
40-
- [ ] **TASK-091** — Fract index algorithm parity: crsql_fract_key_between output match → `.tasks/backlog/TASK-091-fract-index-algorithm-parity.md`
41-
- [x] **TASK-092** — db_version advancement parity: version increments at same moments → `.tasks/active/TASK-092-db-version-advancement-parity.md`
42-
- **DIVERGENCE FOUND**: No-op UPDATE advances db_version in Rust/C but NOT in Zig
43-
- Zig: 1 → 1 (no change); Rust/C: 1 → 2 (advances)
44-
- All other test cases pass (12 passed, 1 failed, 1 divergence)
45-
- [ ] **TASK-093** — rows_impacted counter timing: reset timing match → `.tasks/backlog/TASK-093-rows-impacted-counter-timing.md`
46-
- [ ] **TASK-094** — ALTER TABLE history preservation: clock history + backfill match → `.tasks/backlog/TASK-094-alter-table-history-preservation.md`
38+
- [x] **TASK-089** — API surface completeness ✓ `.tasks/done/TASK-089-api-surface-completeness.md`
39+
- 10 gaps found: 4 functions + 2 modules actionable, 4 internal functions excluded
40+
- Test: `zig/harness/test-api-surface.sh`
41+
- [x] **TASK-090** — Trigger/clock logic equivalence ✓ `.tasks/done/TASK-090-trigger-clock-logic-equivalence.md`
42+
- **13 DIVERGENCES**: sentinel row timing, resurrection col_version, seq ordering
43+
- Test: `zig/harness/test-trigger-parity.sh`
44+
- [x] **TASK-091** — Fract index algorithm parity ✓ `.tasks/done/TASK-091-fract-index-algorithm-parity.md`
45+
- **BYTE-IDENTICAL**: 12/12 tests pass
46+
- Test: `zig/harness/test-fract-parity.sh`
47+
- [x] **TASK-092** — db_version advancement parity ✓ `.tasks/done/TASK-092-db-version-advancement-parity.md`
48+
- **1 DIVERGENCE**: No-op UPDATE handling (Rust advances, Zig doesn't)
49+
- 12 passed, 1 failed
50+
- Test: `zig/harness/test-db-version-parity.sh`
51+
- [ ] **TASK-093** — rows_impacted counter timing → `.tasks/backlog/TASK-093-rows-impacted-counter-timing.md`
52+
- [ ] **TASK-094** — ALTER TABLE history preservation → `.tasks/backlog/TASK-094-alter-table-history-preservation.md`
4753

4854
## Context / Evidence
4955

0 commit comments

Comments
 (0)