4.9 KiB
4.9 KiB
T4.5 — P4 composition gate
| Field | Value |
|---|---|
| Phase | P4 — Verification |
| Size | M — 1 to 3 days |
| Status | Not started |
| Flags | gate |
| Spec | inlined below |
| Blocks | P5 |
Goal
Prove the verifier port, the snapshot barrier, lazy blob access and the retention guard hold simultaneously, and that any verifier implementation is interchangeable.
Phase gate criterion: fail-closed proven by fault injection.
Facts (inlined — no spec read needed)
- The framework cannot break the agent it runs. Verification failures degrade the record, never the work. Any path where a verifier can fail into an agent's execution is a defect — and the composition is where such a path appears, since no single verifier test runs a full fleet.
- The properties no single P4 task owns:
- a panicking verifier must not disturb a concurrent healthy verifier's snapshot, nor the run;
- the barrier must hold while all three external mutators fire, including at once;
- laziness must survive fan-out — one text-reading verifier must not cause the exit-code verifiers beside it to fetch anything;
- retention must refuse while any verifier is outstanding, which is the
Verifyingcase T4.4 checks and the composition makes real.
- Verification decides, grading explains. A gate that lets a verifier outcome be influenced by grading state has collapsed the two.
Steps
- Assemble a verifier fleet on one attempt: exit-code, text-reading, panicking, hanging, erroring — five implementations, one fan-out.
- Fire all three snapshot mutators concurrently while the fleet is mid-flight.
- Assert every fail-closed outcome, every unaffected outcome, the blob call count, and the retention refusal, in one test.
- Swap the fleet for a completely different set of verifier implementations; assert the run's outcome shape is unchanged.
- Make this the required CI job gating P5.
Acceptance
- With the five-verifier fleet on one attempt: the three bad verifiers resolve to
Fail, the two good ones return their own results, and the run completes. - The snapshot holds across all three mutators fired concurrently.
- Exit-code verifiers record zero blob reads even while a sibling reads text.
- Reduction is refused for the whole duration of
Verifying.
Verify
Harness: five verifier implementations; a counting BlobStore wrapper per
verifier, not one shared counter; rendezvous channels so mutators fire only once
every verifier has demonstrably parked.
Integration test — tests/it_p4_composition.rs:
- Fleet fail-closed: assert panic →
Fail, hang →Fail, error →Fail, and that the exit-code and text verifiers return their own correct results. A fleet where one bad verifier poisons the others is the failure. - Run survives: assert the run reaches a terminal state and that agent work was never blocked — compare run duration against a no-verifier baseline.
- Concurrent mutators: with the fleet parked, fire a rewind, a cancel and an
intent recovery at once. Assert every held
EpisodeViewis byte-identical to its entry snapshot. - Per-verifier laziness: assert the exit-code verifiers' counters are exactly 0 while the text verifier's is exactly the number of refs it requested. A single shared counter hides this.
- Retention interlock: attempt reduction at three points — during
Verifying, afterVerifyingbut duringGrading, and after terminalUngraded. Assert refuse, refuse, permit. - Verifier interchange: re-run the entire test with a second, unrelated set of five verifier implementations. Assert identical run-level outcomes.
- Isolation after panic: run a healthy verifier in the same process after
the panicking one; assert
Pass. A poisoned mutex passes every earlier step. - Regression: re-run P0–P3 gates and all P4 suites.
Command: cargo test -p verify --test it_p4_composition -- --test-threads=1
False pass:
- Running the five verifiers sequentially. Fan-out is the declared concurrency shape, and cross-contamination between verifiers only exists concurrently.
- Step 3 firing the mutators one at a time. Each is already covered by T4.2; the gate is about them arriving together.
- One shared blob counter in step 4 — the sibling's reads mask the exit-code verifiers' zero.
- Step 5 testing only the
Gradingrefusal, leaving theUngraded-is-permitted half untested. That is the half that strands storage in production.
Traps
- Catching the verifier panic in the test rather than asserting the framework catches it.
- Asserting the run "did not error" instead of asserting it reached a terminal state with a complete log.
Background (not required to do this task): rust-agentic-sys.md §1, §10 · rust-agentic-task.md