Repository navigation
ci(commands): give /plan-fleet the corrected cost model (CLOUD-367) - #325
Conversation
CLOUD-367 The planner-fleet dispatch pattern lives only in chat: re-typed every time, and its caps are re-derived or ignored
Why Dispatching N planning-only sibling sessions over the ready queue is a repeatable procedure that exists nowhere durable. Measured 2026-08-11: seven planners were dispatched over CLOUD-333, 224, 218, 249, 328, 46 and 171, and every step — compute the frontier, subtract what open PRs and non-archived sessions already hold, partition by file domain, write a standalone prompt per child — was reconstructed from scratch in chat. The user's words: "so I don't have to keep typing the same thing." Chat is the one destination that stores nothing. A procedure that survives only in a transcript is re-derived on each use, and the parts most likely to be dropped on a re-derivation are the ones that are not obvious:
Acceptance
Refinement — Ready (the dispatch procedure becomes an invokable artifact; its model stays in the memory that already owns fan-out) Refinement gate: Definition of Ready & Done. This body carries only specializations.
Stated assumption
|
a5a2cac to
a0c22b5
Compare
|
/fast-forward |
|
Triggered from #325 (comment) by @wenzowski. Trying to fast forward Target branch ( commit 040fa566b81b0c89387ac7bdb833e0e43d028e1c (HEAD -> main, origin/main)
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-217Pull request ( commit a0c22b548f5375d5f9531ed5b60e2683d26f83d8 (pull_request/claude/ready-issues-handoff-qnra4q)
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 19:38:42 2026 +0000
ci(commands): give /plan-fleet the corrected cost model
The command shipped with the framing the memory beside it had already
dropped: "the honest signal that you are past it is a rising re-verify rate",
plus a pointer to `mem:workflow/agent-fanout` for "the current cap", which
that memory no longer states in those terms.
Two authorities disagreeing about the same fact, in the file whose §1 was
"one authority each, no duplication" — and the command is the reachable one,
so a dispatcher invoking `/plan-fleet` got the superseded model while the
memory it points at carried the current one.
Replaced with the cost table the memory already holds: re-verifying is free
because `land` laps without a model turn, and what actually costs is a rebase
conflict, CI minutes on a voided run, and tokens. Objective restated as pace
of landed work per token, with the saturated-queue target and the CSMA/CD
framing, and the CI-minutes row now names CLOUD-369 as unbuilt rather than
implying a control exists.
Refs: CLOUD-367Can't fast forward * a0c22b548f5375d5f9531ed5b60e2683d26f83d8 ci(commands): give /plan-fleet the corrected cost model
| * 040fa566b81b0c89387ac7bdb833e0e43d028e1c docs(resolve): mark §5's max_effect as specified, not implemented
|/
* 42ef66342352cf2219fd94ab2503dc228e1dd8b5 test(session): compare the whole store across a restart, not a subset
commit 42ef66342352cf2219fd94ab2503dc228e1dd8b5
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-83Rebase locally, and then force push to |
a0c22b5 to
7114482
Compare
|
/fast-forward |
|
Triggered from #325 (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 711448266ae35e411dfbe13454a382f80314fb42 (pull_request/claude/ready-issues-handoff-qnra4q)
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 19:38:42 2026 +0000
ci(commands): give /plan-fleet the corrected cost model
The command shipped with the framing the memory beside it had already
dropped: "the honest signal that you are past it is a rising re-verify rate",
plus a pointer to `mem:workflow/agent-fanout` for "the current cap", which
that memory no longer states in those terms.
Two authorities disagreeing about the same fact, in the file whose §1 was
"one authority each, no duplication" — and the command is the reachable one,
so a dispatcher invoking `/plan-fleet` got the superseded model while the
memory it points at carried the current one.
Replaced with the cost table the memory already holds: re-verifying is free
because `land` laps without a model turn, and what actually costs is a rebase
conflict, CI minutes on a voided run, and tokens. Objective restated as pace
of landed work per token, with the saturated-queue target and the CSMA/CD
framing, and the CI-minutes row now names CLOUD-369 as unbuilt rather than
implying a control exists.
Refs: CLOUD-367Can't fast forward * 711448266ae35e411dfbe13454a382f80314fb42 ci(commands): give /plan-fleet the corrected cost model
| * 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 |
7114482 to
4ce69f0
Compare
|
/fast-forward |
|
Triggered from #325 (comment) by @wenzowski. Trying to fast forward Target branch ( commit a2ed37d212a129bfc423cf34a378d890ce19a0d2 (HEAD -> main, origin/main)
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 19:23:26 2026 +0000
fix(lint): add the lint rows to the two committed surface lists, and fix the census's clippy denial
`verify` found three things local iteration had not:
- `spec.rs` pins the emitted row set and the derived read-only allowlist as two
committed lists. Both are hand-maintained on purpose — a verb added, renamed
or re-parented has to be stated — so `lint`/`lint brief` join them, the noun
with its kind because the whole subtree is `read`.
- The census's positional lookup used `map(..).unwrap_or_else(panic!)`, which is
`clippy::map_unwrap_or`; a `let ... else` says the same thing.
- rustfmt over the new module, the surface row and the tests.
Refs: CLOUD-84Pull request ( commit 4ce69f02249fff25f1c866afa373032a289e8fd3 (pull_request/claude/ready-issues-handoff-qnra4q)
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 19:38:42 2026 +0000
ci(commands): give /plan-fleet the corrected cost model
The command shipped with the framing the memory beside it had already
dropped: "the honest signal that you are past it is a rising re-verify rate",
plus a pointer to `mem:workflow/agent-fanout` for "the current cap", which
that memory no longer states in those terms.
Two authorities disagreeing about the same fact, in the file whose §1 was
"one authority each, no duplication" — and the command is the reachable one,
so a dispatcher invoking `/plan-fleet` got the superseded model while the
memory it points at carried the current one.
Replaced with the cost table the memory already holds: re-verifying is free
because `land` laps without a model turn, and what actually costs is a rebase
conflict, CI minutes on a voided run, and tokens. Objective restated as pace
of landed work per token, with the saturated-queue target and the CSMA/CD
framing, and the CI-minutes row now names CLOUD-369 as unbuilt rather than
implying a control exists.
Refs: CLOUD-367Can't fast forward * 4ce69f02249fff25f1c866afa373032a289e8fd3 ci(commands): give /plan-fleet the corrected cost model
| * a2ed37d212a129bfc423cf34a378d890ce19a0d2 fix(lint): add the lint rows to the two committed surface lists, and fix the census's clippy denial
| * bb83902973a8652f1db3dbcc68820996559d5310 feat(lint): gate a delegation brief on the facts that do not inherit
|/
* 3fde7a81593c6a60d838b6006f8db6a638b4738f fix(bench): keep the workflow's summary block off shellcheck's SC2016
commit 3fde7a81593c6a60d838b6006f8db6a638b4738f
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-207Rebase locally, and then force push to |
71cdd8e to
ce692d5
Compare
|
/fast-forward |
|
Triggered from #325 (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 ce692d5e71e28d481184643beba271cd370ddc99 (pull_request/claude/ready-issues-handoff-qnra4q)
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 19:38:42 2026 +0000
ci(commands): give /plan-fleet the corrected cost model
The command shipped with the framing the memory beside it had already
dropped: "the honest signal that you are past it is a rising re-verify rate",
plus a pointer to `mem:workflow/agent-fanout` for "the current cap", which
that memory no longer states in those terms.
Two authorities disagreeing about the same fact, in the file whose §1 was
"one authority each, no duplication" — and the command is the reachable one,
so a dispatcher invoking `/plan-fleet` got the superseded model while the
memory it points at carried the current one.
Replaced with the cost table the memory already holds: re-verifying is free
because `land` laps without a model turn, and what actually costs is a rebase
conflict, CI minutes on a voided run, and tokens. Objective restated as pace
of landed work per token, with the saturated-queue target and the CSMA/CD
framing, and the CI-minutes row now names CLOUD-369 as unbuilt rather than
implying a control exists.
Refs: CLOUD-367Can't fast forward * ce692d5e71e28d481184643beba271cd370ddc99 ci(commands): give /plan-fleet the corrected cost model
| * 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 |
ce692d5 to
c711d65
Compare
|
/fast-forward |
|
Triggered from #325 (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 c711d654344bf34dac1320a950eb82b4fcd355f8 (pull_request/claude/ready-issues-handoff-qnra4q)
Author: Claude <noreply@anthropic.com>
Date: Tue Aug 11 19:38:42 2026 +0000
ci(commands): give /plan-fleet the corrected cost model
The command shipped with the framing the memory beside it had already
dropped: "the honest signal that you are past it is a rising re-verify rate",
plus a pointer to `mem:workflow/agent-fanout` for "the current cap", which
that memory no longer states in those terms.
Two authorities disagreeing about the same fact, in the file whose §1 was
"one authority each, no duplication" — and the command is the reachable one,
so a dispatcher invoking `/plan-fleet` got the superseded model while the
memory it points at carried the current one.
Replaced with the cost table the memory already holds: re-verifying is free
because `land` laps without a model turn, and what actually costs is a rebase
conflict, CI minutes on a voided run, and tokens. Objective restated as pace
of landed work per token, with the saturated-queue target and the CSMA/CD
framing, and the CI-minutes row now names CLOUD-369 as unbuilt rather than
implying a control exists.
Refs: CLOUD-367Can't fast forward * c711d654344bf34dac1320a950eb82b4fcd355f8 ci(commands): give /plan-fleet the corrected cost model
| * 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 |
The lease bounds confirming runs at one. That is right for cost and wrong for latency: after every merge the queue is empty, and the next branch starts cold — rebase, verify, then a full matrix — before `main` can move again. Measured on PR #325: 8 laps, 8 greens, zero commits landed. Two advisory fields, on the terms `branch:` already set (CLOUD-420) — read by CI and by waiters, never by a predicate that decides ownership: - `head:` names the commit about to become `main`, so a waiter can linearize onto the trunk that is COMING rather than onto the one the holder is about to replace. Rebasing onto current `origin/main` warms nothing; it pays the same staleness earlier. - `next:` names one admitted successor, so the matrix that overlaps the holder's merge is bought instead of started cold afterwards. The waiter writes `next:` itself through `reserve`, which is forced rather than chosen: waiters are registered nowhere, so the holder cannot name one. It re-mints the holder's lease with a single field added — same holder id, same expiry, same branch, same head — so the holder keeps holding and `mine` still answers for it. One CAS-guarded slot cannot hold two branches, so the bound is two whatever the fleet size, and `authorises` enforces it at the runner rather than by convention. `renew` and `hold` carry the reservation across each beat, or a holder would erase it within 30 seconds of a waiter writing it. `acquire` deliberately does not: a fresh turn whose predecessor's successor was carried forward would authorise a third branch, then a fourth. Refs: CLOUD-369, CLOUD-420 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0172rfy7QRfyCEgw8KFYfBVJ
…essor A waiter that rebases onto `origin/main` warms nothing: the branch holding the lease is about to replace that commit, so the waiter is stale again the instant it wins — the cold window paid earlier and no cheaper. Pre-warming is a linearization, and the base worth linearizing against is the one the lease publishes as `head:`. Four changes to the lap, all inside the existing lease: - Lost the lease: rebase onto the holder's head and re-verify there, so this branch's turn costs a ready and a push rather than a rebase, a verify and a matrix. Every failure falls back to `origin/main` — a conflict against a base that may never land is information, not the one real decision the lap stops for. - The bet is recorded and settled, never assumed. A speculative rebase puts another branch's unlanded commits into this history, and fast-forwarding from there would land somebody else's work as a side effect of ours. `origin/main --is-ancestor HEAD` does not catch it: the speculated base is itself a descendant of main. So `spec_base` is settled at the top of every lap, before anything can push — won keeps the tree, pending keeps it, lost resets it. - Reserve the successor slot and, if admitted, ready and push without the lease so that matrix overlaps the holder's merge. Everything past the push needs the lease, so the pass ends there rather than duplicating the ready/push pair CLOUD-254 owns. - Re-confirm inside the hold. `acquire` waits up to a TTL, so the winner is at its most stale in the instant it wins; readying there buys a matrix the fast-forward will refuse. Asked as "did main move since this lap rebased", not as an ancestry query, for the reason above. `LAND_LOCK_AGE` carries the wait count into `acquire`, so a branch that has lost repeatedly probes a freed lease sooner — the capture effect is what turned 8 laps and 8 greens into zero commits landed on #325. `fetch_main` and `charge_wait` are extracted rather than copied: both now have two call sites, and a duplicated `die` is a second authority on what the failure means. Refs: CLOUD-369 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0172rfy7QRfyCEgw8KFYfBVJ
c711d65 to
a4dec2f
Compare
A survey that classifies a corpus needs cases whose answer is known because getting them wrong is a mistake already made, and the run must fail when one misclassifies. Round one of this survey matched policy files by filename and was wrong in both directions; round two was built to make that unrepresentable and reproduced the same blind spot in a new costume, fetching only paths guessed in advance. The canaries are what caught it. The transferable half is the sentence about instruments: absence of evidence is a claim about the instrument before it is a claim about the world. The corollary for any classifier is that a name may decide what gets read, and only the text decides what a thing is. Refs: CLOUD-315
a4dec2f to
d330760
Compare
The command shipped with the framing the memory beside it had already dropped: "the honest signal that you are past it is a rising re-verify rate", plus a pointer to `mem:workflow/agent-fanout` for "the current cap", which that memory no longer states in those terms. Two authorities disagreeing about the same fact, in the file whose §1 was "one authority each, no duplication" — and the command is the reachable one, so a dispatcher invoking `/plan-fleet` got the superseded model while the memory it points at carried the current one. Replaced with the cost table the memory already holds: re-verifying is free because `land` laps without a model turn, and what actually costs is a rebase conflict, CI minutes on a voided run, and tokens. Objective restated as pace of landed work per token, with the saturated-queue target and the CSMA/CD framing, and the CI-minutes row now names CLOUD-369 as unbuilt rather than implying a control exists. Refs: CLOUD-367
…nheriting a spent window Two defects in `plan-hold`, both measured 2026-08-12 in one session. THE ANSWER WAS INVISIBLE. `plan-hold-guard` gates `PreToolUse` on `ExitPlanMode|AskUserQuestion`; `plan-hold-release` listens on `UserPromptSubmit`. Those are different event classes, and a human answering either of those two tools produces a TOOL RESULT, not a prompt. So for the exact case the mechanism exists to serve, the release could never fire: a hold armed, an `AskUserQuestion` answered, the hold still live afterwards, ended by removing its sentinel by hand. CLOUD-451's third acceptance bullet, unmet since it landed. `plan-hold-release-tool` is the missing half, on `PostToolUse` over the same two tools the guard already names. No classifier: `plan-hold-release` must decide whether a prompt came from a person because `UserPromptSubmit` carries machine turns too, while here the provenance is structural — the event fires only after a tool whose whole purpose is to ask a human. The `tool_name` check is belt to the matcher's braces, because a matcher widened later would otherwise turn every tool call into a release. CLOUD-435's cost argument does not reach it for the reason it does not reach `plan-hold-guard`: these two tools fire at most once per turn, and it is invoked by path, so no task-runner startup is paid. THE WINDOW ERODED SILENTLY. A second launch answered "already held" and exited, which keeps one sleeper and hands the new handoff whatever is left of the old one's cap. Some answers reach neither release path — a reply typed mid-turn arrives `<`-wrapped and reads as a machine turn — so that handoff's hold never releases, the next inherits a partly spent window, and eventually one is guarded by a hold about to cap and the container is reclaimed while somebody is reading. Nothing blocks, so nothing announces it. A second launch now releases the incumbent and arms fresh: at most one sleeper still, and every handoff gets the whole cap. Released, never signalled — a killed hold wakes nothing. Deliberately NOT touching `plan-hold-release-check`'s `<`-prefix classifier. The mid-turn false negative is real, but widening a literal without measuring it over real transcripts is the mistake CLOUD-252 and CLOUD-323 exist to prevent. The erosion fix removes its compounding cost; the false negative itself stays recorded on CLOUD-485. Refs: CLOUD-485
d330760 to
cd1d105
Compare
|



Follow-up to #297, which landed
/plan-fleetand the memory beside it in an inconsistent state.What was wrong
#297 corrected the fan-out cost model in
mem:workflow/agent-fanoutbut left the same claim standing in.claude/commands/plan-fleet.md. The command shipped saying:and pointing at
mem:workflow/agent-fanoutfor "the current cap" — which that memory no longer states in those terms, because the same PR replaced the cap with a cost table.So the two authorities disagreed about one fact, in the change whose §1 was explicitly "one authority each, no duplication between them". The direction is what makes it worth a follow-up rather than a note: the command is the reachable surface — someone invoking
/plan-fleetreads it and may never open the memory — so the superseded model was the one on the path of travel.The fix
## What bounds the fleetnow carries the same cost table the memory holds:mainvoidslandlapping without a model turnRe-verifying is free:
landlaps unattended, so a moved base spends CPU and wall-clock — both free in this sandbox — and zero tokens. Throttling a fleet to avoid it optimises against a cost that does not exist.The objective is restated as pace of landed work per token, with the saturated-queue target and the CSMA/CD framing: coordination between sessions is impossible by construction, so collisions are designed for rather than prevented.
The CI-minutes row names CLOUD-369 as unbuilt, rather than implying a control exists where none does.
Gate
Unchanged from #297 —
memories-checkselects tracked markdown viagit ls-files '*.md', which reaches.claude/commands/, so themem:reference stays bound by the existing conjunct.Refs: CLOUD-367
Generated by Claude Code