Repository navigation
feat(install): ship a single-binary-first install path with a verified download - #310
Conversation
CLOUD-65 Ship a single-binary-first install path and package-manager distribution
Why Acceptance
|
905ccc6 to
ce1fda5
Compare
|
/fast-forward |
|
Triggered from #310 (comment) by @wenzowski. Trying to fast forward Target branch ( commit ec3639a5f87a043c350ff85159db768a3fe3cfcf (HEAD -> main, origin/main)
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 18:51:35 2026 +0000
fix(session-start): run doctor inside the hook's synchronous window
`mise install` returning is not "the toolchain is settled". doctor provisions
what mise does not own — the rustup cross targets above all — and left outside
the SessionStart window it runs for the first time inside the first
`mise run verify`, concurrently with whatever the sandbox is still laying down.
Measured on a cold container 2026-08-07: that first verify died in doctor with a
`detected conflict` rollback on rust-std-aarch64-apple-darwin, and a second
doctor, nothing changed but time, exited 0. The message names cross-compilation,
so it costs the reader a debugging session on a machine where nothing is broken.
This is not a retry. It does not make the race survivable, it empties the window
the race needs: by the time any task runs, the targets are already installed.
CLOUD-220's per-toolchain mutex in target-ensure serializes concurrent writers
and still stands — it covers the warm case (the verify graph racing itself,
CLOUD-201), this covers the cold one. doctor itself is unchanged.
The step goes through the hook's existing `step` helper, so it inherits the
loud-failure contract, and runs after mise install (its rustup half needs the
provisioned toolchain) and before container-preflight (a halt, which wants
provisioning finished). tests/session-start.bats records every mise invocation
through its stub and asserts that ordering, rather than grepping the source;
doctor joins container-preflight in being neutralised there, because its rustup
half reaches the network and test:bats already depends on `doctor --no-targets`.
Refs: CLOUD-218Pull request ( commit ce1fda51c8d5e3a12e2abca2725db7ded19d12ed (pull_request/wenzowski/cloud-65-ship-a-single-binary-first-install-path-and-package-manager)
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 19:08:09 2026 +0000
feat(install): ship a single-binary-first install path with a verified download
Releases have carried seven target archives since CLOUD-108, with nothing on the
consuming side: no install script, no binstall metadata, and nothing asserting
that the asset name a consumer resolves is the one `mise-tasks/dist` writes.
"Single-binary-first" was an ordering claim the repo could not make.
install.sh is POSIX sh, assumes only what `curl … | sh` already proves is
present, and verifies every download against the SHA-256 digest the release API
reports — a mismatch, or an asset with no digest, installs nothing and there is
no flag to skip it. Measured: api.github.com answers pretty-printed, so the
parsing commits to neither wire form. The token reaches curl on stdin rather
than argv, where ps would show it.
install-check is the gate. dist owns archive naming and install.sh owns its
target list; the gate calls both rather than scraping either, derives the matrix
from the workflow that decides it, resolves the binstall template against the
same answer, and fails on a committed executable.
Refs: CLOUD-65Can't fast forward * ce1fda51c8d5e3a12e2abca2725db7ded19d12ed feat(install): ship a single-binary-first install path with a verified download
| * ec3639a5f87a043c350ff85159db768a3fe3cfcf fix(session-start): run doctor inside the hook's synchronous window
|/
* 37bf48d4f645e57937455ef8ca4d791a4859bf4a feat(spec): emit the derived read-only allowlist beside the tree
commit 37bf48d4f645e57937455ef8ca4d791a4859bf4a
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 18:58:45 2026 +0000
feat(spec): emit the derived read-only allowlist beside the tree
`spec::read_only_allowlist` derives §5's agent allowlist from the same walk
that builds the emitted tree, so an unclassified command cannot leak in — but
it had no caller outside this crate's own tests. A derivation nothing emits is
one every consumer has to re-implement, and this filter's failure mode is
advertising a write-bearing verb as agent-safe: `enforce` may run user-supplied
commands and `hook` adjudicates someone else's write, and both stay off the
list only because the filter is `effect == read`.
So emit it. `SpecDocument` flattens the command tree at the document root and
adds `read_only_allowlist` beside it, which keeps every root key a consumer
already reads (`path`, `subcommands`, …) exactly where it was.
The pinning test asserts the emitted key IS the `effect == read` filter over
the emitted tree, recomputed from the same document — not a second copy of the
path list, which `allowlist_is_exactly_the_read_commands` already holds.
Refs: CLOUD-217Rebase locally, and then force push to |
ce1fda5 to
fa488f4
Compare
|
/fast-forward |
|
Triggered from #310 (comment) by @wenzowski. Trying to fast forward Target branch ( commit f34f99a3a2c842b064087c3deb6e2476f412ed29 (HEAD -> main, origin/main)
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 18:59:53 2026 +0000
test(findings): hold the no-escalation law over the bytes, not over a struct
CLOUD-80's residual acceptance clause — "escalation on re-touch is absent" —
was carried by an in-memory test that drives `FindingRecord::upsert` on a
struct. That test cannot see either way a stored tier could actually move: a
write path that re-derives it, and a later scan that re-rates the rule. Both
need a real store dir.
Two store-level tests, asserted over the record file:
- `an_nth_observation_never_re_tiers_the_stored_record` runs the same identity
through `record` five times, total over `RuleSeverity::ALL` so every
`AdvisoryTier` is exercised, and compares the persisted `"tier"` line
byte-for-byte against the first observation's — while asserting the
occurrence count did move, so it cannot pass on a store that recorded
nothing.
- `a_re_rated_rule_never_re_tiers_a_settled_finding` records one identity at
`warn`, then again at `deny`, and pins that the settled record keeps tier
`caution` and severity `warn`. Severity is not an identity input, so the
re-rating routes to the same record; `record` reusing it rather than
refreshing from the rule now firing was documented and untested.
Negative control run before landing: re-deriving the tier in `record`'s update
path, plus a count-keyed escalation, fails both new tests and leaves the
in-memory sibling green.
Refs: CLOUD-80Pull request ( commit fa488f43daccc14b3a9698a6a514bb89217c50d6 (pull_request/wenzowski/cloud-65-ship-a-single-binary-first-install-path-and-package-manager)
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 19:08:09 2026 +0000
feat(install): ship a single-binary-first install path with a verified download
Releases have carried seven target archives since CLOUD-108, with nothing on the
consuming side: no install script, no binstall metadata, and nothing asserting
that the asset name a consumer resolves is the one `mise-tasks/dist` writes.
"Single-binary-first" was an ordering claim the repo could not make.
install.sh is POSIX sh, assumes only what `curl … | sh` already proves is
present, and verifies every download against the SHA-256 digest the release API
reports — a mismatch, or an asset with no digest, installs nothing and there is
no flag to skip it. Measured: api.github.com answers pretty-printed, so the
parsing commits to neither wire form. The token reaches curl on stdin rather
than argv, where ps would show it.
install-check is the gate. dist owns archive naming and install.sh owns its
target list; the gate calls both rather than scraping either, derives the matrix
from the workflow that decides it, resolves the binstall template against the
same answer, and fails on a committed executable.
Refs: CLOUD-65Can't fast forward * fa488f43daccc14b3a9698a6a514bb89217c50d6 feat(install): ship a single-binary-first install path with a verified download
| * f34f99a3a2c842b064087c3deb6e2476f412ed29 test(findings): hold the no-escalation law over the bytes, not over a struct
|/
* ec3639a5f87a043c350ff85159db768a3fe3cfcf fix(session-start): run doctor inside the hook's synchronous window
commit ec3639a5f87a043c350ff85159db768a3fe3cfcf
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 18:51:35 2026 +0000
fix(session-start): run doctor inside the hook's synchronous window
`mise install` returning is not "the toolchain is settled". doctor provisions
what mise does not own — the rustup cross targets above all — and left outside
the SessionStart window it runs for the first time inside the first
`mise run verify`, concurrently with whatever the sandbox is still laying down.
Measured on a cold container 2026-08-07: that first verify died in doctor with a
`detected conflict` rollback on rust-std-aarch64-apple-darwin, and a second
doctor, nothing changed but time, exited 0. The message names cross-compilation,
so it costs the reader a debugging session on a machine where nothing is broken.
This is not a retry. It does not make the race survivable, it empties the window
the race needs: by the time any task runs, the targets are already installed.
CLOUD-220's per-toolchain mutex in target-ensure serializes concurrent writers
and still stands — it covers the warm case (the verify graph racing itself,
CLOUD-201), this covers the cold one. doctor itself is unchanged.
The step goes through the hook's existing `step` helper, so it inherits the
loud-failure contract, and runs after mise install (its rustup half needs the
provisioned toolchain) and before container-preflight (a halt, which wants
provisioning finished). tests/session-start.bats records every mise invocation
through its stub and asserts that ordering, rather than grepping the source;
doctor joins container-preflight in being neutralised there, because its rustup
half reaches the network and test:bats already depends on `doctor --no-targets`.
Refs: CLOUD-218Rebase locally, and then force push to |
a0b4190 to
3febad6
Compare
|
/fast-forward |
|
Triggered from #310 (comment) by @wenzowski. Trying to fast forward Target branch ( commit 42ef66342352cf2219fd94ab2503dc228e1dd8b5 (HEAD -> main, origin/main)
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 19:09:46 2026 +0000
test(session): compare the whole store across a restart, not a subset
The predicate is that a kill-and-restart preserves the findings store
byte-for-byte, and the assertion was over the fields the test helper
happens to parse — fingerprint, rule and instance counts. It would not
have noticed the tier, the presentation or a disposition changing across
the restart, which are exactly the fields no verb can vary yet and so the
ones nothing else is watching.
Refs: CLOUD-83Pull request ( commit 3febad6dfeb8f98563d95b03741f3695b27ac4e0 (pull_request/wenzowski/cloud-65-ship-a-single-binary-first-install-path-and-package-manager)
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 19:08:09 2026 +0000
feat(install): ship a single-binary-first install path with a verified download
Releases have carried seven target archives since CLOUD-108, with nothing on the
consuming side: no install script, no binstall metadata, and nothing asserting
that the asset name a consumer resolves is the one `mise-tasks/dist` writes.
"Single-binary-first" was an ordering claim the repo could not make.
install.sh is POSIX sh, assumes only what `curl … | sh` already proves is
present, and verifies every download against the SHA-256 digest the release API
reports — a mismatch, or an asset with no digest, installs nothing and there is
no flag to skip it. Measured: api.github.com answers pretty-printed, so the
parsing commits to neither wire form. The token reaches curl on stdin rather
than argv, where ps would show it.
install-check is the gate. dist owns archive naming and install.sh owns its
target list; the gate calls both rather than scraping either, derives the matrix
from the workflow that decides it, resolves the binstall template against the
same answer, and fails on a committed executable.
Refs: CLOUD-65Can't fast forward * 3febad6dfeb8f98563d95b03741f3695b27ac4e0 feat(install): ship a single-binary-first install path with a verified download
| * 42ef66342352cf2219fd94ab2503dc228e1dd8b5 test(session): compare the whole store across a restart, not a subset
| * 497f6d5b98b533c525d6da0dc678604082dfa83a test(session): pin what a warm fork must keep
| * e7165259b0afb55ca82b7d23a72b18bb6987cb88 feat(state): record where a session left the journal
| * 708d12c6e7e58b4653f18373c94798ec9eb33233 feat(session): give a warm fork somewhere to resume from
|/
* 78d313aea99ff609bf6e24360538bd8ba4543354 fix(lock-complete): name lock-check as the remedy, not the raw regeneration command
commit 78d313aea99ff609bf6e24360538bd8ba4543354
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 18:56:11 2026 +0000
fix(lock-complete): name lock-check as the remedy, not the raw regeneration command
The unlocked-tool pointer spelled out the regeneration command, which put the
literal into an executable line — where tests/lock-complete.bats's "makes no
network call" assertion reads it as this gate fetching. That assertion is a
coarse string match on purpose and is worth keeping coarse, so the message
moves instead.
`mise run lock-check` is also the better pointer on its own terms: it is the
single sanctioned lockfile writer, since `[settings] lockfile = false` denies
the write to everything else (CLOUD-223).
Refs: CLOUD-333Rebase locally, and then force push to |
06d03b7 to
652c038
Compare
|
/fast-forward |
|
Triggered from #310 (comment) by @wenzowski. Trying to fast forward Target branch ( commit 3fde7a81593c6a60d838b6006f8db6a638b4738f (HEAD -> main, origin/main)
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 19:30:27 2026 +0000
fix(bench): keep the workflow's summary block off shellcheck's SC2016
A backtick pair inside single quotes reads as legacy command substitution
to shellcheck, which actionlint runs over every `run:` block, so the
summary line failed the gate. Double-quoted with escaped backticks keeps
the markdown code span and says nothing shellcheck has to guess at.
Prettier's table realignment in README rides along — hk staged it.
Refs: CLOUD-207Pull request ( commit 652c0386b7172840672f590770e0600f7ce55b44 (pull_request/wenzowski/cloud-65-ship-a-single-binary-first-install-path-and-package-manager)
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 19:08:09 2026 +0000
feat(install): ship a single-binary-first install path with a verified download
Releases have carried seven target archives since CLOUD-108, with nothing on the
consuming side: no install script, no binstall metadata, and nothing asserting
that the asset name a consumer resolves is the one `mise-tasks/dist` writes.
"Single-binary-first" was an ordering claim the repo could not make.
install.sh is POSIX sh, assumes only what `curl … | sh` already proves is
present, and verifies every download against the SHA-256 digest the release API
reports — a mismatch, or an asset with no digest, installs nothing and there is
no flag to skip it. Measured: api.github.com answers pretty-printed, so the
parsing commits to neither wire form. The token reaches curl on stdin rather
than argv, where ps would show it.
install-check is the gate. dist owns archive naming and install.sh owns its
target list; the gate calls both rather than scraping either, derives the matrix
from the workflow that decides it, resolves the binstall template against the
same answer, and fails on a committed executable.
Refs: CLOUD-65Can't fast forward * 652c0386b7172840672f590770e0600f7ce55b44 feat(install): ship a single-binary-first install path with a verified download
| * 3fde7a81593c6a60d838b6006f8db6a638b4738f fix(bench): keep the workflow's summary block off shellcheck's SC2016
| * 8854916d55b1126fcae162c601404edfc5cc6f9e feat(bench): measure the invocation cost, publish it, and gate it
|/
* 040fa566b81b0c89387ac7bdb833e0e43d028e1c docs(resolve): mark §5's max_effect as specified, not implemented
commit 040fa566b81b0c89387ac7bdb833e0e43d028e1c
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 19:24:16 2026 +0000
docs(resolve): mark §5's max_effect as specified, not implemented
Two module headers asserted `max_effect` as an invariant already in the tree:
`resolve.rs` said the raise-only override rule "extends §5's `max_effect`
invariant to the config layer", and `verbs.rs` said sharing the effect
vocabulary keeps "the raise-only `max_effect` rule" meaning one thing across
the tool. Neither is true today. `max_effect` is per-flag effect annotations
plus a monotone maximum over them; `Effect` is declared per command and carries
no ordering, so there is nothing to take a maximum of.
That is the CLOUD-198 class, and `resolve.rs` was the costly instance: it
presented this layer's raise-only rule as a corollary of a mechanism that
exists, so a reader checking whether config-layer monotonicity was load-bearing
on its own found a sentence telling them it was not.
Both now name the invariant as specified-not-implemented and point at CLOUD-27,
which is where the implementation rides. No test: the Ready block puts the test
obligation on the implementation path, and a doc comment stating a gap has
nothing to assert until the gap closes.
Refs: CLOUD-217Rebase locally, and then force push to |
5574b57 to
466e90e
Compare
|
/fast-forward |
|
Triggered from #310 (comment) by @wenzowski. Trying to fast forward Target branch ( commit 6569aeb303e942642dd22feab96f221c67f7027e (HEAD -> main, origin/main)
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 19:02:59 2026 +0000
feat(rules)!: rename the command kind's `run` column to `check`, reserve `fix`
House style §9 specifies the `command` rule kind as a check/fix duality: an
inspection-only `check` is the gate — the only side enforcement ever runs — and
an optional mutating `fix` repairs what it condemned. The shipped `Rule` carried
one `run` column, which does not express the split.
§2 declares no back-compatibility surface and no deprecation aliases, so every
config written against `run` becomes a breaking migration later. Today the key
appears in no shipped config at all — only fixtures, tests and the README — so
the rename costs nothing. It will not stay that way.
`fix` parses and is executed by nothing: serialised fix execution is a
capability this engine does not have. `enforce` refuses a row declaring one
rather than running the check side and silently ignoring the repair, which would
report green over work nobody did. Reserving the key now means an author who
writes it is writing the final spelling.
`run` survives as a refusal-only field, the shape `OverrideConfig::
min_batten_version` already uses: "unknown field `run`" reads as a typo, where
this is a rename with one specific fix, and the refusal has to carry it
(CLOUD-122). It is deliberately outside `Rule::columns` — that census classifies
the columns a kind may carry, and no kind carries this one.
Both refusals are exit 1, not the exit 2 the issue's §5/§7 asked for. §7 makes 2
the policy verdict — a violation found in the repository, or a mediated call
denied — while a config naming a key or a capability this build does not have is
the config-or-usage class. §5 spells the structurally identical sibling refusal
(a rule the read-only half cannot honestly run) as exit 1, and `run_static` has
implemented it that way since CLOUD-170; the issue's §3 describes that same
landed refusal as exit 2, which is falsified by the code it describes. Emitting
2 here would also make an engine gap read to a harness as a policy deny, which
is what the structural fail-open exists to prevent.
Refs: CLOUD-215Pull request ( commit 466e90e572b3cbcfff37dc60a88bb0867e0ae8ae (pull_request/wenzowski/cloud-65-ship-a-single-binary-first-install-path-and-package-manager)
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 19:08:09 2026 +0000
feat(install): ship a single-binary-first install path with a verified download
Releases have carried seven target archives since CLOUD-108, with nothing on the
consuming side: no install script, no binstall metadata, and nothing asserting
that the asset name a consumer resolves is the one `mise-tasks/dist` writes.
"Single-binary-first" was an ordering claim the repo could not make.
install.sh is POSIX sh, assumes only what `curl … | sh` already proves is
present, and verifies every download against the SHA-256 digest the release API
reports — a mismatch, or an asset with no digest, installs nothing and there is
no flag to skip it. Measured: api.github.com answers pretty-printed, so the
parsing commits to neither wire form. The token reaches curl on stdin rather
than argv, where ps would show it.
install-check is the gate. dist owns archive naming and install.sh owns its
target list; the gate calls both rather than scraping either, derives the matrix
from the workflow that decides it, resolves the binstall template against the
same answer, and fails on a committed executable.
Refs: CLOUD-65Can't fast forward * 466e90e572b3cbcfff37dc60a88bb0867e0ae8ae feat(install): ship a single-binary-first install path with a verified download
| * 6569aeb303e942642dd22feab96f221c67f7027e feat(rules)!: rename the command kind's `run` column to `check`, reserve `fix`
|/
* 18bb41610d628560c9babe07224ae6c30478c81c ci(hooks): remind once when the instruction surface moves under a running session
commit 18bb41610d628560c9babe07224ae6c30478c81c
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 19:29:23 2026 +0000
ci(hooks): remind once when the instruction surface moves under a running session
A session loads its hook wiring and reads its instruction files once, at start.
When the contract then changes on `main`, the running session keeps executing the
old one with no signal that it drifted: `issue-guard` landed mid-session and a
live session readied a PR whose branch and commits carried no `CLOUD-<n>` key,
because that hook was never in that session's wiring.
`contract-drift` hashes the tracked contract surface (`AGENTS.md`,
`.claude/rules`, `.claude/settings.json`, `hk.pkl`, `mise-tasks`) in one
`git hash-object` pass — 63 files, 8ms — keyed per session under `.git/`, and
names the paths that moved. Silence is the default; a change-set is reported once,
because reporting overwrites the snapshot. Pointer-only: paths and a count, never
a byte of the file, since a reminder carrying the new text is a mirror and a
mirror is cleared by reading the hook instead of the file.
Not `PreToolUse`, which the Ready block specified: that event's only model-facing
channel is exit 2, which blocks the call, and CLOUD-97 and CLOUD-219 each ruled a
deny out independently. `PostToolBatch` documents `additionalContext` firing
"before the next model request", which is the same instant as "before the next
lifecycle step"; `SessionStart` seeds the snapshot before any tool runs, since an
autonomous session's first batch is routinely fetch+rebase and a snapshot written
after it would record the drift as its baseline.
Measured while building it: the `PostToolBatch` entry took effect within the
session that added it, so the channel works and the premise is narrower than the
title reads. The reminder therefore tells the agent to check whether a new hook is
loaded rather than asserting it cannot be.
Refs: CLOUD-187Rebase locally, and then force push to |
466e90e to
cba3551
Compare
|
/fast-forward |
|
Triggered from #310 (comment) by @wenzowski. Trying to fast forward Target branch ( commit 6947232b13bc17174986a0e3bfe528d848617519 (HEAD -> main, origin/main)
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 19:57:59 2026 +0000
perf(hk): glob batten-check and macos-link-check, and gate the glob's honesty
`hk check` ran three steps unconditionally, because a step with no `glob` is
never skipped. `batten-check` is `cargo run -p batten -- check`, so every
commit paid a debug build whatever it touched: 9.47s cold from a fresh clone
(CLOUD-212 run 1, D8) against 0.16s for the cheapest gate in the same run, and
133-141ms warm, forever.
The glob is the union of what `batten check` ACTUALLY reads, which is not the
two things the obvious reading gives. Read at run_check_with in
crates/batten/src/lib.rs, it evaluates every `[[rule]]` over that rule's own
glob — and those reach outside crates/, to mise.toml, .github/workflows/*.yml
and tests/*.bats — plus `budget::measure_all`, because a declared budget is a
gate under `check` and not only under `policy budget` (CLOUD-50). That last one
makes AGENTS.md and .serena/project.yml genuine inputs, so "a Markdown-only
change cannot change this verdict" is false for the most-edited Markdown file
in the repo, and a glob written from the obvious reading would have switched
the gate off for it. Cargo.toml and Cargo.lock are in because the task uses
`cargo run` deliberately, to judge the engine and the config as the pair that
ships.
`macos-link-check` globs the manifests alone: its predicate is `cargo metadata
--filter-platform` over the resolved graph, and nothing else can move that
graph. `no-docs-tree` deliberately keeps no glob — its input is the whole
index, including a docs/ path an earlier commit left behind, and at ~95ms that
is affordable.
Deriving batten-check's list from batten.toml by hand makes hk.pkl a second
authority over a set the config already defines, and a second authority narrows
silently: add a rule whose glob names a new path and the step stops running for
commits that touch only it, with nothing going red. So the glob ships with its
mechanism (non-negotiable 2). `mise run batten-glob-check` is a pure function
of the two committed files — every rule glob and every declared budget path
must be covered verbatim or subsumed by a `P/**` entry, and a config it can
parse nothing out of is exit 2 rather than a vacuous pass.
Selection is asserted as data, never as a timing: `hk check --plan --json`
prints each step's status without running it, and tests/hk-selection.bats pins
both directions over the repository's own hk.pkl — README.md skips
batten-check, AGENTS.md does not.
Refs: CLOUD-224Pull request ( commit cba3551b83c8aa06856f963002510f66a420a42b (pull_request/wenzowski/cloud-65-ship-a-single-binary-first-install-path-and-package-manager)
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 19:08:09 2026 +0000
feat(install): ship a single-binary-first install path with a verified download
Releases have carried seven target archives since CLOUD-108, with nothing on the
consuming side: no install script, no binstall metadata, and nothing asserting
that the asset name a consumer resolves is the one `mise-tasks/dist` writes.
"Single-binary-first" was an ordering claim the repo could not make.
install.sh is POSIX sh, assumes only what `curl … | sh` already proves is
present, and verifies every download against the SHA-256 digest the release API
reports — a mismatch, or an asset with no digest, installs nothing and there is
no flag to skip it. Measured: api.github.com answers pretty-printed, so the
parsing commits to neither wire form. The token reaches curl on stdin rather
than argv, where ps would show it.
install-check is the gate. dist owns archive naming and install.sh owns its
target list; the gate calls both rather than scraping either, derives the matrix
from the workflow that decides it, resolves the binstall template against the
same answer, and fails on a committed executable.
Refs: CLOUD-65Can't fast forward * cba3551b83c8aa06856f963002510f66a420a42b feat(install): ship a single-binary-first install path with a verified download
| * 6947232b13bc17174986a0e3bfe528d848617519 perf(hk): glob batten-check and macos-link-check, and gate the glob's honesty
|/
* 6569aeb303e942642dd22feab96f221c67f7027e feat(rules)!: rename the command kind's `run` column to `check`, reserve `fix`
commit 6569aeb303e942642dd22feab96f221c67f7027e
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 19:02:59 2026 +0000
feat(rules)!: rename the command kind's `run` column to `check`, reserve `fix`
House style §9 specifies the `command` rule kind as a check/fix duality: an
inspection-only `check` is the gate — the only side enforcement ever runs — and
an optional mutating `fix` repairs what it condemned. The shipped `Rule` carried
one `run` column, which does not express the split.
§2 declares no back-compatibility surface and no deprecation aliases, so every
config written against `run` becomes a breaking migration later. Today the key
appears in no shipped config at all — only fixtures, tests and the README — so
the rename costs nothing. It will not stay that way.
`fix` parses and is executed by nothing: serialised fix execution is a
capability this engine does not have. `enforce` refuses a row declaring one
rather than running the check side and silently ignoring the repair, which would
report green over work nobody did. Reserving the key now means an author who
writes it is writing the final spelling.
`run` survives as a refusal-only field, the shape `OverrideConfig::
min_batten_version` already uses: "unknown field `run`" reads as a typo, where
this is a rename with one specific fix, and the refusal has to carry it
(CLOUD-122). It is deliberately outside `Rule::columns` — that census classifies
the columns a kind may carry, and no kind carries this one.
Both refusals are exit 1, not the exit 2 the issue's §5/§7 asked for. §7 makes 2
the policy verdict — a violation found in the repository, or a mediated call
denied — while a config naming a key or a capability this build does not have is
the config-or-usage class. §5 spells the structurally identical sibling refusal
(a rule the read-only half cannot honestly run) as exit 1, and `run_static` has
implemented it that way since CLOUD-170; the issue's §3 describes that same
landed refusal as exit 2, which is falsified by the code it describes. Emitting
2 here would also make an engine gap read to a harness as a policy deny, which
is what the structural fail-open exists to prevent.
Refs: CLOUD-215Rebase locally, and then force push to |
4e9769b to
573b93b
Compare
|
/fast-forward |
|
Triggered from #310 (comment) by @wenzowski. Trying to fast forward Target branch ( commit b642e2dd35304790923f2a63bbbf0f9f68c9b4c2 (HEAD -> main, origin/main)
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 20:47:33 2026 +0000
perf(ci): run the shell suite in parallel, since it is the pole of the gate
`hk` runs two chains concurrently, and the shell one was the longer by 133s:
on run 31531728503 the cargo chain's last step finished 166s into `mise run
ci`, while `test:bats` ran to 247.01s of the step's 298.87s. 800 tests, one at
a time. `verify` runs the same gate, so a `land` lap paid it twice.
That put it in the exponent of the lap economics. A lap dies whenever `main`
moves under it, so with mean inter-land gap 487s the expected CI runs per
landed PR is e^(L/G) — which is why shortening the lap is the one lever that
cuts latency and the bill together rather than trading one for the other.
Measured here: 248s serial -> 72.73s, 808 tests, same assertions, nothing
skipped or sampled. The remaining pole is the cargo chain at 166s.
`rush` rather than GNU parallel, which bats would otherwise look for: GNU
parallel ships as a source tarball with no aqua/ubi/release coordinate, so it
cannot be a [tools] key at all, and an `apt-get install` in ci.yml is the
second-authority drift `ci-tools-check` exists to refuse. rush is prebuilt per
platform, so `mise.lock` holds a checksum for every platform this repo
installs on.
Across files, never within one. The suites that model lock and race behaviour
are precisely the ones a within-file schedule would interleave with their own
setup, and across-files is already enough — the largest file is 49 of 808
tests, so the schedule is not tail-bound.
Two things ship with it:
- `tests/attribution-check.bats` built its end-to-end fixture in the real
worktree and the real index. Serially that was invisible; in parallel it is
a race against the sibling case that reads the same tracked set, plus
.git/index.lock contention. It now builds a throwaway repo, which is the
convention issue-guard.bats already set.
- A speed-up is the kind of fix that rots silently — drop the flag and
everything still passes, just slower, and `timeout-check`'s 3.4x headroom
absorbs a return to serial without a red build. So the invocation is now a
gate over mise.toml: serial is an exit code, not a slower Tuesday.
Refs: CLOUD-386Pull request ( commit 573b93b152e69c4045e0282c803caafc69c32035 (pull_request/wenzowski/cloud-65-ship-a-single-binary-first-install-path-and-package-manager)
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 19:08:09 2026 +0000
feat(install): ship a single-binary-first install path with a verified download
Releases have carried seven target archives since CLOUD-108, with nothing on the
consuming side: no install script, no binstall metadata, and nothing asserting
that the asset name a consumer resolves is the one `mise-tasks/dist` writes.
"Single-binary-first" was an ordering claim the repo could not make.
install.sh is POSIX sh, assumes only what `curl … | sh` already proves is
present, and verifies every download against the SHA-256 digest the release API
reports — a mismatch, or an asset with no digest, installs nothing and there is
no flag to skip it. Measured: api.github.com answers pretty-printed, so the
parsing commits to neither wire form. The token reaches curl on stdin rather
than argv, where ps would show it.
install-check is the gate. dist owns archive naming and install.sh owns its
target list; the gate calls both rather than scraping either, derives the matrix
from the workflow that decides it, resolves the binstall template against the
same answer, and fails on a committed executable.
Refs: CLOUD-65Can't fast forward * 573b93b152e69c4045e0282c803caafc69c32035 feat(install): ship a single-binary-first install path with a verified download
| * b642e2dd35304790923f2a63bbbf0f9f68c9b4c2 perf(ci): run the shell suite in parallel, since it is the pole of the gate
|/
* 55321601f0fb1cf8e5d9c5a97129e36b11df62fb chore(deps): drop dev-dependencies that duplicate the normal ones
commit 55321601f0fb1cf8e5d9c5a97129e36b11df62fb
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 20:04:52 2026 +0000
chore(deps): drop dev-dependencies that duplicate the normal ones
`serde_json` was listed in both `[dependencies]` and `[dev-dependencies]`, and
so were `flate2`, `tar` and `toml` — the identical redundancy, in the same
table, filed as one bullet because one of the four was noticed. A normal
dependency is already in scope for every test target, so all four entries
declared nothing; the comment above them explained a provision-suite need that
`[dependencies]` had long since covered.
`jsonschema` stays: it is the one crate the tests link and the shipped binary
does not.
Predicate is the Ready block's — the crate and its test targets still build.
Refs: CLOUD-217Rebase locally, and then force push to |
|
/fast-forward |
|
Triggered from #310 (comment) by @wenzowski. Trying to fast forward Target branch ( commit 37e69f1f2bf15e960b74104a027df9b3a4d82026 (HEAD -> main, origin/main)
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 19:31:44 2026 +0000
build(release): publish a SHA256SUMS manifest and gate its coverage
A release carried no checksum of any kind, so every packaging channel that
consumes one was blocked: a Homebrew formula pins it, an aqua/mise registry
entry carries it, and `cargo binstall` resolves assets whose integrity nobody
could check. It is also the floor beneath CLOUD-264's signature decision and
independent of it.
`mise-tasks/checksums` hashes the release's OWN assets, read back after upload,
rather than a list restated in the workflow. The seven dist legs are independent
with `fail-fast: false`, so no leg can see the whole release — and deriving from
what is published means a future asset is covered by the edit that starts
publishing it, with no second edit here. The format is `sha256sum`'s own, so
verifying is the ordinary tool with no flags.
`release-assets-check` grows the rules that catch a manifest a packager would
otherwise discover: `checksums-missing`, `-omits`, `-orphan`, `-self`, `-empty`,
and `-mismatch`. The last one reads BYTES, not names: uploads are `--clobber`
idempotent and the stated recovery is a per-target re-run, so an asset can be
replaced after the manifest was cut and names alone would still agree.
Refs: CLOUD-278Pull request ( commit 80ada245f839c4efb546e5df5db15994a7aeb079 (pull_request/wenzowski/cloud-65-ship-a-single-binary-first-install-path-and-package-manager)
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 19:08:09 2026 +0000
feat(install): ship a single-binary-first install path with a verified download
Releases have carried seven target archives since CLOUD-108, with nothing on the
consuming side: no install script, no binstall metadata, and nothing asserting
that the asset name a consumer resolves is the one `mise-tasks/dist` writes.
"Single-binary-first" was an ordering claim the repo could not make.
install.sh is POSIX sh, assumes only what `curl … | sh` already proves is
present, and verifies every download against the SHA-256 digest the release API
reports — a mismatch, or an asset with no digest, installs nothing and there is
no flag to skip it. Measured: api.github.com answers pretty-printed, so the
parsing commits to neither wire form. The token reaches curl on stdin rather
than argv, where ps would show it.
install-check is the gate. dist owns archive naming and install.sh owns its
target list; the gate calls both rather than scraping either, derives the matrix
from the workflow that decides it, resolves the binstall template against the
same answer, and fails on a committed executable.
Refs: CLOUD-65Can't fast forward * 80ada245f839c4efb546e5df5db15994a7aeb079 feat(install): ship a single-binary-first install path with a verified download
| * 37e69f1f2bf15e960b74104a027df9b3a4d82026 build(release): publish a SHA256SUMS manifest and gate its coverage
|/
* eee9bb4bade0799923aff7d983911a169743d839 feat(config): gate the predecessor consumer's repo name
commit eee9bb4bade0799923aff7d983911a169743d839
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 21:10:41 2026 +0000
feat(config): gate the predecessor consumer's repo name
CLOUD-73 asks what stays in the consumer's repo and what moves into Batten. Its
structural half is settled by history — this repository was scaffolded fresh, so
none of the domain modules it names (entity grep, pdfium, CA privacy detectors,
evidence and provenance semantics, the ICM design protocol) has ever been here,
and there is no `tools/batten/` to keep them out of. Its other half is
non-negotiable rule 1, and that one had a hole.
CLOUD-26's acceptance named four literals. Two are rows; the comment beside them
dropped the other two on the ground that `forbid` was a case-sensitive literal
substring check, so a literal with an English reading was wrong in both
directions at once. CLOUD-283 gave `forbid` a `regex` alternative, which retires
that premise — so the two exclusions are re-decided on the merits rather than
inherited from a limit that no longer exists.
The predecessor consumer's repo name goes in, as `(?i)complian`: it is squarely
what rule 1 forbids, `(?i)` catches the capitalised form the old literal would
have missed, and it costs nothing — zero lines under crates/ carry it, and this
engine's vocabulary is policy / gate / predicate / verdict.
The vendor name stays out, and the comment now says that is a decision rather
than a gap: it names the vendor, not a consumer, `github.com/button-inc/batten`
is Batten's own home, and the consumer-facing half is already gated by
`no-origin-literal-in-fixtures`.
The fixture test spells the new shape with a capital, which is precisely the
half a literal could not have caught.
Refs: CLOUD-73Rebase locally, and then force push to |
80ada24 to
a141a2d
Compare
|
/fast-forward |
|
Triggered from #310 (comment) by @wenzowski. Trying to fast forward Target branch ( commit b913c980bda0a9ade266915b4bf527918576a4c7 (HEAD -> main, origin/main)
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 21:17:30 2026 +0000
docs(fanout): re-measure land contention — a lap now loses by arithmetic
The measurement recorded three hours earlier had `main` at a 487s mean gap
against a ~170s `verify`, giving N ≈ 2.9 with the WIP cap at 2 — "at the
ceiling", and the model held. It no longer does. Over `main`'s ten commits to
21:09Z the mean gap is 436s while a full lap is `verify` + CI + the
fast-forward wait, call it 8–10 minutes. CLOUD-122 lost 16 consecutive laps
across two `mise run land` invocations, every one green on CI.
The interesting half is a lap-loss shape the N model does not price. Three of
the sixteen were the documented case — `main` moved before CI finished, the
verdict void, cancelled early and cheap. Five were not: CI went green,
`/fast-forward` was posted, and the bot then stayed SILENT while `main` moved.
`land` infers a refusal from `main` moving rather than from the bot answering,
so the log reads as a refusal that never happened. The bot is not rejecting the
fast-forward; it is slower than `main`, and that silence is a third term the
verify + CI lap cost never included.
Raising `LAND_MAX_LAPS` buys more attempts per invocation without changing the
per-lap odds, so it spends CI minutes against a losing bet. The lever is
unchanged and now more urgent: shorten the lap, or quiet `main`; never add
sessions.
Refs: CLOUD-122Pull request ( commit a141a2d02a3b3cddd2194d305ba8cdafd5dc4b32 (pull_request/wenzowski/cloud-65-ship-a-single-binary-first-install-path-and-package-manager)
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 19:08:09 2026 +0000
feat(install): ship a single-binary-first install path with a verified download
Releases have carried seven target archives since CLOUD-108, with nothing on the
consuming side: no install script, no binstall metadata, and nothing asserting
that the asset name a consumer resolves is the one `mise-tasks/dist` writes.
"Single-binary-first" was an ordering claim the repo could not make.
install.sh is POSIX sh, assumes only what `curl … | sh` already proves is
present, and verifies every download against the SHA-256 digest the release API
reports — a mismatch, or an asset with no digest, installs nothing and there is
no flag to skip it. Measured: api.github.com answers pretty-printed, so the
parsing commits to neither wire form. The token reaches curl on stdin rather
than argv, where ps would show it.
install-check is the gate. dist owns archive naming and install.sh owns its
target list; the gate calls both rather than scraping either, derives the matrix
from the workflow that decides it, resolves the binstall template against the
same answer, and fails on a committed executable.
Refs: CLOUD-65Can't fast forward * b913c980bda0a9ade266915b4bf527918576a4c7 docs(fanout): re-measure land contention — a lap now loses by arithmetic
* a517ff7e7d3c690c25a9185a333d74c23db5f430 feat(refusal): one refusal type, so no deny can ship a bare "no"
| * a141a2d02a3b3cddd2194d305ba8cdafd5dc4b32 feat(install): ship a single-binary-first install path with a verified download
|/
* 37e69f1f2bf15e960b74104a027df9b3a4d82026 build(release): publish a SHA256SUMS manifest and gate its coverage
commit 37e69f1f2bf15e960b74104a027df9b3a4d82026
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 19:31:44 2026 +0000
build(release): publish a SHA256SUMS manifest and gate its coverage
A release carried no checksum of any kind, so every packaging channel that
consumes one was blocked: a Homebrew formula pins it, an aqua/mise registry
entry carries it, and `cargo binstall` resolves assets whose integrity nobody
could check. It is also the floor beneath CLOUD-264's signature decision and
independent of it.
`mise-tasks/checksums` hashes the release's OWN assets, read back after upload,
rather than a list restated in the workflow. The seven dist legs are independent
with `fail-fast: false`, so no leg can see the whole release — and deriving from
what is published means a future asset is covered by the edit that starts
publishing it, with no second edit here. The format is `sha256sum`'s own, so
verifying is the ordinary tool with no flags.
`release-assets-check` grows the rules that catch a manifest a packager would
otherwise discover: `checksums-missing`, `-omits`, `-orphan`, `-self`, `-empty`,
and `-mismatch`. The last one reads BYTES, not names: uploads are `--clobber`
idempotent and the stated recovery is a per-target re-run, so an asset can be
replaced after the manifest was cut and names alone would still agree.
Refs: CLOUD-278Rebase locally, and then force push to |
|
Triggered from #310 (comment) by @wenzowski. Trying to fast forward Target branch ( commit b913c980bda0a9ade266915b4bf527918576a4c7 (HEAD -> main, origin/main)
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 21:17:30 2026 +0000
docs(fanout): re-measure land contention — a lap now loses by arithmetic
The measurement recorded three hours earlier had `main` at a 487s mean gap
against a ~170s `verify`, giving N ≈ 2.9 with the WIP cap at 2 — "at the
ceiling", and the model held. It no longer does. Over `main`'s ten commits to
21:09Z the mean gap is 436s while a full lap is `verify` + CI + the
fast-forward wait, call it 8–10 minutes. CLOUD-122 lost 16 consecutive laps
across two `mise run land` invocations, every one green on CI.
The interesting half is a lap-loss shape the N model does not price. Three of
the sixteen were the documented case — `main` moved before CI finished, the
verdict void, cancelled early and cheap. Five were not: CI went green,
`/fast-forward` was posted, and the bot then stayed SILENT while `main` moved.
`land` infers a refusal from `main` moving rather than from the bot answering,
so the log reads as a refusal that never happened. The bot is not rejecting the
fast-forward; it is slower than `main`, and that silence is a third term the
verify + CI lap cost never included.
Raising `LAND_MAX_LAPS` buys more attempts per invocation without changing the
per-lap odds, so it spends CI minutes against a losing bet. The lever is
unchanged and now more urgent: shorten the lap, or quiet `main`; never add
sessions.
Refs: CLOUD-122Pull request ( commit a141a2d02a3b3cddd2194d305ba8cdafd5dc4b32 (pull_request/wenzowski/cloud-65-ship-a-single-binary-first-install-path-and-package-manager)
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 19:08:09 2026 +0000
feat(install): ship a single-binary-first install path with a verified download
Releases have carried seven target archives since CLOUD-108, with nothing on the
consuming side: no install script, no binstall metadata, and nothing asserting
that the asset name a consumer resolves is the one `mise-tasks/dist` writes.
"Single-binary-first" was an ordering claim the repo could not make.
install.sh is POSIX sh, assumes only what `curl … | sh` already proves is
present, and verifies every download against the SHA-256 digest the release API
reports — a mismatch, or an asset with no digest, installs nothing and there is
no flag to skip it. Measured: api.github.com answers pretty-printed, so the
parsing commits to neither wire form. The token reaches curl on stdin rather
than argv, where ps would show it.
install-check is the gate. dist owns archive naming and install.sh owns its
target list; the gate calls both rather than scraping either, derives the matrix
from the workflow that decides it, resolves the binstall template against the
same answer, and fails on a committed executable.
Refs: CLOUD-65Can't fast forward * b913c980bda0a9ade266915b4bf527918576a4c7 docs(fanout): re-measure land contention — a lap now loses by arithmetic
* a517ff7e7d3c690c25a9185a333d74c23db5f430 feat(refusal): one refusal type, so no deny can ship a bare "no"
| * a141a2d02a3b3cddd2194d305ba8cdafd5dc4b32 feat(install): ship a single-binary-first install path with a verified download
|/
* 37e69f1f2bf15e960b74104a027df9b3a4d82026 build(release): publish a SHA256SUMS manifest and gate its coverage
commit 37e69f1f2bf15e960b74104a027df9b3a4d82026
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 19:31:44 2026 +0000
build(release): publish a SHA256SUMS manifest and gate its coverage
A release carried no checksum of any kind, so every packaging channel that
consumes one was blocked: a Homebrew formula pins it, an aqua/mise registry
entry carries it, and `cargo binstall` resolves assets whose integrity nobody
could check. It is also the floor beneath CLOUD-264's signature decision and
independent of it.
`mise-tasks/checksums` hashes the release's OWN assets, read back after upload,
rather than a list restated in the workflow. The seven dist legs are independent
with `fail-fast: false`, so no leg can see the whole release — and deriving from
what is published means a future asset is covered by the edit that starts
publishing it, with no second edit here. The format is `sha256sum`'s own, so
verifying is the ordinary tool with no flags.
`release-assets-check` grows the rules that catch a manifest a packager would
otherwise discover: `checksums-missing`, `-omits`, `-orphan`, `-self`, `-empty`,
and `-mismatch`. The last one reads BYTES, not names: uploads are `--clobber`
idempotent and the stated recovery is a per-target re-run, so an asset can be
replaced after the manifest was cut and names alone would still agree.
Refs: CLOUD-278Rebase locally, and then force push to |
|
Triggered from #310 (comment) by @wenzowski. Trying to fast forward Target branch ( commit b913c980bda0a9ade266915b4bf527918576a4c7 (HEAD -> main, origin/main)
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 21:17:30 2026 +0000
docs(fanout): re-measure land contention — a lap now loses by arithmetic
The measurement recorded three hours earlier had `main` at a 487s mean gap
against a ~170s `verify`, giving N ≈ 2.9 with the WIP cap at 2 — "at the
ceiling", and the model held. It no longer does. Over `main`'s ten commits to
21:09Z the mean gap is 436s while a full lap is `verify` + CI + the
fast-forward wait, call it 8–10 minutes. CLOUD-122 lost 16 consecutive laps
across two `mise run land` invocations, every one green on CI.
The interesting half is a lap-loss shape the N model does not price. Three of
the sixteen were the documented case — `main` moved before CI finished, the
verdict void, cancelled early and cheap. Five were not: CI went green,
`/fast-forward` was posted, and the bot then stayed SILENT while `main` moved.
`land` infers a refusal from `main` moving rather than from the bot answering,
so the log reads as a refusal that never happened. The bot is not rejecting the
fast-forward; it is slower than `main`, and that silence is a third term the
verify + CI lap cost never included.
Raising `LAND_MAX_LAPS` buys more attempts per invocation without changing the
per-lap odds, so it spends CI minutes against a losing bet. The lever is
unchanged and now more urgent: shorten the lap, or quiet `main`; never add
sessions.
Refs: CLOUD-122Pull request ( commit a141a2d02a3b3cddd2194d305ba8cdafd5dc4b32 (pull_request/wenzowski/cloud-65-ship-a-single-binary-first-install-path-and-package-manager)
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 19:08:09 2026 +0000
feat(install): ship a single-binary-first install path with a verified download
Releases have carried seven target archives since CLOUD-108, with nothing on the
consuming side: no install script, no binstall metadata, and nothing asserting
that the asset name a consumer resolves is the one `mise-tasks/dist` writes.
"Single-binary-first" was an ordering claim the repo could not make.
install.sh is POSIX sh, assumes only what `curl … | sh` already proves is
present, and verifies every download against the SHA-256 digest the release API
reports — a mismatch, or an asset with no digest, installs nothing and there is
no flag to skip it. Measured: api.github.com answers pretty-printed, so the
parsing commits to neither wire form. The token reaches curl on stdin rather
than argv, where ps would show it.
install-check is the gate. dist owns archive naming and install.sh owns its
target list; the gate calls both rather than scraping either, derives the matrix
from the workflow that decides it, resolves the binstall template against the
same answer, and fails on a committed executable.
Refs: CLOUD-65Can't fast forward * b913c980bda0a9ade266915b4bf527918576a4c7 docs(fanout): re-measure land contention — a lap now loses by arithmetic
* a517ff7e7d3c690c25a9185a333d74c23db5f430 feat(refusal): one refusal type, so no deny can ship a bare "no"
| * a141a2d02a3b3cddd2194d305ba8cdafd5dc4b32 feat(install): ship a single-binary-first install path with a verified download
|/
* 37e69f1f2bf15e960b74104a027df9b3a4d82026 build(release): publish a SHA256SUMS manifest and gate its coverage
commit 37e69f1f2bf15e960b74104a027df9b3a4d82026
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 19:31:44 2026 +0000
build(release): publish a SHA256SUMS manifest and gate its coverage
A release carried no checksum of any kind, so every packaging channel that
consumes one was blocked: a Homebrew formula pins it, an aqua/mise registry
entry carries it, and `cargo binstall` resolves assets whose integrity nobody
could check. It is also the floor beneath CLOUD-264's signature decision and
independent of it.
`mise-tasks/checksums` hashes the release's OWN assets, read back after upload,
rather than a list restated in the workflow. The seven dist legs are independent
with `fail-fast: false`, so no leg can see the whole release — and deriving from
what is published means a future asset is covered by the edit that starts
publishing it, with no second edit here. The format is `sha256sum`'s own, so
verifying is the ordinary tool with no flags.
`release-assets-check` grows the rules that catch a manifest a packager would
otherwise discover: `checksums-missing`, `-omits`, `-orphan`, `-self`, `-empty`,
and `-mismatch`. The last one reads BYTES, not names: uploads are `--clobber`
idempotent and the stated recovery is a per-target re-run, so an asset can be
replaced after the manifest was cut and names alone would still agree.
Refs: CLOUD-278Rebase locally, and then force push to |
|
Triggered from #310 (comment) by @wenzowski. Trying to fast forward Target branch ( commit b913c980bda0a9ade266915b4bf527918576a4c7 (HEAD -> main, origin/main)
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 21:17:30 2026 +0000
docs(fanout): re-measure land contention — a lap now loses by arithmetic
The measurement recorded three hours earlier had `main` at a 487s mean gap
against a ~170s `verify`, giving N ≈ 2.9 with the WIP cap at 2 — "at the
ceiling", and the model held. It no longer does. Over `main`'s ten commits to
21:09Z the mean gap is 436s while a full lap is `verify` + CI + the
fast-forward wait, call it 8–10 minutes. CLOUD-122 lost 16 consecutive laps
across two `mise run land` invocations, every one green on CI.
The interesting half is a lap-loss shape the N model does not price. Three of
the sixteen were the documented case — `main` moved before CI finished, the
verdict void, cancelled early and cheap. Five were not: CI went green,
`/fast-forward` was posted, and the bot then stayed SILENT while `main` moved.
`land` infers a refusal from `main` moving rather than from the bot answering,
so the log reads as a refusal that never happened. The bot is not rejecting the
fast-forward; it is slower than `main`, and that silence is a third term the
verify + CI lap cost never included.
Raising `LAND_MAX_LAPS` buys more attempts per invocation without changing the
per-lap odds, so it spends CI minutes against a losing bet. The lever is
unchanged and now more urgent: shorten the lap, or quiet `main`; never add
sessions.
Refs: CLOUD-122Pull request ( commit a141a2d02a3b3cddd2194d305ba8cdafd5dc4b32 (pull_request/wenzowski/cloud-65-ship-a-single-binary-first-install-path-and-package-manager)
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 19:08:09 2026 +0000
feat(install): ship a single-binary-first install path with a verified download
Releases have carried seven target archives since CLOUD-108, with nothing on the
consuming side: no install script, no binstall metadata, and nothing asserting
that the asset name a consumer resolves is the one `mise-tasks/dist` writes.
"Single-binary-first" was an ordering claim the repo could not make.
install.sh is POSIX sh, assumes only what `curl … | sh` already proves is
present, and verifies every download against the SHA-256 digest the release API
reports — a mismatch, or an asset with no digest, installs nothing and there is
no flag to skip it. Measured: api.github.com answers pretty-printed, so the
parsing commits to neither wire form. The token reaches curl on stdin rather
than argv, where ps would show it.
install-check is the gate. dist owns archive naming and install.sh owns its
target list; the gate calls both rather than scraping either, derives the matrix
from the workflow that decides it, resolves the binstall template against the
same answer, and fails on a committed executable.
Refs: CLOUD-65Can't fast forward * b913c980bda0a9ade266915b4bf527918576a4c7 docs(fanout): re-measure land contention — a lap now loses by arithmetic
* a517ff7e7d3c690c25a9185a333d74c23db5f430 feat(refusal): one refusal type, so no deny can ship a bare "no"
| * a141a2d02a3b3cddd2194d305ba8cdafd5dc4b32 feat(install): ship a single-binary-first install path with a verified download
|/
* 37e69f1f2bf15e960b74104a027df9b3a4d82026 build(release): publish a SHA256SUMS manifest and gate its coverage
commit 37e69f1f2bf15e960b74104a027df9b3a4d82026
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 19:31:44 2026 +0000
build(release): publish a SHA256SUMS manifest and gate its coverage
A release carried no checksum of any kind, so every packaging channel that
consumes one was blocked: a Homebrew formula pins it, an aqua/mise registry
entry carries it, and `cargo binstall` resolves assets whose integrity nobody
could check. It is also the floor beneath CLOUD-264's signature decision and
independent of it.
`mise-tasks/checksums` hashes the release's OWN assets, read back after upload,
rather than a list restated in the workflow. The seven dist legs are independent
with `fail-fast: false`, so no leg can see the whole release — and deriving from
what is published means a future asset is covered by the edit that starts
publishing it, with no second edit here. The format is `sha256sum`'s own, so
verifying is the ordinary tool with no flags.
`release-assets-check` grows the rules that catch a manifest a packager would
otherwise discover: `checksums-missing`, `-omits`, `-orphan`, `-self`, `-empty`,
and `-mismatch`. The last one reads BYTES, not names: uploads are `--clobber`
idempotent and the stated recovery is a per-target re-run, so an asset can be
replaced after the manifest was cut and names alone would still agree.
Refs: CLOUD-278Rebase locally, and then force push to |
|
Triggered from #310 (comment) by @wenzowski. Trying to fast forward Target branch ( commit b913c980bda0a9ade266915b4bf527918576a4c7 (HEAD -> main, origin/main)
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 21:17:30 2026 +0000
docs(fanout): re-measure land contention — a lap now loses by arithmetic
The measurement recorded three hours earlier had `main` at a 487s mean gap
against a ~170s `verify`, giving N ≈ 2.9 with the WIP cap at 2 — "at the
ceiling", and the model held. It no longer does. Over `main`'s ten commits to
21:09Z the mean gap is 436s while a full lap is `verify` + CI + the
fast-forward wait, call it 8–10 minutes. CLOUD-122 lost 16 consecutive laps
across two `mise run land` invocations, every one green on CI.
The interesting half is a lap-loss shape the N model does not price. Three of
the sixteen were the documented case — `main` moved before CI finished, the
verdict void, cancelled early and cheap. Five were not: CI went green,
`/fast-forward` was posted, and the bot then stayed SILENT while `main` moved.
`land` infers a refusal from `main` moving rather than from the bot answering,
so the log reads as a refusal that never happened. The bot is not rejecting the
fast-forward; it is slower than `main`, and that silence is a third term the
verify + CI lap cost never included.
Raising `LAND_MAX_LAPS` buys more attempts per invocation without changing the
per-lap odds, so it spends CI minutes against a losing bet. The lever is
unchanged and now more urgent: shorten the lap, or quiet `main`; never add
sessions.
Refs: CLOUD-122Pull request ( commit a141a2d02a3b3cddd2194d305ba8cdafd5dc4b32 (pull_request/wenzowski/cloud-65-ship-a-single-binary-first-install-path-and-package-manager)
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 19:08:09 2026 +0000
feat(install): ship a single-binary-first install path with a verified download
Releases have carried seven target archives since CLOUD-108, with nothing on the
consuming side: no install script, no binstall metadata, and nothing asserting
that the asset name a consumer resolves is the one `mise-tasks/dist` writes.
"Single-binary-first" was an ordering claim the repo could not make.
install.sh is POSIX sh, assumes only what `curl … | sh` already proves is
present, and verifies every download against the SHA-256 digest the release API
reports — a mismatch, or an asset with no digest, installs nothing and there is
no flag to skip it. Measured: api.github.com answers pretty-printed, so the
parsing commits to neither wire form. The token reaches curl on stdin rather
than argv, where ps would show it.
install-check is the gate. dist owns archive naming and install.sh owns its
target list; the gate calls both rather than scraping either, derives the matrix
from the workflow that decides it, resolves the binstall template against the
same answer, and fails on a committed executable.
Refs: CLOUD-65Can't fast forward * b913c980bda0a9ade266915b4bf527918576a4c7 docs(fanout): re-measure land contention — a lap now loses by arithmetic
* a517ff7e7d3c690c25a9185a333d74c23db5f430 feat(refusal): one refusal type, so no deny can ship a bare "no"
| * a141a2d02a3b3cddd2194d305ba8cdafd5dc4b32 feat(install): ship a single-binary-first install path with a verified download
|/
* 37e69f1f2bf15e960b74104a027df9b3a4d82026 build(release): publish a SHA256SUMS manifest and gate its coverage
commit 37e69f1f2bf15e960b74104a027df9b3a4d82026
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 19:31:44 2026 +0000
build(release): publish a SHA256SUMS manifest and gate its coverage
A release carried no checksum of any kind, so every packaging channel that
consumes one was blocked: a Homebrew formula pins it, an aqua/mise registry
entry carries it, and `cargo binstall` resolves assets whose integrity nobody
could check. It is also the floor beneath CLOUD-264's signature decision and
independent of it.
`mise-tasks/checksums` hashes the release's OWN assets, read back after upload,
rather than a list restated in the workflow. The seven dist legs are independent
with `fail-fast: false`, so no leg can see the whole release — and deriving from
what is published means a future asset is covered by the edit that starts
publishing it, with no second edit here. The format is `sha256sum`'s own, so
verifying is the ordinary tool with no flags.
`release-assets-check` grows the rules that catch a manifest a packager would
otherwise discover: `checksums-missing`, `-omits`, `-orphan`, `-self`, `-empty`,
and `-mismatch`. The last one reads BYTES, not names: uploads are `--clobber`
idempotent and the stated recovery is a per-target re-run, so an asset can be
replaced after the manifest was cut and names alone would still agree.
Refs: CLOUD-278Rebase locally, and then force push to |
|
Triggered from #310 (comment) by @wenzowski. Trying to fast forward Target branch ( commit b913c980bda0a9ade266915b4bf527918576a4c7 (HEAD -> main, origin/main)
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 21:17:30 2026 +0000
docs(fanout): re-measure land contention — a lap now loses by arithmetic
The measurement recorded three hours earlier had `main` at a 487s mean gap
against a ~170s `verify`, giving N ≈ 2.9 with the WIP cap at 2 — "at the
ceiling", and the model held. It no longer does. Over `main`'s ten commits to
21:09Z the mean gap is 436s while a full lap is `verify` + CI + the
fast-forward wait, call it 8–10 minutes. CLOUD-122 lost 16 consecutive laps
across two `mise run land` invocations, every one green on CI.
The interesting half is a lap-loss shape the N model does not price. Three of
the sixteen were the documented case — `main` moved before CI finished, the
verdict void, cancelled early and cheap. Five were not: CI went green,
`/fast-forward` was posted, and the bot then stayed SILENT while `main` moved.
`land` infers a refusal from `main` moving rather than from the bot answering,
so the log reads as a refusal that never happened. The bot is not rejecting the
fast-forward; it is slower than `main`, and that silence is a third term the
verify + CI lap cost never included.
Raising `LAND_MAX_LAPS` buys more attempts per invocation without changing the
per-lap odds, so it spends CI minutes against a losing bet. The lever is
unchanged and now more urgent: shorten the lap, or quiet `main`; never add
sessions.
Refs: CLOUD-122Pull request ( commit a141a2d02a3b3cddd2194d305ba8cdafd5dc4b32 (pull_request/wenzowski/cloud-65-ship-a-single-binary-first-install-path-and-package-manager)
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 19:08:09 2026 +0000
feat(install): ship a single-binary-first install path with a verified download
Releases have carried seven target archives since CLOUD-108, with nothing on the
consuming side: no install script, no binstall metadata, and nothing asserting
that the asset name a consumer resolves is the one `mise-tasks/dist` writes.
"Single-binary-first" was an ordering claim the repo could not make.
install.sh is POSIX sh, assumes only what `curl … | sh` already proves is
present, and verifies every download against the SHA-256 digest the release API
reports — a mismatch, or an asset with no digest, installs nothing and there is
no flag to skip it. Measured: api.github.com answers pretty-printed, so the
parsing commits to neither wire form. The token reaches curl on stdin rather
than argv, where ps would show it.
install-check is the gate. dist owns archive naming and install.sh owns its
target list; the gate calls both rather than scraping either, derives the matrix
from the workflow that decides it, resolves the binstall template against the
same answer, and fails on a committed executable.
Refs: CLOUD-65Can't fast forward * b913c980bda0a9ade266915b4bf527918576a4c7 docs(fanout): re-measure land contention — a lap now loses by arithmetic
* a517ff7e7d3c690c25a9185a333d74c23db5f430 feat(refusal): one refusal type, so no deny can ship a bare "no"
| * a141a2d02a3b3cddd2194d305ba8cdafd5dc4b32 feat(install): ship a single-binary-first install path with a verified download
|/
* 37e69f1f2bf15e960b74104a027df9b3a4d82026 build(release): publish a SHA256SUMS manifest and gate its coverage
commit 37e69f1f2bf15e960b74104a027df9b3a4d82026
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 19:31:44 2026 +0000
build(release): publish a SHA256SUMS manifest and gate its coverage
A release carried no checksum of any kind, so every packaging channel that
consumes one was blocked: a Homebrew formula pins it, an aqua/mise registry
entry carries it, and `cargo binstall` resolves assets whose integrity nobody
could check. It is also the floor beneath CLOUD-264's signature decision and
independent of it.
`mise-tasks/checksums` hashes the release's OWN assets, read back after upload,
rather than a list restated in the workflow. The seven dist legs are independent
with `fail-fast: false`, so no leg can see the whole release — and deriving from
what is published means a future asset is covered by the edit that starts
publishing it, with no second edit here. The format is `sha256sum`'s own, so
verifying is the ordinary tool with no flags.
`release-assets-check` grows the rules that catch a manifest a packager would
otherwise discover: `checksums-missing`, `-omits`, `-orphan`, `-self`, `-empty`,
and `-mismatch`. The last one reads BYTES, not names: uploads are `--clobber`
idempotent and the stated recovery is a per-target re-run, so an asset can be
replaced after the manifest was cut and names alone would still agree.
Refs: CLOUD-278Rebase locally, and then force push to |
|
Triggered from #310 (comment) by @wenzowski. Trying to fast forward Target branch ( commit b913c980bda0a9ade266915b4bf527918576a4c7 (HEAD -> main, origin/main)
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 21:17:30 2026 +0000
docs(fanout): re-measure land contention — a lap now loses by arithmetic
The measurement recorded three hours earlier had `main` at a 487s mean gap
against a ~170s `verify`, giving N ≈ 2.9 with the WIP cap at 2 — "at the
ceiling", and the model held. It no longer does. Over `main`'s ten commits to
21:09Z the mean gap is 436s while a full lap is `verify` + CI + the
fast-forward wait, call it 8–10 minutes. CLOUD-122 lost 16 consecutive laps
across two `mise run land` invocations, every one green on CI.
The interesting half is a lap-loss shape the N model does not price. Three of
the sixteen were the documented case — `main` moved before CI finished, the
verdict void, cancelled early and cheap. Five were not: CI went green,
`/fast-forward` was posted, and the bot then stayed SILENT while `main` moved.
`land` infers a refusal from `main` moving rather than from the bot answering,
so the log reads as a refusal that never happened. The bot is not rejecting the
fast-forward; it is slower than `main`, and that silence is a third term the
verify + CI lap cost never included.
Raising `LAND_MAX_LAPS` buys more attempts per invocation without changing the
per-lap odds, so it spends CI minutes against a losing bet. The lever is
unchanged and now more urgent: shorten the lap, or quiet `main`; never add
sessions.
Refs: CLOUD-122Pull request ( commit a141a2d02a3b3cddd2194d305ba8cdafd5dc4b32 (pull_request/wenzowski/cloud-65-ship-a-single-binary-first-install-path-and-package-manager)
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 19:08:09 2026 +0000
feat(install): ship a single-binary-first install path with a verified download
Releases have carried seven target archives since CLOUD-108, with nothing on the
consuming side: no install script, no binstall metadata, and nothing asserting
that the asset name a consumer resolves is the one `mise-tasks/dist` writes.
"Single-binary-first" was an ordering claim the repo could not make.
install.sh is POSIX sh, assumes only what `curl … | sh` already proves is
present, and verifies every download against the SHA-256 digest the release API
reports — a mismatch, or an asset with no digest, installs nothing and there is
no flag to skip it. Measured: api.github.com answers pretty-printed, so the
parsing commits to neither wire form. The token reaches curl on stdin rather
than argv, where ps would show it.
install-check is the gate. dist owns archive naming and install.sh owns its
target list; the gate calls both rather than scraping either, derives the matrix
from the workflow that decides it, resolves the binstall template against the
same answer, and fails on a committed executable.
Refs: CLOUD-65Can't fast forward * b913c980bda0a9ade266915b4bf527918576a4c7 docs(fanout): re-measure land contention — a lap now loses by arithmetic
* a517ff7e7d3c690c25a9185a333d74c23db5f430 feat(refusal): one refusal type, so no deny can ship a bare "no"
| * a141a2d02a3b3cddd2194d305ba8cdafd5dc4b32 feat(install): ship a single-binary-first install path with a verified download
|/
* 37e69f1f2bf15e960b74104a027df9b3a4d82026 build(release): publish a SHA256SUMS manifest and gate its coverage
commit 37e69f1f2bf15e960b74104a027df9b3a4d82026
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 19:31:44 2026 +0000
build(release): publish a SHA256SUMS manifest and gate its coverage
A release carried no checksum of any kind, so every packaging channel that
consumes one was blocked: a Homebrew formula pins it, an aqua/mise registry
entry carries it, and `cargo binstall` resolves assets whose integrity nobody
could check. It is also the floor beneath CLOUD-264's signature decision and
independent of it.
`mise-tasks/checksums` hashes the release's OWN assets, read back after upload,
rather than a list restated in the workflow. The seven dist legs are independent
with `fail-fast: false`, so no leg can see the whole release — and deriving from
what is published means a future asset is covered by the edit that starts
publishing it, with no second edit here. The format is `sha256sum`'s own, so
verifying is the ordinary tool with no flags.
`release-assets-check` grows the rules that catch a manifest a packager would
otherwise discover: `checksums-missing`, `-omits`, `-orphan`, `-self`, `-empty`,
and `-mismatch`. The last one reads BYTES, not names: uploads are `--clobber`
idempotent and the stated recovery is a per-target re-run, so an asset can be
replaced after the manifest was cut and names alone would still agree.
Refs: CLOUD-278Rebase locally, and then force push to |
|
Triggered from #310 (comment) by @wenzowski. Trying to fast forward Target branch ( commit b913c980bda0a9ade266915b4bf527918576a4c7 (HEAD -> main, origin/main)
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 21:17:30 2026 +0000
docs(fanout): re-measure land contention — a lap now loses by arithmetic
The measurement recorded three hours earlier had `main` at a 487s mean gap
against a ~170s `verify`, giving N ≈ 2.9 with the WIP cap at 2 — "at the
ceiling", and the model held. It no longer does. Over `main`'s ten commits to
21:09Z the mean gap is 436s while a full lap is `verify` + CI + the
fast-forward wait, call it 8–10 minutes. CLOUD-122 lost 16 consecutive laps
across two `mise run land` invocations, every one green on CI.
The interesting half is a lap-loss shape the N model does not price. Three of
the sixteen were the documented case — `main` moved before CI finished, the
verdict void, cancelled early and cheap. Five were not: CI went green,
`/fast-forward` was posted, and the bot then stayed SILENT while `main` moved.
`land` infers a refusal from `main` moving rather than from the bot answering,
so the log reads as a refusal that never happened. The bot is not rejecting the
fast-forward; it is slower than `main`, and that silence is a third term the
verify + CI lap cost never included.
Raising `LAND_MAX_LAPS` buys more attempts per invocation without changing the
per-lap odds, so it spends CI minutes against a losing bet. The lever is
unchanged and now more urgent: shorten the lap, or quiet `main`; never add
sessions.
Refs: CLOUD-122Pull request ( commit a141a2d02a3b3cddd2194d305ba8cdafd5dc4b32 (pull_request/wenzowski/cloud-65-ship-a-single-binary-first-install-path-and-package-manager)
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 19:08:09 2026 +0000
feat(install): ship a single-binary-first install path with a verified download
Releases have carried seven target archives since CLOUD-108, with nothing on the
consuming side: no install script, no binstall metadata, and nothing asserting
that the asset name a consumer resolves is the one `mise-tasks/dist` writes.
"Single-binary-first" was an ordering claim the repo could not make.
install.sh is POSIX sh, assumes only what `curl … | sh` already proves is
present, and verifies every download against the SHA-256 digest the release API
reports — a mismatch, or an asset with no digest, installs nothing and there is
no flag to skip it. Measured: api.github.com answers pretty-printed, so the
parsing commits to neither wire form. The token reaches curl on stdin rather
than argv, where ps would show it.
install-check is the gate. dist owns archive naming and install.sh owns its
target list; the gate calls both rather than scraping either, derives the matrix
from the workflow that decides it, resolves the binstall template against the
same answer, and fails on a committed executable.
Refs: CLOUD-65Can't fast forward * b913c980bda0a9ade266915b4bf527918576a4c7 docs(fanout): re-measure land contention — a lap now loses by arithmetic
* a517ff7e7d3c690c25a9185a333d74c23db5f430 feat(refusal): one refusal type, so no deny can ship a bare "no"
| * a141a2d02a3b3cddd2194d305ba8cdafd5dc4b32 feat(install): ship a single-binary-first install path with a verified download
|/
* 37e69f1f2bf15e960b74104a027df9b3a4d82026 build(release): publish a SHA256SUMS manifest and gate its coverage
commit 37e69f1f2bf15e960b74104a027df9b3a4d82026
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 19:31:44 2026 +0000
build(release): publish a SHA256SUMS manifest and gate its coverage
A release carried no checksum of any kind, so every packaging channel that
consumes one was blocked: a Homebrew formula pins it, an aqua/mise registry
entry carries it, and `cargo binstall` resolves assets whose integrity nobody
could check. It is also the floor beneath CLOUD-264's signature decision and
independent of it.
`mise-tasks/checksums` hashes the release's OWN assets, read back after upload,
rather than a list restated in the workflow. The seven dist legs are independent
with `fail-fast: false`, so no leg can see the whole release — and deriving from
what is published means a future asset is covered by the edit that starts
publishing it, with no second edit here. The format is `sha256sum`'s own, so
verifying is the ordinary tool with no flags.
`release-assets-check` grows the rules that catch a manifest a packager would
otherwise discover: `checksums-missing`, `-omits`, `-orphan`, `-self`, `-empty`,
and `-mismatch`. The last one reads BYTES, not names: uploads are `--clobber`
idempotent and the stated recovery is a per-target re-run, so an asset can be
replaced after the manifest was cut and names alone would still agree.
Refs: CLOUD-278Rebase locally, and then force push to |
|
Triggered from #310 (comment) by @wenzowski. Trying to fast forward Target branch ( commit b913c980bda0a9ade266915b4bf527918576a4c7 (HEAD -> main, origin/main)
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 21:17:30 2026 +0000
docs(fanout): re-measure land contention — a lap now loses by arithmetic
The measurement recorded three hours earlier had `main` at a 487s mean gap
against a ~170s `verify`, giving N ≈ 2.9 with the WIP cap at 2 — "at the
ceiling", and the model held. It no longer does. Over `main`'s ten commits to
21:09Z the mean gap is 436s while a full lap is `verify` + CI + the
fast-forward wait, call it 8–10 minutes. CLOUD-122 lost 16 consecutive laps
across two `mise run land` invocations, every one green on CI.
The interesting half is a lap-loss shape the N model does not price. Three of
the sixteen were the documented case — `main` moved before CI finished, the
verdict void, cancelled early and cheap. Five were not: CI went green,
`/fast-forward` was posted, and the bot then stayed SILENT while `main` moved.
`land` infers a refusal from `main` moving rather than from the bot answering,
so the log reads as a refusal that never happened. The bot is not rejecting the
fast-forward; it is slower than `main`, and that silence is a third term the
verify + CI lap cost never included.
Raising `LAND_MAX_LAPS` buys more attempts per invocation without changing the
per-lap odds, so it spends CI minutes against a losing bet. The lever is
unchanged and now more urgent: shorten the lap, or quiet `main`; never add
sessions.
Refs: CLOUD-122Pull request ( commit a141a2d02a3b3cddd2194d305ba8cdafd5dc4b32 (pull_request/wenzowski/cloud-65-ship-a-single-binary-first-install-path-and-package-manager)
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 19:08:09 2026 +0000
feat(install): ship a single-binary-first install path with a verified download
Releases have carried seven target archives since CLOUD-108, with nothing on the
consuming side: no install script, no binstall metadata, and nothing asserting
that the asset name a consumer resolves is the one `mise-tasks/dist` writes.
"Single-binary-first" was an ordering claim the repo could not make.
install.sh is POSIX sh, assumes only what `curl … | sh` already proves is
present, and verifies every download against the SHA-256 digest the release API
reports — a mismatch, or an asset with no digest, installs nothing and there is
no flag to skip it. Measured: api.github.com answers pretty-printed, so the
parsing commits to neither wire form. The token reaches curl on stdin rather
than argv, where ps would show it.
install-check is the gate. dist owns archive naming and install.sh owns its
target list; the gate calls both rather than scraping either, derives the matrix
from the workflow that decides it, resolves the binstall template against the
same answer, and fails on a committed executable.
Refs: CLOUD-65Can't fast forward * b913c980bda0a9ade266915b4bf527918576a4c7 docs(fanout): re-measure land contention — a lap now loses by arithmetic
* a517ff7e7d3c690c25a9185a333d74c23db5f430 feat(refusal): one refusal type, so no deny can ship a bare "no"
| * a141a2d02a3b3cddd2194d305ba8cdafd5dc4b32 feat(install): ship a single-binary-first install path with a verified download
|/
* 37e69f1f2bf15e960b74104a027df9b3a4d82026 build(release): publish a SHA256SUMS manifest and gate its coverage
commit 37e69f1f2bf15e960b74104a027df9b3a4d82026
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 19:31:44 2026 +0000
build(release): publish a SHA256SUMS manifest and gate its coverage
A release carried no checksum of any kind, so every packaging channel that
consumes one was blocked: a Homebrew formula pins it, an aqua/mise registry
entry carries it, and `cargo binstall` resolves assets whose integrity nobody
could check. It is also the floor beneath CLOUD-264's signature decision and
independent of it.
`mise-tasks/checksums` hashes the release's OWN assets, read back after upload,
rather than a list restated in the workflow. The seven dist legs are independent
with `fail-fast: false`, so no leg can see the whole release — and deriving from
what is published means a future asset is covered by the edit that starts
publishing it, with no second edit here. The format is `sha256sum`'s own, so
verifying is the ordinary tool with no flags.
`release-assets-check` grows the rules that catch a manifest a packager would
otherwise discover: `checksums-missing`, `-omits`, `-orphan`, `-self`, `-empty`,
and `-mismatch`. The last one reads BYTES, not names: uploads are `--clobber`
idempotent and the stated recovery is a per-target re-run, so an asset can be
replaced after the manifest was cut and names alone would still agree.
Refs: CLOUD-278Rebase locally, and then force push to |
|
Triggered from #310 (comment) by @wenzowski. Trying to fast forward Target branch ( commit b913c980bda0a9ade266915b4bf527918576a4c7 (HEAD -> main, origin/main)
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 21:17:30 2026 +0000
docs(fanout): re-measure land contention — a lap now loses by arithmetic
The measurement recorded three hours earlier had `main` at a 487s mean gap
against a ~170s `verify`, giving N ≈ 2.9 with the WIP cap at 2 — "at the
ceiling", and the model held. It no longer does. Over `main`'s ten commits to
21:09Z the mean gap is 436s while a full lap is `verify` + CI + the
fast-forward wait, call it 8–10 minutes. CLOUD-122 lost 16 consecutive laps
across two `mise run land` invocations, every one green on CI.
The interesting half is a lap-loss shape the N model does not price. Three of
the sixteen were the documented case — `main` moved before CI finished, the
verdict void, cancelled early and cheap. Five were not: CI went green,
`/fast-forward` was posted, and the bot then stayed SILENT while `main` moved.
`land` infers a refusal from `main` moving rather than from the bot answering,
so the log reads as a refusal that never happened. The bot is not rejecting the
fast-forward; it is slower than `main`, and that silence is a third term the
verify + CI lap cost never included.
Raising `LAND_MAX_LAPS` buys more attempts per invocation without changing the
per-lap odds, so it spends CI minutes against a losing bet. The lever is
unchanged and now more urgent: shorten the lap, or quiet `main`; never add
sessions.
Refs: CLOUD-122Pull request ( commit a141a2d02a3b3cddd2194d305ba8cdafd5dc4b32 (pull_request/wenzowski/cloud-65-ship-a-single-binary-first-install-path-and-package-manager)
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 19:08:09 2026 +0000
feat(install): ship a single-binary-first install path with a verified download
Releases have carried seven target archives since CLOUD-108, with nothing on the
consuming side: no install script, no binstall metadata, and nothing asserting
that the asset name a consumer resolves is the one `mise-tasks/dist` writes.
"Single-binary-first" was an ordering claim the repo could not make.
install.sh is POSIX sh, assumes only what `curl … | sh` already proves is
present, and verifies every download against the SHA-256 digest the release API
reports — a mismatch, or an asset with no digest, installs nothing and there is
no flag to skip it. Measured: api.github.com answers pretty-printed, so the
parsing commits to neither wire form. The token reaches curl on stdin rather
than argv, where ps would show it.
install-check is the gate. dist owns archive naming and install.sh owns its
target list; the gate calls both rather than scraping either, derives the matrix
from the workflow that decides it, resolves the binstall template against the
same answer, and fails on a committed executable.
Refs: CLOUD-65Can't fast forward * b913c980bda0a9ade266915b4bf527918576a4c7 docs(fanout): re-measure land contention — a lap now loses by arithmetic
* a517ff7e7d3c690c25a9185a333d74c23db5f430 feat(refusal): one refusal type, so no deny can ship a bare "no"
| * a141a2d02a3b3cddd2194d305ba8cdafd5dc4b32 feat(install): ship a single-binary-first install path with a verified download
|/
* 37e69f1f2bf15e960b74104a027df9b3a4d82026 build(release): publish a SHA256SUMS manifest and gate its coverage
commit 37e69f1f2bf15e960b74104a027df9b3a4d82026
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 19:31:44 2026 +0000
build(release): publish a SHA256SUMS manifest and gate its coverage
A release carried no checksum of any kind, so every packaging channel that
consumes one was blocked: a Homebrew formula pins it, an aqua/mise registry
entry carries it, and `cargo binstall` resolves assets whose integrity nobody
could check. It is also the floor beneath CLOUD-264's signature decision and
independent of it.
`mise-tasks/checksums` hashes the release's OWN assets, read back after upload,
rather than a list restated in the workflow. The seven dist legs are independent
with `fail-fast: false`, so no leg can see the whole release — and deriving from
what is published means a future asset is covered by the edit that starts
publishing it, with no second edit here. The format is `sha256sum`'s own, so
verifying is the ordinary tool with no flags.
`release-assets-check` grows the rules that catch a manifest a packager would
otherwise discover: `checksums-missing`, `-omits`, `-orphan`, `-self`, `-empty`,
and `-mismatch`. The last one reads BYTES, not names: uploads are `--clobber`
idempotent and the stated recovery is a per-target re-run, so an asset can be
replaced after the manifest was cut and names alone would still agree.
Refs: CLOUD-278Rebase locally, and then force push to |
a141a2d to
b490fa0
Compare
|
Triggered from #310 (comment) by @wenzowski. Trying to fast forward Target branch ( commit b913c980bda0a9ade266915b4bf527918576a4c7 (HEAD -> main, origin/main)
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 21:17:30 2026 +0000
docs(fanout): re-measure land contention — a lap now loses by arithmetic
The measurement recorded three hours earlier had `main` at a 487s mean gap
against a ~170s `verify`, giving N ≈ 2.9 with the WIP cap at 2 — "at the
ceiling", and the model held. It no longer does. Over `main`'s ten commits to
21:09Z the mean gap is 436s while a full lap is `verify` + CI + the
fast-forward wait, call it 8–10 minutes. CLOUD-122 lost 16 consecutive laps
across two `mise run land` invocations, every one green on CI.
The interesting half is a lap-loss shape the N model does not price. Three of
the sixteen were the documented case — `main` moved before CI finished, the
verdict void, cancelled early and cheap. Five were not: CI went green,
`/fast-forward` was posted, and the bot then stayed SILENT while `main` moved.
`land` infers a refusal from `main` moving rather than from the bot answering,
so the log reads as a refusal that never happened. The bot is not rejecting the
fast-forward; it is slower than `main`, and that silence is a third term the
verify + CI lap cost never included.
Raising `LAND_MAX_LAPS` buys more attempts per invocation without changing the
per-lap odds, so it spends CI minutes against a losing bet. The lever is
unchanged and now more urgent: shorten the lap, or quiet `main`; never add
sessions.
Refs: CLOUD-122Pull request ( commit b490fa05e8b70055f712f61cc6b2d6b2b9bace09 (pull_request/wenzowski/cloud-65-ship-a-single-binary-first-install-path-and-package-manager)
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 19:08:09 2026 +0000
feat(install): ship a single-binary-first install path with a verified download
Releases have carried seven target archives since CLOUD-108, with nothing on the
consuming side: no install script, no binstall metadata, and nothing asserting
that the asset name a consumer resolves is the one `mise-tasks/dist` writes.
"Single-binary-first" was an ordering claim the repo could not make.
install.sh is POSIX sh, assumes only what `curl … | sh` already proves is
present, and verifies every download against the SHA-256 digest the release API
reports — a mismatch, or an asset with no digest, installs nothing and there is
no flag to skip it. Measured: api.github.com answers pretty-printed, so the
parsing commits to neither wire form. The token reaches curl on stdin rather
than argv, where ps would show it.
install-check is the gate. dist owns archive naming and install.sh owns its
target list; the gate calls both rather than scraping either, derives the matrix
from the workflow that decides it, resolves the binstall template against the
same answer, and fails on a committed executable.
Refs: CLOUD-65Fast forwarding $ git push origin b490fa05e8b70055f712f61cc6b2d6b2b9bace09:main
remote: error: GH013: Repository rule violations found for refs/heads/main.
remote: Review all repository rules at https://github.com/button-inc/batten/rules?ref=refs%2Fheads%2Fmain
remote:
remote: - Required status check "final" is expected.
remote:
To https://github.com/button-inc/batten.git
! [remote rejected] b490fa05e8b70055f712f61cc6b2d6b2b9bace09 -> main (push declined due to repository rule violations)
error: failed to push some refs to 'https://github.com/button-inc/batten.git' |
|
Triggered from #310 (comment) by @wenzowski. Trying to fast forward Target branch ( commit b913c980bda0a9ade266915b4bf527918576a4c7 (HEAD -> main, origin/main)
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 21:17:30 2026 +0000
docs(fanout): re-measure land contention — a lap now loses by arithmetic
The measurement recorded three hours earlier had `main` at a 487s mean gap
against a ~170s `verify`, giving N ≈ 2.9 with the WIP cap at 2 — "at the
ceiling", and the model held. It no longer does. Over `main`'s ten commits to
21:09Z the mean gap is 436s while a full lap is `verify` + CI + the
fast-forward wait, call it 8–10 minutes. CLOUD-122 lost 16 consecutive laps
across two `mise run land` invocations, every one green on CI.
The interesting half is a lap-loss shape the N model does not price. Three of
the sixteen were the documented case — `main` moved before CI finished, the
verdict void, cancelled early and cheap. Five were not: CI went green,
`/fast-forward` was posted, and the bot then stayed SILENT while `main` moved.
`land` infers a refusal from `main` moving rather than from the bot answering,
so the log reads as a refusal that never happened. The bot is not rejecting the
fast-forward; it is slower than `main`, and that silence is a third term the
verify + CI lap cost never included.
Raising `LAND_MAX_LAPS` buys more attempts per invocation without changing the
per-lap odds, so it spends CI minutes against a losing bet. The lever is
unchanged and now more urgent: shorten the lap, or quiet `main`; never add
sessions.
Refs: CLOUD-122Pull request ( commit b490fa05e8b70055f712f61cc6b2d6b2b9bace09 (pull_request/wenzowski/cloud-65-ship-a-single-binary-first-install-path-and-package-manager)
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 19:08:09 2026 +0000
feat(install): ship a single-binary-first install path with a verified download
Releases have carried seven target archives since CLOUD-108, with nothing on the
consuming side: no install script, no binstall metadata, and nothing asserting
that the asset name a consumer resolves is the one `mise-tasks/dist` writes.
"Single-binary-first" was an ordering claim the repo could not make.
install.sh is POSIX sh, assumes only what `curl … | sh` already proves is
present, and verifies every download against the SHA-256 digest the release API
reports — a mismatch, or an asset with no digest, installs nothing and there is
no flag to skip it. Measured: api.github.com answers pretty-printed, so the
parsing commits to neither wire form. The token reaches curl on stdin rather
than argv, where ps would show it.
install-check is the gate. dist owns archive naming and install.sh owns its
target list; the gate calls both rather than scraping either, derives the matrix
from the workflow that decides it, resolves the binstall template against the
same answer, and fails on a committed executable.
Refs: CLOUD-65Fast forwarding $ git push origin b490fa05e8b70055f712f61cc6b2d6b2b9bace09:main
remote: error: GH013: Repository rule violations found for refs/heads/main.
remote: Review all repository rules at https://github.com/button-inc/batten/rules?ref=refs%2Fheads%2Fmain
remote:
remote: - Required status check "final" is expected.
remote:
To https://github.com/button-inc/batten.git
! [remote rejected] b490fa05e8b70055f712f61cc6b2d6b2b9bace09 -> main (push declined due to repository rule violations)
error: failed to push some refs to 'https://github.com/button-inc/batten.git' |
|
Triggered from #310 (comment) by @wenzowski. Trying to fast forward Target branch ( commit b913c980bda0a9ade266915b4bf527918576a4c7 (HEAD -> main, origin/main)
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 21:17:30 2026 +0000
docs(fanout): re-measure land contention — a lap now loses by arithmetic
The measurement recorded three hours earlier had `main` at a 487s mean gap
against a ~170s `verify`, giving N ≈ 2.9 with the WIP cap at 2 — "at the
ceiling", and the model held. It no longer does. Over `main`'s ten commits to
21:09Z the mean gap is 436s while a full lap is `verify` + CI + the
fast-forward wait, call it 8–10 minutes. CLOUD-122 lost 16 consecutive laps
across two `mise run land` invocations, every one green on CI.
The interesting half is a lap-loss shape the N model does not price. Three of
the sixteen were the documented case — `main` moved before CI finished, the
verdict void, cancelled early and cheap. Five were not: CI went green,
`/fast-forward` was posted, and the bot then stayed SILENT while `main` moved.
`land` infers a refusal from `main` moving rather than from the bot answering,
so the log reads as a refusal that never happened. The bot is not rejecting the
fast-forward; it is slower than `main`, and that silence is a third term the
verify + CI lap cost never included.
Raising `LAND_MAX_LAPS` buys more attempts per invocation without changing the
per-lap odds, so it spends CI minutes against a losing bet. The lever is
unchanged and now more urgent: shorten the lap, or quiet `main`; never add
sessions.
Refs: CLOUD-122Pull request ( commit b490fa05e8b70055f712f61cc6b2d6b2b9bace09 (pull_request/wenzowski/cloud-65-ship-a-single-binary-first-install-path-and-package-manager)
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 19:08:09 2026 +0000
feat(install): ship a single-binary-first install path with a verified download
Releases have carried seven target archives since CLOUD-108, with nothing on the
consuming side: no install script, no binstall metadata, and nothing asserting
that the asset name a consumer resolves is the one `mise-tasks/dist` writes.
"Single-binary-first" was an ordering claim the repo could not make.
install.sh is POSIX sh, assumes only what `curl … | sh` already proves is
present, and verifies every download against the SHA-256 digest the release API
reports — a mismatch, or an asset with no digest, installs nothing and there is
no flag to skip it. Measured: api.github.com answers pretty-printed, so the
parsing commits to neither wire form. The token reaches curl on stdin rather
than argv, where ps would show it.
install-check is the gate. dist owns archive naming and install.sh owns its
target list; the gate calls both rather than scraping either, derives the matrix
from the workflow that decides it, resolves the binstall template against the
same answer, and fails on a committed executable.
Refs: CLOUD-65Fast forwarding $ git push origin b490fa05e8b70055f712f61cc6b2d6b2b9bace09:main
remote: error: GH013: Repository rule violations found for refs/heads/main.
remote: Review all repository rules at https://github.com/button-inc/batten/rules?ref=refs%2Fheads%2Fmain
remote:
remote: - Required status check "final" is expected.
remote:
To https://github.com/button-inc/batten.git
! [remote rejected] b490fa05e8b70055f712f61cc6b2d6b2b9bace09 -> main (push declined due to repository rule violations)
error: failed to push some refs to 'https://github.com/button-inc/batten.git' |
|
/fast-forward |
…d download Releases have carried seven target archives since CLOUD-108, with nothing on the consuming side: no install script, no binstall metadata, and nothing asserting that the asset name a consumer resolves is the one `mise-tasks/dist` writes. "Single-binary-first" was an ordering claim the repo could not make. install.sh is POSIX sh, assumes only what `curl … | sh` already proves is present, and verifies every download against the SHA-256 digest the release API reports — a mismatch, or an asset with no digest, installs nothing and there is no flag to skip it. Measured: api.github.com answers pretty-printed, so the parsing commits to neither wire form. The token reaches curl on stdin rather than argv, where ps would show it. install-check is the gate. dist owns archive naming and install.sh owns its target list; the gate calls both rather than scraping either, derives the matrix from the workflow that decides it, resolves the binstall template against the same answer, and fails on a committed executable. Refs: CLOUD-65
b490fa0 to
99691f7
Compare
|
|
/fast-forward |



Releases have carried seven target archives since CLOUD-108, with nothing on the
consuming side. There was no install script, no
[package.metadata.binstall],and nothing asserting that the asset name a consumer resolves is the one
mise-tasks/distwrites. "Single-binary-first" is an ordering claim — the binaryworks with no package manager, and package managers are conveniences on top — and
the repo could not make it: the only way to get a release binary was to read a
workflow.
What lands
install.sh— POSIX sh, assuming only whatcurl … | shalready proves ispresent. Detects the target (musl on Linux, so the binary runs whatever the
host's glibc), resolves the release through the API, downloads the archive and
installs
battento${XDG_BIN_HOME:-$HOME/.local/bin}. Pointer-only KEY=VALUEoutput; exit
0/1/2per house-style §7.It refuses to install unverified bytes. Every asset is checked against the
SHA-256 digest the release API reports for it, and a mismatch — or an asset the
API reports no digest for — installs nothing. There is no flag to skip it. What
that digest is not is a supply-chain signature: both halves come from GitHub,
so it proves the bytes arrived intact, not that they are the bytes a maintainer
intended. CLOUD-278 (checksum manifest) and CLOUD-264 (signature format) remain
the stronger claims; this is the floor beneath them, and it needed neither to
land — the API already carries the digest, measured on all ten assets of
v0.0.61.
Two things measured rather than assumed.
api.github.life-white.ukanswerspretty-printed JSON (403 lines for v0.0.61) where every other client here
sees compact, so the parsing commits to neither wire form and a test pins both.
And the token reaches curl through
--config -on stdin rather than argv, wherepswould show it to any other user on the box — which is also why there is nowgetfallback, since wget has no equivalent.mise run install-check— the mechanism CLOUD-65's three acceptance clausesdid not have. A rename in
distthat forgets one of its two readers is green inCI, green in the release matrix, and a 404 on a user's machine. So the gate
proves the contract by running it:
distowns archive naming(
archive_stem,archive_ext— the latter extracted frommainso it iscallable),
install.showns its target list (--targets) and what it will askfor (
--asset-name), and the gate calls each. The matrix has one authority andit is the workflow's, the same property
release-assets-checkholds. TheWindows exclusion is derived through
dist's ownis_windows_target, neverrestated. It also fails on a committed executable, judged on format magic rather
than a path convention, reporting the path and never a byte.
The one restatement that cannot be avoided is the binstall template — cargo
reads TOML, not a shell function — so the gate resolves it, honouring
pkg-fmtand its per-target override, and compares against
dist's answer for all seventargets.
What this deliberately does not claim
cargo binstall battenby name needs the crate on a registry, whichCLOUD-205 defers along with the public repository. What works today is
cargo binstall --git …, and the registry form starts working on the day thatdecision is revisited with no further edit. The remaining half of
"package-manager distribution" — Homebrew, the mise/aqua registries — is
downstream of both a public repo and CLOUD-278's manifest, and is recorded on
CLOUD-65 rather than attempted here.
README keeps its existing status note verbatim and gains an Install section that
states plainly that a token is required while the repository is private. That
reads CLOUD-205's "no install docs" as "do not imply public availability", not
"do not build or document the private path" — the same decision asks the release
machinery to keep running "so the flip is cheap when it comes."
Verification
mise run install-checkgreen;tests/install-check.bats(11) red on eachmistake it names — a matrix target install.sh does not serve, a target it
claims that no leg builds, a rename in
dist, a binstall URL resolvingelsewhere, a committed ELF — and exit 2 on each way of being unable to look.
tests/install.bats(13) drives the real code path against afile://fixture API rather than a stubbed
curl, so the request shape and redirecthandling are exercised. Both refusal cases assert nothing reached the
destination.
install.shagainstv0.0.61in this sandboxinstalled a
static-piemusl binary reportingbatten 0.0.61, withverified=sha256. That clause matters because a release-triggered pathreaches no PR and no gate — the hole that cost CLOUD-258 six silent releases.
Refs: CLOUD-65