diff --git a/.claude/rules/toolchain.md b/.claude/rules/toolchain.md index 15a20c12b..f62b68756 100644 --- a/.claude/rules/toolchain.md +++ b/.claude/rules/toolchain.md @@ -274,16 +274,28 @@ call` with no `CLOUD-*` key **in that same paragraph** stops the lap. Two open - `filed-here-check` is that stop's sibling, and the second half of CLOUD-514: `deferral-check` prices a decision left with no home, this prices a home opened instead of a fix. `board-write-record` (a `PostToolUse` body) records every row - this branch put on the board — kind, id, the tracker's `updatedAt`, and the - `ready-lint` verdict over the body the **tracker returned** — and `land` calls - this beside `deferral-check`, refusing when a row this branch CREATED was stored - `unready`. Three states rather than two: `ready` passes, `unready` refuses, and + this branch put on the board — kind, id, the tracker's `updatedAt`, the + `ready-lint` verdict over the body the **tracker returned**, and the diff + overlap — and `land` calls this beside `deferral-check`, refusing when a row + this branch CREATED was stored `unready`. Three states rather than two: `ready` passes, `unready` refuses, and `-` — the recorder could not lint — passes, because reading "not answered" as "refused" turns a verdict about the environment into one about the row. Comments are recorded and never gated: a comment on the row that already owns a finding is the honest common case, and pricing it pushes the pressure toward - silence, which is the failure `finding-sink-check` exists to catch. The verdict - is unforgeable by the author for the reason the receipt pattern usually is not — + silence, which is the failure `finding-sink-check` exists to catch. + **A second refusal prices PROXIMITY** (`filed-over-own-diff`, CLOUD-514 phase + 3), because the first prices only refinement and a Ready block is prose — + measured, four rows filed in three and a half minutes and every one recorded + `ready`, so the toll certified the punts instead of reversing them. The + recorder's fifth column is `board-diff-overlap`: the tracked paths the row's + body names intersected with `origin/main...HEAD`, basenames resolved (exact + matching found none of the three real rows) and an ambiguous one resolving to + nothing. Non-zero stops the lap, and the load-bearing difference is that there + is **no prose remedy** — fix it here, comment on the row that owns it, file it + after landing from a clean tree, or `BATTEN_FILED_HERE_OVERLAP=1`, which + records which rows it overrode. It prices filing against **the diff** and + claims nothing wider: a punt about code the branch never touched is invisible + to it. The verdict is unforgeable by the author for the reason the receipt pattern usually is not — `ready-lint` over a payload the caller assembles was measured green three times against text in a local file, once under an id no row carried. Fails open on an absent record, and a branch predating the recorder can never have one: the store diff --git a/fuzz/Cargo.lock b/fuzz/Cargo.lock index 758d6a916..f95de1320 100644 --- a/fuzz/Cargo.lock +++ b/fuzz/Cargo.lock @@ -102,7 +102,7 @@ checksum = "7d902e3d592a523def97af8f317b08ce16b7ab854c1985a0c671e6f15cebc236" [[package]] name = "batten" -version = "0.0.88" +version = "0.0.89" dependencies = [ "anyhow", "clap", diff --git a/mise-tasks/board-diff-overlap b/mise-tasks/board-diff-overlap new file mode 100755 index 000000000..87753cf7b --- /dev/null +++ b/mise-tasks/board-diff-overlap @@ -0,0 +1,118 @@ +#!/usr/bin/env bash +#MISE description="Effect: how many paths an issue body names that this branch is also changing (body on stdin; prints the count and the paths)" +# +# CLOUD-514's missing half. `filed-here-check` charges a new row a complete Ready +# block, on the theory that filing is cheap and fixing is expensive so the toll +# reverses the arithmetic. Its own header stated the bound honestly: it prices +# "without anything judging whether a given spin-off was lazy", and "it does not +# compare the row to the diff". +# +# WHY THE TOLL DID NOT BITE. Measured 2026-08-20: four rows filed in three and a +# half minutes, then twelve spent writing four Ready blocks to pay for them, and +# `board-write-record` recorded every one `ready`. A toll denominated in PROSE is +# denominated in the one currency an agent has without limit, so it reversed +# nothing — it certified the punts. CLOUD-514 wrote the re-open predicate this +# satisfies ("a Ready block written to satisfy `ready-lint` rather than to be +# worked"), and its acceptance — "the cheapest path through the gate for a defect +# in the branch's own diff is to fix it" — was unmet. +# +# WHAT THIS DECIDES, AND WHY IT IS NOT A JUDGEMENT. One fact: how many paths the +# body names that this branch is also changing. A set intersection over two file +# lists, the shape the protected-path gate already uses ({verb} x {protected}). +# It scores no prose, compares no semantics and infers no intent, so +# non-negotiable 3 holds. Whether a given spin-off was lazy stays exactly as +# unjudged as `filed-here-check` leaves it. +# +# BASENAMES RESOLVE, AND THAT IS MEASURED RATHER THAN ASSUMED. Bodies here write +# `git.rs:107`, not `crates/batten/src/git.rs`. Against the three rows this was +# built from, exact path matching finds ZERO and basename resolution finds all +# three — so exact matching would have shipped a sensor blind to its own corpus. +# An AMBIGUOUS basename resolves to NOTHING rather than to a guess, which is the +# "could not look" reading this repo draws everywhere; 28 of 530 tracked +# basenames are ambiguous in this tree. +# +# POINTER-ONLY IS STRUCTURAL, not careful: only paths TRACKED IN THIS REPOSITORY +# can reach the output, so a body's prose, a customer name or a pasted credential +# cannot (non-negotiable 4). +# +# The Python is inline for `macos-link-check`'s reason — one file, so the code +# and the mutations that corrupt it cannot drift into two authorities — but fed +# through a QUOTED heredoc rather than `-c "…"`, so nothing inside it is subject +# to a second round of shell expansion. Both inputs arrive as env vars, which +# leaves stdin free for the script itself. +# +# Usage: board-diff-overlap (an issue body on stdin) +# Prints: ` ...` on success, or `-` when it could not look. +# Exit 0 always — this is a sensor; `filed-here-check` is the gate. +# +# The mutation drops the basename arm, which is the whole reason it sees +# anything: every body in the corpus names `git.rs`, not the tracked path. +#MUTANT overlap-exact-only|s/^ cands = by_base.get(.*)$/ cands = []/|a short form resolves to the tracked path +# And an ambiguous basename must stay unresolved: guessing one of several is a +# wrong answer wearing a right answer's shape. +#MUTANT overlap-guesses-ambiguous|s/if len(cands) == 1:/if len(cands) >= 1:/|an ambiguous basename resolves to nothing +# The intersection is with what this branch CHANGES, not with what it tracks. Drop +# that term and every row naming any file in the repository is refused, which is a +# gate nobody can work under and therefore a gate that gets switched off. +#MUTANT overlap-ignores-the-diff|s/^overlap = sorted(named & changed)$/overlap = sorted(named)/|a row naming only untouched files reports nothing +set -uo pipefail + +body=$(cat) || { + echo - + exit 0 +} +[ -n "$body" ] || { + echo - + exit 0 +} + +# Both halves come from git, and either being unavailable is "could not look". +changed=$(git diff --name-only origin/main...HEAD 2>/dev/null) || { + echo - + exit 0 +} +tracked=$(git ls-files 2>/dev/null) || { + echo - + exit 0 +} +[ -n "$tracked" ] || { + echo - + exit 0 +} + +BODY="$body" CHANGED="$changed" TRACKED="$tracked" python3 - <<'PY' 2>/dev/null || echo - +import collections +import os +import re + +tracked = [line for line in os.environ["TRACKED"].splitlines() if line] +changed = {line for line in os.environ["CHANGED"].splitlines() if line} +body = os.environ["BODY"] + +by_base = collections.defaultdict(list) +for path in tracked: + by_base[path.rsplit("/", 1)[-1]].append(path) +exact = set(tracked) + +# Three shapes, because bodies here use all three: a dotted path or filename, a +# backticked `mise-tasks/`, and a bare backticked task name with no +# extension at all. +tokens = set(re.findall(r"[A-Za-z0-9_][A-Za-z0-9_./-]*\.[A-Za-z0-9]+", body)) +tokens |= set(re.findall(r"`(mise-tasks/[A-Za-z0-9_.-]+)`", body)) +tokens |= {m for m in re.findall(r"`([a-z0-9][a-z0-9-]{3,})`", body) if m in by_base} + +named = set() +for token in tokens: + token = token.rstrip(".,;:") + if token in exact: + named.add(token) + continue + cands = by_base.get(token.rsplit("/", 1)[-1], []) + # Exactly one candidate resolves. Several is ambiguous and resolves to none: + # guessing which file was meant is a wrong answer wearing a right one's shape. + if len(cands) == 1: + named.add(cands[0]) + +overlap = sorted(named & changed) +print(f"{len(overlap)} {' '.join(overlap)}".strip() if overlap else "0") +PY diff --git a/mise-tasks/board-write-record b/mise-tasks/board-write-record index 52759f50f..550485783 100755 --- a/mise-tasks/board-write-record +++ b/mise-tasks/board-write-record @@ -44,8 +44,24 @@ # board does not have. # # POINTER-ONLY IS LOAD-BEARING HERE (non-negotiable 4), not decorative: the text -# this reads is the entire issue body. Four fields reach the file — kind, id, -# updatedAt, verdict — and nothing is ever printed. +# this reads is the entire issue body. Five fields reach the file — kind, id, +# updatedAt, verdict, and the diff overlap — and nothing is ever printed. +# +# THE FIFTH FIELD IS PHASE 3 (CLOUD-514), and it is what the first two phases +# left out. The `verdict` column prices REFINEMENT: it asks whether the new row +# was written to Ready. It cannot ask the question the issue's acceptance is +# actually about — whether the row names code THIS BRANCH IS HOLDING OPEN — +# because a Ready block is prose, and prose is the one currency an agent has +# without limit. Measured 2026-08-20 on this branch: four rows filed in three and +# a half minutes, twelve more spent writing four Ready blocks, and every one +# recorded `ready`. The toll did not reverse the arithmetic; it certified it. +# +# So the overlap column records how many paths the row's body names that +# `origin/main...HEAD` is also changing — `mise-tasks/board-diff-overlap`, which +# owns the predicate and its measurement. It is recorded for a groom as well as a +# create, for the same reason the verdict is: a later reading of the same row by +# the same mechanism is simply the current one. Pointer-only holds by +# construction there — only paths tracked in this repository can appear. # # FAILS OPEN AND SILENT on everything it cannot establish. A recorder that # blocked or noised a board write would cause the failure `finding-sink-check` @@ -67,6 +83,10 @@ # comment too, so an issue-key column fills with comment uuids and sink 2 becomes # unobservable while every count still looks right. #MUTANT comment-id-from-response|s/^if \[ "\$kind" = comment \]; then$/if false; then/|records the issue key its input names +# The mutation stops asking the diff question at all, so every row records `-` +# and `filed-here-check` reads "could not look" for a row filed straight over the +# branch's own open files — phase 3 wired shut while every count still looks right. +#MUTANT overlap-never-measured|s/^\t\toverlap=\$(printf .*$/\t\toverlap=-/|a row whose body names a changed file records a non-zero overlap set -uo pipefail # @@ -218,10 +238,23 @@ if [ "$kind" = issue ]; then fi fi +# THE DIFF COLUMN. `-` is "could not look" here exactly as it is for the verdict: +# outside a checkout, no `origin/main` to diff against, or a body the tracker did +# not return. The description read is the tracker's RESPONSE, not the caller's +# argument, so it is unforgeable for the same reason the verdict is. +overlap=- +if [ "$kind" = issue ]; then + description=$(jq -r '.description // empty' <<<"$row" 2>/dev/null) || description="" + if [ -n "$description" ]; then + overlap=$(printf '%s' "$description" | "$(dirname -- "${BASH_SOURCE[0]}")/board-diff-overlap" 2>/dev/null) || overlap=- + fi + [ -n "$overlap" ] || overlap=- +fi + mkdir -p "$git_dir/batten-receipts" 2>/dev/null || exit 0 # Slashes are the one character a filename cannot carry; the substitution matches # every other branch-keyed receipt here. record="$git_dir/batten-receipts/board-writes.${branch//\//-}" -printf '%s %s %s %s\n' "$kind" "$id" "$updated" "$verdict" >>"$record" 2>/dev/null || exit 0 +printf '%s %s %s %s %s\n' "$kind" "$id" "$updated" "$verdict" "$overlap" >>"$record" 2>/dev/null || exit 0 exit 0 diff --git a/mise-tasks/filed-here-check b/mise-tasks/filed-here-check index 544d4b22d..a56da56b4 100755 --- a/mise-tasks/filed-here-check +++ b/mise-tasks/filed-here-check @@ -15,11 +15,39 @@ # guardrail points at. Making the new row cost a complete Ready block is what # flips that, without anything judging whether a given spin-off was lazy. # -# WHAT IT DOES NOT DO, because non-negotiable 3 forbids it: it does not compare -# the row to the diff, score its prose, or decide whether it should have been -# fixed instead. It reads four fields the recorder wrote and compares one of them -# to a literal. No tracker read — a gate inside `land` has no credential, which -# is `claim-check`'s "agents fetch, gates decide" split. +# TWO REFUSALS NOW, AND THE SECOND IS THE ONE THE ISSUE'S ACCEPTANCE ASKED FOR. +# +# `filed-unrefined` (above) prices REFINEMENT. Its bound was stated honestly in +# this header from the day it shipped — "it does not compare the row to the diff" +# — and that bound turned out to be the whole gap. A Ready block is prose, and +# prose is the one currency an agent has without limit: measured 2026-08-20, four +# rows filed in three and a half minutes, twelve more spent writing four §1–§8 +# blocks, and the recorder stored `ready` for every one. The toll did not reverse +# the arithmetic. It certified the punts. +# +# `filed-over-own-diff` prices PROXIMITY instead, which is what CLOUD-514's +# acceptance is about: "the cheapest path through the gate for a defect in the +# branch's own diff is to fix it." A row spun out of code you have open is a +# different object from a row about work elsewhere, and only the first is a punt. +# The predicate is a set intersection over two file lists — the paths the body +# names, the paths `origin/main...HEAD` changes — computed by the recorder via +# `mise-tasks/board-diff-overlap`, which owns it and its measurement. +# +# THE LOAD-BEARING DIFFERENCE FROM THE FIRST REFUSAL: there is no prose remedy. +# The ways out are fix it here, stop touching that file, file it after landing +# from a clean tree, or take a recorded override a human can see. None of those +# is payable in typing, which is the property `filed-unrefined` lacked. +# +# WHAT IT STILL DOES NOT DO, because non-negotiable 3 forbids it: it scores no +# prose, compares no semantics and decides nothing about whether a spin-off was +# lazy. It reads five fields the recorder wrote and compares two of them to +# literals. No tracker read — a gate inside `land` has no credential, which is +# `claim-check`'s "agents fetch, gates decide" split. +# +# THE RESIDUAL, stated rather than discovered later: this prices filing against +# THE DIFF. A punt about code the branch never touched is invisible to it, and so +# is a row filed before the file is touched. It closes the case that happened +# four times in one session and claims nothing wider. # # THE VERDICT IS THE TRACKER'S, NOT THE AUTHOR'S. `ready-lint` over a payload the # caller assembles is forgeable and was measured to be — green three times over @@ -44,8 +72,20 @@ # retrofitting one is impossible: the store lives under `$GIT_DIR`, is never # committed, and dies with the container. # -# POINTER, NEVER PAYLOAD (rule 4): the refusal names the id and nothing else. The -# recorder never wrote a title or a body, so there is none here to leak. +# POINTER, NEVER PAYLOAD (rule 4): a refusal names the id, and for the diff +# refusal one tracked path. The recorder never wrote a title or a body, so there +# is none here to leak, and only a path tracked in this repository can reach the +# overlap column at all. +# +# `BATTEN_FILED_HERE_OVERLAP=1` mints over the diff refusals and RECORDS WHICH +# ONES, the `BATTEN_CLAIM_TAKEOVER` idiom. It is not folded into +# `BATTEN_FILED_HERE_BYPASS`, because "this record is unreadable" and "I meant to +# file this row against code I have open" are different decisions, and the second +# is legitimate often enough to need a route that is not a blanket off-switch — +# the row that documents a change you are landing, for one. The override is worth +# having only if it leaves a trace: it writes what it overrode to +# `$GIT_DIR/batten-receipts/filed-here-overrides.` and prints the same on +# stdout, so a reviewer sees a decision rather than a silence. # # Usage: mise run filed-here-check (no input; the record is on disk) # @@ -67,6 +107,22 @@ # a row groomed to Ready still carries its creation-time `unready` and is refused # for it — the state that held PR #525 with the third remedy unreachable. #MUTANT first-verdict-wins|s/^\trebuilt=""$/\trebuilt="\$latest"/|a groom recorded after the create supersedes it +# The mutation lets a row filed over the branch's own open files through, which is +# the entire second refusal: spinning a defect out of code you are holding open +# goes back to being free. +#MUTANT overlap-passes|s/^\t\treport "\$id filed-over-own-diff.*$/\t\t:/|a row naming a file this branch is changing stops the lap +# The mutation reads a zero count as an overlap, so a row that names only files +# this branch never touched is refused — the false positive that would make the +# gate unusable and get it switched off. +#MUTANT zero-overlap-refused|s/^\t0) continue ;;$/\t0) ;;/|a row naming only untouched files passes +# The mutation reads "could not look" as a refusal, so a branch with no +# `origin/main` to diff against — a fresh clone, a detached recorder — is stopped +# over the environment rather than over the row. +#MUTANT overlap-unanswered-refused|s/^\t-) continue ;;$/\t-) ;;/|a row the recorder could not measure passes +# The override must stay OPT-IN and must stay RECORDED. Dropping the record turns +# a visible decision into a silence, which is the state the override exists to +# avoid. +#MUTANT override-unrecorded|s/^\t\toverridden="\${overridden:+\$overridden }\$id"$/\t\toverridden="\$overridden"/|the override records which rows it overrode set -uo pipefail [ -n "${BATTEN_FILED_HERE_BYPASS:-}" ] && exit 0 @@ -89,11 +145,18 @@ if [ ! -r "$record" ]; then fi violations=0 +unrefined=0 +overdiff=0 creates=0 comments=0 -report() { # pointer-only: the id, never a title or a body +overridden="" +report() { # pointer-only: an id, and for the diff refusal one tracked path echo "$1" >&2 violations=$((violations + 1)) + case "${2:-}" in + unrefined) unrefined=$((unrefined + 1)) ;; + overdiff) overdiff=$((overdiff + 1)) ;; + esac } # THE LAST VERDICT PER ID WINS, NOT EVERY LINE (CLOUD-514). The recorder now @@ -109,8 +172,13 @@ report() { # pointer-only: the id, never a title or a body # `_` for the updatedAt column: it is the recorder's forgery-resistant half and # this gate has no use for it, and naming it `_` is what keeps the linter from # reading a deliberate placeholder as a dead variable. +# +# THE FIFTH COLUMN IS OPTIONAL BY CONSTRUCTION. A record written before phase 3 +# has four fields, so `overlap` reads empty — which becomes `-`, "could not +# look", and passes. A branch cannot be refused for a question its recorder was +# never able to ask. latest="" -while read -r kind id _ verdict; do +while read -r kind id _ verdict overlap; do [ -n "$kind" ] || continue case "$kind" in comment) @@ -136,21 +204,93 @@ while read -r kind id _ verdict; do *) rebuilt="${rebuilt:+$rebuilt }$entry" ;; esac done - latest="${rebuilt:+$rebuilt }$id=${verdict:--}" + # ` ...` from the recorder, comma-joined so one entry stays one + # shell word. Absent or blank is `-`, the same "could not look" the verdict + # column already draws. + packed=${overlap:--} + packed=${packed// /,} + latest="${rebuilt:+$rebuilt }$id=${verdict:--}=${packed}" done <"$record" for entry in $latest; do id=${entry%%=*} - verdict=${entry#*=} + rest=${entry#*=} + verdict=${rest%%=*} case "$verdict" in unready) ;; ready) continue ;; *) continue ;; # not answered — see the three-states note above esac - report "$id filed-unrefined" + report "$id filed-unrefined" unrefined +done + +# THE DIFF REFUSAL, in its own pass because a row can earn both: "never groomed +# to Ready" and "names code this branch is holding open" are different facts, and +# neither subsumes the other. +for entry in $latest; do + id=${entry%%=*} + rest=${entry#*=} + overlap=${rest#*=} + # THREE STATES, exactly as the verdict column has: `-` is the recorder saying + # it could not look and passes, `0` is a measurement that found nothing and + # passes, and a count with at least one path after it is the refusal. Anything + # else is a line this reader cannot parse, which is skipped and never judged. + case "$overlap" in + -) continue ;; + 0) continue ;; + [1-9]*,*) ;; + *) continue ;; + esac + paths=${overlap#*,} + if [ -n "${BATTEN_FILED_HERE_OVERLAP:-}" ]; then + overridden="${overridden:+$overridden }$id" + continue + fi + saved_ifs=$IFS + IFS=, + for path in $paths; do + report "$id filed-over-own-diff $path" overdiff + done + IFS=$saved_ifs done -if [ "$violations" -ne 0 ]; then +# THE OVERRIDE LEAVES A TRACE, which is the only thing that makes it worth having +# (the `BATTEN_CLAIM_TAKEOVER` idiom). A blanket off-switch and a recorded +# decision look identical to the branch and completely different to a reviewer. +if [ -n "$overridden" ]; then + line="filed-here-override $(date -u +%Y-%m-%dT%H:%M:%SZ) $overridden" + mkdir -p "$git_dir/batten-receipts" 2>/dev/null && + printf '%s\n' "$line" >>"$git_dir/batten-receipts/filed-here-overrides.${branch//\//-}" 2>/dev/null + echo "filed-here-check: diff overlap overridden (BATTEN_FILED_HERE_OVERLAP): $overridden" +fi + +if [ "$overdiff" -ne 0 ]; then + cat <<'REASON' >&2 +::error:: filed-here-check: a row this branch filed names code this branch has open. + +That is the punt CLOUD-514 is about. A defect you found in your own diff is a +defect you are already holding the file for, so spinning it onto the board is +cheaper than fixing it — and the board becomes the escape hatch every other +guardrail points at. + +There is deliberately NO PROSE REMEDY here. `filed-unrefined` charges a Ready +block, and a Ready block is payable in typing; this one is not. Four ways +forward: + + 1. Fix it here. You have the file open. + 2. If it belongs to a row that already exists, comment there — comments are + recorded and never gated. + 3. File it after this lands, from a clean tree, when it is no longer your diff. + 4. If you mean it — a row documenting the change you are landing, say — run + `BATTEN_FILED_HERE_OVERLAP=1 mise run land`. It records which rows it + overrode, so this reads as a decision rather than as a silence. + +This judges no content and reads no tracker: it intersects the tracked paths the +row's body names with `git diff --name-only origin/main...HEAD`. +REASON +fi + +if [ "$unrefined" -ne 0 ]; then cat <<'REASON' >&2 ::error:: filed-here-check: a row this branch filed was never groomed to Ready. @@ -170,7 +310,8 @@ Three ways forward, cheapest first: This judges no content and reads no tracker: it compares the verdict `board-write-record` stored at creation to `ready`. REASON - exit 1 fi -echo "filed-here-check: $creates row(s) filed on \"$branch\", $comments comment(s) — every filed row was refined at creation" +[ "$violations" -eq 0 ] || exit 1 + +echo "filed-here-check: $creates row(s) filed on \"$branch\", $comments comment(s) — every filed row was refined at creation and none names this branch's own diff" diff --git a/mise.toml b/mise.toml index b74757ef1..7cac215fa 100644 --- a/mise.toml +++ b/mise.toml @@ -313,7 +313,7 @@ CI_ANSWERED_CONCLUSIONS = "success,neutral,failure,timed_out,action_required" # # Seeded with the gates this bundle touches, so every "mutation-checked per # CLOUD-418" obligation in it is cashed rather than cited. -MUTANT_GATES = "msrv-pin-agreement,issue-read-check,cap-drift,bot-issue,renovate-config-validator,lock-complete,signing-posture,mcp-timeout-budget,land,land-lock,checks-green,ci-lease-precondition,finding-sink-check,issue-search-check,issue-search-guard,run-shape-guard,graph-check,board-move-guard,board-write-record,filed-here-check,reclaim-census,done-pr-check,claim-check,pr-unsubscribed,fanout-guard,connector-allow-resolve,connector-allow-guard,mcp-attach-check,serena-mcp,mcp-grant-sync,mcp-allow-check" +MUTANT_GATES = "msrv-pin-agreement,issue-read-check,cap-drift,bot-issue,renovate-config-validator,lock-complete,signing-posture,mcp-timeout-budget,land,land-lock,checks-green,ci-lease-precondition,finding-sink-check,issue-search-check,issue-search-guard,run-shape-guard,graph-check,board-move-guard,board-write-record,board-diff-overlap,filed-here-check,reclaim-census,done-pr-check,claim-check,pr-unsubscribed,fanout-guard,connector-allow-resolve,connector-allow-guard,mcp-attach-check,serena-mcp,mcp-grant-sync,mcp-allow-check" # --- GitHub reachability behind an egress proxy (Claude Code web sandbox etc.) --- # mise resolves every tool's release through GitHub's *API* host, api.github.com. diff --git a/tests/board-diff-overlap.bats b/tests/board-diff-overlap.bats new file mode 100644 index 000000000..08d920b58 --- /dev/null +++ b/tests/board-diff-overlap.bats @@ -0,0 +1,241 @@ +#!/usr/bin/env bats +# CLOUD-514, phase 3. The sensor that answers "how many paths does this row's +# body name that the branch is also changing". +# +# Every test runs inside a throwaway `git init` for `board-write-record.bats`'s +# reason and one more: the subject reads `origin/main...HEAD` and `git ls-files`, +# so a suite running in this checkout would be measured against the real +# session's own diff and its verdicts would change commit by commit. + +setup() { + SENSOR="$BATS_TEST_DIRNAME/../mise-tasks/board-diff-overlap" + REPO="$BATS_TEST_TMPDIR/repo" + rm -rf "$REPO" + mkdir -p "$REPO/src" "$REPO/other" "$REPO/mise-tasks" + git -C "$REPO" init --quiet --initial-branch=main + # Per fixture, never inherited — a CI runner carries no global identity, so a + # bare `git commit` here fails only there (CLOUD-513). + git -C "$REPO" config user.email t@example.com + git -C "$REPO" config user.name t + printf 'a\n' >"$REPO/src/git.rs" + printf 'b\n' >"$REPO/src/lint.rs" + # The ambiguity fixture: one basename, two tracked paths. + printf 'c\n' >"$REPO/src/mod.rs" + printf 'd\n' >"$REPO/other/mod.rs" + printf 'e\n' >"$REPO/mise-tasks/macos-link-check" + git -C "$REPO" add -A + git -C "$REPO" commit -q -m base + git -C "$REPO" update-ref refs/remotes/origin/main HEAD + git -C "$REPO" checkout -q -b work + cd "$REPO" || return 1 +} + +# Stage a change to each named path and commit it, so `origin/main...HEAD` has +# exactly that set. +changing() { + local path + for path in "$@"; do printf 'changed\n' >>"$REPO/$path"; done + git -C "$REPO" commit -q -am change +} + +# --- what it sees -------------------------------------------------------------- + +# THE CASE THE WHOLE DESIGN TURNS ON. Bodies here write `git.rs:107`, never +# `src/git.rs`: measured against the three rows this was built from, exact path +# matching finds ZERO and basename resolution finds all three. A sensor matching +# only tracked paths would have shipped blind to its own corpus. +@test "a short form resolves to the tracked path" { + changing src/git.rs + run bash -c "printf '%s' 'The refusal is at git.rs:107.' | '$SENSOR'" + [ "$status" -eq 0 ] + [ "$output" = "1 src/git.rs" ] +} + +@test "a full tracked path resolves too" { + changing src/git.rs + run bash -c "printf '%s' 'See src/git.rs for the shape.' | '$SENSOR'" + [ "$output" = "1 src/git.rs" ] +} + +# A task file has no extension at all, so neither of the two shapes above reaches +# it. Bodies name one in backticks; that is the third arm. +@test "a backticked task name with no extension resolves" { + changing mise-tasks/macos-link-check + run bash -c "printf '%s' 'The gate is \`macos-link-check\`.' | '$SENSOR'" + [ "$output" = "1 mise-tasks/macos-link-check" ] +} + +@test "two named changed files are both reported, sorted" { + changing src/git.rs src/lint.rs + run bash -c "printf '%s' 'Both git.rs and lint.rs are wrong.' | '$SENSOR'" + [ "$output" = "2 src/git.rs src/lint.rs" ] +} + +# --- what it must NOT see ------------------------------------------------------ + +# THE OTHER DIRECTION (CLOUD-418). The intersection is with what the branch +# CHANGES, not with what it tracks: a sensor that dropped that term would refuse +# every row naming any file in the repository, which is a gate nobody can work +# under and therefore a gate that gets switched off. +@test "a row naming only untouched files reports nothing" { + changing src/git.rs + run bash -c "printf '%s' 'The bug is in lint.rs:12.' | '$SENSOR'" + [ "$output" = "0" ] +} + +# The same body against a branch holding nothing open. Filing after landing, from +# a clean tree, is one of the four remedies the gate prints — so it has to be a +# remedy the sensor actually honours. +@test "the same body reports nothing once the branch changes nothing" { + run bash -c "printf '%s' 'The refusal is at git.rs:107.' | '$SENSOR'" + [ "$output" = "0" ] +} + +# GUESSING IS A WRONG ANSWER WEARING A RIGHT ANSWER'S SHAPE. `mod.rs` names two +# tracked files here; 28 of 530 tracked basenames are ambiguous in the real tree. +# An ambiguous basename resolves to nothing, the "could not look" reading this +# repo draws everywhere. +# BOTH candidates are changed here, deliberately: with only one of them in the +# diff, a sensor that guessed and happened to pick the other would report nothing +# and read as correct. Changing both leaves no wrong guess that looks right. +@test "an ambiguous basename resolves to nothing" { + changing src/mod.rs other/mod.rs + run bash -c "printf '%s' 'The bug is in mod.rs:3.' | '$SENSOR'" + [ "$output" = "0" ] +} + +# ...and the disambiguated form still resolves, so the rule above is a refusal to +# guess rather than a blind spot over a whole basename. +@test "the same file named by its full path resolves despite the ambiguity" { + changing src/mod.rs + run bash -c "printf '%s' 'The bug is in src/mod.rs:3.' | '$SENSOR'" + [ "$output" = "1 src/mod.rs" ] +} + +# A name that resolves to no tracked file at all is not an error and not a +# finding — a body may name anything. +@test "a path naming no tracked file is ignored rather than reported" { + changing src/git.rs + run bash -c "printf '%s' 'Compare against upstream/foo.rs and README.other.' | '$SENSOR'" + [ "$output" = "0" ] +} + +# --- pointer, never payload (non-negotiable 4) --------------------------------- + +# The input is an entire issue body. The only thing that may leave this sensor is +# a path tracked in this repository, which is a structural property rather than a +# careful one: nothing else can survive the intersection. +@test "nothing from the body but a tracked path is emitted" { + changing src/git.rs + run bash -c "printf '%s' 'git.rs leaks hunter2 for acct 00219 at evil.example.com.' | '$SENSOR'" + [ "$output" = "1 src/git.rs" ] +} + +# --- could not look ------------------------------------------------------------ + +# `-` is a third answer, not a zero: the gate reads it as "not answered" and +# passes, because reporting a fact about the environment as a fact about the row +# is the confusion the record's verdict column already exists to avoid. +@test "an empty body reports a dash rather than a zero" { + run bash -c "printf '' | '$SENSOR'" + [ "$status" -eq 0 ] + [ "$output" = "-" ] +} + +@test "outside a git checkout it reports a dash" { + cd "$BATS_TEST_TMPDIR" || return 1 + mkdir -p nowhere + cd nowhere || return 1 + run bash -c "printf '%s' 'git.rs:107' | '$SENSOR'" + [ "$status" -eq 0 ] + [ "$output" = "-" ] +} + +# A clone with no `origin/main` cannot answer either — and must not answer `0`, +# which would read as "this row names nothing you have open". +@test "a checkout with no origin/main reports a dash" { + git -C "$REPO" update-ref -d refs/remotes/origin/main + run bash -c "printf '%s' 'git.rs:107' | '$SENSOR'" + [ "$status" -eq 0 ] + [ "$output" = "-" ] +} + +# It is a SENSOR. `filed-here-check` is the gate, and a sensor that exited +# non-zero would stop the `PostToolUse` body that calls it. +@test "it never exits non-zero, whatever it finds" { + changing src/git.rs + run bash -c "printf '%s' 'git.rs:107' | '$SENSOR'" + [ "$status" -eq 0 ] + run bash -c "printf '%s' 'nothing at all' | '$SENSOR'" + [ "$status" -eq 0 ] +} + +# --- the replay (CLOUD-514) ---------------------------------------------------- +# +# THE RECALL MEASUREMENT, over the corpus that produced this sensor rather than +# over fixtures written to pass it. `tests/fixtures/board-diff-overlap/` holds +# the three rows this branch spun off on 2026-08-20 as the tracker stored them, +# the branch's tracked file list, and the diff exactly as it stood at 00:39 when +# they were filed. Reconstructing that tree and feeding the bodies back is the +# only evidence that the predicate fires on real punts and not just on shapes +# invented alongside it (CLOUD-248's precedent, CLOUD-633's obligation). +# +# EXACT PATH MATCHING FINDS ZERO OF THE THREE — measured, and the reason the +# basename arm exists at all: every body writes `git.rs:107`, never +# `crates/batten/src/git.rs`. +replay_repo() { + local fixtures="$BATS_TEST_DIRNAME/fixtures/board-diff-overlap" + local replay="$BATS_TEST_TMPDIR/replay" path + rm -rf "$replay" + mkdir -p "$replay" + git -C "$replay" init --quiet --initial-branch=main + git -C "$replay" config user.email t@example.com + git -C "$replay" config user.name t + while IFS= read -r path; do + [ -n "$path" ] || continue + mkdir -p "$replay/$(dirname -- "$path")" + printf 'base\n' >"$replay/$path" + done <"$fixtures/tracked-at-filing.txt" + git -C "$replay" add -A + git -C "$replay" commit -q -m base + git -C "$replay" update-ref refs/remotes/origin/main HEAD + git -C "$replay" checkout -q -b work + while IFS= read -r path; do + [ -n "$path" ] || continue + printf 'changed\n' >>"$replay/$path" + done <"$fixtures/changed-at-filing.txt" + git -C "$replay" commit -q -am change + printf '%s\n' "$replay" +} + +@test "REPLAY: all three rows this branch spun off name code it was holding open" { + local replay fixtures + fixtures="$BATS_TEST_DIRNAME/fixtures/board-diff-overlap" + replay=$(replay_repo) + cd "$replay" || return 1 + + run bash -c "'$SENSOR' < '$fixtures/CLOUD-739.md'" + [ "$output" = "1 crates/batten/src/git.rs" ] + + run bash -c "'$SENSOR' < '$fixtures/CLOUD-740.md'" + [ "$output" = "1 crates/batten/src/git.rs" ] + + run bash -c "'$SENSOR' < '$fixtures/CLOUD-737.md'" + [ "$output" = "1 mise-tasks/macos-link-check" ] +} + +# THE CONTROL, and without it the case above is not a measurement. The same three +# bodies against a branch holding nothing open must report nothing: what fires is +# the intersection with the diff, not the mere mention of a file that exists. +@test "REPLAY CONTROL: the same three bodies report nothing from a clean tree" { + local replay fixtures row + fixtures="$BATS_TEST_DIRNAME/fixtures/board-diff-overlap" + replay=$(replay_repo) + cd "$replay" || return 1 + git checkout -q -B work refs/remotes/origin/main + + for row in 737 739 740; do + run bash -c "'$SENSOR' < '$fixtures/CLOUD-$row.md'" + [ "$output" = "0" ] + done +} diff --git a/tests/board-write-record.bats b/tests/board-write-record.bats index 86d1183f6..237c0b746 100644 --- a/tests/board-write-record.bats +++ b/tests/board-write-record.bats @@ -107,7 +107,7 @@ comment_event() { [ "$status" -eq 0 ] [ -f "$(record)" ] run cat "$(record)" - [[ "$output" == "issue CLOUD-999 2026-08-13T00:00:00.000Z ready" ]] + [[ "$output" == "issue CLOUD-999 2026-08-13T00:00:00.000Z ready -" ]] } # THE ROW THIS DESIGN TURNS ON. `ready-lint`'s §8 rule cross-checks prose claiming @@ -122,7 +122,7 @@ comment_event() { run bash -c "'$REC' < $(event mcp__Linear__save_issue "$body" CLOUD-1)" [ "$status" -eq 0 ] run cat "$(record)" - [[ "$output" == *" ready" ]] + [[ "$output" == *" ready -" ]] } @test "an unrefined row records a verdict of unready rather than being refused" { @@ -130,7 +130,55 @@ comment_event() { [ "$status" -eq 0 ] [ -z "$output" ] run cat "$(record)" - [[ "$output" == *" unready" ]] + [[ "$output" == *" unready -" ]] +} + +# --- the diff column (CLOUD-514, phase 3) ------------------------------------- +# +# The overlap is `-` everywhere above because a throwaway `git init` has no +# `origin/main` to diff against — which is the fail-open reading, and the reason +# every case that predates this column still holds. These two build the ref so +# the column can carry a real answer, in both directions. +with_diff() { # a branch whose diff against origin/main touches ONE tracked file + printf 'x\n' >"$REPO/keeper.rs" + printf 'y\n' >"$REPO/untouched.rs" + git -C "$REPO" add keeper.rs untouched.rs + git -C "$REPO" commit -q -m base + git -C "$REPO" update-ref refs/remotes/origin/main HEAD + git -C "$REPO" checkout -q -b work + printf 'x2\n' >"$REPO/keeper.rs" + git -C "$REPO" commit -q -am change +} + +@test "a row whose body names a changed file records a non-zero overlap" { + with_diff + run bash -c "'$REC' < $(event mcp__Linear__save_issue 'The bug is in keeper.rs:12.')" + [ "$status" -eq 0 ] + run cat "$(record)" + [[ "$output" == *" 1 keeper.rs" ]] +} + +# THE OTHER DIRECTION (CLOUD-418): a suite that only ever asserts the firing +# cannot tell a working sensor from one that names every file in the repository. +@test "a row naming only a file this branch never touched records a zero overlap" { + with_diff + run bash -c "'$REC' < $(event mcp__Linear__save_issue 'The bug is in untouched.rs:12.')" + [ "$status" -eq 0 ] + run cat "$(record)" + [[ "$output" == *" 0" ]] + [[ "$output" != *untouched.rs* ]] +} + +# POINTER, NEVER PAYLOAD (non-negotiable 4). The recorder reads an entire issue +# body; the only thing that may reach the file from it is a tracked path. +@test "nothing from the body but a tracked path reaches the record" { + with_diff + run bash -c "'$REC' < $(event mcp__Linear__save_issue 'keeper.rs leaks hunter2 for acct 00219.')" + [ "$status" -eq 0 ] + run cat "$(record)" + [[ "$output" == *keeper.rs* ]] + [[ "$output" != *hunter2* ]] + [[ "$output" != *00219* ]] } # An update is not a board write this branch is answerable for. Recording it @@ -153,13 +201,13 @@ comment_event() { run bash -c "'$REC' < $(event mcp__Linear__save_issue 'Just a sentence, no Ready block.')" [ "$status" -eq 0 ] run cat "$(record)" - [[ "$output" == *" unready" ]] + [[ "$output" == *" unready -" ]] run bash -c "'$REC' < $(event mcp__Linear__save_issue '' '' CLOUD-999)" [ "$status" -eq 0 ] [ "$(wc -l <"$(record)")" -eq 2 ] run tail -1 "$(record)" - [[ "$output" == "issue CLOUD-999 2026-08-13T00:00:00.000Z ready" ]] + [[ "$output" == "issue CLOUD-999 2026-08-13T00:00:00.000Z ready -" ]] } # The exception is narrow on purpose: it grants nothing a fresh create would not @@ -209,7 +257,7 @@ comment_event() { run bash -c "'$REC' < $(comment_event mcp__Linear__save_comment CLOUD-42)" [ "$status" -eq 0 ] run cat "$(record)" - [[ "$output" == "comment CLOUD-42 2026-08-13T00:00:00.000Z -" ]] + [[ "$output" == "comment CLOUD-42 2026-08-13T00:00:00.000Z - -" ]] } # The regression case, stated as a shape rather than a value: whatever a comment @@ -236,7 +284,7 @@ comment_event() { run bash -c "'$REC' < $(comment_event mcp__Linear__save_comment '' parentId)" [ "$status" -eq 0 ] run cat "$(record)" - [[ "$output" == "comment - 2026-08-13T00:00:00.000Z -" ]] + [[ "$output" == "comment - 2026-08-13T00:00:00.000Z - -" ]] } # CLOUD-178 measured the same connector under three names depending on the diff --git a/tests/filed-here-check.bats b/tests/filed-here-check.bats index 9154e8dd2..70372e976 100644 --- a/tests/filed-here-check.bats +++ b/tests/filed-here-check.bats @@ -210,3 +210,165 @@ record() { printf '%s\n' "$@" >>"$RECORD"; } [[ "$output" == *"1 row(s) filed"* ]] [[ "$output" == *"2 comment(s)"* ]] } + +# --- the diff refusal (CLOUD-514, phase 3) ------------------------------------- +# +# `filed-unrefined` prices REFINEMENT and its bound was stated honestly from the +# day it shipped — "it does not compare the row to the diff". That bound was the +# whole gap: a Ready block is prose, and prose is the currency an agent has +# without limit. Measured 2026-08-20, four rows filed in three and a half minutes +# and every one recorded `ready`. +# +# The fifth column is the recorder's `board-diff-overlap` reading, `` then +# the overlapping tracked paths. + +@test "a row naming a file this branch is changing stops the lap" { + record "issue CLOUD-900 2026-08-19T00:00:00.000Z ready 1 crates/batten/src/git.rs" + run "$GATE" + [ "$status" -eq 1 ] + [[ "$output" == *"CLOUD-900 filed-over-own-diff crates/batten/src/git.rs"* ]] +} + +# THE OTHER DIRECTION (CLOUD-418). A refusal over every filed row is not a gate, +# it is an outage, and this is the reading that separates the two. +@test "a row naming only untouched files passes" { + record "issue CLOUD-900 2026-08-19T00:00:00.000Z ready 0" + run "$GATE" + [ "$status" -eq 0 ] + [[ "$output" != *"filed-over-own-diff"* ]] +} + +# THREE STATES HERE TOO. `-` is the recorder saying it could not look — no +# `origin/main`, outside a checkout, a body the tracker did not return — and +# reading it as a refusal turns a verdict about the environment into one about +# the row. +@test "a row the recorder could not measure passes" { + record "issue CLOUD-900 2026-08-19T00:00:00.000Z ready -" + run "$GATE" + [ "$status" -eq 0 ] + [[ "$output" != *"filed-over-own-diff"* ]] +} + +# A RECORD WRITTEN BEFORE THIS COLUMN EXISTED HAS FOUR FIELDS. A branch cannot be +# refused for a question its recorder was never able to ask, and the store lives +# under `$GIT_DIR` where it cannot be migrated. +@test "a four-field line predating the column is not refused" { + record "issue CLOUD-900 2026-08-19T00:00:00.000Z ready" + run "$GATE" + [ "$status" -eq 0 ] + [[ "$output" != *"filed-over-own-diff"* ]] +} + +@test "every overlapping path is named, one pointer per line" { + record "issue CLOUD-900 2026-08-19T00:00:00.000Z ready 2 a/one.rs b/two.rs" + run "$GATE" + [ "$status" -eq 1 ] + [[ "$output" == *"CLOUD-900 filed-over-own-diff a/one.rs"* ]] + [[ "$output" == *"CLOUD-900 filed-over-own-diff b/two.rs"* ]] +} + +# The two refusals are different facts about the same row and neither subsumes +# the other, so a row can earn both and must report both. +@test "a row that is both unrefined and over the diff reports both" { + record "issue CLOUD-900 2026-08-19T00:00:00.000Z unready 1 a/one.rs" + run "$GATE" + [ "$status" -eq 1 ] + [[ "$output" == *"CLOUD-900 filed-unrefined"* ]] + [[ "$output" == *"CLOUD-900 filed-over-own-diff a/one.rs"* ]] +} + +# LAST LINE WINS HERE TOO. The recorder writes a fresh line when a row this +# branch filed is groomed, and a row rewritten to be about work elsewhere is the +# second of the four remedies. +@test "a later reading with no overlap supersedes an earlier one" { + record "issue CLOUD-900 2026-08-19T00:00:00.000Z ready 1 a/one.rs" \ + "issue CLOUD-900 2026-08-19T01:00:00.000Z ready 0" + run "$GATE" + [ "$status" -eq 0 ] + [[ "$output" != *"filed-over-own-diff"* ]] +} + +@test "and a later reading WITH an overlap supersedes a clean one" { + record "issue CLOUD-900 2026-08-19T00:00:00.000Z ready 0" \ + "issue CLOUD-900 2026-08-19T01:00:00.000Z ready 1 a/one.rs" + run "$GATE" + [ "$status" -eq 1 ] + [[ "$output" == *"filed-over-own-diff a/one.rs"* ]] +} + +# COMMENTS STAY UNGATED on this axis too. A comment on the row that already owns +# the finding is sink 2 and the honest common case; pricing it pushes the +# pressure toward silence. +@test "a comment is never gated on the diff either" { + record "comment CLOUD-900 2026-08-19T00:00:00.000Z - 1 a/one.rs" + run "$GATE" + [ "$status" -eq 0 ] + [[ "$output" != *"filed-over-own-diff"* ]] +} + +# THE REFUSAL HAS NO PROSE REMEDY, which is the load-bearing difference from +# `filed-unrefined`: a Ready block is payable in typing and this is not. +@test "the diff refusal names four remedies and none of them is writing more prose" { + record "issue CLOUD-900 2026-08-19T00:00:00.000Z ready 1 a/one.rs" + run "$GATE" + [[ "$output" == *"Fix it here"* ]] + [[ "$output" == *"comment there"* ]] + [[ "$output" == *"after this lands"* ]] + [[ "$output" == *"BATTEN_FILED_HERE_OVERLAP=1"* ]] + [[ "$output" != *"ready-lint"* ]] +} + +# POINTER, NEVER PAYLOAD (rule 4): a path is all the recorder ever wrote, so a +# path is all this can name. +@test "the diff refusal carries the id and one path and nothing else" { + record "issue CLOUD-900 2026-08-19T00:00:00.000Z ready 1 a/one.rs" + run "$GATE" + [[ "$output" != *"2026-08-19T00:00:00.000Z"* ]] +} + +# --- the override -------------------------------------------------------------- +# +# Not folded into `BATTEN_FILED_HERE_BYPASS`: "this record is unreadable" and "I +# meant to file this row against code I have open" are different decisions, and +# the second is legitimate often enough — a row documenting the change you are +# landing — to need a route that is not a blanket off-switch. + +@test "the override lets the diff refusal through" { + record "issue CLOUD-900 2026-08-19T00:00:00.000Z ready 1 a/one.rs" + run env BATTEN_FILED_HERE_OVERLAP=1 "$GATE" + [ "$status" -eq 0 ] + [[ "$output" != *"filed-over-own-diff"* ]] +} + +# THE ONLY THING THAT MAKES IT WORTH HAVING. A blanket off-switch and a recorded +# decision look identical to the branch and completely different to a reviewer. +@test "the override records which rows it overrode" { + record "issue CLOUD-900 2026-08-19T00:00:00.000Z ready 1 a/one.rs" \ + "issue CLOUD-901 2026-08-19T00:00:00.000Z ready 1 b/two.rs" + run env BATTEN_FILED_HERE_OVERLAP=1 "$GATE" + [ "$status" -eq 0 ] + [[ "$output" == *"BATTEN_FILED_HERE_OVERLAP"* ]] + [[ "$output" == *"CLOUD-900"* ]] + [[ "$output" == *"CLOUD-901"* ]] + run cat "$REPO/.git/batten-receipts/filed-here-overrides.work" + [ "$status" -eq 0 ] + [[ "$output" == *"CLOUD-900 CLOUD-901"* ]] +} + +# It is the DIFF override, not a bypass: a row filed unrefined is still refused. +@test "the override does not excuse an unrefined row" { + record "issue CLOUD-900 2026-08-19T00:00:00.000Z unready 1 a/one.rs" + run env BATTEN_FILED_HERE_OVERLAP=1 "$GATE" + [ "$status" -eq 1 ] + [[ "$output" == *"CLOUD-900 filed-unrefined"* ]] + [[ "$output" != *"filed-over-own-diff"* ]] +} + +# And it writes nothing when it overrode nothing — a receipt for a decision +# nobody made reads as a decision somebody made. +@test "the override records nothing when there was nothing to override" { + record "issue CLOUD-900 2026-08-19T00:00:00.000Z ready 0" + run env BATTEN_FILED_HERE_OVERLAP=1 "$GATE" + [ "$status" -eq 0 ] + [ ! -e "$REPO/.git/batten-receipts/filed-here-overrides.work" ] +} diff --git a/tests/fixtures/board-diff-overlap/CLOUD-737.md b/tests/fixtures/board-diff-overlap/CLOUD-737.md new file mode 100644 index 000000000..829baa517 --- /dev/null +++ b/tests/fixtures/board-diff-overlap/CLOUD-737.md @@ -0,0 +1,3 @@ +Both Darwin release legs build on `ubuntu-latest` through `cargo-zigbuild`, with no Apple SDK (`release-artifacts.yml:101-104`). +`macos-link-check` exists to keep that buildable. `release-artifacts.yml:12-14` records the reasoning. +`libgit2-sys` declares a `links` key, so `macos-link-check` rule 1 excludes it outright today. diff --git a/tests/fixtures/board-diff-overlap/CLOUD-739.md b/tests/fixtures/board-diff-overlap/CLOUD-739.md new file mode 100644 index 000000000..fb0e305c9 --- /dev/null +++ b/tests/fixtures/board-diff-overlap/CLOUD-739.md @@ -0,0 +1,4 @@ +`DIFF_CONFIG` (`git.rs:107`) pins 20 config keys via `-c` and `DIFF_FLAGS` (`:139`) 6 flags. +`landing` decides merged-ness by patch identity, never reachability (CLOUD-36). +That is re-derivable on `gix-diff` plus the `sha2` already vendored. +the rebase, squash and cherry-pick shapes `tests/primitives.rs`' keystone fixture already builds. diff --git a/tests/fixtures/board-diff-overlap/CLOUD-740.md b/tests/fixtures/board-diff-overlap/CLOUD-740.md new file mode 100644 index 000000000..f0d432823 --- /dev/null +++ b/tests/fixtures/board-diff-overlap/CLOUD-740.md @@ -0,0 +1,6 @@ +The last slice of CLOUD-320's `git.rs` row: everything the earlier three left behind. +Reads: `uncommitted`, `changed_paths`, `check_ignore`, `worktrees`. Writes: `update_ref`, `worktree_remove`, `stash_create`. +`no_second_git_invoker_exists` currently forbids a literal `Command::new("git")` outside this module. +`worktrees` reads git's OWN vocabulary (CLOUD-46). `worktree list --porcelain`: `locked` and `prunable`. +`stash_create` does not capture an untracked-only tree, pinned by `a_snapshot_captures_a_dirty_tree_and_nothing_else`. +The module doc rewritten from "mid-migration" to what it actually is. diff --git a/tests/fixtures/board-diff-overlap/changed-at-filing.txt b/tests/fixtures/board-diff-overlap/changed-at-filing.txt new file mode 100644 index 000000000..b07ef4d2d --- /dev/null +++ b/tests/fixtures/board-diff-overlap/changed-at-filing.txt @@ -0,0 +1,11 @@ +Cargo.lock +Cargo.toml +crates/batten/Cargo.toml +crates/batten/src/git.rs +crates/batten/tests/config_trust.rs +fuzz/Cargo.lock +mise-tasks/macos-link-check +tests/fixtures/link-check/dormant-optional.json +tests/fixtures/link-check/enabled-optional.json +tests/fixtures/link-check/framework-crate.json +tests/macos-link-check.bats diff --git a/tests/fixtures/board-diff-overlap/tracked-at-filing.txt b/tests/fixtures/board-diff-overlap/tracked-at-filing.txt new file mode 100644 index 000000000..013d83fb7 --- /dev/null +++ b/tests/fixtures/board-diff-overlap/tracked-at-filing.txt @@ -0,0 +1,604 @@ +.claude/commands/plan-fleet.md +.claude/hooks/batten-hook.sh +.claude/hooks/git-hook +.claude/hooks/session-start.sh +.claude/rules/commits.md +.claude/rules/rust.md +.claude/rules/toolchain.md +.claude/settings.json +.claude/skills/batten/SKILL.md +.editorconfig +.gitattributes +.github/workflows/auto-bot-land.yml +.github/workflows/auto-release-land.yml +.github/workflows/branch-hygiene.yml +.github/workflows/ci-drift.yml +.github/workflows/ci.yml +.github/workflows/commit-lint.yml +.github/workflows/coverage.yml +.github/workflows/fast-forward.yml +.github/workflows/fuzz.yml +.github/workflows/hook-latency-drift.yml +.github/workflows/land-divergence.yml +.github/workflows/lock-currency.yml +.github/workflows/nonverdict-rate.yml +.github/workflows/perf.yml +.github/workflows/release-artifacts.yml +.github/workflows/release-assets.yml +.github/workflows/release-plz.yml +.github/workflows/scorecard.yml +.github/workflows/test.yml +.github/workflows/timeout-drift.yml +.github/workflows/zizmor-advisories.yml +.github/workflows/zizmor.yml +.gitignore +.gitmodules +.mcp.json +.prettierignore +.serena/.gitignore +.serena/memories/connector-allowlist-recovery.md +.serena/memories/core.md +.serena/memories/github-access.md +.serena/memories/github-rest-etiquette.md +.serena/memories/memory_maintenance.md +.serena/memories/prior-art-and-issue-hygiene.md +.serena/memories/serena-setup.md +.serena/memories/session-transcript-access.md +.serena/memories/toolchain-and-hooks.md +.serena/memories/workflow/agent-fanout.md +.serena/memories/workflow/board-states.md +.serena/memories/workflow/landing-loop.md +.serena/project.yml +.shellcheckrc +.taplo.toml +AGENTS.md +CHANGELOG.md +CLAUDE.md +CODE_OF_CONDUCT.md +CONTRIBUTING.md +Cargo.lock +Cargo.toml +LICENSE-APACHE +README.md +SECURITY.md +action.yml +batten.example.toml +batten.toml +bench/tokens/RESULTS.md +bench/tokens/fixtures/build-warning/batten.toml.in +bench/tokens/fixtures/build-warning/build.log.in +bench/tokens/fixtures/scan-pointer/batten.toml.in +bench/tokens/fixtures/scan-pointer/src/collect.rs.in +bench/tokens/fixtures/scan-pointer/src/dispatch.rs.in +bench/tokens/fixtures/scan-pointer/src/emit.rs.in +bench/tokens/fixtures/scan-pointer/src/ingest.rs.in +bench/tokens/fixtures/scan-pointer/src/plan.rs.in +bench/tokens/fixtures/scan-pointer/src/render.rs.in +bench/tokens/fixtures/scan-pointer/src/resolve.rs.in +bench/tokens/fixtures/scan-pointer/src/verify.rs.in +bench/tokens/method.toml +bench/tokens/workloads.toml +completions/batten.bash +completions/batten.fish +completions/batten.zsh +crates/batten/CHANGELOG.md +crates/batten/Cargo.toml +crates/batten/src/action.rs +crates/batten/src/attribution.rs +crates/batten/src/baseline.rs +crates/batten/src/brief.rs +crates/batten/src/budget.rs +crates/batten/src/bypass.rs +crates/batten/src/capture.rs +crates/batten/src/ci.rs +crates/batten/src/cli.rs +crates/batten/src/commit.rs +crates/batten/src/completion.rs +crates/batten/src/config.rs +crates/batten/src/decision.rs +crates/batten/src/defects.rs +crates/batten/src/design.rs +crates/batten/src/doctor.rs +crates/batten/src/drain.rs +crates/batten/src/effect.rs +crates/batten/src/emission.rs +crates/batten/src/epoch.rs +crates/batten/src/error.rs +crates/batten/src/exec.rs +crates/batten/src/exit.rs +crates/batten/src/findings.rs +crates/batten/src/git.rs +crates/batten/src/hook.rs +crates/batten/src/identity.rs +crates/batten/src/init.rs +crates/batten/src/journal.rs +crates/batten/src/judge.rs +crates/batten/src/lib.rs +crates/batten/src/lint.rs +crates/batten/src/main.rs +crates/batten/src/markers.rs +crates/batten/src/output.rs +crates/batten/src/outputs.rs +crates/batten/src/provision.rs +crates/batten/src/receipt.rs +crates/batten/src/redirect.rs +crates/batten/src/refusal.rs +crates/batten/src/render.rs +crates/batten/src/resolve.rs +crates/batten/src/rules.rs +crates/batten/src/secrets.rs +crates/batten/src/selfwrite.rs +crates/batten/src/session.rs +crates/batten/src/severity.rs +crates/batten/src/spec.rs +crates/batten/src/starter.toml +crates/batten/src/state.rs +crates/batten/src/stop.rs +crates/batten/src/store.rs +crates/batten/src/surface.rs +crates/batten/src/transcript.rs +crates/batten/src/trust.rs +crates/batten/src/verbs.rs +crates/batten/src/waiver.rs +crates/batten/src/worktree.rs +crates/batten/tests/acceptance_corpus.rs +crates/batten/tests/advisory_drain.rs +crates/batten/tests/attribution.rs +crates/batten/tests/baseline.rs +crates/batten/tests/bundle.rs +crates/batten/tests/claim_receipt.rs +crates/batten/tests/cli.rs +crates/batten/tests/commit.rs +crates/batten/tests/common/mod.rs +crates/batten/tests/config_epoch.rs +crates/batten/tests/config_lint.rs +crates/batten/tests/config_schema.rs +crates/batten/tests/config_show.rs +crates/batten/tests/config_trust.rs +crates/batten/tests/decision_record.rs +crates/batten/tests/defects.rs +crates/batten/tests/design_audit.rs +crates/batten/tests/doctor.rs +crates/batten/tests/done_not_landed.rs +crates/batten/tests/enforce_journal.rs +crates/batten/tests/extension_surfaces.rs +crates/batten/tests/fail_on_warning.rs +crates/batten/tests/fixture_repos.rs +crates/batten/tests/fixtures/acceptance-corpus/batten.toml.in +crates/batten/tests/fixtures/acceptance-corpus/bin/checker.in +crates/batten/tests/fixtures/acceptance-corpus/bin/shape-lint.in +crates/batten/tests/fixtures/acceptance-corpus/src/lib.rs.in +crates/batten/tests/fixtures/briefs/complete.md +crates/batten/tests/fixtures/briefs/missing-check.md +crates/batten/tests/fixtures/briefs/unrunnable-check.md +crates/batten/tests/fixtures/ci/rules-legacy.json +crates/batten/tests/fixtures/ci/rules-merge-methods.json +crates/batten/tests/fixtures/ci/rules-required-checks.json +crates/batten/tests/fixtures/ci/rules-two-pr-rules.json +crates/batten/tests/fixtures/hooks/README.md +crates/batten/tests/fixtures/hooks/claude-code.json +crates/batten/tests/fixtures/hooks/codex-cli.json +crates/batten/tests/fixtures/hooks/copilot-cli-stringified-args.json +crates/batten/tests/fixtures/hooks/copilot-cli.json +crates/batten/tests/fixtures/hooks/cursor-bom.json +crates/batten/tests/fixtures/hooks/cursor.json +crates/batten/tests/fixtures/hooks/gemini-cli.json +crates/batten/tests/fixtures/hooks/wiring/README.md +crates/batten/tests/fixtures/hooks/wiring/claude-code.json +crates/batten/tests/fixtures/hooks/wiring/codex-cli.json +crates/batten/tests/fixtures/hooks/wiring/copilot-cli.json +crates/batten/tests/fixtures/hooks/wiring/cursor.json +crates/batten/tests/fixtures/hooks/wiring/gemini-cli.json +crates/batten/tests/fixtures/repos/command-enforce/batten.toml.in +crates/batten/tests/fixtures/repos/command-enforce/expected.in +crates/batten/tests/fixtures/repos/command-enforce/lib.rs.in +crates/batten/tests/fixtures/repos/forbid-clean/batten.toml.in +crates/batten/tests/fixtures/repos/forbid-clean/expected.in +crates/batten/tests/fixtures/repos/forbid-clean/lib.rs.in +crates/batten/tests/fixtures/repos/forbid-deny/batten.toml.in +crates/batten/tests/fixtures/repos/forbid-deny/expected.in +crates/batten/tests/fixtures/repos/forbid-deny/lib.rs.in +crates/batten/tests/fixtures/repos/forbid-exclude-comment/batten.toml.in +crates/batten/tests/fixtures/repos/forbid-exclude-comment/expected.in +crates/batten/tests/fixtures/repos/forbid-exclude-comment/run.sh.in +crates/batten/tests/fixtures/repos/forbid-pattern-and-regex/batten.toml.in +crates/batten/tests/fixtures/repos/forbid-pattern-and-regex/expected.in +crates/batten/tests/fixtures/repos/forbid-pattern-and-regex/run.sh.in +crates/batten/tests/fixtures/repos/forbid-regex-cluster/batten.toml.in +crates/batten/tests/fixtures/repos/forbid-regex-cluster/expected.in +crates/batten/tests/fixtures/repos/forbid-regex-cluster/run.sh.in +crates/batten/tests/fixtures/repos/forbid-warn/batten.toml.in +crates/batten/tests/fixtures/repos/forbid-warn/expected.in +crates/batten/tests/fixtures/repos/forbid-warn/lib.rs.in +crates/batten/tests/fixtures/repos/judge-advisory/batten.toml.in +crates/batten/tests/fixtures/repos/judge-advisory/expected.in +crates/batten/tests/fixtures/repos/judge-advisory/lib.rs.in +crates/batten/tests/fixtures/repos/missing-severity/batten.toml.in +crates/batten/tests/fixtures/repos/missing-severity/expected.in +crates/batten/tests/fixtures/repos/missing-severity/lib.rs.in +crates/batten/tests/fixtures/repos/pipeline-mediated-call/batten.toml.in +crates/batten/tests/fixtures/repos/pipeline-mediated-call/expected.in +crates/batten/tests/fixtures/repos/pipeline-mediated-call/lib.rs.in +crates/batten/tests/fixtures/repos/ratchet-unresolvable-base/batten.toml.in +crates/batten/tests/fixtures/repos/ratchet-unresolvable-base/expected.in +crates/batten/tests/fixtures/repos/ratchet-unresolvable-base/lib.rs.in +crates/batten/tests/fixtures/repos/receipt-mediated-call/batten.toml.in +crates/batten/tests/fixtures/repos/receipt-mediated-call/expected.in +crates/batten/tests/fixtures/repos/receipt-mediated-call/lib.rs.in +crates/batten/tests/fixtures/repos/secrets-uninstalled/batten.toml.in +crates/batten/tests/fixtures/repos/secrets-uninstalled/expected.in +crates/batten/tests/fixtures/repos/secrets-uninstalled/lib.rs.in +crates/batten/tests/fixtures/repos/shape-mediated-call/batten.toml.in +crates/batten/tests/fixtures/repos/shape-mediated-call/expected.in +crates/batten/tests/fixtures/repos/shape-mediated-call/lib.rs.in +crates/batten/tests/fixtures/transcripts/bypass-different-op.jsonl.in +crates/batten/tests/fixtures/transcripts/bypass-mediated.jsonl.in +crates/batten/tests/fixtures/transcripts/bypass-sandbox-denial.jsonl.in +crates/batten/tests/fixtures/transcripts/bypass-standalone.jsonl.in +crates/batten/tests/fixtures/transcripts/completed-session.jsonl.in +crates/batten/tests/fixtures/transcripts/mid-turn-session.jsonl.in +crates/batten/tests/fixtures/transcripts/session.jsonl.in +crates/batten/tests/fixtures/transcripts/stop-hook-session.jsonl.in +crates/batten/tests/fixtures/transcripts/unprompted-memory-write.jsonl.in +crates/batten/tests/fuzz_corpus.rs +crates/batten/tests/guardrail_bypass.rs +crates/batten/tests/identity_churn.rs +crates/batten/tests/init.rs +crates/batten/tests/issue_key.rs +crates/batten/tests/judge_kind.rs +crates/batten/tests/mediated_verbs.rs +crates/batten/tests/pipeline_shapes.rs +crates/batten/tests/pointer_only.rs +crates/batten/tests/primitives.rs +crates/batten/tests/process_group.rs +crates/batten/tests/provision.rs +crates/batten/tests/ratchet.rs +crates/batten/tests/secrets_kind.rs +crates/batten/tests/submodule.rs +crates/batten/tests/surface.rs +crates/batten/tests/waivers.rs +crates/batten/tests/walker.rs +crates/batten/tests/zero_config.rs +deny.toml +fuzz/Cargo.lock +fuzz/Cargo.toml +fuzz/artifacts/README.md +fuzz/corpus/config_parse/batten.example.toml +fuzz/corpus/config_parse/batten.toml +fuzz/corpus/hook_decode/claude-code.json +fuzz/corpus/hook_decode/codex-cli.json +fuzz/corpus/hook_decode/copilot-cli-stringified-args.json +fuzz/corpus/hook_decode/copilot-cli.json +fuzz/corpus/hook_decode/cursor-bom.json +fuzz/corpus/hook_decode/cursor.json +fuzz/corpus/hook_decode/gemini-cli.json +fuzz/corpus/hook_decode/no-event-key-takes-the-default +fuzz/fuzz_targets/config_parse.rs +fuzz/fuzz_targets/hook_decode.rs +fuzz/properties.rs +hk.pkl +install.sh +man/batten-attribution-check.1 +man/batten-attribution-identity.1 +man/batten-attribution.1 +man/batten-baseline.1 +man/batten-capture-list.1 +man/batten-capture-prune.1 +man/batten-capture-show.1 +man/batten-capture.1 +man/batten-check.1 +man/batten-commit-check.1 +man/batten-commit.1 +man/batten-config-epoch.1 +man/batten-config-lint.1 +man/batten-config-show.1 +man/batten-config.1 +man/batten-defects-add.1 +man/batten-defects-query.1 +man/batten-defects.1 +man/batten-design-audit.1 +man/batten-design.1 +man/batten-doctor.1 +man/batten-enforce.1 +man/batten-exec.1 +man/batten-generate-completions.1 +man/batten-generate-hooks.1 +man/batten-generate-man.1 +man/batten-generate-markdown.1 +man/batten-generate-schema.1 +man/batten-generate.1 +man/batten-hook.1 +man/batten-init.1 +man/batten-lint-brief.1 +man/batten-lint.1 +man/batten-payload-field.1 +man/batten-payload.1 +man/batten-policy-budget.1 +man/batten-policy.1 +man/batten-provision-apply.1 +man/batten-provision-status.1 +man/batten-provision.1 +man/batten-receipt-record.1 +man/batten-receipt-status.1 +man/batten-receipt.1 +man/batten-spec.1 +man/batten-state-adopt.1 +man/batten-state-list.1 +man/batten-state-migrate.1 +man/batten-state-record.1 +man/batten-state.1 +man/batten-worktree-reclaim.1 +man/batten-worktree-status.1 +man/batten-worktree.1 +man/batten.1 +mise-tasks/alive +mise-tasks/attestation-check +mise-tasks/attribution-check +mise-tasks/awk-regex-check +mise-tasks/batten-glob-check +mise-tasks/board-move-guard +mise-tasks/board-write-record +mise-tasks/bot-issue +mise-tasks/branch-age-check +mise-tasks/cap-drift +mise-tasks/checks-green +mise-tasks/checksums +mise-tasks/ci-drift +mise-tasks/ci-lease-precondition +mise-tasks/ci-local-parity +mise-tasks/ci-tools-check +mise-tasks/ci-wait +mise-tasks/claim-check +mise-tasks/claim-race-check +mise-tasks/claimed-keys +mise-tasks/closing-key-check +mise-tasks/config-lint +mise-tasks/connector-allow-guard +mise-tasks/connector-allow-resolve +mise-tasks/connector-verb-guard +mise-tasks/container-preflight +mise-tasks/contract-drift +mise-tasks/darwin-link +mise-tasks/deferral-check +mise-tasks/derived-check +mise-tasks/dist +mise-tasks/doctor +mise-tasks/doctor-check +mise-tasks/done-check +mise-tasks/done-pr-check +mise-tasks/egress-check +mise-tasks/fanout-guard +mise-tasks/filed-here-check +mise-tasks/finding-sink-check +mise-tasks/gh-guard +mise-tasks/gh-guard-check +mise-tasks/gh-preflight +mise-tasks/graph-check +mise-tasks/hook-latency-drift +mise-tasks/hook-matcher-check +mise-tasks/hook-pin-check +mise-tasks/hook-profile-check +mise-tasks/hooks-wiring-check +mise-tasks/install-check +mise-tasks/issue-read-check +mise-tasks/issue-read-guard +mise-tasks/issue-search-check +mise-tasks/issue-search-guard +mise-tasks/land +mise-tasks/land-divergence +mise-tasks/land-divergence-assert +mise-tasks/land-lock +mise-tasks/land-lock-check +mise-tasks/landed-check +mise-tasks/license-table-check +mise-tasks/linear-check +mise-tasks/lock-complete +mise-tasks/macos-link-check +mise-tasks/main-watch +mise-tasks/man-pages +mise-tasks/mcp-allow-check +mise-tasks/mcp-attach-check +mise-tasks/mcp-grant-sync +mise-tasks/mcp-timeout-budget +mise-tasks/memories-check +mise-tasks/mise-action-floor +mise-tasks/mise-pin-agreement +mise-tasks/module-map-check +mise-tasks/msrv-pin-agreement +mise-tasks/mutant +mise-tasks/nonverdict-assert +mise-tasks/nonverdict-scan +mise-tasks/ntia-check +mise-tasks/payload-field +mise-tasks/perf +mise-tasks/perf-assert +mise-tasks/perf-compare +mise-tasks/perf-gate +mise-tasks/perf-pair +mise-tasks/perf-record +mise-tasks/pipefail-grep-check +mise-tasks/pkl-check +mise-tasks/pr-unsubscribed +mise-tasks/publish-credential-check +mise-tasks/ready-guard +mise-tasks/ready-lint +mise-tasks/reclaim-census +mise-tasks/reference-check +mise-tasks/release-assets-check +mise-tasks/release-due +mise-tasks/release-tracking-check +mise-tasks/released +mise-tasks/render/cli +mise-tasks/renovate-config-validator +mise-tasks/report-only-check +mise-tasks/rules-drift +mise-tasks/run-shape-guard +mise-tasks/sbom +mise-tasks/sbom-binary +mise-tasks/sbom-check +mise-tasks/schema-check +mise-tasks/semver +mise-tasks/serena-mcp +mise-tasks/signing-posture +mise-tasks/singleton +mise-tasks/skill-check +mise-tasks/sonar-gate +mise-tasks/step-receipt +mise-tasks/stop-guard +mise-tasks/stop-posture-check +mise-tasks/target-ensure +mise-tasks/task-registry +mise-tasks/timeout-check +mise-tasks/timeout-drift +mise-tasks/token-bench +mise-tasks/token-bench-check +mise-tasks/transcript-corpus-check +mise-tasks/tree-clean +mise-tasks/verified +mise-tasks/with-lock +mise.lock +mise.toml +release-plz.toml +renovate.json5 +rustfmt.toml +schema/batten.local.schema.json +schema/batten.schema.json +skills/batten/SKILL.md +tests/alive.bats +tests/attestation-check.bats +tests/attribution-check.bats +tests/awk-regex-check.bats +tests/bats +tests/batten-glob-check.bats +tests/board-move-guard.bats +tests/board-write-record.bats +tests/bot-issue.bats +tests/branch-age-check.bats +tests/cap-drift.bats +tests/checks-green.bats +tests/checksums.bats +tests/ci-lease-precondition.bats +tests/ci-local-parity.bats +tests/ci-tools-check.bats +tests/ci-wait.bats +tests/claim-check.bats +tests/claim-race-check.bats +tests/claimed-keys.bats +tests/closing-key-check.bats +tests/commit-attribution.bats +tests/commit-convention.bats +tests/config-lint.bats +tests/connector-allow-guard.bats +tests/connector-allow-resolve.bats +tests/connector-verb-guard.bats +tests/container-preflight.bats +tests/contract-drift.bats +tests/cross-check.bats +tests/darwin-link.bats +tests/deferral-check.bats +tests/derived-check.bats +tests/dist.bats +tests/doctor-race.bats +tests/doctor.bats +tests/done-check.bats +tests/done-pr-check.bats +tests/egress-check.bats +tests/fanout-guard.bats +tests/filed-here-check.bats +tests/finding-sink-check.bats +tests/fixtures/link-check/dormant-optional.json +tests/fixtures/link-check/enabled-optional.json +tests/fixtures/link-check/framework-crate.json +tests/gh-guard.bats +tests/git-hook.bats +tests/graph-check.bats +tests/helpers.bash +tests/helpers.bats +tests/hk-selection.bats +tests/hook-latency-drift.bats +tests/hook-matcher-check.bats +tests/hook-pin-check.bats +tests/hook-profile-check.bats +tests/hooks-wiring-check.bats +tests/install-check.bats +tests/install.bats +tests/issue-read-check.bats +tests/issue-read-guard.bats +tests/issue-search-check.bats +tests/issue-search-guard.bats +tests/land-divergence-assert.bats +tests/land-divergence.bats +tests/land-lock-check.bats +tests/land-lock.bats +tests/land.bats +tests/landed-check.bats +tests/license-table-check.bats +tests/linear-check.bats +tests/lock-complete.bats +tests/macos-link-check.bats +tests/main-watch.bats +tests/mcp-allow-check.bats +tests/mcp-attach-check.bats +tests/mcp-grant-sync.bats +tests/mcp-timeout-budget.bats +tests/memories-check.bats +tests/mise-action-floor.bats +tests/mise-pin-agreement.bats +tests/module-map-check.bats +tests/msrv-pin-agreement.bats +tests/mutant.bats +tests/nonverdict-assert.bats +tests/nonverdict-scan.bats +tests/ntia-check.bats +tests/perf-assert.bats +tests/perf-compare.bats +tests/perf-pair.bats +tests/perf-record.bats +tests/pipefail-grep-check.bats +tests/pkl-check.bats +tests/pr-unsubscribed.bats +tests/pre-commit-staging.bats +tests/prebuilt-lint.bats +tests/publish-credential-check.bats +tests/ready-guard.bats +tests/ready-lint-deferral.bats +tests/ready-lint.bats +tests/reclaim-census.bats +tests/reference-check.bats +tests/release-assets-check.bats +tests/release-due.bats +tests/release-tracking-check.bats +tests/released.bats +tests/render-cli.bats +tests/renovate-config-validator.bats +tests/report-only-check.bats +tests/rules-drift.bats +tests/run-shape-guard-quoting.bats +tests/run-shape-guard.bats +tests/sbom-binary.bats +tests/sbom-check.bats +tests/schema-check.bats +tests/semver.bats +tests/serena-mcp.bats +tests/session-start.bats +tests/signing-posture.bats +tests/singleton.bats +tests/skill-check.bats +tests/sonar-gate.bats +tests/step-receipt.bats +tests/stop-guard.bats +tests/stop-posture-check.bats +tests/target-ensure.bats +tests/target-race.bats +tests/task-fail-closed.bats +tests/task-registry.bats +tests/test-bats-parallel.bats +tests/timeout-check.bats +tests/timeout-drift.bats +tests/token-bench.bats +tests/transcript-corpus-check.bats +tests/tree-clean.bats +tests/verified.bats +tests/verify.bats +tests/with-lock.bats +tests/zizmor-split.bats