Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
3ee05e2
feat(mutate): a spawning-side verb whose gate suite is a DECLARED path
wenzowski Aug 31, 2026
6be59d4
refactor(ci)!: retire the mutation runner onto `batten mutate`, and p…
wenzowski Sep 1, 2026
fbd3f41
fix(mutate): repair seven declared mutations the first reachable swee…
wenzowski Sep 1, 2026
ae7b129
test(policy): name CLOUD-989 as the owner of four masked mutations
wenzowski Sep 1, 2026
9d0e54e
fix(mutant): re-aim graph-check's receipt row at the store CLOUD-312 …
wenzowski Sep 1, 2026
068ef9f
fix(mutate): resolve the lent runner inside the arm that can use it
wenzowski Sep 1, 2026
0b40c57
fix(mutate): place the new module, carry the fixture arms, and leave …
wenzowski Sep 1, 2026
fcfb2fe
feat(mutate): state the new surface rows where the spec is declared b…
wenzowski Sep 1, 2026
10cb86e
fix(mutate): count could-not-look apart from uncaught in the sweep su…
wenzowski Sep 1, 2026
2afe7a8
feat(mutate): regenerate the golden schema and correct the noun's eff…
wenzowski Sep 1, 2026
bcfdbe8
feat(mutate): regenerate the completions and man pages the new verb d…
wenzowski Sep 1, 2026
d9a0766
feat(mutate): regenerate the derived artifacts across the mcp rebase
wenzowski Sep 1, 2026
1b1c14f
test(policy): declare the landing-loop preset's mutation, which main …
wenzowski Sep 1, 2026
40be2c9
fix(mutate): reconcile the enforced set and the artifacts across main…
wenzowski Sep 1, 2026
23c246b
fix(mutate): gate the bats arm on unix, where the vendored runner can…
wenzowski Sep 1, 2026
bd37c37
fix(mutate): gate the bats fixture constants with the arm that reads …
wenzowski Sep 1, 2026
5a9924b
fix(mutate): resolve a declared suite without naming a cargo target
wenzowski Sep 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 22 additions & 13 deletions .claude/rules/policy-modules.md
Original file line number Diff line number Diff line change
Expand Up @@ -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/<gate>.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/<x>.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/<gate>.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 <KEY>|<why>` 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
Expand Down
33 changes: 19 additions & 14 deletions .github/workflows/mutant.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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/<gate>.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/<gate>.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:
Expand Down
20 changes: 20 additions & 0 deletions .serena/memories/core.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <path>` 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
Expand Down
2 changes: 0 additions & 2 deletions bench/suites/RESULTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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` |
Expand Down Expand Up @@ -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` |
Expand Down
Loading