Files

109 lines
4.7 KiB
Markdown
Raw Permalink Normal View History

2026-08-17 23:05:20 -07:00
# T2.6 — Crash matrix
| Field | Value |
|---|---|
| Phase | P2 — Durability hard parts |
| Size | L — over 3 days |
| Status | Not started |
| Flags | — |
| Spec | inlined below |
| Blocks | — |
## Goal
Systematic `kill -9` at every kernel transition, in a loop, verifying
recoverability after each. This is the phase gate.
## Facts (inlined — no spec read needed)
- P2 is the phase most likely to be skipped and most expensive to retrofit. The
matrix is what proves the three preceding tasks actually hold together.
- What "recoverable" means concretely: on restart, every attempt resolves to a
legal state per T0.2's table; every `Dispatched` intent resolves per T2.2's
class table; the fold from LSN 0 reproduces the state exactly (T0.8).
- "Zero double-applied effects" is the second, independent assertion. An
idempotent tool retried after a crash must leave one effect, not two.
- Lessons this suite exists to catch:
- *A green suite says nothing about coverage.* A prior idempotency feature
generated its own keys and checked for duplicates among them — unreachable
for a whole phase, with tests asserting the count was zero.
- *A test run that prints nothing cannot distinguish slow from hung.* Per-test
progress and per-test timeouts from the first commit.
## Steps
1. Enumerate the crash points: every kernel transition emitted by T1.3/T1.4, plus
T2.1's intra-intent windows, plus each of T0.6's four table writes.
2. Build the harness as a **real process kill** (`kill -9` on a child), not a
panic-and-catch. Shadow paging behaviour under a hard kill is part of what is
being tested.
3. Drive a workload with a scripted stub model (T1.2) and the three effect-class
tools (T2.2), so both durability and effect recovery are exercised.
4. Seed the crash-point selection from a recorded seed. Print the seed on
failure — an unreproducible crash matrix failure is not a finding.
5. After each restart assert: state is legal, intents resolve, the cold re-fold
matches, effects applied exactly once.
6. Emit per-test progress and enforce a per-test timeout so a hang is
distinguishable from a slow case.
7. Run 500 randomized crash points in CI; keep the exhaustive enumeration as a
longer nightly job.
## Acceptance
- 500 randomized crash points, **zero unrecoverable states, zero double-applied
effects**.
## Phase gate
P2 closes on a green crash matrix.
## Verify
**Harness:** the P2 capstone. Child process under `kill -9`; the external
side-effect ledger from T2.2; the three effect-class tools; the stub model; a
recorded seed printed on every run.
**Integration test**`tests/it_crash_matrix.rs`:
1. Enumerate crash points: every kernel transition, T2.1's two intent windows,
T0.6's four table writes. Assert the enumerated count matches an expected
constant, so a newly added transition without a crash point fails the test.
2. For each of 500 seeded random points: run the workload, `kill -9` at the
point, restart, then assert **all four** properties:
- every attempt is in a legal state per T0.2's table;
- every `Dispatched` intent resolved per T2.2's class table;
- `assert_refold_identical` passes;
- the external ledger shows **exactly one** effect per intended effect.
3. Print the seed and the crash point on failure; a failure that cannot be
replayed is not a finding.
4. Per-test progress output and a per-test timeout, so a hang is distinguishable
from a slow case.
5. Keep the **exhaustive** enumeration as a nightly job; the 500-point random
sample runs in CI.
**Command:**
`cargo test -p durability --features test-hooks --test it_crash_matrix -- --nocapture`
**False pass:**
- `panic!` instead of `kill -9`. Destructors run, buffers flush, and the whole
matrix passes against an implementation with no durability at all.
- Counting effects only among those the harness itself generated. That is the
documented prior failure: an idempotency feature generated its own keys and
checked for duplicates among them — unreachable for a whole phase, with tests
asserting the count was zero. Count against the **external ledger**.
- Step 1 omitted, so new transitions silently escape the matrix.
- Asserting recoverability as "the process restarted without error". Restarting
cleanly into wrong state is the failure being hunted.
## Traps
- Simulating a crash with `panic!` and unwinding. Destructors run, which is
exactly what a real crash does not do.
- Counting only effects the harness itself created — the coverage failure quoted
above, repeated.
---
Background (not required to do this task):
[rust-agentic-sys.md](../../../rust-agentic-sys.md) §8, §19 ·
[rust-agentic-task.md](../../../rust-agentic-task.md)