Skip to content

CLOUD-1163 unit 11: withdraw the replay harness, which never had a consumer - #835

Merged
wenzowski merged 1 commit into
mainfrom
claude/bundle-bats-retirements-fah2t0
Sep 2, 2026
Merged

wenzowski merged 1 commit into
mainfrom
claude/bundle-bats-retirements-fah2t0

Conversation

@wenzowski

Copy link
Copy Markdown
Contributor

Closes CLOUD-1163's unit 11. mise-tasks/replay.sh, mise-tasks/replay-pointers.py and tests/replay.bats are deleted whole, with no successor at either end — a withdrawal rather than a port, and the ledger says so in seventeen arms.

Why this is a deletion rather than a blocked row

CLOUD-1115 is the argument for the deletion, not against it, and the row records the inversion. That issue's title is that replay's tree arm "has never run against a real gate and cannot pass it", and its own §8 opens "Blockers. None, in either direction."

Confirmed at 9e7a9a91 rather than inherited: a scan of mise.toml, hk.pkl, .claude/settings.json and .github/workflows/ finds the word only in prose, and the sole caller of replay-pointers.py was replay.sh:80 itself. A gate off the landing path, invoked by nothing, whose one live arm cannot pass, is a gate deciding nothing.

What the withdrawal costs, stated rather than implied

Not the fidelity property. replay (CLOUD-909) was to be the evidence that a migrated gate answers the way the bash it replaced did. That property is unestablished either way, because the harness never ran against a real migration — so this removes an unexercised instrument, not a result.

The fifteen case arms pinned the instrument's own discrimination: that it refuses a vacuous pass, refuses an identity translation (1=1, the naive assert_equal $status 1 written down), and reports a divergence instead of admitting one. Those are real properties of a thing nobody built a consumer for, and they die with it. The ledger says that plainly rather than implying the cases moved somewhere.

One finding is carried forward in prose because it cost a measurement and would otherwise be re-derived: batten check renders path:line rule for a tree-scoped row and no remedy at all, because non-negotiable rule 4 is its output contract. A fidelity harness that greps refusal output for remedy prose therefore reports every faithful migration of a tree gate as having lost one. The remedy has to be read from the declaration, never from the refusal.

Ledger shape

Two // withdrawn: file arms naming each other, as withdrawn_subjects demands, plus fifteen case arms. mise-tasks/replay-pointers.py takes no arm and gets none — governed_when_deleted excludes .py — and it is still named as the suite's co-subject and dies in this same delta, so SubjectFacts::died holds across both declared subjects.

Verification

mise run verify green on this head. Corpus regenerated from a full run: 2080/2080 cases over 115 suites, tests/replay.bats gone from bench/suites/RESULTS.md.

One scope correction, recorded here because it changes the bundle

CLOUD-1163's unit 7 (suite-bench-check + suite-bench) is out, on the row's own 2026-09-01 comment rather than on my judgement: tests/suite-bench-check.bats:2 declares both programs as its # subject: and SubjectFacts::died is .all(), so the suite cannot be split; suite-bench.sh is a producer that writes bench/suites/RESULTS.md, and no has_policy_surface admits a writer. Naming it raises V-RETIREMENT-SUBJECT-ALIVE, not naming it raises SUBJECT_ALIVE. It needs a disposition for producers, which is a row rather than a bundle member.

Refs: CLOUD-1163, CLOUD-1115, CLOUD-909

🤖 Generated with Claude Code

https://claude.ai/code/session_01X6dcTC79zN5KBiMrZs6hac


Generated by Claude Code

@linear-code

linear-code Bot commented Sep 2, 2026 •

Copy link
Copy Markdown
CLOUD-1163 Retire the eight small multi-program units — 59.7s, and SIX of eight land today: BOTH remaining blockers (CLOUD-1108, CLOUD-1115) disclaim blocking in their own bodies, by name

COLUMN CORRECTED 2026-09-02 — In Progress → Todo; unit 8 landed, the other seven are unclaimed

Measured against origin/main 9ece058c and the nine open PRs (file lists, not titles):

A claim nobody is working is not a claim. Back to Todo; the Ready block below still holds and blockedBy CLOUD-1251 still binds unit 4 only.


CORRECTION (2026-08-31, second pass) — the two BLOCKERS were read from this row's own table instead of from the rows they cite, and both cited rows say the opposite IN BOLD

