110 lines
5.1 KiB
Markdown
110 lines
5.1 KiB
Markdown
# 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:
|
|||
|
|
1. every key is tenant-scoped (T0.1) — including keys written by T0.6's commit;
|
|||
|
|
2. every record carries `SchemaVersion` and decodes through the upcaster chain
|
|||
|
|
(T0.3, T0.4);
|
|||
|
|
3. all four table writes are atomic (T0.6);
|
|||
|
|
4. 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
|
|||
|
|
|
|||
|
|
1. Build a **second `EventLog` implementation**: a deliberately naive in-memory
|
|||
|
|
one, correct but sharing no code with the `redb` backend.
|
|||
|
|
2. Build a second `BlobStore` the same way.
|
|||
|
|
3. 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.
|
|||
|
|
4. Write the cross-invariant matrix (below) as a single test binary that
|
|||
|
|
exercises every P0 task in one path.
|
|||
|
|
5. Wire T0.8's `assert_refold_identical` into that path so composition and
|
|||
|
|
re-derivability are asserted together.
|
|||
|
|
6. Make this binary a **required CI job** and the P1 entry condition.
|
|||
|
|
|
|||
|
|
## Acceptance
|
|||
|
|
|
|||
|
|
- Both `EventLog` implementations and both `BlobStore` implementations 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`:
|
|||
|
|
1. **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.
|
|||
|
|
2. **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.
|
|||
|
|
3. **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.
|
|||
|
|
4. **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.
|
|||
|
|
5. **LSN under contention across branches:** 16 concurrent writers across 3
|
|||
|
|
branches of 2 runs; assert per-branch `1..=n` gap-free and no cross-branch
|
|||
|
|
interference.
|
|||
|
|
6. **Blob/log linkage:** put a blob, reference it from a committed record, drop
|
|||
|
|
state, re-fold, resolve the ref. Assert the bytes.
|
|||
|
|
7. 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](../../../rust-agentic-sys.md) §3, §7, §8 ·
|
|||
|
|
[rust-agentic-task.md](../../../rust-agentic-task.md)
|