fix(ci): define the deps-install seam the provisioner already calls - #798
Conversation
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 |
|
Warning Review limit reachedNext included review available in 35 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 (4)
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 |
`batten` is absent at SessionStart on a fresh container, so every mediated call fails open in silence for the first ~2.5 minutes of a session and for the whole of any session where the SessionStart hook does not reach its build step. The engine mediates nothing and nothing says so. The bootstrap seam was never missing. The environment's setup script runs `mise run deps-install` on every provision, before any session starts, and soft-fails with a stderr line when the task is absent -- which this repository has never defined. `mise tasks ls` carried `install-check` and `install:local` and no `deps*` at all, so the call has been dangling for the life of the defect. Define it, and its `deps` report sibling that the same script calls. The task runs `install.sh` -- the one install interface -- and never `install:local`, which builds the working tree's binary and is a dev-clone convenience rather than a provisioning path: it assumes a Rust toolchain and a 141-second compile that provisioning cannot. Routing through `mise run` is load-bearing rather than incidental. The setup script shadows `mise` with a wrapper that prepends the GitHub API and asset hosts to NO_PROXY and sets MISE_GITHUB_TOKEN from the session PAT, because the agent proxy answers 403 for third-party release repos. A task body inherits that environment; a bare `./install.sh` in the setup script would not. Verified both arms on this container. Success installs v0.0.137, digest-verified, and is silent at exit 0. A refusal emits the `::error::` line naming which failure it was and propagates install.sh's own code, keeping could-not-look (2) distinct from a decided refusal (1). Two defects found and fixed while writing it, both recorded in comments beside the code: a default inline `run` body executes under `sh -e`, so the first draft's bare call aborted the task before the branch that reports the failure could run; and backticks inside the double-quoted messages would have been command substitution on the binary's own name. Refs: CLOUD-1085
CLOUD-1085's second half. The first closed the cause -- nothing installed `batten` before the first hook fired -- and this closes the silence: a session where that happened said nothing, and an absent reference monitor is indistinguishable from a passing one from outside. The reporter serves exactly two events and seeds the per-session snapshot at whichever arrives first. So a seed at `PostToolBatch` means `SessionStart` never reached the engine, and since the hosts register it by bare name the usual cause is that no binary resolved when that event fired. Measured on the container that produced the row: SessionStart receipt at 04:37:21, binary at 04:39:58, first snapshot at 04:40:48 -- three and a half minutes in which every mediated call failed open and nothing reported it. The mechanism is free. The snapshot is already keyed per session and already written in that branch, so the same write that rate-limits the drift notice rate-limits this one: emitted once per session, never again. Pointer-only and advisory. It rides the existing channel, never exit 2 -- an absent monitor is news, not a verdict about the call in hand. It does not claim which calls preceded it, because this process cannot see them, and it names the provisioning step rather than a policy remedy. RE-DECIDES ONE LANDED CASE, quoted in place rather than deleted. `the_first_batch_of_a_session_seeds_the_snapshot_silently` asserted silence on exactly this condition. Its reasoning -- a session that started after a change has already read the new files -- is right about `SessionStart` and wrong about the case it covered, so it moves to the mirror case and the original becomes the discriminating one §7 asks for: no snapshot entry produces the advisory, an entry produces silence. One stated bound: a consumer that deliberately does not register `SessionStart` would see this every session. The row specifies the simple predicate and it is implemented as written; narrowing it needs the wiring, which `doctor hooks` reads and this reporter should not. Refs: CLOUD-1085
…ry advisory `each_session_is_told_about_what_moved_under_it_and_not_about_the_rest` asserted `pipe_notice() == None` for a session seeded now. The property it owns is ISOLATION -- a session that started after a change is not nudged about one it already has -- and `== None` is a proxy for that which also couples the case to every other advisory the channel may ever carry. The unmediated-start notice makes the coupling bite. This fixture drives `PostToolBatch` only, which is by construction the condition that notice reports, so the fresh session now receives it: a different notice about a different fact, and the isolation claim is untouched. Asserted over what the notice CLAIMS instead. The fresh session must not name the moved file and must carry no change-set; the already-running one is still told. That discriminates the same way and stops the next advisory from failing a case that is not about it. Refs: CLOUD-1085
… slips Three gate refusals from the first land lap, all mine. `inline-task-bodies-not-growing` refused mise.toml 31->33. Both new tasks were nine-line inline bodies. The ratchet's own block records that the wider literal was rejected precisely because a one-line `run` shim is the campaign SUCCEEDING, and reading that made clear the bodies were wrong on the merits rather than merely over budget: each caught install.sh's exit status only to re-word it, which is a verdict replaced rather than propagated (CLOUD-1090's shape, one file over). Collapsed. `deps-install` is now the install script alone, so its own diagnosis and its own 0/1/2 reach the caller intact instead of being translated. `deps` is `command -v batten && batten --version`, where `command -v` both answers the question -- which binary does PATH find, the one the bare-name registrations resolve -- and supplies the failure exit without a branch to write. The comment describing the ratchet then tripped it a second time. The row matches a literal substring with no anchoring and no regex column (CLOUD-1058), so it reads prose as readily as code: spelling the pattern out incremented the count again with no task added. Reworded to name the shape in words, with that recorded in place so the next author does not rediscover it. Both spellings are now level with base, 31 and 2. clippy doc_markdown twice, same slip copied: an unbackticked event name in contract.rs and again in the test's doc comment. Refs: CLOUD-1085
d1b24e4 to
c52854e
Compare
|
❌ The last analysis has failed. |
|
/fast-forward |
CLOUD-312 row 10. `.claude/hooks/session-start.sh` was the last by-path hook
registration this repository owned. It did three separable things and only one
of them was its own:
ordering -> the declaration order of ten `[[hook.handler]] on = "session-start"`
rows in batten.toml. `dispatch` runs handlers in config order, so
install < doctor < preflight is now readable from the authority
instead of grepped out of a stubbed call log.
the bound -> `timeout_ms`, imposed by the parent. The script had none, on any
step, so a hung `mise install` wedged the session.
the report -> `mise.toml`'s `session:*` tasks, which keep the `step` helper's
log-and-tail contract verbatim. The door does not provide it:
stdout on a passing exit is advisory text, so a step that prints
on success would announce itself every session (CLOUD-891).
Verified by dispatching the real rows, not by reasoning about them: exit 0, the
whole provisioning ran, and the reply carried exactly the two lines the retired
script also emitted. `crates/batten/tests/session_provisioning.rs` is the tier —
ten cases over the compiled binary against a fixture whose handlers are stubs,
proving order, the bound (0.749s against a 30s sleep), silence on a pass, the
reason reaching the session on a failure, and that stdout is never forwarded.
The bootstrap objection that pulled this from #714 is answered rather than
argued: `deps-install` (CLOUD-1085, #798) puts the released binary on PATH at
provisioning, before Claude launches, so there is something to dispatch with.
`session:batten` then replaces it with this checkout's build.
Two corrections the live dispatch measured, both of my own writing:
`signing-posture` prints on success, so registered bare it added a line the
retired script's `step` wrapper had swallowed — repointed at `session:signing`;
and the claim that only `session-census` may speak was wrong, because
`container-preflight` speaks too and did under the old script as well.
NOT READY TO LAND. Three cases in two governed suites grep the retired script
and go red: `tests/commit-attribution.bats:41` and
`tests/container-preflight.bats:131,138`. Measured over the whole corpus —
those three and nothing else. Both suites declare subjects that survive
(`hk.pkl`, `mise.toml`, `mise-tasks/container-preflight.sh`), so
`bats-tests-not-deleted` refuses deleting them and `shell-retirement` refuses
editing them: `only_drops_a_retired_reference` admits a removed line that names
a retired path, and a removed `@test` block's other lines name nothing.
`V-SHELL-RULE-EDITED` declares no override and no bypass_env.
Admits: dddb39ae4779d81289438ea653f09f6610f0945146dd12f8dab939d528de807b
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: batten.toml
Admits-head: 094058e
Admits-epoch: 121f33e1fca81092b00c148867959d9bf6cb54a82832540296b8b7f4fb2b7efb
Admits-author: alec@wenzowski.com
Admits-prev: 0d15402fe6e42f8c889653b827876c756c2d35c008671b41bf49c239f3b0360e
Admits-answer-lost: CLOUD-312 row 10 stays open, .claude/settings.json keeps the last by-path registration on any surface this repository owns, and 295 lines of bash keep ordering provisioning with no bound on any step. The verified work — ten rows, ten tasks, a compiled tier, and a live dispatch that returned exit 0 — would exist only in a container the platform reclaims.
Admits-answer-precondition: This commit writes ten hook.handler rows into batten.toml plus the corrections measured while dispatching them. The handler table is compiled-in config with no other surface: no mise task, rego module or CLI verb can add a row to it, so writing batten.toml directly is the only route, and every row lands in a diff a reviewer reads beside the script it replaces.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply: batten.toml is the owning surface for hook.handler and for its own comments. R-RESTORE-IT would discard the rows and leave the deleted script with nothing dispatching its steps.
… blocker Three comments in `[tasks."test:bats"]` said the body "runs under /bin/sh with no `set -e`". Measured false: a mise task declaring no `shell =` runs under `sh -e`, and `shell = "bash -c"` is the every-line-runs case. The discriminator is a DECLARED SHELL, not the file. This cost a debugging cycle in #798, where a bare call aborted the task before its own error branch could run. `.claude/rules/toolchain.md` names the contrast at the section readers generalise from, so the next author does not re-derive the three comments. `batten.toml`'s `[hook] exclusive` blocker note named `hooks-wiring-check` on the `$HOME` surface. That stopped being the blocker when CLOUD-1167 landed `input.tree.external`. The real blocker is four live sibling registrations, and the row now carries the table plus the measurement `siblings: 2, merged_siblings: 2`. Admits: 12bc0d5675f0158bf1441a5f15ea8ad6ad04b6e46b25c30153d189a44185b023 Admits-rule: protected-mutation Admits-verdict: V-PROTECTED-MUTATION Admits-subject: batten.toml Admits-head: c52854e Admits-epoch: c81e3ad211cddfce765e42d79469223b10d01ff822929696d3914e643004f748 Admits-author: alec@wenzowski.com Admits-prev: - Admits-answer-lost: The next reader scoping CLOUD-1160 is sent at a blocker that no longer exists instead of at the four live sibling registrations that are the real one. That already happened once this session and cost the row a wrong scoping before it was returned to Backlog. Admits-answer-precondition: batten.toml is itself the owning surface for policy config, and the change is prose INSIDE it: the blocker note at :5185 names hooks-wiring-check on the $HOME surface, which stopped being the blocker when CLOUD-1167 landed input.tree.external. No other surface can express a correction to batten.toml's own comments, and the diff is three sentences a reviewer reads in place. Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because batten.toml IS the owning surface for its own comments; there is no narrower surface to redirect to. R-RESTORE-IT would discard the correction and leave the stale blocker naming a retired mechanism.
CLOUD-312 row 10. `.claude/hooks/session-start.sh` was the last by-path hook
registration this repository owned. It did three separable things and only one
of them was its own:
ordering -> the declaration order of ten `[[hook.handler]] on = "session-start"`
rows in batten.toml. `dispatch` runs handlers in config order, so
install < doctor < preflight is now readable from the authority
instead of grepped out of a stubbed call log.
the bound -> `timeout_ms`, imposed by the parent. The script had none, on any
step, so a hung `mise install` wedged the session.
the report -> `mise.toml`'s `session:*` tasks, which keep the `step` helper's
log-and-tail contract verbatim. The door does not provide it:
stdout on a passing exit is advisory text, so a step that prints
on success would announce itself every session (CLOUD-891).
Verified by dispatching the real rows, not by reasoning about them: exit 0, the
whole provisioning ran, and the reply carried exactly the two lines the retired
script also emitted. `crates/batten/tests/session_provisioning.rs` is the tier —
ten cases over the compiled binary against a fixture whose handlers are stubs,
proving order, the bound (0.749s against a 30s sleep), silence on a pass, the
reason reaching the session on a failure, and that stdout is never forwarded.
The bootstrap objection that pulled this from #714 is answered rather than
argued: `deps-install` (CLOUD-1085, #798) puts the released binary on PATH at
provisioning, before Claude launches, so there is something to dispatch with.
`session:batten` then replaces it with this checkout's build.
Two corrections the live dispatch measured, both of my own writing:
`signing-posture` prints on success, so registered bare it added a line the
retired script's `step` wrapper had swallowed — repointed at `session:signing`;
and the claim that only `session-census` may speak was wrong, because
`container-preflight` speaks too and did under the old script as well.
NOT READY TO LAND. Three cases in two governed suites grep the retired script
and go red: `tests/commit-attribution.bats:41` and
`tests/container-preflight.bats:131,138`. Measured over the whole corpus —
those three and nothing else. Both suites declare subjects that survive
(`hk.pkl`, `mise.toml`, `mise-tasks/container-preflight.sh`), so
`bats-tests-not-deleted` refuses deleting them and `shell-retirement` refuses
editing them: `only_drops_a_retired_reference` admits a removed line that names
a retired path, and a removed `@test` block's other lines name nothing.
`V-SHELL-RULE-EDITED` declares no override and no bypass_env.
Admits: dddb39ae4779d81289438ea653f09f6610f0945146dd12f8dab939d528de807b
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: batten.toml
Admits-head: 094058e
Admits-epoch: 121f33e1fca81092b00c148867959d9bf6cb54a82832540296b8b7f4fb2b7efb
Admits-author: alec@wenzowski.com
Admits-prev: 0d15402fe6e42f8c889653b827876c756c2d35c008671b41bf49c239f3b0360e
Admits-answer-lost: CLOUD-312 row 10 stays open, .claude/settings.json keeps the last by-path registration on any surface this repository owns, and 295 lines of bash keep ordering provisioning with no bound on any step. The verified work — ten rows, ten tasks, a compiled tier, and a live dispatch that returned exit 0 — would exist only in a container the platform reclaims.
Admits-answer-precondition: This commit writes ten hook.handler rows into batten.toml plus the corrections measured while dispatching them. The handler table is compiled-in config with no other surface: no mise task, rego module or CLI verb can add a row to it, so writing batten.toml directly is the only route, and every row lands in a diff a reviewer reads beside the script it replaces.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply: batten.toml is the owning surface for hook.handler and for its own comments. R-RESTORE-IT would discard the rows and leave the deleted script with nothing dispatching its steps.
… blocker Three comments in `[tasks."test:bats"]` said the body "runs under /bin/sh with no `set -e`". Measured false: a mise task declaring no `shell =` runs under `sh -e`, and `shell = "bash -c"` is the every-line-runs case. The discriminator is a DECLARED SHELL, not the file. This cost a debugging cycle in #798, where a bare call aborted the task before its own error branch could run. `.claude/rules/toolchain.md` names the contrast at the section readers generalise from, so the next author does not re-derive the three comments. `batten.toml`'s `[hook] exclusive` blocker note named `hooks-wiring-check` on the `$HOME` surface. That stopped being the blocker when CLOUD-1167 landed `input.tree.external`. The real blocker is four live sibling registrations, and the row now carries the table plus the measurement `siblings: 2, merged_siblings: 2`. Admits: 12bc0d5675f0158bf1441a5f15ea8ad6ad04b6e46b25c30153d189a44185b023 Admits-rule: protected-mutation Admits-verdict: V-PROTECTED-MUTATION Admits-subject: batten.toml Admits-head: c52854e Admits-epoch: c81e3ad211cddfce765e42d79469223b10d01ff822929696d3914e643004f748 Admits-author: alec@wenzowski.com Admits-prev: - Admits-answer-lost: The next reader scoping CLOUD-1160 is sent at a blocker that no longer exists instead of at the four live sibling registrations that are the real one. That already happened once this session and cost the row a wrong scoping before it was returned to Backlog. Admits-answer-precondition: batten.toml is itself the owning surface for policy config, and the change is prose INSIDE it: the blocker note at :5185 names hooks-wiring-check on the $HOME surface, which stopped being the blocker when CLOUD-1167 landed input.tree.external. No other surface can express a correction to batten.toml's own comments, and the diff is three sentences a reviewer reads in place. Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because batten.toml IS the owning surface for its own comments; there is no narrower surface to redirect to. R-RESTORE-IT would discard the correction and leave the stale blocker naming a retired mechanism. Refs: CLOUD-1085, CLOUD-1268, CLOUD-1160
CLOUD-312 row 10. `.claude/hooks/session-start.sh` was the last by-path hook
registration this repository owned. It did three separable things and only one
of them was its own:
ordering -> the declaration order of ten `[[hook.handler]] on = "session-start"`
rows in batten.toml. `dispatch` runs handlers in config order, so
install < doctor < preflight is now readable from the authority
instead of grepped out of a stubbed call log.
the bound -> `timeout_ms`, imposed by the parent. The script had none, on any
step, so a hung `mise install` wedged the session.
the report -> `mise.toml`'s `session:*` tasks, which keep the `step` helper's
log-and-tail contract verbatim. The door does not provide it:
stdout on a passing exit is advisory text, so a step that prints
on success would announce itself every session (CLOUD-891).
Verified by dispatching the real rows, not by reasoning about them: exit 0, the
whole provisioning ran, and the reply carried exactly the two lines the retired
script also emitted. `crates/batten/tests/session_provisioning.rs` is the tier —
ten cases over the compiled binary against a fixture whose handlers are stubs,
proving order, the bound (0.749s against a 30s sleep), silence on a pass, the
reason reaching the session on a failure, and that stdout is never forwarded.
The bootstrap objection that pulled this from #714 is answered rather than
argued: `deps-install` (CLOUD-1085, #798) puts the released binary on PATH at
provisioning, before Claude launches, so there is something to dispatch with.
`session:batten` then replaces it with this checkout's build.
Two corrections the live dispatch measured, both of my own writing:
`signing-posture` prints on success, so registered bare it added a line the
retired script's `step` wrapper had swallowed — repointed at `session:signing`;
and the claim that only `session-census` may speak was wrong, because
`container-preflight` speaks too and did under the old script as well.
NOT READY TO LAND. Three cases in two governed suites grep the retired script
and go red: `tests/commit-attribution.bats:41` and
`tests/container-preflight.bats:131,138`. Measured over the whole corpus —
those three and nothing else. Both suites declare subjects that survive
(`hk.pkl`, `mise.toml`, `mise-tasks/container-preflight.sh`), so
`bats-tests-not-deleted` refuses deleting them and `shell-retirement` refuses
editing them: `only_drops_a_retired_reference` admits a removed line that names
a retired path, and a removed `@test` block's other lines name nothing.
`V-SHELL-RULE-EDITED` declares no override and no bypass_env.
Admits: dddb39ae4779d81289438ea653f09f6610f0945146dd12f8dab939d528de807b
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: batten.toml
Admits-head: 094058e
Admits-epoch: 121f33e1fca81092b00c148867959d9bf6cb54a82832540296b8b7f4fb2b7efb
Admits-author: alec@wenzowski.com
Admits-prev: 0d15402fe6e42f8c889653b827876c756c2d35c008671b41bf49c239f3b0360e
Admits-answer-lost: CLOUD-312 row 10 stays open, .claude/settings.json keeps the last by-path registration on any surface this repository owns, and 295 lines of bash keep ordering provisioning with no bound on any step. The verified work — ten rows, ten tasks, a compiled tier, and a live dispatch that returned exit 0 — would exist only in a container the platform reclaims.
Admits-answer-precondition: This commit writes ten hook.handler rows into batten.toml plus the corrections measured while dispatching them. The handler table is compiled-in config with no other surface: no mise task, rego module or CLI verb can add a row to it, so writing batten.toml directly is the only route, and every row lands in a diff a reviewer reads beside the script it replaces.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply: batten.toml is the owning surface for hook.handler and for its own comments. R-RESTORE-IT would discard the rows and leave the deleted script with nothing dispatching its steps.
Refs: CLOUD-312, CLOUD-1085, CLOUD-898
… blocker Three comments in `[tasks."test:bats"]` said the body "runs under /bin/sh with no `set -e`". Measured false: a mise task declaring no `shell =` runs under `sh -e`, and `shell = "bash -c"` is the every-line-runs case. The discriminator is a DECLARED SHELL, not the file. This cost a debugging cycle in #798, where a bare call aborted the task before its own error branch could run. `.claude/rules/toolchain.md` names the contrast at the section readers generalise from, so the next author does not re-derive the three comments. `batten.toml`'s `[hook] exclusive` blocker note named `hooks-wiring-check` on the `$HOME` surface. That stopped being the blocker when CLOUD-1167 landed `input.tree.external`. The real blocker is four live sibling registrations, and the row now carries the table plus the measurement `siblings: 2, merged_siblings: 2`. Admits: 12bc0d5675f0158bf1441a5f15ea8ad6ad04b6e46b25c30153d189a44185b023 Admits-rule: protected-mutation Admits-verdict: V-PROTECTED-MUTATION Admits-subject: batten.toml Admits-head: c52854e Admits-epoch: c81e3ad211cddfce765e42d79469223b10d01ff822929696d3914e643004f748 Admits-author: alec@wenzowski.com Admits-prev: - Admits-answer-lost: The next reader scoping CLOUD-1160 is sent at a blocker that no longer exists instead of at the four live sibling registrations that are the real one. That already happened once this session and cost the row a wrong scoping before it was returned to Backlog. Admits-answer-precondition: batten.toml is itself the owning surface for policy config, and the change is prose INSIDE it: the blocker note at :5185 names hooks-wiring-check on the $HOME surface, which stopped being the blocker when CLOUD-1167 landed input.tree.external. No other surface can express a correction to batten.toml's own comments, and the diff is three sentences a reviewer reads in place. Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because batten.toml IS the owning surface for its own comments; there is no narrower surface to redirect to. R-RESTORE-IT would discard the correction and leave the stale blocker naming a retired mechanism. Refs: CLOUD-1085, CLOUD-1268, CLOUD-1160
CLOUD-312 row 10. `.claude/hooks/session-start.sh` was the last by-path hook
registration this repository owned. It did three separable things and only one
of them was its own:
ordering -> the declaration order of ten `[[hook.handler]] on = "session-start"`
rows in batten.toml. `dispatch` runs handlers in config order, so
install < doctor < preflight is now readable from the authority
instead of grepped out of a stubbed call log.
the bound -> `timeout_ms`, imposed by the parent. The script had none, on any
step, so a hung `mise install` wedged the session.
the report -> `mise.toml`'s `session:*` tasks, which keep the `step` helper's
log-and-tail contract verbatim. The door does not provide it:
stdout on a passing exit is advisory text, so a step that prints
on success would announce itself every session (CLOUD-891).
Verified by dispatching the real rows, not by reasoning about them: exit 0, the
whole provisioning ran, and the reply carried exactly the two lines the retired
script also emitted. `crates/batten/tests/session_provisioning.rs` is the tier —
ten cases over the compiled binary against a fixture whose handlers are stubs,
proving order, the bound (0.749s against a 30s sleep), silence on a pass, the
reason reaching the session on a failure, and that stdout is never forwarded.
The bootstrap objection that pulled this from #714 is answered rather than
argued: `deps-install` (CLOUD-1085, #798) puts the released binary on PATH at
provisioning, before Claude launches, so there is something to dispatch with.
`session:batten` then replaces it with this checkout's build.
Two corrections the live dispatch measured, both of my own writing:
`signing-posture` prints on success, so registered bare it added a line the
retired script's `step` wrapper had swallowed — repointed at `session:signing`;
and the claim that only `session-census` may speak was wrong, because
`container-preflight` speaks too and did under the old script as well.
NOT READY TO LAND. Three cases in two governed suites grep the retired script
and go red: `tests/commit-attribution.bats:41` and
`tests/container-preflight.bats:131,138`. Measured over the whole corpus —
those three and nothing else. Both suites declare subjects that survive
(`hk.pkl`, `mise.toml`, `mise-tasks/container-preflight.sh`), so
`bats-tests-not-deleted` refuses deleting them and `shell-retirement` refuses
editing them: `only_drops_a_retired_reference` admits a removed line that names
a retired path, and a removed `@test` block's other lines name nothing.
`V-SHELL-RULE-EDITED` declares no override and no bypass_env.
Admits: dddb39ae4779d81289438ea653f09f6610f0945146dd12f8dab939d528de807b
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: batten.toml
Admits-head: 094058e
Admits-epoch: 121f33e1fca81092b00c148867959d9bf6cb54a82832540296b8b7f4fb2b7efb
Admits-author: alec@wenzowski.com
Admits-prev: 0d15402fe6e42f8c889653b827876c756c2d35c008671b41bf49c239f3b0360e
Admits-answer-lost: CLOUD-312 row 10 stays open, .claude/settings.json keeps the last by-path registration on any surface this repository owns, and 295 lines of bash keep ordering provisioning with no bound on any step. The verified work — ten rows, ten tasks, a compiled tier, and a live dispatch that returned exit 0 — would exist only in a container the platform reclaims.
Admits-answer-precondition: This commit writes ten hook.handler rows into batten.toml plus the corrections measured while dispatching them. The handler table is compiled-in config with no other surface: no mise task, rego module or CLI verb can add a row to it, so writing batten.toml directly is the only route, and every row lands in a diff a reviewer reads beside the script it replaces.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply: batten.toml is the owning surface for hook.handler and for its own comments. R-RESTORE-IT would discard the rows and leave the deleted script with nothing dispatching its steps.
Refs: CLOUD-312, CLOUD-1085, CLOUD-898
Closes CLOUD-1085.
battenis absent at SessionStart on a fresh container, so every mediated call fails open insilence for the first ~2.5 minutes of a session, and for the whole of any session where the
SessionStart hook does not reach its build step. An absent reference monitor and a passing one are
indistinguishable from outside.
The seam was never missing — it was a dangling call
The environment's setup script runs
mise run deps-installon every provision, before any sessionstarts, and soft-fails with a stderr line when the task is absent. This repository never defined
it.
mise tasks lscarriedinstall-checkandinstall:localand nodeps*at all, so thatcall has been landing on nothing for the life of the defect, printing
setup: 'deps-install' incompleteinto a log nobody reads as a finding.Defining the task is the entire fix. Nothing outside this repository changes, which is what
CLOUD-1085 §1 asks for: "the change is in the environment configuration that provisions a
container, which must invoke it before any hook fires" — and that configuration already makes the
call. Its
depsreport sibling, which the same script calls at step 7, is defined here too.install.sh, neverinstall:localThe row is explicit that
install:localis "a dev-clone convenience that supersedes a releasebuild, not a provisioning path". It builds the working tree's binary, so it assumes a Rust
toolchain and a 141-second compile (measured, CLOUD-1085) that provisioning cannot. One install
interface (§1); this is the caller it lacked.
install:localstays where it is — it supersedes thereleased binary with the working tree's for dev work, which is a different job.
Routing through
mise runis load-bearingThe setup script shadows
misewith a wrapper that prepends the GitHub API and asset hosts toNO_PROXYand setsMISE_GITHUB_TOKENfrom the session PAT, because the agent proxy answers 403for third-party release repos. A task body inherits that environment. A bare
./install.shin thesetup script would not reach the release API at all, so the seam and the proxy fix are the same
mechanism rather than two independent facts.
Shown able to fail (CLOUD-418)
Both arms exercised on this container, into a temp destination so the live binary was untouched:
v0.0.137,verified=sha256, silent, exit 0::error::naming which failure, propagatesinstall.sh's own code2stays distinct from1per house style §7 — could-not-look (no curl, unreachable API) against arefusal
install.shdecided (bad digest, unwritable destination). Collapsing them would send areader with a network problem hunting a corrupted asset.
Two defects found and fixed while writing it
Both are recorded in comments beside the code rather than only here.
runbody executes undersh -e. The first draft used a bare./install.shfollowed bystatus=$?; the failing call aborted the task and the branch thatreports which failure never ran — mise printed its own
ERROR task failedand nothing else.The
|| status=$?capture is the shapetests/task-fail-closed.batsalready admits(CLOUD-318). Note this differs from the
shell = "bash -c"bodies.claude/rules/toolchain.mddescribes, which genuinely do not stop.binary's own name. Quoting it the way every surrounding comment does would have run it.
The refusal message also does not claim nothing was written:
install.shinstalls and thenrefuses when the destination is off PATH, so that wording is false in the failure mode most likely
to be hit. What is true in every arm — and what matters, since
.claude/settings.jsonregisters theengine by bare name — is that the name does not resolve.
Not in this change
CLOUD-1085 §2(b) — the advisory when a session's
SessionStartregistration did not run — is theother half of the row's acceptance and is engine work in
contract.rs. It is not here yet.Restoring
.claude/container-setup.shis explicitly out of scope; the design is one install scriptand no harness-specific bootstrap, and #711/#713 took that decision deliberately.
🤖 Generated with Claude Code
https://claude.ai/code/session_0134WDLd3bJpLPxZyRS6zPW7
Generated by Claude Code