# ER bring-up residual — #199 investigation

> **2026-08-19 UPDATE — two premises of this file's earlier plan are REFUTED, and the expensive
> arm is DEAD.** Read this header before anything below it. Superseded detail is corrected here,
> not annotated in place.

## SETTLED 2026-08-19 (source-read at origin/main 9ea595c; code byte-identical to landed 901a9f5)

### A. The 20-run pre-#197 arm is NOT needed — #197 cannot introduce OR unmask the erhost face

- **#197 (`5eb6a3b`) does not touch `crates/spt/src/cli.rs` at all** — not in the diffstat.
  The briefed command `git show 5eb6a3b -- crates/spt/src/cli.rs` returns 0 bytes. Its only
  `spt`-crate file is `rc.rs`, and that hunk is **comments only** (comment block at `rc.rs:2082`,
  test comment at `:2867`). Zero behavior in rc.
- **The erhost refusal is emitted by product code**, not the rig: `broker.rs:5712-5724`, the
  timeout arm of the ready-wait poll in `dispatch_bring_up_within`. The bound is
  `BRINGUP_READY_WAIT = 30s` (`broker.rs:4668`); `BRINGUP_READY_POLL = 50ms` (`:4670`).
  `erhost` is the rig's adapter fixture name (`engine_room_bringup_e2e.rs:231` et al) interpolated
  through `room.adapter`. **The earlier guess that this was the rig's assertion text is WRONG.**
- The loop is reached **only after the gate admitted** — the sentence says "the code was accepted".
- #197's entire behavior change fires only on `Presented::Absent`. For a presented code
  `classify_attempt` is **bit-identical**: throttle first (order preserved), then `verified` →
  `Admitted`/`Refused`, exactly the old bool. Read the post-fix body, not the message.
- 5eb6a3b's broker hunks are `@1712/@1751/@1784` + two test hunks. **The spawn thread, the poll
  loop, and `BRINGUP_READY_WAIT` are untouched.**

⇒ The population of attempts reaching that loop is identical pre- and post-#197. 1/20-pre and
1/20-post are the same independent flake. An x20 at a pre-#197 sha would measure a population
#197 provably does not change.

### B. Open question 3 (are the two faces one mechanism a step earlier?) — answered NO, structurally

- **seat-contention face** = rc CLIENT-side: `current_driver()` reads `driven_by` off the perch
  ON DISK, prints, and `return Ok(())` **before any broker traffic**.
- **erhost face** = entirely INSIDE the broker, after admission, polling the broker's own session
  table via `session_for_endpoint`.
- Different data, different process, and **the sign is opposite**: a held/stale session would make
  `session_for_endpoint` return `Some` IMMEDIATELY, not time out. This face is "no session ever
  appeared".

### C. THE RIG WAS RCA-BLIND — this is why 40 runs hold zero daemon-side evidence

Measured: `ENGINE_ROOM_SPAWN_FAIL` appears in **0 of 40** logs (rca-v2-runs + rca-v2-fix-runs).
**That absence is NOT evidence** — `ENGINE_ROOM_BROUGHT_UP` is absent too, and that one is on the
SUCCESS path the passing control arm must have taken. A channel omitting the success line is not
carrying the failure line either.

Mechanism:
- rig `spawn_broker` (`engine_room_bringup_e2e.rs:148-158`) gives `spt daemon run`
  `.stderr(Stdio::from(File))` → `<home>/brain.stderr.log`.
- `spt daemon run` calls `stderrlog::install("broker", 0)` at **`cli.rs:7633`**, within its first
  statements. `redirect_stderr_to` (`stderrlog.rs:101`) does a Windows
  `SetStdHandle(STD_ERROR_HANDLE, …)` and `mem::forget`s the handle — **the process std-error slot
  is repointed and the rig's inherited file is abandoned from that line on.**
- the brain child does the same at `brainproc.rs:200`.
- everything after lands in `SPT_HOME/logs/daemon.stderr.log`
  (`stderrlog.rs:34` basename / `:42` path), inside the rig's temp SPT_HOME, destroyed at teardown.