This row's 2026-08-31 correction fixed three wrong reasons by reading the programs. It did not re-read the two BLOCKER ROWS, and that is where the remaining error was. Read each of them at head and both disclaim blocking this row, by name:

  • Unit 9 (run-shape-guard, 14.0s) is NOT blocked by CLOUD-1108. That row was re-derived 2026-08-31 against origin/main 0683ce53 and its point 3 names this row explicitly: "CLOUD-1151's build order lists this row third and says it blocks CLOUD-1163's run-shape-guard unit. On the evidence above that unit is not blocked by file-granularity — it is a whole-file retirement whose four successors are already written, and what it owes is the ledger arms and the deletion, not a ratchet change." Verified in the tree rather than inherited: policy/run-shape.rego raises V-COMMIT-STDIN-UNBOUND, V-FOREGROUND-SLEEP and V-BACKGROUND-TIMER, and policy/task-substitution.rego is tracked and opens by naming this exact family. N = K = 4. The #MISE description at run-shape-guard.sh:2 names those same three families and nothing else; the fourth is the cargo clause on the same line. There is no family left waiting, so the file is deletable WHOLE — the ratchet's one admitted disposition.

  • Unit 11 (replay, 18.9s) is NOT blocked by CLOUD-1115. That row's §8 opens: "Blockers (§8). None, in either direction. This row blocks nothing, and a blocks CLOUD-910 relation added here on 2026-08-28 was wrong and has been removed" — the identical relation, wrong once already, re-derived here a second time. Its own measurement is the argument: "nothing refuses a retirement for a missing or failing replay… replay appears in no hk.pkl step, no mise.toml task graph, no workflow and no rule row." Confirmed at head: a pattern scan of mise.toml, hk.pkl and .claude/settings.json for replay returns two PROSE comments (mise.toml:911,952) and no invocation; the only caller of replay-pointers.py is replay.sh:80 itself.

    And CLOUD-1115 is the argument FOR unit 11's deletion, not against it. A gate whose tree arm "has never run against a real gate and cannot pass", off the landing path, invoked by nothing, is a gate deciding nothing. Its disposition is the // withdrawn: arm — subjects-and-reason with no successors at all, policy/shell-retirement.rego:708 — which is the one ledger arm that has never been used (CLOUD-1176). Unit 11 is the campaign's first honest candidate for it, and it is the single largest member of this row.

Both blockers were carried on this row's TITLE-adjacent verdict rather than on the cited row's body. That is CLOUD-1166's class one level up from the three this row already corrected: not classifying a program instead of reading it, but classifying a BLOCKER instead of reading it. The reading took minutes, as it did the first three times.

The seconds are stale, and every rank moved

The 52.2s in the old title and the per-unit column below predate CLOUD-1198's corpus regeneration. Re-derived from bench/suites/RESULTS.md at origin/main:

unit members' suites was now disposition at head
11 replay 7.1 18.9 lands — // withdrawn:
6 token-bench 16.4 + token-bench-check 0.4 10.6 16.8 lands
9 run-shape-guard 13.6 + -quoting 0.4 11.8 14.0 lands — whole-file
4 mcp-allow-check 5.9 + connector-allow-guard 0.5 + -resolve 0.4 9.5 6.8 blocked (CLOUD-1251)
10 perf-compare 0.7 + perf-gate 0.2 1.0 0.9 lands
8 gh-guard 0.9 1.0 0.9 lands
7 suite-bench-check 0.9 0.7 0.9 lands
3 container-preflight 0.4 + egress-check 0.1 0.5 0.5 not migrating (a verdict)
52.2 59.7

Unit 11 went from fourth-largest to largest, and units 11 and 9 together — 32.9s, 55% of this row — were the two carried as blocked. The row is now: six units and 52.4s land today, 6.8s is genuinely blocked on CLOUD-1251, and 0.5s is not migrating.

Arm-4 evidence for the one surviving block, quoted rather than categorised: connector-allow-resolve.sh:142 is for candidate in /tmp/mcp-config-cse_*.json; do — a glob over a set discovered at runtime, under a suffix minted per session, which [[rule.external]]'s one-declared-path-per-row shape cannot express. That is CLOUD-1251 and it is real.


Why

Units 3, 4, 6, 7, 8, 9, 10 and 11 of the 83-unit partition. Eight independent deltas, 19 programs, 52.2s combined. They are one row because each is small and none glues to another; each still lands as its own PR with its own ledger arms.

unit members suites s gates #MUTANT
9 run-shape-guard, payload-field run-shape-guard 11.2, run-shape-guard-quoting 0.6 11.8 1 3
6 token-bench, token-bench-check token-bench 10.4, token-bench-check 0.2 10.6 1 1
4 connector-allow-guard, connector-allow-resolve, mcp-allow-check mcp-allow-check 8.6, +2 9.5 3 4
11 replay.sh + replay-pointers.py replay 7.1 7.1 0 0
10 perf-gate, perf-compare perf-compare 0.8, perf-gate 0.2 1.0 2 2
8 gh-guard, gh-guard-check gh-guard 1.0 1.0 1 1
7 suite-bench-check, suite-bench suite-bench-check 0.7 0.7 1 2
3 container-preflight, egress-check, gh-preflight container-preflight 0.4, egress-check 0.1 0.5 1 1

