# T7.7 — `turmoil` suite | Field | Value | |---|---| | Phase | P7 — Distribution | | Size | L — over 3 days | | Status | Not started | | Flags | parallel-ok | | Spec | inlined below | | Blocks | — | ## Goal Network partition, latency and node loss simulated deterministically. This is the P7 gate. ## Facts (inlined — no spec read needed) - Test tooling and what each covers: - **`turmoil`** — network partition and latency simulation. - **`loom`** — the lock-free bits. - **`tokio::time::pause`** — time. - Together these are **weaker than a seeded scheduler and sufficient with discipline**. The residual risk is stated honestly rather than engineered around: tokio's cancellation is cooperative, so a `select!`-dropped future stops at its next await and not before. - What the suite must prove: **no double-execution and no lost run under partition, with automatic recovery**. - Double-execution is prevented by fencing (T7.3), not by the partition never happening. This suite is what demonstrates the fence actually fires. - `parallel-ok`: this task does not gate others and can be built alongside the rest of P7. - Open: whether a lint, a wrapper type, or a `loom` harness is the right enforcement for cancellation rigour under tokio. Unresolved — do not invent a fourth mechanism here. ## Steps 1. Stand up a two-node topology under `turmoil`, both nodes claiming runs from the same Postgres backend. 2. Partition scenario: isolate node A mid-run, let its lease expire, watch node B reclaim, then heal the partition and assert node A's writes are fenced off. 3. Latency scenario: inject delays that push heartbeats close to the TTL, and assert renewals either succeed or cancel the local scope — never a silent continue. 4. Node-loss scenario: kill a node outright; assert the run is reclaimed and completes exactly once. 5. Add `loom` coverage for any lock-free structure introduced in P7. 6. Use `tokio::time::pause` for all TTL and deadline arithmetic so the suite runs in seconds. 7. Print per-scenario progress and enforce per-scenario timeouts — a suite that prints nothing cannot distinguish slow from hung. ## Acceptance - **No double-execution and no lost run under partition; recovery is automatic.** ## Phase gate P7 closes on two nodes surviving a partition with no double-execution. ## Verify **Harness:** `turmoil` two-node topology; `loom` for any lock-free structure; `tokio::time::pause` for TTL arithmetic; the external side-effect ledger as the double-execution witness. **Integration test** — `tests/it_turmoil.rs`: 1. **Partition:** isolate node A mid-run. Assert node B reclaims after lease expiry, the run completes, and the ledger shows **exactly one** effect per intended effect. 2. Heal the partition; assert A's writes are fenced (cross-check T7.3) and that A does not resume executing. 3. **Latency:** inject delays that push heartbeats close to the TTL. Assert renewals either succeed or **cancel the local scope** — assert no case where a worker continues past a failed renewal. 4. **Node loss:** kill a node outright. Assert the run is reclaimed and completes exactly once. 5. Assert **no lost run**: every spawned run reaches a terminal state across all scenarios; count spawned vs terminal. 6. Recovery is **automatic** — assert no manual intervention hook was invoked. 7. `loom` test for each lock-free structure added in P7. 8. Per-scenario progress output and per-scenario timeouts. 9. Print the seed; a failure that cannot be replayed is not a finding. **Command:** `cargo test -p distribution --test it_turmoil -- --nocapture` **False pass:** - Asserting the run completed. **Completing twice also completes** — the ledger count is the only assertion that distinguishes them, and double-execution is the primary risk this suite exists to retire. - Real `sleep` for TTL expiry, making the suite slow enough that it stops being run — which is the same as not having it. - Step 5 omitted: a run that is silently dropped during a partition leaves no error, and every other assertion passes. - Testing only the partition scenario. Node loss and slow-network cases exercise different reclaim paths. ## Traps - Asserting only that the run completes. Completing twice also completes. - Real sleeps for TTL expiry, which makes the suite too slow to run and therefore unrun. --- Background (not required to do this task): [rust-agentic-sys.md](../../../rust-agentic-sys.md) §6, §9.4, §18, §19 · [rust-agentic-task.md](../../../rust-agentic-task.md)