# SEMAPHORE (#242) — pre-plan grill, doc-grounded (doyle, 2026-08-29)

Milestone: releases#242, six members attached (#237 #238 #239 #241 #235 #229), state backlog.
Method: every member interrogated against the record (issue body + comments, CONTEXT/frames docs,
REQ registry, KNOWN-HAZARDS, broker/lifecycle source at main `4b63de27`). Gray areas either RULED
here with the grounding cited, or NAMED as an operator fork. NOW-SIGNAL (#23, GREENLIT, 9 open
members) was deliberately NOT poached — the 422 one-parent refusal was correct; it stands as the
natural next milestone after SEMAPHORE.

## Members with NO gray areas

- **#241 emission single-write** — BUILT, all waves gated PASS, parked at `d0fdd58d`
  (feat/emit-single-write). Assembly-only work remains: compose off current main with the
  pick-fidelity audit (the new `xtask pick-audit` from IR-57 — its first real assembly is this
  one), never-executed-cells list in the hand-off. Never-executed at golden depth: the converted
  emitters' e2e paths ran in the lane batteries but the assembled composition is first-run.
- **#237 schema internal-codes sweep** — already RULED at the W1 gate (my PR#171 comment
  5458378678): ALL internal codes out of schema descriptions (ADR refs included — dead pointers
  to adapter authors), gate widens to manifest.schema.json in the same change. Shape known
  (manifest.rs doc comments → regen). Coverage = manifest_schema_shape drift test + widened gate.

## Members with gray areas — RULED here

