### IR-122 — the field rig's isolated-daemon stop cannot run from an spt-hosted session, so every run leaves a live daemon holding the subject exe inside the pool it just released

- **Status:** OPEN, recurring. Instance 1 attempt 2 (`rig/arm2-driver-stdout.txt`: `RIG_DAEMON_STOP_REFUSED_OR_ERRORED — reported, not forced`). Instance 2 run `20260912T231404Z` under frozen r10 (`rig_daemon_stop_EXIT=3`, `cleanup-failures.txt`, `rig-daemon-stop.err`). Recorded by hertz 2026-09-12 23:50Z at doyle's direction; **this is a RIG defect, and any product-surface change requires a separate contract review.**
- **The refusal.** `DAEMON_STOP_REFUSED: a broker-stopping command is never run by an spt endpoint ($OWL_SESSION_ID is set). ... No flag overrides this, --force included.` The guard keys on the CALLER'S SESSION ENV, so it fires identically no matter which rig step reaches it and no matter what `SPT_HOME` the command targets.
- **Not a new fault, and not an accident.** `readiness-r4-as-read.md:107-109` already states it: v9 DELIBERATELY leaves `OWL_SESSION_ID` set, the refusal is expected to recur, and it is "reported, never worked around silently." The rig chooses a loud known refusal over an unproven workaround — that choice is right, and it is the reason this entry is about the leftover, not about the refusal.
- **What it actually costs.** Cleanup completes with `steps_failed=1` and the isolated daemon SURVIVES. Measured in instance 2: supervisor `daemon run --detached` and its child `daemon brain --generation 0 --start-reason cold`, born 331 ms apart, both executing `<worktree>/target/release/spt.exe`. They hold the SUBJECT exe open inside a pool `pool_release` has already returned, so the next build into that pool can fail on a locked `spt.exe` — a red belonging to neither lane. See memory [[live-agents-lock-target-debug-spt-exe]], [[shared-target-two-live-trees]].
- **The counterfactual the refusal does NOT license (doyle, correcting hertz in this instance).** That the guard refused does not prove the isolated-target command *would have* stopped the resident broker. The guard reads the env, not the routing; establishing what the command would have reached needs routing evidence nobody has gathered. State the refusal, never the averted disaster.
- **Disposal, and it is the same four-field procedure both times** (`readiness-r4-as-read.md:110-118`): identify on pid, `CreationDate`, exact `ExecutablePath` and `CommandLine` — a bare pid match is unsound because pid reuse on this box is measured, see [[pid-reuse-across-reboot]]. The supervisor must still equal `<isolated-home>/daemon.pid`, and the brain's parent must be that supervisor. Then a fresh authorization NAMING THE NEW PIDS (the prior one is pid-specific and spent), supervisor first so the brain cannot respawn, `Stop-Process -Id` alone — never a tree kill, an image-name kill, or any action against the resident node daemon, which is a DIFFERENT binary under `AppData\Local\spt-core\bin\`. Post-state ASSERTED, not announced, or it is [[watchdog-reports-a-kill-it-never-made]].
- **Mechanism.** A cleanup step that is structurally unrunnable in the environment the rig always runs in. It cannot be fixed by retrying, it never fails loudly enough to block the run, and its residue lands in a shared build pool rather than in the run's own directory — so the cost is paid by whoever builds next, not by the run that caused it.
- **Rule (scoped by doyle 2026-09-12 — a bare pid-file kill is NOT approved as sufficient).** A rig that starts a daemon owns stopping it by a route that works from the rig's own environment, and `<isolated-home>/daemon.pid` is the pid SOURCE, never the authorization. Any rig cleanup that terminates a process must, at the moment of acting: re-read creation time, exact `ExecutablePath`, `CommandLine` and isolated-home association FRESH; confirm the supervisor still equals the pid file and the brain's parent is that supervisor; stop SUPERVISOR-FIRST so no child respawns; and report failure EXPLICITLY rather than escalating scope. Any mismatch or unreadable identity STOPS the action. No PID-only automation and no product-guard bypass is approved. A cleanup step whose failure leaves state in a SHARED resource reports that residue as an identity the next consumer can act on — pid, path, command line — not merely as a failed step.
- **Kin:** [[IR-42]] (pool enforcement speaks at the next build, not at claim time), memory rig-ownership-reap-only-your-own, reap-root-needs-authenticated-ancestry, e2e-leaked-daemons-shared-box, no-machinewide-killon-shared-runner.
- **Ripe when:** before the next field run. NOT a one-line change: the stop route is small, but the identity re-verification, supervisor-first ordering and explicit failure reporting above are part of the fix and are what make it safe. · **Size:** small.

### IR-123 — a helper that LOGS cannot also RETURN through stdout: `record` tees, so `face=$(face_report ...)` captures the log block and the acceptance gate can never pass

