=== target: run 34310511612 at sha f6110c2a12df0dd50b87dfb60a2ec4120b5cf98d === run_attempt=1 head_sha=f6110c2a12df0dd50b87dfb60a2ec4120b5cf98d status=completed conclusion=failure === C7: run terminal, EVERY job green, expect NINE === .spt/preserved/golden-272-r3-drive/accept-r3.sh: line 36: jq: command not found run status=completed conclusion=failure jobs= .spt/preserved/golden-272-r3-drive/accept-r3.sh: line 38: jq: command not found .spt/preserved/golden-272-r3-drive/accept-r3.sh: line 39: jq: command not found .spt/preserved/golden-272-r3-drive/accept-r3.sh: line 40: jq: command not found PASS run terminal FAIL non-terminal jobs FAIL job(s) not success .spt/preserved/golden-272-r3-drive/accept-r3.sh: line 43: jq: command not found .spt/preserved/golden-272-r3-drive/accept-r3.sh: line 44: [: : integer expression expected FAIL job count < 9 — an early job list is NOT the run's job set === C2: the WINDOWS 'Docs drift gate' STEP conclusion (skip != pass) === .spt/preserved/golden-272-r3-drive/accept-r3.sh: line 51: jq: command not found .spt/preserved/golden-272-r3-drive/accept-r3.sh: line 52: jq: command not found FAIL no Windows 'Docs drift gate' STEP found at all — absence is not a pass (linux docs drift step, for contrast only — NOT evidence for the Windows criterion:) .spt/preserved/golden-272-r3-drive/accept-r3.sh: line 58: jq: command not found === fetching run log (gh refuses this mid-run; run is terminal now) === log bytes: 12076773 (stderr: 0 bytes) === C1/C3: FLOOR_DOCS and FLOOR_END === FLOOR_DOCS occurrences: 2 2 FLOOR_DOCS PASS FLOOR_DOCS PASS FLOOR_END occurrences: 16 16 FLOOR_END PASS FLOOR_END PASS === C4: Summary lines == 2 === Summary lines: 4 76106:test (self-hosted, Windows, hfenduleam) Test — Phase A (light pool; bounded parallelism) — windows 2026-09-09T04:46:56.1477365Z Summary [ 641.931s] 3346 tests run: 3346 passed (4 slow, 4 leaky), 1 skipped 76447:test (self-hosted, Windows, hfenduleam) Test — Phase B (heavy class, serialized, on a now-quiet box) — windows 2026-09-09T05:07:48.3556113Z Summary [1164.007s] 234 tests run: 233 passed (1 slow), 1 failed, 0 skipped 80832:test (self-hosted, Linux, kitsubito) Test — Phase A (light pool; full parallel) — linux 2026-09-09T04:25:21.6357186Z Summary [ 41.701s] 3325 tests run: 3325 passed (2 leaky), 1 skipped 81072:test (self-hosted, Linux, kitsubito) Test — Phase B (heavy class, serialized, on a now-quiet box) — linux 2026-09-09T04:44:35.3653049Z Summary [1152.093s] 219 tests run: 219 passed (1 slow), 0 skipped FAIL Summary lines == 4, expected exactly 2 === C5/C6: twohost roles — B's verdict from B's OWN SERVED count === 76421:test (self-hosted, Windows, hfenduleam) Test — Phase B (heavy class, serialized, on a now-quiet box) — windows 2026-09-09T05:07:24.9517742Z PASS [ 0.014s] (210/234) spt-daemon::twohost_web two_host_web_helper_role_a 76423:test (self-hosted, Windows, hfenduleam) Test — Phase B (heavy class, serialized, on a now-quiet box) — windows 2026-09-09T05:07:24.9766056Z PASS [ 0.012s] (212/234) spt-daemon::twohost_web two_host_web_role_b 81055:test (self-hosted, Linux, kitsubito) Test — Phase B (heavy class, serialized, on a now-quiet box) — linux 2026-09-09T04:44:34.2332146Z PASS [ 0.006s] (204/219) spt-daemon::twohost_web two_host_web_helper_role_a 81057:test (self-hosted, Linux, kitsubito) Test — Phase B (heavy class, serialized, on a now-quiet box) — linux 2026-09-09T04:44:34.2447910Z PASS [ 0.006s] (206/219) spt-daemon::twohost_web two_host_web_role_b 85338:twohost-b Two-host web serving — role B (owner) 2026-09-09T05:21:54.1038168Z test two_host_web_helper_role_a ... ok 85398:twohost-b Two-host web serving — role B (owner) 2026-09-09T05:22:16.9982022Z test two_host_web_role_b ... ok 88519:twohost-a Two-host web serving — role A (requester) 2026-09-09T05:22:00.8151195Z test two_host_web_role_b ... ok 88557:twohost-a Two-host web serving — role A (requester) 2026-09-09T05:22:06.9825269Z test two_host_web_helper_role_a ... ok .spt/preserved/golden-272-r3-drive/accept-r3.sh: line 91: jq: command not found --- role_b SERVED evidence (B's own count, never A's poll) --- 4965:traceability UNKNOWN STEP 2026-09-09T04:19:34.3298910Z "title": "The equal-generation lease rung is idempotent for the same connection. (ADR-0047 decision 3, AMENDING ADR-0044's ladder inside the equal-gen rung; hertz v0.39.4 field bug 4, PINNED via OBS breadcrumbs on authorized same-seam `daemon refresh` 2026-07-22 — gen+1 premise FALSIFIED.) TODAY: a Control/Take subscribe with same identity + same nonzero gen classifies 'same lease, silent re-take' (broker.rs equal-gen branch — correct, no revoke) but re-take = become_controller, which unconditionally takes+drops the prior seat (writer exits channel-closed) with NO same-conn check — designed for the dead-seat dispatcher-restart successor, it also fires against the SAME LIVE conn re-served 15ms apart by post-cycle dispatcher replay: the lease kills its own writer, the rc viewer freezes until detach+re-attach (the field 'update freezes PTYs'). FIX: keyed (endpoint/session, by, conn, gen) — same-conn equal-gen = IDEMPOTENT REPLAY: seat + writer PRESERVED, no controller-replaced, no second initial batch; breadcrumb answers decision=idempotent (additive vocab). Equal-gen DIFFERENT-conn keeps today's silent swap (the ADR-0038 fix-6 successor — must not regress); strictly-newer keeps loud supersession, strictly-older keeps busy. BANKED open question (build-time look, not a blocker): why the dispatcher double-served one held attach 15ms apart — may have siblings. Gate: impl — the same-conn discriminator in resolve_subscribe's equal-gen branch + the idempotent decision label; unit — the four ladder cells (same-conn idempotent / different-conn swap / newer supersedes / older busy) against a mock seat; int — the bug-4 rig: replay one held Attach after a brain refresh, assert ONE writer + zero controller-replaced + the original rc interactive (RED-first), plus the different-conn successor leg stays green (redispatch/brain_swap suites).", 8425:traceability UNKNOWN STEP 2026-09-09T04:19:34.5092392Z "title": "B-2 (REMOTE-TRUTH triage §B-2, REDUCED @bdc1242): a stale ONLINE+CONTROLLED stamp on a live-session perch self-heals — the info.json driven_by/controlled RECORD is made to match the broker's SINK-TABLE TRUTH. ROOT (persisted-stale-stamp class, doyle Q1): the livehost control reap gates on !has_session (reconcile_hosted_liveness), and a brain-only update KEEPS the session (REQ-UPD-3), so a controller stamp that went stale WHILE the session lived was never re-derived from broker truth. NOT a transport bug: (a) a persisted conn is the REQ-UPD-3 feature; (b) an idle-severed conn eventually EOFs via QUIC keepalive → handle_conn detach (path 1) — and the reason paths 1-3 previously failed to clean up was the B-1 broker floor-lock POISON WEDGE (cleanup panicked under the poisoned lock), now fixed. REDUCTION (doyle, my ground-truth): the prescription was 80% pre-built — converge_perch_stamps (broker.rs, REQ-HAZARD-CONTROL-STAMP-CONVERGENCE) ALREADY converges info.json driven_by/controlled to the broker's controller_by/has_controller on EVERY KIND_SESSIONS poll, and the livehost reconcile already TRIGGERS that poll per tick (query_live_session_endpoints). So NO new IPC query, NO new livehost arm, NO 5th detach path — the ONLY gap is that a controller whose WRITER THREAD died (severed conn: the writer failed a socket write, or a detach dropped by the prior B-1 wedge) still reports controller_by=Some/has_controller=true, so converge keeps the stale stamp. FIX: a broker-side lazy-reap in the KIND_SESSIONS snapshot closure — OutputLog::reap_dead_controller() drops a controller whose _writer.is_finished() BEFORE controller_by/has_controller are read, so the reply + the off-lock converge both see the honest (cleared) state and the stamp clears. LOCK-SAFE: the reap drops the sink in-memory ONLY (no stamp_driven_by → no info.json I/O under the log lock, the KH 7.12/5.16 lock-across-effect discipline); the OFF-lock converge_perch_stamps writes the honest stamp. KH 7.15 held by construction: the reap only ever CLEARS, never latches driven_by; a LIVE (idle, parked-on-rx.recv) controller is is_finished()==false so it is NEVER false-reaped. RESIDUAL (doyle Q2 accepted): a TRULY IDLE severed controller (writer parked on recv, no output, conn not yet EOF'd) stays is_finished()==false and converges only on output-resume / conn-EOF — that harder active-probe case is the RESERVED REQ-HAZARD-DRIVEN-BY-IDLE-REMOTE-EVICT (SessionInfo.controller_by doc), deliberately NOT built here so the reserved seed keeps its scope. Red-first: a dead-writer sink → reap clears it (controller_by honest None → converge clears the stamp); a live-writer sink → UNTOUCHED (no-false-reap control).", 13305:traceability UNKNOWN STEP 2026-09-09T04:19:34.6686595Z "title": "An online agent-family endpoint with NO session surface reads as message-reachable, not as a harness-hosted live agent and not as a plain ONLINE. (hertz v0.39.0 field report 2026-07-21; doyle PRODUCT RULING — deliberately NEITHER of the two options offered.) OBSERVED: an adapterless `spt ready` perch (type=ready_agent, adapter=null, ready/alive true) displays plain ONLINE with '(unknown adapter)'. SOURCE: picker/model.rs display_status returns Online for every non-live_agent type (~680-681) BEFORE consulting controllable, and amber HarnessOnly is live-agent-only (~683-687). RULING: that type gate is CORRECT and STAYS. 'ONLINE - HARNESS ONLY' means one specific thing — a LIVE AGENT whose session surface is owned by a harness rather than a broker PTY. Broadening it to 'online non-controllable agent-family' would make one label mean two different things, which is how a status label starts lying. An adapterless ready receiver is a THIRD truth: message-reachable, no session surface at all, nothing to attach to ever. So: a DISTINCT display state (working name 'ONLINE - MESSAGE ONLY') keyed on the endpoint TYPE (ready_agent), never on absence-of-adapter, and never an invented adapter name. SECOND RULING (same surface, separate lie): '(unknown adapter)' is itself a small diagnostic untruth — adapter=null is ABSENT, deliberately so, not unknown; the copy must say absent. Gate: impl — the distinct display state + the absent-adapter copy; unit — the display table gains the ready_agent row and the existing live_agent/gateway rows are UNCHANGED (this must not perturb the HarnessOnly gate), plus a label assertion for the new state.", 15127:traceability UNKNOWN STEP 2026-09-09T04:19:34.7465547Z "title": "THE BROKER RECORDS A BRING-UP WHEN IT STARTS ONE, and the reserved-id bind gate reads that record as well as the sessions table. The gate's premise is that the reserved id passes only as THE COMPLETION OF A BRING-UP THE BROKER STARTED (REQ-ER-RESERVED-ID-SPAWN-REFUSAL), but the only fact it can consult is the sessions table, which records COMPLETED-ness — so between the broker creating the harness child and the session row being inserted there is a window in which the broker's OWN spawned harness asks the gate whether a bring-up it is itself completing exists, and is told no. Measured end to end on releases#199: the child's first act is `spt api bind engine-room`, the arm that answers is `ER_HOSTED_PROBE:no-row sessions=0` (dial OK, query OK, table EMPTY — named 3/3 in a channel proven to speak), the bind is refused RESERVED_ID, the harness exits bind-failed, no session ever registers, the ready-wait burns its full bound and the operator is told the engine room is not up. THE DEFECT IS A MISSING FACT, NOT A TOLERANT READER: the fix writes started-ness where it happens instead of teaching every reader of the sessions table to tolerate a counterfeit of its consequence, so the ledger lives OUTSIDE that table and the wake gate, the zombie verdict, every process-id reader and the rollback path are untouched by construction rather than by discipline. The record is endpoint-keyed with a deadline, written before the launch thread fires, swept on insert like the admit tickets, removed the moment the session row appears, and expiring harmlessly when a spawn fails so no phantom entry can outlive the bring-up that wrote it. Its TTL is the bring-up's own ready-wait, because the window it covers is exactly the window the bring-up is willing to wait through. It reaches the gate as one additive field on the brain's sessions reply — internal IPC, no P2P wire change. THE RESIDUAL IS STATED RATHER THAN ABSORBED: the added arm admits a hand-run bind of the reserved id during a live in-flight window, which satisfies the same not-as-a-first-mover premise (the broker did start a bring-up), is bounded by the TTL, and collides into the existing CONFLICT arm. Gate: impl — the endpoint-keyed in-flight ledger written pre-spawn on the bring-up path and cleared at registration, carried on the sessions reply, and the hosted predicate answering row-OR-in-flight; unit — a bring-up recorded before its session exists answers the predicate true, a registration clears the entry, an expired entry answers false, a sweep on insert drops what is no longer live, and an ordinary spawn writes no entry at all.", 15361:traceability UNKNOWN STEP 2026-09-09T04:19:34.7621837Z "title": "Passing the engine room's bring-up gate BRINGS THE ENGINE ROOM UP: the node spawns its session through the adapter bound at the ceremony, and the operator's attach then lands on it. Today it does not, and the endpoint is entirely inaccessible (releases#71, operator-reported): the ceremony provisions a record, the gate is complete and correct, and NOTHING ANYWHERE SPAWNS THE SESSION — `BringUp::Admitted` briefs and returns proceed, and the attach falls through to the ordinary no-live-session refusal. THE SPAWN LIVES DAEMON-SIDE (ruled): the broker owns hosting, so a client-side spawn would replicate hosting logic in the CLI and race the attach that follows it; the daemon already launches harnesses on its own behalf at autostart, which is the shipped precedent for the mechanism and for its threading. The attach that follows the spawn WAITS FOR READY UNDER A BOUND — a wait with no bound turns a broken adapter into a hung terminal, and no wait at all reintroduces the same no-live-session refusal one race later. THE ENTRY POINT IS PART OF THIS REQUIREMENT AND IS STATED HERE BECAUSE IT IS WHERE THE DEFECT ACTUALLY BITES: with no session hosted, `spt rc engine-room` never subscribes at all — it resolves no local session, resolves no owning node for an endpoint that is deliberately never advertised, and returns the reported 'No live session' before any code reaches the daemon, so the typed code is verified NOWHERE and the Admitted arm is unreachable in exactly the flow the operator ran. A fix that adds a spawn leg to that arm without giving a session-less bring-up a way to reach the gate closes nothing. VERIFICATION STAYS DAEMON-SIDE WHEREVER THE ENTRY LANDS: the adversary in this threat model is an agent on the box, which can speak the broker's IPC and never run a line of the CLI, so a client-side check is a courtesy and the rule must be evaluated where the session is hosted. THE ENTRY IS AN ADDITIVE BROKER REQUEST carrying the code (ruled): the daemon runs the same classifier it already owns, spawns on admit, and replies the session the operator then attaches to. Additive is the ADR-0001-safe shape, and an N-1 broker that does not know the verb answers unknown — WHICH THE CLIENT REPORTS HONESTLY IN THE OPERATOR'S OWN WORDS, naming the version skew, and NEVER falls through silently to the resolve path that produces the very no-live-session refusal this requirement exists to kill. The spawn RUNS OFF THE REQUEST HANDLER on its own thread, following the autostart replay's shipped shape, because the launch dials the broker as a client and must not run inline inside the handler serving that request. THE GATE IS SPENT ONCE PER BRING-UP, and the deciding argument is NOT budget arithmetic but the TOTP WINDOW RACE: the bounded spawn wait can outlive the ±1 ceremony window, so a code re-presented at the following subscribe can be REFUSED by the same gate that just admitted it — charging the human a ledger failure and rebuilding gate-admits-then-attach-refuses one seam later, which is the exact shape #71 exists to kill. The admitted bring-up therefore mints a SINGLE-USE ADMIT TICKET for the subscribe that seats the controller: a random secret of at least 128 bits, returned in the reply, SCOPED TO THE SESSION ID the bring-up spawned, and EXPIRING 30 SECONDS AFTER THE REPLY. A CONNECTION-KEYED exemption was ruled first and is STRUCTURALLY UNREDEEMABLE at this topology, which is why the key is a ticket and why that is stated here rather than left as an implementation choice: an rc attach opens a net stream, and the subscribe that seats the controller is sent by the DISPATCHER'S OWN broker connection, never by the connection that asked for the bring-up, so the two connection ids can never match and a conn-keyed exemption could never fire at all. The key must be the thing that actually crosses the seam, and the attach's code field is already the end-to-end pipe for exactly this class of secret, so the ticket rides that field behind a reserved `admit:` prefix and no new wire field appears. THE PREFIX IS RESERVED OUT OF THE TOTP PATH STRUCTURALLY: a prefixed string is never evaluated as a code guess, because an expired ticket is not a wrong guess and must not spend a unit of the human's attempt budget — which also stops anyone on the box from ratcheting the human toward a backoff by replaying a dead ticket. The ticket is consumed BY PRESENTATION rather than by success, so a second presentation is refused even inside its window and a ticket presented for another session is spent as it is refused; expired, already-spent, wrong-session and never-minted all answer ONE uniform sentence, so a caller learns only that this string does not seat it and never whether an admit is outstanding. The TTL starts at the REPLY — after the spawn, after the bounded wait — so it covers only the client's attach turnaround and never a harness start. Entropy, TTL and single use are the ticket's whole defense, because the field it rides spends no ledger. EXEMPTION PRECEDENCE, ADDED FOR releases#203 (doyle ruling 2026-08-20): the seated-connection re-serve exemption is evaluated BEFORE the presented ticket, and that ORDER is part of the requirement rather than an implementation detail — because the ticket this requirement mints is SINGLE-USE and the connection it seats re-presents it on every re-subscribe. With the ticket matched first, a gap resume or dispatcher reconstruction of a ticket-seated controller answered `Unredeemed` and the refusal UNSEATED the human mid-session; the seat this requirement exists to establish was then lost to the very credential that established it, and the exemption was structurally unreachable for any re-serve carrying its spent ticket. Measured on golden 32341702157 (Linux leg): seat on a redeemed ticket, the SAME conn re-serving ~700ms later with `req_gen` unchanged, `engine-room-admit-unredeemed`, `session-detach was_controller=true`. Latent since the ticket shipped and first witnessed by a new e2e, not by a report — the availability half of this requirement had no same-connection witness at all. THE ORDER IS NOT A WIDENING, and the reason belongs in the requirement because it is what makes the precedence safe to state: the exemption's discriminant is the seated CONNECTION id, which belongs to another process's socket and cannot be claimed by asking, so the only attach it admits on a spent ticket is one arriving on the connection ALREADY SEATED — granted nothing it does not already hold, and dying with that connection. A ticket presented by any other connection still meets the match and is refused on its own merits, ledger untouched. Gate: doc — the engine-room setup guide's bring-up section, stating that the first bring-up spawns the session and what a bounded wait failing looks like; impl — the daemon-side spawn on admit through the ceremony-bound adapter, the bounded wait-for-ready, and the session-less entry path that carries the code to the gate; unit — an admitted bring-up spawns exactly once through the BOUND adapter, a refused one spawns nothing, a bring-up over an already-live session spawns no second session, the wait's bound is proven to be a bound (an adapter that never reports ready ends in a typed failure rather than a hang), one bring-up spends exactly one ledger attempt, the admit ticket is proven to seat the controller ACROSS THE REAL TWO-CONNECTION TOPOLOGY (the row that would have caught a conn-keyed exemption before it shipped), a SECOND presentation of the same ticket is refused, an EXPIRED ticket is refused WITH THE ATTEMPT LEDGER UNTOUCHED, a ticket presented for a DIFFERENT session is refused, and every non-redeeming presentation answers the SAME sentence so the refusal discloses nothing, a SPENT ticket re-presented by the connection that is ALREADY SEATED keeps that seat (the same-connection gap-resume row, pinned by an explicit conn-id assertion and a FIXED attach generation so a fresh-connection re-serve cannot masquerade as it), and that same spent ticket presented by a DIFFERENT connection is still refused with the ledger untouched (the non-widening control, without which deleting the refusal arm outright would satisfy the row above); int — a provisioned engine room with a registered adapter comes up end to end from the invocation the operator runs and the controller is seated on the session that was spawned.", 16141:traceability UNKNOWN STEP 2026-09-09T04:19:34.7903251Z "title": "`endpoint purge` against the engine room RESETS it rather than deleting it, and requires OS elevation to do so (ADR-0052 decision 4). Reset-not-delete is what keeps 'one engine room per node' structural: a node from which the engine room can be deleted is a node that can be left with no governance surface at all, and the recovery path would then be a create verb — the very thing REQ-ER-RESERVED-ENDPOINT removes. Elevation is the right gate HERE (unlike bring-up, REQ-ER-BRINGUP-TOTP-GATE) because the ceremony sets node-scope facts — the home subnet and the bound harness adapter — and because it must work at bootstrap, when the node holds no subnet material to prove a TOTP against. The reset is the ONLY way to change either fact. Gate: doc — ADR-0052 decision 4; impl — the engine-room arm of the purge path, its elevation requirement, and the reset outcome distinct from the delete outcome; unit — the purge decision table (engine room plus elevation yields reset, engine room unelevated refuses, an ordinary endpoint is unaffected) and a reset record retaining its reserved identity.", 16280:traceability UNKNOWN STEP 2026-09-09T04:19:34.7944425Z "id": "REQ-ER-RESERVED-ENDPOINT", 16334:traceability UNKNOWN STEP 2026-09-09T04:19:34.7962538Z "id": "REQ-ER-RESERVED-ID-SPAWN-REFUSAL", 16335:traceability UNKNOWN STEP 2026-09-09T04:19:34.7972980Z "title": "THE ENGINE ROOM'S ID IS RESERVED AT EVERY VERB THAT MINTS AN IDENTITY, and this is a BYPASS rather than a naming nit (ruled): `spt endpoint run engine-room` mints the engine-room identity and hosts a session under it WITHOUT EVER PASSING THE TOTP GATE, because the gate guards the ATTACH and not the NAME. An agent on the box that can spawn the reserved id owns the surface that sets the node's access posture, which is precisely the authority the bring-up ceremony exists to price. Every spawn-family verb therefore refuses the reserved id outright and NAMES `spt rc engine-room` AS THE ONLY ENTRY, so the refusal teaches the correct path instead of inviting a workaround — a reasoning agent told only 'no' invents a route, which is the prescribe-then-mislead lesson turned around. THE REFUSAL GETS A CANARY UNIT PER SITE, and that is required evidence rather than diligence: a capability limit that lives only in prose or only in a prompt is not a limit, and the canary is what makes the prohibition tool-layer-enforced (the standing lesson from the strike-and-amend class). THE FAMILY IS WIDER THAN THE VERB THE REPORT NAMED, and enumerating it is part of the requirement because a refusal at one door with the rest left open reads as closed while remaining open: the direct `endpoint run` path; the interactive picker's create-new entry, which never passes through that path's arguments; the `--save` startup default, which PERSISTS a bypass that then replays with no gate at every daemon start, so it is refused at save AND skipped loudly at replay; and the identity-minting listener verb, which binds the reserved id to a perch with no harness and no gate at all — IN because the stated harm is minting the identity without the gate, and the engine room has exactly ONE entry, its bring-up. THE FAMILY CLOSES BY ENUMERATION RATHER THAN BY THE SITES ANYONE HAPPENED TO NAME: the whole id-minting and spawn-dispatch surface is swept before the refusals are written and the site COUNT is reported with the refusal list, so a site that exists but was not thought of fails the sweep rather than shipping open. A site that cannot refuse is named in the evidence rather than left silent. THE PERSISTED-DEFAULT ARM CARRIES ITS OWN ROW, because a record written BEFORE this fix already exists on real nodes: refusing at save alone leaves those replaying ungated at every daemon start, so the replay arm must skip a pre-existing reserved-id default loudly and be tested against one. Gate: doc — the reference help for the refusing verbs and the engine-room guide's entry sentence; impl — the reserved-id refusal at every enumerated site, each naming `spt rc engine-room`; unit — a canary per site asserting the reserved id is refused and NOTHING is minted, spawned, persisted or replayed, that the refusal names the bring-up entry, that a PRE-EXISTING persisted reserved-id default is skipped loudly at replay, and that an ordinary id is unaffected at the same site.", 17096:traceability UNKNOWN STEP 2026-09-09T04:19:34.8277121Z "title": "The per-job companion to REQ-FIXTURE-BIN-BUILD-EDGE, and it is a DRIFT GUARD, NOT A FINDER. `xtask perjob-map` walks every CI job step by step and asks, for each narrow cargo invocation, whether the cross-package fixture bins its tests consume were built EARLIER IN THAT JOB -- by an explicit `cargo build -p X --bin B` or by an earlier workspace-shaped build. On this tree the answer is 7 narrow invocations, 0 unguaranteed, which is what establishes that the 11 repo-wide reds are NOT CI defects. THAT ZERO IS A FACT ABOUT TODAY'S WORKFLOWS AND NOTHING MORE, and the requirement says so because the tool's value is entirely in the FUTURE case: the day somebody adds a narrow invocation to a job that does not build what it consumes. A requirement that framed this as proof the class is closed would license deleting it the moment the register entry is written. THE ONE MODELLING TRAP, already paid for once: a workspace-shaped run emits the plain binaries only if it actually builds integration-test targets -- `--workspace` filtered to `-E 'kind(lib) + kind(bin)'` compiles lib/bin harnesses and emits NO plain fixture exe on a clean pool, which is exactly what `ci.yml:102-106` documents and hand-prebuilds around. The first prototype credited it as a builder and returned a GREEN over the one known-real gap; the kind-filter test is therefore a required unit row, not an optional one. THE SCRIPT-BLIND HOLE IS PRINTED IN THE COMMAND'S OWN OUTPUT RATHER THAN FOOTNOTED IN A DOC. The model reads `run:` blocks in workflow YAML; narrow cargo invocations ALSO live inside `.github/ci/*.ps1` and `*.sh`, which those blocks merely invoke, and no `run:` parser can see inside them. Rather than leave that as a limitation somebody has to remember, the scripts are scanned with the SAME detector and every narrow invocation they contain is listed as UNMODELLED with the cross-package bins it consumes: today 2 instances (`g6-curve.ps1`, `g6-postbounce.ps1`, both `-p spt-daemon --test inject_control_wedge`), both consuming NONE, so the hole is real and its instance count is measured at zero instead of asserted. An UNMODELLED invocation that DOES consume a cross-package bin counts as a gap and reds the command, because a verdict nobody can compute must not read green. THAT SCAN NEEDED ITS OWN CORRECTION, AND THE DIRECTION MATTERS: scanning the scripts raw read `reap-census.ps1`'s comment-based-help block -- which DESCRIBES a `cargo test -p spt --test ...` failure in prose -- as a real invocation consuming three cross-package bins, i.e. a fabricated gap inside the section whose entire job is honesty about what cannot be seen. PowerShell `<# #>` blocks and whole-line `#` comments are stripped; a TRAILING `#` deliberately is not, because in sh that would eat `${var#foo}` and losing a real invocation is the worse direction here. THE YAML IS PARSED BY A DELIBERATELY SMALL SUBSET READER, AND ITS FAILURE MODE IS ACCOUNTED RATHER THAN TRUSTED: xtask carries no parser dependency, and a silent mis-parse would DROP steps, after which every job those steps belonged to would read guaranteed -- a green manufactured by not looking. So the reader counts the `run:` command keys present in the file and refuses (exit 2) unless it attributed every one of them to a job. A `run:` that opens a mapping rather than a command (`defaults.run.shell`) is not a command key on either side of that count. LIMITS THAT REMAIN OPEN AND ARE NAMED IN THE COMMAND'S OWN DOC: `uses:` steps are not followed, and guarantee is tracked per job in step order without modelling artifact reuse across jobs on a persistent self-hosted workdir -- and our golden boxes ARE persistent, so a WORKSPACE verdict is sound only within its job and a clean runner could still expose what reuse masks. Gate: impl -- `crates/xtask/src/perjob.rs` and its `perjob-map` subcommand: the consumed-bin index over integration-test targets and over the unit population (unit tests get no `CARGO_BIN_EXE_*` at all, which is why `-E 'kind(lib)+kind(bin)'` is a CONSUMER step), the step walker with its prebuild and workspace-build guarantees, the accounted YAML subset reader, the script scan with its comment stripping, and the reuse of REQ-FIXTURE-BIN-BUILD-EDGE's detector rather than a second one (mapping through a different detector would produce a per-job verdict over a different population); unit -- the parser refusing an unattributable `run:`, block and inline bodies both surviving while `uses:` and `#` lines do not, cargo invocations stopping at a shell separator and `nextest run` not being read as the shorter `test` verb, the kind-filtered workspace run classified as consumer rather than builder, flag values requiring the whole flag (`--bins` is not `--bin s`), script prose not being read as an invocation, and a job guaranteeing by prebuild, by workspace build, and failing when neither ran. THE GUARD WAS OBSERVED FIRING ON A REAL FALSE STATE, not only in tests: while this lane was in progress the checker's own test fixture named `CARGO_BIN_EXE_git_fixture` unescaped in a tracked `src/` file, which entered the unit-consumer population and made this map report a gap in `ci.yml`'s unit lane and exit 1. Kin REQ-FIXTURE-BIN-BUILD-EDGE (the repo-wide half, whose detector and helpers this reuses).", 19231:traceability UNKNOWN STEP 2026-09-09T04:19:34.9013473Z "id": "REQ-HAZARD-CONFLICT-BOTH-PRESERVED", 22375:traceability UNKNOWN STEP 2026-09-09T04:19:35.0211436Z "title": "A FLOOD of operator input on one brain↔broker connection deadlocks the broker PERMANENTLY (entire broker — no new/existing attach; the controller stays latched because the per-conn handler can't process the detach). ROOT (doyle /diagnose, code-grounded + HITL capture, the v0.13.0 P1 ctrl+V re-open): `serve_attach` processes a whole `NetStreamData` batch of N operator `Input` records in its inner `for rec in decoder.push()` loop, calling `brain.send_effect(op_id, &bytes)` N times WITHOUT returning to `read_event()` — so the brain writes N `KIND_INPUT` frames back-to-back and drains nothing. The broker's single-threaded per-conn handler answers EACH with `send_frame(applied_envelope)` on the SAME conn (B5 exactly-once ack, KNOWN-HAZARDS 7.2). With the brain not reading, the broker→brain return direction fills (~10 frames = the IPC pipe buffer) → `send_frame` BLOCKS → the handler stops reading → the brain's writes block too → mutual full-duplex DEADLOCK. Capture pinned it: 11 input frames, write_input 11/11 (P0 holds — the PTY write is fine), ack send START=11 / END=10 (frame #11's applied-ack never returns). Same class as the v0.12.1 L0 two-conn split. Windows Terminal's ctrl+V paste accelerator was the trigger (injects the clipboard as a char-by-char key flood) but the deadlock is generic to ANY input flood, NOT ctrl+V-specific and NOT a P0 (PTY-write) or W1 (output-drain) regression. The applied-ack is load-bearing ONLY for `shellchan` (one-at-a-time spool delivery WAITS on `BrokerEvent::Applied`); `serve_attach` DISCARDS it (the operator/rc path is fire-and-forward, op_id for dedup only, never gates on the ack). FIX (doyle-approved): CONDITIONAL ACK — `InputReq` gains `ack: bool` (serde default = true, N-1-safe: an older brain's input still acks = today's behavior). `serve_attach`'s operator path calls `send_effect_no_ack` (ack=false) → `dispatch_input` writes NO applied frame → the per-conn handler never writes back while servicing the flood → it always drains → no deadlock (cures ANY input flood). `shellchan` keeps `send_effect` (ack=true) and its `Applied`-wait. Exactly-once PRESERVED: the broker still dedups by (session, op_id) at the applied-set regardless of the ack. N-1 caveat: an OLD resident broker (self-update window) ignores `ack=false` → still acks → the deadlock persists until a broker restart (inherent KNOWN-HAZARDS 7.9 broker-resident-wire-change class). (v0.13.0)", 26779:traceability UNKNOWN STEP 2026-09-09T04:19:35.1689926Z "title": "A SLOW controller must not starve a concurrent `rc --view` VIEWER. W1 (REQ-HAZARD-INJECT-CONTROL-COEXIST) moved the controller SOCKET WRITE off the drain thread onto controller_writer, but left the bounded HANDOFF (ControllerJob::deliver) as an INLINE try_send SLEEP-POLL on the drain (broker.rs:1450-1457 → deliver:669-685, up to CONTROLLER_WRITE_DEADLINE=5s). So when a controller drains slower than the PTY floods, its CONTROLLER_CHANNEL_DEPTH(4096) channel fills, deliver() polls inline, and the DRAIN THREAD is throttled to the controller's read rate → OutputLog::append's viewer fan-out (try_send) stops running → a concurrent VIEWER receives only the initial replay then nothing (root 'b4', warm forkpty: a_journaled c1=0/EVICT=0/got_output=FALSE; steady-state-near-full = no recovery; forkpty-only, floods harder than Windows ConPTY). The viewer-not-starved-by-a-busy-session property is legitimate (rc --view of a noisy session must show LIVE output). FIX: the controller becomes a SINGLE NON-BLOCKING try_send (like a viewer), done IN append() under the log lock; deliver()'s sleep-poll DELETED; the drain NEVER sleeps. ControllerSink gains a stateful last_ok deadline → a TRULY-stalled controller (continuous-Full past CONTROLLER_WRITE_DEADLINE) is evicted (bounded-wedge preserved); a slow-but-alive controller DROPS frames + falls behind the ring (resume-from-floor, the existing reconnect case). B2 GAPLESS-HANDOFF PRESERVED via a CONTIGUOUS delivered_through: controller_writer advances the cursor ONLY when the written seq == cursor (next expected); a gap from a drop FREEZES the cursor at last-contiguous so a re-attaching brain's resume_seq never skips a dropped chunk (a high-watermark advance past the gap would be a not-exactly-once resume = B2 violation, doyle's gate). (v0.13.0)", 34989:traceability UNKNOWN STEP 2026-09-09T04:19:35.4578888Z "title": "`api listen` must not ASSERT hosting topology it does not know. (hertz v0.39.0 field RCA 2026-07-21, doyle re-grounded at source.) OBSERVED: the published adapter sequence `api bind` then identity-preserving `api listen --session-id` produces a self-contradictory live record — controlled=true AND controllable=false on a broker-hosted PTY endpoint — which controlled-precedence masks blue while attached and which a DETACH then unmasks as amber HARNESS ONLY. Detach is not the root; it only reveals the bad stamp. SOURCE: api/startup.rs passes controllable=Some(false) UNCONDITIONALLY on the relay/listen path (~191-195, reasoning 'the harness owns the process, so there is no broker PTY'), and establish_perch resolves controllable = controllable.or_else(|| prior…) (~379) — explicit wins, so that Some(false) OVERWRITES the Some(true) an earlier `api bind` EARNED. The carry-forward discipline that protects cwd/adapter/rest_state does not protect this field precisely BECAUSE the listen path is not silent about it. The defect is an ASSUMPTION about hosting authority made by a path that does not know the answer. FIX (preferred): represent LISTENER CUSTODY separately from PTY HOSTING AUTHORITY, so establishing a listener says nothing about who owns the session surface. Merely preserving controllable=true when a broker-hosted session with that session_id exists is weaker — it leaves listen guessing rather than removing the guess. Gate: impl — listen no longer asserts hosting topology for a session it does not host; unit — the stamp resolution table over (prior controllable, listen path, broker-hosted session present); int — bind -> listen -> control -> detach ends at alive=true, controlled=false, controllable=true, display ONLINE. TITLE AMENDMENT 2026-07-27 (doyle ruling, todlando build; rides the build PR per registry-mints-ride-build-PRs): THE REQ PRESERVES A **LIVING** HOSTING ARRANGEMENT ACROSS LISTENER RE-BINDS; IT DOES NOT RESURRECT A DEAD ONE'S CAPABILITY STAMP. FIELD CASE: emphasys rendered ONLINE for 25+ minutes with BOTH recorded pids dead, because its listener-only wake re-bind INHERITED a controllable=Some(true) earned in an earlier broker-PTY life, and the reconcile sweep exempts Some(true) rows from relay-death convergence (livehost.rs) — so an expired capability stamp ROUTED a liveness proof and the row was exempt from EVERY liveness model. The carry-forward is now scoped: Some(true) survives a listener re-bind unless the prior record's RELAY pid is provably Gone. Liveness of the arrangement is judged via the relay-role pid (REQ-PID-ROLE-EVIDENCE), the first record-internal key that actually measures it — NOT via 'earning pid alive', which was falsified pre-build: for a BrokerPty row the record holds no pid of the hosting life at all, only the announcing CLI's.", 36486:traceability UNKNOWN STEP 2026-09-09T04:19:35.5082271Z "title": "Mesh row fan-out: registry rows stay OWN-AUTHORED; the only change is the push target widens from directly-paired peers to ALL roster members (a wider DIRECT fan-out, never a third-party relay). Every row/message still arrives from its author over a handshake → KNOWN-HAZARDS 7.5 (origin = handshake node) and 4.10 (eviction lease: any future update comes from that node itself, alive) PRESERVED VERBATIM. Closes the staggered A→B→C repro: C (roster-seeded with A at pairing) initiates to A, seed-proof admits C unpaired, A learns C, both push directly.", 40329:traceability UNKNOWN STEP 2026-09-09T04:19:35.5802259Z "title": "THE FILE_ACCESS_HELPER CATEGORY HANDS AN AGENT THE EXACT `spt fetch` LINE FOR A FILE IT WAS GIVEN, AND NEVER MORE THAN ONCE PER (MESSAGE, PATH) (releases#17, ADR-0058 Amendment 1, operator directive 2026-09-06 widening the category from carries-attachments to user-quoted-a-path). TWO TRIGGERS, ONE OUTPUT SHAPE. (a) A DELIVERED MESSAGE CARRIES ATTACHMENTS: the signal emits one `spt fetch ` line per attachment, taken VERBATIM from the envelope rather than rebuilt, so the line an agent runs is the link the sender minted. (b) A USER'S MESSAGE QUOTES A FILEPATH THAT EXISTS ON THE USER'S NODE: core AUTO-REGISTERS that path as a REFERENCE-SERVED entry -- a file or dir entry, NEVER a snapshot, because the user said look at this and not keep this as it was, and a live reference costs no copy -- with ttl 24h, origin = the message short-ID, and audience = THE ONE ENDPOINT THAT RECEIVED THE MESSAGE (REQ-WEB-ENTRY-AUDIENCE), then hands that endpoint the fetch line. SAME-NODE USER AND AGENT REGISTER NOTHING: the signal says the path is local and readable, because serving a file to a process that can already open it buys an audit entry and no access. A REMOTE USER -- the case #17 was minted for -- has the file on THEIR node, so registration happens THERE on the agent's behalf: a cross-node serve-this-path-for-endpoint-X request authorized by the user's attach session, riding the stream family of REQ-WEB-CROSS-NODE-PROXY, which is why this rider lands after W1. GUARDS, EACH ITS OWN CELL: the path must EXIST on the owning node at signal time (a quoted path that is not there emits NOTHING rather than a dead link); ABSOLUTE OR ~-ROOTED PATHS ONLY, because a relative path has no anchor and would silently name a different file on the other node; AT MOST 5 PER MESSAGE; a directory registers a dir entry under the same ttl and audience. EVERY ENTRY IT MINTS IS ENUMERABLE IN `spt serve list` WITH ITS ORIGIN, so an automatic exposure is exactly as visible as a deliberate one and what-am-I-exposing keeps its single answer. DELTA DISCIPLINE on the standing now-signal rule: once per (message, path), so a re-poll in the same session emits nothing. Gate: doc -- the shells/frames.md now-signal category table and the attachments page's helper section; impl -- the category, the attachment trigger, the quoted-path detector, the auto-registration carrying ttl and audience and origin, the cross-node register-on-my-behalf request; unit -- the attachment trigger's exact emitted line, the quoted-path trigger registering with a 24h ttl and the receiving endpoint as audience, each guard as its own cell (missing path silent, relative path skipped, the cap of 5, a directory registering a dir entry), the same-node case saying local and registering nothing, and the once-per-(message,path) delta holding across a re-poll; int -- a remote user's quoted path served to the named endpoint end to end.", 41956:traceability UNKNOWN STEP 2026-09-09T04:19:35.6027327Z "title": "B5 (F028, perri F-a; DEFECT daemon, OBSERVED-ONCE, HIGH): the peer pump STALLS under rapid rc attach/EOF-detach/--take churn. Fresh 0.22.0 daemon ~10min after restart, during rapid rc cycling: `peer pump: STALLED (last tick 122s)`; while stalled `spt rc --view` -> `RC_FAIL: attach request: brain IPC read deadline elapsed` (repeatable) and controlled-clear stopped propagating. Daemon restart recovered + endpoints auto-revived. Prior class: REQ-HAZARD-PUMP-IPC-DEADLINE (reader-thread+channel carrier), REQ-broker-QUIC-deadline (bounded_block_on) — something in the rc-churn path can still wedge the pump tick. perri holds exact timestamps + a repro candidate (rapid attach/detach/take against one endpoint) — REQUEST before RCA. See triage B5.", 44963:traceability UNKNOWN STEP 2026-09-09T04:19:35.6488304Z "title": "RESERVED EXIT 96 — account/credential refusal from a psyche_resume turn: the inner tool refused for account-level reasons (spend/usage cap, expired/revoked credential, org quota) — session healthy, code healthy, retry correct-but-pointless until a HUMAN acts. Core discriminates on the EXIT CODE ALONE (text-blind, the exit-95 layering exactly: adapters own text matching because their inner tool's wording is theirs to track; core's contract survives any rewording). SEMANTICS ruled 2026-07-26: (1) OWN PACING, fully separate from the C3(b) strike budget — an account refusal fails FAST (refused before a billed turn), so ten near-instant cycles could exhaust the defect budget in seconds and kill the psyche host as a thrashing component while nothing thrashes; the strike budget is a DEFECT budget and an outage must not be able to spend it (fold-with-higher-threshold REFUSED at ruling: it keeps the bug in a quieter form). Slow capped exponential ~60s doubling to ~15m cap, held INDEFINITELY (no give-up: a cap clears on human action or a calendar boundary — unpredictable but CERTAIN — and a permanently-given-up psyche is invisible), reset on first success, no state to unwind. (2) DISTINCT SURFACE: never the defect-shaped PSYCHE_TURN_FAIL prefix — renders as its own class (adapter-side PSYCHE_TURN_REFUSED; core-side an additive class discriminator beside psyche_host_error, never a fold into it), NAMES the actor and action (a human, the inner tool's account — not the endpoint/adapter/session), carries the inner tool's own words via the dual-stream tail (REQ-PSYCHE-TURN-STREAM-EVIDENCE), and states HOW LONG it has been refused. (3) NEVER reseeds — custody is fine; a reseed would destroy a healthy transcript for nothing. (4) Never counted as a crashloop defect. Adapter contract half: claude-spt docs/design/RESERVED-EXIT-ACCOUNT-REFUSAL.md (appendix of observed CC strings is informative-not-contract, populated ONLY from observed bytes — the invented-string chain of 2026-07-26 is the anti-pattern it refuses). Gate at activation: unit — exit 96 classifies refused (no strike increment, no reseed, own backoff schedule engaged); 95/generic/None unchanged; surface renders the distinct label + duration.", 45472:traceability UNKNOWN STEP 2026-09-09T04:19:35.6582481Z "title": "A COMMUNE THAT WAS EXPECTED AND NEVER ARRIVED IS DETECTABLE POSITIVELY, PER ATTEMPT, RATHER THAN INFERRED FROM AN ABSENCE. ROOT (releases#96, split out of the #90 recon): the summarizer spawn killed at its bound dies BEFORE writing the drop file, so it produces no file and no ingest -- and every presence-based check is structurally blind to it, because there is nothing on disk to find and nothing failed loudly enough to stamp. AN ABSENCE CANNOT BE OBSERVED; AN EXPECTATION CAN. THE DISCRIMINATOR IS A WRITE-AHEAD INGEST-INTENT MARKER: the lifecycle persists an expected-ingest record (timestamp + trigger) BEFORE launching the summarizer spawn, a completed ingest CONSUMES it, and a marker still standing past the spawn budget with no corresponding drop positively identifies the killed-at-bound case for that attempt -- no staleness threshold to tune and no guessing. THE ORDERING IS THE PROPERTY, not an implementation detail: a marker written after the spawn returns cannot survive the case it exists to detect, exactly as an intention recorded only after an answer arrives cannot survive an answer that never comes. SURFACING rides where the degraded party is already listening -- at resume/compose, beside the un-ingested-drop warning, saying that a commune was expected and never ingested and that the reader may be resuming stale. The marker is therefore written by the daemon lifecycle and read by the CLI compose path, so its storage must serve both. A killed-at-bound attempt counts on the SOFT timeout budget, never the hard one. Gate: impl -- the marker persisted before the spawn with its timestamp and trigger, consumed on ingest completion, the orphan classified past the budget, the resume/compose surfacing, and the storage readable from the CLI compose seam; unit -- a spawn killed at its bound leaves a standing marker with no drop and classifies as the expected-never-ingested case, a completed ingest leaves NO marker (the pair asserted together, since the orphan row alone passes an implementation that never consumes and would then report every healthy commune as orphaned), a marker whose drop IS present is not classified orphaned, and the write-ahead ORDERING is asserted directly by observing the marker from a spawn that never returns -- a post-spawn write passes every other row and fails this one.", 45554:traceability UNKNOWN STEP 2026-09-09T04:19:35.6598453Z "title": "EVERY WRITE INTO A SHARED CONTEXT-STORE WORKTREE IS SERIALIZED BY AN EXCLUSIVE ADVISORY LOCK ON THAT WORKTREE, AND A LOSER WAITS RATHER THAN FAILING (releases#221): concurrent per-agent project-tier ingests commit into ONE shared `p-` worktree and race git's `index.lock` and the branch's HEAD ref -- 6 PSYCHE_INGEST_FAIL across 3 agents measured on HFENDULEAM 2026-08-25, `fatal: Unable to create '.../worktrees//index.lock': File exists` and `cannot lock ref 'HEAD': is at X but expected Y`. THE LOCK LIVES AT THE LEAF GIT OPS OF `BranchStore` -- `commit_in_worktree`, `fast_forward_worktree`, `merge_commit_in_worktree` -- NOT at the ingest call site (doyle ruling 2): the ingest path is one of SIX writers of the same worktree (spt-live reconcile, syncmerge fast-forward + merge, contextstore conflict/rename/monic arms), several of them in OTHER PROCESSES, so a narrow guard leaves the identical collision reachable through a rarer door. It is an EXCLUSIVE FILE LOCK ON A STABLE NEVER-RENAMED SENTINEL keyed by the WORKTREE PATH (the `worker_seq` precedent: fs2, RAII-released on handle drop or process death, so a crashed holder strands no file and there is no stale-lock class to sweep) -- NEVER git's own `index.lock`, which stays exactly what `sweep_stale_index_locks` treats it as. THE LOCK IS NON-REENTRANT AND ACQUIRED ONLY AT THE LEAF, so a caller walking many worktrees (the rename loop) acquires them ONE AT A TIME and never nests -- the no-lock-ordering claim is an ASSERTION IN A UNIT, not a comment (doyle ruling 2 rider). ACQUISITION BLOCKS WITH A BOUND (doyle ruling 3): default 10s (two pulse periods; the guarded op is sub-second, so contention is milliseconds), overridable ONLY by a test-scoped `SPT_TEST_*` env var that never enters the operator vocabulary. A pulse tick MAY block -- it blocks only that endpoint's own driver thread, and failing fast is the very defect. ON TIMEOUT the acquire returns an ERROR carrying THE SENTINEL PATH AND THE ELAPSED WAIT BESIDE THE BOUND (at-budget vs under-budget is the only discriminator between a deadlock and a slow box), and that error rides the EXISTING capture-and-continue seam: PSYCHE_INGEST_FAIL is printed, a hard-ingest strike accrues, and THE DROP FILE IS PRESERVED -- nothing is consumed, so nothing is lost, and the F-032 preserve-pending arm (REQ-HAZARD-COMMUNE-INGEST-BLACKHOLE) is untouched beneath it. THE LOCK IS THE ONLY MECHANISM: no retry belt over git's own lock errors (doyle ruling 4) -- a second handler would make a broken lock invisible, and an index.lock failure observed WHILE the sentinel is held is a FINDING to file, never a transient to swallow. Sibling surface: REQ-PSYCHE-INGEST-FAILURE-LOUD (the misplaced-drop half of the same observability seam). Gate: impl -- the sentinel path fn + bounded exclusive acquire + its wiring at the three leaf ops; unit -- an acquire held by one handle makes a second measure elapsed >= the hold and THEN succeed (the lock is load-bearing, proven by elapsed not by a rate), the timeout error names the sentinel path AND the elapsed AND the bound, the bound is env-overridable in test scope only, a multi-worktree caller's acquisitions are sequential and never nested (the lock-ordering assertion), and a timed-out ingest DELETES NO DROP. int -- TWO PROCESSES committing concurrently into one shared worktree: zero failures, every slice PRESENT by existence read, per-arm durations reported, plus a DETERMINISTIC arm where process A holds the sentinel for a known T and process B's acquire measures elapsed >= T then succeeds.", 46459:traceability UNKNOWN STEP 2026-09-09T04:19:35.6747596Z "title": "PUMP-TRUTH W1 (RE-SCOPED post round-2 empirical lock — the DIAL is EXONERATED, healthy ~100ms): a pump worker-leg PEER-REPLY read to a connect-then-silent / half-alive peer must drop THAT peer as an ORDINARY per-peer failure (peer_outcome's non-TimedOut arm -> PUMP_PEER_FAIL -> drop conn + redial, round CONTINUES, heartbeat advances), NEVER burn the brain's 30s PUMP_PEER_IO_TIMEOUT carrier deadline into a whole-round TimedOut POISON -> supervise_pump doubling-backoff restart. ROOT (deployah leg-instrumented capture, enlyzeam, 3 identical rounds): DIAL_EXIT 96ms ok, LEG i=3 update ms=30025 err[TimedOut] = the wedge. request_update (propagate.rs:373-375) opens the update stream + sends UpdRecord::Query (all bounded, all land), then BLOCKS read_event_until(deadline=call_deadline()=30s) on the peer's Offer/UpToDate reply; a peer that accepts the stream but never answers burns the full 30s -> TimedOut -> peer_outcome (pump/mod.rs:601) POISON -> whole-round abort + restart (= the field PEER_PUMP_FAIL: brain IPC read deadline, always-zero PUMP_PEER_FAIL). request_sync (sync.rs:374-376) is the LATENT TWIN (SKIPS the reply-read only when the want-set is empty; bites the moment it is non-empty against a silent peer). FIX (both legs): (a) reclassify the reply-read no-progress timeout OUT of TimedOut to a non-poison kind (Brain::read_peer_reply_until) so peer_outcome drops ONLY that peer -- poison RESERVED strictly for a genuine broker-IPC-CARRIER desync (the carrier ops net_open_stream/subscribe/send keep raw TimedOut); the abandoned peer stream is safe (exactly-once seq cursor stays contiguous, a late reply matches no live stream id). (b) budget-decouple the reply-read below 30s (Brain::reply_read_deadline = now + min(io_timeout, 10s)) so a silent peer drops promptly even in the still-sequential pre-W2 pump and can never race the carrier deadline. Files: propagate.rs (request_update) + sync.rs (request_sync) + brain.rs (reply_read_deadline + read_peer_reply_until) + pump/mod.rs (peer_outcome poison reserved for carrier-desync). Gate: a connect-then-silent peer at fan#0 -> the update leg drops it ordinarily within the reply-read budget, round continues + heartbeat advances, NO PEER_PUMP_RESTART; happy path (live peer) unchanged; + the sync-non-empty-want-set latent case. Kin REQ-PUMP-PEER-ISOLATION (W2 concurrency, VALIDATED by this root) + REQ-HAZARD-PUMP-IPC-DEADLINE (the poison it must stop mis-firing on a peer).", 46525:traceability UNKNOWN STEP 2026-09-09T04:19:35.6756992Z "title": "PUMP-TRUTH W2 (architectural, operator ruling 2026-07-08): one peer must NOT block or poison all others -- peer discovery is async / per-peer-independent. Two coupled defects in run_peer_pump: (1) SEQUENTIAL fan-out (for peer in fan_targets dials one-at-a-time, each up to the bound -> peer N+1 waits behind peer N); (2) WHOLE-ROUND POISON (peer_outcome(...)? -- one TimedOut aborts the ENTIRE round via ? -> supervise_pump doubling-backoff restart, resetting ALL conns). FIX: per-peer concurrency + fault isolation -- the pump issues non-blocking dial requests; the broker (already async tokio+iroh) returns connection/presence results as async events (the D4c presence seam), no serial per-peer block; a peer TimedOut drops + reschedules ONLY that peer, NEVER aborts the round or restarts the pump. Supervised-restart is RESERVED for a dead BROKER conn, not a dead peer (the single-thread+bounded-read A-half REQ-HAZARD-PUMP-IPC-DEADLINE was defensive -- it stopped the infinite wedge but coupled every peer's fate; this decouples). Gate: a 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. Depends on W1 (a fast-failing dial is the precondition for clean per-peer scheduling).", 56116:traceability UNKNOWN STEP 2026-09-09T04:19:35.8403720Z "title": "F-2 (REMOTE-TRUTH triage §F-2, field-repro'd hall-bf 2026-07-04): a /clear must not sever a SURVIVING poll listener's relay address — post-clear owl-path send hit NO_PERCH while ready was present and the inject path healthy. ROOT (source-certain): the relay registry row (id→addr + owning pid, registered by the LISTENER process itself at PollListener::bind, listener.rs:109) is DELETED by the adapter's soft `api session-end` (reporting.rs:231) fired for the DEPARTING session at /clear; but the poll listener SURVIVES /clear (a session-independent process, still bound on its port), so the deletion destroys a TRUE row. The C-2 boundary re-stamp (REQ-HAZARD-BOUNDARY-READY-STRAND) restores ready + status online but CANNOT re-register — only the listener process knows its socket addr — so every subsequent send lookup misses → NO_PERCH forever (until a listener restart re-binds). FIX: the SOFT arm of cmd_session_end unregisters CONDITIONALLY through the single liveness resolver (liveness::is_registry_entry_alive — the KH 2.5-aware resolver clean_stale_entries routes through): a row whose owner is still ALIVE is PRESERVED (the row is LISTENER-scoped truth, not session-scoped; the listener outliving /clear is the designed shape), a dead/offline row is removed (today's cleanup kept). The ERASE arm stays unconditional (a hard wipe orphans any listener; its row dies with the endpoint). Every legitimate teardown keeps its OWN unregister untouched: PollListener close/close_busy/Drop (listener.rs) and the stop verbs (cli.rs:5574/:10924). Defense-in-depth unchanged: a wrongly-preserved dead row still self-heals at delivery (deliver.rs failed-dial sweep, REQ-HAZARD-REGISTRY-STALE-CLEAN). Red-first: soft session-end with a live registered owner → row survives and lookup still resolves (pre-fix: deleted → NO_PERCH).", 57078:traceability UNKNOWN STEP 2026-09-09T04:19:35.8536288Z "title": "RESCOPED at W2 activation 2026-07-22 (doyle verify-first, C3-retirement precedent): the CLEAN-CASE chain this REQ was minted for is ALREADY SHIPPED under REQ-STREAM-LEASE-CLASSES / ADR-0040 decision 6, verified against @7c0f12d — StreamLifetime is opener-declared and on the wire, serde-default Durable so N-1 openers keep exact today-semantics (msg.rs:810-836 + the additive-wire unit @msg.rs:1408); rc attach/view is the SOLE ConnectionBound opener (attach.rs:667-677); the broker binds the class at open (broker.rs:5689) and nethost carries it per StreamEntry (nethost.rs:1059-1076); the conn-exit sweep FINs each ConnectionBound stream toward its target and terminal-retires the local row while Durable rows never enter it (broker.rs:4118-4123/4227-4235/4429-4446); the target's serve_attach EOF arm runs detach_session_gen(serve_gen) so controller/viewer stamps clear on exactly one generation (attach.rs:580-597); the late-close gen guard (broker.rs:2179-2192, unit @8416) and converge_perch_stamps close the race; int coverage rides endpoint_lifecycle.rs:241. Building that again would re-implement shipped code — the C3 lesson. WHAT REMAINS, and what this REQ now owns: RESTART-REPLAY RE-ESTABLISHMENT. ADR-0038 dispatch replay re-serves every retained opener Attach across a dispatcher generation, and a ConnectionBound opener REPLAYING is a class contradiction — the conn that declared the class cannot exist after the restart that killed it, so re-serving resurrects a seat whose declared lifetime already ended. FIX: the dispatcher's restart-durable classification (ADR-0038 decision 2, dispatch.rs first_line) gains lifetime, and the replay filter TERMINAL-RETIRES ConnectionBound openers instead of re-serving them. Deliberately PRECISE, not ADR-0038's rejected clear-table-on-restart: Durable/brain-swap semantics are untouched and the brain_swap / daemon_refresh / redispatch D1+D1b families staying green IS the proof. The filter runs UPSTREAM of the W1 idempotence key (endpoint/session, by, conn, gen, from_seq) — a filtered opener never reaches serve, so the two mechanisms compose rather than overlap. Gate: impl — lifetime on the replay classification + the terminal-retire filter; unit — the filter predicate (ConnectionBound retires / Durable replays / unclassifiable stays terminal as today); int — RED-first: kill the rc opener raw, restart the broker, pre-fix the seat and CONTROLLED stamps re-establish via replay, post-fix they stay clear, with the D-legs green alongside. Kin REQ-STREAM-LEASE-CLASSES (the shipped clean case), REQ-ATTACH-IDEMPOTENT-REPLAY (downstream), REQ-HAZARD-DRIVEN-BY-IDLE-REMOTE-EVICT (the live no-FIN sibling, same wave). RESCOPE 2 — MEASURED 2026-07-22 (todlando, Leg A rig `restart_replay_lifetime.rs`, GREEN with its sibling probe passing): the FIX ABOVE IS NOT OWED. The premise was pre-registered as falsifiable by doyle BEFORE the rig was built, and the rig falsified it: in the residual ordering (dispatcher generation dies FIRST so no serve worker and no Served report, THEN the rc opener dies RAW with no detach frame) a fresh generation does NOT resurrect the seat — stamps stay clear and no DISPATCH line touches the dead opener's row. The population is closed BY COMPOSITION: the conn-exit sweep fires for this kill shape (`STREAM_CONNBOUND_RETIRE:: opener conn exited — FIN + terminal retire`), the ConnectionBound opener row leaves the table entirely, its peer row carries the FIN as finished=true, and the worker's family-aware finished_row_is_terminal(Attach,true) retires it UNSERVED. TWO PRE-REGISTERED SUB-QUESTIONS ANSWERED, and the first is the non-obvious finding: (1) `retire` does NOT exclude from the CLAIM condition on this path — the dead opener's row is STILL in the dispatcher's claimable enumeration, merely finished=true, so what stops the replay sits DOWNSTREAM of the claim (retire-on-sight in the worker), which is ADR-0038 decision 1's `excluded by lifecycle state` arm and NOT its `removed from the enumeration` arm; (2) the sweep DID run for this kill shape. Consequently NO wire field (already settled independently: the re-served row registers Durable, class-blind by design at nethost.rs:1028-1041 / 1763-1771, so a lifetime field on the opener reply would report Durable for exactly the population a filter exists to catch) and NO broker-side enumeration filter are built. Stages drop to int ONLY — impl/unit are deactivated rather than pre-failed (the `activate, don't pre-fail` rule; the C2/C3 verify-first precedent), and the int evidence is the standing regression guard the property never had. SCOPE LIMIT STATED, NOT IMPLIED — and TIGHTENED 2026-07-22 (doyle rider, todlando self-flagged): the measurement rides the IN-PROCESS DUPLEX, which is the honest word and is tighter than the `loopback` this text first said. net_dial_loopback / open_loopback_pair yield a RecvHalf::Loopback duplex with NO QUIC ANYWHERE and NO idle timeout — a different transport leaf from RecvHalf::Quic; BindScope::Loopback plus a real net_dial WOULD be real QUIC over 127.0.0.1, and the two senses of `loopback` in this repo are NOT the same thing (the naming collision is itself an ADR-0040 amendment item, so future rigs pick their venue deliberately). Consequently the cross-node `conn.closed()` arm (nethost.rs:1000-1021) is a different mechanism reaching the same outcome and is NOT exercised, and a real-QUIC transport death sans FIN remains Leg B's instrument-first residual under REQ-HAZARD-DRIVEN-BY-IDLE-REMOTE-EVICT.", 60002:traceability UNKNOWN STEP 2026-09-09T04:19:35.8912404Z "title": "IR-15 (INFRA-REGISTER, doyle-ruled 2026-08-03): a TEST that captures a pid and POLLS it to a verdict must poll the process it CAPTURED, never the number. `provably_gone` answers 'is *a* process at this pid', which is correct for a pid just handed over and WRONG for one captured seconds earlier: between capture and verdict the OS may recycle the number onto an unrelated process, and the bare-pid oracle then reads the stranger as the captured process, still alive, until the deadline burns. Specimen: golden 30776330383, daemon::tests::a_tree_teardown_reaches_a_grandchild_the_service_spawned sat the FULL 10s window (10.176s vs 0.19/0.24s passes) on a pid-churning Phase A leg, with a Cargo-dep-pin delta that has no mechanism path to it. THE PRODUCT PATH ALREADY AUTHENTICATES pid against birth stamp (process_identity, ADR-0047 decision 1, KNOWN-HAZARDS 7.51); the TEST oracle is the half that never got the stamp -- two death oracles in one tree and the tests poll the pid-only one. FIX: a PinnedProc identity (pid + birth stamp captured at FIND time, or handed in via from_stamp where a spawn helper or a stored pid_started_at already holds one) whose provably_gone reads Absent=>gone, Present(same stamp)=>not gone, Present(DIFFERENT stamp)=>gone (reuse: ours died), Present(_)+unstamped=>not gone (degrade to bare-pid), Unproven=>not gone. POLARITY IS LOAD-BEARING AND RULED: every uncertain arm errs toward NOT GONE, i.e. toward a red, because the two failure directions are not comparable -- a false GONE hides a real kill-tree miss behind a green gate and is unrecoverable, a false NOT-GONE is a visible recoverable flake. The pre-existing property that this oracle can only ever false-RED is therefore PRESERVED, not traded: a green from a poller built on it stays exactly as conclusive as before, which is what the same-sha-rerun authorization rests on. PLATFORM STRENGTH DIFFERS AND MUST NOT BE FLATTENED: Windows creation FILETIME is 100ns so Present(different) is decisive; Linux starttime is clock ticks since boot at 10ms granularity, so a reuse landing inside one tick reads Present(same) and errs RED (the safe direction) -- this NARROWS the window and does not close it, and Present(same) on Linux is never proof the pinned process survived. Image-path corroboration (releases#120) is the second axis that closes the remainder and composes onto the SAME pinned identity. SCOPE IS THE TEST-SIDE ORACLE: one shared helper covering the swept call sites (daemon.rs teardown test + endpoint_lifecycle.rs relay_pid, the latter not yet red), never a patch to whichever test happened to fire; product-side callers (teardown.rs root_provably_gone, servicehost.rs) stay out and belong to #120's product lane.", 60342:traceability UNKNOWN STEP 2026-09-09T04:19:35.8984070Z "title": "When a message reaches an endpoint because an access ENTRY admitted its sender, and the receiving agent holds NO MONIC about that sender, the delivery edge composes a system-authored TRUST WARNING and delivers it alongside the message. The entry is a routing decision — often made by a human at a knock, sometimes weeks earlier — and it says the peer may speak; it says nothing about whether the receiving agent has decided anything ABOUT them, and the warning is what closes that gap before the agent acts. THE CLASSIFICATION QUESTION IS MONIC-ONLY: an access entry naming the peer is NOT a classification, since the entry is the very thing that let them in, and reading it as evidence of judgement would silence the warning exactly when it is warranted. THREE RATIFIED NON-WARNING CASES, stated because a warning that fires on invited traffic teaches agents to ignore warnings, which costs it its only job: SAME-NODE arrivals are inside the node's own trust unit; a REPLY is correlated to the agent's own outbound, so it is traffic the agent itself invited; and a POSTURE-OPEN pass admits nobody in particular — no entry named this peer, the endpoint simply is not refusing anyone. A WILDCARD entry warns exactly as a named one does: it admitted a peer the agent never named at all. THE WARNING NEVER CHANGES DELIVERY. The gate already allowed the message; the warning is advisory text that rides alongside, is skipped for a duplicate (the replay check precedes it, so a retried message does not re-warn), and — when it cannot be delivered at all — costs a loud diagnostic naming the unwarned delivery rather than withholding a message the gate permitted. IT IS NEVER PART OF THE PEER'S BODY, because a warning inside the body is indistinguishable from one the sender wrote — precisely what a stranger would forge. THE CARRIER IS THE DELIVERED MESSAGE'S OWN ENVELOPE (REQ-TRUST-WARNING-ENVELOPE, which changed the carrier and left this rule untouched): the block rides as a `trust-warning` attribute composed by the RECEIVING node, so the caution and the message it is about reach the agent in ONE arrival, and the sender authors the text in neither design — an attribute is not the body, and it is composed exactly where a matched monic's is. Where the body is ALREADY A TYPED ENVELOPE it can carry no attribute, and there the warning keeps its own system-authored delivery under a RESERVED author id (not a legal endpoint id, so no peer can author under it), delivered FIRST over the same cascade the message takes, so the caution is read before the message it is about. THE DECISION CONSUMES THE GATE'S OWN VERDICT AND NOTHING ELSE: the pass reason IS the posture question (an entry matched means the chain never reached a mode tier), so there is no second read of the access store, whose snapshot would be taken after the decision it purports to explain. The receiver's own mind is read as a PLAIN FILE off the tracked root, never through the store's ensure-worktree path, because that path spawns git and this question is asked on the inbound message path. An unreadable monic is not a classification, so a husk makes the warning fire rather than suppressing it. TWO LIMITS, RULED (doyle 2026-07-31, at T6's acceptance): (1) a sender the daemon could not prove IS still warned about, and that warning is UNSUPPRESSIBLE by design rather than merely fail-safe — a peer nobody can name is more of a stranger, not less — since monic-only classification means no provable id yields no monic, so every unproven sender on an entry-admitted pass warns, which is honest — they ARE unproven — and self-heals as the fleet's daemons come to stamp their senders. THE TEXT DISCIPLINE IS THE LIMIT THAT KEEPS WARN-MORE FROM BECOMING WARN-NOISE, and is required, not stylistic: the block names the admitting RULE as the way out and NEVER prints a classify command that cannot be run, because an instruction the reader cannot carry out is what turns an unsuppressible warning into noise the agent learns to skip; (2) the override text (the elevation-gated verb, own requirement) replaces ONLY the advisory paragraph — RATIFIED at the same acceptance, on the grounding that override text is agent-behavior instruction and therefore a prompt-injection surface, so keeping the factual spine unforgeable BOUNDS that surface: a whole-block override would let one elevated write hide who is knocking, which no legitimate override needs — the line naming the sender and stating that no note is held, and the line saying how to classify them, are always core-composed, so an override can change what the agent is cautioned about but never hide who is knocking. Gate: impl — the pure warn-or-not decision over the verdict's pass reason and the monic predicate, the store-free tracked-root read, the composer with its reserved author, and the delivery-alongside at the WAN edge ahead of the message legs; unit — the full table (every pass reason x monic-held/not, with the three non-warning reasons asserted by name and the wildcard case asserted to warn), the composer naming the peer, the three cautions and the exact classify command, the override replacing the advisory while identity survives, the unnamed-sender text carrying no unrunnable command, and the tracked-root read answering identically to the store form while creating no worktree for an agent that has no mind.", 60558:traceability UNKNOWN STEP 2026-09-09T04:19:35.9030916Z "title": "The trust warning RIDES THE DELIVERED MESSAGE'S OWN ENVELOPE as a `trust-warning` attribute composed by the RECEIVING node, so an agent reads the caution and the message it is about in ONE arrival rather than two. THE CARRIER CHANGES; THE RULE DOES NOT — when a warning is owed (REQ-TRUST-WARNING) and how often it surfaces (REQ-TRUST-WARNING-CADENCE) are untouched by this requirement, which settles only WHERE the composed block travels. THE GROUNDING IS THAT A SEPARATE DELIVERY IS A SEPARATE INJECTION: under an spt-hosted harness each delivery becomes its own context injection, so a caution delivered alongside costs the agent a second interruption for a block it must read with the message anyway. THE UNFORGEABILITY REASON IS PRESERVED, NOT TRADED, and this is the distinction the change turns on: the ratified rule forbids SPLICING THE WARNING INTO THE PEER'S BODY, because a warning inside the body is one a sender could have written. An envelope attribute is not the body — it is composed by the receiving node exactly as a matched monic's `mnemonics-json` is, so the sender authors it in neither design. THE ATTRIBUTE IS ADAPTER-VISIBLE BY DECISION, NOT BY ACCIDENT: the surface question was resolved before building and it was decidable rather than preferential, because the payload is text the AGENT MUST READ and any carrier stripped before the EVENT would ship a caution that never surfaces. It therefore enters the PUBLISHED envelope surface, which obliges the public envelope documentation to state it and a re-rendering adapter to surface it. FOR THIS ATTRIBUTE, BEING IGNORED IS THE FAILURE, which INVERTS the mnemonics-json precedent: an adapter safely ignoring an unknown attribute is exactly how a security caution goes missing, so 'additive and N-1-safe' is necessary and NOT sufficient here, and the doc must bind a custody pipeline that re-renders a delivery to carry the attribute through, on the precedent already set for carrying matched monics through intact. THE FAIL-SAFE IS NAMED, NOT DISCOVERED. A body that is ALREADY A TYPED ENVELOPE carries no attribute — it rides verbatim by construction, and splicing into a finished envelope would mean hand-rolling the grammar a second time. For exactly those deliveries the warning KEEPS its own system-authored delivery under the reserved author, unchanged. The second injection therefore survives precisely where no carrier exists and nowhere else, and the caution is never traded for the fix. THE INBOUND VALUE IS INERT BY CONSTRUCTION. A sender-supplied body that is itself a typed envelope rides verbatim through the wire, the spool and every renderer, so a peer can write this attribute into one and have it arrive wearing the receiver's own voice. Every point at which a sender-supplied body enters this node STRIPS the receiver-composed attributes BEFORE any is attached, so the value a recipient reads is the receiver's own by construction rather than by the sender's restraint. THE STRIP IS BY CLASS, NOT BY NAME: receiver-composed attributes are a named set, because two name-strips at one seam authored in two lanes is a drift pair, and inertizing the class costs exactly what inertizing one name costs while holding for the next receiver-composed attribute too. Attributes an envelope's own AUTHOR legitimately carries (`type`, `from`, a notify's id, an alarm's times) ride end-to-end intact — the strip removes named receiver-composed attributes from an otherwise untouched envelope, and it is not the wholesale re-compose that re-stamping an envelope's TYPE performs. A key that could not be re-emitted safely is DROPPED rather than written back unescaped. Gate: doc — the public envelope surface states the attribute, that the receiving node composes it, the MUST-surface obligation on a re-rendering adapter with ignored-is-failure as its reason, and the typed-envelope fail-safe; impl — the attribute constant and the receiver-composed class in the envelope grammar, the class strip at each sender-supplied ingress, the attach at the WAN edge through the envelope renderer so a matched monic still evaluates under the one rule, and the fail-safe branch that keeps the separate delivery when the body carries no attribute; unit — a forged inbound trust-warning attribute is inert on a delivered envelope, an admitted stranger's message carries the warning on its OWN envelope with no second delivery spooled, a typed-envelope body still draws the separate system-authored delivery, the strip preserves every other attribute and the body, and the cadence still claims only after the carrying message was actually delivered.",