# Golden 34946493637 (S4 `35d6f7a3`) — twohost-a/b red — RCA (doyle, 2026-09-15 09:58Z)

## What failed
One PAIRED cell, the web HELPER leg: A `two_host_web_helper_role_a` panicked at `twohost_web.rs:1353` ("no helper record on w2-helper-audience within 900s"); B `two_host_web_role_b` panicked at `:1035` ("served only 4 of 5 requester cells"). Every other twohost cell on both sides passed (ladder, inbound probe, web deny/fetch/range). Floors PASS on both boxes.

## Mechanism — instrumented, then confirmed in source
- B (kitsubito, Linux) daemon: `WEB_SERVE_FOR: stream=5 origin=a8193928… outcome=failed reason=DOCS_PORT_DISCOVERY_FAILED: Connection refused (os error 111)`. A (hfenduleam): `HELPER_SERVE_FOR: path=/home/reavus/…/quoted-by-the-user.md outcome=failed reason=DOCS_PORT_DISCOVERY_FAILED: Connection refused (os error 111)` — B's WebErr line relayed over the stream. No helper record is written on a failed outcome, so A's 900 s wait expires; B's served counter stays at 4.
- Source (candidate S4): `crates/spt-daemon/src/webproxy.rs:522-525` — inside the DAEMON's `serve_for`, `crate::servehost::docs_port(&crate::endpoint::seed_socket_name())` opens a local-socket connection to the daemon's OWN seed socket to ask for the bound docs port, and maps EVERY error to `DOCS_PORT_DISCOVERY_FAILED: {error}`. There is no fallback arm here; the `may_fall_back` (NotFound | ConnectionRefused ⇒ configured port) predicate exists only in the CLI's `serveverb::node_and_port`. `os error 111` = the self-connect was refused (socket path present, no listener at that name in the test daemon). The daemon already knows its bound port in-process (`docshost::bound_docs_port()`, the static published by the listener guard; `servehost.rs:161` serves `DocsStatus` from exactly that) — the self-IPC round trip is the defect surface.
- Introduced by the candidate: `f06d4cb6 fix(serving): discover the broker-bound docs port` and `4c6ae0a4 fix(serving): distinguish absent broker from failed discovery` (2026-09-11, fold lane), plus `docshost.rs +197 / servehost.rs +240 / serveverb.rs +321 / webproxy.rs +40` vs origin/main. The last green twohost golden (34481993681 @ a9e786b2, 09-10) predates ALL four #304 lanes: served cell 5/5, no `DOCS_PORT` string anywhere in either log. This run is the FIRST cross-host exercise of the candidate's serve-for path; the single-host consumer legs cannot reach it.

## Classification
CANDIDATE PRODUCT RED (deterministic on the receiving daemon; not a flake, not resource, not #308/#309). A same-sha rerun cannot pass this leg. ADR-0050: red golden ⇒ fix lane ⇒ new head ⇒ new golden; tested sha == shipped sha.

## Consequence for the earlier referral (Windows #309-mechanism red)
Options A/B/C for the Windows cell are moot for S4: the milestone needs a successor S5 with the serve-for fix, and the S5 golden re-executes every leg (traceability and Linux test rigs already fixed; kitsubito 268.9 GB, hfenduleam 158 GB). The Windows cell's rate question remains open for S5's run; hertz's cohort rig stays parked unless the operator wants the rate before S5.

## Refinement (todlando source-read, 10:00Z) — REPLACES the "in-process bound port" fix shape above
- Production `serve_for` runs in the **brain child** (`brainproc.rs:422-440`); the docs listener and the atomic bound port live in the **broker** (`daemon.rs:310-316`, `:461`). So the brain's IPC ask is the design; an unconditional in-process `bound_docs_port()` would break production helpers. The defect is WHICH broker it asks: `webproxy.rs:522` asks the CANONICAL `seed_socket_name()`, not the broker the brain is actually connected to. On Linux the transport is abstract-namespace UDS (`transport.rs:36-54`): an unbound name is ECONNREFUSED with no stale file.
- The twohost rig's role B (`twohost_web.rs:725`) hosts its brain under an invented broker name and binds neither the canonical seed listener nor a docs listener; role A binds both (`:1112`, `:1117`). Under the candidate's contract (REQ-WEB-URL-BOUND-PORT: served URLs use the daemon's actually bound docs port, never a default) B can no longer serve-for. Two-part fix: **product** — serve_for asks the owning broker (todlando, `fix/304-serve-for-owning-broker`), with a single-host regression cell (non-canonical broker name + bound docs listener ⇒ serve-for succeeds; red on S4, green on the fix); **rig** — role B answers DocsStatus and binds a broker-backed docs listener (hertz, second commit).

## Correction 2 (todlando, 10:02Z) — REPLACES the two-part shape above
`DocsStatus` is handled by the SEED control plane (`seedmap.rs:182`), not the broker protocol; the broker and seed sockets speak different protocols, so "ask the owning broker" is not a valid product change either. The brain asking the canonical `seed_socket_name()` IS the design, and a production daemon always runs that plane. Therefore the defect is RIG-ONLY: role B of `twohost_web.rs` never starts the canonical seed control nor a docs listener, which the candidate's bound-port contract now requires. Discriminator being run on kitsubito (todlando, S4 product code unchanged): no-seed probe (expect red) vs full-fixture control with seed control + docs listener (expect green). If the control is green, S5 is a TEST-ONLY successor (hertz: role B starts seed control + binds a broker-backed docs listener), exactly like S3.

## Discriminator RESULT (todlando, kitsubito, unchanged S4 product code, 10:16Z) — RIG-ONLY PROVEN
- Bare B-shaped fixture (invented broker name, no seed control, no docs listener): native exit **101**, real `WEB_SERVE_FOR … failed reason=DOCS_PORT_DISCOVERY_FAILED: Connection refused (os error 111)` — the golden's exact signature reproduced single-host.
- Same fixture + canonical `seedmap::serve_seed_control` + broker-backed docs listener: native exit **0**, `Registered` at the actual bound port 33319.
- Compile 0, pool released, no survivors; receipts + stderr mirrored at `.spt/preserved/304-web-helper-rca/linux/` (24 hashes verified). No product change, no commit; the probe source was removed after.

## Ruling (doyle under the operator's delegation) and path
1. hertz rig-only fix on top of S4 (`test/304-twohost-b-seed-docs-listener`, one commit), proven by todlando's control; no product change. → S5 (test-only). Same gate as S3 (blob scope, trailer, traceable both pins, equivalence of shipped inputs ⇒ field carry-forward stands).
2. Re-gate S5 (traceable both pins, blob scope, trailer), re-pin the package, deployah runs the S5 golden. #308 stays a backlog fast-follow; #309 stays on record (now with instrumented proof from this run).