- **Status:** OPEN, mechanism recorded 2026-09-13 (hertz, found by exercising the D2 successor; confirmed by doyle from a direct source read of frozen r10). **Frozen r10 is PRESERVED as-is**; the return channel is corrected in its successor only.
- **Instance.** `record()` is `echo "$1" | tee -a "$R/findings.txt"` — it writes to the log AND to stdout, deliberately, so a live operator sees the run. `face_report()` calls `record` twice (a FACE_COUNTS line and a FACE= line) and then `echo`es the face as its return value. The single caller reads it as `setup_face=$(face_report "$SETUP_OUT" "$SETUP_ERR" "SETUP" bootstrap)`, so the captured value is **the whole tee'd log block with the face appended**, and `[ "$setup_face" != reconciled ]` is true no matter what the product printed. The Arm-B acceptance gate — rc 0 AND face reconciled AND pair written, all three required — is therefore **unpassable on the ACCEPTED path**.
- **Why no rig run caught it.** The stub cases that reached this line were REFUSALS, whose expected outcome is exactly the branch the defect forces (exit 3, Arm B held). A gate that always refuses looks identical to a gate that correctly refused a refusal. The accepted path had no arm at all until the D2 successor's control ran one with `LAN_FIREWALL_RECONCILED`, exit 0 and a pair measured present — see memory `setup-suppresses-failing-arm`.
- **The same shape, twice more, in the successor's own new code** (both caught by the same control, both mine): `D2_T1_VALID=$(d2_validate_at t1)` captured the validator's tee'd log ahead of its verdict; and `local point="$1" doc="$R/d2-$point.json"` rebuilt [[IR-121]]'s declaration-ordering defect, so three documents present on disk all read `NO_DOCUMENT`. The precedent for the fix was already in the same file: r10's own `handoff_request` comment records that `spec=$(handoff_request ...)` was broken by exactly this tee and was repaired by setting parent globals and returning nothing on stdout.
- **Scope of what this does and does not invalidate (doyle).** It does NOT invalidate r10's observed product refusal: the named rule reported `[5, 20]` and positive reconciliation was independently absent. It DOES invalidate any claim that the frozen rig could have admitted a successful setup — that path was never reachable.
- **Mechanism.** A dual-purpose channel: the same stream carries human-readable logging and a machine-read value, so any helper that logs before returning hands its caller both. It fails silently and in the direction that looks conservative (a gate that refuses), which is why it survives review and why refusal-only exercises confirm it.
- **Rule.** A function that calls `record` (or any logger that writes to stdout) **returns nothing on stdout**: it sets a named global, or writes its value to a file the caller reads. Every acceptance gate is exercised on BOTH paths — an accepting arm with real positive evidence and a refusing arm — because an acceptance test alone cannot distinguish a working gate from one that accepts everything, and a refusal test alone cannot distinguish a working gate from one that refuses everything.
- **Kin:** [[IR-121]] (a wrong value with no error, same file), [[IR-118]], memory setup-suppresses-failing-arm, make-a-new-rig-red-on-purpose, every-return-arm-is-a-copy, zero-match-filter-reads-as-absent.
- **Ripe when:** now — the successor already carries the fix at its call sites and in its own helper; the register entry is the part that outlives this lane. · **Size:** procedure.

### IR-124 — a cleanup that runs `serve lan --stop` UNCONDITIONALLY creates the daemon it exists to remove, when the setup it is cleaning up after never ran

- **Status:** OPEN, measured once in the field. D2 field run `20260913T041410Z`, driver `2e3aec1b`, recorded by hertz 2026-09-13 at doyle's direction. Archive `.spt/preserved/hertz-fp-driver-review/d2/field-run-20260913T041410Z/` (72 files, manifest `defa5d29…`). **This is a RIG defect. It is mine — I wrote the driver — and doyle's admission review missed the unconditional stop path as well; both are recorded because a review that misses a path is part of the mechanism, not an aside.**
- **Instance.** The elevated setup was **DECLINED and NOT STARTED** by liam at 04:18:55Z — measured, unambiguous, and the right call: his leg was ready at 04:18:35Z against a hard 04:19:10Z expiry and he refused rather than race the teardown. Nothing ran, nothing mutated. The driver then entered cleanup and executed its own unconditional `env SPT_INSTALL_NO_FIREWALL=1 SPT_HOME=<isolated-home> <exe> serve lan --stop`, which dispatched through the daemon (`ensure_running`, `serveverb.rs:191`) and **CREATED** a supervisor (`daemon run --detached`, Windows PID 45944, `04:19:18.0312050Z`) and a brain (`daemon brain --generation 0 --start-reason cold`, PID 22524, `04:19:19.7696380Z`, parent 45944). `fp-home-d2`, measured ABSENT at the 04:13:58Z at-launch recheck, existed with `daemon.pid`, `brain.ready` and a full tree by 04:19:18Z.
- **Why the hold could not stop it.** doyle held the elevated teardown for exactly this reason and liam held all commands. But this stop is **internal to the running driver**, not a handoff, so no authorization gate reached it: the only levers were killing the driver or editing a running script, and both are forbidden. **A guard that only governs dispatched work does not govern a step the subject performs itself.**
- **The pre-dispatch markers named the mechanism and did not prevent it.** `daemon_may_run — serve dispatches through the daemon (ensure_running, serveverb.rs:191); the elevated leg may leave one running` was set BEFORE the setup was requested. The driver correctly refused to infer absence from its own expectation — and then triggered the very mechanism the marker warned about, from the cleanup path, on the branch where the warning did not apply.
- **Three things kept apart, because collapsing any two misstates the run.** (1) Liam's testimony: NOT_STARTED, no mutation — still true, he created none of it. (2) The driver's conservative verdict: `CLEANUP=PENDING`, `residual_state=UNREADABLE`, `residue=UNMEASURED` — an honest refusal to claim clean, not a contradiction of liam. (3) An **actually observed** residual, created by the driver itself. That third thing is a measurement; the first two are testimony and abstention.
- **Mechanism.** A teardown verb that is also a *start* verb. `serve lan --stop` must reach the daemon to ask it to stop, so invoking it when no daemon exists starts one. Cleanup paths are written for the case where setup succeeded, and are reached on every path including the ones where setup never happened; the branch nobody exercises is the branch where cleanup has nothing to clean.
- **Rule.** A cleanup step whose command can CREATE the resource it removes must be **conditional on evidence that the resource exists** — not on the state markers, which say "may exist" by design, and not on the fact that cleanup was entered. Where the existence check is itself unavailable, the step is SKIPPED and reported as skipped, because an unnecessary teardown that spawns is strictly worse than a labelled gap. Exercise every cleanup path from the **setup-never-ran** state, not only from the setup-succeeded state.
- **Kin:** [[IR-122]] (the other half: the stop that *cannot* run leaves a daemon; this is the stop that *should not* run and makes one), memory setup-suppresses-failing-arm, gate-against-documented-design, remedy-must-run-from-refusal-state.
- **Ripe when:** before any repeat field run — a repeat under this driver reproduces the residue. · **Size:** small.

### IR-125 — the driver's own executable sweep reported ZERO processes on the pinned exe while `Win32_Process` showed TWO, at the same instant