- `spt_home()` (`perch.rs:34`) uses `SPT_HOME` **verbatim**, and the rig sets it to `home` — so the
  sink is exactly `<home>/logs/daemon.stderr.log`.

Not a new hazard: `REQ-DAEMON-STDERR-PERSIST` exists for it, and `cli.rs:7626-7631` +
`brainproc.rs:195-199` both name "the incident-night RCA-blind gap". **The product fixed the
blindness; the rig still read the pre-redirect pipe, so the fix never reached the harness.** The
panel was also mislabelled "brain stderr" while holding the broker's first few lines.

## BUILT 2026-08-19 (rig-only, diagnostics-only, no product change)

`daemon_diagnostics(home, brain_log)` in `engine_room_bringup_e2e.rs` (just above `reap`), tagged
`[int->REQ-DAEMON-STDERR-PERSIST]`. Concatenates the pre-redirect window and the sink, labelling
both with their paths. All 5 raw `read_to_string(&brain_log)` sites swapped to it (0 remain); 6
panel labels `=== brain stderr ===` → `=== daemon diagnostics ===` (+ the never-seated variant).
Sink path built from the rig's own `home` + exported `stderrlog::STDERR_LOG_BASENAME` (the `"logs"`
segment is the one re-spelled literal — no const is exported for it).

## DOYLE'S RULINGS 2026-08-19 — all four items closed

1. **Reap accounted.** 71.32 gross mine; net-exceeded-gross accepted with the moving-baseline
   caveat. Discipline record complete.
2. **Poolguard → IR-49 (infra register, NOT the board)** — poolguard is gater-triaged build infra
   per the 2026-08-02 mandate, so filing board-side would have been wrong. He re-verified at
   source: `lib.rs:424-436` final arm is pure `git_ancestry(tip, head)`, `:474-479` the tri-state
   probe; the branch-vanished (`:391`) and re-pointed (`:399`) arms still fire, so **the hole is
   exactly landed-member-branch-still-exists**. Patch-id fallback ENDORSED with a residual to state
   in the fix: **a conflict-adjusted pick changes patch-id, so such a lane still reads InFlight** —
   its release path is branch deletion (the vanished arm), reachable but manual. Rides "next
   poolguard touch", NOT mine.
   ⚠ The register commit `4799031` is **LOCAL-ONLY** — push held for the tag window. Do not look
   for it on origin. (My pool claim recorded that same base, so the lane identity is consistent.)
3. **#199 arm-kill ACCEPTED**, both load-bearing halves independently verified by him. The 20-run
   pre-#197 arm is DEAD — do not spend it. The q3 closure (faces structurally separate, opposite
   sign) is adopted into the record.
4. **Rig instrument GO, with a scope fence**, filed class-wide as **IR-50**. Ownership: mine, inside
   the GO'd #199 lane (dispatch-split rule covers test rework as its own work class). Fence: the
   panel sites + mislabels in `engine_room_bringup_e2e.rs` only, sink path read FROM
   `spt_daemon::stderrlog`, **no product change, no touching the 30s bound**. The sibling-rig census
   (every other e2e spawning the daemon with a stderr capture) is **HERTZ-class, queued in IR-50 —
   do not expand into it.** Rig-blindness gets no board issue; the four-panel fix rides #199 as lane
   content. My BROUGHT_UP-absence control is cited in the entry as the right absence discipline.

**Scope discrepancy reported to him (SENT), awaiting only if he wants a trim:** his fence says
"four panel sites + two mislabels"; the file's real topology is **5 producer sites** (167 — the
`spawn_broker` PRECONDITION panic, 362, 662, 974, 1406; 662/974 are struct FIELDS feeding multiple
asserts) and **7 print sites** (359, 1049, 1064, 1160, 1236, 1256, 1403). I did all of them,
because a panel still labelled "brain stderr" while holding both streams would re-create the exact
IR-50 defect, and leaving 167 blind loses the brain-never-came-up case where the sink matters most.
Inside the fence's intent; over its literal count. He can trim.

## x40 RED 4 — THE INSTRUMENT PAID OFF (2026-08-19, main @4799031, code == 9ea595c)

Run 4 of 40, erhost-not-up face, FAIL 62.983s. Panel carried **97.4 KB** where all 40 of doyle's
runs carried **zero**.

- **Control satisfied:** `ENGINE_ROOM_BROUGHT_UP:engine-room session=1 adapter=erhost` IS present.
  The sink carries broker output on this run, so absence in it is now a fact. Stop-and-fix arm did
  not fire.
- **`ENGINE_ROOM_SPAWN_FAIL` = 0** ⇒ `launch_harness_brokered_in` returned Ok. **The launch did not
  error.** Kills the spawn-defect arm; selects the other.
- **Stronger than "late":** full event census over the 58.3s sink — 327 write-start, 324
  transport-close, **2 controller-attach (mono 2201, 2767 — both the CONTROL arm)**, 2 writer-exit,
  1 session-detach (session=1 @2202), 1 stream-sub-attach, 1 presence-sub-attach. Only session ids
  1 and 2 exist. **Across the offline arm's entire 30s window (mono ≈28 000 → 58 307): ZERO session
  events.** Never registered at all, with no error on the launch meant to produce it.
- **THE RIG IS NOT ISOLATED (new, separate defect):** `BRAIN_NET_CONSUMERS_UP … dispatcher + peer
  pump started`; 3× `NET_FAMILY_GATE: binding IPv4-only`; 3× `PAIR_MEET_UP:erhome … addrs:
  {Ip(100.68.35.65:…), Ip(192.168.1.81:…)}` — this box's real Tailscale + LAN addresses; plus an
  `INBOUND_REACHABILITY` warning whose firewall rule admits
  `C:\actions-runner\_work\spt-bs-core\…\spt.exe`. A hermetic e2e is binding real interfaces.
- **Sink is SHARED + append:** broker (pid 50828, generation 0) and brain (pid 45756) both append to
  the same `daemon.stderr.log`; the header stamp and `BRAIN_UP` interleave. Read it accordingly.

**HYPOTHESIS, NOT ESTABLISHED — do not promote:** [[subnet-peer-pump-stall-rca]] — a peer-pump dial
does NOT fast-fail; an offline-but-resolvable peer blocks past the 30s bound. Shape matches (a 30s
bound blown with NO error anywhere, rate-shaped not deterministic, on a shared box). Causation NOT
measured, pump NOT timed against the bring-up.

**REMAINING BLIND SPOT (IR-50 class, one layer down):** the HARNESS's own output is nowhere —
`mock-session` writes nothing to the sink, so "started and failed to connect" is still
indistinguishable from "never ran". The launch says Ok, the broker says nothing arrived, and the one
witness between them is unread. NOT building it before doyle rules whether it is my lane or the
hertz-class census.

Asked doyle: (a) harness-stderr capture — my lane or census lane? (b) rig non-isolation — own
register id, or #199 lane content? (I want it to have its own id, not ride mine.)

## STATE / NEXT
- Next after compile-check: re-run the offline cell enough times to catch the face WITH the panel
  live, and read whether `ENGINE_ROOM_SPAWN_FAIL` fired. That discriminates "launch errored
  instantly" from "registration merely late" — the client cannot, since it burns the full 30s
  either way.
- CARRIED, unowned: `cli.rs:4183` (`GoStep::ConfirmThenTake`) does the same raw
  `read_info(&perch).and_then(|i| i.driven_by)` with no liveness filter — truthfulness, not lockout.
  `picker/model.rs:1259` guards correctly (precedent, not defect).
- doyle's rca-v2 logs are on THIS box and readable:
  `<temp>/claude/C--Users-decid-Documents-projects-spt-core/386ea03c-4e6a-4566-acac-76199ebdc0dd/scratchpad/{rca-v2-runs,rca-v2-fix-runs}`.
