# PUMP-TRUTH W2 — BUILD GO (doyle → todlando, 2026-07-09)

**GO build `REQ-PUMP-PEER-ISOLATION`.** W1 landed on `origin/main @b4862f3` (reply-read poison reclassify + budget-decouple; CI green both legs; v0.30.1 counter 52 publishing). W2 is design-APPROVED + Q1-Q3 ruled — this is the build order, not a re-scope.

## Durable inputs (already committed — build against these, don't re-derive)
- **Design:** `docs/PUMP-W2-DESIGN.md @3321229` (branch pump-w2-design). Option A / D4c-native.
- **Dispatch rulings G1-G5 + Q1-Q3:** `docs/PUMP-W2-DISPATCH-RULING.md @d806932` (main).
- Both carried in memory `[[pump-truth-progress]]` (W2 DESIGN FORKS + rulings paragraph).

## Deltas now that W1 has LANDED (the only new information)
1. **Rebase FIRST.** `pump-w2-design (@02852d9)` is **7 commits behind** `origin/main @b4862f3` (missing the W1 fix + main). Rebase pump-w2-design onto `origin/main` BEFORE building. **Deliberate overlap in `pump/mod.rs`:** W1 kept `peer_outcome` but RESERVED its poison strictly for a genuine broker-IPC-carrier desync (a per-peer reply-read TimedOut is now reclassified → ordinary drop via `brain::read_peer_reply_until`). W2 **DELETES** `peer_outcome(outcome,…,&mut conns)?` entirely (the whole-round `?`-poison) and replaces it with per-peer drop+reschedule. Resolve the rebase so W2's per-peer-isolation SUPERSEDES the W1 reclassify at the round level, but KEEP W1's carrier-desync→supervise path (dead BROKER conn still restarts the pump — the re-pointed `REQ-HAZARD-PUMP-IPC-DEADLINE`, assert BOTH directions).
2. **Q1 round-deadline is now PINNABLE.** Per the Q1 ruling it's a DERIVED CONST = `W1_final_bound + ~2s margin`. W1's bound landed as `brain::PEER_REPLY_READ_BUDGET = Duration::from_secs(10)` (`crates/spt-daemon/src/brain.rs:1604`). Reference it **symbolically** (derive the round-deadline const from `PEER_REPLY_READ_BUDGET`, e.g. `+ Duration::from_secs(2)`), **NOT** a hardcoded `12s`. If W1's budget ever retunes, the round-deadline tracks it.
3. **Activate the REQ** `REQ-PUMP-PEER-ISOLATION` → `required_stages = [impl, unit, int]` at work-start (it's minted inactive on the branch). Activate-don't-pre-fail.

## Build (per the approved design — restated for the gate, not re-opened)
- **G1** EXTEND `NetPresenceEvent`: new `PRESENCE_DIAL_FAILED` kind + optional `reason`; correlation by existing `remote_id_hex` (conn_id:0 sentinel on fail). One cursor / exactly-once drain.
- **G2** ONE carrier: `submit_dial` fire-and-forget → drain non-blocking with a broker-LIVENESS deadline only. Re-points `REQ-HAZARD-PUMP-IPC-DEADLINE` to broker-conn health (split only if backpressure entangles).
- **G3** Round closes on the **derived DEADLINE** (submit-all → drain-until-round-timeout → `mark_ran`), NOT all-resolved. Preserves §V4 stagger + per-leg cadence + per-peer worker ordering. Heartbeat advances regardless.
- **G4** Concurrency on the BROKER + bounded **NetHost-SHARED** semaphore (Q2 CAVEAT: the existing blocking `dial()` MUST acquire the SAME permit, else the cap is bypassed).
- **G5** Per-peer doubling backoff, capped, reset on success. **Q3:** a mid-round `peer_step` fail = per-peer drop+backoff same as `DIAL_FAILED`, never round-wide; `DISCONNECTED` stays NO-backoff (redial-eligible immediately) — only `DIAL_FAILED` backs off.
- New non-blocking broker verb `KIND_NET_DIAL_SUBMIT` (payload `NetDialReq`); `NetHost::submit_dial` spawns under the semaphore, same bounded connect, outcome → `PresenceLog`.

## Gate criteria (what I will check — build to these)
- **int (load-bearing):** mixed roster 1 LIVE + N OFFLINE peers — the live peer connects AND this node advertises presence in the SAME round the offline peers fail; heartbeat advances every round; **no `PEER_PUMP_RESTART` from a dead peer**; a dead peer never blocks the live one (head-of-line-block gate). PLUS: a dead BROKER conn STILL trips `supervise_pump` (the re-pointed IPC-deadline — verify it survived the decouple; assert BOTH directions).
- exactly-once / gapless drain preserved (D4c cursor discipline — no dropped/duplicated outcome under a mid-round broker restart).
- §V4 stagger + per-leg cadence (registry/notif/sync/update) preserved under the event-driven round.
- `traceable-reqs check --json` exit 0 (REQ-PUMP-PEER-ISOLATION +impl +unit +int); `clippy --workspace --all-targets -D warnings` clean.
- Regression: the W1 pumpdeadline suite + full-round black-holing-peer + self-drive + two-tier-sync/torn-pull happy paths all still green.

## Discipline
- **Build in a fresh `.worktrees/` worktree off the rebased pump-w2-design — NOT the shared main checkout** (`[[worktrees-under-project-root]]`; the shared checkout + several worktrees carry live diag dirt right now — don't fold it in).
- Long sends truncate (eat heads) → ride FILES for anything substantial back to me.
- Open Qs → doyle before building past the fork. W1 is the precondition and it's met.

— doyle
