ci(deps): carry licence rows forward for bumped action pins - #806
Conversation
Every row here was copied verbatim from the same repository's existing row with only the sha changed, after confirming every candidate licence file is byte-identical between the two commits. No licence was re-derived and no verdict inferred. Refs: CLOUD-1213
CLOUD-1213 `sbom-check`'s licence table needs a human on every action pin move, and only a person can regenerate it
Why Found while fixing CLOUD-1207 and deliberately not repaired there, because
What #771 already settled, so this row is smaller than when it was filed The original body carried two concerns. The second — that the lane burned CI So what remains is only the first: should this class need a person at all? A correction to the measurement above (2026-08-31) This row said *"Measured on #676: 34 × * The options
Refinement — Ready Refinement gate: Definition of Ready & Done. This body carries only specializations.
{
"source_of_truth": "mise-tasks/sbom-actions.tsv",
"gate": { "task": "sbom-check", "exits": [0, 1] },
"commit_type": "ci",
"blockers": [],
"tests": [
{
"file": "tests/sbom-check.bats",
"mutation": "drop the per-pin unmapped loop, so a moved action pin reports mapped"
}
]
}REOPENED 2026-09-01. Closed against #788 with §2's Option 1 never built — Acceptance
|
|
❌ The last analysis has failed. |
|
/fast-forward |
… claim `sbom-actions-currency` opens a PR that `verify` refuses: there is no claim receipt, and the agent one attests a refinement no bot performed. #806 landed with a `--takeover` claim against CLOUD-1213, which was a fake claim, and every future carry PR meets the same wall. `batten claim carry` is the honest receipt. It attests DERIVABILITY rather than a branch name — a prefix exemption would be a password wearing a branch name — so it mints only when the branch appends rows whose repo the base table already maps, changing the sha alone, and touches nothing else. The verb takes no argument on purpose: the subject is the branch's own diff against its merge base, and a caller that could name its own subject could name one that is derivable while changing something else. Also repairs the `snapshots` task, stale since the integration targets moved under `tests/it/`. Refs: CLOUD-1295
…ceipt
486 lines of shell and 323 of bats, six subcommands, all of it GitHub API plus
one local receipt write. `mise-tasks/bot-issue.sh` and `tests/bot-issue.bats`
are deleted; the predicates land as `crates/batten/src/bot.rs` and six new
leaves — `batten pr derive|file|link|ensure|closes` and `batten claim bot`.
TWO HALVES, KEPT APART INSIDE ONE MODULE. The predicates — is this pull request
one of the lane's, which manifests it touched, what Conventional type its
subject declares, whether a body still CLOSES a key rather than merely naming
one — are pure functions with their own unit tier. The `forge` half underneath
is the only thing that talks to anybody.
THE CONSUMER FACTS ARE `[bot_lane]` IN batten.toml, not literals in the crate.
Which repository, which bot logins, which manifests the lane owns, the marker
strings, the tracker's key prefix and the branch prefix — every value the
retired program spelled internally is a config row now, so a grep of
`crates/batten` for a bot's name or a manifest path returns nothing
(non-negotiable rule 1). `document_facts` caught the first draft's doc comment
naming a workflow glob as an example, and that is the gate working. The derived
row's body is a tracked template with `{{...}}` placeholders rather than a page
of markdown inside a TOML value; an unfilled placeholder refuses at
substitution rather than rendering a template artifact into a tracker row.
THE FORGE'S OWN CLIENT, NOT THIS CRATE'S HTTP TRANSPORT. `pr_watch` is the
landed precedent (CLOUD-1143) and `gh` resolves the credential outside the
crate, where `fetch` would have put token resolution inside it next to no
config row that declares one. `policy/spawn-adapters.rego` places `bot` on
`pr_watch`'s own argument. The commit that widened `fetch::Call` for a PATCH
this port turned out not to need is reverted earlier on this branch.
THE EXIT TABLE CHANGED AND THE WORKFLOW FOLLOWS IT. `bot-issue` used 1 for
"refused, not this lane's" and 2 for "could not look"; house style §7 makes a
refusal 2 and a could-not-look 3. `auto-bot-land.yml`'s two steps are repointed
and their `case` arms moved with them — reading 1 as an ordinary refusal there
would have treated a usage error as a pass.
THE THIRD RECEIPT KIND IS WIRED. `verify` now tries `claim`, then `bot`, then
`carry`, so a licence-carry branch no longer needs the fake `--takeover` claim
#806 landed with. `sbom-actions-currency`'s generated body gains
`DO-NOT-CLOSE CLOUD-1213`, without which every carry pull request stalls on
`closing-key-check` exactly as #806 did and needs a human to patch it.
WHAT IS NOT DONE, AND WHY IT IS FILED RATHER THAN FIXED. The plan called for a
`checks = ["carry"]` row beside `claim-needs-receipt`. That row is wrong: a
receipt rule's `checks` is a CONJUNCTION, so a second row is a second AND and
would deny every ordinary write on every branch. `verify`'s shell body can
express the disjunction and the mediated gate cannot — a live false positive
for the bot receipt since CLOUD-693, which this change's third kind inherits.
CLOUD-1297 owns it, with the disjunction as its §2.
THE LEDGER. Two file arms and 22 case arms, `kind:verb` on each because the
lane needs stdin, spawns with its own arguments and performs writes — none of
which a tree-scoped module may do. `bot-issue` leaves `$MUTANT_GATES` and needs
no `#MUTANT-EXEMPT`: `mutant-census` enumerates `mise-tasks/*.sh` and
`policy/*.rego`, so a deleted program is not in the census at all. Its two
declared mutations are re-homed as named cases —
`idempotence_a_second_call_on_the_same_pr_files_nothing` and
`a_key_named_but_not_closed_is_refused` — each carrying the mutation it stands
for in its comment.
THE LOAD-BEARING CASE IS REPLAYED, NOT ASSERTED. The dying suite ran the REAL
`ready-lint` over the REAL derived payload, because "the derived block is
checkable by the same gate that checks a human's" is the claim that makes a
mechanical row honest. `the_derived_block_passes_ready_lint` does the same over
this repository's committed template and its committed `[[pattern]]` grammar —
and it earned its place immediately, catching that `prettier` reshapes the
template's bullets on every `mise run fmt`.
A KNOWN LAG, STATED RATHER THAN HIDDEN. `auto-bot-land.yml` installs `batten`
from the latest RELEASE, so its two repointed steps fail loudly with "command
not found" between this merge and the next release. That is the same gap
CLOUD-1143 recorded for `checks-green`, and it fails the job rather than
passing silently.
131 suites after the deletion, 686.8s serial on this container. The saving is
`tests/bot-issue.bats`' own 2.9s share of the corpus it was measured in, 0.3%;
the rest of the totals' difference is machine noise and is not claimed.
Refs: CLOUD-1295
Refs: CLOUD-1164
Refs: CLOUD-1213
Refs: CLOUD-1297
Admits: 1c68cb4a5d20efa873c0ed2b7564b7cfdf53c0b6ebb9c79e211eea4b8445bcc5
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: batten.toml
Admits-head: a11e917
Admits-epoch: 650a76ba70a8b5645ffc58aedb1bdde72394e39026b5c1391a926934ea49702c
Admits-author: alec@wenzowski.com
Admits-prev: c7d8beede76f452a09d1a6c47a615b728f2876a60e055ef065975a27903f003f
Admits-answer-lost: mise-tasks/bot-issue.sh stays alive as a second implementation of predicates the engine now owns, which is the state policy/shell-retirement.rego's V-SHELL-RULE-EDITED exists to end -- the ported verbs cannot read a lane nobody declared, so refusing this write refuses the retirement rather than this line of it.
Admits-answer-precondition: The bot lane's consumer facts -- which forge repository, which bot logins, which manifests the lane owns, the marker strings, the tracker's key prefix and the branch prefix -- are exactly the values non-negotiable rule 1 forbids in crates/batten. Retiring mise-tasks/bot-issue.sh onto `batten pr derive|file|link|ensure|closes` and `batten claim bot` moves the MATCHER into the engine and leaves those facts here, so batten.toml is the owning surface for them and no other surface can carry them. The write is one additive [bot_lane] block, visible in this pull request's own diff, which is where reviewers read it.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because batten.toml IS the owning surface for a consumer's policy facts; there is no other surface that owns them. A local override cannot carry them either: adding a login to `bots` or a path to `owned_manifests` turns a refusal into a filed row, which is a weakening dressed as an addition and house style section 8's raise-only rule does not admit it. R-RESTORE-IT does not apply because the write is additive and deliberate rather than an accident to undo -- restoring the file would delete the table the ported verbs read.
Admits: ddd46be8d9ae384d97c38556832349bfc8dc319895761edb2255894b821b3763
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: .github/workflows/auto-bot-land.yml
Admits-head: a11e917
Admits-epoch: 8e6d5f2f19b383997365f6b72ff52816d72c7029208dbb81a40389acbd65e67f
Admits-author: alec@wenzowski.com
Admits-prev: -
Admits-answer-lost: The bot lane's lander breaks outright at its next tick: both steps call a task the retirement removed, so no bot pull request gets a row and none is landed. The alternative is keeping mise-tasks/bot-issue.sh alive as a second implementation, which is the state V-SHELL-RULE-EDITED exists to end.
Admits-answer-precondition: auto-bot-land.yml invokes `mise run bot-issue ensure` and `mise run bot-issue closes`, and this change deletes mise-tasks/bot-issue.sh. Leaving the calls would leave the lander invoking a task that no longer exists, so the workflow is the owning surface for its own steps and there is nowhere else the repoint could be made. The write is two invocation repoints -- `batten pr ensure` and `batten pr closes` -- plus the comment naming the retired program, and nothing about what the workflow decides changes.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because .github/workflows/auto-bot-land.yml IS the owning surface for the lander's own steps; a workflow's run lines cannot be declared anywhere else. R-RESTORE-IT does not apply because the change is a deliberate repoint at a successor this same commit lands, and restoring the file would leave a workflow calling a deleted program.
Admits: a3bb7b559f5118c0fe2cc4e4c8e2336e2bcbf1262a0df2578070e44446a20751
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: .github/workflows/sbom-actions-currency.yml
Admits-head: a11e917
Admits-epoch: 8e6d5f2f19b383997365f6b72ff52816d72c7029208dbb81a40389acbd65e67f
Admits-author: alec@wenzowski.com
Admits-prev: e03b883be1a24354b6eaeedb59a2c39f16052d832ef6dde2ceab292ca964fa5b
Admits-answer-lost: Every carry pull request `sbom-actions-currency` opens stalls on `closing-key-check` exactly as #806 did, and each one needs a human to patch its body before it can land -- which is the manual step CLOUD-1213 exists to remove, on the lane whose blockage this whole change is about.
Admits-answer-precondition: The generated pull request body carries `Refs: CLOUD-1213` and no closing marker, so `closing-key-check` reads it as a body naming a key non-closingly and stalls the landing. Measured on #806, where the body was patched by hand to get it through -- the defect repeating on every future carry pull request. The body is generated by this workflow's own `gh pr create` step, so the workflow is the owning surface for it and no other surface can set it. The write is one added line, `DO-NOT-CLOSE CLOUD-1213`, in the same printf list, visible in this pull request's diff.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because .github/workflows/sbom-actions-currency.yml IS the owning surface for the body it generates; a workflow's own `gh pr create` arguments cannot be declared anywhere else. R-RESTORE-IT does not apply because the change is a deliberate one-line addition rather than an accident to undo, and restoring the file would put back the body that stalls.
Admits: 1588b760e38a64a36f5aaef031bd5b3e8f5e1160c0f94eec486b41dbca5d335c
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: .serena/memories/core.md
Admits-head: a11e917
Admits-epoch: a5e521241d3b4c861cd0ffb0813ac2c5eb0bfd1b3b364e13418f67888e3de545
Admits-author: alec@wenzowski.com
Admits-prev: 121e5c62ce26f6caa46a19393e76ae86d200b8b113fccdaf2bb1196df372865b
Admits-answer-lost: `bot.rs` lands with no map row, `module-map-check` fails the gate, and the retirement cannot commit at all -- so refusing this write refuses the whole change rather than this bullet of it. A reader looking up what `bot.rs` owns would find nothing where every other module is described.
Admits-answer-precondition: `module-map-check` refuses a `crates/batten/src/*.rs` module with no row in `.serena/memories/core.md`, and this change adds `crates/batten/src/bot.rs`. The memory IS the module map's authority -- AGENTS.md points there for the per-module layout precisely so the tree is not restated in a budgeted file -- so there is no other surface a row could go on. The write was made through Serena's `edit_memory`, which is the sanctioned route, and it is one added bullet visible in this pull request's diff.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because `.serena/memories/core.md` IS the owning surface for the module map; AGENTS.md defers to it by name and `module-map-check` reads it as the authority. R-RESTORE-IT does not apply because the write is one additive bullet describing a module this same commit introduces -- restoring the file would leave the map missing a row the gate demands.
… claim `sbom-actions-currency` opens a PR that `verify` refuses: there is no claim receipt, and the agent one attests a refinement no bot performed. #806 landed with a `--takeover` claim against CLOUD-1213, which was a fake claim, and every future carry PR meets the same wall. `batten claim carry` is the honest receipt. It attests DERIVABILITY rather than a branch name — a prefix exemption would be a password wearing a branch name — so it mints only when the branch appends rows whose repo the base table already maps, changing the sha alone, and touches nothing else. The verb takes no argument on purpose: the subject is the branch's own diff against its merge base, and a caller that could name its own subject could name one that is derivable while changing something else. Also repairs the `snapshots` task, stale since the integration targets moved under `tests/it/`. Refs: CLOUD-1295
…ceipt
486 lines of shell and 323 of bats, six subcommands, all of it GitHub API plus
one local receipt write. `mise-tasks/bot-issue.sh` and `tests/bot-issue.bats`
are deleted; the predicates land as `crates/batten/src/bot.rs` and six new
leaves — `batten pr derive|file|link|ensure|closes` and `batten claim bot`.
TWO HALVES, KEPT APART INSIDE ONE MODULE. The predicates — is this pull request
one of the lane's, which manifests it touched, what Conventional type its
subject declares, whether a body still CLOSES a key rather than merely naming
one — are pure functions with their own unit tier. The `forge` half underneath
is the only thing that talks to anybody.
THE CONSUMER FACTS ARE `[bot_lane]` IN batten.toml, not literals in the crate.
Which repository, which bot logins, which manifests the lane owns, the marker
strings, the tracker's key prefix and the branch prefix — every value the
retired program spelled internally is a config row now, so a grep of
`crates/batten` for a bot's name or a manifest path returns nothing
(non-negotiable rule 1). `document_facts` caught the first draft's doc comment
naming a workflow glob as an example, and that is the gate working. The derived
row's body is a tracked template with `{{...}}` placeholders rather than a page
of markdown inside a TOML value; an unfilled placeholder refuses at
substitution rather than rendering a template artifact into a tracker row.
THE FORGE'S OWN CLIENT, NOT THIS CRATE'S HTTP TRANSPORT. `pr_watch` is the
landed precedent (CLOUD-1143) and `gh` resolves the credential outside the
crate, where `fetch` would have put token resolution inside it next to no
config row that declares one. `policy/spawn-adapters.rego` places `bot` on
`pr_watch`'s own argument. The commit that widened `fetch::Call` for a PATCH
this port turned out not to need is reverted earlier on this branch.
THE EXIT TABLE CHANGED AND THE WORKFLOW FOLLOWS IT. `bot-issue` used 1 for
"refused, not this lane's" and 2 for "could not look"; house style §7 makes a
refusal 2 and a could-not-look 3. `auto-bot-land.yml`'s two steps are repointed
and their `case` arms moved with them — reading 1 as an ordinary refusal there
would have treated a usage error as a pass.
THE THIRD RECEIPT KIND IS WIRED. `verify` now tries `claim`, then `bot`, then
`carry`, so a licence-carry branch no longer needs the fake `--takeover` claim
#806 landed with. `sbom-actions-currency`'s generated body gains
`DO-NOT-CLOSE CLOUD-1213`, without which every carry pull request stalls on
`closing-key-check` exactly as #806 did and needs a human to patch it.
WHAT IS NOT DONE, AND WHY IT IS FILED RATHER THAN FIXED. The plan called for a
`checks = ["carry"]` row beside `claim-needs-receipt`. That row is wrong: a
receipt rule's `checks` is a CONJUNCTION, so a second row is a second AND and
would deny every ordinary write on every branch. `verify`'s shell body can
express the disjunction and the mediated gate cannot — a live false positive
for the bot receipt since CLOUD-693, which this change's third kind inherits.
CLOUD-1297 owns it, with the disjunction as its §2.
THE LEDGER. Two file arms and 22 case arms, `kind:verb` on each because the
lane needs stdin, spawns with its own arguments and performs writes — none of
which a tree-scoped module may do. `bot-issue` leaves `$MUTANT_GATES` and needs
no `#MUTANT-EXEMPT`: `mutant-census` enumerates `mise-tasks/*.sh` and
`policy/*.rego`, so a deleted program is not in the census at all. Its two
declared mutations are re-homed as named cases —
`idempotence_a_second_call_on_the_same_pr_files_nothing` and
`a_key_named_but_not_closed_is_refused` — each carrying the mutation it stands
for in its comment.
THE LOAD-BEARING CASE IS REPLAYED, NOT ASSERTED. The dying suite ran the REAL
`ready-lint` over the REAL derived payload, because "the derived block is
checkable by the same gate that checks a human's" is the claim that makes a
mechanical row honest. `the_derived_block_passes_ready_lint` does the same over
this repository's committed template and its committed `[[pattern]]` grammar —
and it earned its place immediately, catching that `prettier` reshapes the
template's bullets on every `mise run fmt`.
A KNOWN LAG, STATED RATHER THAN HIDDEN. `auto-bot-land.yml` installs `batten`
from the latest RELEASE, so its two repointed steps fail loudly with "command
not found" between this merge and the next release. That is the same gap
CLOUD-1143 recorded for `checks-green`, and it fails the job rather than
passing silently.
129 suites after the deletion, 706.1s serial on this container, regenerated on
the rebased base rather than the one this change was written against — `main`
retired two more suites in between, and a corpus derived from the older report
would publish a cost for a suite that is gone. The saving is
`tests/bot-issue.bats`' own 2.9s share of the corpus it was measured in, 0.3%;
the rest of the totals' difference is those two suites plus machine noise, and
is not claimed.
Refs: CLOUD-1295
Refs: CLOUD-1164
Refs: CLOUD-1213
Refs: CLOUD-1297
Admits: 1c68cb4a5d20efa873c0ed2b7564b7cfdf53c0b6ebb9c79e211eea4b8445bcc5
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: batten.toml
Admits-head: a11e917
Admits-epoch: 650a76ba70a8b5645ffc58aedb1bdde72394e39026b5c1391a926934ea49702c
Admits-author: alec@wenzowski.com
Admits-prev: c7d8beede76f452a09d1a6c47a615b728f2876a60e055ef065975a27903f003f
Admits-answer-lost: mise-tasks/bot-issue.sh stays alive as a second implementation of predicates the engine now owns, which is the state policy/shell-retirement.rego's V-SHELL-RULE-EDITED exists to end -- the ported verbs cannot read a lane nobody declared, so refusing this write refuses the retirement rather than this line of it.
Admits-answer-precondition: The bot lane's consumer facts -- which forge repository, which bot logins, which manifests the lane owns, the marker strings, the tracker's key prefix and the branch prefix -- are exactly the values non-negotiable rule 1 forbids in crates/batten. Retiring mise-tasks/bot-issue.sh onto `batten pr derive|file|link|ensure|closes` and `batten claim bot` moves the MATCHER into the engine and leaves those facts here, so batten.toml is the owning surface for them and no other surface can carry them. The write is one additive [bot_lane] block, visible in this pull request's own diff, which is where reviewers read it.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because batten.toml IS the owning surface for a consumer's policy facts; there is no other surface that owns them. A local override cannot carry them either: adding a login to `bots` or a path to `owned_manifests` turns a refusal into a filed row, which is a weakening dressed as an addition and house style section 8's raise-only rule does not admit it. R-RESTORE-IT does not apply because the write is additive and deliberate rather than an accident to undo -- restoring the file would delete the table the ported verbs read.
Admits: ddd46be8d9ae384d97c38556832349bfc8dc319895761edb2255894b821b3763
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: .github/workflows/auto-bot-land.yml
Admits-head: a11e917
Admits-epoch: 8e6d5f2f19b383997365f6b72ff52816d72c7029208dbb81a40389acbd65e67f
Admits-author: alec@wenzowski.com
Admits-prev: -
Admits-answer-lost: The bot lane's lander breaks outright at its next tick: both steps call a task the retirement removed, so no bot pull request gets a row and none is landed. The alternative is keeping mise-tasks/bot-issue.sh alive as a second implementation, which is the state V-SHELL-RULE-EDITED exists to end.
Admits-answer-precondition: auto-bot-land.yml invokes `mise run bot-issue ensure` and `mise run bot-issue closes`, and this change deletes mise-tasks/bot-issue.sh. Leaving the calls would leave the lander invoking a task that no longer exists, so the workflow is the owning surface for its own steps and there is nowhere else the repoint could be made. The write is two invocation repoints -- `batten pr ensure` and `batten pr closes` -- plus the comment naming the retired program, and nothing about what the workflow decides changes.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because .github/workflows/auto-bot-land.yml IS the owning surface for the lander's own steps; a workflow's run lines cannot be declared anywhere else. R-RESTORE-IT does not apply because the change is a deliberate repoint at a successor this same commit lands, and restoring the file would leave a workflow calling a deleted program.
Admits: a3bb7b559f5118c0fe2cc4e4c8e2336e2bcbf1262a0df2578070e44446a20751
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: .github/workflows/sbom-actions-currency.yml
Admits-head: a11e917
Admits-epoch: 8e6d5f2f19b383997365f6b72ff52816d72c7029208dbb81a40389acbd65e67f
Admits-author: alec@wenzowski.com
Admits-prev: e03b883be1a24354b6eaeedb59a2c39f16052d832ef6dde2ceab292ca964fa5b
Admits-answer-lost: Every carry pull request `sbom-actions-currency` opens stalls on `closing-key-check` exactly as #806 did, and each one needs a human to patch its body before it can land -- which is the manual step CLOUD-1213 exists to remove, on the lane whose blockage this whole change is about.
Admits-answer-precondition: The generated pull request body carries `Refs: CLOUD-1213` and no closing marker, so `closing-key-check` reads it as a body naming a key non-closingly and stalls the landing. Measured on #806, where the body was patched by hand to get it through -- the defect repeating on every future carry pull request. The body is generated by this workflow's own `gh pr create` step, so the workflow is the owning surface for it and no other surface can set it. The write is one added line, `DO-NOT-CLOSE CLOUD-1213`, in the same printf list, visible in this pull request's diff.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because .github/workflows/sbom-actions-currency.yml IS the owning surface for the body it generates; a workflow's own `gh pr create` arguments cannot be declared anywhere else. R-RESTORE-IT does not apply because the change is a deliberate one-line addition rather than an accident to undo, and restoring the file would put back the body that stalls.
Admits: 1588b760e38a64a36f5aaef031bd5b3e8f5e1160c0f94eec486b41dbca5d335c
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: .serena/memories/core.md
Admits-head: a11e917
Admits-epoch: a5e521241d3b4c861cd0ffb0813ac2c5eb0bfd1b3b364e13418f67888e3de545
Admits-author: alec@wenzowski.com
Admits-prev: 121e5c62ce26f6caa46a19393e76ae86d200b8b113fccdaf2bb1196df372865b
Admits-answer-lost: `bot.rs` lands with no map row, `module-map-check` fails the gate, and the retirement cannot commit at all -- so refusing this write refuses the whole change rather than this bullet of it. A reader looking up what `bot.rs` owns would find nothing where every other module is described.
Admits-answer-precondition: `module-map-check` refuses a `crates/batten/src/*.rs` module with no row in `.serena/memories/core.md`, and this change adds `crates/batten/src/bot.rs`. The memory IS the module map's authority -- AGENTS.md points there for the per-module layout precisely so the tree is not restated in a budgeted file -- so there is no other surface a row could go on. The write was made through Serena's `edit_memory`, which is the sanctioned route, and it is one added bullet visible in this pull request's diff.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because `.serena/memories/core.md` IS the owning surface for the module map; AGENTS.md defers to it by name and `module-map-check` reads it as the authority. R-RESTORE-IT does not apply because the write is one additive bullet describing a module this same commit introduces -- restoring the file would leave the map missing a row the gate demands.
…eceipt
486 lines of shell and 323 of bats, six subcommands, all of it GitHub API plus
one local receipt write. `mise-tasks/bot-issue.sh` and `tests/bot-issue.bats`
are deleted; the predicates land as `crates/batten/src/bot.rs` and six new
leaves — `batten pr derive|file|link|ensure|closes` and `batten claim bot`.
TWO HALVES, KEPT APART INSIDE ONE MODULE. The predicates — is this pull request
one of the lane's, which manifests it touched, what Conventional type its
subject declares, whether a body still CLOSES a key rather than merely naming
one — are pure functions with their own unit tier. The `forge` half underneath
is the only thing that talks to anybody.
THE CONSUMER FACTS ARE `[bot_lane]` IN batten.toml, not literals in the crate.
Which repository, which bot logins, which manifests the lane owns, the marker
strings, the tracker's key prefix and the branch prefix — every value the
retired program spelled internally is a config row now, so a grep of
`crates/batten` for a bot's name or a manifest path returns nothing
(non-negotiable rule 1). `document_facts` caught the first draft's doc comment
naming a workflow glob as an example, and that is the gate working. The derived
row's body is a tracked template with `{{...}}` placeholders rather than a page
of markdown inside a TOML value; an unfilled placeholder refuses at
substitution rather than rendering a template artifact into a tracker row.
THE FORGE'S OWN CLIENT, NOT THIS CRATE'S HTTP TRANSPORT. `pr_watch` is the
landed precedent (CLOUD-1143) and `gh` resolves the credential outside the
crate, where `fetch` would have put token resolution inside it next to no
config row that declares one. `policy/spawn-adapters.rego` places `bot` on
`pr_watch`'s own argument. The commit that widened `fetch::Call` for a PATCH
this port turned out not to need is reverted earlier on this branch.
THE EXIT TABLE CHANGED AND THE WORKFLOW FOLLOWS IT. `bot-issue` used 1 for
"refused, not this lane's" and 2 for "could not look"; house style §7 makes a
refusal 2 and a could-not-look 3. `auto-bot-land.yml`'s two steps are repointed
and their `case` arms moved with them — reading 1 as an ordinary refusal there
would have treated a usage error as a pass.
THE THIRD RECEIPT KIND IS WIRED. `verify` now tries `claim`, then `bot`, then
`carry`, so a licence-carry branch no longer needs the fake `--takeover` claim
#806 landed with. `sbom-actions-currency`'s generated body gains
`DO-NOT-CLOSE CLOUD-1213`, without which every carry pull request stalls on
`closing-key-check` exactly as #806 did and needs a human to patch it.
WHAT IS NOT DONE, AND WHY IT IS FILED RATHER THAN FIXED. The plan called for a
`checks = ["carry"]` row beside `claim-needs-receipt`. That row is wrong: a
receipt rule's `checks` is a CONJUNCTION, so a second row is a second AND and
would deny every ordinary write on every branch. `verify`'s shell body can
express the disjunction and the mediated gate cannot — a live false positive
for the bot receipt since CLOUD-693, which this change's third kind inherits.
CLOUD-1297 owns it, with the disjunction as its §2.
THE LEDGER. Two file arms and 22 case arms, `kind:verb` on each because the
lane needs stdin, spawns with its own arguments and performs writes — none of
which a tree-scoped module may do. `bot-issue` leaves `$MUTANT_GATES` and needs
no `#MUTANT-EXEMPT`: `mutant-census` enumerates `mise-tasks/*.sh` and
`policy/*.rego`, so a deleted program is not in the census at all. Its two
declared mutations are re-homed as named cases —
`idempotence_a_second_call_on_the_same_pr_files_nothing` and
`a_key_named_but_not_closed_is_refused` — each carrying the mutation it stands
for in its comment.
THE LOAD-BEARING CASE IS REPLAYED, NOT ASSERTED. The dying suite ran the REAL
`ready-lint` over the REAL derived payload, because "the derived block is
checkable by the same gate that checks a human's" is the claim that makes a
mechanical row honest. `the_derived_block_passes_ready_lint` does the same over
this repository's committed template and its committed `[[pattern]]` grammar —
and it earned its place immediately, catching that `prettier` reshapes the
template's bullets on every `mise run fmt`.
A KNOWN LAG, STATED RATHER THAN HIDDEN. `auto-bot-land.yml` installs `batten`
from the latest RELEASE, so its two repointed steps fail loudly with "command
not found" between this merge and the next release. That is the same gap
CLOUD-1143 recorded for `checks-green`, and it fails the job rather than
passing silently.
129 suites after the deletion, 706.1s serial on this container, regenerated on
the rebased base rather than the one this change was written against — `main`
retired two more suites in between, and a corpus derived from the older report
would publish a cost for a suite that is gone. The saving is
`tests/bot-issue.bats`' own 2.9s share of the corpus it was measured in, 0.3%;
the rest of the totals' difference is those two suites plus machine noise, and
is not claimed.
BREAKING CHANGE: `config::Config` and `resolve::Resolved` each gain a
`bot_lane` field, so any code constructing either with a struct literal must
name it. `semver check` reports it as `constructible_struct_adds_field` and it
is declared rather than worked around: the alternative is `#[non_exhaustive]`,
which is a break in its own right and a wider one — it would close both structs
to literal construction for every field, not just this one.
Refs: CLOUD-1295
Refs: CLOUD-1164
Refs: CLOUD-1213
Refs: CLOUD-1297
Admits: 1c68cb4a5d20efa873c0ed2b7564b7cfdf53c0b6ebb9c79e211eea4b8445bcc5
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: batten.toml
Admits-head: a11e917
Admits-epoch: 650a76ba70a8b5645ffc58aedb1bdde72394e39026b5c1391a926934ea49702c
Admits-author: alec@wenzowski.com
Admits-prev: c7d8beede76f452a09d1a6c47a615b728f2876a60e055ef065975a27903f003f
Admits-answer-lost: mise-tasks/bot-issue.sh stays alive as a second implementation of predicates the engine now owns, which is the state policy/shell-retirement.rego's V-SHELL-RULE-EDITED exists to end -- the ported verbs cannot read a lane nobody declared, so refusing this write refuses the retirement rather than this line of it.
Admits-answer-precondition: The bot lane's consumer facts -- which forge repository, which bot logins, which manifests the lane owns, the marker strings, the tracker's key prefix and the branch prefix -- are exactly the values non-negotiable rule 1 forbids in crates/batten. Retiring mise-tasks/bot-issue.sh onto `batten pr derive|file|link|ensure|closes` and `batten claim bot` moves the MATCHER into the engine and leaves those facts here, so batten.toml is the owning surface for them and no other surface can carry them. The write is one additive [bot_lane] block, visible in this pull request's own diff, which is where reviewers read it.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because batten.toml IS the owning surface for a consumer's policy facts; there is no other surface that owns them. A local override cannot carry them either: adding a login to `bots` or a path to `owned_manifests` turns a refusal into a filed row, which is a weakening dressed as an addition and house style section 8's raise-only rule does not admit it. R-RESTORE-IT does not apply because the write is additive and deliberate rather than an accident to undo -- restoring the file would delete the table the ported verbs read.
Admits: ddd46be8d9ae384d97c38556832349bfc8dc319895761edb2255894b821b3763
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: .github/workflows/auto-bot-land.yml
Admits-head: a11e917
Admits-epoch: 8e6d5f2f19b383997365f6b72ff52816d72c7029208dbb81a40389acbd65e67f
Admits-author: alec@wenzowski.com
Admits-prev: -
Admits-answer-lost: The bot lane's lander breaks outright at its next tick: both steps call a task the retirement removed, so no bot pull request gets a row and none is landed. The alternative is keeping mise-tasks/bot-issue.sh alive as a second implementation, which is the state V-SHELL-RULE-EDITED exists to end.
Admits-answer-precondition: auto-bot-land.yml invokes `mise run bot-issue ensure` and `mise run bot-issue closes`, and this change deletes mise-tasks/bot-issue.sh. Leaving the calls would leave the lander invoking a task that no longer exists, so the workflow is the owning surface for its own steps and there is nowhere else the repoint could be made. The write is two invocation repoints -- `batten pr ensure` and `batten pr closes` -- plus the comment naming the retired program, and nothing about what the workflow decides changes.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because .github/workflows/auto-bot-land.yml IS the owning surface for the lander's own steps; a workflow's run lines cannot be declared anywhere else. R-RESTORE-IT does not apply because the change is a deliberate repoint at a successor this same commit lands, and restoring the file would leave a workflow calling a deleted program.
Admits: a3bb7b559f5118c0fe2cc4e4c8e2336e2bcbf1262a0df2578070e44446a20751
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: .github/workflows/sbom-actions-currency.yml
Admits-head: a11e917
Admits-epoch: 8e6d5f2f19b383997365f6b72ff52816d72c7029208dbb81a40389acbd65e67f
Admits-author: alec@wenzowski.com
Admits-prev: e03b883be1a24354b6eaeedb59a2c39f16052d832ef6dde2ceab292ca964fa5b
Admits-answer-lost: Every carry pull request `sbom-actions-currency` opens stalls on `closing-key-check` exactly as #806 did, and each one needs a human to patch its body before it can land -- which is the manual step CLOUD-1213 exists to remove, on the lane whose blockage this whole change is about.
Admits-answer-precondition: The generated pull request body carries `Refs: CLOUD-1213` and no closing marker, so `closing-key-check` reads it as a body naming a key non-closingly and stalls the landing. Measured on #806, where the body was patched by hand to get it through -- the defect repeating on every future carry pull request. The body is generated by this workflow's own `gh pr create` step, so the workflow is the owning surface for it and no other surface can set it. The write is one added line, `DO-NOT-CLOSE CLOUD-1213`, in the same printf list, visible in this pull request's diff.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because .github/workflows/sbom-actions-currency.yml IS the owning surface for the body it generates; a workflow's own `gh pr create` arguments cannot be declared anywhere else. R-RESTORE-IT does not apply because the change is a deliberate one-line addition rather than an accident to undo, and restoring the file would put back the body that stalls.
Admits: 1588b760e38a64a36f5aaef031bd5b3e8f5e1160c0f94eec486b41dbca5d335c
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: .serena/memories/core.md
Admits-head: a11e917
Admits-epoch: a5e521241d3b4c861cd0ffb0813ac2c5eb0bfd1b3b364e13418f67888e3de545
Admits-author: alec@wenzowski.com
Admits-prev: 121e5c62ce26f6caa46a19393e76ae86d200b8b113fccdaf2bb1196df372865b
Admits-answer-lost: `bot.rs` lands with no map row, `module-map-check` fails the gate, and the retirement cannot commit at all -- so refusing this write refuses the whole change rather than this bullet of it. A reader looking up what `bot.rs` owns would find nothing where every other module is described.
Admits-answer-precondition: `module-map-check` refuses a `crates/batten/src/*.rs` module with no row in `.serena/memories/core.md`, and this change adds `crates/batten/src/bot.rs`. The memory IS the module map's authority -- AGENTS.md points there for the per-module layout precisely so the tree is not restated in a budgeted file -- so there is no other surface a row could go on. The write was made through Serena's `edit_memory`, which is the sanctioned route, and it is one added bullet visible in this pull request's diff.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because `.serena/memories/core.md` IS the owning surface for the module map; AGENTS.md defers to it by name and `module-map-check` reads it as the authority. R-RESTORE-IT does not apply because the write is one additive bullet describing a module this same commit introduces -- restoring the file would leave the map missing a row the gate demands.
… claim `sbom-actions-currency` opens a PR that `verify` refuses: there is no claim receipt, and the agent one attests a refinement no bot performed. #806 landed with a `--takeover` claim against CLOUD-1213, which was a fake claim, and every future carry PR meets the same wall. `batten claim carry` is the honest receipt. It attests DERIVABILITY rather than a branch name — a prefix exemption would be a password wearing a branch name — so it mints only when the branch appends rows whose repo the base table already maps, changing the sha alone, and touches nothing else. The verb takes no argument on purpose: the subject is the branch's own diff against its merge base, and a caller that could name its own subject could name one that is derivable while changing something else. Also repairs the `snapshots` task, stale since the integration targets moved under `tests/it/`. Refs: CLOUD-1295
…eceipt
486 lines of shell and 323 of bats, six subcommands, all of it GitHub API plus
one local receipt write. `mise-tasks/bot-issue.sh` and `tests/bot-issue.bats`
are deleted; the predicates land as `crates/batten/src/bot.rs` and six new
leaves — `batten pr derive|file|link|ensure|closes` and `batten claim bot`.
TWO HALVES, KEPT APART INSIDE ONE MODULE. The predicates — is this pull request
one of the lane's, which manifests it touched, what Conventional type its
subject declares, whether a body still CLOSES a key rather than merely naming
one — are pure functions with their own unit tier. The `forge` half underneath
is the only thing that talks to anybody.
THE CONSUMER FACTS ARE `[bot_lane]` IN batten.toml, not literals in the crate.
Which repository, which bot logins, which manifests the lane owns, the marker
strings, the tracker's key prefix and the branch prefix — every value the
retired program spelled internally is a config row now, so a grep of
`crates/batten` for a bot's name or a manifest path returns nothing
(non-negotiable rule 1). `document_facts` caught the first draft's doc comment
naming a workflow glob as an example, and that is the gate working. The derived
row's body is a tracked template with `{{...}}` placeholders rather than a page
of markdown inside a TOML value; an unfilled placeholder refuses at
substitution rather than rendering a template artifact into a tracker row.
THE FORGE'S OWN CLIENT, NOT THIS CRATE'S HTTP TRANSPORT. `pr_watch` is the
landed precedent (CLOUD-1143) and `gh` resolves the credential outside the
crate, where `fetch` would have put token resolution inside it next to no
config row that declares one. `policy/spawn-adapters.rego` places `bot` on
`pr_watch`'s own argument. The commit that widened `fetch::Call` for a PATCH
this port turned out not to need is reverted earlier on this branch.
THE EXIT TABLE CHANGED AND THE WORKFLOW FOLLOWS IT. `bot-issue` used 1 for
"refused, not this lane's" and 2 for "could not look"; house style §7 makes a
refusal 2 and a could-not-look 3. `auto-bot-land.yml`'s two steps are repointed
and their `case` arms moved with them — reading 1 as an ordinary refusal there
would have treated a usage error as a pass.
THE THIRD RECEIPT KIND IS WIRED. `verify` now tries `claim`, then `bot`, then
`carry`, so a licence-carry branch no longer needs the fake `--takeover` claim
#806 landed with. `sbom-actions-currency`'s generated body gains
`DO-NOT-CLOSE CLOUD-1213`, without which every carry pull request stalls on
`closing-key-check` exactly as #806 did and needs a human to patch it.
WHAT IS NOT DONE, AND WHY IT IS FILED RATHER THAN FIXED. The plan called for a
`checks = ["carry"]` row beside `claim-needs-receipt`. That row is wrong: a
receipt rule's `checks` is a CONJUNCTION, so a second row is a second AND and
would deny every ordinary write on every branch. `verify`'s shell body can
express the disjunction and the mediated gate cannot — a live false positive
for the bot receipt since CLOUD-693, which this change's third kind inherits.
CLOUD-1297 owns it, with the disjunction as its §2.
TWO GATES CAUGHT REAL DEFECTS IN THIS CHANGE AND BOTH ARE FIXED HERE.
`no-appeal-to-authority` refused `carry.rs` and its tier for naming a real
action repository as fixture data — a third party's name in the crate, which is
the class the rule exists for, and the fixtures now use neutral names.
`module-layering` refused both new modules as unplaced, which is the coverage
clause working: `bot` and `carry` are placed with their reasons.
ONE EDIT IS DECLINED RATHER THAN FORCED, and CLOUD-1299 owns it. Retiring the
program makes `verify`'s no-receipt remedy name a task that no longer exists —
but `tests/verify.bats` asserts that message contains `bot-issue receipt`, and a
governed suite has two landable shapes, neither of which is an edit.
`repoints_at_the_declared_invocation` cannot admit it either: it requires the
replaced span to be a PATH reference, and a task name plus a subcommand is not
one. So the remedy names the live verb FIRST and keeps the old name as the thing
it replaced — true, useful mid-transition, and satisfying the pin — with the
reason at the site rather than left to read as a slip.
THE LEDGER. Two file arms and 22 case arms, `kind:verb` on each because the
lane needs stdin, spawns with its own arguments and performs writes — none of
which a tree-scoped module may do. `bot-issue` leaves `$MUTANT_GATES` and needs
no `#MUTANT-EXEMPT`: `mutant-census` enumerates `mise-tasks/*.sh` and
`policy/*.rego`, so a deleted program is not in the census at all. The arm
carries a `runs:` field naming `batten claim bot` — ONE arm per deleted path,
which `V-RETIREMENT-AMBIGUOUS` requires, so the field names the only invocation
a GOVERNED caller loses; the lander's two workflow steps are ungoverned and
their repoints are free. Its two
declared mutations are re-homed as named cases —
`idempotence_a_second_call_on_the_same_pr_files_nothing` and
`a_key_named_but_not_closed_is_refused` — each carrying the mutation it stands
for in its comment.
THE LOAD-BEARING CASE IS REPLAYED, NOT ASSERTED. The dying suite ran the REAL
`ready-lint` over the REAL derived payload, because "the derived block is
checkable by the same gate that checks a human's" is the claim that makes a
mechanical row honest. `the_derived_block_passes_ready_lint` does the same over
this repository's committed template and its committed `[[pattern]]` grammar —
and it earned its place immediately, catching that `prettier` reshapes the
template's bullets on every `mise run fmt`.
A KNOWN LAG, STATED RATHER THAN HIDDEN. `auto-bot-land.yml` installs `batten`
from the latest RELEASE, so its two repointed steps fail loudly with "command
not found" between this merge and the next release. That is the same gap
CLOUD-1143 recorded for `checks-green`, and it fails the job rather than
passing silently.
129 suites after the deletion, 706.1s serial on this container, regenerated on
the rebased base rather than the one this change was written against — `main`
retired two more suites in between, and a corpus derived from the older report
would publish a cost for a suite that is gone. The saving is
`tests/bot-issue.bats`' own 2.9s share of the corpus it was measured in, 0.3%;
the rest of the totals' difference is those two suites plus machine noise, and
is not claimed.
BREAKING CHANGE: `config::Config` and `resolve::Resolved` each gain a
`bot_lane` field, so any code constructing either with a struct literal must
name it. `semver check` reports it as `constructible_struct_adds_field` and it
is declared rather than worked around: the alternative is `#[non_exhaustive]`,
which is a break in its own right and a wider one — it would close both structs
to literal construction for every field, not just this one.
Refs: CLOUD-1295
Refs: CLOUD-1164
Refs: CLOUD-1213
Refs: CLOUD-1297
Refs: CLOUD-1299
Admits: 1c68cb4a5d20efa873c0ed2b7564b7cfdf53c0b6ebb9c79e211eea4b8445bcc5
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: batten.toml
Admits-head: a11e917
Admits-epoch: 650a76ba70a8b5645ffc58aedb1bdde72394e39026b5c1391a926934ea49702c
Admits-author: alec@wenzowski.com
Admits-prev: c7d8beede76f452a09d1a6c47a615b728f2876a60e055ef065975a27903f003f
Admits-answer-lost: mise-tasks/bot-issue.sh stays alive as a second implementation of predicates the engine now owns, which is the state policy/shell-retirement.rego's V-SHELL-RULE-EDITED exists to end -- the ported verbs cannot read a lane nobody declared, so refusing this write refuses the retirement rather than this line of it.
Admits-answer-precondition: The bot lane's consumer facts -- which forge repository, which bot logins, which manifests the lane owns, the marker strings, the tracker's key prefix and the branch prefix -- are exactly the values non-negotiable rule 1 forbids in crates/batten. Retiring mise-tasks/bot-issue.sh onto `batten pr derive|file|link|ensure|closes` and `batten claim bot` moves the MATCHER into the engine and leaves those facts here, so batten.toml is the owning surface for them and no other surface can carry them. The write is one additive [bot_lane] block, visible in this pull request's own diff, which is where reviewers read it.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because batten.toml IS the owning surface for a consumer's policy facts; there is no other surface that owns them. A local override cannot carry them either: adding a login to `bots` or a path to `owned_manifests` turns a refusal into a filed row, which is a weakening dressed as an addition and house style section 8's raise-only rule does not admit it. R-RESTORE-IT does not apply because the write is additive and deliberate rather than an accident to undo -- restoring the file would delete the table the ported verbs read.
Admits: ddd46be8d9ae384d97c38556832349bfc8dc319895761edb2255894b821b3763
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: .github/workflows/auto-bot-land.yml
Admits-head: a11e917
Admits-epoch: 8e6d5f2f19b383997365f6b72ff52816d72c7029208dbb81a40389acbd65e67f
Admits-author: alec@wenzowski.com
Admits-prev: -
Admits-answer-lost: The bot lane's lander breaks outright at its next tick: both steps call a task the retirement removed, so no bot pull request gets a row and none is landed. The alternative is keeping mise-tasks/bot-issue.sh alive as a second implementation, which is the state V-SHELL-RULE-EDITED exists to end.
Admits-answer-precondition: auto-bot-land.yml invokes `mise run bot-issue ensure` and `mise run bot-issue closes`, and this change deletes mise-tasks/bot-issue.sh. Leaving the calls would leave the lander invoking a task that no longer exists, so the workflow is the owning surface for its own steps and there is nowhere else the repoint could be made. The write is two invocation repoints -- `batten pr ensure` and `batten pr closes` -- plus the comment naming the retired program, and nothing about what the workflow decides changes.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because .github/workflows/auto-bot-land.yml IS the owning surface for the lander's own steps; a workflow's run lines cannot be declared anywhere else. R-RESTORE-IT does not apply because the change is a deliberate repoint at a successor this same commit lands, and restoring the file would leave a workflow calling a deleted program.
Admits: a3bb7b559f5118c0fe2cc4e4c8e2336e2bcbf1262a0df2578070e44446a20751
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: .github/workflows/sbom-actions-currency.yml
Admits-head: a11e917
Admits-epoch: 8e6d5f2f19b383997365f6b72ff52816d72c7029208dbb81a40389acbd65e67f
Admits-author: alec@wenzowski.com
Admits-prev: e03b883be1a24354b6eaeedb59a2c39f16052d832ef6dde2ceab292ca964fa5b
Admits-answer-lost: Every carry pull request `sbom-actions-currency` opens stalls on `closing-key-check` exactly as #806 did, and each one needs a human to patch its body before it can land -- which is the manual step CLOUD-1213 exists to remove, on the lane whose blockage this whole change is about.
Admits-answer-precondition: The generated pull request body carries `Refs: CLOUD-1213` and no closing marker, so `closing-key-check` reads it as a body naming a key non-closingly and stalls the landing. Measured on #806, where the body was patched by hand to get it through -- the defect repeating on every future carry pull request. The body is generated by this workflow's own `gh pr create` step, so the workflow is the owning surface for it and no other surface can set it. The write is one added line, `DO-NOT-CLOSE CLOUD-1213`, in the same printf list, visible in this pull request's diff.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because .github/workflows/sbom-actions-currency.yml IS the owning surface for the body it generates; a workflow's own `gh pr create` arguments cannot be declared anywhere else. R-RESTORE-IT does not apply because the change is a deliberate one-line addition rather than an accident to undo, and restoring the file would put back the body that stalls.
Admits: 1588b760e38a64a36f5aaef031bd5b3e8f5e1160c0f94eec486b41dbca5d335c
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: .serena/memories/core.md
Admits-head: a11e917
Admits-epoch: a5e521241d3b4c861cd0ffb0813ac2c5eb0bfd1b3b364e13418f67888e3de545
Admits-author: alec@wenzowski.com
Admits-prev: 121e5c62ce26f6caa46a19393e76ae86d200b8b113fccdaf2bb1196df372865b
Admits-answer-lost: `bot.rs` lands with no map row, `module-map-check` fails the gate, and the retirement cannot commit at all -- so refusing this write refuses the whole change rather than this bullet of it. A reader looking up what `bot.rs` owns would find nothing where every other module is described.
Admits-answer-precondition: `module-map-check` refuses a `crates/batten/src/*.rs` module with no row in `.serena/memories/core.md`, and this change adds `crates/batten/src/bot.rs`. The memory IS the module map's authority -- AGENTS.md points there for the per-module layout precisely so the tree is not restated in a budgeted file -- so there is no other surface a row could go on. The write was made through Serena's `edit_memory`, which is the sanctioned route, and it is one added bullet visible in this pull request's diff.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because `.serena/memories/core.md` IS the owning surface for the module map; AGENTS.md defers to it by name and `module-map-check` reads it as the authority. R-RESTORE-IT does not apply because the write is one additive bullet describing a module this same commit introduces -- restoring the file would leave the map missing a row the gate demands.
… claim `sbom-actions-currency` opens a PR that `verify` refuses: there is no claim receipt, and the agent one attests a refinement no bot performed. #806 landed with a `--takeover` claim against CLOUD-1213, which was a fake claim, and every future carry PR meets the same wall. `batten claim carry` is the honest receipt. It attests DERIVABILITY rather than a branch name — a prefix exemption would be a password wearing a branch name — so it mints only when the branch appends rows whose repo the base table already maps, changing the sha alone, and touches nothing else. The verb takes no argument on purpose: the subject is the branch's own diff against its merge base, and a caller that could name its own subject could name one that is derivable while changing something else. Also repairs the `snapshots` task, stale since the integration targets moved under `tests/it/`. Refs: CLOUD-1295
…eceipt
486 lines of shell and 323 of bats, six subcommands, all of it GitHub API plus
one local receipt write. `mise-tasks/bot-issue.sh` and `tests/bot-issue.bats`
are deleted; the predicates land as `crates/batten/src/bot.rs` and six new
leaves — `batten pr derive|file|link|ensure|closes` and `batten claim bot`.
TWO HALVES, KEPT APART INSIDE ONE MODULE. The predicates — is this pull request
one of the lane's, which manifests it touched, what Conventional type its
subject declares, whether a body still CLOSES a key rather than merely naming
one — are pure functions with their own unit tier. The `forge` half underneath
is the only thing that talks to anybody.
THE CONSUMER FACTS ARE `[bot_lane]` IN batten.toml, not literals in the crate.
Which repository, which bot logins, which manifests the lane owns, the marker
strings, the tracker's key prefix and the branch prefix — every value the
retired program spelled internally is a config row now, so a grep of
`crates/batten` for a bot's name or a manifest path returns nothing
(non-negotiable rule 1). `document_facts` caught the first draft's doc comment
naming a workflow glob as an example, and that is the gate working. The derived
row's body is a tracked template with `{{...}}` placeholders rather than a page
of markdown inside a TOML value; an unfilled placeholder refuses at
substitution rather than rendering a template artifact into a tracker row.
THE FORGE'S OWN CLIENT, NOT THIS CRATE'S HTTP TRANSPORT. `pr_watch` is the
landed precedent (CLOUD-1143) and `gh` resolves the credential outside the
crate, where `fetch` would have put token resolution inside it next to no
config row that declares one. `policy/spawn-adapters.rego` places `bot` on
`pr_watch`'s own argument. The commit that widened `fetch::Call` for a PATCH
this port turned out not to need is reverted earlier on this branch.
THE EXIT TABLE CHANGED AND THE WORKFLOW FOLLOWS IT. `bot-issue` used 1 for
"refused, not this lane's" and 2 for "could not look"; house style §7 makes a
refusal 2 and a could-not-look 3. `auto-bot-land.yml`'s two steps are repointed
and their `case` arms moved with them — reading 1 as an ordinary refusal there
would have treated a usage error as a pass.
THE THIRD RECEIPT KIND IS WIRED. `verify` now tries `claim`, then `bot`, then
`carry`, so a licence-carry branch no longer needs the fake `--takeover` claim
#806 landed with. `sbom-actions-currency`'s generated body gains
`DO-NOT-CLOSE CLOUD-1213`, without which every carry pull request stalls on
`closing-key-check` exactly as #806 did and needs a human to patch it.
WHAT IS NOT DONE, AND WHY IT IS FILED RATHER THAN FIXED. The plan called for a
`checks = ["carry"]` row beside `claim-needs-receipt`. That row is wrong: a
receipt rule's `checks` is a CONJUNCTION, so a second row is a second AND and
would deny every ordinary write on every branch. `verify`'s shell body can
express the disjunction and the mediated gate cannot — a live false positive
for the bot receipt since CLOUD-693, which this change's third kind inherits.
CLOUD-1297 owns it, with the disjunction as its §2.
TWO GATES CAUGHT REAL DEFECTS IN THIS CHANGE AND BOTH ARE FIXED HERE.
`no-appeal-to-authority` refused `carry.rs` and its tier for naming a real
action repository as fixture data — a third party's name in the crate, which is
the class the rule exists for, and the fixtures now use neutral names.
`module-layering` refused both new modules as unplaced, which is the coverage
clause working: `bot` and `carry` are placed with their reasons.
ONE EDIT IS DECLINED RATHER THAN FORCED, and CLOUD-1299 owns it. Retiring the
program makes `verify`'s no-receipt remedy name a task that no longer exists —
but `tests/verify.bats` asserts that message contains `bot-issue receipt`, and a
governed suite has two landable shapes, neither of which is an edit.
`repoints_at_the_declared_invocation` cannot admit it either: it requires the
replaced span to be a PATH reference, and a task name plus a subcommand is not
one. So the remedy names the live verb FIRST and keeps the old name as the thing
it replaced — true, useful mid-transition, and satisfying the pin — with the
reason at the site rather than left to read as a slip.
THE LEDGER. Two file arms and 22 case arms, `kind:verb` on each because the
lane needs stdin, spawns with its own arguments and performs writes — none of
which a tree-scoped module may do. `bot-issue` leaves `$MUTANT_GATES` and needs
no `#MUTANT-EXEMPT`: the census enumerates `mise-tasks/*.sh` and `policy/*.rego`
— `mutate::subjects`, since `main` retired `mutant-census` onto the engine under
this branch — so a deleted program is not in the census at all. The arm
carries a `runs:` field naming `batten claim bot` — ONE arm per deleted path,
which `V-RETIREMENT-AMBIGUOUS` requires, so the field names the only invocation
a GOVERNED caller loses; the lander's two workflow steps are ungoverned and
their repoints are free. Its two
declared mutations are re-homed as named cases —
`idempotence_a_second_call_on_the_same_pr_files_nothing` and
`a_key_named_but_not_closed_is_refused` — each carrying the mutation it stands
for in its comment.
THE LOAD-BEARING CASE IS REPLAYED, NOT ASSERTED. The dying suite ran the REAL
`ready-lint` over the REAL derived payload, because "the derived block is
checkable by the same gate that checks a human's" is the claim that makes a
mechanical row honest. `the_derived_block_passes_ready_lint` does the same over
this repository's committed template and its committed `[[pattern]]` grammar —
and it earned its place immediately, catching that `prettier` reshapes the
template's bullets on every `mise run fmt`.
A KNOWN LAG, STATED RATHER THAN HIDDEN. `auto-bot-land.yml` installs `batten`
from the latest RELEASE, so its two repointed steps fail loudly with "command
not found" between this merge and the next release. That is the same gap
CLOUD-1143 recorded for `checks-green`, and it fails the job rather than
passing silently.
127 suites after the deletion, 748.7s serial on this container, regenerated on
the rebased base rather than the one this change was written against — `main`
retired four more suites in between, `mutant` and `mutant-census` among them,
and a corpus derived from an older report publishes a cost for a suite that is
gone. The saving this change can claim is `tests/bot-issue.bats`' own 2.9s share
of the corpus it was measured in, 0.3%; the totals differ by more in both
directions across runs, and that is those four suites plus machine noise rather
than anything this branch did.
BREAKING CHANGE: `config::Config` and `resolve::Resolved` each gain a
`bot_lane` field, so any code constructing either with a struct literal must
name it. `semver check` reports it as `constructible_struct_adds_field` and it
is declared rather than worked around: the alternative is `#[non_exhaustive]`,
which is a break in its own right and a wider one — it would close both structs
to literal construction for every field, not just this one.
Refs: CLOUD-1295
Refs: CLOUD-1164
Refs: CLOUD-1213
Refs: CLOUD-1297
Refs: CLOUD-1299
Admits: 1c68cb4a5d20efa873c0ed2b7564b7cfdf53c0b6ebb9c79e211eea4b8445bcc5
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: batten.toml
Admits-head: a11e917
Admits-epoch: 650a76ba70a8b5645ffc58aedb1bdde72394e39026b5c1391a926934ea49702c
Admits-author: alec@wenzowski.com
Admits-prev: c7d8beede76f452a09d1a6c47a615b728f2876a60e055ef065975a27903f003f
Admits-answer-lost: mise-tasks/bot-issue.sh stays alive as a second implementation of predicates the engine now owns, which is the state policy/shell-retirement.rego's V-SHELL-RULE-EDITED exists to end -- the ported verbs cannot read a lane nobody declared, so refusing this write refuses the retirement rather than this line of it.
Admits-answer-precondition: The bot lane's consumer facts -- which forge repository, which bot logins, which manifests the lane owns, the marker strings, the tracker's key prefix and the branch prefix -- are exactly the values non-negotiable rule 1 forbids in crates/batten. Retiring mise-tasks/bot-issue.sh onto `batten pr derive|file|link|ensure|closes` and `batten claim bot` moves the MATCHER into the engine and leaves those facts here, so batten.toml is the owning surface for them and no other surface can carry them. The write is one additive [bot_lane] block, visible in this pull request's own diff, which is where reviewers read it.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because batten.toml IS the owning surface for a consumer's policy facts; there is no other surface that owns them. A local override cannot carry them either: adding a login to `bots` or a path to `owned_manifests` turns a refusal into a filed row, which is a weakening dressed as an addition and house style section 8's raise-only rule does not admit it. R-RESTORE-IT does not apply because the write is additive and deliberate rather than an accident to undo -- restoring the file would delete the table the ported verbs read.
Admits: ddd46be8d9ae384d97c38556832349bfc8dc319895761edb2255894b821b3763
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: .github/workflows/auto-bot-land.yml
Admits-head: a11e917
Admits-epoch: 8e6d5f2f19b383997365f6b72ff52816d72c7029208dbb81a40389acbd65e67f
Admits-author: alec@wenzowski.com
Admits-prev: -
Admits-answer-lost: The bot lane's lander breaks outright at its next tick: both steps call a task the retirement removed, so no bot pull request gets a row and none is landed. The alternative is keeping mise-tasks/bot-issue.sh alive as a second implementation, which is the state V-SHELL-RULE-EDITED exists to end.
Admits-answer-precondition: auto-bot-land.yml invokes `mise run bot-issue ensure` and `mise run bot-issue closes`, and this change deletes mise-tasks/bot-issue.sh. Leaving the calls would leave the lander invoking a task that no longer exists, so the workflow is the owning surface for its own steps and there is nowhere else the repoint could be made. The write is two invocation repoints -- `batten pr ensure` and `batten pr closes` -- plus the comment naming the retired program, and nothing about what the workflow decides changes.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because .github/workflows/auto-bot-land.yml IS the owning surface for the lander's own steps; a workflow's run lines cannot be declared anywhere else. R-RESTORE-IT does not apply because the change is a deliberate repoint at a successor this same commit lands, and restoring the file would leave a workflow calling a deleted program.
Admits: a3bb7b559f5118c0fe2cc4e4c8e2336e2bcbf1262a0df2578070e44446a20751
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: .github/workflows/sbom-actions-currency.yml
Admits-head: a11e917
Admits-epoch: 8e6d5f2f19b383997365f6b72ff52816d72c7029208dbb81a40389acbd65e67f
Admits-author: alec@wenzowski.com
Admits-prev: e03b883be1a24354b6eaeedb59a2c39f16052d832ef6dde2ceab292ca964fa5b
Admits-answer-lost: Every carry pull request `sbom-actions-currency` opens stalls on `closing-key-check` exactly as #806 did, and each one needs a human to patch its body before it can land -- which is the manual step CLOUD-1213 exists to remove, on the lane whose blockage this whole change is about.
Admits-answer-precondition: The generated pull request body carries `Refs: CLOUD-1213` and no closing marker, so `closing-key-check` reads it as a body naming a key non-closingly and stalls the landing. Measured on #806, where the body was patched by hand to get it through -- the defect repeating on every future carry pull request. The body is generated by this workflow's own `gh pr create` step, so the workflow is the owning surface for it and no other surface can set it. The write is one added line, `DO-NOT-CLOSE CLOUD-1213`, in the same printf list, visible in this pull request's diff.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because .github/workflows/sbom-actions-currency.yml IS the owning surface for the body it generates; a workflow's own `gh pr create` arguments cannot be declared anywhere else. R-RESTORE-IT does not apply because the change is a deliberate one-line addition rather than an accident to undo, and restoring the file would put back the body that stalls.
Admits: 1588b760e38a64a36f5aaef031bd5b3e8f5e1160c0f94eec486b41dbca5d335c
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: .serena/memories/core.md
Admits-head: a11e917
Admits-epoch: a5e521241d3b4c861cd0ffb0813ac2c5eb0bfd1b3b364e13418f67888e3de545
Admits-author: alec@wenzowski.com
Admits-prev: 121e5c62ce26f6caa46a19393e76ae86d200b8b113fccdaf2bb1196df372865b
Admits-answer-lost: `bot.rs` lands with no map row, `module-map-check` fails the gate, and the retirement cannot commit at all -- so refusing this write refuses the whole change rather than this bullet of it. A reader looking up what `bot.rs` owns would find nothing where every other module is described.
Admits-answer-precondition: `module-map-check` refuses a `crates/batten/src/*.rs` module with no row in `.serena/memories/core.md`, and this change adds `crates/batten/src/bot.rs`. The memory IS the module map's authority -- AGENTS.md points there for the per-module layout precisely so the tree is not restated in a budgeted file -- so there is no other surface a row could go on. The write was made through Serena's `edit_memory`, which is the sanctioned route, and it is one added bullet visible in this pull request's diff.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because `.serena/memories/core.md` IS the owning surface for the module map; AGENTS.md defers to it by name and `module-map-check` reads it as the authority. R-RESTORE-IT does not apply because the write is one additive bullet describing a module this same commit introduces -- restoring the file would leave the map missing a row the gate demands.
Admits: cbb7e7b9a66ad1ec32b1d2e5f2df9e67efc920620236ceb72e667aa6e772fdc7
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: policy/spawn-adapters.rego
Admits-head: 6da96ce
Admits-epoch: d8992f36146d4a8f3bd0250644ab0624b29c8cc7abd4b5640b4c4aa2c6c5efe6
Admits-author: alec@wenzowski.com
Admits-prev: -
Admits-answer-lost: The rebase cannot complete, so the retirement cannot land at all. Taking `main`'s side alone leaves `bot` unplaced and `spawn-adapters` refuses `crates/batten/src/bot.rs`; taking this branch's side alone deletes `mutate` and reintroduces the refusal on the module `main` just landed.
Admits-answer-precondition: This is a REBASE CONFLICT RESOLUTION, and both sides of it are additions to the same set literal. `main` placed `mutate` when it retired `mise-tasks/mutant.sh`; this branch places `bot` when it retires `mise-tasks/bot-issue.sh`. Neither adapter can be dropped -- `spawn-adapters` refuses a spawn in a module the table has not placed, so losing either side reintroduces the refusal the other change already answered. The resolution is the union of the two rows plus both reasons, which is what the file would hold had the two landed in sequence rather than concurrently. There is no other surface: the adapter table IS this module.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because `policy/spawn-adapters.rego` IS the owning surface for the adapter placement table; the set literal cannot be declared anywhere else. R-RESTORE-IT does not apply because restoring the file to either side is precisely what loses one of the two placements -- there is no version of this file that carries both without this write.
… claim `sbom-actions-currency` opens a PR that `verify` refuses: there is no claim receipt, and the agent one attests a refinement no bot performed. #806 landed with a `--takeover` claim against CLOUD-1213, which was a fake claim, and every future carry PR meets the same wall. `batten claim carry` is the honest receipt. It attests DERIVABILITY rather than a branch name — a prefix exemption would be a password wearing a branch name — so it mints only when the branch appends rows whose repo the base table already maps, changing the sha alone, and touches nothing else. The verb takes no argument on purpose: the subject is the branch's own diff against its merge base, and a caller that could name its own subject could name one that is derivable while changing something else. Also repairs the `snapshots` task, stale since the integration targets moved under `tests/it/`. Refs: CLOUD-1295
…eceipt
486 lines of shell and 323 of bats, six subcommands, all of it GitHub API plus
one local receipt write. `mise-tasks/bot-issue.sh` and `tests/bot-issue.bats`
are deleted; the predicates land as `crates/batten/src/bot.rs` and six new
leaves — `batten pr derive|file|link|ensure|closes` and `batten claim bot`.
TWO HALVES, KEPT APART INSIDE ONE MODULE. The predicates — is this pull request
one of the lane's, which manifests it touched, what Conventional type its
subject declares, whether a body still CLOSES a key rather than merely naming
one — are pure functions with their own unit tier. The `forge` half underneath
is the only thing that talks to anybody.
THE CONSUMER FACTS ARE `[bot_lane]` IN batten.toml, not literals in the crate.
Which repository, which bot logins, which manifests the lane owns, the marker
strings, the tracker's key prefix and the branch prefix — every value the
retired program spelled internally is a config row now, so a grep of
`crates/batten` for a bot's name or a manifest path returns nothing
(non-negotiable rule 1). `document_facts` caught the first draft's doc comment
naming a workflow glob as an example, and that is the gate working. The derived
row's body is a tracked template with `{{...}}` placeholders rather than a page
of markdown inside a TOML value; an unfilled placeholder refuses at
substitution rather than rendering a template artifact into a tracker row.
THE FORGE'S OWN CLIENT, NOT THIS CRATE'S HTTP TRANSPORT. `pr_watch` is the
landed precedent (CLOUD-1143) and `gh` resolves the credential outside the
crate, where `fetch` would have put token resolution inside it next to no
config row that declares one. `policy/spawn-adapters.rego` places `bot` on
`pr_watch`'s own argument. The commit that widened `fetch::Call` for a PATCH
this port turned out not to need is reverted earlier on this branch.
THE EXIT TABLE CHANGED AND THE WORKFLOW FOLLOWS IT. `bot-issue` used 1 for
"refused, not this lane's" and 2 for "could not look"; house style §7 makes a
refusal 2 and a could-not-look 3. `auto-bot-land.yml`'s two steps are repointed
and their `case` arms moved with them — reading 1 as an ordinary refusal there
would have treated a usage error as a pass.
THE THIRD RECEIPT KIND IS WIRED. `verify` now tries `claim`, then `bot`, then
`carry`, so a licence-carry branch no longer needs the fake `--takeover` claim
`DO-NOT-CLOSE CLOUD-1213`, without which every carry pull request stalls on
`closing-key-check` exactly as #806 did and needs a human to patch it.
WHAT IS NOT DONE, AND WHY IT IS FILED RATHER THAN FIXED. The plan called for a
`checks = ["carry"]` row beside `claim-needs-receipt`. That row is wrong: a
receipt rule's `checks` is a CONJUNCTION, so a second row is a second AND and
would deny every ordinary write on every branch. `verify`'s shell body can
express the disjunction and the mediated gate cannot — a live false positive
for the bot receipt since CLOUD-693, which this change's third kind inherits.
CLOUD-1297 owns it, with the disjunction as its §2.
TWO GATES CAUGHT REAL DEFECTS IN THIS CHANGE AND BOTH ARE FIXED HERE.
`no-appeal-to-authority` refused `carry.rs` and its tier for naming a real
action repository as fixture data — a third party's name in the crate, which is
the class the rule exists for, and the fixtures now use neutral names.
`module-layering` refused both new modules as unplaced, which is the coverage
clause working: `bot` and `carry` are placed with their reasons.
ONE EDIT IS DECLINED RATHER THAN FORCED, and CLOUD-1299 owns it. Retiring the
program makes `verify`'s no-receipt remedy name a task that no longer exists —
but `tests/verify.bats` asserts that message contains `bot-issue receipt`, and a
governed suite has two landable shapes, neither of which is an edit.
`repoints_at_the_declared_invocation` cannot admit it either: it requires the
replaced span to be a PATH reference, and a task name plus a subcommand is not
one. So the remedy names the live verb FIRST and keeps the old name as the thing
it replaced — true, useful mid-transition, and satisfying the pin — with the
reason at the site rather than left to read as a slip.
THE LEDGER. Two file arms and 22 case arms, `kind:verb` on each because the
lane needs stdin, spawns with its own arguments and performs writes — none of
which a tree-scoped module may do. `bot-issue` leaves `$MUTANT_GATES` and needs
no `#MUTANT-EXEMPT`: the census enumerates `mise-tasks/*.sh` and `policy/*.rego`
— `mutate::subjects`, since `main` retired `mutant-census` onto the engine under
this branch — so a deleted program is not in the census at all. The arm
carries a `runs:` field naming `batten claim bot` — ONE arm per deleted path,
which `V-RETIREMENT-AMBIGUOUS` requires, so the field names the only invocation
a GOVERNED caller loses; the lander's two workflow steps are ungoverned and
their repoints are free. Its two
declared mutations are re-homed as named cases —
`idempotence_a_second_call_on_the_same_pr_files_nothing` and
`a_key_named_but_not_closed_is_refused` — each carrying the mutation it stands
for in its comment.
THE LOAD-BEARING CASE IS REPLAYED, NOT ASSERTED. The dying suite ran the REAL
`ready-lint` over the REAL derived payload, because "the derived block is
checkable by the same gate that checks a human's" is the claim that makes a
mechanical row honest. `the_derived_block_passes_ready_lint` does the same over
this repository's committed template and its committed `[[pattern]]` grammar —
and it earned its place immediately, catching that `prettier` reshapes the
template's bullets on every `mise run fmt`.
A KNOWN LAG, STATED RATHER THAN HIDDEN. `auto-bot-land.yml` installs `batten`
from the latest RELEASE, so its two repointed steps fail loudly with "command
not found" between this merge and the next release. That is the same gap
CLOUD-1143 recorded for `checks-green`, and it fails the job rather than
passing silently.
127 suites after the deletion, 748.7s serial on this container, regenerated on
the rebased base rather than the one this change was written against — `main`
retired four more suites in between, `mutant` and `mutant-census` among them,
and a corpus derived from an older report publishes a cost for a suite that is
gone. The saving this change can claim is `tests/bot-issue.bats`' own 2.9s share
of the corpus it was measured in, 0.3%; the totals differ by more in both
directions across runs, and that is those four suites plus machine noise rather
than anything this branch did.
BREAKING CHANGE: `config::Config` and `resolve::Resolved` each gain a
`bot_lane` field, so any code constructing either with a struct literal must
name it. `semver check` reports it as `constructible_struct_adds_field` and it
is declared rather than worked around: the alternative is `#[non_exhaustive]`,
which is a break in its own right and a wider one — it would close both structs
to literal construction for every field, not just this one.
Refs: CLOUD-1295
Refs: CLOUD-1164
Refs: CLOUD-1213
Refs: CLOUD-1297
Refs: CLOUD-1299
Admits: 1c68cb4a5d20efa873c0ed2b7564b7cfdf53c0b6ebb9c79e211eea4b8445bcc5
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: batten.toml
Admits-head: a11e917
Admits-epoch: 650a76ba70a8b5645ffc58aedb1bdde72394e39026b5c1391a926934ea49702c
Admits-author: alec@wenzowski.com
Admits-prev: c7d8beede76f452a09d1a6c47a615b728f2876a60e055ef065975a27903f003f
Admits-answer-lost: mise-tasks/bot-issue.sh stays alive as a second implementation of predicates the engine now owns, which is the state policy/shell-retirement.rego's V-SHELL-RULE-EDITED exists to end -- the ported verbs cannot read a lane nobody declared, so refusing this write refuses the retirement rather than this line of it.
Admits-answer-precondition: The bot lane's consumer facts -- which forge repository, which bot logins, which manifests the lane owns, the marker strings, the tracker's key prefix and the branch prefix -- are exactly the values non-negotiable rule 1 forbids in crates/batten. Retiring mise-tasks/bot-issue.sh onto `batten pr derive|file|link|ensure|closes` and `batten claim bot` moves the MATCHER into the engine and leaves those facts here, so batten.toml is the owning surface for them and no other surface can carry them. The write is one additive [bot_lane] block, visible in this pull request's own diff, which is where reviewers read it.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because batten.toml IS the owning surface for a consumer's policy facts; there is no other surface that owns them. A local override cannot carry them either: adding a login to `bots` or a path to `owned_manifests` turns a refusal into a filed row, which is a weakening dressed as an addition and house style section 8's raise-only rule does not admit it. R-RESTORE-IT does not apply because the write is additive and deliberate rather than an accident to undo -- restoring the file would delete the table the ported verbs read.
Admits: ddd46be8d9ae384d97c38556832349bfc8dc319895761edb2255894b821b3763
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: .github/workflows/auto-bot-land.yml
Admits-head: a11e917
Admits-epoch: 8e6d5f2f19b383997365f6b72ff52816d72c7029208dbb81a40389acbd65e67f
Admits-author: alec@wenzowski.com
Admits-prev: -
Admits-answer-lost: The bot lane's lander breaks outright at its next tick: both steps call a task the retirement removed, so no bot pull request gets a row and none is landed. The alternative is keeping mise-tasks/bot-issue.sh alive as a second implementation, which is the state V-SHELL-RULE-EDITED exists to end.
Admits-answer-precondition: auto-bot-land.yml invokes `mise run bot-issue ensure` and `mise run bot-issue closes`, and this change deletes mise-tasks/bot-issue.sh. Leaving the calls would leave the lander invoking a task that no longer exists, so the workflow is the owning surface for its own steps and there is nowhere else the repoint could be made. The write is two invocation repoints -- `batten pr ensure` and `batten pr closes` -- plus the comment naming the retired program, and nothing about what the workflow decides changes.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because .github/workflows/auto-bot-land.yml IS the owning surface for the lander's own steps; a workflow's run lines cannot be declared anywhere else. R-RESTORE-IT does not apply because the change is a deliberate repoint at a successor this same commit lands, and restoring the file would leave a workflow calling a deleted program.
Admits: a3bb7b559f5118c0fe2cc4e4c8e2336e2bcbf1262a0df2578070e44446a20751
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: .github/workflows/sbom-actions-currency.yml
Admits-head: a11e917
Admits-epoch: 8e6d5f2f19b383997365f6b72ff52816d72c7029208dbb81a40389acbd65e67f
Admits-author: alec@wenzowski.com
Admits-prev: e03b883be1a24354b6eaeedb59a2c39f16052d832ef6dde2ceab292ca964fa5b
Admits-answer-lost: Every carry pull request `sbom-actions-currency` opens stalls on `closing-key-check` exactly as #806 did, and each one needs a human to patch its body before it can land -- which is the manual step CLOUD-1213 exists to remove, on the lane whose blockage this whole change is about.
Admits-answer-precondition: The generated pull request body carries `Refs: CLOUD-1213` and no closing marker, so `closing-key-check` reads it as a body naming a key non-closingly and stalls the landing. Measured on #806, where the body was patched by hand to get it through -- the defect repeating on every future carry pull request. The body is generated by this workflow's own `gh pr create` step, so the workflow is the owning surface for it and no other surface can set it. The write is one added line, `DO-NOT-CLOSE CLOUD-1213`, in the same printf list, visible in this pull request's diff.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because .github/workflows/sbom-actions-currency.yml IS the owning surface for the body it generates; a workflow's own `gh pr create` arguments cannot be declared anywhere else. R-RESTORE-IT does not apply because the change is a deliberate one-line addition rather than an accident to undo, and restoring the file would put back the body that stalls.
Admits: 1588b760e38a64a36f5aaef031bd5b3e8f5e1160c0f94eec486b41dbca5d335c
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: .serena/memories/core.md
Admits-head: a11e917
Admits-epoch: a5e521241d3b4c861cd0ffb0813ac2c5eb0bfd1b3b364e13418f67888e3de545
Admits-author: alec@wenzowski.com
Admits-prev: 121e5c62ce26f6caa46a19393e76ae86d200b8b113fccdaf2bb1196df372865b
Admits-answer-lost: `bot.rs` lands with no map row, `module-map-check` fails the gate, and the retirement cannot commit at all -- so refusing this write refuses the whole change rather than this bullet of it. A reader looking up what `bot.rs` owns would find nothing where every other module is described.
Admits-answer-precondition: `module-map-check` refuses a `crates/batten/src/*.rs` module with no row in `.serena/memories/core.md`, and this change adds `crates/batten/src/bot.rs`. The memory IS the module map's authority -- AGENTS.md points there for the per-module layout precisely so the tree is not restated in a budgeted file -- so there is no other surface a row could go on. The write was made through Serena's `edit_memory`, which is the sanctioned route, and it is one added bullet visible in this pull request's diff.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because `.serena/memories/core.md` IS the owning surface for the module map; AGENTS.md defers to it by name and `module-map-check` reads it as the authority. R-RESTORE-IT does not apply because the write is one additive bullet describing a module this same commit introduces -- restoring the file would leave the map missing a row the gate demands.
Admits: cbb7e7b9a66ad1ec32b1d2e5f2df9e67efc920620236ceb72e667aa6e772fdc7
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: policy/spawn-adapters.rego
Admits-head: 6da96ce
Admits-epoch: d8992f36146d4a8f3bd0250644ab0624b29c8cc7abd4b5640b4c4aa2c6c5efe6
Admits-author: alec@wenzowski.com
Admits-prev: -
Admits-answer-lost: The rebase cannot complete, so the retirement cannot land at all. Taking `main`'s side alone leaves `bot` unplaced and `spawn-adapters` refuses `crates/batten/src/bot.rs`; taking this branch's side alone deletes `mutate` and reintroduces the refusal on the module `main` just landed.
Admits-answer-precondition: This is a REBASE CONFLICT RESOLUTION, and both sides of it are additions to the same set literal. `main` placed `mutate` when it retired `mise-tasks/mutant.sh`; this branch places `bot` when it retires `mise-tasks/bot-issue.sh`. Neither adapter can be dropped -- `spawn-adapters` refuses a spawn in a module the table has not placed, so losing either side reintroduces the refusal the other change already answered. The resolution is the union of the two rows plus both reasons, which is what the file would hold had the two landed in sequence rather than concurrently. There is no other surface: the adapter table IS this module.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because `policy/spawn-adapters.rego` IS the owning surface for the adapter placement table; the set literal cannot be declared anywhere else. R-RESTORE-IT does not apply because restoring the file to either side is precisely what loses one of the two placements -- there is no version of this file that carries both without this write.
…eceipt
486 lines of shell and 323 of bats, six subcommands, all of it GitHub API plus
one local receipt write. `mise-tasks/bot-issue.sh` and `tests/bot-issue.bats`
are deleted; the predicates land as `crates/batten/src/bot.rs` and six new
leaves — `batten pr derive|file|link|ensure|closes` and `batten claim bot`.
TWO HALVES, KEPT APART INSIDE ONE MODULE. The predicates — is this pull request
one of the lane's, which manifests it touched, what Conventional type its
subject declares, whether a body still CLOSES a key rather than merely naming
one — are pure functions with their own unit tier. The `forge` half underneath
is the only thing that talks to anybody.
THE CONSUMER FACTS ARE `[bot_lane]` IN batten.toml, not literals in the crate.
Which repository, which bot logins, which manifests the lane owns, the marker
strings, the tracker's key prefix and the branch prefix — every value the
retired program spelled internally is a config row now, so a grep of
`crates/batten` for a bot's name or a manifest path returns nothing
(non-negotiable rule 1). `document_facts` caught the first draft's doc comment
naming a workflow glob as an example, and that is the gate working. The derived
row's body is a tracked template with `{{...}}` placeholders rather than a page
of markdown inside a TOML value; an unfilled placeholder refuses at
substitution rather than rendering a template artifact into a tracker row.
THE FORGE'S OWN CLIENT, NOT THIS CRATE'S HTTP TRANSPORT. `pr_watch` is the
landed precedent (CLOUD-1143) and `gh` resolves the credential outside the
crate, where `fetch` would have put token resolution inside it next to no
config row that declares one. `policy/spawn-adapters.rego` places `bot` on
`pr_watch`'s own argument. The commit that widened `fetch::Call` for a PATCH
this port turned out not to need is reverted earlier on this branch.
THE EXIT TABLE CHANGED AND THE WORKFLOW FOLLOWS IT. `bot-issue` used 1 for
"refused, not this lane's" and 2 for "could not look"; house style §7 makes a
refusal 2 and a could-not-look 3. `auto-bot-land.yml`'s two steps are repointed
and their `case` arms moved with them — reading 1 as an ordinary refusal there
would have treated a usage error as a pass.
THE THIRD RECEIPT KIND IS WIRED. `verify` now tries `claim`, then `bot`, then
`carry`, so a licence-carry branch no longer needs the fake `--takeover` claim
#806 landed with. `sbom-actions-currency`'s generated body gains
`DO-NOT-CLOSE CLOUD-1213`, without which every carry pull request stalls on
`closing-key-check` exactly as #806 did and needs a human to patch it.
WHAT IS NOT DONE, AND WHY IT IS FILED RATHER THAN FIXED. The plan called for a
`checks = ["carry"]` row beside `claim-needs-receipt`. That row is wrong: a
receipt rule's `checks` is a CONJUNCTION, so a second row is a second AND and
would deny every ordinary write on every branch. `verify`'s shell body can
express the disjunction and the mediated gate cannot — a live false positive
for the bot receipt since CLOUD-693, which this change's third kind inherits.
CLOUD-1297 owns it, with the disjunction as its §2.
TWO GATES CAUGHT REAL DEFECTS IN THIS CHANGE AND BOTH ARE FIXED HERE.
`no-appeal-to-authority` refused `carry.rs` and its tier for naming a real
action repository as fixture data — a third party's name in the crate, which is
the class the rule exists for, and the fixtures now use neutral names.
`module-layering` refused both new modules as unplaced, which is the coverage
clause working: `bot` and `carry` are placed with their reasons.
ONE EDIT IS DECLINED RATHER THAN FORCED, and CLOUD-1299 owns it. Retiring the
program makes `verify`'s no-receipt remedy name a task that no longer exists —
but `tests/verify.bats` asserts that message contains `bot-issue receipt`, and a
governed suite has two landable shapes, neither of which is an edit.
`repoints_at_the_declared_invocation` cannot admit it either: it requires the
replaced span to be a PATH reference, and a task name plus a subcommand is not
one. So the remedy names the live verb FIRST and keeps the old name as the thing
it replaced — true, useful mid-transition, and satisfying the pin — with the
reason at the site rather than left to read as a slip.
THE LEDGER. Two file arms and 22 case arms, `kind:verb` on each because the
lane needs stdin, spawns with its own arguments and performs writes — none of
which a tree-scoped module may do. `bot-issue` leaves `$MUTANT_GATES` and needs
no `#MUTANT-EXEMPT`: the census enumerates `mise-tasks/*.sh` and `policy/*.rego`
— `mutate::subjects`, since `main` retired `mutant-census` onto the engine under
this branch — so a deleted program is not in the census at all. The arm
carries a `runs:` field naming `batten claim bot` — ONE arm per deleted path,
which `V-RETIREMENT-AMBIGUOUS` requires, so the field names the only invocation
a GOVERNED caller loses; the lander's two workflow steps are ungoverned and
their repoints are free. Its two
declared mutations are re-homed as named cases —
`idempotence_a_second_call_on_the_same_pr_files_nothing` and
`a_key_named_but_not_closed_is_refused` — each carrying the mutation it stands
for in its comment.
THE LOAD-BEARING CASE IS REPLAYED, NOT ASSERTED. The dying suite ran the REAL
`ready-lint` over the REAL derived payload, because "the derived block is
checkable by the same gate that checks a human's" is the claim that makes a
mechanical row honest. `the_derived_block_passes_ready_lint` does the same over
this repository's committed template and its committed `[[pattern]]` grammar —
and it earned its place immediately, catching that `prettier` reshapes the
template's bullets on every `mise run fmt`.
A KNOWN LAG, STATED RATHER THAN HIDDEN. `auto-bot-land.yml` installs `batten`
from the latest RELEASE, so its two repointed steps fail loudly with "command
not found" between this merge and the next release. That is the same gap
CLOUD-1143 recorded for `checks-green`, and it fails the job rather than
passing silently.
126 suites after the deletion, 561.9s serial on this container, regenerated on
the rebased base rather than the one this change was written against — `main`
retired several more suites in between, `mutant`, `mutant-census` and
`session-start` among them, and a corpus derived from an older report publishes a
cost for a suite that is gone. The saving this change can claim is
`tests/bot-issue.bats`' own 2.9s share of the corpus it was measured in, 0.3%;
the totals differ by far more across runs, and that is those other retirements
plus machine noise rather than anything this branch did.
Refs: CLOUD-1295
Refs: CLOUD-1164
Refs: CLOUD-1213
Refs: CLOUD-1297
Refs: CLOUD-1299
Admits: 1c68cb4a5d20efa873c0ed2b7564b7cfdf53c0b6ebb9c79e211eea4b8445bcc5
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: batten.toml
Admits-head: a11e917
Admits-epoch: 650a76ba70a8b5645ffc58aedb1bdde72394e39026b5c1391a926934ea49702c
Admits-author: alec@wenzowski.com
Admits-prev: c7d8beede76f452a09d1a6c47a615b728f2876a60e055ef065975a27903f003f
Admits-answer-lost: mise-tasks/bot-issue.sh stays alive as a second implementation of predicates the engine now owns, which is the state policy/shell-retirement.rego's V-SHELL-RULE-EDITED exists to end -- the ported verbs cannot read a lane nobody declared, so refusing this write refuses the retirement rather than this line of it.
Admits-answer-precondition: The bot lane's consumer facts -- which forge repository, which bot logins, which manifests the lane owns, the marker strings, the tracker's key prefix and the branch prefix -- are exactly the values non-negotiable rule 1 forbids in crates/batten. Retiring mise-tasks/bot-issue.sh onto `batten pr derive|file|link|ensure|closes` and `batten claim bot` moves the MATCHER into the engine and leaves those facts here, so batten.toml is the owning surface for them and no other surface can carry them. The write is one additive [bot_lane] block, visible in this pull request's own diff, which is where reviewers read it.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because batten.toml IS the owning surface for a consumer's policy facts; there is no other surface that owns them. A local override cannot carry them either: adding a login to `bots` or a path to `owned_manifests` turns a refusal into a filed row, which is a weakening dressed as an addition and house style section 8's raise-only rule does not admit it. R-RESTORE-IT does not apply because the write is additive and deliberate rather than an accident to undo -- restoring the file would delete the table the ported verbs read.
Admits: ddd46be8d9ae384d97c38556832349bfc8dc319895761edb2255894b821b3763
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: .github/workflows/auto-bot-land.yml
Admits-head: a11e917
Admits-epoch: 8e6d5f2f19b383997365f6b72ff52816d72c7029208dbb81a40389acbd65e67f
Admits-author: alec@wenzowski.com
Admits-prev: -
Admits-answer-lost: The bot lane's lander breaks outright at its next tick: both steps call a task the retirement removed, so no bot pull request gets a row and none is landed. The alternative is keeping mise-tasks/bot-issue.sh alive as a second implementation, which is the state V-SHELL-RULE-EDITED exists to end.
Admits-answer-precondition: auto-bot-land.yml invokes `mise run bot-issue ensure` and `mise run bot-issue closes`, and this change deletes mise-tasks/bot-issue.sh. Leaving the calls would leave the lander invoking a task that no longer exists, so the workflow is the owning surface for its own steps and there is nowhere else the repoint could be made. The write is two invocation repoints -- `batten pr ensure` and `batten pr closes` -- plus the comment naming the retired program, and nothing about what the workflow decides changes.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because .github/workflows/auto-bot-land.yml IS the owning surface for the lander's own steps; a workflow's run lines cannot be declared anywhere else. R-RESTORE-IT does not apply because the change is a deliberate repoint at a successor this same commit lands, and restoring the file would leave a workflow calling a deleted program.
Admits: a3bb7b559f5118c0fe2cc4e4c8e2336e2bcbf1262a0df2578070e44446a20751
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: .github/workflows/sbom-actions-currency.yml
Admits-head: a11e917
Admits-epoch: 8e6d5f2f19b383997365f6b72ff52816d72c7029208dbb81a40389acbd65e67f
Admits-author: alec@wenzowski.com
Admits-prev: e03b883be1a24354b6eaeedb59a2c39f16052d832ef6dde2ceab292ca964fa5b
Admits-answer-lost: Every carry pull request `sbom-actions-currency` opens stalls on `closing-key-check` exactly as #806 did, and each one needs a human to patch its body before it can land -- which is the manual step CLOUD-1213 exists to remove, on the lane whose blockage this whole change is about.
Admits-answer-precondition: The generated pull request body carries `Refs: CLOUD-1213` and no closing marker, so `closing-key-check` reads it as a body naming a key non-closingly and stalls the landing. Measured on #806, where the body was patched by hand to get it through -- the defect repeating on every future carry pull request. The body is generated by this workflow's own `gh pr create` step, so the workflow is the owning surface for it and no other surface can set it. The write is one added line, `DO-NOT-CLOSE CLOUD-1213`, in the same printf list, visible in this pull request's diff.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because .github/workflows/sbom-actions-currency.yml IS the owning surface for the body it generates; a workflow's own `gh pr create` arguments cannot be declared anywhere else. R-RESTORE-IT does not apply because the change is a deliberate one-line addition rather than an accident to undo, and restoring the file would put back the body that stalls.
Admits: 1588b760e38a64a36f5aaef031bd5b3e8f5e1160c0f94eec486b41dbca5d335c
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: .serena/memories/core.md
Admits-head: a11e917
Admits-epoch: a5e521241d3b4c861cd0ffb0813ac2c5eb0bfd1b3b364e13418f67888e3de545
Admits-author: alec@wenzowski.com
Admits-prev: 121e5c62ce26f6caa46a19393e76ae86d200b8b113fccdaf2bb1196df372865b
Admits-answer-lost: `bot.rs` lands with no map row, `module-map-check` fails the gate, and the retirement cannot commit at all -- so refusing this write refuses the whole change rather than this bullet of it. A reader looking up what `bot.rs` owns would find nothing where every other module is described.
Admits-answer-precondition: `module-map-check` refuses a `crates/batten/src/*.rs` module with no row in `.serena/memories/core.md`, and this change adds `crates/batten/src/bot.rs`. The memory IS the module map's authority -- AGENTS.md points there for the per-module layout precisely so the tree is not restated in a budgeted file -- so there is no other surface a row could go on. The write was made through Serena's `edit_memory`, which is the sanctioned route, and it is one added bullet visible in this pull request's diff.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because `.serena/memories/core.md` IS the owning surface for the module map; AGENTS.md defers to it by name and `module-map-check` reads it as the authority. R-RESTORE-IT does not apply because the write is one additive bullet describing a module this same commit introduces -- restoring the file would leave the map missing a row the gate demands.
Admits: cbb7e7b9a66ad1ec32b1d2e5f2df9e67efc920620236ceb72e667aa6e772fdc7
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: policy/spawn-adapters.rego
Admits-head: 6da96ce
Admits-epoch: d8992f36146d4a8f3bd0250644ab0624b29c8cc7abd4b5640b4c4aa2c6c5efe6
Admits-author: alec@wenzowski.com
Admits-prev: -
Admits-answer-lost: The rebase cannot complete, so the retirement cannot land at all. Taking `main`'s side alone leaves `bot` unplaced and `spawn-adapters` refuses `crates/batten/src/bot.rs`; taking this branch's side alone deletes `mutate` and reintroduces the refusal on the module `main` just landed.
Admits-answer-precondition: This is a REBASE CONFLICT RESOLUTION, and both sides of it are additions to the same set literal. `main` placed `mutate` when it retired `mise-tasks/mutant.sh`; this branch places `bot` when it retires `mise-tasks/bot-issue.sh`. Neither adapter can be dropped -- `spawn-adapters` refuses a spawn in a module the table has not placed, so losing either side reintroduces the refusal the other change already answered. The resolution is the union of the two rows plus both reasons, which is what the file would hold had the two landed in sequence rather than concurrently. There is no other surface: the adapter table IS this module.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because `policy/spawn-adapters.rego` IS the owning surface for the adapter placement table; the set literal cannot be declared anywhere else. R-RESTORE-IT does not apply because restoring the file to either side is precisely what loses one of the two placements -- there is no version of this file that carries both without this write.
… claim `sbom-actions-currency` opens a PR that `verify` refuses: there is no claim receipt, and the agent one attests a refinement no bot performed. #806 landed with a `--takeover` claim against CLOUD-1213, which was a fake claim, and every future carry PR meets the same wall. `batten claim carry` is the honest receipt. It attests DERIVABILITY rather than a branch name — a prefix exemption would be a password wearing a branch name — so it mints only when the branch appends rows whose repo the base table already maps, changing the sha alone, and touches nothing else. The verb takes no argument on purpose: the subject is the branch's own diff against its merge base, and a caller that could name its own subject could name one that is derivable while changing something else. Also repairs the `snapshots` task, stale since the integration targets moved under `tests/it/`. Refs: CLOUD-1295
…eceipt
486 lines of shell and 323 of bats, six subcommands, all of it GitHub API plus
one local receipt write. `mise-tasks/bot-issue.sh` and `tests/bot-issue.bats`
are deleted; the predicates land as `crates/batten/src/bot.rs` and six new
leaves — `batten pr derive|file|link|ensure|closes` and `batten claim bot`.
TWO HALVES, KEPT APART INSIDE ONE MODULE. The predicates — is this pull request
one of the lane's, which manifests it touched, what Conventional type its
subject declares, whether a body still CLOSES a key rather than merely naming
one — are pure functions with their own unit tier. The `forge` half underneath
is the only thing that talks to anybody.
THE CONSUMER FACTS ARE `[bot_lane]` IN batten.toml, not literals in the crate.
Which repository, which bot logins, which manifests the lane owns, the marker
strings, the tracker's key prefix and the branch prefix — every value the
retired program spelled internally is a config row now, so a grep of
`crates/batten` for a bot's name or a manifest path returns nothing
(non-negotiable rule 1). `document_facts` caught the first draft's doc comment
naming a workflow glob as an example, and that is the gate working. The derived
row's body is a tracked template with `{{...}}` placeholders rather than a page
of markdown inside a TOML value; an unfilled placeholder refuses at
substitution rather than rendering a template artifact into a tracker row.
THE FORGE'S OWN CLIENT, NOT THIS CRATE'S HTTP TRANSPORT. `pr_watch` is the
landed precedent (CLOUD-1143) and `gh` resolves the credential outside the
crate, where `fetch` would have put token resolution inside it next to no
config row that declares one. `policy/spawn-adapters.rego` places `bot` on
`pr_watch`'s own argument. The commit that widened `fetch::Call` for a PATCH
this port turned out not to need is reverted earlier on this branch.
THE EXIT TABLE CHANGED AND THE WORKFLOW FOLLOWS IT. `bot-issue` used 1 for
"refused, not this lane's" and 2 for "could not look"; house style §7 makes a
refusal 2 and a could-not-look 3. `auto-bot-land.yml`'s two steps are repointed
and their `case` arms moved with them — reading 1 as an ordinary refusal there
would have treated a usage error as a pass.
THE THIRD RECEIPT KIND IS WIRED. `verify` now tries `claim`, then `bot`, then
`carry`, so a licence-carry branch no longer needs the fake `--takeover` claim
`DO-NOT-CLOSE CLOUD-1213`, without which every carry pull request stalls on
`closing-key-check` exactly as #806 did and needs a human to patch it.
WHAT IS NOT DONE, AND WHY IT IS FILED RATHER THAN FIXED. The plan called for a
`checks = ["carry"]` row beside `claim-needs-receipt`. That row is wrong: a
receipt rule's `checks` is a CONJUNCTION, so a second row is a second AND and
would deny every ordinary write on every branch. `verify`'s shell body can
express the disjunction and the mediated gate cannot — a live false positive
for the bot receipt since CLOUD-693, which this change's third kind inherits.
CLOUD-1297 owns it, with the disjunction as its §2.
TWO GATES CAUGHT REAL DEFECTS IN THIS CHANGE AND BOTH ARE FIXED HERE.
`no-appeal-to-authority` refused `carry.rs` and its tier for naming a real
action repository as fixture data — a third party's name in the crate, which is
the class the rule exists for, and the fixtures now use neutral names.
`module-layering` refused both new modules as unplaced, which is the coverage
clause working: `bot` and `carry` are placed with their reasons.
ONE EDIT IS DECLINED RATHER THAN FORCED, and CLOUD-1299 owns it. Retiring the
program makes `verify`'s no-receipt remedy name a task that no longer exists —
but `tests/verify.bats` asserts that message contains `bot-issue receipt`, and a
governed suite has two landable shapes, neither of which is an edit.
`repoints_at_the_declared_invocation` cannot admit it either: it requires the
replaced span to be a PATH reference, and a task name plus a subcommand is not
one. So the remedy names the live verb FIRST and keeps the old name as the thing
it replaced — true, useful mid-transition, and satisfying the pin — with the
reason at the site rather than left to read as a slip.
THE LEDGER. Two file arms and 22 case arms, `kind:verb` on each because the
lane needs stdin, spawns with its own arguments and performs writes — none of
which a tree-scoped module may do. `bot-issue` leaves `$MUTANT_GATES` and needs
no `#MUTANT-EXEMPT`: the census enumerates `mise-tasks/*.sh` and `policy/*.rego`
— `mutate::subjects`, since `main` retired `mutant-census` onto the engine under
this branch — so a deleted program is not in the census at all. The arm
carries a `runs:` field naming `batten claim bot` — ONE arm per deleted path,
which `V-RETIREMENT-AMBIGUOUS` requires, so the field names the only invocation
a GOVERNED caller loses; the lander's two workflow steps are ungoverned and
their repoints are free. Its two
declared mutations are re-homed as named cases —
`idempotence_a_second_call_on_the_same_pr_files_nothing` and
`a_key_named_but_not_closed_is_refused` — each carrying the mutation it stands
for in its comment.
THE LOAD-BEARING CASE IS REPLAYED, NOT ASSERTED. The dying suite ran the REAL
`ready-lint` over the REAL derived payload, because "the derived block is
checkable by the same gate that checks a human's" is the claim that makes a
mechanical row honest. `the_derived_block_passes_ready_lint` does the same over
this repository's committed template and its committed `[[pattern]]` grammar —
and it earned its place immediately, catching that `prettier` reshapes the
template's bullets on every `mise run fmt`.
A KNOWN LAG, STATED RATHER THAN HIDDEN. `auto-bot-land.yml` installs `batten`
from the latest RELEASE, so its two repointed steps fail loudly with "command
not found" between this merge and the next release. That is the same gap
CLOUD-1143 recorded for `checks-green`, and it fails the job rather than
passing silently.
126 suites after the deletion, 561.9s serial on this container, regenerated on
the rebased base rather than the one this change was written against — `main`
retired several more suites in between, `mutant`, `mutant-census` and
`session-start` among them, and a corpus derived from an older report publishes a
cost for a suite that is gone. The saving this change can claim is
`tests/bot-issue.bats`' own 2.9s share of the corpus it was measured in, 0.3%;
the totals differ by far more across runs, and that is those other retirements
plus machine noise rather than anything this branch did.
Refs: CLOUD-1295
Refs: CLOUD-1164
Refs: CLOUD-1213
Refs: CLOUD-1297
Refs: CLOUD-1299
Admits: 1c68cb4a5d20efa873c0ed2b7564b7cfdf53c0b6ebb9c79e211eea4b8445bcc5
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: batten.toml
Admits-head: a11e917
Admits-epoch: 650a76ba70a8b5645ffc58aedb1bdde72394e39026b5c1391a926934ea49702c
Admits-author: alec@wenzowski.com
Admits-prev: c7d8beede76f452a09d1a6c47a615b728f2876a60e055ef065975a27903f003f
Admits-answer-lost: mise-tasks/bot-issue.sh stays alive as a second implementation of predicates the engine now owns, which is the state policy/shell-retirement.rego's V-SHELL-RULE-EDITED exists to end -- the ported verbs cannot read a lane nobody declared, so refusing this write refuses the retirement rather than this line of it.
Admits-answer-precondition: The bot lane's consumer facts -- which forge repository, which bot logins, which manifests the lane owns, the marker strings, the tracker's key prefix and the branch prefix -- are exactly the values non-negotiable rule 1 forbids in crates/batten. Retiring mise-tasks/bot-issue.sh onto `batten pr derive|file|link|ensure|closes` and `batten claim bot` moves the MATCHER into the engine and leaves those facts here, so batten.toml is the owning surface for them and no other surface can carry them. The write is one additive [bot_lane] block, visible in this pull request's own diff, which is where reviewers read it.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because batten.toml IS the owning surface for a consumer's policy facts; there is no other surface that owns them. A local override cannot carry them either: adding a login to `bots` or a path to `owned_manifests` turns a refusal into a filed row, which is a weakening dressed as an addition and house style section 8's raise-only rule does not admit it. R-RESTORE-IT does not apply because the write is additive and deliberate rather than an accident to undo -- restoring the file would delete the table the ported verbs read.
Admits: ddd46be8d9ae384d97c38556832349bfc8dc319895761edb2255894b821b3763
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: .github/workflows/auto-bot-land.yml
Admits-head: a11e917
Admits-epoch: 8e6d5f2f19b383997365f6b72ff52816d72c7029208dbb81a40389acbd65e67f
Admits-author: alec@wenzowski.com
Admits-prev: -
Admits-answer-lost: The bot lane's lander breaks outright at its next tick: both steps call a task the retirement removed, so no bot pull request gets a row and none is landed. The alternative is keeping mise-tasks/bot-issue.sh alive as a second implementation, which is the state V-SHELL-RULE-EDITED exists to end.
Admits-answer-precondition: auto-bot-land.yml invokes `mise run bot-issue ensure` and `mise run bot-issue closes`, and this change deletes mise-tasks/bot-issue.sh. Leaving the calls would leave the lander invoking a task that no longer exists, so the workflow is the owning surface for its own steps and there is nowhere else the repoint could be made. The write is two invocation repoints -- `batten pr ensure` and `batten pr closes` -- plus the comment naming the retired program, and nothing about what the workflow decides changes.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because .github/workflows/auto-bot-land.yml IS the owning surface for the lander's own steps; a workflow's run lines cannot be declared anywhere else. R-RESTORE-IT does not apply because the change is a deliberate repoint at a successor this same commit lands, and restoring the file would leave a workflow calling a deleted program.
Admits: a3bb7b559f5118c0fe2cc4e4c8e2336e2bcbf1262a0df2578070e44446a20751
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: .github/workflows/sbom-actions-currency.yml
Admits-head: a11e917
Admits-epoch: 8e6d5f2f19b383997365f6b72ff52816d72c7029208dbb81a40389acbd65e67f
Admits-author: alec@wenzowski.com
Admits-prev: e03b883be1a24354b6eaeedb59a2c39f16052d832ef6dde2ceab292ca964fa5b
Admits-answer-lost: Every carry pull request `sbom-actions-currency` opens stalls on `closing-key-check` exactly as #806 did, and each one needs a human to patch its body before it can land -- which is the manual step CLOUD-1213 exists to remove, on the lane whose blockage this whole change is about.
Admits-answer-precondition: The generated pull request body carries `Refs: CLOUD-1213` and no closing marker, so `closing-key-check` reads it as a body naming a key non-closingly and stalls the landing. Measured on #806, where the body was patched by hand to get it through -- the defect repeating on every future carry pull request. The body is generated by this workflow's own `gh pr create` step, so the workflow is the owning surface for it and no other surface can set it. The write is one added line, `DO-NOT-CLOSE CLOUD-1213`, in the same printf list, visible in this pull request's diff.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because .github/workflows/sbom-actions-currency.yml IS the owning surface for the body it generates; a workflow's own `gh pr create` arguments cannot be declared anywhere else. R-RESTORE-IT does not apply because the change is a deliberate one-line addition rather than an accident to undo, and restoring the file would put back the body that stalls.
Admits: 1588b760e38a64a36f5aaef031bd5b3e8f5e1160c0f94eec486b41dbca5d335c
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: .serena/memories/core.md
Admits-head: a11e917
Admits-epoch: a5e521241d3b4c861cd0ffb0813ac2c5eb0bfd1b3b364e13418f67888e3de545
Admits-author: alec@wenzowski.com
Admits-prev: 121e5c62ce26f6caa46a19393e76ae86d200b8b113fccdaf2bb1196df372865b
Admits-answer-lost: `bot.rs` lands with no map row, `module-map-check` fails the gate, and the retirement cannot commit at all -- so refusing this write refuses the whole change rather than this bullet of it. A reader looking up what `bot.rs` owns would find nothing where every other module is described.
Admits-answer-precondition: `module-map-check` refuses a `crates/batten/src/*.rs` module with no row in `.serena/memories/core.md`, and this change adds `crates/batten/src/bot.rs`. The memory IS the module map's authority -- AGENTS.md points there for the per-module layout precisely so the tree is not restated in a budgeted file -- so there is no other surface a row could go on. The write was made through Serena's `edit_memory`, which is the sanctioned route, and it is one added bullet visible in this pull request's diff.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because `.serena/memories/core.md` IS the owning surface for the module map; AGENTS.md defers to it by name and `module-map-check` reads it as the authority. R-RESTORE-IT does not apply because the write is one additive bullet describing a module this same commit introduces -- restoring the file would leave the map missing a row the gate demands.
Admits: cbb7e7b9a66ad1ec32b1d2e5f2df9e67efc920620236ceb72e667aa6e772fdc7
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: policy/spawn-adapters.rego
Admits-head: 6da96ce
Admits-epoch: d8992f36146d4a8f3bd0250644ab0624b29c8cc7abd4b5640b4c4aa2c6c5efe6
Admits-author: alec@wenzowski.com
Admits-prev: -
Admits-answer-lost: The rebase cannot complete, so the retirement cannot land at all. Taking `main`'s side alone leaves `bot` unplaced and `spawn-adapters` refuses `crates/batten/src/bot.rs`; taking this branch's side alone deletes `mutate` and reintroduces the refusal on the module `main` just landed.
Admits-answer-precondition: This is a REBASE CONFLICT RESOLUTION, and both sides of it are additions to the same set literal. `main` placed `mutate` when it retired `mise-tasks/mutant.sh`; this branch places `bot` when it retires `mise-tasks/bot-issue.sh`. Neither adapter can be dropped -- `spawn-adapters` refuses a spawn in a module the table has not placed, so losing either side reintroduces the refusal the other change already answered. The resolution is the union of the two rows plus both reasons, which is what the file would hold had the two landed in sequence rather than concurrently. There is no other surface: the adapter table IS this module.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because `policy/spawn-adapters.rego` IS the owning surface for the adapter placement table; the set literal cannot be declared anywhere else. R-RESTORE-IT does not apply because restoring the file to either side is precisely what loses one of the two placements -- there is no version of this file that carries both without this write.
… claim `sbom-actions-currency` opens a PR that `verify` refuses: there is no claim receipt, and the agent one attests a refinement no bot performed. #806 landed with a `--takeover` claim against CLOUD-1213, which was a fake claim, and every future carry PR meets the same wall. `batten claim carry` is the honest receipt. It attests DERIVABILITY rather than a branch name — a prefix exemption would be a password wearing a branch name — so it mints only when the branch appends rows whose repo the base table already maps, changing the sha alone, and touches nothing else. The verb takes no argument on purpose: the subject is the branch's own diff against its merge base, and a caller that could name its own subject could name one that is derivable while changing something else. Also repairs the `snapshots` task, stale since the integration targets moved under `tests/it/`. Refs: CLOUD-1295
…eceipt
486 lines of shell and 323 of bats, six subcommands, all of it GitHub API plus
one local receipt write. `mise-tasks/bot-issue.sh` and `tests/bot-issue.bats`
are deleted; the predicates land as `crates/batten/src/bot.rs` and six new
leaves — `batten pr derive|file|link|ensure|closes` and `batten claim bot`.
TWO HALVES, KEPT APART INSIDE ONE MODULE. The predicates — is this pull request
one of the lane's, which manifests it touched, what Conventional type its
subject declares, whether a body still CLOSES a key rather than merely naming
one — are pure functions with their own unit tier. The `forge` half underneath
is the only thing that talks to anybody.
THE CONSUMER FACTS ARE `[bot_lane]` IN batten.toml, not literals in the crate.
Which repository, which bot logins, which manifests the lane owns, the marker
strings, the tracker's key prefix and the branch prefix — every value the
retired program spelled internally is a config row now, so a grep of
`crates/batten` for a bot's name or a manifest path returns nothing
(non-negotiable rule 1). `document_facts` caught the first draft's doc comment
naming a workflow glob as an example, and that is the gate working. The derived
row's body is a tracked template with `{{...}}` placeholders rather than a page
of markdown inside a TOML value; an unfilled placeholder refuses at
substitution rather than rendering a template artifact into a tracker row.
THE FORGE'S OWN CLIENT, NOT THIS CRATE'S HTTP TRANSPORT. `pr_watch` is the
landed precedent (CLOUD-1143) and `gh` resolves the credential outside the
crate, where `fetch` would have put token resolution inside it next to no
config row that declares one. `policy/spawn-adapters.rego` places `bot` on
`pr_watch`'s own argument. The commit that widened `fetch::Call` for a PATCH
this port turned out not to need is reverted earlier on this branch.
THE EXIT TABLE CHANGED AND THE WORKFLOW FOLLOWS IT. `bot-issue` used 1 for
"refused, not this lane's" and 2 for "could not look"; house style §7 makes a
refusal 2 and a could-not-look 3. `auto-bot-land.yml`'s two steps are repointed
and their `case` arms moved with them — reading 1 as an ordinary refusal there
would have treated a usage error as a pass.
THE THIRD RECEIPT KIND IS WIRED. `verify` now tries `claim`, then `bot`, then
`carry`, so a licence-carry branch no longer needs the fake `--takeover` claim
`DO-NOT-CLOSE CLOUD-1213`, without which every carry pull request stalls on
`closing-key-check` exactly as #806 did and needs a human to patch it.
WHAT IS NOT DONE, AND WHY IT IS FILED RATHER THAN FIXED. The plan called for a
`checks = ["carry"]` row beside `claim-needs-receipt`. That row is wrong: a
receipt rule's `checks` is a CONJUNCTION, so a second row is a second AND and
would deny every ordinary write on every branch. `verify`'s shell body can
express the disjunction and the mediated gate cannot — a live false positive
for the bot receipt since CLOUD-693, which this change's third kind inherits.
CLOUD-1297 owns it, with the disjunction as its §2.
TWO GATES CAUGHT REAL DEFECTS IN THIS CHANGE AND BOTH ARE FIXED HERE.
`no-appeal-to-authority` refused `carry.rs` and its tier for naming a real
action repository as fixture data — a third party's name in the crate, which is
the class the rule exists for, and the fixtures now use neutral names.
`module-layering` refused both new modules as unplaced, which is the coverage
clause working: `bot` and `carry` are placed with their reasons.
ONE EDIT IS DECLINED RATHER THAN FORCED, and CLOUD-1299 owns it. Retiring the
program makes `verify`'s no-receipt remedy name a task that no longer exists —
but `tests/verify.bats` asserts that message contains `bot-issue receipt`, and a
governed suite has two landable shapes, neither of which is an edit.
`repoints_at_the_declared_invocation` cannot admit it either: it requires the
replaced span to be a PATH reference, and a task name plus a subcommand is not
one. So the remedy names the live verb FIRST and keeps the old name as the thing
it replaced — true, useful mid-transition, and satisfying the pin — with the
reason at the site rather than left to read as a slip.
THE LEDGER. Two file arms and 22 case arms, `kind:verb` on each because the
lane needs stdin, spawns with its own arguments and performs writes — none of
which a tree-scoped module may do. `bot-issue` leaves `$MUTANT_GATES` and needs
no `#MUTANT-EXEMPT`: the census enumerates `mise-tasks/*.sh` and `policy/*.rego`
— `mutate::subjects`, since `main` retired `mutant-census` onto the engine under
this branch — so a deleted program is not in the census at all. The arm
carries a `runs:` field naming `batten claim bot` — ONE arm per deleted path,
which `V-RETIREMENT-AMBIGUOUS` requires, so the field names the only invocation
a GOVERNED caller loses; the lander's two workflow steps are ungoverned and
their repoints are free. Its two
declared mutations are re-homed as named cases —
`idempotence_a_second_call_on_the_same_pr_files_nothing` and
`a_key_named_but_not_closed_is_refused` — each carrying the mutation it stands
for in its comment.
THE LOAD-BEARING CASE IS REPLAYED, NOT ASSERTED. The dying suite ran the REAL
`ready-lint` over the REAL derived payload, because "the derived block is
checkable by the same gate that checks a human's" is the claim that makes a
mechanical row honest. `the_derived_block_passes_ready_lint` does the same over
this repository's committed template and its committed `[[pattern]]` grammar —
and it earned its place immediately, catching that `prettier` reshapes the
template's bullets on every `mise run fmt`.
A KNOWN LAG, STATED RATHER THAN HIDDEN. `auto-bot-land.yml` installs `batten`
from the latest RELEASE, so its two repointed steps fail loudly with "command
not found" between this merge and the next release. That is the same gap
CLOUD-1143 recorded for `checks-green`, and it fails the job rather than
passing silently.
126 suites after the deletion, 561.9s serial on this container, regenerated on
the rebased base rather than the one this change was written against — `main`
retired several more suites in between, `mutant`, `mutant-census` and
`session-start` among them, and a corpus derived from an older report publishes a
cost for a suite that is gone. The saving this change can claim is
`tests/bot-issue.bats`' own 2.9s share of the corpus it was measured in, 0.3%;
the totals differ by far more across runs, and that is those other retirements
plus machine noise rather than anything this branch did.
Refs: CLOUD-1295
Refs: CLOUD-1164
Refs: CLOUD-1213
Refs: CLOUD-1297
Refs: CLOUD-1299
Admits: 1c68cb4a5d20efa873c0ed2b7564b7cfdf53c0b6ebb9c79e211eea4b8445bcc5
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: batten.toml
Admits-head: a11e917
Admits-epoch: 650a76ba70a8b5645ffc58aedb1bdde72394e39026b5c1391a926934ea49702c
Admits-author: alec@wenzowski.com
Admits-prev: c7d8beede76f452a09d1a6c47a615b728f2876a60e055ef065975a27903f003f
Admits-answer-lost: mise-tasks/bot-issue.sh stays alive as a second implementation of predicates the engine now owns, which is the state policy/shell-retirement.rego's V-SHELL-RULE-EDITED exists to end -- the ported verbs cannot read a lane nobody declared, so refusing this write refuses the retirement rather than this line of it.
Admits-answer-precondition: The bot lane's consumer facts -- which forge repository, which bot logins, which manifests the lane owns, the marker strings, the tracker's key prefix and the branch prefix -- are exactly the values non-negotiable rule 1 forbids in crates/batten. Retiring mise-tasks/bot-issue.sh onto `batten pr derive|file|link|ensure|closes` and `batten claim bot` moves the MATCHER into the engine and leaves those facts here, so batten.toml is the owning surface for them and no other surface can carry them. The write is one additive [bot_lane] block, visible in this pull request's own diff, which is where reviewers read it.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because batten.toml IS the owning surface for a consumer's policy facts; there is no other surface that owns them. A local override cannot carry them either: adding a login to `bots` or a path to `owned_manifests` turns a refusal into a filed row, which is a weakening dressed as an addition and house style section 8's raise-only rule does not admit it. R-RESTORE-IT does not apply because the write is additive and deliberate rather than an accident to undo -- restoring the file would delete the table the ported verbs read.
Admits: ddd46be8d9ae384d97c38556832349bfc8dc319895761edb2255894b821b3763
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: .github/workflows/auto-bot-land.yml
Admits-head: a11e917
Admits-epoch: 8e6d5f2f19b383997365f6b72ff52816d72c7029208dbb81a40389acbd65e67f
Admits-author: alec@wenzowski.com
Admits-prev: -
Admits-answer-lost: The bot lane's lander breaks outright at its next tick: both steps call a task the retirement removed, so no bot pull request gets a row and none is landed. The alternative is keeping mise-tasks/bot-issue.sh alive as a second implementation, which is the state V-SHELL-RULE-EDITED exists to end.
Admits-answer-precondition: auto-bot-land.yml invokes `mise run bot-issue ensure` and `mise run bot-issue closes`, and this change deletes mise-tasks/bot-issue.sh. Leaving the calls would leave the lander invoking a task that no longer exists, so the workflow is the owning surface for its own steps and there is nowhere else the repoint could be made. The write is two invocation repoints -- `batten pr ensure` and `batten pr closes` -- plus the comment naming the retired program, and nothing about what the workflow decides changes.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because .github/workflows/auto-bot-land.yml IS the owning surface for the lander's own steps; a workflow's run lines cannot be declared anywhere else. R-RESTORE-IT does not apply because the change is a deliberate repoint at a successor this same commit lands, and restoring the file would leave a workflow calling a deleted program.
Admits: a3bb7b559f5118c0fe2cc4e4c8e2336e2bcbf1262a0df2578070e44446a20751
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: .github/workflows/sbom-actions-currency.yml
Admits-head: a11e917
Admits-epoch: 8e6d5f2f19b383997365f6b72ff52816d72c7029208dbb81a40389acbd65e67f
Admits-author: alec@wenzowski.com
Admits-prev: e03b883be1a24354b6eaeedb59a2c39f16052d832ef6dde2ceab292ca964fa5b
Admits-answer-lost: Every carry pull request `sbom-actions-currency` opens stalls on `closing-key-check` exactly as #806 did, and each one needs a human to patch its body before it can land -- which is the manual step CLOUD-1213 exists to remove, on the lane whose blockage this whole change is about.
Admits-answer-precondition: The generated pull request body carries `Refs: CLOUD-1213` and no closing marker, so `closing-key-check` reads it as a body naming a key non-closingly and stalls the landing. Measured on #806, where the body was patched by hand to get it through -- the defect repeating on every future carry pull request. The body is generated by this workflow's own `gh pr create` step, so the workflow is the owning surface for it and no other surface can set it. The write is one added line, `DO-NOT-CLOSE CLOUD-1213`, in the same printf list, visible in this pull request's diff.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because .github/workflows/sbom-actions-currency.yml IS the owning surface for the body it generates; a workflow's own `gh pr create` arguments cannot be declared anywhere else. R-RESTORE-IT does not apply because the change is a deliberate one-line addition rather than an accident to undo, and restoring the file would put back the body that stalls.
Admits: 1588b760e38a64a36f5aaef031bd5b3e8f5e1160c0f94eec486b41dbca5d335c
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: .serena/memories/core.md
Admits-head: a11e917
Admits-epoch: a5e521241d3b4c861cd0ffb0813ac2c5eb0bfd1b3b364e13418f67888e3de545
Admits-author: alec@wenzowski.com
Admits-prev: 121e5c62ce26f6caa46a19393e76ae86d200b8b113fccdaf2bb1196df372865b
Admits-answer-lost: `bot.rs` lands with no map row, `module-map-check` fails the gate, and the retirement cannot commit at all -- so refusing this write refuses the whole change rather than this bullet of it. A reader looking up what `bot.rs` owns would find nothing where every other module is described.
Admits-answer-precondition: `module-map-check` refuses a `crates/batten/src/*.rs` module with no row in `.serena/memories/core.md`, and this change adds `crates/batten/src/bot.rs`. The memory IS the module map's authority -- AGENTS.md points there for the per-module layout precisely so the tree is not restated in a budgeted file -- so there is no other surface a row could go on. The write was made through Serena's `edit_memory`, which is the sanctioned route, and it is one added bullet visible in this pull request's diff.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because `.serena/memories/core.md` IS the owning surface for the module map; AGENTS.md defers to it by name and `module-map-check` reads it as the authority. R-RESTORE-IT does not apply because the write is one additive bullet describing a module this same commit introduces -- restoring the file would leave the map missing a row the gate demands.
Admits: cbb7e7b9a66ad1ec32b1d2e5f2df9e67efc920620236ceb72e667aa6e772fdc7
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: policy/spawn-adapters.rego
Admits-head: 6da96ce
Admits-epoch: d8992f36146d4a8f3bd0250644ab0624b29c8cc7abd4b5640b4c4aa2c6c5efe6
Admits-author: alec@wenzowski.com
Admits-prev: -
Admits-answer-lost: The rebase cannot complete, so the retirement cannot land at all. Taking `main`'s side alone leaves `bot` unplaced and `spawn-adapters` refuses `crates/batten/src/bot.rs`; taking this branch's side alone deletes `mutate` and reintroduces the refusal on the module `main` just landed.
Admits-answer-precondition: This is a REBASE CONFLICT RESOLUTION, and both sides of it are additions to the same set literal. `main` placed `mutate` when it retired `mise-tasks/mutant.sh`; this branch places `bot` when it retires `mise-tasks/bot-issue.sh`. Neither adapter can be dropped -- `spawn-adapters` refuses a spawn in a module the table has not placed, so losing either side reintroduces the refusal the other change already answered. The resolution is the union of the two rows plus both reasons, which is what the file would hold had the two landed in sequence rather than concurrently. There is no other surface: the adapter table IS this module.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because `policy/spawn-adapters.rego` IS the owning surface for the adapter placement table; the set literal cannot be declared anywhere else. R-RESTORE-IT does not apply because restoring the file to either side is precisely what loses one of the two placements -- there is no version of this file that carries both without this write.
… claim `sbom-actions-currency` opens a PR that `verify` refuses: there is no claim receipt, and the agent one attests a refinement no bot performed. #806 landed with a `--takeover` claim against CLOUD-1213, which was a fake claim, and every future carry PR meets the same wall. `batten claim carry` is the honest receipt. It attests DERIVABILITY rather than a branch name — a prefix exemption would be a password wearing a branch name — so it mints only when the branch appends rows whose repo the base table already maps, changing the sha alone, and touches nothing else. The verb takes no argument on purpose: the subject is the branch's own diff against its merge base, and a caller that could name its own subject could name one that is derivable while changing something else. Also repairs the `snapshots` task, stale since the integration targets moved under `tests/it/`. Refs: CLOUD-1295
…eceipt
486 lines of shell and 323 of bats, six subcommands, all of it GitHub API plus
one local receipt write. `mise-tasks/bot-issue.sh` and `tests/bot-issue.bats`
are deleted; the predicates land as `crates/batten/src/bot.rs` and six new
leaves — `batten pr derive|file|link|ensure|closes` and `batten claim bot`.
TWO HALVES, KEPT APART INSIDE ONE MODULE. The predicates — is this pull request
one of the lane's, which manifests it touched, what Conventional type its
subject declares, whether a body still CLOSES a key rather than merely naming
one — are pure functions with their own unit tier. The `forge` half underneath
is the only thing that talks to anybody.
THE CONSUMER FACTS ARE `[bot_lane]` IN batten.toml, not literals in the crate.
Which repository, which bot logins, which manifests the lane owns, the marker
strings, the tracker's key prefix and the branch prefix — every value the
retired program spelled internally is a config row now, so a grep of
`crates/batten` for a bot's name or a manifest path returns nothing
(non-negotiable rule 1). `document_facts` caught the first draft's doc comment
naming a workflow glob as an example, and that is the gate working. The derived
row's body is a tracked template with `{{...}}` placeholders rather than a page
of markdown inside a TOML value; an unfilled placeholder refuses at
substitution rather than rendering a template artifact into a tracker row.
THE FORGE'S OWN CLIENT, NOT THIS CRATE'S HTTP TRANSPORT. `pr_watch` is the
landed precedent (CLOUD-1143) and `gh` resolves the credential outside the
crate, where `fetch` would have put token resolution inside it next to no
config row that declares one. `policy/spawn-adapters.rego` places `bot` on
`pr_watch`'s own argument. The commit that widened `fetch::Call` for a PATCH
this port turned out not to need is reverted earlier on this branch.
THE EXIT TABLE CHANGED AND THE WORKFLOW FOLLOWS IT. `bot-issue` used 1 for
"refused, not this lane's" and 2 for "could not look"; house style §7 makes a
refusal 2 and a could-not-look 3. `auto-bot-land.yml`'s two steps are repointed
and their `case` arms moved with them — reading 1 as an ordinary refusal there
would have treated a usage error as a pass.
THE THIRD RECEIPT KIND IS WIRED. `verify` now tries `claim`, then `bot`, then
`carry`, so a licence-carry branch no longer needs the fake `--takeover` claim
`DO-NOT-CLOSE CLOUD-1213`, without which every carry pull request stalls on
`closing-key-check` exactly as #806 did and needs a human to patch it.
WHAT IS NOT DONE, AND WHY IT IS FILED RATHER THAN FIXED. The plan called for a
`checks = ["carry"]` row beside `claim-needs-receipt`. That row is wrong: a
receipt rule's `checks` is a CONJUNCTION, so a second row is a second AND and
would deny every ordinary write on every branch. `verify`'s shell body can
express the disjunction and the mediated gate cannot — a live false positive
for the bot receipt since CLOUD-693, which this change's third kind inherits.
CLOUD-1297 owns it, with the disjunction as its §2.
TWO GATES CAUGHT REAL DEFECTS IN THIS CHANGE AND BOTH ARE FIXED HERE.
`no-appeal-to-authority` refused `carry.rs` and its tier for naming a real
action repository as fixture data — a third party's name in the crate, which is
the class the rule exists for, and the fixtures now use neutral names.
`module-layering` refused both new modules as unplaced, which is the coverage
clause working: `bot` and `carry` are placed with their reasons.
ONE EDIT IS DECLINED RATHER THAN FORCED, and CLOUD-1299 owns it. Retiring the
program makes `verify`'s no-receipt remedy name a task that no longer exists —
but `tests/verify.bats` asserts that message contains `bot-issue receipt`, and a
governed suite has two landable shapes, neither of which is an edit.
`repoints_at_the_declared_invocation` cannot admit it either: it requires the
replaced span to be a PATH reference, and a task name plus a subcommand is not
one. So the remedy names the live verb FIRST and keeps the old name as the thing
it replaced — true, useful mid-transition, and satisfying the pin — with the
reason at the site rather than left to read as a slip.
THE LEDGER. Two file arms and 22 case arms, `kind:verb` on each because the
lane needs stdin, spawns with its own arguments and performs writes — none of
which a tree-scoped module may do. `bot-issue` leaves `$MUTANT_GATES` and needs
no `#MUTANT-EXEMPT`: the census enumerates `mise-tasks/*.sh` and `policy/*.rego`
— `mutate::subjects`, since `main` retired `mutant-census` onto the engine under
this branch — so a deleted program is not in the census at all. The arm
carries a `runs:` field naming `batten claim bot` — ONE arm per deleted path,
which `V-RETIREMENT-AMBIGUOUS` requires, so the field names the only invocation
a GOVERNED caller loses; the lander's two workflow steps are ungoverned and
their repoints are free. Its two
declared mutations are re-homed as named cases —
`idempotence_a_second_call_on_the_same_pr_files_nothing` and
`a_key_named_but_not_closed_is_refused` — each carrying the mutation it stands
for in its comment.
THE LOAD-BEARING CASE IS REPLAYED, NOT ASSERTED. The dying suite ran the REAL
`ready-lint` over the REAL derived payload, because "the derived block is
checkable by the same gate that checks a human's" is the claim that makes a
mechanical row honest. `the_derived_block_passes_ready_lint` does the same over
this repository's committed template and its committed `[[pattern]]` grammar —
and it earned its place immediately, catching that `prettier` reshapes the
template's bullets on every `mise run fmt`.
A KNOWN LAG, STATED RATHER THAN HIDDEN. `auto-bot-land.yml` installs `batten`
from the latest RELEASE, so its two repointed steps fail loudly with "command
not found" between this merge and the next release. That is the same gap
CLOUD-1143 recorded for `checks-green`, and it fails the job rather than
passing silently.
126 suites after the deletion, 561.9s serial on this container, regenerated on
the rebased base rather than the one this change was written against — `main`
retired several more suites in between, `mutant`, `mutant-census` and
`session-start` among them, and a corpus derived from an older report publishes a
cost for a suite that is gone. The saving this change can claim is
`tests/bot-issue.bats`' own 2.9s share of the corpus it was measured in, 0.3%;
the totals differ by far more across runs, and that is those other retirements
plus machine noise rather than anything this branch did.
Refs: CLOUD-1295
Refs: CLOUD-1164
Refs: CLOUD-1213
Refs: CLOUD-1297
Refs: CLOUD-1299
Admits: 1c68cb4a5d20efa873c0ed2b7564b7cfdf53c0b6ebb9c79e211eea4b8445bcc5
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: batten.toml
Admits-head: a11e917
Admits-epoch: 650a76ba70a8b5645ffc58aedb1bdde72394e39026b5c1391a926934ea49702c
Admits-author: alec@wenzowski.com
Admits-prev: c7d8beede76f452a09d1a6c47a615b728f2876a60e055ef065975a27903f003f
Admits-answer-lost: mise-tasks/bot-issue.sh stays alive as a second implementation of predicates the engine now owns, which is the state policy/shell-retirement.rego's V-SHELL-RULE-EDITED exists to end -- the ported verbs cannot read a lane nobody declared, so refusing this write refuses the retirement rather than this line of it.
Admits-answer-precondition: The bot lane's consumer facts -- which forge repository, which bot logins, which manifests the lane owns, the marker strings, the tracker's key prefix and the branch prefix -- are exactly the values non-negotiable rule 1 forbids in crates/batten. Retiring mise-tasks/bot-issue.sh onto `batten pr derive|file|link|ensure|closes` and `batten claim bot` moves the MATCHER into the engine and leaves those facts here, so batten.toml is the owning surface for them and no other surface can carry them. The write is one additive [bot_lane] block, visible in this pull request's own diff, which is where reviewers read it.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because batten.toml IS the owning surface for a consumer's policy facts; there is no other surface that owns them. A local override cannot carry them either: adding a login to `bots` or a path to `owned_manifests` turns a refusal into a filed row, which is a weakening dressed as an addition and house style section 8's raise-only rule does not admit it. R-RESTORE-IT does not apply because the write is additive and deliberate rather than an accident to undo -- restoring the file would delete the table the ported verbs read.
Admits: ddd46be8d9ae384d97c38556832349bfc8dc319895761edb2255894b821b3763
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: .github/workflows/auto-bot-land.yml
Admits-head: a11e917
Admits-epoch: 8e6d5f2f19b383997365f6b72ff52816d72c7029208dbb81a40389acbd65e67f
Admits-author: alec@wenzowski.com
Admits-prev: -
Admits-answer-lost: The bot lane's lander breaks outright at its next tick: both steps call a task the retirement removed, so no bot pull request gets a row and none is landed. The alternative is keeping mise-tasks/bot-issue.sh alive as a second implementation, which is the state V-SHELL-RULE-EDITED exists to end.
Admits-answer-precondition: auto-bot-land.yml invokes `mise run bot-issue ensure` and `mise run bot-issue closes`, and this change deletes mise-tasks/bot-issue.sh. Leaving the calls would leave the lander invoking a task that no longer exists, so the workflow is the owning surface for its own steps and there is nowhere else the repoint could be made. The write is two invocation repoints -- `batten pr ensure` and `batten pr closes` -- plus the comment naming the retired program, and nothing about what the workflow decides changes.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because .github/workflows/auto-bot-land.yml IS the owning surface for the lander's own steps; a workflow's run lines cannot be declared anywhere else. R-RESTORE-IT does not apply because the change is a deliberate repoint at a successor this same commit lands, and restoring the file would leave a workflow calling a deleted program.
Admits: a3bb7b559f5118c0fe2cc4e4c8e2336e2bcbf1262a0df2578070e44446a20751
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: .github/workflows/sbom-actions-currency.yml
Admits-head: a11e917
Admits-epoch: 8e6d5f2f19b383997365f6b72ff52816d72c7029208dbb81a40389acbd65e67f
Admits-author: alec@wenzowski.com
Admits-prev: e03b883be1a24354b6eaeedb59a2c39f16052d832ef6dde2ceab292ca964fa5b
Admits-answer-lost: Every carry pull request `sbom-actions-currency` opens stalls on `closing-key-check` exactly as #806 did, and each one needs a human to patch its body before it can land -- which is the manual step CLOUD-1213 exists to remove, on the lane whose blockage this whole change is about.
Admits-answer-precondition: The generated pull request body carries `Refs: CLOUD-1213` and no closing marker, so `closing-key-check` reads it as a body naming a key non-closingly and stalls the landing. Measured on #806, where the body was patched by hand to get it through -- the defect repeating on every future carry pull request. The body is generated by this workflow's own `gh pr create` step, so the workflow is the owning surface for it and no other surface can set it. The write is one added line, `DO-NOT-CLOSE CLOUD-1213`, in the same printf list, visible in this pull request's diff.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because .github/workflows/sbom-actions-currency.yml IS the owning surface for the body it generates; a workflow's own `gh pr create` arguments cannot be declared anywhere else. R-RESTORE-IT does not apply because the change is a deliberate one-line addition rather than an accident to undo, and restoring the file would put back the body that stalls.
Admits: 1588b760e38a64a36f5aaef031bd5b3e8f5e1160c0f94eec486b41dbca5d335c
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: .serena/memories/core.md
Admits-head: a11e917
Admits-epoch: a5e521241d3b4c861cd0ffb0813ac2c5eb0bfd1b3b364e13418f67888e3de545
Admits-author: alec@wenzowski.com
Admits-prev: 121e5c62ce26f6caa46a19393e76ae86d200b8b113fccdaf2bb1196df372865b
Admits-answer-lost: `bot.rs` lands with no map row, `module-map-check` fails the gate, and the retirement cannot commit at all -- so refusing this write refuses the whole change rather than this bullet of it. A reader looking up what `bot.rs` owns would find nothing where every other module is described.
Admits-answer-precondition: `module-map-check` refuses a `crates/batten/src/*.rs` module with no row in `.serena/memories/core.md`, and this change adds `crates/batten/src/bot.rs`. The memory IS the module map's authority -- AGENTS.md points there for the per-module layout precisely so the tree is not restated in a budgeted file -- so there is no other surface a row could go on. The write was made through Serena's `edit_memory`, which is the sanctioned route, and it is one added bullet visible in this pull request's diff.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because `.serena/memories/core.md` IS the owning surface for the module map; AGENTS.md defers to it by name and `module-map-check` reads it as the authority. R-RESTORE-IT does not apply because the write is one additive bullet describing a module this same commit introduces -- restoring the file would leave the map missing a row the gate demands.
Admits: cbb7e7b9a66ad1ec32b1d2e5f2df9e67efc920620236ceb72e667aa6e772fdc7
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: policy/spawn-adapters.rego
Admits-head: 6da96ce
Admits-epoch: d8992f36146d4a8f3bd0250644ab0624b29c8cc7abd4b5640b4c4aa2c6c5efe6
Admits-author: alec@wenzowski.com
Admits-prev: -
Admits-answer-lost: The rebase cannot complete, so the retirement cannot land at all. Taking `main`'s side alone leaves `bot` unplaced and `spawn-adapters` refuses `crates/batten/src/bot.rs`; taking this branch's side alone deletes `mutate` and reintroduces the refusal on the module `main` just landed.
Admits-answer-precondition: This is a REBASE CONFLICT RESOLUTION, and both sides of it are additions to the same set literal. `main` placed `mutate` when it retired `mise-tasks/mutant.sh`; this branch places `bot` when it retires `mise-tasks/bot-issue.sh`. Neither adapter can be dropped -- `spawn-adapters` refuses a spawn in a module the table has not placed, so losing either side reintroduces the refusal the other change already answered. The resolution is the union of the two rows plus both reasons, which is what the file would hold had the two landed in sequence rather than concurrently. There is no other surface: the adapter table IS this module.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because `policy/spawn-adapters.rego` IS the owning surface for the adapter placement table; the set literal cannot be declared anywhere else. R-RESTORE-IT does not apply because restoring the file to either side is precisely what loses one of the two placements -- there is no version of this file that carries both without this write.
… claim `sbom-actions-currency` opens a PR that `verify` refuses: there is no claim receipt, and the agent one attests a refinement no bot performed. #806 landed with a `--takeover` claim against CLOUD-1213, which was a fake claim, and every future carry PR meets the same wall. `batten claim carry` is the honest receipt. It attests DERIVABILITY rather than a branch name — a prefix exemption would be a password wearing a branch name — so it mints only when the branch appends rows whose repo the base table already maps, changing the sha alone, and touches nothing else. The verb takes no argument on purpose: the subject is the branch's own diff against its merge base, and a caller that could name its own subject could name one that is derivable while changing something else. Also repairs the `snapshots` task, stale since the integration targets moved under `tests/it/`. Refs: CLOUD-1295
…eceipt
486 lines of shell and 323 of bats, six subcommands, all of it GitHub API plus
one local receipt write. `mise-tasks/bot-issue.sh` and `tests/bot-issue.bats`
are deleted; the predicates land as `crates/batten/src/bot.rs` and six new
leaves — `batten pr derive|file|link|ensure|closes` and `batten claim bot`.
TWO HALVES, KEPT APART INSIDE ONE MODULE. The predicates — is this pull request
one of the lane's, which manifests it touched, what Conventional type its
subject declares, whether a body still CLOSES a key rather than merely naming
one — are pure functions with their own unit tier. The `forge` half underneath
is the only thing that talks to anybody.
THE CONSUMER FACTS ARE `[bot_lane]` IN batten.toml, not literals in the crate.
Which repository, which bot logins, which manifests the lane owns, the marker
strings, the tracker's key prefix and the branch prefix — every value the
retired program spelled internally is a config row now, so a grep of
`crates/batten` for a bot's name or a manifest path returns nothing
(non-negotiable rule 1). `document_facts` caught the first draft's doc comment
naming a workflow glob as an example, and that is the gate working. The derived
row's body is a tracked template with `{{...}}` placeholders rather than a page
of markdown inside a TOML value; an unfilled placeholder refuses at
substitution rather than rendering a template artifact into a tracker row.
THE FORGE'S OWN CLIENT, NOT THIS CRATE'S HTTP TRANSPORT. `pr_watch` is the
landed precedent (CLOUD-1143) and `gh` resolves the credential outside the
crate, where `fetch` would have put token resolution inside it next to no
config row that declares one. `policy/spawn-adapters.rego` places `bot` on
`pr_watch`'s own argument. The commit that widened `fetch::Call` for a PATCH
this port turned out not to need is reverted earlier on this branch.
THE EXIT TABLE CHANGED AND THE WORKFLOW FOLLOWS IT. `bot-issue` used 1 for
"refused, not this lane's" and 2 for "could not look"; house style §7 makes a
refusal 2 and a could-not-look 3. `auto-bot-land.yml`'s two steps are repointed
and their `case` arms moved with them — reading 1 as an ordinary refusal there
would have treated a usage error as a pass.
THE THIRD RECEIPT KIND IS WIRED. `verify` now tries `claim`, then `bot`, then
`carry`, so a licence-carry branch no longer needs the fake `--takeover` claim
`DO-NOT-CLOSE CLOUD-1213`, without which every carry pull request stalls on
`closing-key-check` exactly as #806 did and needs a human to patch it.
WHAT IS NOT DONE, AND WHY IT IS FILED RATHER THAN FIXED. The plan called for a
`checks = ["carry"]` row beside `claim-needs-receipt`. That row is wrong: a
receipt rule's `checks` is a CONJUNCTION, so a second row is a second AND and
would deny every ordinary write on every branch. `verify`'s shell body can
express the disjunction and the mediated gate cannot — a live false positive
for the bot receipt since CLOUD-693, which this change's third kind inherits.
CLOUD-1297 owns it, with the disjunction as its §2.
TWO GATES CAUGHT REAL DEFECTS IN THIS CHANGE AND BOTH ARE FIXED HERE.
`no-appeal-to-authority` refused `carry.rs` and its tier for naming a real
action repository as fixture data — a third party's name in the crate, which is
the class the rule exists for, and the fixtures now use neutral names.
`module-layering` refused both new modules as unplaced, which is the coverage
clause working: `bot` and `carry` are placed with their reasons.
ONE EDIT IS DECLINED RATHER THAN FORCED, and CLOUD-1299 owns it. Retiring the
program makes `verify`'s no-receipt remedy name a task that no longer exists —
but `tests/verify.bats` asserts that message contains `bot-issue receipt`, and a
governed suite has two landable shapes, neither of which is an edit.
`repoints_at_the_declared_invocation` cannot admit it either: it requires the
replaced span to be a PATH reference, and a task name plus a subcommand is not
one. So the remedy names the live verb FIRST and keeps the old name as the thing
it replaced — true, useful mid-transition, and satisfying the pin — with the
reason at the site rather than left to read as a slip.
THE LEDGER. Two file arms and 22 case arms, `kind:verb` on each because the
lane needs stdin, spawns with its own arguments and performs writes — none of
which a tree-scoped module may do. `bot-issue` leaves `$MUTANT_GATES` and needs
no `#MUTANT-EXEMPT`: the census enumerates `mise-tasks/*.sh` and `policy/*.rego`
— `mutate::subjects`, since `main` retired `mutant-census` onto the engine under
this branch — so a deleted program is not in the census at all. The arm
carries a `runs:` field naming `batten claim bot` — ONE arm per deleted path,
which `V-RETIREMENT-AMBIGUOUS` requires, so the field names the only invocation
a GOVERNED caller loses; the lander's two workflow steps are ungoverned and
their repoints are free. Its two
declared mutations are re-homed as named cases —
`idempotence_a_second_call_on_the_same_pr_files_nothing` and
`a_key_named_but_not_closed_is_refused` — each carrying the mutation it stands
for in its comment.
THE LOAD-BEARING CASE IS REPLAYED, NOT ASSERTED. The dying suite ran the REAL
`ready-lint` over the REAL derived payload, because "the derived block is
checkable by the same gate that checks a human's" is the claim that makes a
mechanical row honest. `the_derived_block_passes_ready_lint` does the same over
this repository's committed template and its committed `[[pattern]]` grammar —
and it earned its place immediately, catching that `prettier` reshapes the
template's bullets on every `mise run fmt`.
A KNOWN LAG, STATED RATHER THAN HIDDEN. `auto-bot-land.yml` installs `batten`
from the latest RELEASE, so its two repointed steps fail loudly with "command
not found" between this merge and the next release. That is the same gap
CLOUD-1143 recorded for `checks-green`, and it fails the job rather than
passing silently.
126 suites after the deletion, 561.9s serial on this container, regenerated on
the rebased base rather than the one this change was written against — `main`
retired several more suites in between, `mutant`, `mutant-census` and
`session-start` among them, and a corpus derived from an older report publishes a
cost for a suite that is gone. The saving this change can claim is
`tests/bot-issue.bats`' own 2.9s share of the corpus it was measured in, 0.3%;
the totals differ by far more across runs, and that is those other retirements
plus machine noise rather than anything this branch did.
Refs: CLOUD-1295
Refs: CLOUD-1164
Refs: CLOUD-1213
Refs: CLOUD-1297
Refs: CLOUD-1299
Admits: 1c68cb4a5d20efa873c0ed2b7564b7cfdf53c0b6ebb9c79e211eea4b8445bcc5
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: batten.toml
Admits-head: a11e917
Admits-epoch: 650a76ba70a8b5645ffc58aedb1bdde72394e39026b5c1391a926934ea49702c
Admits-author: alec@wenzowski.com
Admits-prev: c7d8beede76f452a09d1a6c47a615b728f2876a60e055ef065975a27903f003f
Admits-answer-lost: mise-tasks/bot-issue.sh stays alive as a second implementation of predicates the engine now owns, which is the state policy/shell-retirement.rego's V-SHELL-RULE-EDITED exists to end -- the ported verbs cannot read a lane nobody declared, so refusing this write refuses the retirement rather than this line of it.
Admits-answer-precondition: The bot lane's consumer facts -- which forge repository, which bot logins, which manifests the lane owns, the marker strings, the tracker's key prefix and the branch prefix -- are exactly the values non-negotiable rule 1 forbids in crates/batten. Retiring mise-tasks/bot-issue.sh onto `batten pr derive|file|link|ensure|closes` and `batten claim bot` moves the MATCHER into the engine and leaves those facts here, so batten.toml is the owning surface for them and no other surface can carry them. The write is one additive [bot_lane] block, visible in this pull request's own diff, which is where reviewers read it.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because batten.toml IS the owning surface for a consumer's policy facts; there is no other surface that owns them. A local override cannot carry them either: adding a login to `bots` or a path to `owned_manifests` turns a refusal into a filed row, which is a weakening dressed as an addition and house style section 8's raise-only rule does not admit it. R-RESTORE-IT does not apply because the write is additive and deliberate rather than an accident to undo -- restoring the file would delete the table the ported verbs read.
Admits: ddd46be8d9ae384d97c38556832349bfc8dc319895761edb2255894b821b3763
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: .github/workflows/auto-bot-land.yml
Admits-head: a11e917
Admits-epoch: 8e6d5f2f19b383997365f6b72ff52816d72c7029208dbb81a40389acbd65e67f
Admits-author: alec@wenzowski.com
Admits-prev: -
Admits-answer-lost: The bot lane's lander breaks outright at its next tick: both steps call a task the retirement removed, so no bot pull request gets a row and none is landed. The alternative is keeping mise-tasks/bot-issue.sh alive as a second implementation, which is the state V-SHELL-RULE-EDITED exists to end.
Admits-answer-precondition: auto-bot-land.yml invokes `mise run bot-issue ensure` and `mise run bot-issue closes`, and this change deletes mise-tasks/bot-issue.sh. Leaving the calls would leave the lander invoking a task that no longer exists, so the workflow is the owning surface for its own steps and there is nowhere else the repoint could be made. The write is two invocation repoints -- `batten pr ensure` and `batten pr closes` -- plus the comment naming the retired program, and nothing about what the workflow decides changes.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because .github/workflows/auto-bot-land.yml IS the owning surface for the lander's own steps; a workflow's run lines cannot be declared anywhere else. R-RESTORE-IT does not apply because the change is a deliberate repoint at a successor this same commit lands, and restoring the file would leave a workflow calling a deleted program.
Admits: a3bb7b559f5118c0fe2cc4e4c8e2336e2bcbf1262a0df2578070e44446a20751
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: .github/workflows/sbom-actions-currency.yml
Admits-head: a11e917
Admits-epoch: 8e6d5f2f19b383997365f6b72ff52816d72c7029208dbb81a40389acbd65e67f
Admits-author: alec@wenzowski.com
Admits-prev: e03b883be1a24354b6eaeedb59a2c39f16052d832ef6dde2ceab292ca964fa5b
Admits-answer-lost: Every carry pull request `sbom-actions-currency` opens stalls on `closing-key-check` exactly as #806 did, and each one needs a human to patch its body before it can land -- which is the manual step CLOUD-1213 exists to remove, on the lane whose blockage this whole change is about.
Admits-answer-precondition: The generated pull request body carries `Refs: CLOUD-1213` and no closing marker, so `closing-key-check` reads it as a body naming a key non-closingly and stalls the landing. Measured on #806, where the body was patched by hand to get it through -- the defect repeating on every future carry pull request. The body is generated by this workflow's own `gh pr create` step, so the workflow is the owning surface for it and no other surface can set it. The write is one added line, `DO-NOT-CLOSE CLOUD-1213`, in the same printf list, visible in this pull request's diff.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because .github/workflows/sbom-actions-currency.yml IS the owning surface for the body it generates; a workflow's own `gh pr create` arguments cannot be declared anywhere else. R-RESTORE-IT does not apply because the change is a deliberate one-line addition rather than an accident to undo, and restoring the file would put back the body that stalls.
Admits: 1588b760e38a64a36f5aaef031bd5b3e8f5e1160c0f94eec486b41dbca5d335c
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: .serena/memories/core.md
Admits-head: a11e917
Admits-epoch: a5e521241d3b4c861cd0ffb0813ac2c5eb0bfd1b3b364e13418f67888e3de545
Admits-author: alec@wenzowski.com
Admits-prev: 121e5c62ce26f6caa46a19393e76ae86d200b8b113fccdaf2bb1196df372865b
Admits-answer-lost: `bot.rs` lands with no map row, `module-map-check` fails the gate, and the retirement cannot commit at all -- so refusing this write refuses the whole change rather than this bullet of it. A reader looking up what `bot.rs` owns would find nothing where every other module is described.
Admits-answer-precondition: `module-map-check` refuses a `crates/batten/src/*.rs` module with no row in `.serena/memories/core.md`, and this change adds `crates/batten/src/bot.rs`. The memory IS the module map's authority -- AGENTS.md points there for the per-module layout precisely so the tree is not restated in a budgeted file -- so there is no other surface a row could go on. The write was made through Serena's `edit_memory`, which is the sanctioned route, and it is one added bullet visible in this pull request's diff.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because `.serena/memories/core.md` IS the owning surface for the module map; AGENTS.md defers to it by name and `module-map-check` reads it as the authority. R-RESTORE-IT does not apply because the write is one additive bullet describing a module this same commit introduces -- restoring the file would leave the map missing a row the gate demands.
Admits: cbb7e7b9a66ad1ec32b1d2e5f2df9e67efc920620236ceb72e667aa6e772fdc7
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: policy/spawn-adapters.rego
Admits-head: 6da96ce
Admits-epoch: d8992f36146d4a8f3bd0250644ab0624b29c8cc7abd4b5640b4c4aa2c6c5efe6
Admits-author: alec@wenzowski.com
Admits-prev: -
Admits-answer-lost: The rebase cannot complete, so the retirement cannot land at all. Taking `main`'s side alone leaves `bot` unplaced and `spawn-adapters` refuses `crates/batten/src/bot.rs`; taking this branch's side alone deletes `mutate` and reintroduces the refusal on the module `main` just landed.
Admits-answer-precondition: This is a REBASE CONFLICT RESOLUTION, and both sides of it are additions to the same set literal. `main` placed `mutate` when it retired `mise-tasks/mutant.sh`; this branch places `bot` when it retires `mise-tasks/bot-issue.sh`. Neither adapter can be dropped -- `spawn-adapters` refuses a spawn in a module the table has not placed, so losing either side reintroduces the refusal the other change already answered. The resolution is the union of the two rows plus both reasons, which is what the file would hold had the two landed in sequence rather than concurrently. There is no other surface: the adapter table IS this module.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because `policy/spawn-adapters.rego` IS the owning surface for the adapter placement table; the set literal cannot be declared anywhere else. R-RESTORE-IT does not apply because restoring the file to either side is precisely what loses one of the two placements -- there is no version of this file that carries both without this write.
… claim `sbom-actions-currency` opens a PR that `verify` refuses: there is no claim receipt, and the agent one attests a refinement no bot performed. #806 landed with a `--takeover` claim against CLOUD-1213, which was a fake claim, and every future carry PR meets the same wall. `batten claim carry` is the honest receipt. It attests DERIVABILITY rather than a branch name — a prefix exemption would be a password wearing a branch name — so it mints only when the branch appends rows whose repo the base table already maps, changing the sha alone, and touches nothing else. The verb takes no argument on purpose: the subject is the branch's own diff against its merge base, and a caller that could name its own subject could name one that is derivable while changing something else. Also repairs the `snapshots` task, stale since the integration targets moved under `tests/it/`. Refs: CLOUD-1295
…eceipt
486 lines of shell and 323 of bats, six subcommands, all of it GitHub API plus
one local receipt write. `mise-tasks/bot-issue.sh` and `tests/bot-issue.bats`
are deleted; the predicates land as `crates/batten/src/bot.rs` and six new
leaves — `batten pr derive|file|link|ensure|closes` and `batten claim bot`.
TWO HALVES, KEPT APART INSIDE ONE MODULE. The predicates — is this pull request
one of the lane's, which manifests it touched, what Conventional type its
subject declares, whether a body still CLOSES a key rather than merely naming
one — are pure functions with their own unit tier. The `forge` half underneath
is the only thing that talks to anybody.
THE CONSUMER FACTS ARE `[bot_lane]` IN batten.toml, not literals in the crate.
Which repository, which bot logins, which manifests the lane owns, the marker
strings, the tracker's key prefix and the branch prefix — every value the
retired program spelled internally is a config row now, so a grep of
`crates/batten` for a bot's name or a manifest path returns nothing
(non-negotiable rule 1). `document_facts` caught the first draft's doc comment
naming a workflow glob as an example, and that is the gate working. The derived
row's body is a tracked template with `{{...}}` placeholders rather than a page
of markdown inside a TOML value; an unfilled placeholder refuses at
substitution rather than rendering a template artifact into a tracker row.
THE FORGE'S OWN CLIENT, NOT THIS CRATE'S HTTP TRANSPORT. `pr_watch` is the
landed precedent (CLOUD-1143) and `gh` resolves the credential outside the
crate, where `fetch` would have put token resolution inside it next to no
config row that declares one. `policy/spawn-adapters.rego` places `bot` on
`pr_watch`'s own argument. The commit that widened `fetch::Call` for a PATCH
this port turned out not to need is reverted earlier on this branch.
THE EXIT TABLE CHANGED AND THE WORKFLOW FOLLOWS IT. `bot-issue` used 1 for
"refused, not this lane's" and 2 for "could not look"; house style §7 makes a
refusal 2 and a could-not-look 3. `auto-bot-land.yml`'s two steps are repointed
and their `case` arms moved with them — reading 1 as an ordinary refusal there
would have treated a usage error as a pass.
THE THIRD RECEIPT KIND IS WIRED. `verify` now tries `claim`, then `bot`, then
`carry`, so a licence-carry branch no longer needs the fake `--takeover` claim
`DO-NOT-CLOSE CLOUD-1213`, without which every carry pull request stalls on
`closing-key-check` exactly as #806 did and needs a human to patch it.
WHAT IS NOT DONE, AND WHY IT IS FILED RATHER THAN FIXED. The plan called for a
`checks = ["carry"]` row beside `claim-needs-receipt`. That row is wrong: a
receipt rule's `checks` is a CONJUNCTION, so a second row is a second AND and
would deny every ordinary write on every branch. `verify`'s shell body can
express the disjunction and the mediated gate cannot — a live false positive
for the bot receipt since CLOUD-693, which this change's third kind inherits.
CLOUD-1297 owns it, with the disjunction as its §2.
TWO GATES CAUGHT REAL DEFECTS IN THIS CHANGE AND BOTH ARE FIXED HERE.
`no-appeal-to-authority` refused `carry.rs` and its tier for naming a real
action repository as fixture data — a third party's name in the crate, which is
the class the rule exists for, and the fixtures now use neutral names.
`module-layering` refused both new modules as unplaced, which is the coverage
clause working: `bot` and `carry` are placed with their reasons.
ONE EDIT IS DECLINED RATHER THAN FORCED, and CLOUD-1299 owns it. Retiring the
program makes `verify`'s no-receipt remedy name a task that no longer exists —
but `tests/verify.bats` asserts that message contains `bot-issue receipt`, and a
governed suite has two landable shapes, neither of which is an edit.
`repoints_at_the_declared_invocation` cannot admit it either: it requires the
replaced span to be a PATH reference, and a task name plus a subcommand is not
one. So the remedy names the live verb FIRST and keeps the old name as the thing
it replaced — true, useful mid-transition, and satisfying the pin — with the
reason at the site rather than left to read as a slip.
THE LEDGER. Two file arms and 22 case arms, `kind:verb` on each because the
lane needs stdin, spawns with its own arguments and performs writes — none of
which a tree-scoped module may do. `bot-issue` leaves `$MUTANT_GATES` and needs
no `#MUTANT-EXEMPT`: the census enumerates `mise-tasks/*.sh` and `policy/*.rego`
— `mutate::subjects`, since `main` retired `mutant-census` onto the engine under
this branch — so a deleted program is not in the census at all. The arm
carries a `runs:` field naming `batten claim bot` — ONE arm per deleted path,
which `V-RETIREMENT-AMBIGUOUS` requires, so the field names the only invocation
a GOVERNED caller loses; the lander's two workflow steps are ungoverned and
their repoints are free. Its two
declared mutations are re-homed as named cases —
`idempotence_a_second_call_on_the_same_pr_files_nothing` and
`a_key_named_but_not_closed_is_refused` — each carrying the mutation it stands
for in its comment.
THE LOAD-BEARING CASE IS REPLAYED, NOT ASSERTED. The dying suite ran the REAL
`ready-lint` over the REAL derived payload, because "the derived block is
checkable by the same gate that checks a human's" is the claim that makes a
mechanical row honest. `the_derived_block_passes_ready_lint` does the same over
this repository's committed template and its committed `[[pattern]]` grammar —
and it earned its place immediately, catching that `prettier` reshapes the
template's bullets on every `mise run fmt`.
A KNOWN LAG, STATED RATHER THAN HIDDEN. `auto-bot-land.yml` installs `batten`
from the latest RELEASE, so its two repointed steps fail loudly with "command
not found" between this merge and the next release. That is the same gap
CLOUD-1143 recorded for `checks-green`, and it fails the job rather than
passing silently.
126 suites after the deletion, 561.9s serial on this container, regenerated on
the rebased base rather than the one this change was written against — `main`
retired several more suites in between, `mutant`, `mutant-census` and
`session-start` among them, and a corpus derived from an older report publishes a
cost for a suite that is gone. The saving this change can claim is
`tests/bot-issue.bats`' own 2.9s share of the corpus it was measured in, 0.3%;
the totals differ by far more across runs, and that is those other retirements
plus machine noise rather than anything this branch did.
Refs: CLOUD-1295
Refs: CLOUD-1164
Refs: CLOUD-1213
Refs: CLOUD-1297
Refs: CLOUD-1299
Admits: 1c68cb4a5d20efa873c0ed2b7564b7cfdf53c0b6ebb9c79e211eea4b8445bcc5
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: batten.toml
Admits-head: a11e917
Admits-epoch: 650a76ba70a8b5645ffc58aedb1bdde72394e39026b5c1391a926934ea49702c
Admits-author: alec@wenzowski.com
Admits-prev: c7d8beede76f452a09d1a6c47a615b728f2876a60e055ef065975a27903f003f
Admits-answer-lost: mise-tasks/bot-issue.sh stays alive as a second implementation of predicates the engine now owns, which is the state policy/shell-retirement.rego's V-SHELL-RULE-EDITED exists to end -- the ported verbs cannot read a lane nobody declared, so refusing this write refuses the retirement rather than this line of it.
Admits-answer-precondition: The bot lane's consumer facts -- which forge repository, which bot logins, which manifests the lane owns, the marker strings, the tracker's key prefix and the branch prefix -- are exactly the values non-negotiable rule 1 forbids in crates/batten. Retiring mise-tasks/bot-issue.sh onto `batten pr derive|file|link|ensure|closes` and `batten claim bot` moves the MATCHER into the engine and leaves those facts here, so batten.toml is the owning surface for them and no other surface can carry them. The write is one additive [bot_lane] block, visible in this pull request's own diff, which is where reviewers read it.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because batten.toml IS the owning surface for a consumer's policy facts; there is no other surface that owns them. A local override cannot carry them either: adding a login to `bots` or a path to `owned_manifests` turns a refusal into a filed row, which is a weakening dressed as an addition and house style section 8's raise-only rule does not admit it. R-RESTORE-IT does not apply because the write is additive and deliberate rather than an accident to undo -- restoring the file would delete the table the ported verbs read.
Admits: ddd46be8d9ae384d97c38556832349bfc8dc319895761edb2255894b821b3763
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: .github/workflows/auto-bot-land.yml
Admits-head: a11e917
Admits-epoch: 8e6d5f2f19b383997365f6b72ff52816d72c7029208dbb81a40389acbd65e67f
Admits-author: alec@wenzowski.com
Admits-prev: -
Admits-answer-lost: The bot lane's lander breaks outright at its next tick: both steps call a task the retirement removed, so no bot pull request gets a row and none is landed. The alternative is keeping mise-tasks/bot-issue.sh alive as a second implementation, which is the state V-SHELL-RULE-EDITED exists to end.
Admits-answer-precondition: auto-bot-land.yml invokes `mise run bot-issue ensure` and `mise run bot-issue closes`, and this change deletes mise-tasks/bot-issue.sh. Leaving the calls would leave the lander invoking a task that no longer exists, so the workflow is the owning surface for its own steps and there is nowhere else the repoint could be made. The write is two invocation repoints -- `batten pr ensure` and `batten pr closes` -- plus the comment naming the retired program, and nothing about what the workflow decides changes.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because .github/workflows/auto-bot-land.yml IS the owning surface for the lander's own steps; a workflow's run lines cannot be declared anywhere else. R-RESTORE-IT does not apply because the change is a deliberate repoint at a successor this same commit lands, and restoring the file would leave a workflow calling a deleted program.
Admits: a3bb7b559f5118c0fe2cc4e4c8e2336e2bcbf1262a0df2578070e44446a20751
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: .github/workflows/sbom-actions-currency.yml
Admits-head: a11e917
Admits-epoch: 8e6d5f2f19b383997365f6b72ff52816d72c7029208dbb81a40389acbd65e67f
Admits-author: alec@wenzowski.com
Admits-prev: e03b883be1a24354b6eaeedb59a2c39f16052d832ef6dde2ceab292ca964fa5b
Admits-answer-lost: Every carry pull request `sbom-actions-currency` opens stalls on `closing-key-check` exactly as #806 did, and each one needs a human to patch its body before it can land -- which is the manual step CLOUD-1213 exists to remove, on the lane whose blockage this whole change is about.
Admits-answer-precondition: The generated pull request body carries `Refs: CLOUD-1213` and no closing marker, so `closing-key-check` reads it as a body naming a key non-closingly and stalls the landing. Measured on #806, where the body was patched by hand to get it through -- the defect repeating on every future carry pull request. The body is generated by this workflow's own `gh pr create` step, so the workflow is the owning surface for it and no other surface can set it. The write is one added line, `DO-NOT-CLOSE CLOUD-1213`, in the same printf list, visible in this pull request's diff.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because .github/workflows/sbom-actions-currency.yml IS the owning surface for the body it generates; a workflow's own `gh pr create` arguments cannot be declared anywhere else. R-RESTORE-IT does not apply because the change is a deliberate one-line addition rather than an accident to undo, and restoring the file would put back the body that stalls.
Admits: 1588b760e38a64a36f5aaef031bd5b3e8f5e1160c0f94eec486b41dbca5d335c
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: .serena/memories/core.md
Admits-head: a11e917
Admits-epoch: a5e521241d3b4c861cd0ffb0813ac2c5eb0bfd1b3b364e13418f67888e3de545
Admits-author: alec@wenzowski.com
Admits-prev: 121e5c62ce26f6caa46a19393e76ae86d200b8b113fccdaf2bb1196df372865b
Admits-answer-lost: `bot.rs` lands with no map row, `module-map-check` fails the gate, and the retirement cannot commit at all -- so refusing this write refuses the whole change rather than this bullet of it. A reader looking up what `bot.rs` owns would find nothing where every other module is described.
Admits-answer-precondition: `module-map-check` refuses a `crates/batten/src/*.rs` module with no row in `.serena/memories/core.md`, and this change adds `crates/batten/src/bot.rs`. The memory IS the module map's authority -- AGENTS.md points there for the per-module layout precisely so the tree is not restated in a budgeted file -- so there is no other surface a row could go on. The write was made through Serena's `edit_memory`, which is the sanctioned route, and it is one added bullet visible in this pull request's diff.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because `.serena/memories/core.md` IS the owning surface for the module map; AGENTS.md defers to it by name and `module-map-check` reads it as the authority. R-RESTORE-IT does not apply because the write is one additive bullet describing a module this same commit introduces -- restoring the file would leave the map missing a row the gate demands.
Admits: cbb7e7b9a66ad1ec32b1d2e5f2df9e67efc920620236ceb72e667aa6e772fdc7
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: policy/spawn-adapters.rego
Admits-head: 6da96ce
Admits-epoch: d8992f36146d4a8f3bd0250644ab0624b29c8cc7abd4b5640b4c4aa2c6c5efe6
Admits-author: alec@wenzowski.com
Admits-prev: -
Admits-answer-lost: The rebase cannot complete, so the retirement cannot land at all. Taking `main`'s side alone leaves `bot` unplaced and `spawn-adapters` refuses `crates/batten/src/bot.rs`; taking this branch's side alone deletes `mutate` and reintroduces the refusal on the module `main` just landed.
Admits-answer-precondition: This is a REBASE CONFLICT RESOLUTION, and both sides of it are additions to the same set literal. `main` placed `mutate` when it retired `mise-tasks/mutant.sh`; this branch places `bot` when it retires `mise-tasks/bot-issue.sh`. Neither adapter can be dropped -- `spawn-adapters` refuses a spawn in a module the table has not placed, so losing either side reintroduces the refusal the other change already answered. The resolution is the union of the two rows plus both reasons, which is what the file would hold had the two landed in sequence rather than concurrently. There is no other surface: the adapter table IS this module.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because `policy/spawn-adapters.rego` IS the owning surface for the adapter placement table; the set literal cannot be declared anywhere else. R-RESTORE-IT does not apply because restoring the file to either side is precisely what loses one of the two placements -- there is no version of this file that carries both without this write.
… claim `sbom-actions-currency` opens a PR that `verify` refuses: there is no claim receipt, and the agent one attests a refinement no bot performed. #806 landed with a `--takeover` claim against CLOUD-1213, which was a fake claim, and every future carry PR meets the same wall. `batten claim carry` is the honest receipt. It attests DERIVABILITY rather than a branch name — a prefix exemption would be a password wearing a branch name — so it mints only when the branch appends rows whose repo the base table already maps, changing the sha alone, and touches nothing else. The verb takes no argument on purpose: the subject is the branch's own diff against its merge base, and a caller that could name its own subject could name one that is derivable while changing something else. Also repairs the `snapshots` task, stale since the integration targets moved under `tests/it/`. Refs: CLOUD-1295
…eceipt
486 lines of shell and 323 of bats, six subcommands, all of it GitHub API plus
one local receipt write. `mise-tasks/bot-issue.sh` and `tests/bot-issue.bats`
are deleted; the predicates land as `crates/batten/src/bot.rs` and six new
leaves — `batten pr derive|file|link|ensure|closes` and `batten claim bot`.
TWO HALVES, KEPT APART INSIDE ONE MODULE. The predicates — is this pull request
one of the lane's, which manifests it touched, what Conventional type its
subject declares, whether a body still CLOSES a key rather than merely naming
one — are pure functions with their own unit tier. The `forge` half underneath
is the only thing that talks to anybody.
THE CONSUMER FACTS ARE `[bot_lane]` IN batten.toml, not literals in the crate.
Which repository, which bot logins, which manifests the lane owns, the marker
strings, the tracker's key prefix and the branch prefix — every value the
retired program spelled internally is a config row now, so a grep of
`crates/batten` for a bot's name or a manifest path returns nothing
(non-negotiable rule 1). `document_facts` caught the first draft's doc comment
naming a workflow glob as an example, and that is the gate working. The derived
row's body is a tracked template with `{{...}}` placeholders rather than a page
of markdown inside a TOML value; an unfilled placeholder refuses at
substitution rather than rendering a template artifact into a tracker row.
THE FORGE'S OWN CLIENT, NOT THIS CRATE'S HTTP TRANSPORT. `pr_watch` is the
landed precedent (CLOUD-1143) and `gh` resolves the credential outside the
crate, where `fetch` would have put token resolution inside it next to no
config row that declares one. `policy/spawn-adapters.rego` places `bot` on
`pr_watch`'s own argument. The commit that widened `fetch::Call` for a PATCH
this port turned out not to need is reverted earlier on this branch.
THE EXIT TABLE CHANGED AND THE WORKFLOW FOLLOWS IT. `bot-issue` used 1 for
"refused, not this lane's" and 2 for "could not look"; house style §7 makes a
refusal 2 and a could-not-look 3. `auto-bot-land.yml`'s two steps are repointed
and their `case` arms moved with them — reading 1 as an ordinary refusal there
would have treated a usage error as a pass.
THE THIRD RECEIPT KIND IS WIRED. `verify` now tries `claim`, then `bot`, then
`carry`, so a licence-carry branch no longer needs the fake `--takeover` claim
`DO-NOT-CLOSE CLOUD-1213`, without which every carry pull request stalls on
`closing-key-check` exactly as #806 did and needs a human to patch it.
WHAT IS NOT DONE, AND WHY IT IS FILED RATHER THAN FIXED. The plan called for a
`checks = ["carry"]` row beside `claim-needs-receipt`. That row is wrong: a
receipt rule's `checks` is a CONJUNCTION, so a second row is a second AND and
would deny every ordinary write on every branch. `verify`'s shell body can
express the disjunction and the mediated gate cannot — a live false positive
for the bot receipt since CLOUD-693, which this change's third kind inherits.
CLOUD-1297 owns it, with the disjunction as its §2.
TWO GATES CAUGHT REAL DEFECTS IN THIS CHANGE AND BOTH ARE FIXED HERE.
`no-appeal-to-authority` refused `carry.rs` and its tier for naming a real
action repository as fixture data — a third party's name in the crate, which is
the class the rule exists for, and the fixtures now use neutral names.
`module-layering` refused both new modules as unplaced, which is the coverage
clause working: `bot` and `carry` are placed with their reasons.
ONE EDIT IS DECLINED RATHER THAN FORCED, and CLOUD-1299 owns it. Retiring the
program makes `verify`'s no-receipt remedy name a task that no longer exists —
but `tests/verify.bats` asserts that message contains `bot-issue receipt`, and a
governed suite has two landable shapes, neither of which is an edit.
`repoints_at_the_declared_invocation` cannot admit it either: it requires the
replaced span to be a PATH reference, and a task name plus a subcommand is not
one. So the remedy names the live verb FIRST and keeps the old name as the thing
it replaced — true, useful mid-transition, and satisfying the pin — with the
reason at the site rather than left to read as a slip.
THE LEDGER. Two file arms and 22 case arms, `kind:verb` on each because the
lane needs stdin, spawns with its own arguments and performs writes — none of
which a tree-scoped module may do. `bot-issue` leaves `$MUTANT_GATES` and needs
no `#MUTANT-EXEMPT`: the census enumerates `mise-tasks/*.sh` and `policy/*.rego`
— `mutate::subjects`, since `main` retired `mutant-census` onto the engine under
this branch — so a deleted program is not in the census at all. The arm
carries a `runs:` field naming `batten claim bot` — ONE arm per deleted path,
which `V-RETIREMENT-AMBIGUOUS` requires, so the field names the only invocation
a GOVERNED caller loses; the lander's two workflow steps are ungoverned and
their repoints are free. Its two
declared mutations are re-homed as named cases —
`idempotence_a_second_call_on_the_same_pr_files_nothing` and
`a_key_named_but_not_closed_is_refused` — each carrying the mutation it stands
for in its comment.
THE LOAD-BEARING CASE IS REPLAYED, NOT ASSERTED. The dying suite ran the REAL
`ready-lint` over the REAL derived payload, because "the derived block is
checkable by the same gate that checks a human's" is the claim that makes a
mechanical row honest. `the_derived_block_passes_ready_lint` does the same over
this repository's committed template and its committed `[[pattern]]` grammar —
and it earned its place immediately, catching that `prettier` reshapes the
template's bullets on every `mise run fmt`.
A KNOWN LAG, STATED RATHER THAN HIDDEN. `auto-bot-land.yml` installs `batten`
from the latest RELEASE, so its two repointed steps fail loudly with "command
not found" between this merge and the next release. That is the same gap
CLOUD-1143 recorded for `checks-green`, and it fails the job rather than
passing silently.
126 suites after the deletion, 561.9s serial on this container, regenerated on
the rebased base rather than the one this change was written against — `main`
retired several more suites in between, `mutant`, `mutant-census` and
`session-start` among them, and a corpus derived from an older report publishes a
cost for a suite that is gone. The saving this change can claim is
`tests/bot-issue.bats`' own 2.9s share of the corpus it was measured in, 0.3%;
the totals differ by far more across runs, and that is those other retirements
plus machine noise rather than anything this branch did.
Refs: CLOUD-1295
Refs: CLOUD-1164
Refs: CLOUD-1213
Refs: CLOUD-1297
Refs: CLOUD-1299
Admits: 1c68cb4a5d20efa873c0ed2b7564b7cfdf53c0b6ebb9c79e211eea4b8445bcc5
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: batten.toml
Admits-head: a11e917
Admits-epoch: 650a76ba70a8b5645ffc58aedb1bdde72394e39026b5c1391a926934ea49702c
Admits-author: alec@wenzowski.com
Admits-prev: c7d8beede76f452a09d1a6c47a615b728f2876a60e055ef065975a27903f003f
Admits-answer-lost: mise-tasks/bot-issue.sh stays alive as a second implementation of predicates the engine now owns, which is the state policy/shell-retirement.rego's V-SHELL-RULE-EDITED exists to end -- the ported verbs cannot read a lane nobody declared, so refusing this write refuses the retirement rather than this line of it.
Admits-answer-precondition: The bot lane's consumer facts -- which forge repository, which bot logins, which manifests the lane owns, the marker strings, the tracker's key prefix and the branch prefix -- are exactly the values non-negotiable rule 1 forbids in crates/batten. Retiring mise-tasks/bot-issue.sh onto `batten pr derive|file|link|ensure|closes` and `batten claim bot` moves the MATCHER into the engine and leaves those facts here, so batten.toml is the owning surface for them and no other surface can carry them. The write is one additive [bot_lane] block, visible in this pull request's own diff, which is where reviewers read it.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because batten.toml IS the owning surface for a consumer's policy facts; there is no other surface that owns them. A local override cannot carry them either: adding a login to `bots` or a path to `owned_manifests` turns a refusal into a filed row, which is a weakening dressed as an addition and house style section 8's raise-only rule does not admit it. R-RESTORE-IT does not apply because the write is additive and deliberate rather than an accident to undo -- restoring the file would delete the table the ported verbs read.
Admits: ddd46be8d9ae384d97c38556832349bfc8dc319895761edb2255894b821b3763
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: .github/workflows/auto-bot-land.yml
Admits-head: a11e917
Admits-epoch: 8e6d5f2f19b383997365f6b72ff52816d72c7029208dbb81a40389acbd65e67f
Admits-author: alec@wenzowski.com
Admits-prev: -
Admits-answer-lost: The bot lane's lander breaks outright at its next tick: both steps call a task the retirement removed, so no bot pull request gets a row and none is landed. The alternative is keeping mise-tasks/bot-issue.sh alive as a second implementation, which is the state V-SHELL-RULE-EDITED exists to end.
Admits-answer-precondition: auto-bot-land.yml invokes `mise run bot-issue ensure` and `mise run bot-issue closes`, and this change deletes mise-tasks/bot-issue.sh. Leaving the calls would leave the lander invoking a task that no longer exists, so the workflow is the owning surface for its own steps and there is nowhere else the repoint could be made. The write is two invocation repoints -- `batten pr ensure` and `batten pr closes` -- plus the comment naming the retired program, and nothing about what the workflow decides changes.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because .github/workflows/auto-bot-land.yml IS the owning surface for the lander's own steps; a workflow's run lines cannot be declared anywhere else. R-RESTORE-IT does not apply because the change is a deliberate repoint at a successor this same commit lands, and restoring the file would leave a workflow calling a deleted program.
Admits: a3bb7b559f5118c0fe2cc4e4c8e2336e2bcbf1262a0df2578070e44446a20751
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: .github/workflows/sbom-actions-currency.yml
Admits-head: a11e917
Admits-epoch: 8e6d5f2f19b383997365f6b72ff52816d72c7029208dbb81a40389acbd65e67f
Admits-author: alec@wenzowski.com
Admits-prev: e03b883be1a24354b6eaeedb59a2c39f16052d832ef6dde2ceab292ca964fa5b
Admits-answer-lost: Every carry pull request `sbom-actions-currency` opens stalls on `closing-key-check` exactly as #806 did, and each one needs a human to patch its body before it can land -- which is the manual step CLOUD-1213 exists to remove, on the lane whose blockage this whole change is about.
Admits-answer-precondition: The generated pull request body carries `Refs: CLOUD-1213` and no closing marker, so `closing-key-check` reads it as a body naming a key non-closingly and stalls the landing. Measured on #806, where the body was patched by hand to get it through -- the defect repeating on every future carry pull request. The body is generated by this workflow's own `gh pr create` step, so the workflow is the owning surface for it and no other surface can set it. The write is one added line, `DO-NOT-CLOSE CLOUD-1213`, in the same printf list, visible in this pull request's diff.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because .github/workflows/sbom-actions-currency.yml IS the owning surface for the body it generates; a workflow's own `gh pr create` arguments cannot be declared anywhere else. R-RESTORE-IT does not apply because the change is a deliberate one-line addition rather than an accident to undo, and restoring the file would put back the body that stalls.
Admits: 1588b760e38a64a36f5aaef031bd5b3e8f5e1160c0f94eec486b41dbca5d335c
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: .serena/memories/core.md
Admits-head: a11e917
Admits-epoch: a5e521241d3b4c861cd0ffb0813ac2c5eb0bfd1b3b364e13418f67888e3de545
Admits-author: alec@wenzowski.com
Admits-prev: 121e5c62ce26f6caa46a19393e76ae86d200b8b113fccdaf2bb1196df372865b
Admits-answer-lost: `bot.rs` lands with no map row, `module-map-check` fails the gate, and the retirement cannot commit at all -- so refusing this write refuses the whole change rather than this bullet of it. A reader looking up what `bot.rs` owns would find nothing where every other module is described.
Admits-answer-precondition: `module-map-check` refuses a `crates/batten/src/*.rs` module with no row in `.serena/memories/core.md`, and this change adds `crates/batten/src/bot.rs`. The memory IS the module map's authority -- AGENTS.md points there for the per-module layout precisely so the tree is not restated in a budgeted file -- so there is no other surface a row could go on. The write was made through Serena's `edit_memory`, which is the sanctioned route, and it is one added bullet visible in this pull request's diff.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because `.serena/memories/core.md` IS the owning surface for the module map; AGENTS.md defers to it by name and `module-map-check` reads it as the authority. R-RESTORE-IT does not apply because the write is one additive bullet describing a module this same commit introduces -- restoring the file would leave the map missing a row the gate demands.
Admits: cbb7e7b9a66ad1ec32b1d2e5f2df9e67efc920620236ceb72e667aa6e772fdc7
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: policy/spawn-adapters.rego
Admits-head: 6da96ce
Admits-epoch: d8992f36146d4a8f3bd0250644ab0624b29c8cc7abd4b5640b4c4aa2c6c5efe6
Admits-author: alec@wenzowski.com
Admits-prev: -
Admits-answer-lost: The rebase cannot complete, so the retirement cannot land at all. Taking `main`'s side alone leaves `bot` unplaced and `spawn-adapters` refuses `crates/batten/src/bot.rs`; taking this branch's side alone deletes `mutate` and reintroduces the refusal on the module `main` just landed.
Admits-answer-precondition: This is a REBASE CONFLICT RESOLUTION, and both sides of it are additions to the same set literal. `main` placed `mutate` when it retired `mise-tasks/mutant.sh`; this branch places `bot` when it retires `mise-tasks/bot-issue.sh`. Neither adapter can be dropped -- `spawn-adapters` refuses a spawn in a module the table has not placed, so losing either side reintroduces the refusal the other change already answered. The resolution is the union of the two rows plus both reasons, which is what the file would hold had the two landed in sequence rather than concurrently. There is no other surface: the adapter table IS this module.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because `policy/spawn-adapters.rego` IS the owning surface for the adapter placement table; the set literal cannot be declared anywhere else. R-RESTORE-IT does not apply because restoring the file to either side is precisely what loses one of the two placements -- there is no version of this file that carries both without this write.
…eceipt
486 lines of shell and 323 of bats, six subcommands, all of it GitHub API plus
one local receipt write. `mise-tasks/bot-issue.sh` and `tests/bot-issue.bats`
are deleted; the predicates land as `crates/batten/src/bot.rs` and six new
leaves — `batten pr derive|file|link|ensure|closes` and `batten claim bot`.
TWO HALVES, KEPT APART INSIDE ONE MODULE. The predicates — is this pull request
one of the lane's, which manifests it touched, what Conventional type its
subject declares, whether a body still CLOSES a key rather than merely naming
one — are pure functions with their own unit tier. The `forge` half underneath
is the only thing that talks to anybody.
THE CONSUMER FACTS ARE `[bot_lane]` IN batten.toml, not literals in the crate.
Which repository, which bot logins, which manifests the lane owns, the marker
strings, the tracker's key prefix and the branch prefix — every value the
retired program spelled internally is a config row now, so a grep of
`crates/batten` for a bot's name or a manifest path returns nothing
(non-negotiable rule 1). `document_facts` caught the first draft's doc comment
naming a workflow glob as an example, and that is the gate working. The derived
row's body is a tracked template with `{{...}}` placeholders rather than a page
of markdown inside a TOML value; an unfilled placeholder refuses at
substitution rather than rendering a template artifact into a tracker row.
THE FORGE'S OWN CLIENT, NOT THIS CRATE'S HTTP TRANSPORT. `pr_watch` is the
landed precedent (CLOUD-1143) and `gh` resolves the credential outside the
crate, where `fetch` would have put token resolution inside it next to no
config row that declares one. `policy/spawn-adapters.rego` places `bot` on
`pr_watch`'s own argument. The commit that widened `fetch::Call` for a PATCH
this port turned out not to need is reverted earlier on this branch.
THE EXIT TABLE CHANGED AND THE WORKFLOW FOLLOWS IT. `bot-issue` used 1 for
"refused, not this lane's" and 2 for "could not look"; house style §7 makes a
refusal 2 and a could-not-look 3. `auto-bot-land.yml`'s two steps are repointed
and their `case` arms moved with them — reading 1 as an ordinary refusal there
would have treated a usage error as a pass.
THE THIRD RECEIPT KIND IS WIRED. `verify` now tries `claim`, then `bot`, then
`carry`, so a licence-carry branch no longer needs the fake `--takeover` claim
`DO-NOT-CLOSE CLOUD-1213`, without which every carry pull request stalls on
`closing-key-check` exactly as #806 did and needs a human to patch it.
WHAT IS NOT DONE, AND WHY IT IS FILED RATHER THAN FIXED. The plan called for a
`checks = ["carry"]` row beside `claim-needs-receipt`. That row is wrong: a
receipt rule's `checks` is a CONJUNCTION, so a second row is a second AND and
would deny every ordinary write on every branch. `verify`'s shell body can
express the disjunction and the mediated gate cannot — a live false positive
for the bot receipt since CLOUD-693, which this change's third kind inherits.
CLOUD-1297 owns it, with the disjunction as its §2.
TWO GATES CAUGHT REAL DEFECTS IN THIS CHANGE AND BOTH ARE FIXED HERE.
`no-appeal-to-authority` refused `carry.rs` and its tier for naming a real
action repository as fixture data — a third party's name in the crate, which is
the class the rule exists for, and the fixtures now use neutral names.
`module-layering` refused both new modules as unplaced, which is the coverage
clause working: `bot` and `carry` are placed with their reasons.
ONE EDIT IS DECLINED RATHER THAN FORCED, and CLOUD-1299 owns it. Retiring the
program makes `verify`'s no-receipt remedy name a task that no longer exists —
but `tests/verify.bats` asserts that message contains `bot-issue receipt`, and a
governed suite has two landable shapes, neither of which is an edit.
`repoints_at_the_declared_invocation` cannot admit it either: it requires the
replaced span to be a PATH reference, and a task name plus a subcommand is not
one. So the remedy names the live verb FIRST and keeps the old name as the thing
it replaced — true, useful mid-transition, and satisfying the pin — with the
reason at the site rather than left to read as a slip.
THE LEDGER. Two file arms and 22 case arms, `kind:verb` on each because the
lane needs stdin, spawns with its own arguments and performs writes — none of
which a tree-scoped module may do. `bot-issue` leaves `$MUTANT_GATES` and needs
no `#MUTANT-EXEMPT`: the census enumerates `mise-tasks/*.sh` and `policy/*.rego`
— `mutate::subjects`, since `main` retired `mutant-census` onto the engine under
this branch — so a deleted program is not in the census at all. The arm
carries a `runs:` field naming `batten claim bot` — ONE arm per deleted path,
which `V-RETIREMENT-AMBIGUOUS` requires, so the field names the only invocation
a GOVERNED caller loses; the lander's two workflow steps are ungoverned and
their repoints are free. Its two
declared mutations are re-homed as named cases —
`idempotence_a_second_call_on_the_same_pr_files_nothing` and
`a_key_named_but_not_closed_is_refused` — each carrying the mutation it stands
for in its comment.
THE LOAD-BEARING CASE IS REPLAYED, NOT ASSERTED. The dying suite ran the REAL
`ready-lint` over the REAL derived payload, because "the derived block is
checkable by the same gate that checks a human's" is the claim that makes a
mechanical row honest. `the_derived_block_passes_ready_lint` does the same over
this repository's committed template and its committed `[[pattern]]` grammar —
and it earned its place immediately, catching that `prettier` reshapes the
template's bullets on every `mise run fmt`.
A KNOWN LAG, STATED RATHER THAN HIDDEN. `auto-bot-land.yml` installs `batten`
from the latest RELEASE, so its two repointed steps fail loudly with "command
not found" between this merge and the next release. That is the same gap
CLOUD-1143 recorded for `checks-green`, and it fails the job rather than
passing silently.
126 suites after the deletion, 561.9s serial on this container, regenerated on
the rebased base rather than the one this change was written against — `main`
retired several more suites in between, `mutant`, `mutant-census` and
`session-start` among them, and a corpus derived from an older report publishes a
cost for a suite that is gone. The saving this change can claim is
`tests/bot-issue.bats`' own 2.9s share of the corpus it was measured in, 0.3%;
the totals differ by far more across runs, and that is those other retirements
plus machine noise rather than anything this branch did.
Refs: CLOUD-1295
Refs: CLOUD-1164
Refs: CLOUD-1213
Refs: CLOUD-1297
Refs: CLOUD-1299
Admits: 1c68cb4a5d20efa873c0ed2b7564b7cfdf53c0b6ebb9c79e211eea4b8445bcc5
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: batten.toml
Admits-head: a11e917
Admits-epoch: 650a76ba70a8b5645ffc58aedb1bdde72394e39026b5c1391a926934ea49702c
Admits-author: alec@wenzowski.com
Admits-prev: c7d8beede76f452a09d1a6c47a615b728f2876a60e055ef065975a27903f003f
Admits-answer-lost: mise-tasks/bot-issue.sh stays alive as a second implementation of predicates the engine now owns, which is the state policy/shell-retirement.rego's V-SHELL-RULE-EDITED exists to end -- the ported verbs cannot read a lane nobody declared, so refusing this write refuses the retirement rather than this line of it.
Admits-answer-precondition: The bot lane's consumer facts -- which forge repository, which bot logins, which manifests the lane owns, the marker strings, the tracker's key prefix and the branch prefix -- are exactly the values non-negotiable rule 1 forbids in crates/batten. Retiring mise-tasks/bot-issue.sh onto `batten pr derive|file|link|ensure|closes` and `batten claim bot` moves the MATCHER into the engine and leaves those facts here, so batten.toml is the owning surface for them and no other surface can carry them. The write is one additive [bot_lane] block, visible in this pull request's own diff, which is where reviewers read it.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because batten.toml IS the owning surface for a consumer's policy facts; there is no other surface that owns them. A local override cannot carry them either: adding a login to `bots` or a path to `owned_manifests` turns a refusal into a filed row, which is a weakening dressed as an addition and house style section 8's raise-only rule does not admit it. R-RESTORE-IT does not apply because the write is additive and deliberate rather than an accident to undo -- restoring the file would delete the table the ported verbs read.
Admits: ddd46be8d9ae384d97c38556832349bfc8dc319895761edb2255894b821b3763
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: .github/workflows/auto-bot-land.yml
Admits-head: a11e917
Admits-epoch: 8e6d5f2f19b383997365f6b72ff52816d72c7029208dbb81a40389acbd65e67f
Admits-author: alec@wenzowski.com
Admits-prev: -
Admits-answer-lost: The bot lane's lander breaks outright at its next tick: both steps call a task the retirement removed, so no bot pull request gets a row and none is landed. The alternative is keeping mise-tasks/bot-issue.sh alive as a second implementation, which is the state V-SHELL-RULE-EDITED exists to end.
Admits-answer-precondition: auto-bot-land.yml invokes `mise run bot-issue ensure` and `mise run bot-issue closes`, and this change deletes mise-tasks/bot-issue.sh. Leaving the calls would leave the lander invoking a task that no longer exists, so the workflow is the owning surface for its own steps and there is nowhere else the repoint could be made. The write is two invocation repoints -- `batten pr ensure` and `batten pr closes` -- plus the comment naming the retired program, and nothing about what the workflow decides changes.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because .github/workflows/auto-bot-land.yml IS the owning surface for the lander's own steps; a workflow's run lines cannot be declared anywhere else. R-RESTORE-IT does not apply because the change is a deliberate repoint at a successor this same commit lands, and restoring the file would leave a workflow calling a deleted program.
Admits: a3bb7b559f5118c0fe2cc4e4c8e2336e2bcbf1262a0df2578070e44446a20751
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: .github/workflows/sbom-actions-currency.yml
Admits-head: a11e917
Admits-epoch: 8e6d5f2f19b383997365f6b72ff52816d72c7029208dbb81a40389acbd65e67f
Admits-author: alec@wenzowski.com
Admits-prev: e03b883be1a24354b6eaeedb59a2c39f16052d832ef6dde2ceab292ca964fa5b
Admits-answer-lost: Every carry pull request `sbom-actions-currency` opens stalls on `closing-key-check` exactly as #806 did, and each one needs a human to patch its body before it can land -- which is the manual step CLOUD-1213 exists to remove, on the lane whose blockage this whole change is about.
Admits-answer-precondition: The generated pull request body carries `Refs: CLOUD-1213` and no closing marker, so `closing-key-check` reads it as a body naming a key non-closingly and stalls the landing. Measured on #806, where the body was patched by hand to get it through -- the defect repeating on every future carry pull request. The body is generated by this workflow's own `gh pr create` step, so the workflow is the owning surface for it and no other surface can set it. The write is one added line, `DO-NOT-CLOSE CLOUD-1213`, in the same printf list, visible in this pull request's diff.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because .github/workflows/sbom-actions-currency.yml IS the owning surface for the body it generates; a workflow's own `gh pr create` arguments cannot be declared anywhere else. R-RESTORE-IT does not apply because the change is a deliberate one-line addition rather than an accident to undo, and restoring the file would put back the body that stalls.
Admits: 1588b760e38a64a36f5aaef031bd5b3e8f5e1160c0f94eec486b41dbca5d335c
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: .serena/memories/core.md
Admits-head: a11e917
Admits-epoch: a5e521241d3b4c861cd0ffb0813ac2c5eb0bfd1b3b364e13418f67888e3de545
Admits-author: alec@wenzowski.com
Admits-prev: 121e5c62ce26f6caa46a19393e76ae86d200b8b113fccdaf2bb1196df372865b
Admits-answer-lost: `bot.rs` lands with no map row, `module-map-check` fails the gate, and the retirement cannot commit at all -- so refusing this write refuses the whole change rather than this bullet of it. A reader looking up what `bot.rs` owns would find nothing where every other module is described.
Admits-answer-precondition: `module-map-check` refuses a `crates/batten/src/*.rs` module with no row in `.serena/memories/core.md`, and this change adds `crates/batten/src/bot.rs`. The memory IS the module map's authority -- AGENTS.md points there for the per-module layout precisely so the tree is not restated in a budgeted file -- so there is no other surface a row could go on. The write was made through Serena's `edit_memory`, which is the sanctioned route, and it is one added bullet visible in this pull request's diff.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because `.serena/memories/core.md` IS the owning surface for the module map; AGENTS.md defers to it by name and `module-map-check` reads it as the authority. R-RESTORE-IT does not apply because the write is one additive bullet describing a module this same commit introduces -- restoring the file would leave the map missing a row the gate demands.
Admits: cbb7e7b9a66ad1ec32b1d2e5f2df9e67efc920620236ceb72e667aa6e772fdc7
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: policy/spawn-adapters.rego
Admits-head: 6da96ce
Admits-epoch: d8992f36146d4a8f3bd0250644ab0624b29c8cc7abd4b5640b4c4aa2c6c5efe6
Admits-author: alec@wenzowski.com
Admits-prev: -
Admits-answer-lost: The rebase cannot complete, so the retirement cannot land at all. Taking `main`'s side alone leaves `bot` unplaced and `spawn-adapters` refuses `crates/batten/src/bot.rs`; taking this branch's side alone deletes `mutate` and reintroduces the refusal on the module `main` just landed.
Admits-answer-precondition: This is a REBASE CONFLICT RESOLUTION, and both sides of it are additions to the same set literal. `main` placed `mutate` when it retired `mise-tasks/mutant.sh`; this branch places `bot` when it retires `mise-tasks/bot-issue.sh`. Neither adapter can be dropped -- `spawn-adapters` refuses a spawn in a module the table has not placed, so losing either side reintroduces the refusal the other change already answered. The resolution is the union of the two rows plus both reasons, which is what the file would hold had the two landed in sequence rather than concurrently. There is no other surface: the adapter table IS this module.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because `policy/spawn-adapters.rego` IS the owning surface for the adapter placement table; the set literal cannot be declared anywhere else. R-RESTORE-IT does not apply because restoring the file to either side is precisely what loses one of the two placements -- there is no version of this file that carries both without this write.
… claim `sbom-actions-currency` opens a PR that `verify` refuses: there is no claim receipt, and the agent one attests a refinement no bot performed. #806 landed with a `--takeover` claim against CLOUD-1213, which was a fake claim, and every future carry PR meets the same wall. `batten claim carry` is the honest receipt. It attests DERIVABILITY rather than a branch name — a prefix exemption would be a password wearing a branch name — so it mints only when the branch appends rows whose repo the base table already maps, changing the sha alone, and touches nothing else. The verb takes no argument on purpose: the subject is the branch's own diff against its merge base, and a caller that could name its own subject could name one that is derivable while changing something else. Also repairs the `snapshots` task, stale since the integration targets moved under `tests/it/`. Refs: CLOUD-1295
…eceipt
486 lines of shell and 323 of bats, six subcommands, all of it GitHub API plus
one local receipt write. `mise-tasks/bot-issue.sh` and `tests/bot-issue.bats`
are deleted; the predicates land as `crates/batten/src/bot.rs` and six new
leaves — `batten pr derive|file|link|ensure|closes` and `batten claim bot`.
TWO HALVES, KEPT APART INSIDE ONE MODULE. The predicates — is this pull request
one of the lane's, which manifests it touched, what Conventional type its
subject declares, whether a body still CLOSES a key rather than merely naming
one — are pure functions with their own unit tier. The `forge` half underneath
is the only thing that talks to anybody.
THE CONSUMER FACTS ARE `[bot_lane]` IN batten.toml, not literals in the crate.
Which repository, which bot logins, which manifests the lane owns, the marker
strings, the tracker's key prefix and the branch prefix — every value the
retired program spelled internally is a config row now, so a grep of
`crates/batten` for a bot's name or a manifest path returns nothing
(non-negotiable rule 1). `document_facts` caught the first draft's doc comment
naming a workflow glob as an example, and that is the gate working. The derived
row's body is a tracked template with `{{...}}` placeholders rather than a page
of markdown inside a TOML value; an unfilled placeholder refuses at
substitution rather than rendering a template artifact into a tracker row.
THE FORGE'S OWN CLIENT, NOT THIS CRATE'S HTTP TRANSPORT. `pr_watch` is the
landed precedent (CLOUD-1143) and `gh` resolves the credential outside the
crate, where `fetch` would have put token resolution inside it next to no
config row that declares one. `policy/spawn-adapters.rego` places `bot` on
`pr_watch`'s own argument. The commit that widened `fetch::Call` for a PATCH
this port turned out not to need is reverted earlier on this branch.
THE EXIT TABLE CHANGED AND THE WORKFLOW FOLLOWS IT. `bot-issue` used 1 for
"refused, not this lane's" and 2 for "could not look"; house style §7 makes a
refusal 2 and a could-not-look 3. `auto-bot-land.yml`'s two steps are repointed
and their `case` arms moved with them — reading 1 as an ordinary refusal there
would have treated a usage error as a pass.
THE THIRD RECEIPT KIND IS WIRED. `verify` now tries `claim`, then `bot`, then
`carry`, so a licence-carry branch no longer needs the fake `--takeover` claim
`DO-NOT-CLOSE CLOUD-1213`, without which every carry pull request stalls on
`closing-key-check` exactly as #806 did and needs a human to patch it.
WHAT IS NOT DONE, AND WHY IT IS FILED RATHER THAN FIXED. The plan called for a
`checks = ["carry"]` row beside `claim-needs-receipt`. That row is wrong: a
receipt rule's `checks` is a CONJUNCTION, so a second row is a second AND and
would deny every ordinary write on every branch. `verify`'s shell body can
express the disjunction and the mediated gate cannot — a live false positive
for the bot receipt since CLOUD-693, which this change's third kind inherits.
CLOUD-1297 owns it, with the disjunction as its §2.
TWO GATES CAUGHT REAL DEFECTS IN THIS CHANGE AND BOTH ARE FIXED HERE.
`no-appeal-to-authority` refused `carry.rs` and its tier for naming a real
action repository as fixture data — a third party's name in the crate, which is
the class the rule exists for, and the fixtures now use neutral names.
`module-layering` refused both new modules as unplaced, which is the coverage
clause working: `bot` and `carry` are placed with their reasons.
ONE EDIT IS DECLINED RATHER THAN FORCED, and CLOUD-1299 owns it. Retiring the
program makes `verify`'s no-receipt remedy name a task that no longer exists —
but `tests/verify.bats` asserts that message contains `bot-issue receipt`, and a
governed suite has two landable shapes, neither of which is an edit.
`repoints_at_the_declared_invocation` cannot admit it either: it requires the
replaced span to be a PATH reference, and a task name plus a subcommand is not
one. So the remedy names the live verb FIRST and keeps the old name as the thing
it replaced — true, useful mid-transition, and satisfying the pin — with the
reason at the site rather than left to read as a slip.
THE LEDGER. Two file arms and 22 case arms, `kind:verb` on each because the
lane needs stdin, spawns with its own arguments and performs writes — none of
which a tree-scoped module may do. `bot-issue` leaves `$MUTANT_GATES` and needs
no `#MUTANT-EXEMPT`: the census enumerates `mise-tasks/*.sh` and `policy/*.rego`
— `mutate::subjects`, since `main` retired `mutant-census` onto the engine under
this branch — so a deleted program is not in the census at all. The arm
carries a `runs:` field naming `batten claim bot` — ONE arm per deleted path,
which `V-RETIREMENT-AMBIGUOUS` requires, so the field names the only invocation
a GOVERNED caller loses; the lander's two workflow steps are ungoverned and
their repoints are free. Its two
declared mutations are re-homed as named cases —
`idempotence_a_second_call_on_the_same_pr_files_nothing` and
`a_key_named_but_not_closed_is_refused` — each carrying the mutation it stands
for in its comment.
THE LOAD-BEARING CASE IS REPLAYED, NOT ASSERTED. The dying suite ran the REAL
`ready-lint` over the REAL derived payload, because "the derived block is
checkable by the same gate that checks a human's" is the claim that makes a
mechanical row honest. `the_derived_block_passes_ready_lint` does the same over
this repository's committed template and its committed `[[pattern]]` grammar —
and it earned its place immediately, catching that `prettier` reshapes the
template's bullets on every `mise run fmt`.
A KNOWN LAG, STATED RATHER THAN HIDDEN. `auto-bot-land.yml` installs `batten`
from the latest RELEASE, so its two repointed steps fail loudly with "command
not found" between this merge and the next release. That is the same gap
CLOUD-1143 recorded for `checks-green`, and it fails the job rather than
passing silently.
126 suites after the deletion, 561.9s serial on this container, regenerated on
the rebased base rather than the one this change was written against — `main`
retired several more suites in between, `mutant`, `mutant-census` and
`session-start` among them, and a corpus derived from an older report publishes a
cost for a suite that is gone. The saving this change can claim is
`tests/bot-issue.bats`' own 2.9s share of the corpus it was measured in, 0.3%;
the totals differ by far more across runs, and that is those other retirements
plus machine noise rather than anything this branch did.
Refs: CLOUD-1295
Refs: CLOUD-1164
Refs: CLOUD-1213
Refs: CLOUD-1297
Refs: CLOUD-1299
Admits: 1c68cb4a5d20efa873c0ed2b7564b7cfdf53c0b6ebb9c79e211eea4b8445bcc5
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: batten.toml
Admits-head: a11e917
Admits-epoch: 650a76ba70a8b5645ffc58aedb1bdde72394e39026b5c1391a926934ea49702c
Admits-author: alec@wenzowski.com
Admits-prev: c7d8beede76f452a09d1a6c47a615b728f2876a60e055ef065975a27903f003f
Admits-answer-lost: mise-tasks/bot-issue.sh stays alive as a second implementation of predicates the engine now owns, which is the state policy/shell-retirement.rego's V-SHELL-RULE-EDITED exists to end -- the ported verbs cannot read a lane nobody declared, so refusing this write refuses the retirement rather than this line of it.
Admits-answer-precondition: The bot lane's consumer facts -- which forge repository, which bot logins, which manifests the lane owns, the marker strings, the tracker's key prefix and the branch prefix -- are exactly the values non-negotiable rule 1 forbids in crates/batten. Retiring mise-tasks/bot-issue.sh onto `batten pr derive|file|link|ensure|closes` and `batten claim bot` moves the MATCHER into the engine and leaves those facts here, so batten.toml is the owning surface for them and no other surface can carry them. The write is one additive [bot_lane] block, visible in this pull request's own diff, which is where reviewers read it.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because batten.toml IS the owning surface for a consumer's policy facts; there is no other surface that owns them. A local override cannot carry them either: adding a login to `bots` or a path to `owned_manifests` turns a refusal into a filed row, which is a weakening dressed as an addition and house style section 8's raise-only rule does not admit it. R-RESTORE-IT does not apply because the write is additive and deliberate rather than an accident to undo -- restoring the file would delete the table the ported verbs read.
Admits: ddd46be8d9ae384d97c38556832349bfc8dc319895761edb2255894b821b3763
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: .github/workflows/auto-bot-land.yml
Admits-head: a11e917
Admits-epoch: 8e6d5f2f19b383997365f6b72ff52816d72c7029208dbb81a40389acbd65e67f
Admits-author: alec@wenzowski.com
Admits-prev: -
Admits-answer-lost: The bot lane's lander breaks outright at its next tick: both steps call a task the retirement removed, so no bot pull request gets a row and none is landed. The alternative is keeping mise-tasks/bot-issue.sh alive as a second implementation, which is the state V-SHELL-RULE-EDITED exists to end.
Admits-answer-precondition: auto-bot-land.yml invokes `mise run bot-issue ensure` and `mise run bot-issue closes`, and this change deletes mise-tasks/bot-issue.sh. Leaving the calls would leave the lander invoking a task that no longer exists, so the workflow is the owning surface for its own steps and there is nowhere else the repoint could be made. The write is two invocation repoints -- `batten pr ensure` and `batten pr closes` -- plus the comment naming the retired program, and nothing about what the workflow decides changes.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because .github/workflows/auto-bot-land.yml IS the owning surface for the lander's own steps; a workflow's run lines cannot be declared anywhere else. R-RESTORE-IT does not apply because the change is a deliberate repoint at a successor this same commit lands, and restoring the file would leave a workflow calling a deleted program.
Admits: a3bb7b559f5118c0fe2cc4e4c8e2336e2bcbf1262a0df2578070e44446a20751
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: .github/workflows/sbom-actions-currency.yml
Admits-head: a11e917
Admits-epoch: 8e6d5f2f19b383997365f6b72ff52816d72c7029208dbb81a40389acbd65e67f
Admits-author: alec@wenzowski.com
Admits-prev: e03b883be1a24354b6eaeedb59a2c39f16052d832ef6dde2ceab292ca964fa5b
Admits-answer-lost: Every carry pull request `sbom-actions-currency` opens stalls on `closing-key-check` exactly as #806 did, and each one needs a human to patch its body before it can land -- which is the manual step CLOUD-1213 exists to remove, on the lane whose blockage this whole change is about.
Admits-answer-precondition: The generated pull request body carries `Refs: CLOUD-1213` and no closing marker, so `closing-key-check` reads it as a body naming a key non-closingly and stalls the landing. Measured on #806, where the body was patched by hand to get it through -- the defect repeating on every future carry pull request. The body is generated by this workflow's own `gh pr create` step, so the workflow is the owning surface for it and no other surface can set it. The write is one added line, `DO-NOT-CLOSE CLOUD-1213`, in the same printf list, visible in this pull request's diff.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because .github/workflows/sbom-actions-currency.yml IS the owning surface for the body it generates; a workflow's own `gh pr create` arguments cannot be declared anywhere else. R-RESTORE-IT does not apply because the change is a deliberate one-line addition rather than an accident to undo, and restoring the file would put back the body that stalls.
Admits: 1588b760e38a64a36f5aaef031bd5b3e8f5e1160c0f94eec486b41dbca5d335c
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: .serena/memories/core.md
Admits-head: a11e917
Admits-epoch: a5e521241d3b4c861cd0ffb0813ac2c5eb0bfd1b3b364e13418f67888e3de545
Admits-author: alec@wenzowski.com
Admits-prev: 121e5c62ce26f6caa46a19393e76ae86d200b8b113fccdaf2bb1196df372865b
Admits-answer-lost: `bot.rs` lands with no map row, `module-map-check` fails the gate, and the retirement cannot commit at all -- so refusing this write refuses the whole change rather than this bullet of it. A reader looking up what `bot.rs` owns would find nothing where every other module is described.
Admits-answer-precondition: `module-map-check` refuses a `crates/batten/src/*.rs` module with no row in `.serena/memories/core.md`, and this change adds `crates/batten/src/bot.rs`. The memory IS the module map's authority -- AGENTS.md points there for the per-module layout precisely so the tree is not restated in a budgeted file -- so there is no other surface a row could go on. The write was made through Serena's `edit_memory`, which is the sanctioned route, and it is one added bullet visible in this pull request's diff.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because `.serena/memories/core.md` IS the owning surface for the module map; AGENTS.md defers to it by name and `module-map-check` reads it as the authority. R-RESTORE-IT does not apply because the write is one additive bullet describing a module this same commit introduces -- restoring the file would leave the map missing a row the gate demands.
Admits: cbb7e7b9a66ad1ec32b1d2e5f2df9e67efc920620236ceb72e667aa6e772fdc7
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: policy/spawn-adapters.rego
Admits-head: 6da96ce
Admits-epoch: d8992f36146d4a8f3bd0250644ab0624b29c8cc7abd4b5640b4c4aa2c6c5efe6
Admits-author: alec@wenzowski.com
Admits-prev: -
Admits-answer-lost: The rebase cannot complete, so the retirement cannot land at all. Taking `main`'s side alone leaves `bot` unplaced and `spawn-adapters` refuses `crates/batten/src/bot.rs`; taking this branch's side alone deletes `mutate` and reintroduces the refusal on the module `main` just landed.
Admits-answer-precondition: This is a REBASE CONFLICT RESOLUTION, and both sides of it are additions to the same set literal. `main` placed `mutate` when it retired `mise-tasks/mutant.sh`; this branch places `bot` when it retires `mise-tasks/bot-issue.sh`. Neither adapter can be dropped -- `spawn-adapters` refuses a spawn in a module the table has not placed, so losing either side reintroduces the refusal the other change already answered. The resolution is the union of the two rows plus both reasons, which is what the file would hold had the two landed in sequence rather than concurrently. There is no other surface: the adapter table IS this module.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because `policy/spawn-adapters.rego` IS the owning surface for the adapter placement table; the set literal cannot be declared anywhere else. R-RESTORE-IT does not apply because restoring the file to either side is precisely what loses one of the two placements -- there is no version of this file that carries both without this write.
CLOUD-1213's first acceptance clause. `auto-bot-land` merged only `renovate/**`, so every carry PR needed a person — which is what produced #806's fake `--takeover` claim, the defect CLOUD-1295 exists to remove. THE PAIRING IS THE POINT. The two lanes have different authors: Renovate opens its own PRs, and the carry lane is opened by `sbom-actions-currency` under the default token. A single `BOT_LOGIN` no longer answers, and the lazy widening — any known prefix against any known login — would admit `renovate[bot]` on a `sbom-actions/` branch and this workflow's own token on a `renovate/` one, neither of which either lane can produce. `LANES` is therefore a table of PAIRS and a PR must satisfy one row whole. CLOUD-867's origin test is untouched: `head_repository.full_name` is still what a fork cannot forge, still checked separately, and the prefix stays a filter rather than the trust boundary. Driven over a six-PR fixture before landing, and it caught a real bug: the first spelling read `.prefix` inside a pipe where `.` had already rebound to the branch string, which `jq` refuses at runtime — a workflow that would have errored on its first tick. With the lane bound, both lanes are admitted, both cross-pairings are refused, a fork-headed PR is refused and a human's branch is refused. What still gates a carry PR is unchanged and is not review: the full required check set must be green, and `batten claim carry` bounds what the branch may contain — only the licence table differs, every added row names a repo the base already maps with an identical licence and holder so only the sha moves, and no other tracked path differs from the merge base. Admits: b93a99d9da1523b9d9bd398f5b8c302d989357d8214bffe6e09c5e6aa30e9d12 Admits-rule: protected-mutation Admits-verdict: path write refused Admits-subject: .github/workflows/auto-bot-land.yml Admits-head: 0e089a9 Admits-epoch: 25c305c35f01ee4c99bceeacd1efc47509463adf09d3e5923196f59089cc2d98 Admits-author: alec@wenzowski.com Admits-prev: ddd46be8d9ae384d97c38556832349bfc8dc319895761edb2255894b821b3763 Admits-answer-lost: CLOUD-1213's first acceptance clause stays open and every licence-carry PR keeps needing a person to land it — which is what produced #806's fake `--takeover` claim, the defect CLOUD-1295 exists to remove. The lane can now be landed honestly either way, so what is lost is the automation rather than the correctness; the human asked for the automation explicitly. Admits-answer-precondition: The change extends this lane's on-green auto-merge from `renovate/**` to `sbom-actions/**`, which is a workflow trigger scope, a job `if:` and the resolve step's author test — all of them inside this file and expressible nowhere else. No verb writes a workflow, and the branch scope has to sit on the trigger rather than only in the `if:` for the reason this file already records (CLOUD-493: a job condition is evaluated after the run exists, so 1131 runs in 25 hours were created only to skip). It lands in a pull request, which is what the class asks for, and the diff is what a reviewer reads to see exactly which branches can now merge unattended. Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE is rejected because a GitHub Actions workflow IS its own owning surface — there is no generator and no config table that emits this file. R-RESTORE-IT is rejected because restoring the committed bytes restores the single-lane scope, which is the thing being changed rather than a fault to undo. Refs: CLOUD-1213, CLOUD-1295, CLOUD-867
… claim `sbom-actions-currency` opens a PR that `verify` refuses: there is no claim receipt, and the agent one attests a refinement no bot performed. #806 landed with a `--takeover` claim against CLOUD-1213, which was a fake claim, and every future carry PR meets the same wall. `batten claim carry` is the honest receipt. It attests DERIVABILITY rather than a branch name — a prefix exemption would be a password wearing a branch name — so it mints only when the branch appends rows whose repo the base table already maps, changing the sha alone, and touches nothing else. The verb takes no argument on purpose: the subject is the branch's own diff against its merge base, and a caller that could name its own subject could name one that is derivable while changing something else. Also repairs the `snapshots` task, stale since the integration targets moved under `tests/it/`. Refs: CLOUD-1295
…eceipt
486 lines of shell and 323 of bats, six subcommands, all of it GitHub API plus
one local receipt write. `mise-tasks/bot-issue.sh` and `tests/bot-issue.bats`
are deleted; the predicates land as `crates/batten/src/bot.rs` and six new
leaves — `batten pr derive|file|link|ensure|closes` and `batten claim bot`.
TWO HALVES, KEPT APART INSIDE ONE MODULE. The predicates — is this pull request
one of the lane's, which manifests it touched, what Conventional type its
subject declares, whether a body still CLOSES a key rather than merely naming
one — are pure functions with their own unit tier. The `forge` half underneath
is the only thing that talks to anybody.
THE CONSUMER FACTS ARE `[bot_lane]` IN batten.toml, not literals in the crate.
Which repository, which bot logins, which manifests the lane owns, the marker
strings, the tracker's key prefix and the branch prefix — every value the
retired program spelled internally is a config row now, so a grep of
`crates/batten` for a bot's name or a manifest path returns nothing
(non-negotiable rule 1). `document_facts` caught the first draft's doc comment
naming a workflow glob as an example, and that is the gate working. The derived
row's body is a tracked template with `{{...}}` placeholders rather than a page
of markdown inside a TOML value; an unfilled placeholder refuses at
substitution rather than rendering a template artifact into a tracker row.
THE FORGE'S OWN CLIENT, NOT THIS CRATE'S HTTP TRANSPORT. `pr_watch` is the
landed precedent (CLOUD-1143) and `gh` resolves the credential outside the
crate, where `fetch` would have put token resolution inside it next to no
config row that declares one. `policy/spawn-adapters.rego` places `bot` on
`pr_watch`'s own argument. The commit that widened `fetch::Call` for a PATCH
this port turned out not to need is reverted earlier on this branch.
THE EXIT TABLE CHANGED AND THE WORKFLOW FOLLOWS IT. `bot-issue` used 1 for
"refused, not this lane's" and 2 for "could not look"; house style §7 makes a
refusal 2 and a could-not-look 3. `auto-bot-land.yml`'s two steps are repointed
and their `case` arms moved with them — reading 1 as an ordinary refusal there
would have treated a usage error as a pass.
THE THIRD RECEIPT KIND IS WIRED. `verify` now tries `claim`, then `bot`, then
`carry`, so a licence-carry branch no longer needs the fake `--takeover` claim
`DO-NOT-CLOSE CLOUD-1213`, without which every carry pull request stalls on
`closing-key-check` exactly as #806 did and needs a human to patch it.
WHAT IS NOT DONE, AND WHY IT IS FILED RATHER THAN FIXED. The plan called for a
`checks = ["carry"]` row beside `claim-needs-receipt`. That row is wrong: a
receipt rule's `checks` is a CONJUNCTION, so a second row is a second AND and
would deny every ordinary write on every branch. `verify`'s shell body can
express the disjunction and the mediated gate cannot — a live false positive
for the bot receipt since CLOUD-693, which this change's third kind inherits.
CLOUD-1297 owns it, with the disjunction as its §2.
TWO GATES CAUGHT REAL DEFECTS IN THIS CHANGE AND BOTH ARE FIXED HERE.
`no-appeal-to-authority` refused `carry.rs` and its tier for naming a real
action repository as fixture data — a third party's name in the crate, which is
the class the rule exists for, and the fixtures now use neutral names.
`module-layering` refused both new modules as unplaced, which is the coverage
clause working: `bot` and `carry` are placed with their reasons.
ONE EDIT IS DECLINED RATHER THAN FORCED, and CLOUD-1299 owns it. Retiring the
program makes `verify`'s no-receipt remedy name a task that no longer exists —
but `tests/verify.bats` asserts that message contains `bot-issue receipt`, and a
governed suite has two landable shapes, neither of which is an edit.
`repoints_at_the_declared_invocation` cannot admit it either: it requires the
replaced span to be a PATH reference, and a task name plus a subcommand is not
one. So the remedy names the live verb FIRST and keeps the old name as the thing
it replaced — true, useful mid-transition, and satisfying the pin — with the
reason at the site rather than left to read as a slip.
THE LEDGER. Two file arms and 22 case arms, `kind:verb` on each because the
lane needs stdin, spawns with its own arguments and performs writes — none of
which a tree-scoped module may do. `bot-issue` leaves `$MUTANT_GATES` and needs
no `#MUTANT-EXEMPT`: the census enumerates `mise-tasks/*.sh` and `policy/*.rego`
— `mutate::subjects`, since `main` retired `mutant-census` onto the engine under
this branch — so a deleted program is not in the census at all. The arm
carries a `runs:` field naming `batten claim bot` — ONE arm per deleted path,
which `V-RETIREMENT-AMBIGUOUS` requires, so the field names the only invocation
a GOVERNED caller loses; the lander's two workflow steps are ungoverned and
their repoints are free. Its two
declared mutations are re-homed as named cases —
`idempotence_a_second_call_on_the_same_pr_files_nothing` and
`a_key_named_but_not_closed_is_refused` — each carrying the mutation it stands
for in its comment.
THE LOAD-BEARING CASE IS REPLAYED, NOT ASSERTED. The dying suite ran the REAL
`ready-lint` over the REAL derived payload, because "the derived block is
checkable by the same gate that checks a human's" is the claim that makes a
mechanical row honest. `the_derived_block_passes_ready_lint` does the same over
this repository's committed template and its committed `[[pattern]]` grammar —
and it earned its place immediately, catching that `prettier` reshapes the
template's bullets on every `mise run fmt`.
A KNOWN LAG, STATED RATHER THAN HIDDEN. `auto-bot-land.yml` installs `batten`
from the latest RELEASE, so its two repointed steps fail loudly with "command
not found" between this merge and the next release. That is the same gap
CLOUD-1143 recorded for `checks-green`, and it fails the job rather than
passing silently.
126 suites after the deletion, 561.9s serial on this container, regenerated on
the rebased base rather than the one this change was written against — `main`
retired several more suites in between, `mutant`, `mutant-census` and
`session-start` among them, and a corpus derived from an older report publishes a
cost for a suite that is gone. The saving this change can claim is
`tests/bot-issue.bats`' own 2.9s share of the corpus it was measured in, 0.3%;
the totals differ by far more across runs, and that is those other retirements
plus machine noise rather than anything this branch did.
Refs: CLOUD-1295
Refs: CLOUD-1164
Refs: CLOUD-1213
Refs: CLOUD-1297
Refs: CLOUD-1299
Admits: 1c68cb4a5d20efa873c0ed2b7564b7cfdf53c0b6ebb9c79e211eea4b8445bcc5
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: batten.toml
Admits-head: a11e917
Admits-epoch: 650a76ba70a8b5645ffc58aedb1bdde72394e39026b5c1391a926934ea49702c
Admits-author: alec@wenzowski.com
Admits-prev: c7d8beede76f452a09d1a6c47a615b728f2876a60e055ef065975a27903f003f
Admits-answer-lost: mise-tasks/bot-issue.sh stays alive as a second implementation of predicates the engine now owns, which is the state policy/shell-retirement.rego's V-SHELL-RULE-EDITED exists to end -- the ported verbs cannot read a lane nobody declared, so refusing this write refuses the retirement rather than this line of it.
Admits-answer-precondition: The bot lane's consumer facts -- which forge repository, which bot logins, which manifests the lane owns, the marker strings, the tracker's key prefix and the branch prefix -- are exactly the values non-negotiable rule 1 forbids in crates/batten. Retiring mise-tasks/bot-issue.sh onto `batten pr derive|file|link|ensure|closes` and `batten claim bot` moves the MATCHER into the engine and leaves those facts here, so batten.toml is the owning surface for them and no other surface can carry them. The write is one additive [bot_lane] block, visible in this pull request's own diff, which is where reviewers read it.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because batten.toml IS the owning surface for a consumer's policy facts; there is no other surface that owns them. A local override cannot carry them either: adding a login to `bots` or a path to `owned_manifests` turns a refusal into a filed row, which is a weakening dressed as an addition and house style section 8's raise-only rule does not admit it. R-RESTORE-IT does not apply because the write is additive and deliberate rather than an accident to undo -- restoring the file would delete the table the ported verbs read.
Admits: ddd46be8d9ae384d97c38556832349bfc8dc319895761edb2255894b821b3763
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: .github/workflows/auto-bot-land.yml
Admits-head: a11e917
Admits-epoch: 8e6d5f2f19b383997365f6b72ff52816d72c7029208dbb81a40389acbd65e67f
Admits-author: alec@wenzowski.com
Admits-prev: -
Admits-answer-lost: The bot lane's lander breaks outright at its next tick: both steps call a task the retirement removed, so no bot pull request gets a row and none is landed. The alternative is keeping mise-tasks/bot-issue.sh alive as a second implementation, which is the state V-SHELL-RULE-EDITED exists to end.
Admits-answer-precondition: auto-bot-land.yml invokes `mise run bot-issue ensure` and `mise run bot-issue closes`, and this change deletes mise-tasks/bot-issue.sh. Leaving the calls would leave the lander invoking a task that no longer exists, so the workflow is the owning surface for its own steps and there is nowhere else the repoint could be made. The write is two invocation repoints -- `batten pr ensure` and `batten pr closes` -- plus the comment naming the retired program, and nothing about what the workflow decides changes.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because .github/workflows/auto-bot-land.yml IS the owning surface for the lander's own steps; a workflow's run lines cannot be declared anywhere else. R-RESTORE-IT does not apply because the change is a deliberate repoint at a successor this same commit lands, and restoring the file would leave a workflow calling a deleted program.
Admits: a3bb7b559f5118c0fe2cc4e4c8e2336e2bcbf1262a0df2578070e44446a20751
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: .github/workflows/sbom-actions-currency.yml
Admits-head: a11e917
Admits-epoch: 8e6d5f2f19b383997365f6b72ff52816d72c7029208dbb81a40389acbd65e67f
Admits-author: alec@wenzowski.com
Admits-prev: e03b883be1a24354b6eaeedb59a2c39f16052d832ef6dde2ceab292ca964fa5b
Admits-answer-lost: Every carry pull request `sbom-actions-currency` opens stalls on `closing-key-check` exactly as #806 did, and each one needs a human to patch its body before it can land -- which is the manual step CLOUD-1213 exists to remove, on the lane whose blockage this whole change is about.
Admits-answer-precondition: The generated pull request body carries `Refs: CLOUD-1213` and no closing marker, so `closing-key-check` reads it as a body naming a key non-closingly and stalls the landing. Measured on #806, where the body was patched by hand to get it through -- the defect repeating on every future carry pull request. The body is generated by this workflow's own `gh pr create` step, so the workflow is the owning surface for it and no other surface can set it. The write is one added line, `DO-NOT-CLOSE CLOUD-1213`, in the same printf list, visible in this pull request's diff.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because .github/workflows/sbom-actions-currency.yml IS the owning surface for the body it generates; a workflow's own `gh pr create` arguments cannot be declared anywhere else. R-RESTORE-IT does not apply because the change is a deliberate one-line addition rather than an accident to undo, and restoring the file would put back the body that stalls.
Admits: 1588b760e38a64a36f5aaef031bd5b3e8f5e1160c0f94eec486b41dbca5d335c
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: .serena/memories/core.md
Admits-head: a11e917
Admits-epoch: a5e521241d3b4c861cd0ffb0813ac2c5eb0bfd1b3b364e13418f67888e3de545
Admits-author: alec@wenzowski.com
Admits-prev: 121e5c62ce26f6caa46a19393e76ae86d200b8b113fccdaf2bb1196df372865b
Admits-answer-lost: `bot.rs` lands with no map row, `module-map-check` fails the gate, and the retirement cannot commit at all -- so refusing this write refuses the whole change rather than this bullet of it. A reader looking up what `bot.rs` owns would find nothing where every other module is described.
Admits-answer-precondition: `module-map-check` refuses a `crates/batten/src/*.rs` module with no row in `.serena/memories/core.md`, and this change adds `crates/batten/src/bot.rs`. The memory IS the module map's authority -- AGENTS.md points there for the per-module layout precisely so the tree is not restated in a budgeted file -- so there is no other surface a row could go on. The write was made through Serena's `edit_memory`, which is the sanctioned route, and it is one added bullet visible in this pull request's diff.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because `.serena/memories/core.md` IS the owning surface for the module map; AGENTS.md defers to it by name and `module-map-check` reads it as the authority. R-RESTORE-IT does not apply because the write is one additive bullet describing a module this same commit introduces -- restoring the file would leave the map missing a row the gate demands.
Admits: cbb7e7b9a66ad1ec32b1d2e5f2df9e67efc920620236ceb72e667aa6e772fdc7
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: policy/spawn-adapters.rego
Admits-head: 6da96ce
Admits-epoch: d8992f36146d4a8f3bd0250644ab0624b29c8cc7abd4b5640b4c4aa2c6c5efe6
Admits-author: alec@wenzowski.com
Admits-prev: -
Admits-answer-lost: The rebase cannot complete, so the retirement cannot land at all. Taking `main`'s side alone leaves `bot` unplaced and `spawn-adapters` refuses `crates/batten/src/bot.rs`; taking this branch's side alone deletes `mutate` and reintroduces the refusal on the module `main` just landed.
Admits-answer-precondition: This is a REBASE CONFLICT RESOLUTION, and both sides of it are additions to the same set literal. `main` placed `mutate` when it retired `mise-tasks/mutant.sh`; this branch places `bot` when it retires `mise-tasks/bot-issue.sh`. Neither adapter can be dropped -- `spawn-adapters` refuses a spawn in a module the table has not placed, so losing either side reintroduces the refusal the other change already answered. The resolution is the union of the two rows plus both reasons, which is what the file would hold had the two landed in sequence rather than concurrently. There is no other surface: the adapter table IS this module.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because `policy/spawn-adapters.rego` IS the owning surface for the adapter placement table; the set literal cannot be declared anywhere else. R-RESTORE-IT does not apply because restoring the file to either side is precisely what loses one of the two placements -- there is no version of this file that carries both without this write.
CLOUD-1213's first acceptance clause. `auto-bot-land` merged only `renovate/**`, so every carry PR needed a person — which is what produced #806's fake `--takeover` claim, the defect CLOUD-1295 exists to remove. THE PAIRING IS THE POINT. The two lanes have different authors: Renovate opens its own PRs, and the carry lane is opened by `sbom-actions-currency` under the default token. A single `BOT_LOGIN` no longer answers, and the lazy widening — any known prefix against any known login — would admit `renovate[bot]` on a `sbom-actions/` branch and this workflow's own token on a `renovate/` one, neither of which either lane can produce. `LANES` is therefore a table of PAIRS and a PR must satisfy one row whole. CLOUD-867's origin test is untouched: `head_repository.full_name` is still what a fork cannot forge, still checked separately, and the prefix stays a filter rather than the trust boundary. Driven over a six-PR fixture before landing, and it caught a real bug: the first spelling read `.prefix` inside a pipe where `.` had already rebound to the branch string, which `jq` refuses at runtime — a workflow that would have errored on its first tick. With the lane bound, both lanes are admitted, both cross-pairings are refused, a fork-headed PR is refused and a human's branch is refused. What still gates a carry PR is unchanged and is not review: the full required check set must be green, and `batten claim carry` bounds what the branch may contain — only the licence table differs, every added row names a repo the base already maps with an identical licence and holder so only the sha moves, and no other tracked path differs from the merge base. Admits: b93a99d9da1523b9d9bd398f5b8c302d989357d8214bffe6e09c5e6aa30e9d12 Admits-rule: protected-mutation Admits-verdict: path write refused Admits-subject: .github/workflows/auto-bot-land.yml Admits-head: 0e089a9 Admits-epoch: 25c305c35f01ee4c99bceeacd1efc47509463adf09d3e5923196f59089cc2d98 Admits-author: alec@wenzowski.com Admits-prev: ddd46be8d9ae384d97c38556832349bfc8dc319895761edb2255894b821b3763 Admits-answer-lost: CLOUD-1213's first acceptance clause stays open and every licence-carry PR keeps needing a person to land it — which is what produced #806's fake `--takeover` claim, the defect CLOUD-1295 exists to remove. The lane can now be landed honestly either way, so what is lost is the automation rather than the correctness; the human asked for the automation explicitly. Admits-answer-precondition: The change extends this lane's on-green auto-merge from `renovate/**` to `sbom-actions/**`, which is a workflow trigger scope, a job `if:` and the resolve step's author test — all of them inside this file and expressible nowhere else. No verb writes a workflow, and the branch scope has to sit on the trigger rather than only in the `if:` for the reason this file already records (CLOUD-493: a job condition is evaluated after the run exists, so 1131 runs in 25 hours were created only to skip). It lands in a pull request, which is what the class asks for, and the diff is what a reviewer reads to see exactly which branches can now merge unattended. Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE is rejected because a GitHub Actions workflow IS its own owning surface — there is no generator and no config table that emits this file. R-RESTORE-IT is rejected because restoring the committed bytes restores the single-lane scope, which is the thing being changed rather than a fault to undo. Refs: CLOUD-1213, CLOUD-1295, CLOUD-867
… claim `sbom-actions-currency` opens a PR that `verify` refuses: there is no claim receipt, and the agent one attests a refinement no bot performed. #806 landed with a `--takeover` claim against CLOUD-1213, which was a fake claim, and every future carry PR meets the same wall. `batten claim carry` is the honest receipt. It attests DERIVABILITY rather than a branch name — a prefix exemption would be a password wearing a branch name — so it mints only when the branch appends rows whose repo the base table already maps, changing the sha alone, and touches nothing else. The verb takes no argument on purpose: the subject is the branch's own diff against its merge base, and a caller that could name its own subject could name one that is derivable while changing something else. Also repairs the `snapshots` task, stale since the integration targets moved under `tests/it/`. Refs: CLOUD-1295
…eceipt
486 lines of shell and 323 of bats, six subcommands, all of it GitHub API plus
one local receipt write. `mise-tasks/bot-issue.sh` and `tests/bot-issue.bats`
are deleted; the predicates land as `crates/batten/src/bot.rs` and six new
leaves — `batten pr derive|file|link|ensure|closes` and `batten claim bot`.
TWO HALVES, KEPT APART INSIDE ONE MODULE. The predicates — is this pull request
one of the lane's, which manifests it touched, what Conventional type its
subject declares, whether a body still CLOSES a key rather than merely naming
one — are pure functions with their own unit tier. The `forge` half underneath
is the only thing that talks to anybody.
THE CONSUMER FACTS ARE `[bot_lane]` IN batten.toml, not literals in the crate.
Which repository, which bot logins, which manifests the lane owns, the marker
strings, the tracker's key prefix and the branch prefix — every value the
retired program spelled internally is a config row now, so a grep of
`crates/batten` for a bot's name or a manifest path returns nothing
(non-negotiable rule 1). `document_facts` caught the first draft's doc comment
naming a workflow glob as an example, and that is the gate working. The derived
row's body is a tracked template with `{{...}}` placeholders rather than a page
of markdown inside a TOML value; an unfilled placeholder refuses at
substitution rather than rendering a template artifact into a tracker row.
THE FORGE'S OWN CLIENT, NOT THIS CRATE'S HTTP TRANSPORT. `pr_watch` is the
landed precedent (CLOUD-1143) and `gh` resolves the credential outside the
crate, where `fetch` would have put token resolution inside it next to no
config row that declares one. `policy/spawn-adapters.rego` places `bot` on
`pr_watch`'s own argument. The commit that widened `fetch::Call` for a PATCH
this port turned out not to need is reverted earlier on this branch.
THE EXIT TABLE CHANGED AND THE WORKFLOW FOLLOWS IT. `bot-issue` used 1 for
"refused, not this lane's" and 2 for "could not look"; house style §7 makes a
refusal 2 and a could-not-look 3. `auto-bot-land.yml`'s two steps are repointed
and their `case` arms moved with them — reading 1 as an ordinary refusal there
would have treated a usage error as a pass.
THE THIRD RECEIPT KIND IS WIRED. `verify` now tries `claim`, then `bot`, then
`carry`, so a licence-carry branch no longer needs the fake `--takeover` claim
`DO-NOT-CLOSE CLOUD-1213`, without which every carry pull request stalls on
`closing-key-check` exactly as #806 did and needs a human to patch it.
WHAT IS NOT DONE, AND WHY IT IS FILED RATHER THAN FIXED. The plan called for a
`checks = ["carry"]` row beside `claim-needs-receipt`. That row is wrong: a
receipt rule's `checks` is a CONJUNCTION, so a second row is a second AND and
would deny every ordinary write on every branch. `verify`'s shell body can
express the disjunction and the mediated gate cannot — a live false positive
for the bot receipt since CLOUD-693, which this change's third kind inherits.
CLOUD-1297 owns it, with the disjunction as its §2.
TWO GATES CAUGHT REAL DEFECTS IN THIS CHANGE AND BOTH ARE FIXED HERE.
`no-appeal-to-authority` refused `carry.rs` and its tier for naming a real
action repository as fixture data — a third party's name in the crate, which is
the class the rule exists for, and the fixtures now use neutral names.
`module-layering` refused both new modules as unplaced, which is the coverage
clause working: `bot` and `carry` are placed with their reasons.
ONE EDIT IS DECLINED RATHER THAN FORCED, and CLOUD-1299 owns it. Retiring the
program makes `verify`'s no-receipt remedy name a task that no longer exists —
but `tests/verify.bats` asserts that message contains `bot-issue receipt`, and a
governed suite has two landable shapes, neither of which is an edit.
`repoints_at_the_declared_invocation` cannot admit it either: it requires the
replaced span to be a PATH reference, and a task name plus a subcommand is not
one. So the remedy names the live verb FIRST and keeps the old name as the thing
it replaced — true, useful mid-transition, and satisfying the pin — with the
reason at the site rather than left to read as a slip.
THE LEDGER. Two file arms and 22 case arms, `kind:verb` on each because the
lane needs stdin, spawns with its own arguments and performs writes — none of
which a tree-scoped module may do. `bot-issue` leaves `$MUTANT_GATES` and needs
no `#MUTANT-EXEMPT`: the census enumerates `mise-tasks/*.sh` and `policy/*.rego`
— `mutate::subjects`, since `main` retired `mutant-census` onto the engine under
this branch — so a deleted program is not in the census at all. The arm
carries a `runs:` field naming `batten claim bot` — ONE arm per deleted path,
which `V-RETIREMENT-AMBIGUOUS` requires, so the field names the only invocation
a GOVERNED caller loses; the lander's two workflow steps are ungoverned and
their repoints are free. Its two
declared mutations are re-homed as named cases —
`idempotence_a_second_call_on_the_same_pr_files_nothing` and
`a_key_named_but_not_closed_is_refused` — each carrying the mutation it stands
for in its comment.
THE LOAD-BEARING CASE IS REPLAYED, NOT ASSERTED. The dying suite ran the REAL
`ready-lint` over the REAL derived payload, because "the derived block is
checkable by the same gate that checks a human's" is the claim that makes a
mechanical row honest. `the_derived_block_passes_ready_lint` does the same over
this repository's committed template and its committed `[[pattern]]` grammar —
and it earned its place immediately, catching that `prettier` reshapes the
template's bullets on every `mise run fmt`.
A KNOWN LAG, STATED RATHER THAN HIDDEN. `auto-bot-land.yml` installs `batten`
from the latest RELEASE, so its two repointed steps fail loudly with "command
not found" between this merge and the next release. That is the same gap
CLOUD-1143 recorded for `checks-green`, and it fails the job rather than
passing silently.
126 suites after the deletion, 561.9s serial on this container, regenerated on
the rebased base rather than the one this change was written against — `main`
retired several more suites in between, `mutant`, `mutant-census` and
`session-start` among them, and a corpus derived from an older report publishes a
cost for a suite that is gone. The saving this change can claim is
`tests/bot-issue.bats`' own 2.9s share of the corpus it was measured in, 0.3%;
the totals differ by far more across runs, and that is those other retirements
plus machine noise rather than anything this branch did.
Refs: CLOUD-1295
Refs: CLOUD-1164
Refs: CLOUD-1213
Refs: CLOUD-1297
Refs: CLOUD-1299
Admits: 1c68cb4a5d20efa873c0ed2b7564b7cfdf53c0b6ebb9c79e211eea4b8445bcc5
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: batten.toml
Admits-head: a11e917
Admits-epoch: 650a76ba70a8b5645ffc58aedb1bdde72394e39026b5c1391a926934ea49702c
Admits-author: alec@wenzowski.com
Admits-prev: c7d8beede76f452a09d1a6c47a615b728f2876a60e055ef065975a27903f003f
Admits-answer-lost: mise-tasks/bot-issue.sh stays alive as a second implementation of predicates the engine now owns, which is the state policy/shell-retirement.rego's V-SHELL-RULE-EDITED exists to end -- the ported verbs cannot read a lane nobody declared, so refusing this write refuses the retirement rather than this line of it.
Admits-answer-precondition: The bot lane's consumer facts -- which forge repository, which bot logins, which manifests the lane owns, the marker strings, the tracker's key prefix and the branch prefix -- are exactly the values non-negotiable rule 1 forbids in crates/batten. Retiring mise-tasks/bot-issue.sh onto `batten pr derive|file|link|ensure|closes` and `batten claim bot` moves the MATCHER into the engine and leaves those facts here, so batten.toml is the owning surface for them and no other surface can carry them. The write is one additive [bot_lane] block, visible in this pull request's own diff, which is where reviewers read it.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because batten.toml IS the owning surface for a consumer's policy facts; there is no other surface that owns them. A local override cannot carry them either: adding a login to `bots` or a path to `owned_manifests` turns a refusal into a filed row, which is a weakening dressed as an addition and house style section 8's raise-only rule does not admit it. R-RESTORE-IT does not apply because the write is additive and deliberate rather than an accident to undo -- restoring the file would delete the table the ported verbs read.
Admits: ddd46be8d9ae384d97c38556832349bfc8dc319895761edb2255894b821b3763
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: .github/workflows/auto-bot-land.yml
Admits-head: a11e917
Admits-epoch: 8e6d5f2f19b383997365f6b72ff52816d72c7029208dbb81a40389acbd65e67f
Admits-author: alec@wenzowski.com
Admits-prev: -
Admits-answer-lost: The bot lane's lander breaks outright at its next tick: both steps call a task the retirement removed, so no bot pull request gets a row and none is landed. The alternative is keeping mise-tasks/bot-issue.sh alive as a second implementation, which is the state V-SHELL-RULE-EDITED exists to end.
Admits-answer-precondition: auto-bot-land.yml invokes `mise run bot-issue ensure` and `mise run bot-issue closes`, and this change deletes mise-tasks/bot-issue.sh. Leaving the calls would leave the lander invoking a task that no longer exists, so the workflow is the owning surface for its own steps and there is nowhere else the repoint could be made. The write is two invocation repoints -- `batten pr ensure` and `batten pr closes` -- plus the comment naming the retired program, and nothing about what the workflow decides changes.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because .github/workflows/auto-bot-land.yml IS the owning surface for the lander's own steps; a workflow's run lines cannot be declared anywhere else. R-RESTORE-IT does not apply because the change is a deliberate repoint at a successor this same commit lands, and restoring the file would leave a workflow calling a deleted program.
Admits: a3bb7b559f5118c0fe2cc4e4c8e2336e2bcbf1262a0df2578070e44446a20751
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: .github/workflows/sbom-actions-currency.yml
Admits-head: a11e917
Admits-epoch: 8e6d5f2f19b383997365f6b72ff52816d72c7029208dbb81a40389acbd65e67f
Admits-author: alec@wenzowski.com
Admits-prev: e03b883be1a24354b6eaeedb59a2c39f16052d832ef6dde2ceab292ca964fa5b
Admits-answer-lost: Every carry pull request `sbom-actions-currency` opens stalls on `closing-key-check` exactly as #806 did, and each one needs a human to patch its body before it can land -- which is the manual step CLOUD-1213 exists to remove, on the lane whose blockage this whole change is about.
Admits-answer-precondition: The generated pull request body carries `Refs: CLOUD-1213` and no closing marker, so `closing-key-check` reads it as a body naming a key non-closingly and stalls the landing. Measured on #806, where the body was patched by hand to get it through -- the defect repeating on every future carry pull request. The body is generated by this workflow's own `gh pr create` step, so the workflow is the owning surface for it and no other surface can set it. The write is one added line, `DO-NOT-CLOSE CLOUD-1213`, in the same printf list, visible in this pull request's diff.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because .github/workflows/sbom-actions-currency.yml IS the owning surface for the body it generates; a workflow's own `gh pr create` arguments cannot be declared anywhere else. R-RESTORE-IT does not apply because the change is a deliberate one-line addition rather than an accident to undo, and restoring the file would put back the body that stalls.
Admits: 1588b760e38a64a36f5aaef031bd5b3e8f5e1160c0f94eec486b41dbca5d335c
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: .serena/memories/core.md
Admits-head: a11e917
Admits-epoch: a5e521241d3b4c861cd0ffb0813ac2c5eb0bfd1b3b364e13418f67888e3de545
Admits-author: alec@wenzowski.com
Admits-prev: 121e5c62ce26f6caa46a19393e76ae86d200b8b113fccdaf2bb1196df372865b
Admits-answer-lost: `bot.rs` lands with no map row, `module-map-check` fails the gate, and the retirement cannot commit at all -- so refusing this write refuses the whole change rather than this bullet of it. A reader looking up what `bot.rs` owns would find nothing where every other module is described.
Admits-answer-precondition: `module-map-check` refuses a `crates/batten/src/*.rs` module with no row in `.serena/memories/core.md`, and this change adds `crates/batten/src/bot.rs`. The memory IS the module map's authority -- AGENTS.md points there for the per-module layout precisely so the tree is not restated in a budgeted file -- so there is no other surface a row could go on. The write was made through Serena's `edit_memory`, which is the sanctioned route, and it is one added bullet visible in this pull request's diff.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because `.serena/memories/core.md` IS the owning surface for the module map; AGENTS.md defers to it by name and `module-map-check` reads it as the authority. R-RESTORE-IT does not apply because the write is one additive bullet describing a module this same commit introduces -- restoring the file would leave the map missing a row the gate demands.
Admits: cbb7e7b9a66ad1ec32b1d2e5f2df9e67efc920620236ceb72e667aa6e772fdc7
Admits-rule: protected-mutation
Admits-verdict: V-PROTECTED-MUTATION
Admits-subject: policy/spawn-adapters.rego
Admits-head: 6da96ce
Admits-epoch: d8992f36146d4a8f3bd0250644ab0624b29c8cc7abd4b5640b4c4aa2c6c5efe6
Admits-author: alec@wenzowski.com
Admits-prev: -
Admits-answer-lost: The rebase cannot complete, so the retirement cannot land at all. Taking `main`'s side alone leaves `bot` unplaced and `spawn-adapters` refuses `crates/batten/src/bot.rs`; taking this branch's side alone deletes `mutate` and reintroduces the refusal on the module `main` just landed.
Admits-answer-precondition: This is a REBASE CONFLICT RESOLUTION, and both sides of it are additions to the same set literal. `main` placed `mutate` when it retired `mise-tasks/mutant.sh`; this branch places `bot` when it retires `mise-tasks/bot-issue.sh`. Neither adapter can be dropped -- `spawn-adapters` refuses a spawn in a module the table has not placed, so losing either side reintroduces the refusal the other change already answered. The resolution is the union of the two rows plus both reasons, which is what the file would hold had the two landed in sequence rather than concurrently. There is no other surface: the adapter table IS this module.
Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE does not apply because `policy/spawn-adapters.rego` IS the owning surface for the adapter placement table; the set literal cannot be declared anywhere else. R-RESTORE-IT does not apply because restoring the file to either side is precisely what loses one of the two placements -- there is no version of this file that carries both without this write.
CLOUD-1213's first acceptance clause. `auto-bot-land` merged only `renovate/**`, so every carry PR needed a person — which is what produced #806's fake `--takeover` claim, the defect CLOUD-1295 exists to remove. THE PAIRING IS THE POINT. The two lanes have different authors: Renovate opens its own PRs, and the carry lane is opened by `sbom-actions-currency` under the default token. A single `BOT_LOGIN` no longer answers, and the lazy widening — any known prefix against any known login — would admit `renovate[bot]` on a `sbom-actions/` branch and this workflow's own token on a `renovate/` one, neither of which either lane can produce. `LANES` is therefore a table of PAIRS and a PR must satisfy one row whole. CLOUD-867's origin test is untouched: `head_repository.full_name` is still what a fork cannot forge, still checked separately, and the prefix stays a filter rather than the trust boundary. Driven over a six-PR fixture before landing, and it caught a real bug: the first spelling read `.prefix` inside a pipe where `.` had already rebound to the branch string, which `jq` refuses at runtime — a workflow that would have errored on its first tick. With the lane bound, both lanes are admitted, both cross-pairings are refused, a fork-headed PR is refused and a human's branch is refused. What still gates a carry PR is unchanged and is not review: the full required check set must be green, and `batten claim carry` bounds what the branch may contain — only the licence table differs, every added row names a repo the base already maps with an identical licence and holder so only the sha moves, and no other tracked path differs from the merge base. Admits: b93a99d9da1523b9d9bd398f5b8c302d989357d8214bffe6e09c5e6aa30e9d12 Admits-rule: protected-mutation Admits-verdict: path write refused Admits-subject: .github/workflows/auto-bot-land.yml Admits-head: 0e089a9 Admits-epoch: 25c305c35f01ee4c99bceeacd1efc47509463adf09d3e5923196f59089cc2d98 Admits-author: alec@wenzowski.com Admits-prev: ddd46be8d9ae384d97c38556832349bfc8dc319895761edb2255894b821b3763 Admits-answer-lost: CLOUD-1213's first acceptance clause stays open and every licence-carry PR keeps needing a person to land it — which is what produced #806's fake `--takeover` claim, the defect CLOUD-1295 exists to remove. The lane can now be landed honestly either way, so what is lost is the automation rather than the correctness; the human asked for the automation explicitly. Admits-answer-precondition: The change extends this lane's on-green auto-merge from `renovate/**` to `sbom-actions/**`, which is a workflow trigger scope, a job `if:` and the resolve step's author test — all of them inside this file and expressible nowhere else. No verb writes a workflow, and the branch scope has to sit on the trigger rather than only in the `if:` for the reason this file already records (CLOUD-493: a job condition is evaluated after the run exists, so 1131 runs in 25 hours were created only to skip). It lands in a pull request, which is what the class asks for, and the diff is what a reviewer reads to see exactly which branches can now merge unattended. Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE is rejected because a GitHub Actions workflow IS its own owning surface — there is no generator and no config table that emits this file. R-RESTORE-IT is rejected because restoring the committed bytes restores the single-lane scope, which is the thing being changed rather than a fault to undo. Refs: CLOUD-1213, CLOUD-1295, CLOUD-867
Opened by
sbom-actions-currency.sbom-checkrefuses an action pin with no row inmise-tasks/sbom-actions.tsv, and Renovate cannot write that table — so every action bump opens red and holds theprConcurrentLimit: 1slot (CLOUD-1213).Each row here is the same repository's existing row with only the sha changed. It was carried forward only after every candidate licence file was confirmed byte-identical between the mapped commit and the new one, so the judgement already recorded still describes those exact bytes. Nothing was re-derived.
A pin whose licence files differ, or whose repository has no row at all, is not here — it is reported as a warning in the run log for a person to read.
Refs: CLOUD-1213
DO-NOT-CLOSE CLOUD-1213
CLOUD-1213 built the workflow that opened this PR; this PR is its product, not its completion. Closing it here would move the row on every carry, and its first acceptance clause is not met — see the row.