# W2 — broker viewer/brain decouple: doyle's ruling (2026-07-07)

Ruling on `docs/W2-DESIGN-PROPOSAL.md` @f3c6b4c (REQ-HAZARD-BROKER-VIEWER-BRAIN-DECOUPLE,
LIFECYCLE-TRUTH W2). Premises independently verified against the tree before ruling:
`ViewerSink`/`ControllerSink` + `CONTROLLER_WRITE_DEADLINE` (broker.rs:112/263/148),
blocking `send_frame` legs on the shared `SharedSend` across dispatch (broker.rs:2262–3415),
`stamp_divergence`/`mark_controller_gone`/`has_controller` convergence machinery
(broker.rs:381/562/601). All hold.

**Verdict: APPROVED with the scope bound and riders below. Impl may start.**

## Invariant + mechanics (a), (b), (d): approved as written

- **(a) Bounded sink on the brain conn** — approved. Same discipline, same
  writer-thread + bounded-queue + deadline-evict shape the two existing sinks prove.
  The dispatch-side blocking `send_frame` legs are in scope exactly as far as they can
  park the dispatch thread behind the brain conn — that is the confirmed control-plane
  wedge.
- **(b) Broker-side stamp release on controller detach** — approved. The broker is
  already the single writer of `driven_by`/`viewer_count`; release-on-detach is the
  convergence write it already knows how to make. Reattach-refuse consults live
  `has_controller` truth.
- **(d) Ready ≠ drained promotion gate** — approved. `brain.ready` alone never
  suffices again; drained = the OLD generation's subscriber conn CLOSED or evicted,
  never while blocked writes pend.
- **doc stage** — KNOWN-HAZARDS entry approved verbatim as proposed.

## Q1 — brain-conn evict deadline: brain-specific, 15s

New named constant (`BRAIN_WRITE_DEADLINE = 15s`, 3× the controller bound), same
`last_ok` semantics: the clock measures *full-with-zero-progress*, so a slow-but-
draining brain never evicts. Rationale: the brain fans in every session + psyche +
WAN traffic — burst-stall on the incident night's load is a normal drain profile, and
though evict is self-healing (rewind from `delivered_through`), needless evict churn
= replay bandwidth + reattach races we don't need. 5s protects a per-client conn;
the brain aggregates. Doc-comment the constant with exactly this asymmetry.

## Q2 — scope: minimal-plus. NOT the wholesale verb move this wave

- Stamp release moves broker-side (mechanic b) — that is the piece with a proven
  stuck-`controlled-by` failure behind a dead transit.
- `rc --take` + reattach-refuse resolve against broker-owned controller truth
  (mechanic c) — the broker already holds that table; this is a read-side move.
- Every OTHER verb leg that still transits the brain gets the bounded deadline +
  loud `brain stalled` error. No hang, but no migration either.
- The WHOLESALE take/release verb-set migration is deferred: it is seam churn across
  dispatch (the shared-seam blast radius broker.rs:2262–3415 makes real), and the
  suspend-brain int rig is the arbiter — if the rig passes with the minimal shape,
  the wholesale move is scope creep this wave. If the rig still shows a wedge, we
  return with evidence and I re-rule.

## Q3 — passive resubscribe, with an observability rider

The broker never nudges the brain. An active broker→brain ping is a NEW dependency
in exactly the direction this wave exists to remove; the brain owns its subscribe
lifecycle and self-heals by rewind (invariant leg 1). **Rider (binding):** the evict
must be observable — stamped in the broker log AND surfaced on `daemon status`
(e.g. `brain subscriber: detached (stall-evicted at <T>)`), so a brain that never
returns is a diagnosable fact, not a silent absence.

## Gate conditions (what I will check at wave gate)

1. **Lock scope proof** — every new sink/drain path does its blocking work OFF the
   session/registry locks, `mark_controller_gone`-style (W3-GATE-VERDICT precedent:
   a one-line comment at each site stating the guard is dropped before the block).
2. **Int rig** — as proposed: daemon-hosted real session, NtSuspendProcess +
   SIGSTOP twin, asserts WITHIN the stall window (viewer ticks, `--take` completes,
   stamp releases), then resume + cursor-replay byte identity. Cross-platform leg
   required before PASS (Linux run on kitsubito).
3. **Seam tests** — broker.rs is a shared seam: full workspace nextest, with
   explicit eyes on `driven_by_selfheal` and every controller/viewer sink test.
4. **Per-wave activation** — activate REQ-HAZARD-BROKER-VIEWER-BRAIN-DECOUPLE
   stages in the same commit that starts the work, not before, not the full
   end-state.

Co-authored by: doyle
