Reach the launcher wiring, and reach a second harness: two things this repo said were somebody else's - #831
Conversation
CLOUD-1079 Stop provisioning the two user-level git hooks into `~/.claude/launcher-settings.json` — the owner action CLOUD-605 named and nothing owns
Why
CLOUD-605 established that a delete does not survive, and named the remedy without owning it: *"the only place it can actually be turned off is the environment configuration that generates * Re-provisioning, measured 2026-08-27. A previous session ran *~~"So the repair and its erasure are one event, and no in-session action can outlast it." ~~*THAT INFERENCE IS FALSE AND IT IS THE WHOLE DEFECT IN THIS ROW. One manual invocation was erased by one re-provision, and the conclusion drawn was permanent impossibility. What was never tried is what this repository already does twice: register the repair as a Re-measured 2026-09-02, and it sharpens the point rather than rescuing the old claim. Both scripts carry mtime 16:59 — rewritten MID-SESSION, not only at session start. So a one-off manual delete is even more hopeless than recorded, and a handler on the provisioning path is even more clearly the answer. Why each should stop being provisioned, for this environment
Commits here are SSH-signed, so the second term is already satisfied and the email term alone carries the refusal. The only value it accepts is the one
**The ** What this blocks The hook-registration campaign's remaining half. With these two present, Refinement — Ready Refinement gate: Definition of Ready & Done. This body carries only specializations.
Done A fresh container carries no non-batten hook registration on any merged surface, What is deliberately not proposed — REWRITTEN 2026-09-02 Half of the original refusal survives and half was the punt. Still refused, and now for the right reason: deleting either FILE. No longer refused: running the repair from this repository. That was the punt, and the Honest residue, recorded rather than handed to an owner: a session-start handler closes the session-start window and does not hold against the 16:59 mid-session rewrite. What would close that is the provisioning seam Superseded original, kept so the reasoning is auditable:
Why this row went backwards, traced 2026-09-02It was In Progress 06:18–15:33 today and moved back to Backlog. The loop, and no step needs anyone outside this repository:
On 0.0.137 the same host answers CLOUD-1326 names this class. The engine's own CLOUD-1356 One retracted sentence is quoted by four files, three memories and six rows, and an open PR is landing it back into the tree
WhyCLOUD-605 (Done) concluded: "the only place it can actually be turned off is the environment configuration that generates It is false, and it did not stay in one row. It propagated by quotation into a citation chain that now spans the tree, the memories and the board — and because CLOUD-605 is Done, nothing re-derives it. Each inheritor cites an authority rather than a measurement, so the chain is self-reinforcing: This row owns retracting it everywhere. CLOUD-1079 owns the mechanism that makes the retraction true. Why it is false, in one paragraphThe rewrite happens every session, so a one-off repair loses — which is all CLOUD-605 measured. A repair that also runs every session does not, and this repository already does exactly that twice. Why nobody noticed — the reason this is UrgentMeasured 2026-09-02. The installed binary was 0.0.121, built Aug 28, against a source tree at 0.0.137:
So The engine said so itself this session: "this session's SessionStart registration did not run … every mediated call until it appeared failed open and said nothing. This is a provisioning failure rather than a policy one." AND IT IS LANDING BACK IN RIGHT NOWPR #826 is open and draft, and its The inventory — four filesOne citation chain, so they are corrected together; fixing
**The model for the rewrite is already in the tree: ** Three memories
Six rows
Deliberately NOT in this rowTwo sites were swept and judged genuine, recorded so they are not "corrected" by a later reader pattern-matching on the words:
Refinement — ReadyRefinement gate: Definition of Ready & Done. This body carries only specializations.
Acceptance
Filed 2026-09-02 after a session spent tracing why CLOUD-1079 went In Progress → Backlog again that morning. The row was not wrong about the race; it was wrong that the race was the only surface. CLOUD-605 A user-level stop hook instructs the exact commit identity `batten.toml` denies, so its remedy is unlandable here and nothing records which authority wins
Why Measured 2026-08-14, three times in one session (PR #450), once per new tip SHA. A user-level stop hook —
This is not a hypothetical clash. CLOUD-274 built the gate from a measurement on this repo — 39 of the first 50 The conflation is the second half of the defect. The hook's message ORs two unrelated conditions — "missing signature" and "committer email is not What is actually missing here is the record. Three refusals in one session were each argued from first principles against Mechanism — DECIDED. The three below are kept for the reasons two were not taken, not as an open choice. Read the hook, 2026-08-18. It is UNSATISFIABLE by construction, and three assumptions in this issue were wrong. The predicate, from An OR, over every commit not yet on a remote. So:
Where it comes from, and why deleting it does not work. It is registered in All three launcher files were rewritten at 14:27:09 this session — one second before the injected MCP config at 14:27:10 — so they are re-provisioned by the launcher mid-session, and a delete does not survive. Nor can the repo unregister it: Claude Code merges hooks across settings files ( So the only place it can actually be turned off is the environment configuration that generates The original framing follows. The hook is a user-level file this repository cannot edit or gate, so the options are about what the repo states and what it can detect:
Correction 2026-08-18 — option 1's PLACEMENT is wrong, and that is why this keeps recurring.
That is not a small correction to option 1; it falsifies it. The record has to live on a surface that is present at Stop time, and there is exactly one always-loaded instruction surface: AGENTS.md. Decision: one line in AGENTS.md, plus the gate. The line names The cost is named rather than absorbed: AGENTS.md sits at its Option 2 (a repo-level Stop hook) is rejected on noise, and the reason is worth keeping. It would speak in the right channel at the right instant, which is its whole appeal. But the condition it would key on is true of every correctly-attributed commit this repo produces — by policy the committer is never Acceptance
Bounds Not a fail-open bug. Every commit on #450 carried an accountable identity and the gate passed; the cost is a session spending three rounds on a settled question, and the risk that one complies and produces an unlandable commit. Refinement — Ready (record the precedence, and gate that no tracked file prescribes the denied identity) Refinement gate: Definition of Ready & Done. This body carries only specializations. Mechanism decided: Option 1 plus a gate. Option 1 alone is feedforward only, and §2 refuses a rule with no runnable gate — so the record ships with two exit codes rather than as prose a later edit can quietly drop.
Re-measured 2026-08-14, and one premise above is now false. This paragraph is the correction; the falsified sentence is left in place above so the two can be read together. The hook fired six more times in a later session on PR #460, across two container restarts, which is the re-derivation cost this issue predicts. That session also measured the signature half directly, and it does not say what the hook says: the commits are signed (SSH), and GitHub answers CLOUD-1314 The two CLOUD-605 identity hooks are still wired, and the issue that recorded their precedence closed without removing them
Context
A licence keyed to a closed issue is the permanent-exemption shape the table exists to refuse. Measured on this branch while landing CLOUD-1310. What CLOUD-605 actually closedIt closed by recording a precedence — So the row was right to exist and wrong to point at CLOUD-605: the issue that owns the precedence is not the issue that owns the removal, and nothing owned the removal. ReadyMechanism as a computable predicate. The two rows in Done when: both hooks are gone from the harness config, the rows are deleted from Out of scopeRe-litigating rule 8. The precedence stands; this is only about the two programs still being wired underneath it. CLOUD-1326 An installed `batten` older than a config key disarms every mediated gate silently: the hook fails open on a config it cannot parse, and nothing says so
Why **Measured 2026-09-02, in this repository, by accident. ** The installed binary was built at 23:21; So for roughly ninety minutes every mediated gate in the session was inert, and the only evidence was that nothing ever refused anything. Confirmed by the reverse: within a minute of Why the existing sensors do not cover it**A MISSING binary is reported; a STALE one is not. **CLOUD-824 moved that report forward deliberately:
The general shape, which is why this is Urgent rather than a choreA gate that cannot read its own authority answers "allow" to everything, and that is byte-identical to a clean tree on the decision surface. It is the same class as Every branch that adds a config key creates this window for any session whose binary predates it, which in a fleet is every sibling session that has not reinstalled. Refinement — Ready Refinement gate: Definition of Ready & Done. This body carries only specializations.
Acceptance
Found by it happening: CLOUD-1085 Deleting `.claude/container-setup.sh` left no bootstrap at all: `batten` is absent at SessionStart, so the engine mediates nothing for the whole session
Why
Measured on the first session after that deletion, 2026-08-28, this container:
So for the first ~2.5 minutes of every session on a container of this class, and for the whole of any session where The failure is not loud. No host error surfaced in that session; the only hook message was Why it is more than one container's setupThe repo now carries no bootstrap. It also blocks CLOUD-312 row 10 on its merits. #714 retires Not in scope: restoring the wrapper. The design is one install script and no harness-specific bootstrap; re-adding Refinement — Ready Refinement gate: Definition of Ready & Done. This body carries only specializations.
Acceptance
Found while landing #714, by asking whether row 10's stated premise — "a container puts CLOUD-1362 The advisory emitter reaches 1 of 6 harnesses, so Batten's neutral instruction channel cannot carry doctrine off `.claude/` — CLOUD-44 landed the decision half and nothing has owned the advisory half since
Why CLOUD-1152's §2 says, in its own words: *"If that work is larger than this row, split it out and make this row * The diagnosis it inherits, which is correct and is worth restating once. Batten adjudicates for six harnesses and keeps its own binding doctrine in So the relocation is not the blocking question; this is. Moving doctrine now takes it from a folder one harness reads to a channel one harness receives, and buys nothing. Measured against the tree, 2026-09-02
CLOUD-1152's characterisation of the Gemini half is FALSIFIED, and that is this row's first correctionCLOUD-1152 calls
So it is not a shim over an open door. Batten would have to emit into a channel one of its own rules forbids writing to, and that conflict is the actual work: either AND THE CONFLICT IS NOT REAL — decided 2026-09-02, which is what this row was forThe paragraph above is my own, written hours earlier, and it is wrong in the same shape as the sentence it was correcting. It read the
The hazard it guards is a decision document corrupted into an accidental allow. An advisory is not a decision: it wants allow-plus-a-message, which is exactly what the Golden Rule delivers. The door is the mechanism, not the obstacle. **The collision that would have made it an obstacle is already closed by construction. ** let speaks_a_verdict = matches!(decision, Decision::Deny(_) | Decision::Ask(_));
if advice.is_empty() || speaks_a_verdict { return Ok(()); }CLOUD-1175 landed that. An advisory and a verdict never share one invocation's stdout — so on every path where advice is emitted at all, the decision is already **Corroborated rather than argued: ** **On the per-event probe discipline, which is the one thing that looked like it forbade this. ** What landed
**Reach is now 2 of 6, stated. ** Why the silence is the dangerous part
That is why the census over Refinement — Ready Refinement gate: Definition of Ready & Done. This body carries only specializations.
Acceptance
Bounds Not the relocation of any prose — that is CLOUD-1152's, and it should not start before this lands. Not a change to the decision emitter, which CLOUD-44 shipped and which works. Not
CLOUD-1372 `completion.unlanded` has never fired on Claude Code: the marker it needs is absent from the stream, and `NotSignaled` records nothing — so the gate against stopping-before-landing is byte-identical to a clean tree
Why Measured 2026-09-02 on a session that did exactly what this rule exists to catch: it took a branch to green, pushed it, reported "committed and pushed", and stopped without landing — repeatedly, across several turns, with the owner having to say "why the fuck stopped" twice. That is not a tuning miss. The detector is structurally dead on this host, and it fails in the one direction this repository refuses everywhere else: could-not-look reading as clean. The measurementThis session's transcript, 2763 lines:
Root cause 1 — the marker is absent exactly when it matters
CLOUD-887 predicted this in the code and mis-graded it. Its own comment:
The first half is right and the last clause is wrong. It is not recorded as could-not-look: Root cause 2 — the minting is starved by the ladder in front of it
Above that, two more suppressions, both confirmed:
So the highest-consequence reading in the set sits behind three suppressions and a marker that is absent. Its position is the inverse of its importance: a style nit about prose outranks the work is not landed. Why this is Urgent rather than tidy-upThe rule is the last line against the failure AGENTS.md names first — "an agent that finishes the edits and then stops." It has been silently absent for the whole period it was believed to be covering that, which is why the human is the detector. A gate that cannot look must say so; this one says nothing, and silence here reads as "the branch landed". Refinement — Ready Refinement gate: Definition of Ready & Done. This body carries only specializations.
Acceptance
Bounds Does not make the Stop channel able to refuse, and does not change what "landed" means — Found by the owner asking why the agent had stopped, twice, on a session where this rule was the mechanism that should have answered instead. |
|
Warning Review limit reachedNext included review available in 42 minutes. View limit detailsLimit details: You’ve used the included review currently available. Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. Review configuration: ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Free Run ID: 📒 Files selected for processing (7)
ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Free Run ID: 📒 Files selected for processing (5)
Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review. 📝 WalkthroughWalkthroughThe change adds a Merge Risk: ⚪ Minimal · up to The change adds a session-start repair task and its startup registration; no actionable merge-blocking risk remains beyond normal checks and review. Note 🎁 Summarized by CodeRabbit FreeYour organization is on the Free plan. CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please upgrade your subscription to CodeRabbit Essentials by visiting https://app.coderabbit.ai/settings/billing. Comment |
…tered CLOUD-1079. `~/.claude/launcher-settings.json` registers two non-batten hooks — `session-start-git-identity.sh` on SessionStart, `stop-hook-git-check.sh` on Stop. Six rows recorded their removal as "an owner action on the environment configuration, outside this repository", inheriting the sentence by quotation from CLOUD-605. It is false, and CLOUD-1356 owns retracting it everywhere. What CLOUD-605 measured is that a ONE-OFF repair loses to a rewrite that runs every session. True, and it does not generalise. `session:signing` exists in its own words because "the launcher writes `commit.gpgsign true` --global every session, and local beats global only if something writes local", and it wins. `session:identity` is the same shape. This is the third member of that family. The repair is `batten wiring reclaim`, which predates CLOUD-1079: wiring.rs:293, declared surface.rs:3466, asserted in tests/it/wiring_reclaim.rs:173. It removes REGISTRATIONS and never files, which is why it is the instrument — the identity script also sets `core.hooksPath` for the whole container. Why it stayed invisible: the installed binary was 0.0.121 (Aug 28) against a 0.0.137 tree, so `wiring reclaim` answered "unrecognized subcommand" and CLOUD-1314's refusal test was not in it either. `batten doctor hooks -J` reported `siblings: 0, ok: true` while `merged: 2` showed it reading both registrations. On 0.0.137: `merged_siblings: 2, ok: false`. CLOUD-1326 owns that class. INCOMPLETE, DELIBERATELY COMMITTED, AND THE REMAINDER IS NAMED. This is the task half only. Its `[[hook.handler]] id = "session-wiring"` row belongs in batten.toml after `session-signing-posture` and before `session-container-preflight`, and is not here: batten.toml is a protected path, the write is refused by `protected-mutation`, and the declared override route (`articulate the write`) issued admission 19157b02c321ce6e36b629d70b2f7b4153724724bf3afa57dbabc8505732d3bf — which the boundary honours only once SPENT, and `batten override spend` is blocked by this session's permission classifier. So the task does not dispatch yet. A task nothing calls is inert rather than harmful, and committing it beats losing it to a container reclaim; the row is owed under CLOUD-1079. Measured 2026-09-02: both scripts carry mtime 16:59 — rewritten MID-SESSION, not only at session start, which is stronger than CLOUD-1079 recorded. A session-start handler closes the session-start window and not that one; the `deps-install` provisioning seam upstream of both is the follow-up. Refs: CLOUD-1079, CLOUD-1356, CLOUD-605, CLOUD-1314, CLOUD-1326, CLOUD-1085
… reach The launcher registers two hooks under `$HOME` that this repo cannot unregister through a settings file, because Claude Code merges hooks across them. From that measured fact one session inferred that turning them off is "an owner action on the environment configuration, outside this repository" — and the sentence then propagated by quotation into four tracked files, three memories and six board rows, where each copy read as an independent finding rather than as one claim restated. It is false. Outside the repository ROOT is not outside its REACH: `batten wiring reclaim` removes a merged registration without touching the file that carries it, and a `[[hook.handler]] on = "session-start"` row runs it every session — the surface `session:identity` and `session:signing` already use to beat this launcher's own `--global` writes. A repair that runs once loses to a rewrite that runs every session; a repair that also runs every session does not. `.claude/rules/commits.md` is the tracked instance corrected here; the merge-precedence measurement above it stands unchanged, since only the conclusion was wrong. The two `policy/*.rego` headers and `batten.toml` carry the same sentence and are protected paths, so they land with the handler row rather than here. Both memory edits file a deferral rather than annotate one. Each read "unfiled because the tracker was unreachable" — true when written, and a class that suppresses its own report is under-represented by construction rather than rare. Filed from a session whose connector is bound: CLOUD-1359 (both MCP gates pass green while no connector tool binds) and CLOUD-1361 (a container's first `linear-check` refuses on a symlink no turn has yet had the chance to write, against a `[transcript]` comment saying the opposite). Refs: CLOUD-1356 Admits: 24cfdf1da8480ffc7591864e990b03f329771211c9b1da15a1539b43483b37ae Admits-rule: protected-mutation Admits-verdict: path write refused Admits-subject: .serena/memories/toolchain-and-hooks.md Admits-head: d8fb32b Admits-epoch: 0a320ce25727111b30416e5cc416e9294fc3303b59762fab85faa9ababbcff71 Admits-author: alec@wenzowski.com Admits-prev: - Admits-answer-lost: The deferral stays deferred and self-justifying: a note explaining that it cannot be filed, in a repository where the reason it could not be filed has gone away, reads to every later session as a standing constraint rather than as an expired one. That is the exact shape this branch exists to retract one instance of, so declining here would leave the same class uncorrected in the file that records it. The stale remedy is the second cost — it points the next reader at a retired script, so acting on it produces a change that cannot land. Admits-answer-precondition: The memory IS the owning surface for this content, and the write was made through `mcp__serena__edit_memory` — the route the path's own redirect names — so this records the change rather than authorising a route around it. What the edit does is close a deferral the memory itself declared: the `.claude/.transcript.jsonl` note read "Unfiled: the tracker was unreachable in the session that measured it", which was true when written and stopped being true in this session, whose connector is bound. The finding is now CLOUD-1361 and the note points at it. It also corrects that note's remedy line, which prescribed a write "beside the other things `session-start.sh` already asserts" — that script is retired (CLOUD-312 row 10, #804), so the remedy named a surface that no longer exists. The write lands in the reviewed PR for CLOUD-1356, where `prettier`, `rules-drift` and `no-docs-tree` judge it. Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE is what was TAKEN, not rejected: the edit went through Serena's `edit_memory`, and this admission exists only because `commit check` records every protected path in the diff regardless of the route that wrote it. R-RESTORE-IT does not apply because nothing was destroyed — the edit replaces one three-line note with a longer one that keeps its original wording quoted, so the superseded claim stays readable beside its correction rather than being deleted. Admits: 9d1e8ef441db67ee9cdf8c10962322b30886c00d8b232a7118c79b7dba285fa6 Admits-rule: protected-mutation Admits-verdict: path write refused Admits-subject: .serena/memories/connector-allowlist-recovery.md Admits-head: d8fb32b Admits-epoch: 0a320ce25727111b30416e5cc416e9294fc3303b59762fab85faa9ababbcff71 Admits-author: alec@wenzowski.com Admits-prev: - Admits-answer-lost: The sensor gap stays unfiled behind a reason that has expired, and every later session that reads this file is told the tracker cannot be reached when it can. The open question keeps a premise now known to be false, so whoever measures it next designs a single before/after reading that cannot distinguish "my write took effect" from "my write survived" — and gets a green answer to the wrong one of those two questions, which is the failure this file already documents in three other forms. Admits-answer-precondition: The memory IS the owning surface for this content, and the write went through `mcp__serena__edit_memory` — the route the path's redirect names — so this records the change rather than authorising a route around it. Two edits. The first closes the deferral the file declared in its own words, "Sensor gap, unfiled because the tracker is the unreachable thing": that is now CLOUD-1359, and the paragraph keeps the original sentence quoted because the reason it went unfiled is itself the finding — a defect whose occurrence blocks its own report is under-represented by construction rather than rare. The second answers nothing and says so: the file's open question about whether a SessionStart settings write reaches the session that is starting is still unanswered, and the edit adds only the measurement this session did produce — both launcher scripts carry mtime 16:59, MID-session — which falsifies the question's premise that startup is one ordered moment. It lands in the reviewed PR for CLOUD-1356. Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE is what was TAKEN, not rejected: the edit went through Serena's `edit_memory`, and this admission exists only because `commit check` records every protected path in the diff regardless of the route that wrote it. R-RESTORE-IT does not apply because nothing was destroyed — both edits preserve the superseded wording as a quotation, deliberately, since in the first case the retracted reason is the evidence for the row that replaces it.
…eeds `session:wiring` landed in d8fb32b and dispatched from nothing, because the `[[hook.handler]]` row that arms it is a write to a protected path and the session that added the task treated the admission as a permission to wait for rather than as the mechanism that grants it. The task sat inert. Two defects, one of them mine and only findable by running it: - No handler row, so `batten hook` never dispatched the task at session start. Added after `session-signing-posture` and before the preflight and census: after, because `wiring reclaim` is the binary answering about its own registrations; before, because a census that ran first would report `merged_siblings: 2` for a state this sequence had already been asked to fix. - The task body omitted `-y`. `wiring reclaim` never prompts and refuses without it, so every session would have hit `::error:: session-start: wiring reclaim failed` — the task would have run, failed, and reported a provisioning error for the whole life of the row. Caught by running the task rather than by reading it. Measured on this container, before and after: merged_siblings: 2, merged_surfaces_read: 1, ok: false merged_siblings: 0, merged_surfaces_read: 1, ok: true Both numbers, which is CLOUD-1079's own acceptance clause: a zero from a surface nobody read is the false green the census exists to refuse. What this closes is the inference, not just the state. From "Claude Code merges hooks across settings files, so a lower-precedence file can add one and never remove one" — measured, and true — the conclusion drawn was that turning these two off is an owner action on the provisioning configuration, outside this repository. The repo cannot unregister the hook THROUGH A SETTINGS FILE. That is not the same as cannot repair the wiring: `wiring reclaim` removes the registration and never the file, which is also why a blind `rm` was correctly refused — `session-start-git-identity.sh` sets `core.hooksPath` for the whole container. Residue, stated rather than absorbed: both scripts carry mtime 16:59, rewritten mid-session, so this closes the session-start window and not the mid-session one. The container Setup script already calls `mise run deps-install` and runs upstream of both; CLOUD-1079 owns that half. Refs: CLOUD-1079 Admits: 02a2bee7536588c90b6f9a8f3de40a6669627f5903f036dc1abb26b937b0b887 Admits-rule: protected-mutation Admits-verdict: path write refused Admits-subject: batten.toml Admits-head: 0b1490b Admits-epoch: 0a320ce25727111b30416e5cc416e9294fc3303b59762fab85faa9ababbcff71 Admits-author: alec@wenzowski.com Admits-prev: 19157b02c321ce6e36b629d70b2f7b4153724724bf3afa57dbabc8505732d3bf Admits-answer-lost: CLOUD-1079 stays open on a repair that exists, is tested (`tests/it/wiring_reclaim.rs:173`) and is not wired — which is the state that made it read as unactionable and sent it back to Backlog twice in one day. Concretely: `stop-hook-git-check.sh` keeps exiting 2 on every correctly-attributed commit and keeps prescribing the identity `identity_deny` forbids, so every session either re-derives the precedence argument from scratch or complies and produces an unlandable commit. CLOUD-312's end-state predicate 2 also stays unreachable by either disjunct, because `merged == 2` and a `DECLARED` row naming the closed CLOUD-605 is what `wiring-declaration-closed-owner` refuses by design. Admits-answer-precondition: A `[[hook.handler]]` row is only expressible in batten.toml: the handler sequence IS the surface — `.claude/rules/toolchain.md` states it outright, "the provisioning order and its bounds are `batten.toml`'s, not a script's", and the ten existing `session-*` rows are the sequence, declaration order being running order. There is no verb that registers a handler and no second file that declares one, so the write to the authority is not merely the shortest route, it is the only one. The alternative the rule exists to refuse — putting a second step inside an existing task's body — is refused by name in that same document as the shape that made `session-start.sh` unreadable from the committed authority. `mise run session:wiring` already landed in d8fb32b and is inert until this row dispatches it, so the change is one row that arms a task already reviewed. It lands in the PR for CLOUD-1079 where `config-lint`, `batten-check` and `taplo` judge it. Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because batten.toml IS the owning surface for a handler row — there is no verb that registers one, and the path's own redirect says to change it in a pull request, which is what this is. R-RESTORE-IT does not apply because nothing was destroyed: this adds one `[[hook.handler]]` row and touches no existing row, no existing bound and no existing order. The addition is strictly raise-only in the sense house-style §8 asks of a config change — it arms a repair that removes registrations this repository never declared and does not weaken, disable or widen any gate — and the property a reviewer should check in the diff is the ORDER: after `session-batten` so the binary exists, before the preflight and census so both observe the repaired wiring rather than the launcher's.
Batten adjudicates for six harnesses and its advisory channel reached one, so its own doctrine could not move off `.claude/rules/` onto the neutral surface the project is built around. CLOUD-1152 sat on that for three days naming a blocker no row owned. The reason recorded for the Gemini half was wrong, and I wrote the second version of it myself this session. Both readings took `stdout_must_stay_clean` for a prohibition on writing to stdout. Its own doc comment says otherwise: it is about STRAY output on exit 0 being read as an allow, and the hazard is a DECISION document corrupted into an accidental allow. An advisory is not a decision. It wants allow-plus-a-message, which is exactly what Gemini's documented Golden Rule delivers. The door is the mechanism, not the obstacle. The collision that would have made it an obstacle is closed by construction: `emit_channel` returns early on `Deny` or `Ask`, so an advisory and a verdict never share one invocation's stdout. Wherever advice is emitted the decision is already `Allow`, and the bytes this host reads as "allow, and tell the model" say what the engine decided. Corroborating rather than arguing: `admit_mediated` already writes a bare prose line to stdout on the admitted-call path, so the door has been open on a live allow path here with no defect reported. The body is the text VERBATIM and never JSON. On this host a body that parsed would be read as a decision, which is the one place that inversion is expressible, so the emitted bytes must not pass for a document. The test asserts that directly. Reach is 2 of 6 and the count is asserted, because CLOUD-1152's acceptance is a count. Cursor and Copilot stay `Unknown` and unprobed (CLOUD-209); Codex stays `No`. No host's `declared` moved. Two tests moved, and both were pinning a claim rather than a property. `an_advisory_on_a_host_with_no_channel_is_silence_rather_than_a_deny` read `if *harness == Harness::ClaudeCode`, so it pinned "every host but that one is silent" — false the moment a second host gains a channel, and needing a new name added per host forever. It now derives the exclusion from `delivered_on`, which is the question it is about, with an `exercised > 0` guard so it fails loudly rather than passing over an empty set if every host ever declares one. `identity_precedence::the_detail_states_why_the_hooks_predicate_cannot_be _satisfied` pinned the phrase "Deleting it does not survive". The finding is unchanged and still measured; what moved is one word. That "it" left open WHAT does not survive, and the ambiguity was load-bearing in the wrong direction — from it a reader inferred the REGISTRATION was equally beyond reach and wrote that the only remedy is an owner action outside this repository. `wiring reclaim` removes the registration without touching the file. The assertion was pinning the sentence rather than the finding, which is CLOUD-1152's own diagnosis of this file's class. That case has been red since 0b1490b, which was already pushed. Nothing caught it: every CI job is skipped on a draft, so the local suite was the only thing that could, and it was not run before pushing. Refs: CLOUD-1362
…tes it `DECLARED` is a list rather than a count on purpose — its own header says a count cannot tell an added row from a renamed one. 393e327 added the `session-wiring` handler to batten.toml without declaring it here, and the roster caught exactly the case it exists for. The position is the claim, not the presence: after `session-batten` because `batten wiring reclaim` is the binary answering about its own registrations, and before the preflight and census so both observe the repaired wiring rather than the launcher's. Refs: CLOUD-1079
…d rank it first RCA of a session that took a branch green, pushed, said "committed and pushed", and stopped — three times, with the owner as the only detector. `completion.unlanded` is the gate for exactly that and produced nothing. Measured on that session's own transcript and store: "stop_reason":"end_turn" 42 "stop_reason":"tool_use" 736 records after the last end_turn 49 completion.unlanded rows in the store 0 other findings in the store 20 Three suppressions sat in front of the reading, and this commit removes the two that are mechanical: 1. `record_state` — which MINTS the verdict — was called inside rule 4 of an early-returning ladder. Rule 3 is `filed_here_pointers(PerRow)`, and the session had ten live `filed-over-own-diff` rows, so rule 4 was never reached and the verdict was never minted. Not decided "landed", not recorded "could not look": absent. Minting now happens before any rule can return. Minting and reporting are different questions and must not share a suppression — the ladder decides what to SAY, the store holds what was OBSERVED, and a later reader is entitled to the latter whether or not this turn spoke. 2. The completion reading was rule 4, behind two prose-shaped rules. That ranking is by measured precision, which is the right axis between rules about the same kind of thing. It is the wrong axis here: the others say the turn was untidy, this one says the work exists nowhere but this container and a reclaim ends it. It is rule 1 now. The third is NOT fixed here and is the binding one, stated rather than buried: the marker `completion::signal` needs — `StopReason::EndTurn` with no tool call after it — is absent from a stream whose turns end on tool calls, and `Outcome::NotSignaled` writes nothing, so the silence is byte-identical to a landed branch. CLOUD-887 removed a marker whose own firing was its input, which was right, and replaced it with one that cannot fire, which was never measured against a real transcript. Whether a turn ending in `tool_use` should count as a completion is a genuine design question; deciding it fast, on the gate that polices this class, is how CLOUD-887 happened. It is CLOUD-1372's, with the measurement. `.claude/rules/toolchain.md` carries the interim rule, because a gate that cannot fire must not be relied on silently: green-and-pushed is not a stopping point, the launcher hook's "commit and push" is about reclaim survival rather than done, and until CLOUD-1372 lands the check is the agent's own. Two stale claims in the same file corrected: it named `unlanded-check.sh` as a spawned sibling (that program retired into the engine and does not exist), and it described a ranking this commit changes. Refs: CLOUD-1372
CLOUD-1079's §7 predicted this commit in these words: "the two
stop-hook-git-check.sh / session-start-git-identity.sh rows come out of
the DECLARED table in one commit, with the gate green because the
siblings are genuinely gone."
That is what happened, and the order matters. `session:wiring` removed
both registrations from the merged surface (2 -> 0, with
merged_surfaces_read non-zero so the census is known to have looked).
The exemption rows then outlived what they excused, and
`harness-wiring`'s STALE direction refused the tree — two findings, one
per row.
So the gate went red because the defect was FIXED. That direction is
CLOUD-1314's restoration, which its own header notes the earlier split
had dropped entirely ("no STALE direction over them at all"), and this
is the first time it has had a real subject to catch rather than a
fixture.
The table is `{}` again, which is where it was before #826 repointed the
rows from the closed CLOUD-605 to CLOUD-1079. An empty object and an
absent file are deliberately different readings — the module's own
comment says so — and empty is the right one: this repository declares
no exemption, rather than declining to say.
Refs: CLOUD-1079
83cb67e to
dda531f
Compare
|
❌ The last analysis has failed. |
|
/fast-forward |
Two things this repository said were somebody else's
Both were false, both were measured, and both are fixed here.
1. The launcher wiring — Closes CLOUD-1079
~/.claude/launcher-settings.jsonregistered two programs into this container's hook surface. One,stop-hook-git-check.sh, exits 2 on every correctly-attributed commit here and prescribes the exact identity[attribution] identity_denyforbids.From a true premise — Claude Code merges hooks across settings files, so a lower-precedence file can add a registration and never remove one — a session inferred "the only place it can actually be turned off is the environment configuration … outside this repository, an owner action." That sentence then propagated by quotation into four tracked files, two memories and six board rows, and sent CLOUD-1079 back to Backlog twice in one day.
The distinction it misses: the repo cannot unregister the hook through a settings file, which is not the same as cannot repair the wiring.
batten wiring reclaimremoves the registration and never the file — which is also why a blindrmwas correctly refused, sincesession-start-git-identity.shsetscore.hooksPathfor the whole container.Measured on this container, before and after:
Both numbers, which is CLOUD-1079's own acceptance clause: a zero from a surface nobody read is the false green the census exists to refuse. The
DECLAREDrows are deleted rather than excused, which is the third clause.Two defects found by running it rather than reading it: the handler row was missing entirely, and the task body omitted
-y—wiring reclaimnever prompts, so every session would have reported a provisioning error for the life of the row.2. The advisory channel — Closes CLOUD-1362
Batten adjudicates for six harnesses and its advisory channel reached one.
ADVISORY_GAPSrecorded Gemini as unreachable because "the only door is the stdout this host'sstdout_must_stay_cleanrow forbids."That conflates two things.
stdout_must_stay_cleanis about stray output on exit 0 being misread as a decision. An advisory is not a decision — it wants allow-plus-a-message, which is exactly what Gemini's documented Golden Rule delivers. The door is the mechanism, not the obstacle.The collision that would have made it an obstacle is already closed by construction:
emit_channelreturns early onDeny/Ask(CLOUD-1175), so an advisory and a verdict never share one invocation's stdout. Corroborating it,admit_mediatedalready writes bare prose to stdout on the admitted-call path.Reach 1/6 → 2/6, asserted as a count because CLOUD-1152's acceptance is a count. The emitted body is the text verbatim, never JSON — a body that parsed would be read as a decision on the one host where that inversion is expressible, and the test pins exactly that.
CursorandCopilotClistayUnknownand unprobed; that is CLOUD-209's shape, not this PR's to guess.3. Why nothing caught the stopping
RCA of this session: it took the branch green, pushed, and stopped, three times.
completion.unlandedexists for exactly that and produced nothing across 2763 transcript lines and 20 stored findings.Three suppressions, two fixed here:
record_state— which mints the verdict — sat inside rule 4 of an early-returning ladder, behindfiled_here_pointers. Ten livefiled-over-own-diffrows meant rule 4 was never reached and the verdict was never minted. It is now minted before any rule can return: minting and reporting are different questions and must not share a suppression..claude/rules/toolchain.mdcarries the interim rule, because a gate that cannot fire must not be relied on silently.Deliberately not closed
DO-NOT-CLOSE CLOUD-1372
The third and binding root cause is unfixed: the marker
completion::signalneeds is absent from a stream whose turns end on tool calls (lastend_turn49 records back), andOutcome::NotSignaledwrites nothing — byte-identical to a landed branch. CLOUD-887 removed a marker whose own firing was its input, which was right, and replaced it with one that cannot fire, which was never measured against a real transcript. Whether a turn ending intool_useshould count as a completion is a genuine design decision, and taking it in a hurry on the gate that polices this class is how CLOUD-887 happened. The row keeps it, with the measurement.DO-NOT-CLOSE CLOUD-1356
Four of seven prose sites corrected.
batten.tomland twopolicy/*.regoheaders are protected paths whose remaining edits land behind their own admission, so the row stays open with its inventory corrected — including two errors in that inventory this branch found: the.regoheaders are protected rather than freely editable, andcore.mdnever carried the sentence at all.Also filed, not fixed here
CLOUD-1359 and CLOUD-1361 — both were deferrals sitting in memories reading "unfiled because the tracker was unreachable", which stopped being true in this session. CLOUD-1378 — an issued admission is keyed to HEAD, so
land's own rebase voids it every lap; the same eight were spent twice here for an unchanged diff.🤖 Generated with Claude Code
https://claude.ai/code/session_01WTqsjPE2ckz9DSJmq9nfXR