- **Status:** OPEN, measured in D2 field run `20260913T041410Z` (hertz, 2026-09-13). **A FALSE-NEGATIVE SWEEP, not a false CLEAR** (doyle's classification, and the distinction is load-bearing): the ledger's actual verdict was `residual_state=UNREADABLE` / `CLEANUP=PENDING`, which was correct, and the sweep line is explicitly labelled evidence-only in the driver's own output.
- **Instance.** `SUBJECT_EXE_PROBE=OK running_the_pinned_executable=0 enum_total=618` — the probe enumerated 618 processes and concluded none was running the pinned executable. A `Win32_Process` read moments later returned **two**, both with `ExecutablePath` exactly `…\.worktrees\304-w2-repr\target\release\spt.exe`: PIDs 45944 and 22524. An independent sweep by path, not by pid, agreed: `count=2`.
- **Why it matters despite being labelled evidence-only.** The line reads as a clean box to anyone skimming, and it is the only line in the run that speaks to "is anything of ours still running" in plain terms. It was contradicted only because a native read was performed for a different reason. Had the ledger's verdict logic depended on it, this is a false CLEAR.
- **Candidate cause, NOT diagnosed** (deferred by doyle until cleanup was complete, and still open): the probe's raw path comparison. The Win32 `ExecutablePath` is backslashed Windows form; the driver's pinned `$EXE` is forward-slashed msys-friendly form. A literal comparison between those two spellings of the same file fails for every process. **Unconfirmed** — it is a candidate because it fits, not because it has been measured.
- **Mechanism.** A predicate that can only ever return "no" reports a clean zero over a large, healthy-looking enumeration. `enum_total=618` is what makes it convincing: the instrument demonstrably ran and demonstrably saw the machine, and still could not see the thing it was looking for. Same family as the two other instrument-versus-reality mismatches in this lane, and all three were invisible to stubs.
- **Rule.** A sweep that reports zero carries its own **positive control** — in the same run, the same predicate must find something known present, or the zero is UNMEASURED rather than absent. Where a predicate compares paths across the msys/Windows boundary, normalise both sides and prove the normalisation on a known-matching pair; never compare two spellings of a path and call a non-match an absence.
- **Kin:** memory count-predicate-needs-its-own-positive-control, zero-match-filter-reads-as-absent, windows-firewall-census-clean-zeros, verdict-from-probe-competence, [[IR-118]].
- **Ripe when:** with IR-124, before a repeat field run. · **Size:** small.

### IR-126 — the experimental budget CONTAINED its own read-only preflight, leaving 128 s of a 300 s window for the elevated leg, and the leg was correctly declined

- **Status:** OPEN, measured in D2 field run `20260913T041410Z` (hertz, 2026-09-13). Boundary NOT moved for that run — doyle refused the change mid-flight and was right to; this entry is the scheduling review he asked to be preserved.
- **Measured, from the run's own timestamps.** Run start `04:14:10Z` · pre-setup absence census `04:16:23Z` · handoff request written `04:17:02Z`. So **172 s of the 300 s experimental budget went to in-budget read-only preflight** — CI axis queried by name across five statuses, ancestry census, six-instrument hashing, subject identity, 5470 guard baseline, pre-setup absence census — and the driver printed the remainder itself: `HANDOFF_WAIT bounded to 128s`.
- **The consequence, and it is not hypothetical.** Liam's leg script was ready at `04:18:35Z`. Setup alone had measured ~9 s, plus four PowerShell stamps, plus a t1 capture of unmeasured duration, against 35 s of remaining clock. He DECLINED without mutation, which was the correct call and the one the instruction asked for. **The window was consumed before the work it existed for could start.**
- **Two overruns recorded beside it, as observations and not as permissions.** Experimental activity ended at **306 s of the 300 s budget** (the deadline exits through the EXIT trap, so cleanup is never skipped, but the boundary was crossed). The driver was still running after its 180 s cleanup reporting bound had elapsed; it exited by `04:23:54Z`. Neither overrun was authorised, taken as an extension, or used to do more work.
- **Mechanism.** A budget defined from process start, over a phase order that puts every read-only admission check inside it, gives the elevated leg whatever is left rather than what it needs. The preflight cost is not waste — those checks are why the run was admissible — but charging them to the same clock makes the window's usable size an accident of how slow the host's firewall enumeration is that minute.
- **Rule.** Bound the ELEVATED LEG on its own clock, sized from its measured duration, and start it when the leg is dispatched. Admission checks get a separate bound. A driver that cannot guarantee a usable handoff window should REFUSE TO DISPATCH rather than dispatch a leg that cannot finish — an operator declining at the deadline is the correct outcome of a wrong window, not a failure of the operator.
- **Kin:** [[IR-122]], memory your-cadence-can-be-the-blocker, reap-step-duration-is-not-the-window, twohost-run-window-discipline.
- **Ripe when:** with IR-124 — a repeat field run under the current budget shape reproduces the squeeze. · **Size:** small.

### IR-127 — a nested value-type assignment can leave the native job limit unset while the instrument logs the requested flag and a successful setter

- **Status:** OPEN, rig-instrument hazard, not a product finding. Measured by hertz in group M, 2026-09-13; accepted by doyle in `3XE67XYP`.
- **Instance.** The containment probe assigned `$info.BasicLimitInformation.LimitFlags = 0x2000`, logged the requested KILL_ON_JOB_CLOSE flag and `SetInformationJobObject=True`, read both owned processes as job members, closed the handle, and found both still alive. The nested field is a struct: PowerShell changed a copy, not the value subsequently marshalled. The offline discriminator measured `nested_assignment_flags=0`; modifying the nested value and assigning the whole value back measured `whole_value_assignment_flags=8192`.
- **Correction and evidence.** The corrected probe assigns the whole nested value, calls `QueryInformationJobObject`, and refuses unless the kernel's flags equal the requested flags. Readback `0x2000` then preceded both members being measured GONE after close. Initial invalid arm and corrected measurement are preserved separately under `.spt/preserved/hertz-fp-driver-review/d2/controls-out/20260913T103751Z-M/` (`M3/` and `instrument-repair-20260913T104354Z/M3/`); the latter's `results.txt` carries the offline discriminator.
- **Rule.** Mutating a nested interop value requires assigning that value back to its owner. Successful native setters establish only that the submitted value was accepted: read the effective configuration back and compare it before granting the operation authority. Requested flags are not observed flags.
- **Kin:** [[IR-116]] (a successful call is not a post-state), [[IR-118]] (a complete-looking wrong answer).
- **Ripe when:** any PowerShell/native configuration probe is authored or changed. **Size:** whole-value assignment plus checked native readback; retain the positive and negative discriminator.

### IR-128 — PowerShell `$null` can become an empty native string, so a failed launch is misread as a policy refusal before the policy is reached

- **Status:** OPEN, rig-instrument hazard, not a product finding. Measured by hertz in group M, 2026-09-13; accepted by doyle in `3XE67XYP`.
- **Instance.** Both breakaway probe arms called a P/Invoke string parameter with PowerShell `$null`; both returned `CreateProcess` error **123** and created no child. This is an invalid-name result, not the **5** required to establish breakaway-policy denial. An offline C# string-parameter discriminator returned `IsNull($null)=False`: the PowerShell binder had supplied an empty string rather than native NULL.
- **Correction and evidence.** A C# helper passes the executable explicitly, uses a mutable `StringBuilder` for `CreateProcessW`'s command line, and supplies C# null for the optional current directory. The repaired no-breakaway arm returned **5**, while the BREAKAWAY_OK contrast created a child measured outside the job and still alive after job closure. Original error-123 arms remain invalid and preserved under `.spt/preserved/hertz-fp-driver-review/d2/controls-out/20260913T103751Z-M/M5/`; corrected records live in its `instrument-repair-20260913T104354Z/M5-denied/` and `M5-allowed/`.
- **Managed-string recurrence (group N).** The same binder coercion reached `[IO.File]::Replace($tmp, $record, $null)`: the optional backup path became empty, and publication failed with `The path is empty` before any subject was created. The initial N0 gate correctly refused; its partial record and exception are preserved under `.spt/preserved/hertz-fp-driver-review/d2/controls-out/20260913T1109444702137Z-N/N0/`. The PowerShell publisher now uses the overwrite-rename overload `[IO.File]::Move($tmp, $record, $true)` instead of passing a nullable string. The corrected N0 reached the intended false coverage assertion; native C# publication retains a real C# null.
- **Rule.** At native string seams, distinguish NULL from the empty string and verify what the language binder supplies. Use a typed native-call helper where this distinction is load-bearing. Classify the actual error code at the failing stage: a launch error before policy evaluation cannot establish a policy refusal. Command buffers documented writable must be mutable.
- **Kin:** [[IR-113]] (a failure at the wrong stage is a void discriminator), [[IR-117]] (name the observation that refutes each branch).
- **Ripe when:** any optional native string argument or process-creation probe crosses PowerShell's binder. **Size:** typed call seam and a null/empty discriminator.

### IR-129 — JSON date materialization followed by implicit string parsing loses UTC kind and precision, turning a live creation anchor into a false pid-reuse verdict

- **Status:** OPEN, rig-instrument hazard, not a product finding. Measured by hertz in group M, 2026-09-13; accepted by doyle in `3XE67XYP`. The first M8 exit 0 is explicitly INVALID.
- **Instance.** `ConvertFrom-Json` materialized an ISO birth string as `DateTime`; `[DateTime]::Parse($id.created)` implicitly formatted that object back to text before parsing it. Kind and fractional precision were lost and local time was reinterpreted. The result printed birth timestamps agreeing within a microsecond but computed roughly seven-hour deltas, mislabelling four live registered identities `GONE_PID_REUSED`. The original output remains at `.spt/preserved/hertz-fp-driver-review/d2/controls-out/20260913T103751Z-M/M8/`.
- **Correction and evidence.** The reader handles `DateTime` values directly with `ToUniversalTime`; strings are parsed as `DateTimeOffset` and converted to UTC. Its live-self JSON-roundtrip control measured **9 ticks** difference; a deliberately 500-ms-skewed anchor measured **5,000,009 ticks** and could not match. The corrected final pass covered all **13** registered identities, found every one GONE, and retained the escaped child's earlier SURVIVING readings. Evidence: the same group's `instrument-repair-20260913T104354Z/all-identities-disposition.jsonl` and `final-disposition.stdout`.
- **2026-09-14 recurrence caught before the field window.** The bound promotion instrument repeated the same reader/parse pair at its brain-birth guard. A language-only discriminator using that pair measured **251,998,765,433 ticks** error. Reading JSON with `-DateKind String` instead preserved the timestamp: same-anchor delta **0 ticks**, deliberately wrong 500-ms anchor **5,000,000 ticks** and refused by the unchanged **10-tick** guard. The successor changes only `Read-Json`; the original instrument remains intact. Evidence: `.spt/preserved/hertz-fp-driver-review/d2/promotion-date-discriminator-result.json` and `provision-promote-VGCTKD7A.ps1`. No field process was needed to expose this known hazard.
- **Rule.** Treat deserialization's runtime type as part of the interface. Never reparse a typed date through implicit culture-sensitive formatting. Exercise both a same-process roundtrip and a wrong-anchor negative control; preserve raw timestamps and the computed delta so their agreement can be audited. Match at the actual reader resolution, not by discarding the fractional second.
- **Kin:** [[IR-118]] (successful instrument, wrong answer), [[IR-125]] (an absence predicate needs a known-present control), [[IR-15]] (identity-aware liveness).
- **Ripe when:** any JSON record is used to authenticate process identity or order events. **Size:** type-aware UTC conversion and two local controls.

### IR-130 — a lowercase `$home` local collides with PowerShell's protected `$HOME`, refusing custody qualification before the signer is reached

- **Status:** OPEN, rig-instrument hazard, not a product finding. Measured by hertz on 2026-09-13 in the D2 custody lane; narrow correction and one fresh-root attempt authorized by doyle `5FWLC3F2`. Original refusal remains preserved.
- **Instance.** The prepared custody controller passed syntax parsing and compiled its native observer successfully, then exited **1 after 3.33 s** before any custody arm. Its next statement assigned `$home = Join-Path $armRoot 'home'`. PowerShell variable names are case-insensitive: this is an assignment to automatic `$HOME`, whose measured options were **ReadOnly, AllScope**. A separate language-only reproduction returned **VariableNotWritable**. The original catch deliberately omitted exception details, so that error ID belongs to the minimized reproduction; the original record carries the generic refusal and `arms=[]`.
- **Boundary and evidence.** Compiler native exit **0**, `COMPLETE`, `CONFIRMED_GONE`, active processes **0**; a public fixture and empty `custody-normal` directory existed, but no home, subject arguments, dummy EnvironmentFile, observer execution, signer, git child, real key, or field rig existed. Evidence: `.spt/preserved/hertz-fp-driver-review/d2/AZLCQNZL-CUSTODY-REFUSAL.txt`, `AZLCQNZL-home-variable-repro.json`, `AZLCQNZL-custody-failure-inventory.json`, and `controls-out/custody-6U2UZVXK/`.
- **Correction.** Rename the local to `$qualificationHome` at its assignment/directory call and environment-overlay reference (two source lines), re-pin the controller, and use a fresh qualification root. Do not relax automatic-variable protection, reuse the failed root, or classify this as a rejection of the assembled xtask's seed isolation. The authorized retry's result is separate evidence, not a rewrite of the first attempt.
- **Rule.** PowerShell locals share a case-insensitive namespace with automatic variables: use role-specific names rather than `$home`, `$host`, or `$pid`. Syntax acceptance and a successful subordinate compiler do not prove that controller initialization reached its behavioral discriminator. State the last stage actually executed; an empty retirement list is not evidence that secret-bearing processes ran and were cleaned.
- **Kin:** [[IR-113]] (wrong-stage refusal is a void discriminator), [[IR-128]] (language-boundary failure before the intended policy), [[IR-118]] (plausible result without the claimed observation).
- **Ripe when:** any PowerShell orchestration or custody controller is authored or changed. **Size:** two-line naming correction plus retained refusal and fresh-root qualification evidence.

### IR-131 — a live-agent signing environment leaks into the provisioning process and correctly refuses before the private signer exists

- **Status:** OPEN, rig invocation-environment defect, not a candidate or disk defect. Measured by hertz on 2026-09-14; classified by doyle in `VGCTKD7A`.
- **Instance.** Second fresh #297 preparation admitted fourteen native leaves at `04:51:39.245Z`. The separate provisioning call launched at `04:51:52.802Z` and returned `STAGING_FAILED_PREFLIGHT`, native exit **1**, after **0.45 s**. No rig, private directory, key or field nonce existed. This was a preflight refusal before the `04:59:31Z` deadline, not a stalled launch.
- **Exact check.** A read-only preflight discriminator surfaced `AMBIENT_SIGNING_SECRET_REFUSED` from `Assert-SeedAbsent`. A names-only environment probe found `SPT_RELEASE_SEED` **present**; `SPT_DEBUG_RELEASE_SEED` and `SPT_RELEASE_SEED_CMD` were absent. No values were emitted or changed. The refusal reproduced with **102,099,394,560 bytes free**, and the failing check reads process environment before filesystem preflight: reclaiming disk cannot cure this guard.
- **Evidence gap.** The original catch persisted a failure JSON only after `$publicReady`, set after preflight. Earlier throws produced only a generic stderr phase and an empty evidence directory. Preserve safe, fixed preflight reasons and caller exit evidence before mutation; silence in the directory is not evidence that the command never ran. Original artifacts remain unchanged under `.spt/preserved/hertz-fp-driver-review/d2/`; see `IH4QFFXS-execution-boundary.json`, `X2LPSQ7A-preflight-only-result.json`, and `X2LPSQ7A-signing-env-presence-result.json`.
- **Rule.** Scrub the environment handed to the **provisioning PowerShell process itself**, not merely its later signer child: remove every ambient `OWL_*`/`SPT_*` key except the leg's declared allowlist, recording names before/after, never values. Preserve the live agent's parent environment and lifecycle. The guard remains strict. The new debug signing seed enters only the controlled signer's private `EnvironmentFile`; it never becomes ambient in the provisioning process or helpers.
- **Scrub qualification.** The fresh-child receipt recorded `before_names=["SPT_ENDPOINT_ID","SPT_RELEASE_SEED"]`, `after_names=[]`, and parent names unchanged. This exposed the next startup error: `Stop-Transcript -ErrorAction SilentlyContinue` throws a terminating exception when the required fresh `-NoProfile` host is not transcribing. The successor removes that invalid stop, not the forced-logging policy checks or seed guard. The complete no-mutation preflight then exited **0** with `PREFLIGHT_ONLY_PASS_NO_MUTATION`; no third preparation clock had begun. Evidence: `VGCTKD7A-preflight-result.json`, `VGCTKD7A-preflight-clean-result.json`, and `VGCTKD7A-preflight-clean-environment.json` plus its exit receipt.
- **Kin:** [[IR-107]] (live-agent identity leaking into consumer/test legs), [[IR-130]] (preflight failure hidden by phase-only reporting), [[IR-122]] (live operational context is not an isolated rig).
- **Ripe when:** any leg is launched from a live-agent session. **Size:** explicit child environment boundary and durable preflight evidence; no candidate change.

### IR-132 — requiring every live-fleet `spt.exe` image path to be readable permanently refuses an isolated rig's admission

- **Status:** OPEN, rig-instrument admission defect, not a candidate, environment-scrub or disk defect. Measured by hertz and independently localized by doyle on 2026-09-14; correction ruled in `W8TQ3MZC`, `6IQ4HYIL`, `QKQC2NET` and `MRQX7XEK`.
- **Instance.** Third preparation passed fourteen native admission leaves and genuinely staged the signed set. Promotion then returned `PROMOTION_ADMISSION_FAILED`, native **1**, with `native_records=[]` and shutdown `NOT_STARTED`. Its census required a nonblank CIM `ExecutablePath` for **every** `spt.exe`, not merely one that could belong to the new rig. The read-only predicate replay passed twenty-five preceding statements and failed `SPT_PROCESS_PATH_UNREADABLE` at line 273. No apply, daemon, field nonce or firewall mutation occurred. Receipt: `.spt/preserved/hertz-fp-driver-review/d2/GEL6JAWN-FINAL-REFUSAL.json`.
- **Shared-box mechanism.** The subsequent census exposed persistent PID **52860**, birth `2026-09-12T20:17:00.5331330Z`, with readable birth but unreadable image path; the rig did not exist until `2026-09-14T05:45:32.8201734Z`. Doyle additionally measured blank paths in **12/12** samples, with transient CLI children alongside the persistent process. An image-readability requirement cannot admit this live shared box. Missing per-rig state files remain an independent gate, not a substitute for process-identity reasoning.
- **Correction.** Capture `(pid, CIM CreationDate)` together. A birth provably earlier than rig creation excludes that identity without needing its image; the **10-tick** precision allowance only narrows this exemption. Later or overlapping blank-path identities use the existing limited-query native image/birth/retained-handle seam. A native failure is unresolved, not gone. A successful immediate CIM re-read with a live positive control can establish absence; access-denied or failed queries cannot. PID reuse records the original identity gone and classifies the replacement independently. Unknown birth or a persistent unresolved identity refuses. No fleet-process termination is permitted.
- **Evidence.** Fourteen discriminator cases cover pre-rig exemption, native image resolution, known rig refusal, disappearance versus denied/failed queries, dead-provider control, unknown/equal/precision-overlap births and PID reuse. The final successor passed all fourteen. A read-only live run admitted **17** census hits, including 52860 as `PREDATES_RIG`; actual native self controls measured a **5-tick** CIM/native birth delta, admitted the other-image case and refused the same-image case. Proof: `W8TQ3MZC-census-controls-final.json` and `W8TQ3MZC-census-live-result.json` beside the receipt. Neither proof invoked the product or mutated the rig.
- **Rule.** Admission asks whether an observed identity can belong to the owned rig, not whether the caller can read every process on the host. Preserve query failure, confirmed absence and replacement identity as different outcomes, with pid and birth in the receipt. Re-pin changed instruments; preserve failed windows and obtain a fresh grant rather than extending their clocks.
- **Kin:** [[IR-107]] (live operational context enters the rig), [[IR-129]] (creation-time type/precision), [[IR-125]] (known-present control for absence), [[IR-15]] (identity-aware liveness).
- **Ripe when:** a rig shares a host with elevated perches or short-lived CLI traffic. **Size:** birth-aware census and retained native identity probe, with fail-closed boundary controls.

### IR-133 — a live capture cannot be read with read-only sharing, and daemon stderr belongs to the owned home

- **Status:** Corrected in the r9 rig instrument; genuine fifth-window promotion completed with exit **0**, owned shutdown and final provenance. The subsequent field attempt failed its separate setup-handoff deadline, not this promotion read. Fourth #297 preparation measured by hertz on 2026-09-14; sharing failure localized by doyle in `R2VK8FQN`, run-identity binding ruled in `ANR522S5`, successor accepted in `GV3R6QUV`.
- **Instance.** Fourteen admission leaves and genuine staging passed. The candidate applied daemonlessly, transitioned `applied-pending` → `applied`, brought up broker **14704** and brain **45220**, published the pinned generation-zero readiness record and emitted `BRAIN_PROMOTED` before the instrument stopped its owned job. The instrument returned only `OWNED_PROMOTION_READINESS_FAILED`. Six provisioning/promotion native jobs ended `CONFIRMED_GONE`, active counts zero; all nineteen signing-private files were absent. No field, GO or D3 occurred. Original receipt: `.spt/preserved/hertz-fp-driver-review/d2/W8TQ3MZC-FINAL-REFUSAL.json`.
- **Mechanism.** The loop used `File.ReadAllText` on a capture held open for **Write**, with **Read** sharing, by the run-scoped launcher. Windows checks sharing in both directions: the new reader's default **Read** sharing excludes the existing writer, causing `IOException`, `0x80070020`. A completed step hid this defect because its writer had already closed. Changing only the writer's sharing does not repair the reader.
- **Wrong source behind the exception.** The launcher's stderr capture was zero bytes; the daemon redirects stderr to `SPT_HOME/logs/daemon.stderr.log`. Reading the empty capture with compatible sharing would merely reach the later marker timeout. Read the actual owned-home log with `FileStream(Open, Read, FileShare.ReadWrite | FileShare.Delete)`, matching the existing native launcher's `ReadText` pattern; release each read handle before the next poll.
- **Run binding.** A promotion marker counts only after the exact brain header naming the measured membership PID and readiness generation, and before another process header. Markers before that header, without it, or under another PID/generation refuse. A rolled `.1` can contribute only when absent before this owned launch and appearing during it, with the same header binding; preexisting backups cannot prove this run.
- **Failure evidence.** Attach the instrument's constant assertion name and call-site line to its exception. Before cleanup can replace the active error, retain that name, source line, exception type and HResult. Non-assert exceptions have a null assertion name. Never serialize exception messages or environment values.
- **Qualification.** Thirteen controls passed against `provision-promote-R2VK8FQN.ps1`: a live-held writer reproduces `0x80070020` with `ReadAllText`, the shared reader returns its exact bytes while the writer remains open, header/PID/generation/ordering and rolled-history boundaries refuse stale evidence, the actual rollback guard still refuses, and diagnostics omit a message canary. Proof: `R2VK8FQN-live-held-controls/result.json` beneath the receipt directory. No product invocations, rig mutations or fleet terminations.
- **Live verification.** `provision-R2VK8FQN/promotion-complete.json` records `PROMOTION_COMPLETE`; the supervised promotion exited **0** after **12.9 s**. The real candidate passed the shared-log, PID/generation marker, final membership, durable-state and owned-shutdown checks. Its later field ledger remains a separate failed observation contract; successful provisioning does not turn that ledger green.
- **Kin:** [[IR-132]] (instrument census refusal), [[IR-130]] and [[IR-131]] (phase labels lose the failing predicate). **Ripe when:** polling any live writer or accepting logs as run-specific evidence.

### IR-134 — request latency consumes the elevated capture's execution budget, then a late terminal receipt leaves completed work unmeasured

- **Status:** Instrument correction qualified without a sixth field window. Doyle accepted the execution-clock repair in principle under `HHDKCRH5`, then identified its still-insufficient receipt budget; the derived-window successor follows ruling `P9RLK4VU` and accepted arithmetic `KZG4IN37`. Candidate/packet/nonce/driver/runner repinning remains one future amendment, separate from field authorization. Fifth #297 field run `20260914T063552Z` remains failed and immutable. Its candidate's independent **PRODUCT RED** finding (`M5XKQ2DN`) is unchanged.
- **Measured sequence.** Setup request `06:41:26Z`, elevated setup `06:43:18Z`–`06:43:24Z`, refused capture launcher `06:43:25Z`–`06:43:26Z`, receipt about `06:43:27Z`. The driver had written the capture deadline from request time: 112 seconds of handoff delay left the launcher's `deadline - now - 5` allocation at **−4 seconds**. No native t1 capture ran. The handoff expired before the receipt; cleanup reread its nonce and exit but retained the earlier `timestamp_order=UNMEASURED`, withholding mutation despite subsequently available terminal stamps.
- **Clock correction.** The executor initializes `setup-start.utc`, `elevated-leg-start.epoch` and `d2-t1.deadline` immediately before `run_exactly`, from one UTC observation, with **CreateNew** writes. The independent allocation is **120 seconds**: the existing 90-second capture ceiling, 9 measured seconds around a refused capture, 5-second termination reserve and 16-second margin. Nine seconds is not a measurement of a successful capture. PREPARE initializes nothing; notification latency consumes only the separately bounded handoff. No preparation or activity window is reset or extended.
- **Terminal evidence correction.** On handoff expiry, cleanup makes one final nonce/exit observation and, if terminal, rereads the six stamps before deciding whether completion is proven. A late observation preserves `handoff_wait=EXPIRED` and the original timeout exit separately from `setup_terminal_exit`; it never resumes activity or treats lateness as timely acceptance. Missing, duplicate or wrong nonce evidence and missing or reversed stamps remain unproven. An exceeded leg clock refuses the capture contract independently of terminal completion.
- **No-field proof.** `.spt/preserved/hertz-fp-driver-review/d2/AQ23KKNI-controls/` preserves the actual extracted predecessor/successor predicates, emitted PowerShell initializer, nine receipt/product controls, five clock/ledger controls and clock results. The predecessor retained UNMEASURED after receipt publication; the successor recovered ordered completion without overwriting timeout. The actual initializer refused a second initialization without changing any clock bytes. With the measured 112-second notification delay and capture at leg-start +7, the old allocation is −4 seconds and the new allocation is 90 seconds. No product, firewall, signing or field operation was invoked.
- **The outer-clock blocker, not fixed by the inner clock.** A receipt follows execution: **112 + 120 = 232 seconds** cannot fit the retained 120-second setup handoff. Keeping a 300-second activity window and refusing every GO would be a correct refusal of the wrong shape, not an executable field design. `P9RLK4VU` therefore re-derives future activity as **630 seconds**: pre-dispatch **74** (54 observed +20 margin), setup handoff **240** (112 latency +120 leg +8 margin), post-receipt probes **200**, t2 **100**, final transition **16**. Only setup supersedes the prior 120-second handoff cap; other handoffs retain it.
- **Measured basis and explicit allowance.** The fifth timeline records portfields calls at up to **45 seconds**, census **46 seconds**, and t3 **25 seconds**; the earlier `20260913T041410Z` capture also took 25 seconds. Two analogous portfields calls plus census give **136 seconds**; another **64 seconds** is a labelled allowance for identity capture, D1 and bookkeeping, not a claimed complete post-receipt measurement. T2 receives a **90-second native ceiling**, **5-second launcher reporting reserve** and **5-second shell/reporting margin**. The post-receipt probe deadline cannot spend t2's allocation; t2 cannot spend the final transition reserve.
- **Derived-window controls.** `P9RLK4VU-controls/result.json` records **16 passing controls**, executing extracted real Bash predicates and the wait loop against a virtual wall clock. The complete 630-second window and the measured 54-second pre-dispatch cost pass; 555 seconds remaining fails against the 556-second dispatch requirement, while 556 fits. The actual gate refuses before GO/mutation markers. A receipt after 112-second latency plus 120-second execution fits the new setup allowance; PREPARE and cleanup retain their 120-second caps. Published request and phase deadlines still truncate waits. Preparation remains **600 seconds** and cleanup remains **180 seconds including its 30-second ledger reserve**; an observation deadline cannot override cleanup. No product or field operation was executed.
- **Separate product finding.** Candidate `b8482445` logged one `verify-query` completed at **2650 ms**, followed by a second pre-write snapshot killed at **3027 ms** under its **3000 ms** budget; no reconcile-write ran. The budget **starts before spawn and includes output collection** (`run_ps_inner`, source timing supplied by doyle in `IRXYIJHS`); spawn is not outside this clock. The successor ledger records this query kill separately from handoff/capture failure. Fifth-window cleanup was subsequently closed on existing measurements as **NOTHING_TO_REMOVE**; its original PENDING ledger was not rewritten.
- **Kin:** [[IR-126]] (execution receives leftover scheduling time), [[IR-124]] (cleanup must not race unproven setup). **Rule:** measure execution from execution, preserve timeout and late completion as separate facts, and do not let an instrumentation repair erase a product failure.

### IR-135 — checker pin and runner installation can disagree, and the repair path disguises that version mismatch as an authentication failure

- **Status:** CI precheck remedy implemented in the IR-135 lane; workflow execution awaits integration. Measured by doyle in S4 golden `34946493637`, 2026-09-15; filed by hertz from `3HHHPWCR`. The immediate box mismatch was repaired by hand-install.
- **Instance and mechanism.** The #304 candidate carried `71caedd4`, setting `WANT=0.4.1` in `golden.yml`/`ci.yml`, while kitsubito's `~/.local/bin/traceable-reqs` was **0.2.0**. The install step's mismatch path attempted a private clone without a provisioned `TRACEABLE_REQS_TOKEN` and exited **128 before any check**. The visible failure named authentication, not the wanted and installed versions. A checked-in pin is not evidence that the runner can supply it; the fallback requires a second, independently missing prerequisite.
- **Implemented remedy (QD54OM7S).** Both workflows run an explicit pin/runner precheck and use `.github/ci/traceable-reqs.sh` as the single pin/install owner. It reports the actual version and native version exit; matching requires literal equality and exit zero. A mismatch without `TRACEABLE_REQS_TOKEN` refuses before clone/build, naming the version disagreement and missing credential separately. With a token present, source repair remains available and native clone failure is preserved; token presence never claims access. The installed version is checked before coverage. On kitsubito, eight isolated shell controls passed: matching precheck/install without a token, stale/missing/failing/lookalike versions refused, authenticated repair admission, and clone exit128 retained. The predecessor failed the stale/no-token discriminator with128; the successor refused with1 and both diagnostics. A separate smoke used kitsubito's real0.4.1 binary through both entry points without a token, exit0. No live credential was provisioned or private-source build exercised. Contract and reproduction command: `docs/TRACEABILITY.md` §CI checker admission and installation. PAT provisioning remains the operator's decision, banked since2026-07-30.
- **Kin:** [[IR-104]] (the checker version changes what counts as evidence), [[IR-109]] (the #304 pin is already live). **Ripe when:** the next checker pin or golden-intake/install-step change. **Size:** a version diagnostic and intake assertion; credential provisioning is separate operator work.

### IR-136 — a broker bound under the seed socket's name is not seed control, so a URL-minting rig can omit the very listener contract the product queries

- **Status:** Instance fixed in S5 **`ddd7fc7f070667524578c282894dd44a2e36062f`**, test-only atop S4; S5 golden34957614641 passed, including both twohost roles. Shared fixture-composition remedy implemented and Linux-verified in the IR-136 lane. Origin: doyle's S4 golden `34946493637` twohost helper red, 2026-09-15.
- **Mechanism.** Role B hosted broker IPC under an invented name, with neither `seedmap::serve_seed_control` nor a docs listener. `REQ-WEB-URL-BOUND-PORT` requires `serve_for` to ask the canonical seed-control plane for `DocsStatus` before publishing a URL; the missing plane returned `DOCS_PORT_DISCOVERY_FAILED: Connection refused (os error 111)`. Merely renaming a Broker to `seed_socket_name()` does not provide that protocol: `serve_request` is dispatched by seed control, not Broker. The docs-server fixture had already encountered the same trap. The last green twohost run predated this bound-port contract.
- **Discriminator and scope.** Todlando's unchanged-S4 Linux fixture failed at the exact discovery seam without seed control/listener (**native 101**) and returned `Registered` at the actual bound port **33319** when those fixtures were added (**native 0**); both arms retained the invented B broker name. Evidence: `.spt/preserved/304-web-helper-rca/linux/`. S5 gives B distinct canonical broker/seed sockets, real seed control, a broker-backed ephemeral docs listener, and a discovered-port equality precondition. Helper expectations stay unchanged. A's helper cell relays B's returned URL and does not query its own DocsStatus; its existing broker-on-seed-name path is not this failure. The seven unopted-in Windows passes do not exercise the role-gated twohost bodies.
<!-- [doc->REQ-SERVING-FIXTURE-CONTROL-PLANE] -->
- **Shared remedy (P2Y2GWUP).** `crates/spt-daemon/tests/support/serving.rs` provides `ServingFixture`, included by both `docs_server_e2e` and `twohost_web` rather than exported as product surface. `start` composes the distinct canonical broker and real seed control, waits for seed readiness, binds broker-backed docs on port0, and asserts that seed-control `DocsStatus` returns that actual port. The caller supplies its private home, journal and optional NetHost; socket spelling is no longer a caller choice. `stop` stops and joins both IPC accept loops. The docs listener remains process-lifetime under the existing production interface, so tests require process-per-test isolation and one broker-backed listener per process.
- **Negative-state coverage retained.** The docs fixture uses the separate `control` stage only to distinguish absent control (offline fallback permitted) from running control without a listener (fallback refused); its normal serving case uses the same complete constructor as role B. Role A's seed-named proxy Broker and the standalone docs-less listener remain intentionally separate: neither is the owner's URL-minting control plane. No default/configured port substitutes for discovery, and no production protocol changes.
- **Shared-fixture proof (2026-09-15).** Kitsubito, private tree/target, jobs2: targeted clippy with `-D warnings` **0**; nextest, both migrated binaries, one test thread and zero retries **0**. All **3 docs cases** exercised their real bodies: absent-vs-unbound discovery, HTTP published-surface/security checks through the complete fixture, and the standalone docs-less pointer. The other **7 twohost cases** returned through their unopted role gate; these are compile coverage, not a new cross-host execution claim. Traceable0.4.1: **920complete/0findings**. Raw exits/output/hashes: `.spt/preserved/hertz-ir136-fixture/linux-v2/`; the initial wrapper PATH failure started no cargo and is preserved separately.
- **Ripe when:** another URL-producing fixture is added. Use the shared complete constructor rather than rebuilding its three planes independently; use the partial stage only when missing-listener behavior is the test subject.

### IR-137 — a redirected Start-Process child can share the tool call's console lifetime and die when that call returns

- **Status:** RETIRED, 2026-09-15, doyle ruling `YPLFG3CG`: **no tracked callsite; discipline recorded + receipt**. This closes a Windows ad-hoc rig-craft item, not a CI or candidate defect; no helper or product change.
- **Mechanism and rule.** `Start-Process` with redirected output from a PowerShell tool call was observed to end with **`STATUS_CONTROL_C_EXIT`** when the tool call ended: a returned child PID did not establish detachment. The reported detached form uses a **hidden window without redirects**. Where a child must outlive the call, qualify its survival after the caller returns and give it a durable output path of its own; do not treat process creation or a hidden window alone as lifetime evidence.
- **Origin clarified (YPLFG3CG).** Claude Code PowerShell tool host, 2026-09-15T07:17Z, #304 S3 Windows attempt-2 launch A: redirected `Start-Process pwsh` PID47560; `rustc` exited `0xc000013a` seconds after the tool returned, driver receipt contained only its start line and no refusal file. The same driver relaunched with `-WindowStyle Hidden -PassThru` and no redirects (PID37536) ran25minutes to exit0. There is no tracked failing launcher to repair; the existing tracked g6 launcher already uses the reported good shape.
- **Discipline and receipt.** [Windows ad-hoc rig launch discipline](GOLDEN-CI.md#windows-ad-hoc-rigs-that-outlive-the-tool-call) states the shape, child-owned durable output and post-return PID/birth/output qualification. Preserved harmless-child receipt: `.spt/preserved/hertz-ir137-caller-return/receipt.json`, SHA256 **`e1c9b207544e6313eac5421ebb692786ec18b3a165756b451bff3a704899b178`**. Both valid arms survived under **OMP**, wrote post-return events and exited0; an initial observer-handshake timeout is excluded. All3probe children ended and native handles closed. This does not reproduce the **Claude Code** failure; the host boundary stays explicit. Reopen only for a newly owned callsite or contrary evidence, not to build an unused helper.