- **#235 wake-gate CLAIM fallback.** The deciding arm (`wake_gate_decision`, broker.rs:272) is
  deliberate and documented: past-deadline claim ⇒ `Claim`, "availability over a vanishingly
  rare dup." The premise ("vanishingly rare") is MEASURED FALSE under battery load (winner's
  ConPTY create > 2s ⇒ loser double-creates; REQ-SPAWN-FRESH-TRUTHFUL falsified once at a W2
  battery). **RULING: the decision gains a fourth observable — CLAIM-HOLDER LIVENESS — instead of
  a longer clock.** Extending the deadline moves the window (generous-bound craft: the wait bound
  is a liveness PROXY); probing the holder closes it. Past-deadline + holder ALIVE ⇒ stay
  `Racing` (re-wait); past-deadline + holder DEAD ⇒ `Claim` (the leaked-claim arm survives
  unchanged). [AMENDED TWICE doyle 2026-08-29, second by replacement of the first. The fourth
  observable is HOLDER PROGRESS, not holder liveness: todlando's structural premise check
  (verified by me in source) showed the claim holder is a THREAD OF THE BROKER ITSELF —
  `wake_inflight` is an in-memory `Mutex<HashSet<String>>` (broker.rs:5079), the holder proceeds
  inline through the spawn_phase breadcrumbs (7731→8219), and `WakeClaimGuard` releases on every
  exit including panic (238-256). There is no holder process to probe: an existence/liveness
  probe is a tautology (first amendment's PinnedProc mechanism, and the original
  "image-path-corroborated" parenthetical before it, both named oracles for a process that does
  not exist; PinnedProc/image-path remain right for EXTERNAL holders — releases#120 scope).
  What the deadline actually protected is a holder wedged forever inside ConPTY create. RULING AS
  BUILT: the claim becomes a map entry carrying last_phase + last_phase_at (fed by the existing
  spawn_phase sites, read under the same lock); claimed+progressing ⇒ Racing with NO global
  deadline (a slow spawn is never taken over); claimed+stalled ≥ a generous PER-PHASE wedge
  ceiling ⇒ Claim, loud, with phase + elapsed on the log line; claims are generation-keyed so a
  taken-over wedged holder's late RAII drop cannot release the new claimant's claim (that leak
  re-opens the double-create one wake later); stale-guard-drop-no-op is its own unit cell.]
  The pure-fn truth table stays a unit (progress column replaces the liveness column). Rig: two
  cells — stretched-winner (loser stays Racing the whole stretch, one Created + one typed
  outcome) and deterministically-stalled holder (availability arm keeps coverage; injection via
  test hook, never a sleep race). Deterministic rig per the issue's own shape: stretch the winner past 2s,
  two concurrent spawns, assert one Created + one typed outcome — plus the dead-holder arm as a
  second cell so the availability path keeps its coverage.
- **#229 role-absence asymmetry.** Source: echo_commune absence ⇒ ONE `ECHO_ROLE_ABSENT` warn +
  skip (lifecycle.rs:491-500 at 9ec181e9 — cite corrected by replacement 2026-08-29, was
  488-496 which lands in the tail of the adjacent `ECHO_COMMUNE_SKIP` relative-cwd helper,
  a different mechanism with a similar name; measured todlando, W4 premise check.
  REQ-PSYCHE-ROLE-OPTIONAL-SKIP — whose own history says the pre-fix
  hard-fail-every-fire shape was a defect); psyche_resume absence ⇒ every ephemeral psyche turn
  errs and feeds the failure budget — per-fire STRIKES for a STRUCTURAL condition. **RULING:
  align to skip-with-loud-status.** Absence of `[session.psyche_resume]` is detected ONCE,
  structurally (at bind/first-fire): one loud `PSYCHE_ROLE_ABSENT` line + a surfaced state field
  (the endpoint-list psyche field family), psyche turns SKIPPED, ZERO strikes — the failure
  budget is for a declared role that FAILS, not for one that was never declared. Grounding: the
  silent-degraded-resume hazard is real (commune-ingest class), so absence must be LOUD and
  status-visible — but per-fire strikes are noise wearing signal's clothes: they exhaust a
  budget built for a different mechanism and surface as psyche_host_error churn (#136-adjacent).
  The REQ gains the sentence naming psyche_resume load-bearing-when-declared; missing-role test
  for BOTH polarities per the issue's ask.
- **#238 mid-turn dedup respec (the non-operator half).** The double-parse hazard: mid-turn
  payloads AND the closing AGENT_OUTPUT both traverse ingest, and REQ-IO-SHORTFORM-GATE's
  no-release-parses-twice clause is load-bearing. **RULING: non-overlap stays the ADAPTER'S
  reporting contract** — the integration-checklist row is respec'd from "ONE payload-carrying
  call per turn" to "every reported payload span is reported EXACTLY ONCE across the turn"
  (mid-turn chunks + closing remainder are disjoint), and core's parse remains per-ingest-call
  with no cross-call dedup machinery. Grounding: core-side span dedup would need core to model
  turn assembly, which the funnel design deliberately refuses (adapter knows its harness's turn
  shape; core owns delivery). The doc sweep (frames.md mid-turn paragraph, patterns.md dedup
  note, integration checklist) rides the lane per the issue's item 3 — and folds in the
  parse-vs-16KB-frame-cap ordering sentence (parse runs over the FULL ingested payload; the cap
  bounds only the emitted frame body) queued from perri's filing.
- **#238 rate re-derivation + W3 comment correction** ride the lane as specified (item 4);
  ADR-0048 decisions 1-3 stay perri's locked contract (item 5). Not gray — restated so the
  builder brief carries them.

## Operator forks — RULED (operator, greenlight session 2026-08-29)

1. **#238: ATTRIBUTE on AGENT_OUTPUT** (against the gater lean, ruled on semantics: "it'd be
   confusing if AGENT_OUTPUT excluded *some* agent output"). SCOPE EXTENSION in the same ruling:
   **core-owned shortform messaging reacts to ALL agent output including mid-turn, and the same
   applies to the `;;` seal-mint markers.** Recorded as releases#238 comment 5461768445.
   Lane consequences: the #22 taxonomy row's end-of-turn sentence amends (doc sweep item 3);
   both shortform families parse mid-turn ingested payloads (exactly-once span reporting carries
   dedup, per the ruling above); NEW DESIGN GATE — bare trailing `;;` "seals through end of
   output" under chunked ingest points at text the parser has not seen; design proposal required
   before build (gater lean: defer odd-marker resolution to turn close, per-turn parser state);
   verify the shipped #234 reader's tolerance of an unknown attribute (attr-passthrough
   precedent) at design.
2. **#239: NEW `boundary` frame category** as recommended — event-class, three variants
   (boot/clear/compact) minted whole. Recorded as releases#239 comment 5461768503; the
   needs-operator flag CLEARED on that recorded discussion (board verified: flags none).
3. **Composition GREENLIT as composed.** #242 BACKLOG → GREENLIT executed through alchemy,
   cascade verified to all six members. NOW-SIGNAL #23 intact as the standing next milestone.

## Wave plan (dispatched 2026-08-29)

- **W1 (todlando): #235** — holder-liveness observable per the ruling above.
- **W2 (todlando): #238** — design proposal first (attribute shape, mid-turn parse, bare-seal
  deferral, reader attr tolerance), gater signs the proposal, then build. Blocks perri; largest.