Glue, per unit (each is a $(dirname "$0")/$here resolution or a multi-subject header, neither of which any admission in policy/shell-retirement.rego can match):

  • 9 — run-shape-guard.sh:110 field="$here/payload-field.sh". hook-pin-check.sh:149 names payload-field literally, so it does not join.
  • 6 — tests/token-bench.bats:2 declares both subjects; also token-bench-check.sh:131.
  • 4 — connector-allow-guard.sh:59 and mcp-allow-check.sh:324 → connector-allow-resolve.sh. mcp-allow-check.sh:235 iterates "$(dirname "$0")"/*-guard.sh — a glob, not a named path, so it does not glue gh-guard/ready-guard.
  • 11 — tests/replay.bats:2 declares replay.sh and replay-pointers.py. The .py is ungoverned so it owes no arm, but SubjectFacts::died demands it actually be deleted. The only unit whose closure crosses out of the governed set.
  • 10 — perf-gate.sh:48 runs perf-compare. perf-pair is a mise.toml task, not a program, so it does not join.
  • 8 — tests/gh-guard.bats:2 declares both; also gh-guard.sh:22.
  • 7 — tests/suite-bench-check.bats:2 declares both. This unit owns bench/suites/RESULTS.md, the source of every number in this campaign.
  • 3 — container-preflight.sh:61 → egress-check.sh; :82 → gh-preflight.

The home each unit's decision lands in

A disposition is chosen before a successor is designed (CLOUD-1176). "Port it into crates/batten" is not on the list of homes: house style §2's surface is closed and §9 says consumer-specific behaviour is reconstructed through extension surfaces, never baked into the core.

unit home why
8 consumer module — policy/*.rego a pure argv classifier over input.call.segments; the gh lifecycle vocabulary and the task names it recommends are this repo's, so rule 1 keeps it out of a preset
9 consumer module (policy/run-shape.rego, three families already there) + existing verb for the glue payload-field.sh's successor already ships as batten payload field; the remaining cargo-substitutes-for-a-task family derives from mise.toml task bodies, which are consumer facts
10 existing verb — batten perf / batten perf pair already shipped, effect write
6 consumer module — policy/*.rego, two predicates token-bench-unmethodical is a document predicate over bench/tokens/RESULTS.md (input.tree.lines); token-bench-drift is a producer-written identity verdict (input.tree["tool-verdict"]). token-bench itself is a measurement task and a measurement task is not a gate
7 consumer module — policy/*.rego, plus a [[pattern]] row suite-bench-check is SET EQUALITY over paths — input.tree.tracked against the corpus rows in input.tree.lines — and reads no duration at all; suite-bench produces bench/suites/RESULTS.md
4 consumer module, once a DISCOVERED-path fact exists the MCP config paths are consumer-specific by construction — and external does not reach them, because it declares one path per row and these are globbed per session (see the 2026-08-31 correction)
3 keep-as-task — NOT MIGRATING proxy and token-scope probing is not a completion gate's job, and per CLOUD-1202 that is a recorded verdict rather than a fact to wait for
11 undecided — likely DELETE replay has no successor verb and CLOUD-1115 says its tree arm cannot pass as built; decide the disposition before designing anything

Which of the eight are actually reachable

  • Unblocked: unit 8 (gh-guard-check is a pure argv classifier over input.call.segments; gh-guard.sh:22 only shells to it).
  • Blocked on CLOUD-1108 — unit 9. The ratchet is file-granular and run-shape-guard is multi-family; three of four families already landed as policy/run-shape.rego, and the remaining cargo-substitutes-for-a-task family is expressible but cannot move alone.
  • CORRECTION (2026-08-30) — "benchmark/build execution" was never a blocker. This row previously listed units 6, 7, 10 and 11 as blocked because "no fact carries" a build or a suite result. Measured against the emitted surface rather than inferred: batten perf **and **batten perf pair already ship, effect write, and crates/batten/src/perf.rs builds two binaries and spawns hyperfine. §5's split is check = read and structurally incapable of spawning; enforce/exec are the spawning side, and perf sits there. So an execution is not outside the engine — it is outside check, which is a different sentence. Unit 10 is therefore unblocked, and units 6 and 7 are blocked only on their comparator's inputs, never on the execution. This also re-scopes CLOUD-1171, which generalised the same false premise.
  • Blocked, out-of-root: unit 4 (connector-allow-resolve.sh:142 globs /tmp/mcp-config-cse_*.json), unit 3 (container-preflight reads $HTTPS_PROXY and token scopes). Superseded — see the 2026-08-31 correction below. "Out-of-root" is no longer the reason for either, and it is now the wrong reason for both.

So three of eight land today — units 8, 10, and (comparator-first) 7. That is the honest count and it is why CLOUD-1151 cannot size a wave from unit count.

CORRECTION (2026-08-31) — the blocked verdicts above predate the fact surface they were judged against

Every blocked verdict in this row was written on 2026-08-29/30. Seven fact families have landed since, and the row was never re-read against them. Enumerated from the generated schema/policy-input.schema.json at 0683ce53, input.tree now carries external (CLOUD-1167), tool-verdict (CLOUD-1171), captured (CLOUD-1188), state (CLOUD-1203), commit-meta (CLOUD-1187), git-history, forge (CLOUD-1154) and staged — all Done. Re-derived per unit:

  • **Unit 4 is still blocked, but NOT because the paths are out of root — because they are DISCOVERED rather than DECLARED. **external landed and it takes exactly one path under one named root environment variable per declared row; the schema's own description is "the parsed node of the file found at path beneath the directory the named root environment variable holds", keyed by the declaring row's id. connector-allow-resolve.sh:142 globs /tmp/mcp-config-cse_*.json — a set discovered at runtime, which no declared single path can express, and a consumer cannot enumerate the ids in advance because the suffix is minted per session. That is a real gap with no owning row; it is filed separately and this unit is blockedBy it.

  • **Unit 3 is still blocked, and its reason belongs to a different class entirely. **container-preflight reads $HTTPS_PROXY's VALUE and probes egress and token scopes. external reads a FILE under a root variable, never a variable's value, and nothing on the surface performs a probe. Per CLOUD-1202's decision, this is the scope reminder's "not a reference monitor" clause rather than a missing fact: **the disposition is keep-as-task, and the unit should be recorded as not migrating rather than carried as blocked forever. **CLOUD-1201 reaches the same verdict from the caller's side.

  • Units 6 and 7 — SETTLED, and the premise was wrong in BOTH directions. NEITHER COMPARATOR COMPARES A MEASUREMENT. An earlier revision of this clause said they compare "MEASUREMENTS against a baseline" and sent an implementer to settle which fact carries one. That was asserted from the category "comparator over two records" without reading either program, and it is the same error one level down as the two above.

    Unit 7 (suite-bench-check) reads no duration at all, and its own header says so as a design decision: "It is NOT byte-diffed against a fresh run … Wall clock is not [deterministic] … WHAT IS DETERMINISTIC IS MEMBERSHIP, and that is what rots." The predicate is set equality between two sets of PATHS — git ls-files 'tests/*.bats' against the paths in the third column of bench/suites/RESULTS.md, both directions. Both are on the surface today: input.tree.tracked ("repository-relative paths the working-tree walk yields — paths, never content") and input.tree.lines over the corpus. Unit 7 needs no new fact and is fully expressible now. Its output is already pointer-only by the same rule 4 the module inherits — "Never a duration — a number here would be a second authority over the corpus."

    Unit 7 does need a [[pattern]] row, and this is the one real hazard: the corpus row is parsed for a backticked path inside a Markdown table, and the shell records that exact regex being got wrong once — "Run against a formatted corpus it matched no row at all and reported every tracked suite as missing: 150 findings, all false, from a gate that looked like it was working." prettier owns Markdown here and pads table columns. Carry that case into the successor's suite.

    Unit 6 (token-bench-check) is two predicates, and neither is a threshold either. token-bench-unmethodical asks whether every published figure states workload, baseline, run count and method — a pure document predicate over bench/tokens/RESULTS.md, so input.tree.lines. token-bench-drift asks whether the committed table is byte-identical to what a fresh run produces from committed fixtures — an identity check, not a comparison against a threshold, and exactly the shape input.tree["tool-verdict"] landed for: "read back from a record a producer wrote OUTSIDE the engine, because check is read-only and structurally cannot run a validator — KEYED BY (tool, pinned version, input digest)". The producer runs the generator; the module reads the verdict. So unit 6 is also expressible, and tool-verdict IS the answer for its drift half — the opposite of what this clause previously said.

    Neither unit needs records or produced, and neither needs a measurement fact that does not exist.

~~THE COUNT CHANGES: it is now FOUR of eight, not three. ~~(Superseded 2026-08-31 by the second-pass correction at the top: it is SIX of eight. Units 9 and 11 were blocked on rows that each disclaim blocking.) Unit 6 was carried as blocked on a comparator input it does not need, and reading token-bench-check.sh rather than its category shows it is expressible today. Units 6, 7, 8 and 10 land now. Unit 3 is not migrating (a verdict, not a block); units 4, 9 and 11 remain blocked, on CLOUD-1251, CLOUD-1108 and CLOUD-1115 respectively. Only unit 4 remains blocked, on CLOUD-1251. CLOUD-1108 and CLOUD-1115 each disclaim blocking this row in their own bodies — see the second-pass correction at the top — and both blockedBy relations have been removed.

Every wrong reason in this row came from classifying a program instead of reading it. Three corrections, three instances: "out-of-root" for unit 4, "missing fact" for unit 3, "comparator inputs" for units 6 and 7. The reading in each case took minutes. Read the program before recording why it cannot move.

Two need a runnable successor at a real path, not just a ledger arm

run-shape-guard.sh is .claude/settings.json:24's hook command; serena-mcp.sh (a different unit) is .mcp.json:4's. Deleting either without an executable at that path disarms a live hook silently. Both consumer files are ungoverned, so the repoint is free — but it must happen in the same delta.


Refinement — Ready (eight deltas, one row; units 8, 10 and 7 land today)

Refinement gate: Definition of Ready & Done. This body carries only specializations.

  • Authority boundary (§1). 19 programs and 13 suites across eight PRs. One // carried: arm per deleted path; replay-pointers.py is deleted without an arm (ungoverned) but must be gone. Ten $MUTANT_GATES entries and 14 #MUTANT rows move to successors' tiers.
  • Computable predicate (§2). Each unit's decision is conserved; this row moves where it lives, never what it concludes. Conserve the decision, not the defect (CLOUD-1176): where a unit's shell carries a tracked defect, the successor implements the corrected decision and the defect's own row records the change — a retirement that launders a known defect forward into Rust is the failure this campaign exists to avoid, not evidence of fidelity. What must be shown unchanged is the verdict over every case the dying suite covered, minus the cases a tracked defect row explicitly re-decides.
  • Deliberately not in scope (§2). Deciding CLOUD-1108's file-granularity fix. Changing what any benchmark measures. Merging any two of these units — they are independent and merging them would manufacture the glue this partition exists to avoid.
  • **Effect (§3). **read for the guards and checks. token-bench, suite-bench, perf-* and replay execute suites or builds and keep their existing effect class.
  • Output and exit (§5). Pointer-only per unit. Exit follows the 0/1/2/3 table; could-not-look is 3, never a false 2.
  • **Commit / bump (§6). **refactor(ci) — no bump, per PR. Below 0.1.0 every release-worthy type collapses to a patch, but refactor is not one: it releases nothing at any version. CLOUD-595's correction.
  • Test obligation (§7). Over the compiled binary in crates/batten/tests/; no .bats file is added or edited (V-SHELL-RULE-ADDED refuses one at deny). One arm per deleted path (CLOUD-908). Shown able to fail per CLOUD-418 per unit, with the anti-vacuity mirror each time. For unit 9 specifically: the hook must still deny after the repoint — a test that only checks the module loads would pass over a disarmed .claude/settings.json. Mutated: 14 rows re-homed, mutant-census green across every move. Replayed: per unit; CLOUD-1115 is the standing caveat on replay's tree arm, and unit 11 **is **replay — it cannot be its own instrument.
  • **Blockers (§8). **blockedBy CLOUD-1251 (unit 4 only). blockedBy CLOUD-1108 (unit 9 only). Removed 2026-08-31: CLOUD-1108's own re-derivation names this row's unit 9 as not blocked by it, and CLOUD-1115 states it blocks nothing. relatedTo CLOUD-1151 (the wave owner), CLOUD-1115, CLOUD-908, CLOUD-418.

Weakens: rule-removed rule[harness-wiring-merged]

Weakens: rule-predicate-changed rule[harness-wiring].documents

Weakens: rule-predicate-changed rule[harness-wiring].external

Weakens: rule-predicate-changed rule[harness-wiring].minted

Unit 9's landing carries four config smells against origin/main, declared here rather than only in the commit that performs them. harness-wiring-merged is removed because CLOUD-1160's split of the wiring predicate into two rules cited an engine defect that does not exist, so the halves recombine into one rule. The three predicate changes are harness-wiring gaining the merged rule's external rows, the policy/harness-declared.json document that keeps the exemption table's three directions testable once unit 9 empties it, and CLOUD-1310's minted fact source. config-lint compares bytes with no ranking, so an ADDED fact source reads as a change in the same way a removed one does; each of these reads more rather than less.

Acceptance

  • Eight PRs, 19 programs and 13 suites deleted, one arm per deleted path, replay-pointers.py gone.
  • .claude/settings.json:24 points at a live executable and the hook still denies, asserted end to end.
  • bench/suites/RESULTS.md survives unit 7's retirement — the campaign's own measurement source must not die with its producer.
  • mutant-census green; all 14 mutations honoured.
  • Each unit records whether it landed or why it could not.

Units 3, 4, 6, 7, 8, 9, 10, 11 of 83.

CLOUD-1115 `replay`'s tree arm has never run against a real gate and cannot pass: it captures the working directory instead of the case's inputs, so every carried case is judged over the wrong tree

Why

CLOUD-909 built mise run replay with two grammars. # replay-call:, the mediated arm, has five committed users. # replay:, the tree arm, has exactly one — crates/batten/tests/mise_pin_agreement.rs:53, added by the retirement below and red from its first run. (An earlier draft of this row said the tree arm had never had a committed user; corrected 2026-08-28. The one it has is the one that fails.) — and CLOUD-910's wave 1 makes it the Replayed arm for every gate it retires.

Its first use against a real gate was mise-pin-agreement on 2026-08-28. All 8 carried cases failed, uniformly, and the cause is not the migration:

a scoped launch whose version matches mise.toml passes    head-answer-unreadable
a version .mcp.json names that mise.toml does not pin ...  head-answer-unreadable
... 6 more, identical
::error:: replay: 8 of 8 carried case(s) did not replay faithfully across 1 suite(s)

The migration itself is green on both tiers it can be judged by: the module's 12 test_ rules pass, and crates/batten/tests/mise_pin_agreement.rs drives the compiled binary over real trees and passes. This row is about the instrument.

Three independent defects, measured with a preserved work directory

An out-of-tree copy of replay.sh with the cleanup trap disabled, run over one declaration.

1. The captured fixture is the WORKING DIRECTORY, not the case's inputs

The shim records $PWD and copies $PWD/. as the fixture. Measured, captures-1/1/cwd:

.../work/base-1                          <- the base worktree ROOT

and the case's actual inputs are elsewhere entirely — from the same capture's stdout:

mise-pin-agreement: 1 tool reference(s) in /tmp/bats-run-hFn547/test/1/.mcp.json
agree with /tmp/bats-run-hFn547/test/1/mise.toml, ...

This is not a quirk of one suite. Every structured-config gate in wave 1 takes its inputs as argv paths precisely so its bats suite can point it at $BATS_TEST_TMPDIR; none of them cd anywhere. So the captured fixture is a full copy of the repository, and the head side is asked its question about the repository's own .mcp.json and mise.toml rather than about the case's. That is worse than a failure: had the head side been able to answer, the comparison would have been a confident verdict about the wrong input.

It is also why the run does not fit on disk. 62 carried cases across four suites each copy a whole worktree; the run died with No space left on device on a container with 11 GB free.

2. The head side cannot run at all over such a fixture

captures-1/1/head-err:

batten: Refused by no-secrets: V-SPAWN-ON-READ-VERB (this rule kind runs a
configured command, which a read-effect verb will not do) secrets.

replay.sh copies the head batten.toml into the fixture and commits it as an authority. That authority carries command rows, and check is a read verb that will not spawn — so batten check -J refuses, head-json is empty, and replay-pointers.py returns 3, which surfaces as head-answer-unreadable. Any fixture that is a copy of this repository hits this.

3. The pointer comparison is between incommensurable things

replay.sh builds the base pointer set as awk 'NF { print $1 }' over the captured stdout, on the premise stated in its own header that "the old gate writes path:line <prose>". The gates in this wave do not: they write findings to stderr and a one-line summary to stdout. Measured, captures-1/1/shell-pointers:

mise-pin-agreement:

— the first word of a prose sentence, standing in for a pointer set. On a failing case the same file is empty, because the whole refusal went to stderr. So even with (1) and (2) fixed, a faithful port reports pointer-set-differs on every case in both directions.

Why this was not caught earlier

replay is off the landing path by design — not in verify, not in the hk gate — so nothing exercises the tree arm between runs. CLOUD-1037 is the same shape one grammar over: it found the mediated arm capturing nothing. A second instance in the same task is what makes this a row about the instrument rather than about one declaration.

A fourth, smaller instance of the same class was repaired in passing on CLOUD-910's branch rather than filed: connector_verbs.rs declared base rev bdb2af6, which is in no branch and no tag — a pre-rebase sha invalidated when land rebased the retiring branch. A single unresolvable rev is fail_input, so it took every other declaration down with it. Nothing gates that either.


Refinement — Ready

Refinement gate: Definition of Ready & Done. This body carries only specializations.

  • **Source of truth (§1). **mise-tasks/replay.sh — the shim that writes the capture, and the comparison block that reads it. mise-tasks/replay-pointers.py owns the head-side extraction and is the second half of defect 3.
  • **Computable predicate (§2). **mise run replay exits 0 over a declaration for a gate that takes its inputs as argv paths and writes findings to stderr. Decidable locally; the gate retired under CLOUD-910 is the fixture, and it is red today.
  • **Effect (§3). **effect — unchanged. The task already spawns the base rev's bash in a worktree, which is why it is off the landing path.
  • Generated artifacts (§4). None.
  • Output & exit (§5). Unchanged and pointer-only: the case and how it diverged, never the two answers side by side (non-negotiable rule 4). Defect 3's repair must not become an excuse to print either side's findings.
  • **Commit / bump (§6). **fix — **patch until **0.1.0.
  • Test obligation (§7). Shown able to fail per CLOUD-418: the reproduction above is the case and it is red on the tree as it stands. tests/replay.bats is where it belongs. Assert the capture is the case's declared inputs rather than $PWD, and that a gate writing findings to stderr yields a pointer set the head side can be compared against — a case that only fixes the fixture would still be red on defect 3, which is what makes them separable.
  • Blockers (§8). None, in either direction. **This row blocks nothing, and a **blocks CLOUD-910 relation added here on 2026-08-28 was wrong and has been removed — measured the same day: nothing refuses a retirement for a missing or failing replay. policy/shell-retirement.rego:326 gates only the four conserves arms and carries no replay term; replay appears in no hk.pkl step, no mise.toml task graph, no workflow and no rule row, and its own header says so (mise-tasks/replay.sh:47-51, "OFF THE LANDING PATH"). The empirical proof is that mise-pin-agreement's retirement landed as a90fe8c with its # replay: row admittedly unsatisfiable, and CLOUD-910 is Done. So the Replayed arm is a quality bar with no mechanism behind it — which is this row's subject and the reason to fix the instrument, not a licence to gate a wave on it. relatedTo CLOUD-909 (which built this task and never exercised the tree arm), CLOUD-910 (whose wave 1 is the first consumer of the tree arm — and which landed Done anyway, which is the measurement above), CLOUD-1037 (the same class in the mediated grammar).

Acceptance

  • mise run replay is green for mise-pin-agreement's 8 carried cases, or names a real divergence rather than an artefact of the harness.
  • The captured fixture is the case's inputs. A run over the four existing replay-call: declarations no longer copies a worktree per case, and completes without exhausting disk.
  • A gate that writes its findings to stderr produces a comparable pointer set, and the comparison still prints neither side.
  • An unresolvable base rev is reported as one declaration's defect rather than as fail_input for the whole run.

CLOUD-909 A migrated gate's fidelity is a doc comment: the shell tasks spell `1 = violation` and batten's contract is the inverse, so a carried-over `assert_equal $status 1` asserts the wrong thing and passes

Why

batten policy test (CLOUD-835) gives a migrated module somewhere to put its tests. It cannot say the module answers the way the bash it replaced did — only that the module agrees with itself. CLOUD-845 measured the sharp end of that: a module copied from policy.rs's own doc passed policy test green and gated nothing, because with input as lets the author fabricate the very shape the engine cannot produce. 845's landed fix refuses a with input as naming an unemittable key, which closes that class. It does not establish fidelity.

Today fidelity is a claim in a doc comment. That is not a criticism of the one port that exists — it is careful, and its header is the best evidence available. It is an argument that the evidence has to be produced by a command, because the next twenty will be authored in one PR.

The hazard, measured, and it is not the obvious one

The obvious harness — run both, assert the same exit code — is wrong here, and crates/batten/tests/contract_drift.rs's own header says why:

"the exit numbers are translated rather than copied: the shell tasks use 1 = violation, where batten's contract is the inverse (house-style §7). Carrying a bats assert_equal $status 1 across unchanged would assert 'unreadable input' while meaning 'violation' — and it would pass, which is the false green that hazard exists to name."

So an equality assertion would demand the migration preserve the contract it exists to fix, and the naive carry-over passes it. A human stepped around this by hand, once. Twenty gates in one PR will not get twenty careful humans.

The mechanism

Per carried case (CLOUD-908's first arm) in a dying suite:

  1. Materialise that case's own fixture — not a new one written for the harness. The evidence has to come from the thing being deleted.
  2. Run the bash program as it exists at the base rev: git show <base>:mise-tasks/<x>.sh. It is still in the base tree at the moment of deletion, which is why this can only be produced inside the PR that deletes it, and why that is the right place for it.
  3. Run the new row over the same fixture, through the compiled binary.
  4. Assert:
    • identical pointer set, byte for byte. This is the invariant half and the half worth pinning: a pointer survives the exit-code translation unchanged, and non-negotiable rule 4 already forbids either side carrying anything else.
    • exit code through a declared translation, never a raw equality: shell 1 (violation) → batten 2; shell 0 → 0; shell 2 (could-not-look) → batten 1. The translation is declared per suite and asserted, because that is exactly where the false green lives.
    • the remedy survives. CLOUD-437's clause: a migrated gate's refusal text must still name its remedy. A msg that lost the remedy in translation is a regression no bats case would have caught, and the pointer-set comparison does not see it.

Only carried cases are replayed. Subsumed cases are discharged by CLOUD-908's named successor. Changed cases are expected to diverge — and a replay red on a case not marked changed is the failure this instrument exists to report.

Generalize tests/run-shape.bats, which already drives the compiled binary through batten hook — the door a real call comes through. Off the landing path, the way mutant is: it is evidence for a migration, not a per-land tax.


Refinement — Ready

Refinement gate: Definition of Ready & Done. This body carries only specializations.

  • Source of truth (§1). Two revs of one tree: the base rev's bash program and its suite, and the head's module. The exit-code translation is declared beside the mapping rather than hard-coded in the harness, so a gate that genuinely does not follow the shell convention can say so instead of being silently mistranslated.
  • Computable predicate (§2). For each carried case: pointer sets equal byte-for-byte and the observed exit-code pair is admitted by the declared translation and the new refusal text still names a remedy. Any of the three failing → exit 2, naming the case. A case marked changed is exempt from the first two by declaration; a case not so marked that diverges is the reported failure.
  • Effect (§3). effect — it spawns the base rev's bash program in a fixture directory. That is why it sits off the landing path beside mutant and never inside the mediated call. It is not registered in the hk gate and adds nothing to verify's hot path.
  • Generated artifacts (§4). None.
  • Output / exit (§5). Pointer-only, and this one is load-bearing rather than formal: the harness compares two findings and must report that they differ, plus the case name and path:line — never the two pointer sets side by side, which would put a diff of tracked content on stdout. crates/batten/tests/pointer_only.rs is the existing shape.
  • Commit / bump (§6). feat(tasks) — not !: a new off-path task, no published surface changed, no consumer's config affected.
  • Test obligation (§7). Shown able to fail (CLOUD-418), one observed red each: (a) a module whose pointer set differs from the bash's is reported, naming the case; (b) the naive carry-over — a translation declared as 1 → 1 is refused, because the hazard above was navigated by hand once and the mechanism must be seen catching it; (c) a module that diverges on a case not marked changed is reported, while the same divergence on a case marked changed passes; (d) a migrated refusal whose msg dropped its remedy is reported; (e) the positive arm — a faithful migration replays green across every carried case, which without (a)–(d) would be a harness that admits everything.
  • Blockers (§8). None. Consumes CLOUD-908's carried arm, and lands on the same branch after it.

Acceptance

  • The replay runs per carried case, over the dying suite's own fixtures, against the base rev's bash.
  • Pointer sets are compared byte-for-byte; exit codes go through a declared translation and never a raw equality.
  • The remedy clause is asserted.
  • Each of (a)–(e) observed, not asserted.
  • Off the landing path: verify's wall-clock is unchanged, measured rather than argued.

Review in Linear

@coderabbitai

coderabbitai Bot commented Sep 2, 2026 •

Copy link
Copy Markdown

Review Change Stack

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Free

Run ID: 0577db1c-ed29-4775-9507-813990ae5311

📥 Commits

Reviewing files that changed from the base of the PR and between dd9a3da and 67183e8.

📒 Files selected for processing (5)
  • bench/suites/RESULTS.md
  • crates/batten/tests/it/ratchet.rs
  • mise-tasks/replay-pointers.py
  • mise-tasks/replay.sh
  • tests/replay.bats
💤 Files with no reviewable changes (3)
  • mise-tasks/replay.sh
  • mise-tasks/replay-pointers.py
  • tests/replay.bats

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

The replay harness, replay pointer utility, and replay Bats suite were deleted. The ratchet ledger now documents the withdrawn harness, its former behaviors, and the remaining batten check contract. The benchmark report now lists 115 suites, 471.9 seconds, refreshed suite timings, and no tests/replay.bats entry.

Merge Risk: ⚪ Minimal · up to 67183

This change removes an unused replay harness and updates benchmark records; no actionable merge-blocking risk remains beyond normal checks and review.


Note

🎁 Summarized by CodeRabbit Free

Your organization is on the Free plan. CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please upgrade your subscription to CodeRabbit Essentials by visiting https://app.coderabbit.ai/settings/billing.

Comment @coderabbitai help to get the list of available commands.

…onsumer

CLOUD-1163 unit 11. `mise-tasks/replay.sh`, `mise-tasks/replay-pointers.py` and
`tests/replay.bats` are deleted whole, with no successor at either end. That is a
withdrawal rather than a port, and the ledger says so in seventeen arms.

CLOUD-1115 is the argument FOR this deletion, not against it, and the row records
the inversion: its title is that `replay`'s tree arm "has never run against a real
gate and cannot pass it", and its own section 8 opens "Blockers. None, in either
direction." Confirmed at `9e7a9a91` rather than inherited — a scan of `mise.toml`,
`hk.pkl`, `.claude/settings.json` and `.github/workflows/` finds the word only in
prose, and the sole caller of `replay-pointers.py` was `replay.sh:80` itself. A
gate off the landing path, invoked by nothing, whose one live arm cannot pass, is
a gate deciding nothing.

What is lost is not the fidelity property. `replay` was to be the evidence that a
migrated gate answers the way the bash it replaced did; that property is
unestablished either way, because the harness never ran against a real migration.
The fifteen case arms pinned the instrument's own discrimination — that it refuses
a vacuous pass, refuses an identity translation, and reports a divergence instead
of admitting one. Those are real properties of a thing nobody built a consumer
for, and they die with it. The ledger states that plainly rather than implying the
cases moved somewhere.

One finding is carried forward in prose because it cost a measurement and would
otherwise be re-derived: `batten check` renders `path:line rule` for a tree-scoped
row and no remedy at all, because non-negotiable rule 4 IS its output contract. A
fidelity harness that greps refusal output for remedy prose therefore reports
every faithful migration of a tree gate as having lost one. The remedy has to be
read from the declaration, never from the refusal.

`mise-tasks/replay-pointers.py` takes no arm and gets none: `governed_when_deleted`
excludes `.py`. It is still named as the suite's co-subject and dies in this same
delta, so `SubjectFacts::died` holds across both declared subjects.

Corpus regenerated from a full run: 2080/2080 cases over 115 suites.

Refs: CLOUD-1163, CLOUD-1115, CLOUD-909
@wenzowski
wenzowski marked this pull request as ready for review September 2, 2026 21:01
@wenzowski
wenzowski force-pushed the claude/bundle-bats-retirements-fah2t0 branch from 52b1dd0 to 67183e8 Compare September 2, 2026 21:01
@sonarqubecloud

sonarqubecloud Bot commented Sep 2, 2026

Copy link
Copy Markdown

❌ The last analysis has failed.

See analysis details on SonarQube Cloud

@wenzowski

Copy link
Copy Markdown
Contributor Author

/fast-forward

@wenzowski
wenzowski merged commit 67183e8 into main Sep 2, 2026
9 of 10 checks passed
@wenzowski
wenzowski deleted the claude/bundle-bats-retirements-fah2t0 branch September 2, 2026 21:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant