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

109 lines
4.9 KiB
Markdown
Raw Permalink 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.
# 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 test**`tests/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](../../../rust-agentic-sys.md) §1, §10 ·
[rust-agentic-task.md](../../../rust-agentic-task.md)