Files
poimen/tasks/T4.5-p4-composition-gate.md
T

4.9 KiB
Raw Blame History

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 Verifying case 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

  1. Assemble a verifier fleet on one attempt: exit-code, text-reading, panicking, hanging, erroring — five implementations, one fan-out.
  2. Fire all three snapshot mutators concurrently while the fleet is mid-flight.
  3. Assert every fail-closed outcome, every unaffected outcome, the blob call count, and the retention refusal, in one test.
  4. Swap the fleet for a completely different set of verifier implementations; assert the run's outcome shape is unchanged.
  5. 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 testtests/it_p4_composition.rs:

  1. 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.
  2. Run survives: assert the run reaches a terminal state and that agent work was never blocked — compare run duration against a no-verifier baseline.
  3. Concurrent mutators: with the fleet parked, fire a rewind, a cancel and an intent recovery at once. Assert every held EpisodeView is byte-identical to its entry snapshot.
  4. 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.
  5. Retention interlock: attempt reduction at three points — during Verifying, after Verifying but during Grading, and after terminal Ungraded. Assert refuse, refuse, permit.
  6. Verifier interchange: re-run the entire test with a second, unrelated set of five verifier implementations. Assert identical run-level outcomes.
  7. Isolation after panic: run a healthy verifier in the same process after the panicking one; assert Pass. A poisoned mutex passes every earlier step.
  8. Regression: re-run P0P3 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 Grading refusal, leaving the Ungraded-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