- **W3 (todlando): #239** — boundary category emitter + docs.
- **W4 (todlando): #229** — skip-with-loud-status alignment per the ruling above.
- **hertz: #237** — comment sweep + gate widening + drift-test coverage; plus per-wave consumer
  batteries and any test defects surfaced.
- **doyle: #241** — assembly at milestone close (first real `xtask pick-audit` use where the
  chain is pick-shaped; blob-parity leg where merged), per-wave gates, hand-off to deployah with
  the never-executed-cells list per the amended runbook.

## W2 #238 design gate — RESOLVED (doyle, 2026-08-29, after wave-plan dispatch)

The design gate named in operator fork 1 is CLOSED: todlando's `W2-238-MIDTURN-DESIGN.md`
gated PASS, verdict + full cite re-verification in `W2-238-GATE-VERDICT.md`. Bare-`;;`
resolved as option (C) — pairs mint mid-turn, an odd trailing marker in a `mid` span is
refused by name (`SEAL_BARE_MIDTURN`), ruling 14 verbatim at turn close; the gater lean
(defer to turn close) was WITHDRAWN on the merits (bare region does not exist at
write-time ⇒ deferral buys only buffer + leak-on-crash + a boundary exception). The #234
reader check came back IGNORES-not-passthrough (measured, two sites; wire precedent does
not transfer), design carries the attribute as a named field at five sites; mid rows are
additive, NOT version-gated, with the forward-compat unit BINDING. `IO_LOG_MAX_ROWS`
stays 1000 with a restated basis. Build greenlit.

## W3 #239 design gate — RESOLVED (doyle, 2026-08-29)

todlando's `W3-239-BOUNDARY-DESIGN.md` gated with modification; verdict + verification in
`W3-239-GATE-VERDICT.md`. Wire = ruled new `boundary` category, three variants off
`SessionTrigger::token()`, empty body; emitters at startup.rs boot + reporting.rs
cmd_boundary with the coerced-token rider. CARRIER = **C1** (share io log as-is): his C2
trim-ordering was refused at gate because skip-boundaries eviction breaks the trim's
contiguity arithmetic (iolog.rs:288-289 — front-only trimming is what makes
`last-first+1` exact; holes ⇒ overcount ⇒ rewrite-per-append), and the silently-seeding
default consumer never reads the rows it protects (`--after` is the only historical
reader). B1 (kinds inside IO vocabulary) was withdrawn by the author pre-gate as
re-litigating the operator ruling. Boot-observation contract added to the doc sweep as a
binding rider. Build greenlit on C1.

## Riders (infra, not board members)

- IR-57 pick-fidelity audit: LANDED PR#180 (gate pending, todlando's entry-figure correction
  310/308→302/300 queued for next register batch). First real use = #241's assembly.
- IR-59 disk-floor verb: design ACKED, building.
