diff --git a/.claude/rules/policy-modules.md b/.claude/rules/policy-modules.md index 5f9144bb0..46a98425e 100644 --- a/.claude/rules/policy-modules.md +++ b/.claude/rules/policy-modules.md @@ -337,19 +337,28 @@ passes over a key nothing fills and over a channel nothing populates. Both live instances of that class were found by adding the second tier, not by reading. `mise run policy-test` runs the first tier for every registered module and enabled -preset. A gate that **remains registered** in `$MUTANT_GATES` also needs -`tests/.bats` to exist, because `mutant` resolves a gate's suite by that -name — and a declared mutation whose named case does not exist is reported rather -than silently counted. - -**That clause binds a gate that stays, and says nothing about one being retired**, -which the unscoped wording invited a reader to get backwards (CLOUD-1132, -measured: it was read as a demand for a bats suite beside a new module, and then -as a direct conflict with `shell-retirement`, which refuses adding one). A -retirement LEAVES `$MUTANT_GATES` and takes its suite with it — that is step four -of the two-shapes rule in `.claude/rules/toolchain.md` — so there is no gate left -for this sentence to be about. A new `.rego` module's second tier is -`crates/batten/tests/*.rs` and never a `.bats`. +preset. **A module in `$MUTANT_GATES` DECLARES the suite its mutations must +redden** — `#MUTANT-SUITE crates/batten/tests/.rs`, beside its `#MUTANT` rows — +and a declared mutation whose named case does not exist is reported rather than +silently counted. + +**That is CLOUD-1267's change, and the sentence here used to say the opposite.** +It read _"a gate that remains registered also needs `tests/.bats` to exist, +because `mutant` resolves a gate's suite by that name"_ — which was true, and was +the reason 32 of 32 modules carried a `#MUTANT-EXEMPT`: the runner hardcoded a +path no module may have, `V-SHELL-RULE-ADDED` refuses adding one, and 141 +compiled-binary tiers were therefore unreachable. `batten mutate` resolves the +DECLARED path instead, so a `.rego` module names the tier that actually drives +the engine and the exemption is withdrawn rather than renewed. A new module's +second tier is still `crates/batten/tests/*.rs` and never a `.bats`. + +**Naming a tier is not the same as being covered by it, and the sweep is what +tells them apart.** A tier that drives the FACT a predicate reads — the +`*_facts.rs` family — never installs the module, so no case in it can turn red +under a mutation of the predicate: the row is declared, it SURVIVES, and the +survivor is the finding. `#MUTANT-OWNER |` names the row that owes the +missing tier and **changes no exit code**; a declaration that suppressed the +finding would be the laundering the runner exists to refuse. **Choose a mutation that discriminates.** A mutation over a conjunct that some other conjunct already excludes will survive, and surviving is the only way you diff --git a/.github/workflows/mutant.yml b/.github/workflows/mutant.yml index 053231897..218fc6b41 100644 --- a/.github/workflows/mutant.yml +++ b/.github/workflows/mutant.yml @@ -28,21 +28,26 @@ name: mutant # tested, so a push trigger would re-run work that is by construction already # done. # -# WHAT THIS REPORTS ON ITS FIRST TICK, said here so nobody reads it as a -# regression this workflow introduced. Two of the three findings are resolved by -# the change that adds this file: `privileged-lane` and `run-shape` leave -# `$MUTANT_GATES` carrying a `#MUTANT-EXEMPT` on their modules, because -# `shell-retirement` refuses adding the `tests/.bats` each would need — at -# `deny`, with one `document` route and no override — and their real tier is a -# compiled-binary Rust test. The third, -# `graph-check/receipt-carries-no-ids names-no-case`, is CLOUD-1061's: fixing it -# means editing either `mise-tasks/graph-check.sh` or `tests/graph-check.bats`, -# both governed shell rules that cannot retire, so no route exists for it today -# and none is invented here. +# WHAT THIS REPORTS, said here so nobody reads a finding as a regression this +# workflow introduced. The steps below invoke `mise run mutant` and +# `mise run mutant-census` by TASK NAME, and CLOUD-1267 retired both programs +# onto `batten mutate` without either name moving — so nothing here changed with +# that port, which is what kept the retirement to the two files being retired. # -# **This job is therefore red until CLOUD-1061 lands, and that visibility is the -# point.** The finding has been true and unobserved for weeks; a scheduled job -# reporting it is strictly better than nothing reporting it. It is deliberately +# The sweep is EXPECTED TO BE RED, and each red row names its owner. CLOUD-1267 +# withdrew the `#MUTANT-EXEMPT` rows whose only reason was that `mutant` resolved +# a gate's suite as `tests/.bats`, and put those modules into the enforced +# set against the compiled-binary tier each already named. Where that tier drives +# the FACT a predicate reads rather than the predicate itself, the mutation +# SURVIVES — a real finding, carried with a `#MUTANT-OWNER` naming the row that +# owes a predicate tier, rather than hidden behind a renewed exemption. +# `graph-check/receipt-carries-no-ids names-no-case` is CLOUD-1061's and is +# unchanged: fixing it means editing either `mise-tasks/graph-check.sh` or +# `tests/graph-check.bats`, both governed shell rules that cannot retire, so no +# route exists for it today and none is invented here. +# +# **This job is therefore red, and that visibility is the point.** A survivor +# that nothing reports is indistinguishable from coverage. It is deliberately # NOT in `$CI_REQUIRED_CHECKS`, so it blocks no PR while it says so. on: diff --git a/.serena/memories/core.md b/.serena/memories/core.md index 94c44ef23..4b7f76c52 100644 --- a/.serena/memories/core.md +++ b/.serena/memories/core.md @@ -1101,6 +1101,26 @@ transcript CONTENT needs 1029 first, and nothing landed authorises one. so the matcher choosing which rows adjudicate and the one choosing which results mint cannot drift into a gate nobody can satisfy (CLOUD-178), and `receipt::safe_subject` so writer and reader refuse the same filenames. +- `mutate.rs` — mutation coverage over the declared gate set (CLOUD-418), + retired out of `mise-tasks/mutant.sh` and `mise-tasks/mutant-census.sh` under + CLOUD-1267. **The one behavioural change is the DECLARED suite**: the + predecessor resolved a gate's source with a Rego fallback and its suite as + `tests/$gate.bats` unconditionally, so a mutation applied to a `.rego` module + had no suite that could turn red — 32 modules, 32 `#MUTANT-EXEMPT` rows and + 141 compiled-binary tiers it could not see. `#MUTANT-SUITE ` beside the + `#MUTANT` rows names the tier instead, and `Suite` resolves a `.bats` through + the vendored runner or a `crates/batten/tests/*.rs` through `cargo test +--test`. Two more arms the predecessor lacked: a gate name resolves to a + PRESET directory as well as a task or a module, and `#MUTANT-OWNER` echoes the + row owning a known-dead predicate on its survivor line while **changing no + exit code** — annotation, never an exemption. Conserved whole: the anti-vacuity + term (a listed gate with no declaration FAILS), three-fields-before-the-split, + green-before-mutation, the inert and self-mutating diff tests, both directions + of the too-wide/too-narrow filter, restore-between-rows, and a staged tree that + is a real repository. `Verdict::could_not_look` is what splits exit `3` from + the `2` a survivor answers, which is the acceptance rather than a nicety. + Spawning side per CLOUD-1171 (`perf.rs`'s disposition), so `mutate sweep` is + `write` and only `mutate census` reaches the read-only allowlist. - `verbs.rs` — the mutating-verb table (CLOUD-36): which programs change the world, config-driven (rule 1) and typed by `effect.rs`'s one §5 vocabulary rather than a second severity axis. Each verb carries its own redirect for the diff --git a/bench/suites/RESULTS.md b/bench/suites/RESULTS.md index e08bd9a08..81cf164df 100644 --- a/bench/suites/RESULTS.md +++ b/bench/suites/RESULTS.md @@ -28,7 +28,6 @@ to it pays. | 7.6 | 1.2% | `tests/board-sweep.bats` | | 7.4 | 1.1% | `tests/ready-guard.bats` | | 6.8 | 1.0% | `tests/session-start.bats` | -| 6.6 | 1.0% | `tests/mutant.bats` | | 6.6 | 1.0% | `tests/released.bats` | | 6.5 | 1.0% | `tests/replay.bats` | | 5.4 | 0.8% | `tests/lock-complete.bats` | @@ -75,7 +74,6 @@ to it pays. | 1.3 | 0.2% | `tests/awk-regex-check.bats` | | 1.2 | 0.2% | `tests/fact-record-keying.bats` | | 1.2 | 0.2% | `tests/perf-record.bats` | -| 1.1 | 0.2% | `tests/mutant-census.bats` | | 1.1 | 0.2% | `tests/done-pr-check.bats` | | 1.1 | 0.2% | `tests/land-divergence-assert.bats` | | 1.1 | 0.2% | `tests/linear-check.bats` | diff --git a/completions/batten.bash b/completions/batten.bash index 3141e3df1..0c1a68cb7 100644 --- a/completions/batten.bash +++ b/completions/batten.bash @@ -73,6 +73,9 @@ _batten() { batten,mcp) cmd="batten__subcmd__mcp" ;; + batten,mutate) + cmd="batten__subcmd__mutate" + ;; batten,override) cmd="batten__subcmd__override" ;; @@ -367,6 +370,9 @@ _batten() { batten__subcmd__help,mcp) cmd="batten__subcmd__help__subcmd__mcp" ;; + batten__subcmd__help,mutate) + cmd="batten__subcmd__help__subcmd__mutate" + ;; batten__subcmd__help,override) cmd="batten__subcmd__help__subcmd__override" ;; @@ -484,6 +490,12 @@ _batten() { batten__subcmd__help__subcmd__mcp,call) cmd="batten__subcmd__help__subcmd__mcp__subcmd__call" ;; + batten__subcmd__help__subcmd__mutate,census) + cmd="batten__subcmd__help__subcmd__mutate__subcmd__census" + ;; + batten__subcmd__help__subcmd__mutate,sweep) + cmd="batten__subcmd__help__subcmd__mutate__subcmd__sweep" + ;; batten__subcmd__help__subcmd__override,request) cmd="batten__subcmd__help__subcmd__override__subcmd__request" ;; @@ -580,6 +592,24 @@ _batten() { batten__subcmd__mcp__subcmd__help,help) cmd="batten__subcmd__mcp__subcmd__help__subcmd__help" ;; + batten__subcmd__mutate,census) + cmd="batten__subcmd__mutate__subcmd__census" + ;; + batten__subcmd__mutate,help) + cmd="batten__subcmd__mutate__subcmd__help" + ;; + batten__subcmd__mutate,sweep) + cmd="batten__subcmd__mutate__subcmd__sweep" + ;; + batten__subcmd__mutate__subcmd__help,census) + cmd="batten__subcmd__mutate__subcmd__help__subcmd__census" + ;; + batten__subcmd__mutate__subcmd__help,help) + cmd="batten__subcmd__mutate__subcmd__help__subcmd__help" + ;; + batten__subcmd__mutate__subcmd__help,sweep) + cmd="batten__subcmd__mutate__subcmd__help__subcmd__sweep" + ;; batten__subcmd__override,help) cmd="batten__subcmd__override__subcmd__help" ;; @@ -815,7 +845,7 @@ _batten() { case "${cmd}" in batten) - opts="-q -v -y -h -V --strictness --fail-on-warning --config-from --config-in --silent --quiet --verbose --debug --trace --log-level --no-color --no-input --yes --help --version check enforce exec capture mcp target config lint spec doctor init baseline generate perf policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" + opts="-q -v -y -h -V --strictness --fail-on-warning --config-from --config-in --silent --quiet --verbose --debug --trace --log-level --no-color --no-input --yes --help --version check enforce exec capture mcp target config lint spec doctor init baseline generate perf mutate policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -2617,7 +2647,7 @@ _batten() { return 0 ;; batten__subcmd__help) - opts="check enforce exec capture mcp target config lint spec doctor init baseline generate perf policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" + opts="check enforce exec capture mcp target config lint spec doctor init baseline generate perf mutate policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -3232,6 +3262,48 @@ _batten() { COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 ;; + batten__subcmd__help__subcmd__mutate) + opts="sweep census" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + batten__subcmd__help__subcmd__mutate__subcmd__census) + opts="" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 4 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + batten__subcmd__help__subcmd__mutate__subcmd__sweep) + opts="" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 4 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; batten__subcmd__help__subcmd__override) opts="request spend" if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then @@ -4046,6 +4118,152 @@ _batten() { COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 ;; + batten__subcmd__mutate) + opts="-q -v -y -h --strictness --fail-on-warning --config-from --config-in --silent --quiet --verbose --debug --trace --log-level --no-color --no-input --yes --help sweep census help" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --strictness) + COMPREPLY=($(compgen -W "permissive standard strict" -- "${cur}")) + return 0 + ;; + --config-from) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --config-in) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --log-level) + COMPREPLY=($(compgen -W "silent quiet normal verbose debug trace" -- "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + batten__subcmd__mutate__subcmd__census) + opts="-q -v -y -h --strictness --fail-on-warning --config-from --config-in --silent --quiet --verbose --debug --trace --log-level --no-color --no-input --yes --help" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --strictness) + COMPREPLY=($(compgen -W "permissive standard strict" -- "${cur}")) + return 0 + ;; + --config-from) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --config-in) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --log-level) + COMPREPLY=($(compgen -W "silent quiet normal verbose debug trace" -- "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + batten__subcmd__mutate__subcmd__help) + opts="sweep census help" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + batten__subcmd__mutate__subcmd__help__subcmd__census) + opts="" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 4 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + batten__subcmd__mutate__subcmd__help__subcmd__help) + opts="" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 4 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + batten__subcmd__mutate__subcmd__help__subcmd__sweep) + opts="" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 4 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + batten__subcmd__mutate__subcmd__sweep) + opts="-q -v -y -h --strictness --fail-on-warning --config-from --config-in --silent --quiet --verbose --debug --trace --log-level --no-color --no-input --yes --help" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --strictness) + COMPREPLY=($(compgen -W "permissive standard strict" -- "${cur}")) + return 0 + ;; + --config-from) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --config-in) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --log-level) + COMPREPLY=($(compgen -W "silent quiet normal verbose debug trace" -- "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; batten__subcmd__override) opts="-q -v -y -h --strictness --fail-on-warning --config-from --config-in --silent --quiet --verbose --debug --trace --log-level --no-color --no-input --yes --help request spend help" if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then diff --git a/completions/batten.fish b/completions/batten.fish index 1d33de910..5f41b7e74 100644 --- a/completions/batten.fish +++ b/completions/batten.fish @@ -60,6 +60,7 @@ complete -c batten -n "__fish_batten_needs_command" -f -a "init" -d 'Write a sta complete -c batten -n "__fish_batten_needs_command" -f -a "baseline" -d 'Record the findings that already exist, so only new ones fail' complete -c batten -n "__fish_batten_needs_command" -f -a "generate" -d 'Emit artifacts derived from the command spec, on stdout' complete -c batten -n "__fish_batten_needs_command" -f -a "perf" -d 'Measure this repository\'s own invocation cost' +complete -c batten -n "__fish_batten_needs_command" -f -a "mutate" -d 'Decide whether this repository\'s gates discriminate, rather than merely parse' complete -c batten -n "__fish_batten_needs_command" -f -a "policy" -d 'Inspect the thresholds and path sets this repository holds itself to' complete -c batten -n "__fish_batten_needs_command" -f -a "commit" -d 'The shape a commit must take here: what its subject may say' complete -c batten -n "__fish_batten_needs_command" -f -a "ready" -d 'Whether an issue\'s Ready block satisfies the checkable clauses of the gate' @@ -867,6 +868,75 @@ complete -c batten -n "__fish_batten_using_subcommand perf; and __fish_seen_subc complete -c batten -n "__fish_batten_using_subcommand perf; and __fish_seen_subcommand_from pair" -s h -l help -d 'Print help (see more with \'--help\')' complete -c batten -n "__fish_batten_using_subcommand perf; and __fish_seen_subcommand_from help" -f -a "pair" -d 'Measure this branch and its merge base back to back on one machine, and print both arms as paired records' complete -c batten -n "__fish_batten_using_subcommand perf; and __fish_seen_subcommand_from help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' +complete -c batten -n "__fish_batten_using_subcommand mutate; and not __fish_seen_subcommand_from sweep census help" -l strictness -d 'Raise how strictly gates apply (an override may only tighten policy)' -r -f -a "permissive\t'Advisory: findings are reported without failing the run' +standard\t'The default: a finding is a violation' +strict\t'Everything `Standard` fails on, plus anything advisory'" +complete -c batten -n "__fish_batten_using_subcommand mutate; and not __fish_seen_subcommand_from sweep census help" -l config-from -d 'Read the committed config from a git ref (e.g. origin/main) instead of the working tree' -r +complete -c batten -n "__fish_batten_using_subcommand mutate; and not __fish_seen_subcommand_from sweep census help" -l config-in -d 'Read the committed config from this directory instead of the directory being judged' -r +complete -c batten -n "__fish_batten_using_subcommand mutate; and not __fish_seen_subcommand_from sweep census help" -l log-level -d 'Set the verbosity rung by name' -r -f -a "silent\t'Say nothing but a verdict or a usage error' +quiet\t'Suppress ordinary progress; keep warnings' +normal\t'The default' +verbose\t'Explain what is being checked' +debug\t'Add resolution detail' +trace\t'Add everything'" +complete -c batten -n "__fish_batten_using_subcommand mutate; and not __fish_seen_subcommand_from sweep census help" -l fail-on-warning -d 'Promote a warn-severity finding to a violation (an override may only turn this on)' +complete -c batten -n "__fish_batten_using_subcommand mutate; and not __fish_seen_subcommand_from sweep census help" -l silent -d 'Say nothing but a verdict or a usage error' +complete -c batten -n "__fish_batten_using_subcommand mutate; and not __fish_seen_subcommand_from sweep census help" -s q -l quiet -d 'Suppress ordinary progress (repeatable: -qq is silent)' +complete -c batten -n "__fish_batten_using_subcommand mutate; and not __fish_seen_subcommand_from sweep census help" -s v -l verbose -d 'Explain what is being checked (repeatable: -vv is debug)' +complete -c batten -n "__fish_batten_using_subcommand mutate; and not __fish_seen_subcommand_from sweep census help" -l debug -d 'Add resolution detail' +complete -c batten -n "__fish_batten_using_subcommand mutate; and not __fish_seen_subcommand_from sweep census help" -l trace -d 'Add everything' +complete -c batten -n "__fish_batten_using_subcommand mutate; and not __fish_seen_subcommand_from sweep census help" -l no-color -d 'Never colour stderr, whatever it is attached to' +complete -c batten -n "__fish_batten_using_subcommand mutate; and not __fish_seen_subcommand_from sweep census help" -l no-input -d 'Never prompt; treat the run as unattended' +complete -c batten -n "__fish_batten_using_subcommand mutate; and not __fish_seen_subcommand_from sweep census help" -s y -l yes -d 'Confirm a destructive operation that would otherwise refuse' +complete -c batten -n "__fish_batten_using_subcommand mutate; and not __fish_seen_subcommand_from sweep census help" -s h -l help -d 'Print help (see more with \'--help\')' +complete -c batten -n "__fish_batten_using_subcommand mutate; and not __fish_seen_subcommand_from sweep census help" -f -a "sweep" -d 'Apply every declared mutation to its source and report the ones its declared suite did not catch' +complete -c batten -n "__fish_batten_using_subcommand mutate; and not __fish_seen_subcommand_from sweep census help" -f -a "census" -d 'Report every gate in the tree that is neither mutation-enforced nor carrying a filed exemption' +complete -c batten -n "__fish_batten_using_subcommand mutate; and not __fish_seen_subcommand_from sweep census help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' +complete -c batten -n "__fish_batten_using_subcommand mutate; and __fish_seen_subcommand_from sweep" -l strictness -d 'Raise how strictly gates apply (an override may only tighten policy)' -r -f -a "permissive\t'Advisory: findings are reported without failing the run' +standard\t'The default: a finding is a violation' +strict\t'Everything `Standard` fails on, plus anything advisory'" +complete -c batten -n "__fish_batten_using_subcommand mutate; and __fish_seen_subcommand_from sweep" -l config-from -d 'Read the committed config from a git ref (e.g. origin/main) instead of the working tree' -r +complete -c batten -n "__fish_batten_using_subcommand mutate; and __fish_seen_subcommand_from sweep" -l config-in -d 'Read the committed config from this directory instead of the directory being judged' -r +complete -c batten -n "__fish_batten_using_subcommand mutate; and __fish_seen_subcommand_from sweep" -l log-level -d 'Set the verbosity rung by name' -r -f -a "silent\t'Say nothing but a verdict or a usage error' +quiet\t'Suppress ordinary progress; keep warnings' +normal\t'The default' +verbose\t'Explain what is being checked' +debug\t'Add resolution detail' +trace\t'Add everything'" +complete -c batten -n "__fish_batten_using_subcommand mutate; and __fish_seen_subcommand_from sweep" -l fail-on-warning -d 'Promote a warn-severity finding to a violation (an override may only turn this on)' +complete -c batten -n "__fish_batten_using_subcommand mutate; and __fish_seen_subcommand_from sweep" -l silent -d 'Say nothing but a verdict or a usage error' +complete -c batten -n "__fish_batten_using_subcommand mutate; and __fish_seen_subcommand_from sweep" -s q -l quiet -d 'Suppress ordinary progress (repeatable: -qq is silent)' +complete -c batten -n "__fish_batten_using_subcommand mutate; and __fish_seen_subcommand_from sweep" -s v -l verbose -d 'Explain what is being checked (repeatable: -vv is debug)' +complete -c batten -n "__fish_batten_using_subcommand mutate; and __fish_seen_subcommand_from sweep" -l debug -d 'Add resolution detail' +complete -c batten -n "__fish_batten_using_subcommand mutate; and __fish_seen_subcommand_from sweep" -l trace -d 'Add everything' +complete -c batten -n "__fish_batten_using_subcommand mutate; and __fish_seen_subcommand_from sweep" -l no-color -d 'Never colour stderr, whatever it is attached to' +complete -c batten -n "__fish_batten_using_subcommand mutate; and __fish_seen_subcommand_from sweep" -l no-input -d 'Never prompt; treat the run as unattended' +complete -c batten -n "__fish_batten_using_subcommand mutate; and __fish_seen_subcommand_from sweep" -s y -l yes -d 'Confirm a destructive operation that would otherwise refuse' +complete -c batten -n "__fish_batten_using_subcommand mutate; and __fish_seen_subcommand_from sweep" -s h -l help -d 'Print help (see more with \'--help\')' +complete -c batten -n "__fish_batten_using_subcommand mutate; and __fish_seen_subcommand_from census" -l strictness -d 'Raise how strictly gates apply (an override may only tighten policy)' -r -f -a "permissive\t'Advisory: findings are reported without failing the run' +standard\t'The default: a finding is a violation' +strict\t'Everything `Standard` fails on, plus anything advisory'" +complete -c batten -n "__fish_batten_using_subcommand mutate; and __fish_seen_subcommand_from census" -l config-from -d 'Read the committed config from a git ref (e.g. origin/main) instead of the working tree' -r +complete -c batten -n "__fish_batten_using_subcommand mutate; and __fish_seen_subcommand_from census" -l config-in -d 'Read the committed config from this directory instead of the directory being judged' -r +complete -c batten -n "__fish_batten_using_subcommand mutate; and __fish_seen_subcommand_from census" -l log-level -d 'Set the verbosity rung by name' -r -f -a "silent\t'Say nothing but a verdict or a usage error' +quiet\t'Suppress ordinary progress; keep warnings' +normal\t'The default' +verbose\t'Explain what is being checked' +debug\t'Add resolution detail' +trace\t'Add everything'" +complete -c batten -n "__fish_batten_using_subcommand mutate; and __fish_seen_subcommand_from census" -l fail-on-warning -d 'Promote a warn-severity finding to a violation (an override may only turn this on)' +complete -c batten -n "__fish_batten_using_subcommand mutate; and __fish_seen_subcommand_from census" -l silent -d 'Say nothing but a verdict or a usage error' +complete -c batten -n "__fish_batten_using_subcommand mutate; and __fish_seen_subcommand_from census" -s q -l quiet -d 'Suppress ordinary progress (repeatable: -qq is silent)' +complete -c batten -n "__fish_batten_using_subcommand mutate; and __fish_seen_subcommand_from census" -s v -l verbose -d 'Explain what is being checked (repeatable: -vv is debug)' +complete -c batten -n "__fish_batten_using_subcommand mutate; and __fish_seen_subcommand_from census" -l debug -d 'Add resolution detail' +complete -c batten -n "__fish_batten_using_subcommand mutate; and __fish_seen_subcommand_from census" -l trace -d 'Add everything' +complete -c batten -n "__fish_batten_using_subcommand mutate; and __fish_seen_subcommand_from census" -l no-color -d 'Never colour stderr, whatever it is attached to' +complete -c batten -n "__fish_batten_using_subcommand mutate; and __fish_seen_subcommand_from census" -l no-input -d 'Never prompt; treat the run as unattended' +complete -c batten -n "__fish_batten_using_subcommand mutate; and __fish_seen_subcommand_from census" -s y -l yes -d 'Confirm a destructive operation that would otherwise refuse' +complete -c batten -n "__fish_batten_using_subcommand mutate; and __fish_seen_subcommand_from census" -s h -l help -d 'Print help (see more with \'--help\')' +complete -c batten -n "__fish_batten_using_subcommand mutate; and __fish_seen_subcommand_from help" -f -a "sweep" -d 'Apply every declared mutation to its source and report the ones its declared suite did not catch' +complete -c batten -n "__fish_batten_using_subcommand mutate; and __fish_seen_subcommand_from help" -f -a "census" -d 'Report every gate in the tree that is neither mutation-enforced nor carrying a filed exemption' +complete -c batten -n "__fish_batten_using_subcommand mutate; and __fish_seen_subcommand_from help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' complete -c batten -n "__fish_batten_using_subcommand policy; and not __fish_seen_subcommand_from budget test tools explain help" -l strictness -d 'Raise how strictly gates apply (an override may only tighten policy)' -r -f -a "permissive\t'Advisory: findings are reported without failing the run' standard\t'The default: a finding is a violation' strict\t'Everything `Standard` fails on, plus anything advisory'" @@ -2077,40 +2147,41 @@ complete -c batten -n "__fish_batten_using_subcommand wiring; and __fish_seen_su complete -c batten -n "__fish_batten_using_subcommand wiring; and __fish_seen_subcommand_from reclaim" -s h -l help -d 'Print help (see more with \'--help\')' complete -c batten -n "__fish_batten_using_subcommand wiring; and __fish_seen_subcommand_from help" -f -a "reclaim" -d 'Remove non-batten hook registrations from this host\'s merged surfaces' complete -c batten -n "__fish_batten_using_subcommand wiring; and __fish_seen_subcommand_from help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' -complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "check" -d 'Run the applicable read-only gates against the repository' -complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "enforce" -d 'Run every configured rule, including kinds that execute a configured command' -complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "exec" -d 'Run a command — or a `:::` bundle — and report a pointer to what it wrote' -complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "capture" -d 'Captured command output: navigate what `exec` already ran, without running it again' -complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "mcp" -d 'Dispatch a declared MCP call and hand back a reduction instead of the payload' -complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "target" -d 'Inspect and reclaim this repository\'s build tree' -complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "config" -d 'Inspect configuration' -complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "lint" -d 'Lint an artifact against a declared schema' -complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "spec" -d 'Print the tool\'s own command spec' -complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "doctor" -d 'Diagnose whether Batten can run in this repository' -complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "init" -d 'Write a starter batten.toml, refusing to overwrite an existing one' -complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "baseline" -d 'Record the findings that already exist, so only new ones fail' -complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "generate" -d 'Emit artifacts derived from the command spec, on stdout' -complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "perf" -d 'Measure this repository\'s own invocation cost' -complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "policy" -d 'Inspect the thresholds and path sets this repository holds itself to' -complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "commit" -d 'The shape a commit must take here: what its subject may say' -complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "ready" -d 'Whether an issue\'s Ready block satisfies the checkable clauses of the gate' -complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "checks" -d 'Whether a commit\'s check runs answer the question a landing depends on' -complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "pr" -d 'The pull request a landing drives, and the answers it waits on' -complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "claim" -d 'Whether the issue you are about to pull is actually unclaimed' -complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "semver" -d 'Whether this branch\'s API delta is compatible with the bump it claims' -complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "attribution" -d 'What produced commits may carry about the tooling that made them' -complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "worktree" -d 'Worktrees and the work in them: what is at risk' -complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "override" -d 'Issued admissions: an override is a record, never a variable somebody knows' -complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "provision" -d 'Pinned tools this repository provisions, cached out of tree' -complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "hook" -d 'Adjudicate a mediated tool call read from stdin (a deny is exit 2, the one contract)' -complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "payload" -d 'Read a hook payload from stdin' -complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "receipt" -d 'Verification receipts: SHA-keyed claims a named check passed, invalidated by git facts' -complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "defects" -d 'The append-only defect ledger: the lessons this repository has already paid for' -complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "design" -d 'Design-evidence claims: the integrity of the record behind a decision' -complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "state" -d 'The out-of-tree findings store: which store belongs to this checkout' -complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "record" -d 'Out-of-tree verdict stores: what something else judged, keyed so a stale answer cannot answer' -complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "wiring" -d 'Repair a host\'s hook registrations' -complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' +complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf mutate policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "check" -d 'Run the applicable read-only gates against the repository' +complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf mutate policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "enforce" -d 'Run every configured rule, including kinds that execute a configured command' +complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf mutate policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "exec" -d 'Run a command — or a `:::` bundle — and report a pointer to what it wrote' +complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf mutate policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "capture" -d 'Captured command output: navigate what `exec` already ran, without running it again' +complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf mutate policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "mcp" -d 'Dispatch a declared MCP call and hand back a reduction instead of the payload' +complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf mutate policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "target" -d 'Inspect and reclaim this repository\'s build tree' +complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf mutate policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "config" -d 'Inspect configuration' +complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf mutate policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "lint" -d 'Lint an artifact against a declared schema' +complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf mutate policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "spec" -d 'Print the tool\'s own command spec' +complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf mutate policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "doctor" -d 'Diagnose whether Batten can run in this repository' +complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf mutate policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "init" -d 'Write a starter batten.toml, refusing to overwrite an existing one' +complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf mutate policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "baseline" -d 'Record the findings that already exist, so only new ones fail' +complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf mutate policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "generate" -d 'Emit artifacts derived from the command spec, on stdout' +complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf mutate policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "perf" -d 'Measure this repository\'s own invocation cost' +complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf mutate policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "mutate" -d 'Decide whether this repository\'s gates discriminate, rather than merely parse' +complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf mutate policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "policy" -d 'Inspect the thresholds and path sets this repository holds itself to' +complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf mutate policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "commit" -d 'The shape a commit must take here: what its subject may say' +complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf mutate policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "ready" -d 'Whether an issue\'s Ready block satisfies the checkable clauses of the gate' +complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf mutate policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "checks" -d 'Whether a commit\'s check runs answer the question a landing depends on' +complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf mutate policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "pr" -d 'The pull request a landing drives, and the answers it waits on' +complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf mutate policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "claim" -d 'Whether the issue you are about to pull is actually unclaimed' +complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf mutate policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "semver" -d 'Whether this branch\'s API delta is compatible with the bump it claims' +complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf mutate policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "attribution" -d 'What produced commits may carry about the tooling that made them' +complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf mutate policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "worktree" -d 'Worktrees and the work in them: what is at risk' +complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf mutate policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "override" -d 'Issued admissions: an override is a record, never a variable somebody knows' +complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf mutate policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "provision" -d 'Pinned tools this repository provisions, cached out of tree' +complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf mutate policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "hook" -d 'Adjudicate a mediated tool call read from stdin (a deny is exit 2, the one contract)' +complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf mutate policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "payload" -d 'Read a hook payload from stdin' +complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf mutate policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "receipt" -d 'Verification receipts: SHA-keyed claims a named check passed, invalidated by git facts' +complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf mutate policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "defects" -d 'The append-only defect ledger: the lessons this repository has already paid for' +complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf mutate policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "design" -d 'Design-evidence claims: the integrity of the record behind a decision' +complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf mutate policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "state" -d 'The out-of-tree findings store: which store belongs to this checkout' +complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf mutate policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "record" -d 'Out-of-tree verdict stores: what something else judged, keyed so a stale answer cannot answer' +complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf mutate policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "wiring" -d 'Repair a host\'s hook registrations' +complete -c batten -n "__fish_batten_using_subcommand help; and not __fish_seen_subcommand_from check enforce exec capture mcp target config lint spec doctor init baseline generate perf mutate policy commit ready checks pr claim semver attribution worktree override provision hook payload receipt defects design state record wiring help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' complete -c batten -n "__fish_batten_using_subcommand help; and __fish_seen_subcommand_from capture" -f -a "show" -d 'Print a capture\'s pointer, or the lines a selection asks for, with no second run' complete -c batten -n "__fish_batten_using_subcommand help; and __fish_seen_subcommand_from capture" -f -a "find" -d 'Resolve a stored tool response by the key it carries, with no handle to look up first' complete -c batten -n "__fish_batten_using_subcommand help; and __fish_seen_subcommand_from capture" -f -a "list" -d 'List this repository\'s captures as handles, in a fixed order' @@ -2129,6 +2200,8 @@ complete -c batten -n "__fish_batten_using_subcommand help; and __fish_seen_subc complete -c batten -n "__fish_batten_using_subcommand help; and __fish_seen_subcommand_from generate" -f -a "markdown" -d 'Emit the whole command surface as one markdown reference, on stdout' complete -c batten -n "__fish_batten_using_subcommand help; and __fish_seen_subcommand_from generate" -f -a "schema" -d 'Emit the JSON Schema for a config or policy-input surface, derived from the types that define it' complete -c batten -n "__fish_batten_using_subcommand help; and __fish_seen_subcommand_from perf" -f -a "pair" -d 'Measure this branch and its merge base back to back on one machine, and print both arms as paired records' +complete -c batten -n "__fish_batten_using_subcommand help; and __fish_seen_subcommand_from mutate" -f -a "sweep" -d 'Apply every declared mutation to its source and report the ones its declared suite did not catch' +complete -c batten -n "__fish_batten_using_subcommand help; and __fish_seen_subcommand_from mutate" -f -a "census" -d 'Report every gate in the tree that is neither mutation-enforced nor carrying a filed exemption' complete -c batten -n "__fish_batten_using_subcommand help; and __fish_seen_subcommand_from policy" -f -a "budget" -d 'Judge the always-loaded instruction set against its declared token budget' complete -c batten -n "__fish_batten_using_subcommand help; and __fish_seen_subcommand_from policy" -f -a "test" -d 'Run each registered module\'s own `test_` rules and report the predicates none exercised' complete -c batten -n "__fish_batten_using_subcommand help; and __fish_seen_subcommand_from policy" -f -a "tools" -d 'Print the tool names the mediated-call rows decide, one per line' diff --git a/completions/batten.zsh b/completions/batten.zsh index 1ad7a431a..d87590d5d 100644 --- a/completions/batten.zsh +++ b/completions/batten.zsh @@ -1391,6 +1391,133 @@ esac ;; esac ;; +(mutate) +_arguments "${_arguments_options[@]}" : \ +'--strictness=[Raise how strictly gates apply (an override may only tighten policy)]: :((permissive\:"Advisory\: findings are reported without failing the run" +standard\:"The default\: a finding is a violation" +strict\:"Everything \`Standard\` fails on, plus anything advisory"))' \ +'--config-from=[Read the committed config from a git ref (e.g. origin/main) instead of the working tree]: :_default' \ +'--config-in=[Read the committed config from this directory instead of the directory being judged]: :_default' \ +'--log-level=[Set the verbosity rung by name]: :((silent\:"Say nothing but a verdict or a usage error" +quiet\:"Suppress ordinary progress; keep warnings" +normal\:"The default" +verbose\:"Explain what is being checked" +debug\:"Add resolution detail" +trace\:"Add everything"))' \ +'--fail-on-warning[Promote a warn-severity finding to a violation (an override may only turn this on)]' \ +'*--silent[Say nothing but a verdict or a usage error]' \ +'*-q[Suppress ordinary progress (repeatable\: -qq is silent)]' \ +'*--quiet[Suppress ordinary progress (repeatable\: -qq is silent)]' \ +'*-v[Explain what is being checked (repeatable\: -vv is debug)]' \ +'*--verbose[Explain what is being checked (repeatable\: -vv is debug)]' \ +'*--debug[Add resolution detail]' \ +'*--trace[Add everything]' \ +'--no-color[Never colour stderr, whatever it is attached to]' \ +'--no-input[Never prompt; treat the run as unattended]' \ +'-y[Confirm a destructive operation that would otherwise refuse]' \ +'--yes[Confirm a destructive operation that would otherwise refuse]' \ +'-h[Print help (see more with '\''--help'\'')]' \ +'--help[Print help (see more with '\''--help'\'')]' \ +":: :_batten__subcmd__mutate_commands" \ +"*::: :->mutate" \ +&& ret=0 + + case $state in + (mutate) + words=($line[1] "${words[@]}") + (( CURRENT += 1 )) + curcontext="${curcontext%:*:*}:batten-mutate-command-$line[1]:" + case $line[1] in + (sweep) +_arguments "${_arguments_options[@]}" : \ +'--strictness=[Raise how strictly gates apply (an override may only tighten policy)]: :((permissive\:"Advisory\: findings are reported without failing the run" +standard\:"The default\: a finding is a violation" +strict\:"Everything \`Standard\` fails on, plus anything advisory"))' \ +'--config-from=[Read the committed config from a git ref (e.g. origin/main) instead of the working tree]: :_default' \ +'--config-in=[Read the committed config from this directory instead of the directory being judged]: :_default' \ +'--log-level=[Set the verbosity rung by name]: :((silent\:"Say nothing but a verdict or a usage error" +quiet\:"Suppress ordinary progress; keep warnings" +normal\:"The default" +verbose\:"Explain what is being checked" +debug\:"Add resolution detail" +trace\:"Add everything"))' \ +'--fail-on-warning[Promote a warn-severity finding to a violation (an override may only turn this on)]' \ +'*--silent[Say nothing but a verdict or a usage error]' \ +'*-q[Suppress ordinary progress (repeatable\: -qq is silent)]' \ +'*--quiet[Suppress ordinary progress (repeatable\: -qq is silent)]' \ +'*-v[Explain what is being checked (repeatable\: -vv is debug)]' \ +'*--verbose[Explain what is being checked (repeatable\: -vv is debug)]' \ +'*--debug[Add resolution detail]' \ +'*--trace[Add everything]' \ +'--no-color[Never colour stderr, whatever it is attached to]' \ +'--no-input[Never prompt; treat the run as unattended]' \ +'-y[Confirm a destructive operation that would otherwise refuse]' \ +'--yes[Confirm a destructive operation that would otherwise refuse]' \ +'-h[Print help (see more with '\''--help'\'')]' \ +'--help[Print help (see more with '\''--help'\'')]' \ +&& ret=0 +;; +(census) +_arguments "${_arguments_options[@]}" : \ +'--strictness=[Raise how strictly gates apply (an override may only tighten policy)]: :((permissive\:"Advisory\: findings are reported without failing the run" +standard\:"The default\: a finding is a violation" +strict\:"Everything \`Standard\` fails on, plus anything advisory"))' \ +'--config-from=[Read the committed config from a git ref (e.g. origin/main) instead of the working tree]: :_default' \ +'--config-in=[Read the committed config from this directory instead of the directory being judged]: :_default' \ +'--log-level=[Set the verbosity rung by name]: :((silent\:"Say nothing but a verdict or a usage error" +quiet\:"Suppress ordinary progress; keep warnings" +normal\:"The default" +verbose\:"Explain what is being checked" +debug\:"Add resolution detail" +trace\:"Add everything"))' \ +'--fail-on-warning[Promote a warn-severity finding to a violation (an override may only turn this on)]' \ +'*--silent[Say nothing but a verdict or a usage error]' \ +'*-q[Suppress ordinary progress (repeatable\: -qq is silent)]' \ +'*--quiet[Suppress ordinary progress (repeatable\: -qq is silent)]' \ +'*-v[Explain what is being checked (repeatable\: -vv is debug)]' \ +'*--verbose[Explain what is being checked (repeatable\: -vv is debug)]' \ +'*--debug[Add resolution detail]' \ +'*--trace[Add everything]' \ +'--no-color[Never colour stderr, whatever it is attached to]' \ +'--no-input[Never prompt; treat the run as unattended]' \ +'-y[Confirm a destructive operation that would otherwise refuse]' \ +'--yes[Confirm a destructive operation that would otherwise refuse]' \ +'-h[Print help (see more with '\''--help'\'')]' \ +'--help[Print help (see more with '\''--help'\'')]' \ +&& ret=0 +;; +(help) +_arguments "${_arguments_options[@]}" : \ +":: :_batten__subcmd__mutate__subcmd__help_commands" \ +"*::: :->help" \ +&& ret=0 + + case $state in + (help) + words=($line[1] "${words[@]}") + (( CURRENT += 1 )) + curcontext="${curcontext%:*:*}:batten-mutate-help-command-$line[1]:" + case $line[1] in + (sweep) +_arguments "${_arguments_options[@]}" : \ +&& ret=0 +;; +(census) +_arguments "${_arguments_options[@]}" : \ +&& ret=0 +;; +(help) +_arguments "${_arguments_options[@]}" : \ +&& ret=0 +;; + esac + ;; +esac +;; + esac + ;; +esac +;; (policy) _arguments "${_arguments_options[@]}" : \ '--strictness=[Raise how strictly gates apply (an override may only tighten policy)]: :((permissive\:"Advisory\: findings are reported without failing the run" @@ -3855,6 +3982,30 @@ _arguments "${_arguments_options[@]}" : \ ;; esac ;; +(mutate) +_arguments "${_arguments_options[@]}" : \ +":: :_batten__subcmd__help__subcmd__mutate_commands" \ +"*::: :->mutate" \ +&& ret=0 + + case $state in + (mutate) + words=($line[1] "${words[@]}") + (( CURRENT += 1 )) + curcontext="${curcontext%:*:*}:batten-help-mutate-command-$line[1]:" + case $line[1] in + (sweep) +_arguments "${_arguments_options[@]}" : \ +&& ret=0 +;; +(census) +_arguments "${_arguments_options[@]}" : \ +&& ret=0 +;; + esac + ;; +esac +;; (policy) _arguments "${_arguments_options[@]}" : \ ":: :_batten__subcmd__help__subcmd__policy_commands" \ @@ -4297,6 +4448,7 @@ _batten_commands() { 'baseline:Record the findings that already exist, so only new ones fail' \ 'generate:Emit artifacts derived from the command spec, on stdout' \ 'perf:Measure this repository'\''s own invocation cost' \ +'mutate:Decide whether this repository'\''s gates discriminate, rather than merely parse' \ 'policy:Inspect the thresholds and path sets this repository holds itself to' \ 'commit:The shape a commit must take here\: what its subject may say' \ 'ready:Whether an issue'\''s Ready block satisfies the checkable clauses of the gate' \ @@ -4811,6 +4963,7 @@ _batten__subcmd__help_commands() { 'baseline:Record the findings that already exist, so only new ones fail' \ 'generate:Emit artifacts derived from the command spec, on stdout' \ 'perf:Measure this repository'\''s own invocation cost' \ +'mutate:Decide whether this repository'\''s gates discriminate, rather than merely parse' \ 'policy:Inspect the thresholds and path sets this repository holds itself to' \ 'commit:The shape a commit must take here\: what its subject may say' \ 'ready:Whether an issue'\''s Ready block satisfies the checkable clauses of the gate' \ @@ -5085,6 +5238,24 @@ _batten__subcmd__help__subcmd__mcp__subcmd__call_commands() { local commands; commands=() _describe -t commands 'batten help mcp call commands' commands "$@" } +(( $+functions[_batten__subcmd__help__subcmd__mutate_commands] )) || +_batten__subcmd__help__subcmd__mutate_commands() { + local commands; commands=( +'sweep:Apply every declared mutation to its source and report the ones its declared suite did not catch' \ +'census:Report every gate in the tree that is neither mutation-enforced nor carrying a filed exemption' \ + ) + _describe -t commands 'batten help mutate commands' commands "$@" +} +(( $+functions[_batten__subcmd__help__subcmd__mutate__subcmd__census_commands] )) || +_batten__subcmd__help__subcmd__mutate__subcmd__census_commands() { + local commands; commands=() + _describe -t commands 'batten help mutate census commands' commands "$@" +} +(( $+functions[_batten__subcmd__help__subcmd__mutate__subcmd__sweep_commands] )) || +_batten__subcmd__help__subcmd__mutate__subcmd__sweep_commands() { + local commands; commands=() + _describe -t commands 'batten help mutate sweep commands' commands "$@" +} (( $+functions[_batten__subcmd__help__subcmd__override_commands] )) || _batten__subcmd__help__subcmd__override_commands() { local commands; commands=( @@ -5390,6 +5561,49 @@ _batten__subcmd__mcp__subcmd__help__subcmd__help_commands() { local commands; commands=() _describe -t commands 'batten mcp help help commands' commands "$@" } +(( $+functions[_batten__subcmd__mutate_commands] )) || +_batten__subcmd__mutate_commands() { + local commands; commands=( +'sweep:Apply every declared mutation to its source and report the ones its declared suite did not catch' \ +'census:Report every gate in the tree that is neither mutation-enforced nor carrying a filed exemption' \ +'help:Print this message or the help of the given subcommand(s)' \ + ) + _describe -t commands 'batten mutate commands' commands "$@" +} +(( $+functions[_batten__subcmd__mutate__subcmd__census_commands] )) || +_batten__subcmd__mutate__subcmd__census_commands() { + local commands; commands=() + _describe -t commands 'batten mutate census commands' commands "$@" +} +(( $+functions[_batten__subcmd__mutate__subcmd__help_commands] )) || +_batten__subcmd__mutate__subcmd__help_commands() { + local commands; commands=( +'sweep:Apply every declared mutation to its source and report the ones its declared suite did not catch' \ +'census:Report every gate in the tree that is neither mutation-enforced nor carrying a filed exemption' \ +'help:Print this message or the help of the given subcommand(s)' \ + ) + _describe -t commands 'batten mutate help commands' commands "$@" +} +(( $+functions[_batten__subcmd__mutate__subcmd__help__subcmd__census_commands] )) || +_batten__subcmd__mutate__subcmd__help__subcmd__census_commands() { + local commands; commands=() + _describe -t commands 'batten mutate help census commands' commands "$@" +} +(( $+functions[_batten__subcmd__mutate__subcmd__help__subcmd__help_commands] )) || +_batten__subcmd__mutate__subcmd__help__subcmd__help_commands() { + local commands; commands=() + _describe -t commands 'batten mutate help help commands' commands "$@" +} +(( $+functions[_batten__subcmd__mutate__subcmd__help__subcmd__sweep_commands] )) || +_batten__subcmd__mutate__subcmd__help__subcmd__sweep_commands() { + local commands; commands=() + _describe -t commands 'batten mutate help sweep commands' commands "$@" +} +(( $+functions[_batten__subcmd__mutate__subcmd__sweep_commands] )) || +_batten__subcmd__mutate__subcmd__sweep_commands() { + local commands; commands=() + _describe -t commands 'batten mutate sweep commands' commands "$@" +} (( $+functions[_batten__subcmd__override_commands] )) || _batten__subcmd__override_commands() { local commands; commands=( diff --git a/crates/batten/src/cli.rs b/crates/batten/src/cli.rs index 04c3ab22a..f634cc467 100644 --- a/crates/batten/src/cli.rs +++ b/crates/batten/src/cli.rs @@ -298,6 +298,27 @@ pub enum Command { /// The chosen sub-verb. command: PrCommand, }, + /// Mutation coverage over the declared gate set (CLOUD-1267), ported off + /// `mise-tasks/mutant.sh` and `mise-tasks/mutant-census.sh`. + /// + /// Appended for the same reason `Checks` is: a shifted discriminant is a + /// break the crate has to declare. + Mutate { + /// The chosen sub-verb. + command: MutateCommand, + }, +} + +/// Subcommands of `mutate`. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[non_exhaustive] +pub enum MutateCommand { + /// Apply every declared mutation and report the ones its suite did not + /// catch. + Sweep, + /// Report every gate that is neither enforced nor carrying a filed + /// exemption, in both directions. + Census, } /// Subcommands of `pr`. @@ -1122,6 +1143,14 @@ fn semver_of(matches: &ArgMatches) -> Option { } } +fn mutate_of(matches: &ArgMatches) -> Option { + match matches.subcommand()? { + ("sweep", _) => Some(MutateCommand::Sweep), + ("census", _) => Some(MutateCommand::Census), + _ => None, + } +} + fn perf_of(matches: &ArgMatches) -> Option { match matches.subcommand()? { ("pair", matches) => Some(PerfCommand::Pair { @@ -1490,6 +1519,7 @@ fn command_of((name, matches): (&str, &ArgMatches)) -> Option { "commit" => commit_of(matches).map(|command| Command::Commit { command }), "semver" => semver_of(matches).map(|command| Command::Semver { command }), "perf" => perf_of(matches).map(|command| Command::Perf { command }), + "mutate" => mutate_of(matches).map(|command| Command::Mutate { command }), "ready" => ready_of(matches).map(|command| Command::Ready { command }), "claim" => claim_of(matches).map(|command| Command::Claim { command }), "checks" => checks_of(matches).map(|command| Command::Checks { command }), diff --git a/crates/batten/src/lib.rs b/crates/batten/src/lib.rs index 21464a5f3..99c1e9ada 100644 --- a/crates/batten/src/lib.rs +++ b/crates/batten/src/lib.rs @@ -54,6 +54,7 @@ pub mod lint; pub mod markers; pub mod mcp; pub mod mint; +pub mod mutate; pub mod output; pub mod outputs; /// The in-process patch identity: what a change IS, independent of the commit @@ -244,6 +245,7 @@ pub fn run(cli: Cli, mode: Mode, out: &mut dyn Write, err: &mut dyn Write) -> Re Some(Command::Override { command }) => run_override(command, &overrides, out, err), Some(Command::Semver { command }) => run_semver(command, mode, out, err), Some(Command::Perf { command }) => run_perf(command, out, err), + Some(Command::Mutate { command }) => run_mutate(command, out, err), Some(Command::Wiring { command }) => run_wiring(&command, mode, err), // The refinement gate and the pull-time claim (CLOUD-1121). Both read // the payload the caller supplies — or, under `--issue`, the one the @@ -3808,6 +3810,121 @@ fn run_perf( } } +/// `batten mutate`: does each declared gate have a mutation its declared suite +/// is proven to catch (CLOUD-418, CLOUD-1267)? +/// +/// **The report is the deliverable and the exit code is the verdict**, and the +/// two say different things on purpose. Every finding reaches stdout as a +/// pointer — gate, mutation id, case — because the workflow that runs this cats +/// the file into a step summary, and a run that fails without publishing what it +/// found sends the reader back to re-run a sweep that costs the better part of +/// an hour. The `::error::` summary on stderr carries the count and nothing else. +/// +/// Exit follows the one table: `2` where the sweep decided against the tree, `3` +/// where it could not look, and the split is the acceptance rather than a +/// nicety — a gate whose declared suite cannot be resolved or run must never be +/// reported as "every mutation caught". +fn run_mutate( + command: cli::MutateCommand, + out: &mut dyn Write, + err: &mut dyn Write, +) -> Result { + // ABSOLUTE, and this is a defect rather than tidiness. Every suite runs with + // its cwd inside the STAGED tree, so a relative root reaches the child as a + // path resolved against the copy: the vendored runner is not there under + // `./tests/…`, and `CARGO_TARGET_DIR=./target` would put the build inside + // the tree being mutated. Measured on the first live sweep, which could not + // run `./tests/bats/bin/bats` at all. + let anchor = hook_authority_root(); + let resolved = anchor.canonicalize().unwrap_or_else(|_| anchor.to_owned()); + let root: &Path = &resolved; + let names = match mutate::enforced_set() { + Ok(names) => names, + Err(reason) => { + writeln!(err, "::error:: mutate: {reason}")?; + return Ok(ExitCode::Usage); + } + }; + match command { + cli::MutateCommand::Census => { + let census = mutate::census(root, &names); + for (subject, verdict) in &census.findings { + writeln!(out, "{subject} {verdict}")?; + } + if census.findings.is_empty() { + writeln!( + out, + "mutate census: {} gate(s), every one enforced or exempt by a filed row", + census.subjects + )?; + return Ok(ExitCode::Success); + } + writeln!( + err, + "::error:: mutate census: {} violation(s) over {} gate(s) — a gate outside the \ + enforced set is covered by nothing stronger than \"its suite is green\", which \ + CLOUD-418 measured as insufficient four times. Declare a #MUTANT row and add the \ + name, or carry a #MUTANT-EXEMPT naming the issue that owns the gap.", + census.findings.len(), + census.subjects + )?; + Ok(ExitCode::Violation) + } + cli::MutateCommand::Sweep => { + // The staged tree lives beside the build artefacts rather than in + // the system temporary directory, and it PERSISTS between runs. Both + // are the same economy: a declared suite can be a compiled tier, and + // a tree re-created from scratch every sweep would rebuild the whole + // crate every sweep. `Staged::new` prunes what the tracked set no + // longer names and re-copies only what differs, so a persisted tree + // still carries exactly the tracked bytes. + let work = root.join("target").join("mutate"); + std::fs::create_dir_all(&work)?; + let sweep = match mutate::sweep(root, &names, work) { + Ok(sweep) => sweep, + Err(reason) => { + writeln!(err, "::error:: mutate: {reason}")?; + return Ok(ExitCode::Internal); + } + }; + for finding in &sweep.findings { + writeln!(out, "{finding}")?; + } + let code = sweep.code(); + if code == ExitCode::Success { + writeln!( + out, + "mutate sweep: {} declared mutation(s) across {} gate(s), every one caught", + sweep.declared, sweep.gates + )?; + return Ok(code); + } + // THE TWO CLASSES ARE COUNTED APART. A could-not-look is not a + // suite that passed on broken code — it is a suite nothing could + // ask — and adding them produced `124 of 0 declared mutation(s) … + // were not caught`, a coverage verdict over a denominator of zero. + let unlooked = sweep.unlooked(); + let uncaught = sweep.findings.len() - unlooked; + if uncaught > 0 { + writeln!( + err, + "::error:: mutate sweep: {} of {} declared mutation(s) across {} gate(s) were \ + not caught — a suite that passes on broken code is not coverage", + uncaught, sweep.declared, sweep.gates + )?; + } + if unlooked > 0 { + writeln!( + err, + "::error:: mutate sweep: {unlooked} declared mutation(s) could not be looked \ + at — an unresolvable gate or suite is not a pass" + )?; + } + Ok(code) + } + } +} + fn run_semver( command: SemverCommand, mode: Mode, diff --git a/crates/batten/src/mutate.rs b/crates/batten/src/mutate.rs new file mode 100644 index 000000000..522188d3f --- /dev/null +++ b/crates/batten/src/mutate.rs @@ -0,0 +1,1377 @@ +//! Mutation coverage over the declared gate set (CLOUD-418, CLOUD-1267). +//! +//! # What this decides, and why nothing else in the tree decides it +//! +//! The obligation this repository already had was *"a rule ships with a runnable +//! gate"* — a gate that EXISTS. Nothing required evidence that it +//! DISCRIMINATES, and a test which passes on both the fixed and the broken code +//! satisfies every other rule here. That is this repo's most-repeated failure: +//! `land`'s refusal branch was dead for months (CLOUD-235), `timeout-check`'s +//! budgets were placeholders that could not fire (CLOUD-352), a shape rule whose +//! `pattern` was a program could never match and read as coverage (CLOUD-401) — +//! and then it happened live while building the landing lease, where a +//! concurrency test written for a real race PASSED ON THE BROKEN CODE. +//! +//! So: a gate is covered when a stated one-line corruption of it makes a NAMED +//! case in its declared suite go RED. **A pass under mutation is the defect.** +//! +//! # Why this is a verb rather than the shell task it replaces +//! +//! Its predecessor was `mise-tasks/mutant.sh`, and the predecessor could not +//! reach a single policy module: it resolved a gate's SOURCE with a Rego +//! fallback and its SUITE as `tests/$gate.bats` unconditionally, so a mutation +//! applied to a `.rego` module had no suite that could turn red. Measured at the +//! time of the port: 32 modules, 32 `#MUTANT-EXEMPT` rows, 29 of them citing +//! that exact hole, 0 with a bats suite, and 141 compiled-binary tiers the +//! runner could not see. +//! +//! That hole was unfixable in place. `V-SHELL-RULE-EDITED` declares one route, +//! `R-PORT-AND-RETIRE`, with no override and no `bypass_env`, so the coverage +//! mechanism could only be retired (CLOUD-1111 enumerated the three resolutions +//! and rejected the two that meant editing the program). This module is that +//! retirement. +//! +//! # The effect class +//! +//! `Cost::Effect` on the spawning side: it stages a tracked tree and runs +//! suites, so it cannot be `check`, which is declared `read` and structurally +//! cannot spawn (§5). CLOUD-1171 settled that the engine spawning is legitimate +//! — `batten perf` ships and runs hyperfine — and `perf.rs` is the shape this +//! follows. +//! +//! # The one behavioural change, and everything conserved around it +//! +//! **A gate's suite comes from a DECLARED mapping**: `#MUTANT-SUITE ` +//! beside the `#MUTANT` rows, defaulting to `tests/.bats` when absent. A +//! `.rego` module can therefore name `crates/batten/tests/.rs` — the tier +//! that actually drives the engine — as the suite a mutation must redden. +//! +//! Everything else is conserved from the predecessor, one signal at a time, +//! because each of them is a could-not-look and collapsing one into a pass is +//! the defect this exists to refuse: `no-such-gate`, `no-suite`, +//! `no-mutant-declared` (the anti-vacuity term — a listed gate with no +//! declaration FAILS, it is not skipped), `malformed-row`, `case-already-red`, +//! `names-no-case`, `filter-names-every-case`, `unappliable-mutation`, +//! `inert-mutation`, `self-mutating-row` and `SURVIVED`. +//! +//! Four harness properties travel with them: +//! +//! * **The tracked file is never mutated in place.** Mutating in place staged a +//! mutant into a pushed commit on 2026-08-12; every run builds a throwaway +//! copy of the tracked tree and mutates THAT. +//! * **The copy is a repository.** A suite whose gate asks git for its own +//! enclosing worktree otherwise answered about whatever repository enclosed +//! `$TMPDIR`, and the case came back red for a reason that had nothing to do +//! with the mutation. +//! * **The tree is restored between rows.** A gate composing over a sibling was +//! otherwise judged against the sibling's mutant, so the survivor it reported +//! changed with the sweep ORDER — worse than a missed one. +//! * **The case must be GREEN before it is mutated.** "Red under mutation" is +//! only evidence if the row was green without it; a case that can never pass +//! is red either way and every mutation aimed at it reads as caught. +//! +//! # `#MUTANT-OWNER` is not an exemption +//! +//! A file may declare `#MUTANT-OWNER |`. It is echoed on that +//! file's survivor lines and **changes no exit code** — the sweep is still red. +//! It exists so a predicate already known to be dead is reported with the row +//! that owns it rather than as an anonymous survivor. A declaration that +//! suppressed the finding would be the laundering this whole module refuses. +//! +//! # Output and exit +//! +//! Pointer-only (non-negotiable rule 4): the gate, the mutation id and the case. +//! Never a diff, and never a line of a mutated source. The exit contract is +//! [`crate::ExitCode`]'s: `0` every declared mutation caught, `2` the verdict (a +//! survivor, or any per-row finding), `3` could-not-look — a gate whose declared +//! suite cannot be resolved or run — and `1` usage. + +use std::collections::BTreeMap; +use std::fmt; +use std::path::{Path, PathBuf}; + +use anyhow::{Context as _, Result, bail}; + +/// The declaration markers, each a `#` comment in both bash and Rego so a +/// declaration can live beside the code it describes in either language. +/// +/// Beside the code rather than in a manifest, for the reason `step-receipt`'s +/// spec table lives in `step-receipt`: a declaration in a second file is a +/// second authority that drifts. +const ROW: &str = "#MUTANT "; +const SUITE: &str = "#MUTANT-SUITE "; +const OWNER: &str = "#MUTANT-OWNER "; +const EXEMPT: &str = "#MUTANT-EXEMPT "; + +/// The vendored bats runner, relative to the repository root. +const BATS: &str = "tests/bats/bin/bats"; + +/// Where a preset's modules live, relative to the repository root. +const PRESETS: &str = "crates/batten/src/policy/presets"; + +/// One declared mutation: `#MUTANT |