Files
poimen/tasks/T0.9-p0-composition-gate.md
T

110 lines
5.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.1T0.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 12 — 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)