5.1 KiB
5.1 KiB
T0.9 — P0 composition gate
| Field | Value |
|---|---|
| Phase | P0 — Foundations |
| Size | M — 1 to 3 days |
| Status | Not started |
| Flags | gate |
| Spec | inlined below |
| Blocks | P1 — nothing in P1 starts until this is green |
Goal
Prove T0.1–T0.8 compose. Each was verified alone; this task verifies they work together and interchangeably — that the ports are real seams, not shapes.
Phase gate criterion: types compile, log round-trips, schema fixture test green, drop-and-re-fold byte-identical.
Facts (inlined — no spec read needed)
- A port is only real if a second implementation can be dropped in without editing the suite. P0 ships one implementation of each port; the seam is therefore unproven until a second one exists. T7.1/T7.2 supply the production second implementation much later — too late to learn the port is short.
- The four properties that must hold jointly, not individually:
- every key is tenant-scoped (T0.1) — including keys written by T0.6's commit;
- every record carries
SchemaVersionand decodes through the upcaster chain (T0.3, T0.4); - all four table writes are atomic (T0.6);
- dropping derived state and re-folding from LSN 0 is byte-identical (T0.8).
- The failure this gate exists to catch: each task passing its own test while the composition violates a property none of them owns. A commit path that writes a correctly-scoped log key and an unscoped outbox key passes T0.1's tests and T0.6's tests, and leaks across tenants.
Steps
- Build a second
EventLogimplementation: a deliberately naive in-memory one, correct but sharing no code with theredbbackend. - Build a second
BlobStorethe same way. - Run T0.5's and T0.7's conformance suites against both implementations, unmodified. Any edit needed is a finding about the port, not the test.
- Write the cross-invariant matrix (below) as a single test binary that exercises every P0 task in one path.
- Wire T0.8's
assert_refold_identicalinto that path so composition and re-derivability are asserted together. - Make this binary a required CI job and the P1 entry condition.
Acceptance
- Both
EventLogimplementations and bothBlobStoreimplementations pass the conformance suites unmodified. - The cross-invariant matrix is green.
- P0's stated gate holds: types compile, log round-trips, schema fixture test green, re-fold byte-identical.
Verify
Harness: the two extra port implementations from steps 1–2 — they exist only for this gate and stay in the tree as the anti-drift device.
Integration test — tests/it_p0_composition.rs:
- Port interchange: parameterize the whole test over
[redb, in_memory] × [redb_blobs, in_memory_blobs]. All four combinations must pass. A combination that fails means a caller depends on an implementation detail rather than the trait. - Scope sweep: after a full workload across 2 tenants, scan every raw table — log, state, position, outbox, blobs — and assert every key carries the expected tenant. Not through the read path; through a raw scan.
- Atomicity under composition: arm T0.6's fault hook while a commit carries records, state deltas, a position advance and an outbox entry simultaneously. Assert all-or-nothing across all four.
- Schema + fold: write records at v1, upgrade to v2, then drop state and re-fold. Assert byte-identical state. This is T0.4 and T0.8 composed, and it is the pair most likely to break each other.
- LSN under contention across branches: 16 concurrent writers across 3
branches of 2 runs; assert per-branch
1..=ngap-free and no cross-branch interference. - Blob/log linkage: put a blob, reference it from a committed record, drop state, re-fold, resolve the ref. Assert the bytes.
- Re-run every P0 task's own test suite in the same job, so this gate catches a regression rather than only a composition bug.
Command: cargo test -p foundations --test it_p0_composition
False pass:
- Running the matrix against one implementation "because the second is only a test double". The second implementation is the test — it is the only thing that proves the port is a seam before T7.1 finds out the expensive way.
- Step 2 through the read path, which filters by tenant and passes against a completely unscoped table.
- Step 4 with a v2 change that touches no recorded variant, so no upcaster runs and the fold is trivially identical.
- Treating this gate as optional because the eight underlying suites are green. Every property here is one no single task owns.
Traps
- Deleting the in-memory implementations once T7.1 lands. They are the fast CI path and the drift detector; the Postgres backend is neither.
- Letting the conformance suite grow implementation-specific branches
(
if backend == redb). At that point it no longer tests the port.
Background (not required to do this task): rust-agentic-sys.md §3, §7, §8 · rust-agentic-task.md