Skip to content

refactor: complete the repo-wide tidy sweep (all 70 groups) - #3710

Merged
kyle-sexton merged 56 commits into
mainfrom
claude/hello-yuwqst
Sep 4, 2026
Merged

kyle-sexton merged 56 commits into
mainfrom
claude/hello-yuwqst

Conversation

@kyle-sexton

@kyle-sexton kyle-sexton commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

No related issue: repo-wide maintenance sweep run interactively from a Claude Code session; no tracker issue exists for it.

Summary

The final PR of a whole-repository code-tidying run. Waves 1-5 merged as #3635, 6-7 as #3700, 8 as #3702, 9-12 as #3706; this branch carries the rest and completes the sweep.

All 70 groups are done. Every sweepable code file in the marketplace was covered, in dependency-ordered groups, with three skills applied per group in order: /code-tidying:audit-comment-residue (APPLY), /code-tidying:dissolve-comments, /code-tidying:batch-simplify repo.

The method is the reason this is worth reading. One worker per group, then a fresh-context refutation verifier whose job was to fail to construct a behavior-difference counterexample before the group could commit. No human read these diffs, so the verifier was the only line of defence, and its evidence is quoted in each commit message.

That layer earned its cost. Verifiers corrected their workers on nearly every group, and in several cases the correction was the finding.

Excluded by design: markdown and prose, .claude/**, .github/**, fixtures, vendor and evals trees, JSON/YAML manifests and schemas, lint configs, generated files, and ten generated-then-owned adapter files whose canonical copy is ambiguous. House doctrine enforced throughout: bare (#N) comment citations are sanctioned and preserved, dense rationale comments are deliberately kept, no cross-plugin deduplication, no new GNU-only shell constructs.

Fix

Most of the diff is ordinary tidying: dead variables and fields removed, duplicated predicates extracted, hand-rolled loops replaced with the idiom the file already used, comment residue deleted or re-tensed. The findings below are the ones that are not tidyings.

A concurrency bug in the conformance runner. Every tracker binding mktemps its binding file into $TMPDIR, and the overlay test case derived its path from that file's directory, so the overlay resolved to a single fixed path shared by every run on the host. Two concurrent conformance runs clobbered each other. Measured on separate pre- and post-change trees: 19 of 20 jittered parallel pairs red before, 0 of 130 runs red after, with a deterministic reproduction by planting a poisoned overlay. The failure is whole-suite poisoning that also shifts the reported case count, so matching case counts was never the regression guard it looked like.

Scope, stated carefully because it is easy to overstate: this explains the jira.test.sh entry in scripts/run-plugin-tests-serial.txt, and the evidence is an asymmetry the mechanism predicts (under the CI shape jira loses the race 13 times in 25 while its partner loses 2). The other listed entry, tool-honesty.test.sh, is a markdown contract test with no reference to the tracker; this cannot explain it. #3694 stays open. The collision also cannot fire while jira.test.sh is serial-listed, so this is a precondition for delisting it, not a repair of a currently red lane.

A lease verb reporting a write that never happened. When the store rewrite could not run, mktemp failure left the temp path empty, the redirect failed, && short-circuited past the move, and the exit status came from a trailing jq. Result: exit 0, a renewed_at on stdout, and a store still holding the old timestamp. Now exits 1, the code the contract defines for this class.

Two test suites that were silently lying.

  • spawn-census.test.sh called assert_not_contains twice and never defined it. Both calls died as command not found, incremented nothing, and the suite still exited 0 with 28 passing lines against 30 call sites. The two dead assertions guarded exactly the false green that plugin exists to refuse. Proven by mutation, not argued: emitting the false-green shape left the old suite at exit 0 with zero failures.
  • typos-format.test.sh's spawn tracer had never worked. It delivered PS4 as an exported variable, but bash overwrites and re-exports PS4 at startup, so the tracer matched 0 of 802 trace lines and four of five assertions passed on an empty word list. Repaired via a BASH_ENV preload; 765 of 803 lines now marked. The stale jq expectation was corrected 2 to 1, confirmed with a counting shim rather than the repaired tracer, so a tracer bug could not substitute one wrong number for another.

A --help that dropped three of its four exit codes. fetch-annotations.sh sliced its header with a hardcoded sed -n '2,20p' against a 23-line header, printing Exit codes: and 0 success and then stopping. The same bug was then found surviving in a synced pair elsewhere, where the first fix was not transplantable (no blank line before the code), and was fixed separately on the canonical.

Test-integrity fixes. Two suites registered fixture directories from inside a command substitution, so the append never reached the cleanup trap and they leaked 27 and 6 directories per run. Two probes wrapped a jq count in 2>/dev/null || echo 0 where the expected value is 0, so a broken probe scored identically to a passing assertion. One suite compared two empty greps because it derived a path from the wrong variable.

Coverage findings, which became the run's largest non-tidy result. The repository's answer to "is this file covered?" is unreliable in ways that need different fixes, so they are not one finding:

  • Genuine zero coverage, caught loudly by the gate on a changed file, and silently on unchanged ones.
  • A mapping gap wearing a coverage gap's clothes: gate_common.py reads as zero-coverage but 190 of its 392 entries execute across two suites.
  • False coverage in six distinct shapes: basename collision; a suite naming a path only to assert what the mapper outputs; a mere mention in a comment (one such comment pulled 151 non-exercising suites into a single selection); a selector seeding patterns with the basename including the extension, so import foo is invisible; a bare file-exists check that would pass against an empty file; and a suite that mkdirs its own fake adapter directory and never runs the real file.

The sharpest instance: one adapter script selects 202 suites, of which exactly one exercises it. That number is measured, not reasoned — the method was to poison the file with an early exit 99 on a copied tree and count which suites notice.

And the sharpest consequence: e2e-probe.sh's 16 assertions have never executed, so redirecting its gh issue close to a different repository leaves every automated suite green.

Silent-skip findings. powershell-format.test.sh reports PASS=15 FAIL=0 while skipping 56 of its 71 assertions. check-silent-skips.sh cannot see it: line 159 excludes plugins/*/hooks/*.test.sh as fixtures and line 167 scans only scripts/*.test.sh. Separately, roughly 40% of typos-format.test.sh has never run in CI at all, because it gates on a real typos binary that lives in a different job with no shared PATH.

Verification

Per group, before commit: the repo's own scripts/affected-tests.sh --run with NOT-RUN ecosystems executed manually, shellcheck from the repo root, check-shell-portability.sh, editorconfig-checker, run-ruff.sh, and the package's own suites. Then a fresh-context verifier whose evidence is quoted in the commit.

Union verification over the whole diff: 232 shell suites pass, all 24 NOT-RUN lanes run manually and green (650 babysit-prs tests plus 8 others), and every static gate passes: ruff, shellcheck, shfmt -d, node --check, awk parse, editorconfig-checker, shell portability, em-dash purge, both sync-cluster checks, silent-skips, discriminating-skips, and all four changelog-parity modes.

The verifiers went well past reading diffs. Representative work:

  • Behaviour equivalence checked as bytes, not by reading: 18,142-probe and 13,475-invocation differentials, a 77-shape refusal corpus comparing exit code and stdout and stderr, 297 recorded request bodies compared byte-for-byte, and 32-case A/B runs of real verbs against stubs.
  • Corpora were required to prove they can fail. Seeded defects were killed at up to 316 divergences, and equivalence controls (mutations that must score zero) were mandatory, so a clean result reads as evidence rather than silence.
  • Counterfactuals against the pre-change tree established that new tests were load-bearing rather than decorative: six mutants that survived before and are killed after; four that the pre-change suite could not catch at all.
  • Where a suite could not discriminate a change, that was stated rather than hidden, and the claim was carried by direct byte comparison instead.

Corrections the verifiers made, which are the reason the layer exists:

  • A refactor created a failure mode no test could catch: routing two sites through one helper made a one-token argument transposition expressible for the first time, silently weakening a path-traversal guard on a URL interpolated into a gh api call. Killed by zero of 649 tests. A discriminating test was added.
  • Two false present-tense comment rewrites were caught, one of which would have told a future reader that a closed bug was still live. Two other groups correctly declined to re-tense for the same reason, each settling it by mutating the guard in question.
  • An attribution that would have wrongly closed a tracked bug was narrowed, after the verifier read the record and found it names a different second suite than the worker claimed.
  • A worker deleted an assertion it had written, believing it vacuous; the verifier instrumented every call, found 152 calls with 2 real hits, and showed the deletion was right for one site and wrong for the other two. The assertion was restored.
  • Two tidyings were reverted before shipping on a precedent the plugin's own changelog records: a prior change to the same file family was refused for shifting a line number into a stderr diagnostic on a reachable error path.
  • Numbers were corrected throughout rather than repeated: 26 call sites not 29, PostToolUse not PreToolUse, 27 leaked fixture directories not 16, four copies of a rule not two, eight suites not nine, 202 selected but 193 runnable. One worker claim was fabricated and refuted outright.

Two verifiers also caught their own instrument failures: mutation batteries that silently failed to apply, which would have produced false confirmations, detected by explicit applied-counters and re-run.

One regression survived all of that and was caught on review, which is worth recording plainly. A simplification in generate-adapter.sh replaced tr '[:lower:]' '[:upper:]' with ${PROVIDER_FUNC^^}. The case-folding expansions are bash 4.0+, that script has no version gate to keep one behind, and its shebang is /usr/bin/env bash, so on a stock macOS the generator would abort on every valid spec before writing an adapter. Reverted in 0.39.61; the line is byte-identical to what it replaced. The branch diff was then audited for the whole class rather than the one line reported (case-folding, declare -A, mapfile/readarray, &>>, the ${var@X} transforms, negative array indices, coproc, globstar, wait -n, read -N, printf '%()T'): three case-folding hits, two of them pre-existing ${p,,} in preflight.sh that read as additions only because an shfmt reindent moved their whole case block. One genuine regression, now fixed.

Related

Completes the series begun in #3635 and continued through #3700, #3702 and #3706, each of which merged mid-run and could not carry follow-up work. This branch was reconciled onto the current base after each merge.

Findings recorded in the relevant plugin changelogs under Known issues rather than fixed here, because each is a product or contract decision rather than a tidy:

  • A duplicate-frame deletion path is nondeterministic: its scoring function is not a total order, so ties resolve by directory iteration order and forcing both orders deletes opposite files.
  • The lease protocol's three writing verbs have no assertion on what they write in two adapters, because those mocks record no request body. A third adapter's mock does record it, and 33 assertions read it, so this is per-adapter rather than a family-wide fact.
  • The spawn-census instrument counts zero for a subject invoking an absolute path, resetting PATH, running under env -i, or forking without exec. Three of those emit a tidy spawns=0 rc=0 [], the confidently-wrong-number shape that script's own header exists to refuse.
  • Two rule patterns in ai-slop lack word boundaries and fire on unrelated words. Left unfixed deliberately: that detector is the instrument this sweep is measured with, and changing what it matches mid-run would make earlier and later groups incomparable.
  • Roughly 45 repository .py files are formatter-dirty at HEAD with no CI gate enforcing the formatter, so hook-driven reflow will keep riding into unrelated diffs.
  • scripts/check-shell-portability.sh reasons about GNU-vs-BSD userland (grep/sed/date/stat/mktemp/sort), not bash version, so ${var^^} and ${var,,} pass it. That is the blind spot the generate-adapter.sh regression above went green through. Widening the gate changes the gate's own contract rather than fixing a plugin, so it is filed rather than done here.

🤖 Generated with Claude Code

https://claude.ai/code/session_01LRWf55tWpxcSdpxEMh2eJD

…st count (G49)

Exactly one line of production code changes. `_rlg_spool_dispatch` declared its
two paths in a single `local` statement that repeated the parent path literally;
it now declares `dir` first and derives `spool` from it. The rest is comments and
test-side deduplication.

- statusline-tee.test.sh: four copies of a find-and-count pipeline become one
  `count_spool_records` helper.
- Four comments in statusline-tee.sh and one in bench.test.sh drop history
  narration for the present-tense mechanism, keeping every measurement.

Removed narration, preserved here: ", which is exactly what this file did
before"; "The unprobed fallback keeps a DIRECT call ... behaving exactly as it
did."; "it was spending that process 29 times out of 30"; "a direct run is
byte-for-byte what it was"; and "the old inline \"0.%03d\" printed 1000 ms as
\"0.1000\"".

Verified by an independent fresh-context refutation verifier, which built the
wrong version to prove the right one:
- The single `local` was safe ONLY because it repeated the literal path. The
  verifier constructed the tempting alternative, one `local` deriving spool from
  `$dir`, and ran a real render through it: bash does not expand a same-statement
  `local` assignment, so under this file's `set -u` it dies with "dir: unbound
  variable", empty stdout, exit 1. A total statusline outage. The split is the
  safe direction, and the old form was one refactor away from that failure.
- Hot-path cost is unchanged, measured with strace rather than argued: execve,
  clone and openat counts are identical across three modes and both cold and
  primed renders, and the full syscall multiset matches. The primed render spawns
  two processes and touches two files, before and after.
- 27 artifact comparisons (3 modes x 9 artifacts) are byte-identical, with every
  load-bearing artifact asserted non-empty so the comparison cannot pass
  vacuously. Pointed at the mutant, the same harness reported divergence on 8
  artifacts, proving it discriminates.
- The extracted test helper was mutation-tested three ways, including a plausible
  off-by-one; all four call sites still fail. The extraction did not weaken the
  suite.
- Each rewritten comment's new claim was executed, not read: the user-scope
  fallback really does yield DISABLED from the settings file, the 29-of-30 drain
  arithmetic re-derives from the configured interval, `printf "0.%03d" 1000`
  really does print 0.1000, and a sourced run really does suppress main.

One benign widening the verifier named that the worker did not: the extracted
helper adds `2>/dev/null` to a call site that previously let find's stderr
through on a missing directory. The count is 0 either way, the directory
provably exists at that point, and the suite runner gates on exit code rather
than stderr, so nothing observable changes.

Two simplifications were considered and correctly REJECTED, and the verifier
confirmed both, strengthening one:
- Replacing the read-loop in lib-bench.sh's `median` with mapfile would break
  empty input: a herestring appends a newline, so unfiltered sort yields one
  empty element, the guard never fires, and the report line fails its
  `median=[0-9]+` regex. The existing unit test feeds a PIPE, which both forms
  answer 0, so this regression would have shipped past the suite and surfaced
  only in bench-load.
- Collapsing `_rlg_absorb_jq_lines` to mapfile would break bash 3.2, which this
  file explicitly targets. The verifier forced the version gate and confirmed
  that path really does reach the function.

Pre-existing bug recorded, not fixed here: bench-idle.sh aborts with a division
by zero when given a zero argument, while its sibling bench-load.sh guards the
identical expression and returns 0. Reproduced against a pristine origin/main.

Two measurement cautions for anyone rechecking this: grepping strace output for
the plugin name yields a spurious delta because the script's own path contains
it, and affected-tests.sh prints its selection lines to stderr, so capturing with
2>/dev/null silently reports zero suites.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LRWf55tWpxcSdpxEMh2eJD
Covers G49.

check-changelog-parity.sh green in all four modes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LRWf55tWpxcSdpxEMh2eJD
…alse comments (G09)

- parse-briefing.js: `flattenInline` drops three branches the trailing
  `else if (n.children)` fallback already covered. `strong`, `emphasis` and `link`
  each did exactly `out += flattenInline(n.children)`, with no delimiters and no
  URL appended; `text` and `inlineCode` both did `out += n.value`.
- emit-slides.js: `bucketKey` loses an exact-equality return that its own
  `startsWith` on the next line subsumes, and two tier loops use
  `Object.entries`/`Object.values` instead of `Object.keys` plus index lookup.
- brand-overlay.js: a mutable schema object plus a follow-up mutation loop become
  one strict `z.object` built from two spreads, with the font keys hoisted beside
  the existing colour and logo lists.
- paths.js: `envDir` reads and trims the variable once instead of twice,
  deliberately keeping `||` rather than `??`.

Two comments deleted because the code contradicts them, preserved here: "HIGH
split into chunks of <=5 with topical title; MED/LOW single slide w/ 2-col when
>7" and "MED/LOW too dense even in 2-col -- split into multiple condensed
slides". Both assert a two-column layout above seven items. The verifier read the
sources independently: build-css.js gives `.news-list.compact`
`flex-direction: column` with its own note that MED stays single-column for
prominence over density, build-sections.js applies `compact` to every non-high
tier with no count test anywhere, and the only 7 in the file is `balanceTiers`'
demotion trigger. Two doc comments were corrected the same way: the real split
caps are `MAX_HIGH = 5` and `MAX_MED = 14`, and `parseBulletParagraph` returns a
`date` its signature omitted.

Verified by an independent fresh-context refutation verifier:
- `flattenInline` compared across 37 mdast node types plus deep nesting: zero
  divergences. The one difference found is unreachable and strictly safer --
  `children` set to a falsy non-iterable made the old code throw where the new
  returns "" -- and remark never emits that shape.
- The schema rebuild is identical in key set, KEY ORDER, per-key schema,
  strictness, and error message text across 29 theme and 6 overlay cases. The two
  spreads were confirmed disjoint, so later-wins cannot apply.
- `bucketKey` was swept over 162 curated headings and 32,768 brute-forced
  strings; 35 curated cases actually reach the removed line. Zero divergences.
- The `envDir` change was tested against a 13-case environment table WITH THE
  G08 BUG AS A NEGATIVE CONTROL: the `??` variant diverges on five inputs
  (empty, whitespace, tab/newline, single space, non-breaking space), while this
  rewrite diverges on none. The harness was proven able to catch the bug class
  before its clean result was accepted.
- End-to-end, three CLI runs including one through a real brand overlay produce
  byte-identical decks (md5 match), with non-vacuity asserted at 26 slides and
  69 bullets.

The verifier CORRECTED two of the worker's own claims, both reporting errors
rather than defects:
- The font-key list is NOT netted, contrary to the worker's table. Dropping three
  of the four keys leaves the suite green, because the only relevant test asserts
  rejection and a strict schema still rejects a key removed from the shape. The
  same holds for ten of eleven colour keys.
- `envDir` is partially netted: replacing it with a constant null does fail three
  tests, so the function is reached; only its blank-value semantics are uncovered.
Newly documented gaps: the schema's strictness, both split caps, and
`flattenInline`'s `break` arm are unnetted, and `emit-slides.js` has no test file
at all. Of 20 mutants, the shipped suite killed 5.

Findings recorded, all pre-existing and untouched:
- parse-briefing.js crashes with a TypeError on a briefing containing no `##`
  heading, because an unset bucket index dereferences `children[-1]`. Reproduced
  on both trees; also fires on an empty file, an H1-only file and prose-only.
- On url-policy.js the worker's reasoning holds, with a caveat the verifier
  added: only the RANGE clause of that guard is unreachable (0 hits across 40,000
  brute-forced literals). Its sibling length check fired 9,581 times and is
  load-bearing, so the block must not be read as deletable.
- `## Trends` is recognised then excluded, so its content is silently dropped
  either way. Confirmed end to end: zero occurrences in the emitted deck.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LRWf55tWpxcSdpxEMh2eJD
Covers G09.

check-changelog-parity.sh green in all four modes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LRWf55tWpxcSdpxEMh2eJD
… (G20)

`rank` and `unrank` in zone-crossing-inject.sh printed their result, so all three
call sites paid a command-substitution subshell. They now set `REPLY` and the
callers read it directly, removing three forks from a hook that fires once per
tool batch. `unrank` is hoisted out of an `elif` condition into `next_armed`.

Also: zone-gate.sh drops a `shopt -u nocasematch` that sat immediately before an
unconditional `exit 0`; zone-gate.test.sh reshapes an array so its expansion is
never empty; statusline-tee.sh renames an unused loop variable to `_` and drops
the now-inert pragma that suppressed a finding for it; and comment passes across
five files trade history narration for the present-tense mechanism.

Removed narration is preserved in the file history; the measured facts it carried
(the former six-process resolve budget, the per-shape zones.json reads, the
dirname and jq-per-field counts) are restated as present-tense budgets that the
trace assertions pin.

Verified by an independent fresh-context refutation verifier, which went after
the specific hazards this conversion introduces:
- REPLY is a SHARED name: bash's bare `read` and `select` both write it. The
  verifier grepped the file and its full source closure and found zero bare
  reads, zero `select` compounds, and no other REPLY access. It then traced
  execution order: every `read` in the closure runs 117 lines before the first
  `rank`, and all three call/consume pairs are adjacent statements with nothing
  between them. No two REPLY values are ever live at once.
- Command substitution strips trailing newlines while a variable does not, so
  byte identity was checked across 24 inputs including empty, whitespace-only,
  embedded newline and tab, and hostile values. All identical.
- The `elif` hoist DOES add paths where `unrank` now runs and previously did
  not. The verifier found them (mkdir failure, zone-write failure), confirmed the
  added cost is one in-shell `case` and zero processes, and proved the result is
  unread on those paths across 576 fail-path cases.
- Differential runs over 1,152 payload cases plus 144 telemetry cases compared
  stdout, stderr, exit code and both state files byte for byte: identical, with
  588 cases emitting non-empty output so the comparison is not vacuous. Repeated
  under a locally built bash 4.3 as well as 5.2.
- The fork saving was measured with strace, not asserted: 11 to 8 on the steady
  path and 24 to 21 on a crossing, with an identical exec census. Exactly three
  removed, none added.
- Mutation testing killed 11 of 11, including the two hazards specific to this
  refactor: inserting a bare `read` between a call and its consumption, and
  shadowing REPLY with a `local`. Both fail loudly.
- The bash-4.4 array claim was verified by BUILDING BASH 4.3.0 FROM SOURCE and
  reproducing the unbound-variable error in the test's exact shape.
- The deleted `shopt` was confirmed dead: nothing in the repo sources this
  script, and `exit 0` on the next line ends the process before the setting could
  be observed.

Findings recorded, none blocking:
- REPLY has no `local` discipline, so the three call and consume pairs must stay
  adjacent. Today that is netted by the suite; a future edit inserting a bare
  `read` between them would corrupt the value silently.
- The fork saving is invisible to this plugin's own trace budget test, which
  counts execs in command position and so could never have seen three subshell
  forks appear or disappear.
- The array reshape is prophylactic rather than a live fix: every current call
  site passes a non-empty argument, and the unmodified test is green on bash 4.3.

Two detector findings were deliberately kept as false positives, and the verifier
agreed independently: one is dated provenance about an upstream document that no
longer states a version floor, and the other describes runtime state within a
single execution, not code history.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LRWf55tWpxcSdpxEMh2eJD
`emit-findings.test.sh` had a case that routed a host skip through `pass()`,
contradicting the rule stated thirty lines above it in the same file: a skip
"never routes through pass(), so a proof this host could not run can never be
read off the summary as one that did". It now calls the file's own `skip()`.
The honest count on a root-uid host is 384 passes and 1 host skip, not 385
passes; 384 + 1 = 385, so no case vanished. The real assertion still runs
wherever `chmod a-w` actually bites.

Also in this group: `fingerprint.mjs` extracts `shingleAt` and `sharedCount` from
expressions duplicated across two functions each, and drops a `jaccard` guard the
surviving union check already covers; `extract-breadcrumbs.sh` drops a write-only
awk global; `extract-breadcrumbs.test.sh` drops a helper defined but never
called; `score-golden.sh` collapses a two-process jq pipeline into one; and
sixteen history-narration comments become present-tense hazard statements.

Removed narration is preserved in the file history. The hazards it carried
survive, including the one worth restating: two scripts sharing a rule means the
cross-script agreement their suites assert is blind to a defect they share, so
both suites pin the count itself and not just the agreement.

An independent fresh-context refutation verifier returned FAIL on one change,
which this commit fixes before landing. In `score-golden.sh`, replacing
`map(select(. == $id)) | length == 0` with `index($id) == null` looked equivalent
and is not: jq's `index` does SUBSTRING search on a string, where `map` iterates
and aborts. `cases_run` comes from a model-authored sidecar validated only as
"parses as JSON", so a string there is reachable. With `"cases_run": "c1-long"`,
the old form exits 5 with a type error while the new form exits 0 and scores case
`c1` as covered, because "c1" is a substring of "c1-long". That is a loud failure
turned silent, in a script whose header says it "refuses to guess". The verifier
supplied the fix, `any(. == $id) | not`, which reads as well and still aborts;
this commit carries it, confirmed against a table where it matches the old form
on every array case and reproduces the abort on a string.

The rest of the verifier's evidence:
- The other two jq changes are safe. The slurp rewrite keeps `split("\n")`, so it
  still yields an array; 17 input shapes agree, including empty input, missing
  trailing newline, embedded quotes, backslashes, CRLF and control characters.
  The retained `. as $c` binding is necessary: without it, `.` inside `index(.)`
  rebinds to the array being searched and no stray case is ever detected.
- The awk global removal is safe for a stronger reason than the comment gives:
  old and new read RSTART and RLENGTH at the identical program point, so any awk
  that clobbered them on `sub()` would break both equally. Confirmed byte-
  identical over 1,201 files and 2,606 URLs plus an adversarial corpus.
- Both extractions were byte-identical at every call site, checked over 400
  randomized trials plus boundary indices. The dropped `jaccard` guard returns
  literal 0 for two empty sets, not NaN; measured with Object.is, not argued.
- Comment-only claims proved by stripping comments and diffing: zero executable
  difference in the three files claimed. The detector goes from 16 findings to 0.

Coverage stated plainly: four of these changes landed with no test net (the awk
inline, the jq slurp, the jaccard guard, and the skip fix), and the verifier
confirmed each gap is pre-existing by reproducing it against HEAD. Their
substitutes are the differential runs above and a 385-case arithmetic check.
One worker claim was wrong and is corrected here: this diff carries 14 code-
bearing hunks across 6 files, not 5.

The whole-repo failure the worker could not name is resolved as foreign: two
suites fail, one pre-existing on main (typos-format) and one flaky only under
parallel jobs (work-items). All six provenance suites pass in the same run.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LRWf55tWpxcSdpxEMh2eJD
…ead awk guard (G26)

- glob-tools.sh: the per-pattern match count built a `mktemp`, appended to it,
  sorted it and removed it. It is now a direct pipeline into the same
  `LC_ALL=C sort -u`, removing a temp-file lifecycle per pattern.
- lib/discover.sh: an awk guard that could never fire is removed, along with the
  state variable that existed only to feed it. Line 1 either exits or sets the
  flag and moves on, so the guard's condition is unreachable from line 2 onward.
- detect.sh: two `trap ... EXIT` registrations where the second silently replaced
  the first are merged, two `BEGIN` blocks are combined, and `flush_section`
  collects its markers directly instead of building a comma string and splitting
  it back into an array to sort.
- render-index.sh: five changes, the largest being a row array that was sorted up
  to three times per render and is now sorted once.
- index-drift.sh: a two-branch `case` whose default arm was a bare no-op becomes
  an `if`, with the rationale kept as a lead-in comment.
- Four test files adopt helpers that already existed in this plugin.

Verified by an independent fresh-context refutation verifier, roughly 250
differential comparisons, zero divergences:
- The removed `sort -u` scaffolding was the highest risk, because per-pattern and
  global deduplication produce different output. The verifier established the old
  temp file was created INSIDE the per-pattern block, so the scope was already
  per-pattern and cannot have changed. Confirmed across 88 micro-cases (including
  overlapping expansions, duplicate matches, match order differing from sort
  order, spaces, tabs, unicode and a 500-file set) and 52 fixture-repo runs, each
  under four locales. Both versions pin `LC_ALL=C`. The old code also leaked its
  temp file on an abort because it was never in the trap; the new code creates no
  temp file there at all.
- The dead awk guard was instrumented in the ORIGINAL rule set and hit zero times
  across 20 input shapes: empty file, first line not the opener, opener with no
  closer, CRLF, CR-only, no trailing newline, BOM, and more. Output and exit
  status identical across all 20 and across all 1,389 tracked markdown files.
- `flush_section` emits byte-identical markers across 15 fixtures. The new
  `norm_hits > 0` condition adds nothing and skips nothing, because the old build
  loop's body could not run when no marker was seen. The removal also fixes a
  latent bug: the old split-on-comma round trip would have shredded any marker
  containing a comma, which the marker vocabulary happens never to contain.
  Whole-repository proof: 25,689 lines, 2,203,663 bytes, byte-identical.
- For `render-index.sh`, 80 render comparisons byte-identical, plus this repo's
  own generated index unchanged and still reporting IN-SYNC. The `grep -qF` to
  count substitution was checked at the case that diverges in principle, two
  markers on ONE line: a pre-existing guard exits before the counts can differ.
  The sort hoist was proved by instrumenting each of the three consumers to
  recompute the old sort at its own point and compare; zero mismatches.
- The hot-path hook was traced: identical external-command counts, one builtin
  removed, 11 ms per run before and after.
- Test helpers were mutation-tested. One survivor was proved pre-existing by
  applying the same mutation to the unmodified call sites, where it also
  survives: `git ls-files` reads the index, so the `git add` is load-bearing and
  the commit never was.

The worker corrected an error in its own dispatch brief, and the verifier
confirmed it: all seven scripts in this plugin have paired suites, not just one.
That mattered, because my brief would have wrongly downgraded six files to
propose-only for lack of a test net.

Two pre-existing findings recorded, both reproduced by the verifier and untouched
by this diff:
- detect.sh runs two awk passes that DISAGREE on what a heading is, one capping
  at six hashes and one accepting any number. A file whose line 9 opens with
  seven hashes emits a hint keyed to line 9 while no section row declares a
  section there, so a consumer joining the two on the start line silently drops
  it. A second instance: a language mention before the first heading is keyed to
  section 0, which likewise never exists.
- Two array expansions run unguarded under `set -u` on reachable paths where the
  array is legitimately empty, which errors on bash below 4.4. Not reproducible
  on this host's bash 5.2, and the verifier confirmed no compat level restores
  the old behavior.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LRWf55tWpxcSdpxEMh2eJD
…enance 0.5.3

Covers the context-guard fork reduction (G20), the instruction-placement
temp-file and dead-guard removals (G26), and the provenance skip-as-pass fix
plus its jq membership correction (G48).

check-changelog-parity.sh green in all four modes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LRWf55tWpxcSdpxEMh2eJD
Simplifications, all proven behavior-identical by differential execution
against the pre-edit copies:

- export-sheet-frame-index.js: dropped the local 16-element CELLS array in
  favour of the CELL_IDS registry already imported elsewhere in the tree, and
  replaced the bare inputFiles[8] literal with a named MID_CELL_INDEX. The two
  cell lists were compared element by element and are identical at every index;
  the produced sheet-frame-index.json is byte-identical across cell counts of
  2, 9, 16 and 20.
- expand-visual-gaps.js: rewrote the accumulate-into-array loop as filter/map.
  regionMin carried no cross-iteration state despite the name, so the rewrite
  is a straight transliteration. Verified over 38 curated cases (both window
  boundaries, inverted and zero-width windows, duplicates, out-of-order input,
  NaN and Infinity, fractional and negative seconds) plus 20,000 fuzz
  iterations: zero mismatches.
- repair-synthesis-promotions.js: extracted a promotedDecisions helper, shed
  two unused parameters from applyFileRenames, and hoisted the decisions write
  to the caller. The write remains the first statement executed inside the
  !dryRun branch, so it still precedes every rename. Confirmed by an
  instrumented op-trace over six sliceDir spellings and by crash injection at
  eight boundaries: the resulting trees are byte-identical in all eight.
- rebuild-visual-frames.js: dropped a row field nothing reads.
- list-promotion-candidates.js: named the per-session candidate floor.
- Eighteen redundant trailing newlines removed across nine files. writeStdout
  and writeStderr append a newline unconditionally, so each one was emitting a
  blank line. Measured per CLI: every delta is exactly -1 per emission
  executed, exit codes unchanged, text identical once blank lines collapse.

Adds expand-visual-gaps.test.js. The file previously mapped to zero test
suites, which scripts/affected-tests.sh reports as an error rather than an
empty selection; the no-suite allowlist is for prose and manifests and
explicitly not for code. The new suite covers gap detection, boundary
inclusivity, window order, the minute-rounded region label and the empty-window
case. Mutation-tested: inverting the gap filter kills 4 of 5 cases, rounding to
a floor kills 1, and making either window boundary exclusive kills 3 and 1
respectively.

Verification: 72 test files / 514 tests pass; tsc --noEmit clean;
affected-tests.sh --explain exits 0 over the whole group with no UNMAPPED file.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LRWf55tWpxcSdpxEMh2eJD
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LRWf55tWpxcSdpxEMh2eJD
worktree-add-claim-gate.sh (a live PostToolUse hook):

- Removed the `notes` array. It was declared once and appended to twice, and
  nothing read it: the hook's only agent-visible output, `ctx`, is built from
  the `claimed_any` and `foreign_any` flags alone and names no target. With
  `notes` gone, `claim_err` and its `cat` fork were dead too. One fork fewer
  per claimed target.
- Rewrote `if [[ ! -f "$CLAIM" ]]; then exit 0; fi` as `[[ -f "$CLAIM" ]] ||
  exit 0`, matching the four guards directly above it. The file runs
  `set -uo pipefail` without `-e`, and the statement is top-level rather than
  the last of a function, so no exit status changes.

Test suites: `pr-body-linkage-gate.test.sh` extracts `mk_payload`, replacing
four spellings of the same two-line `jq -n` construction.
`pr-linkage-mcp-gate.test.sh` drops a `dir` parameter that `run()` bound and
never read, at all 26 call sites; every payload already carries its own `cwd`.
`worktree-add-claim-gate.test.sh` extracts `wt_stanza` for a `worktree list
--porcelain | awk -v RS=` pipeline written out 11 times, and
`worktree-create-gate.test.sh` extracts `native_path` for a duplicated
`cygpath -m` block. One `shfmt` conformance fix in the mcp-gate suite.

Verification. A 20-case accept-and-refuse corpus drove pre-edit and post-edit
mirrors of the gate over the accept and no-op set, the claim path, both
`claim_rc == 4` branches, the combined claimed-and-foreign context line, the
kill switch, the helper-missing branch the `[[ -f ]]` rewrite sits on, an empty
session id, helper exits 5 and 2, and a `mktemp` failure. All four transcripts
(two runs each side) hash identically at 443 lines, so the corpus is
deterministic as well as equal.

The corpus was then shown to discriminate, against four mutants of the edited
gate: inverting the `[[ -f ]]` guard differs on 30 lines, deleting the stderr
redirect on 5, dropping the `mktemp` failure `continue` on 1, and clearing
`foreign_any` on 4. The stderr mutant is what proves the removed `cat` did not
change where the helper's stderr lands.

The 26 call-site edits were checked by recording every `run()` invocation in
both mirrors: 26 calls each side, arity 4 to arity 3, and the surviving
argument triple byte-identical at every site. Helper extractions were checked
with a `jq` argv-logging shim (2052 and 44 invocations, all argv identical) and
mutation-tested; `native_path`'s Windows branch was executed through a `cygpath`
stub. The one surviving helper mutant was chased down and shown equivalent, not
a coverage loss, by applying the same mutation to the pre-edit suite.

All five affected suites green and unchanged: 146/0, 28/0, 24 cases, 37 cases,
plus check-hook-wiring-liveness. shellcheck, shfmt, portability and em-dash
gates clean; `affected-tests.sh --run` exit 0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LRWf55tWpxcSdpxEMh2eJD
#3702 squash-merged, so the branch's earlier commits are already in main.
Merging brings the branch onto the current base (the ci.yml collapse, the
docs-only-gate rework and the check-rename-sweep removal) and clears the
changelog-parity version collision that stacking on merged history produced.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LRWf55tWpxcSdpxEMh2eJD
- actionlint-check.test.sh: three identical six-line fake-bin builder loops
  become one `wrap_real_tools <dir> [extra-tool ...]`. Byte-identity was proven
  at all three call sites, contents and file modes, including the site that
  passes `rm` as an extra tool.
- ai-slop/detect.sh: dropped a redundant intermediate array copy. The old code
  built `TARGETS` from `EXPANDED` and then re-read it; the new code sources one
  `mapfile` directly from `EXPANDED`. The deleted line was a faithful copy
  rather than a word-splitting step: the inner quotes in
  `${EXPANDED[@]+"${EXPANDED[@]}"}` survive the unquoted outer expansion, which
  was confirmed by probe against elements containing spaces and glob
  metacharacters.
- ai-slop/emit-findings.sh: deleted a history-narration tail. The two retained
  sentences state the whole contract, preference order and fail-open, and the
  deleted sentence carried only the fact that today's behavior was once a bug's.
- ai-slop/detect.test.sh: T1 residue rewritten, and a stale count fixed. "all
  14" had already drifted; the roster is 15, a number that survives three lines
  above inside a test-enforced assertion string, so the wrong literal is
  replaced by a phrase the loop below it makes exact.
- bash-format.test.sh: the second `REPO_IGN` fixture becomes `REPO_TRANSIENT`.
  One identifier was carrying two unrelated repositories; pointing the renamed
  site back at the original turns the suite red, which is what proves they were
  distinct rather than deliberately shared.
- context-budget/measure.mjs: a ReDoS comment moves to present tense, a
  `flagOnly` local is hoisted to a module-level `FLAG_ONLY`, and a `return null`
  the code itself declared unreachable is deleted.
- context-budget/levers.test.sh: `report_clean` extracted; measure.test.sh gets
  one shfmt conformance fix.

Verification. Both sides of the change were run, not just the new one. All five
affected suites match at HEAD and here, and the full assertion-name output of
each was diffed line for line, not just the counts: actionlint 45/0 (47 lines),
ai-slop 202 cases (204), bash-format 51/0 (54), levers 3/0 (5), measure 61/0
(63), every one identical.

The `mapfile` removal was compared across 11 input shapes (empty, single,
multi, spaces, globs, duplicates, tabs, backslashes, leading dash, empty-string
element, and a combination) and 11 end-to-end invocations against fixtures
chosen to actually fire rules, on bash 5.2 and again on bash 4.3.

`degrade()` was proven non-returning by execution rather than by reading: a
tripwire inserted immediately after the call never fired, and the counterfactual
that neuters `process.exit` shows the deleted line's only observable effect
lives on a path `degrade()` never takes. The `FLAG_ONLY` hoist was checked for
evaluation-timing equivalence across 13 argv shapes.

Helpers were mutation-tested: dropping a wrapped tool and wrapping into the
wrong directory both turn the suite red, as do inverting `report_clean`'s
comparison and breaking three levers. One mutation stayed green, which is
recorded rather than hidden: nothing asserts on `wrap_real_tools`' extra-tool
argument. That gap is inherited, not introduced, and is closed here by direct
byte comparison instead.

Two pattern-boundary defects in ai-slop's own rules were found and deliberately
NOT fixed, because the detector is the instrument this sweep is measured with
and changing what it matches mid-run makes earlier and later groups
incomparable. Recording them so they are not lost:

- `challenges (remain|ahead|persist)` has no word boundary on either side. It
  fires on "challenges remained", "challenges remainder", "challenges
  remaining", "challenges persisted", "challenges persistence", "challenges
  aheadroom", and on "subchallenges remain". It does NOT fire on
  "challenges-adjacent", which the pattern cannot match.
- `not (just|only|simply|merely) [^.]{0,80}but` fires on any following word
  beginning "but": "button", "buttress", "butterfly", "rebuttal".

affected-tests.sh --run exit 0 over 21 suites; shellcheck, shfmt, node --check,
portability, silent-skip and discriminating-skip gates all clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LRWf55tWpxcSdpxEMh2eJD
…dget 0.6.20

Covers the G65 tidyings. The ai-slop entry also records, under Known issues,
the two rule patterns whose missing word boundaries make them fire on unrelated
words. Those are left unfixed on purpose: the detector is the instrument this
sweep is measured with, and changing what it matches mid-run would make earlier
and later groups incomparable.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LRWf55tWpxcSdpxEMh2eJD
- dead-code-scan.sh: removed the `SCOPE` array, genuine dead code inside the
  dead-code scanner. It was declared once and appended to once, with zero reads
  anywhere in the repository. That claim needed care because this file drives
  five namerefs, so "write-only" is exactly what a dynamic binding could
  falsify; every `local -n` was enumerated and every call site passes a string
  literal (`roots`, `root_nested`, `TS_FILES`, `GO_FILES`, `mod_files`). The
  script is never sourced and `SCOPE` was never exported, so no child could
  read it either.
- dead-code-scan.sh: declared the four `read` loop variables `local` in
  `lane_vulture`, `lane_gopls` and `lane_knip`. Only `lane_grep` already did
  this; leaving one lane out would have made three of four consistent and one
  not. None of the twelve names is read outside its own lane body, and no lane
  recurses or runs in a subshell that relied on the leak.
- open-pr-count.sh: dropped the `-z "$count" ||` arm. An empty string cannot
  match `^[0-9]+$`, so the empty case already routed to `emit_unknown`; the
  only side effect the short-circuit suppressed was `BASH_REMATCH`, which
  nothing reads before exit.
- changed-code-files.test.sh: added `assert_equal` and moved two line-count
  comparisons onto it. Both were calling `assert_exit`, so a real failure
  printed "expected: exit 1 / actual: exit 3" for a count. Same predicate,
  accurate diagnostic.
- detect.test.sh: replaced an unresolvable version back-reference. The note
  read "stays as 0.13.3 wrote it"; the constraint it guards is unchanged and
  the sanctioned `(#3126)` citation two lines above already anchors the work.

Verification. A 76-invocation differential over pre-edit and post-edit mirrors,
covering every lane targeted and whole-repo, every usage-error path, the
degraded, drift, empty-output, parse-error and CRLF cases per lane,
foreign-nested-module dropping, non-git and subdir working directories, and
three runs against the real repository: zero divergences in stdout, stderr and
exit code.

That corpus was then proven able to fail, against nine mutations. The decisive
one re-introduced a real read of `${#SCOPE[@]}` into the pre-edit mirror and
diverged on 54 of 76 cases, which is what establishes the array was write-only
rather than merely untested. Swapping `read` field order in the vulture and
gopls lanes diverged on 23 and 7; making the newly-local variables readonly
diverged on 30, confirming the corpus reaches the changed lines.

The `-z` removal was checked over 41 self-built invocations covering empty
output, a lone newline, CRLF, a lone CR, space, tab, whitespace-only, leading
zeros, negatives, floats, JSON, three nonzero exits and gh absent from PATH.
Mutating the guard diverged on 30 of them.

Both migrated assertions were mutation-tested independently and go red with
the corrected message. The plugin's own suites are unchanged on both sides:
162, 53, 11, 9 and 12 checks. affected-tests.sh --run exit 0 over 20 suites,
with each file also explained individually and none unmapped.

The audit-comment-residue detector and its shape library are deliberately
untouched and were confirmed hash-identical to HEAD. They are the instrument
this sweep is measured with; changing what they match mid-run would make
earlier and later groups incomparable. One defect in them is recorded rather
than fixed: `see[[:space:]](pr|mr|issue)` has no word boundary on either side,
so it fires on "see PROJECT", "see MRI" and "see PRESENT", and the same missing
anchor makes `pr[[:space:]]#?[0-9]` fire inside "expr 3", `issue` inside
"reissue 4" and "tissue-2", and `linear` inside "linear-1". This is the third
sighting of that defect class in the run, across two independently owned
detector libraries.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LRWf55tWpxcSdpxEMh2eJD
- audit-encapsulation/detect.sh: extracted `resolves_into_self()` from two
  branches that had spelled the same relative-cite resolution twice. The two
  bodies differ by no code token at all once the one prefix is substituted;
  only their comments differed. The absolute-cite tests were deliberately NOT
  pulled into the helper: the `.claude/skills` branch uses a plain substring
  match and the `plugins/*/skills` branch a root-anchored one, so they answer
  differently for a string like `xR/skills/S/`, and that difference is kept.
  Also dropped two `[[ -s ]]` guards whose files are re-created by a redirect
  on the line immediately above each loop, a dead `rel` alias, and hoisted the
  self-prefix to a quoted variable at both sites.
- audit-noise: extracted `no_targets()` for a two-site status contract and
  `count_negations()` for a pipeline written out eight times; simplified
  `resolve_existing_path`. emit-findings.sh is comment-only: an escaping note
  moved to sit above the function it describes.
- compress: dropped a `${path_hits:-0}` default that could never apply, since
  `wc -l` prints a count on every path including a failed grep; moved the
  caveman suite's stub directory into `mktemp -d` so a run leaves nothing in
  the plugin tree.

Fixed a real bug in two suites. Both registered fixture directories in a bash
array, but the constructor runs inside a command substitution, so the append
happened in a subshell and never reached the trap. The ledger cleaned up
nothing. Measured with an isolated TMPDIR: audit-encapsulation leaked 27
directories per run and audit-progressive-disclosure 6. Both now leak zero,
and a forced-red run leaks zero where HEAD leaked 27.

The ledger is a file, and its records are NUL-delimited rather than
newline-delimited. With newline-delimited records a newline inside TMPDIR
splits one path across two records and the trap removes the truncated prefix,
which is a directory outside the fixture set. Probed directly: the prefix
directory survives now and was deleted before the change.

Verification. Both detector revisions were run over the same pristine tree
(3,523 files, 1,300 markdown) rather than over their own working copies, which
removes the self-scan confound: output is byte-identical across ten
invocations, including 887 raw hits, 717 filtered rows, 38,067 lines of
audit-noise output and 845KB of emit-findings output.

The extracted predicate was compared against the two original inline bodies
over 20 cite texts by 7 source-line prefixes by 2 roots plus missing-line and
missing-file probes: 284 checks, zero mismatches, with 5 of 6 injected mutants
killed and the survivor shown to be a genuinely equivalent mutant. The seven
`write_block` call sites were reproduced by executing each original block and
comparing with `cmp`: all seven byte-identical, including the one where a
`\|` moved from a printf format string to a `%s` argument. emit-findings.sh
was checked by numbering its non-comment lines and hashing them, which proves
no awk source line moved relative to another.

Fixture isolation is pinned rather than assumed: a mutation that makes two
cases share one fixture directory is killed by the suite at both revisions.

All nine suites unchanged on both sides: 11, 77, 201, 35, 38, 6, 14, 17 and
the pairing suite. affected-tests.sh --run exit 0 over 19 suites, with every
file also explained individually and none unmapped.

Three defects found in the detectors and left unfixed, since these are the
instruments this sweep is measured with. `from the feature branch` in
noise-shapes.sh terminates on a bare word where every sibling alternative ends
on a digit class, so it fires on "feature branching", "feature branches" and
"feature branchless": four findings where one is intended. Five
`for (k in declined_*)` loops iterate awk associative arrays, whose order
POSIX leaves unspecified, over a parsed contract surface; only one awk exists
on this machine so no order flip could be exhibited, and it is recorded as
latent rather than observed. And one awk program uses `\x27` a few lines after
a comment stating that `\x` escapes are not portable across awks.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LRWf55tWpxcSdpxEMh2eJD
The `lint` job's editorconfig check failed on this file, the only failure in a
54-check roster. `.editorconfig` sets `indent_style = space` for every file
type, and the JS section overrides only `indent_size`; the checker config
disables IndentSize and MaxLineLength but not indent style.

The file was tab-indented because I formatted it by running biome from the
repository root. The only biome.json in the tree is under plugins/miro, so a
root invocation falls back to biome's own defaults, and biome defaults to tab
indentation. Every sibling suite in this directory is space-indented.

Verified with editorconfig-checker 3.4.0 against the repo's own
.editorconfig-checker.json: exit 0 on this file, and exit 0 across all 62 files
this branch changes. The suite still passes 5 of 5 in isolation and tsc
--noEmit is clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LRWf55tWpxcSdpxEMh2eJD
Twenty-two files reviewed in full; two changed. No gate script was modified,
confirmed by hashing all 52 tracked files under plugins/guardrails against
HEAD: exactly two differ, and both are suites.

- block-noncanonical-commit.test.sh: replaced a three-line inline copy of the
  shared `report` helper with a call to it. This was the only one of the
  plugin's 17 hook suites not using the helper it already sources.
- require-jq-notice-isolation.test.sh: removed a `fired_count=0`
  pre-initialisation that is unconditionally overwritten eleven lines later by
  a `grep -c` capture with no read in between.

Verification. The terminal check changed form from `((FAIL == 0))` to
`[[ $FAIL -eq 0 ]]`, so the two were compared by execution across 14 inputs
under the file's real `set -uo pipefail`: 0, 1, 2, unset, empty, a non-numeric
string, `08`, `007`, both integer extremes, `0x10`, a spaced value, `1+1` and
`-1`. Exit codes are identical in every case. The leading-zero input is not a
divergence: both forms emit the same base error and both return 1.

The suite's ability to fail was then proven rather than assumed, under two
independent mutation classes. Injecting a failing assertion turns both
revisions red with the same counts. Weakening the gate itself, so it exits 0
where it should exit 2, flips eight corpus verdicts and turns both revisions
red. The helper reads the same `PASS` and `FAIL` counters the suite increments,
so the permanently-green failure mode does not apply; the live run reports 213
and 0, matching the previous wording exactly.

A 48-case accept-and-refuse corpus over pre-edit and post-edit mirrors, driving
the real dispatch across three hook lanes, diffs to zero: 31 accepts and 17
refusals, covering every distinct refusal branch, eight near-miss strings that
must stay green, empty and malformed JSON, four kill-switch paths, and the
dependency-missing case. Two gate weakenings confirm the corpus discriminates.

Both suites produce byte-identical output on either side apart from the one
summary line, across all 214 preceding assertion lines. All 17 guardrails hook
suites pass. `grep` over scripts/ and .github/ confirms nothing parses the
summary text, and the new wording matches the repo-wide harness format.

Zero comments were deleted anywhere in the group; the diff removes one code
token and three echo lines.

The remaining twenty files were left alone deliberately. Every apparent
redundancy in them is documented in-file as intentional: fail-closed belts,
sibling guards held divergent on purpose, and generalizations with stated
intent. Three near-identical `effective_dir` implementations in particular
carry docblocks saying the duplication exists so the sibling guards answer
alike, and naming where one copy must NOT reproduce another's behavior.

One residual risk is recorded rather than hidden. This change removes the
plugin's last suite-local terminal check, so a broken shared helper combined
with a weakened gate would now leave 0 of 17 suites red where 1 previously
stayed red. That protection was accidental rather than designed, it was already
absent for the other 16 suites, and both single-fault mutation classes above
still fail loudly. The mitigation is a self-test of the helper, which belongs
in a file outside this group's scope and is routed to the group that owns it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LRWf55tWpxcSdpxEMh2eJD
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LRWf55tWpxcSdpxEMh2eJD
Twenty-two files reviewed in full; two changed, both one-line import cleanups.

- overlap.py: `Path(os.getcwd())` became `Path.cwd()`, and the now-unused
  `import os` was dropped.
- test_install_state.py: dropped a `from contextlib import redirect_stdout` and
  qualified its single call site, matching the same-plugin sibling suite. The
  module import it needs was already present, ten lines above the call.

Verification. The dropped import is the risky half, so it was proven rather
than grepped: an AST scan over the pre-edit file covering Name and Attribute
nodes, every import form including aliases, Global/Nonlocal, `del`, and every
string constant found exactly two references, the import and the one call.
There is no `__import__`, `importlib`, `eval`, `exec`, `globals()`,
`sys.modules`, `getattr` or `__all__` anywhere in the file, so no dynamic path
could reach the name, and no module imports anything from this one but its own
suite. The repo's pinned ruff selects F, so F401 and F821 are both live; probes
confirmed each fires, which means the removed import and the removed use
balance exactly.

`Path.cwd()` is not merely equivalent here, it is the same expression: CPython
3.11's pathlib defines `cwd` as `cls(os.getcwd())`, and the file pins a 3.11
minimum. Seven probes agree on value, equality, type, string round-trip and
parts, including a symlinked working directory, a 1190-character path, a
directory deleted out from under the process (identical FileNotFoundError), and
paths with spaces, newlines and unicode.

A 496-pair differential compared both revisions across four repository shapes,
seven working directories (including two symlinked ones and one outside the
repo), all three subcommands, every flag, both write paths and the whole
argparse surface, comparing exit code, stdout, stderr and a hash of the
resulting file tree: zero divergences. Eleven mutants establish the corpus
bites, killing `.parent`, `Path(".")`, `Path.home()`, a trailing-separator
variant, a PWD-based spelling that is symlink-sensitive, and a relative-path
spelling, at up to 316 divergences each. Two mutants survived and both are
meant to: a `.rstrip('/')` that `.resolve()` absorbs, and the control that
re-injects the original expression, which is the equivalence claim confirmed
from the other direction.

The changed test line was proven to execute rather than assumed: a line-level
trace ties it to three named tests, with eight executions across the suite.

All six Python suites are unchanged at 10, 50, 77, 45, 96 and 31, and their
full verbose test-name output is identical on both sides.

Two things this group deliberately did not touch. `lib/spawn_noise.py` is a
registered sync-cluster canonical whose carried copy in the performance plugin
must stay byte-identical, so any edit at all, including a comment, would need a
fleet sync and a second plugin's version bump; `sync-spawn-noise.sh --check`
and `--check-bump` both pass, and the canonical and its copy hash equal. And
`known-issues/scripts/registry_manager.py` maps to zero test suites, which
`affected-tests.sh` reports as an error rather than an empty selection, so the
worker stopped instead of changing it. That file is byte-identical to the
previous revision and is correctly absent from the no-suite allowlist, which
covers prose and manifests rather than code.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LRWf55tWpxcSdpxEMh2eJD
- restart-consumer.sh: four within-file extractions. `telemetry_fully_fixtured`
  replaces one predicate spelled two different ways in `require_gh` and
  `resolve_target_repo`, which must never disagree about whether a run touches
  the forge. `remove_lock_dir` replaces an `rm -f` of three lock files plus an
  `rmdir`, duplicated in `release_lock` and the `acquire_lock` reclaim path.
  `lock_uint_file` backs `lock_stamp` and `lock_owner_pid`, which were identical
  but for the filename. `print_report_header` replaces three `info` lines
  duplicated between the lock-skipped tick and a full run.
- telemetry-upsert.sh: `require_value "$@"` replaces five copy-pasted argc
  guards. Deliberately argc-based rather than emptiness-based, so `--marker ""`
  still fails on the marker regex rather than on "requires a value".
- machine-behavior.sh: renamed a top-level loop variable whose old name implied
  a `local` it could not have.
- lane-launcher.sh: comment only, proven by diffing the files with comment lines
  stripped. A migration narration becomes present tense, which is the more
  accurate tense: the pre-move layout is not history but a live path
  `resolve_config` still reads under a deprecation warning, and the sentence
  above it still records which layout superseded which.

Verification. A 73-case differential over pre-edit and post-edit copies of
restart-consumer, comparing stdout, stderr, exit code, the lock file tree, the
ledger tree and contents, and the launcher argv log: zero divergences. Coverage
includes every refusal branch, six breaker states, fifteen lock paths including
stale reclaim, a reused pid across boots and the hard ceiling, five unusable
store shapes, and both call sites of the merged predicate. Seven mutants
confirm it discriminates, at up to 47 divergences.

The merged predicate was checked against both original spellings over a 47-row
truth table: they agree on every row, and across the whole reachable domain all
three forms agree. Four rows diverge only for values of two flags that the
script itself can never produce, since both are initialised to 0 and set only to
1 by argument parsing, with no eval, nameref or environment read anywhere.

The two lock-directory removal sites were shown identical modulo indentation,
and a three-way harness over 14 filesystem states plus four unprivileged
permission cases found no difference in caller-visible status or resulting tree.
`lock_uint_file` was compared against both originals over 50 inputs including
missing, empty, whitespace, CRLF, leading-zero, negative, float, huge,
directory, dangling-symlink and unreadable cases: zero divergences.

The argc guard was verified at all five sites over 39 whole-script invocations
with exit codes and messages preserved exactly, including the case that
motivated the design: `--marker ""` still reaches the marker regex. A mutant
that switches to an emptiness check diverges there, which is what proves the
distinction is load-bearing.

Suites are unchanged on both sides of the change: 213, 26, 24, 153 and 91.

Three of the worker's own claims were corrected before this message was written
rather than repeated. It reported that the two merged lock readers had already
drifted, one initialising its fallback to an empty string and the other to zero;
both in fact initialise to an empty string, and the functions were identical
modulo variable and file name. It reported six argc guards; there are five. And
it counted a sixth suite that `affected-tests.sh` does not select for these
files.

One residual difference is disclosed rather than normalised away. With line
numbers left unmasked, four of the 73 cases differ only in the line number bash
prints in its own redirection diagnostics, two of them inside functions this
change never touches. The message text, the path named, the value returned and
the exit code are identical, and the suite case asserting that such diagnostics
never leak into the report passes on both sides.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LRWf55tWpxcSdpxEMh2eJD
Fourteen files reviewed in full; one changed. The low-fd case ran
`batch_read_lines_into` inside a `bash -c` subshell that declares its own
`LINES` and asserts on the subshell's stdout, so the outer `LINES=()` was never
read. It mimicked the sibling cases that do assert on the outer array, which
made it look load-bearing. A comment now records why this case is the exception.

Verification. Every reference to the array in the file was enumerated in line
order: eight callers each reset immediately before their own call, twelve reads
all sit before the deleted line, and the three references after it are inside a
single-quoted `bash -c` string, so they belong to a child process. `bash -c` is
a separate process and the name is exported nowhere, confirmed by probe. The
reset is load-bearing for the other callers because the read function appends
rather than assigns, which is why only this one case could lose it.

The cross-case leakage risk was tested adversarially rather than argued: with a
stale three-element array injected immediately before the case, so that the
edited file reaches it populated where the original would have wiped it, output
is identical on both sides. The skipped case was also forced down its non-skip
branch, and both revisions behave identically there too.

Five mutants of the library under test, including a restoration of the original
file-descriptor defect this case exists to guard, fail identically at both
revisions with the same assertion sets. No mutant survives here while dying
before the change, so nothing the suite could previously catch has been lost.

The suite is unchanged at 28 passed, 1 skipped, 0 failed, with byte-identical
assertion output. The skip is a permission case that cannot be enforced when the
runner is root. All thirteen other files in the group hash-identical to the
previous revision, including every destructive and selection script.

One deferral is worth recording because it was proven rather than asserted.
Merging `manifest_child_token` and `tier_repo_token` in `clean-batch.sh` looks
like an obvious two-function dedup, and the in-file comment saying they must
stay distinct is correct: with them merged, a plan built for the build tier is
authorized under `--tier git` and applies, printing `Tier: git` while removing
build output that tier never gated. Reproduced end to end. The repository's own
suite does not catch it, because no case feeds a build record to `--tier git`,
which is the only input where the two functions differ. That is a pre-existing
coverage gap, recorded here and left for a group that owns the file.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LRWf55tWpxcSdpxEMh2eJD
- dedupe-synthesis-dir.js: unexported an internal-only `hashFile`. Two
  references exist repo-wide, both inside the file. The module has no barrel, no
  namespace importer, no dynamic import, no string-key access, and its package
  is private with neither a `main` nor an `exports` field, so nothing outside
  could reach it. Confirmed by loading the module both ways: the namespace goes
  from two keys to one, and both real importers take only `dedupeSynthesisDir`.
- merge-triage-json.js: `?? 16` became `?? CELL_IDS.length`, with `CELL_IDS`
  added to the import specifier that module was already using. This is tighter
  than the literal, since the validator it feeds slices the registry by that
  number, so the fallback now means "the whole registry" rather than "a number
  that happens to equal it".
- Eight redundant trailing newlines across four files. The shared emit helpers
  append a newline unconditionally, so each explicit one was emitting a blank
  line. Two of the files were internally inconsistent: the other call in the
  same block already omitted it.

Removing those newlines left four call sites wrapping a single expression in a
template literal that no longer did anything, so they were reduced to a bare
argument, matching the two established precedents in this directory. That
reduction is not cosmetic and was verified rather than assumed: the emit helper
is not a plain string conversion, it formats an Error as its stack and an object
as JSON, so a bare argument and a template differ for anything but a string. All
four wrapped expressions are strings, proven by execution, with every throw
source of the one ternary enumerated: hand-thrown Error, JSON syntax error and
filesystem ENOENT all yield a string message. The one template still doing work
was left alone.

Verification. A 17-scenario differential over pre-edit and post-edit copies of
the file that deletes files, comparing kept lists, removed lists and on-disk
survivors: zero divergences. The corpus carries the near-miss keeps as well as
the deletions, including singletons, similar names with different bytes,
duplicate non-image files, a case-variant pair and an empty and a missing
directory. Four seeded defects are caught at two to eight scenarios each, and
two equivalence controls score zero, so the corpus discriminates without being
merely hypersensitive.

The changed fallback is covered by no existing test, so it was driven
deliberately with a purpose-built probe rather than left unverified: ten cases,
five of which take the fallback, with identical manifest bytes, hashes and
thrown message text on both sides.

Every affected command was spawned on both sides: exactly one byte less per
emission, exit codes unchanged, text identical once the blank line collapses,
and every on-disk artifact byte-identical through the production orchestrator.
No consumer parses these streams; the one caller that spawns them inherits
stdio without capturing.

Suites unchanged at 72 files and 514 tests with identical test-name output, and
`tsc --noEmit` clean on both sides.

Two findings on the deletion path were confirmed and deliberately not fixed,
both outside this group's files. `synthesisNameQualityScore` is not a total
order, so duplicates that tie are resolved by directory iteration order; forcing
both orders deletes opposite files, which makes it latent data-loss
nondeterminism rather than a stylistic wrinkle. And two files disagree by one
cell about which cell is a sheet's midpoint, one hardcoding it and the other
computing it.

Twelve further sites in this directory still pass a redundant newline; they
belong to files outside this group and are left for whoever owns them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LRWf55tWpxcSdpxEMh2eJD
Twenty-two files reviewed in full; two changed, both comment work.

- go-format.sh: two disclosure-take comments move from past to present tense,
  at the syntax-error arm and the tool-break arm. This file is a live
  PostToolUse formatter and was frozen for behavior changes for this sweep, so
  comment-only was proven mechanically rather than asserted: two independent
  comment strippers, one using shfmt's own bash parser and one a quote- and
  heredoc-aware parser, both produce byte-identical code from either revision.
  Each stripper was itself shown sensitive by three seeded code mutations,
  including one that only changes quoting form. Every changed line classifies as
  a comment, and the tracker citations survive verbatim.
- eol-normalizer.test.sh: a history-narration paragraph above the banned-process
  list becomes a present-tense statement of why each named process must not
  appear.

That second file also carries one change nobody asked for, disclosed rather than
hidden. Editing it fired this repo's own bash-format PostToolUse hook, which ran
shfmt over the whole file and expanded a one-liner into its multi-line form. The
file was non-conforming before and is conforming now. It cannot be reverted with
the tools a worker is permitted: the only revert path is a git restore, which
would also destroy the intentional comment fix, and any Edit re-fires the hook.
Confirmed by firing the real hook against a scratch copy and watching it rewrite
the same line.

The expansion is inert, which matters because it wraps an `eval` inside a
command substitution. A 25-probe differential ran the original one-liner against
the reformatted block over empty, comment-only, blank-line, trailing-newline and
line-continuation bodies, a body writing to stdout, one returning non-zero, one
calling exit, one unsetting a variable under `set -u`, and inputs with trailing
slashes, tabs, spaces and UTF-8. Zero divergences in captured value, exit status,
and the outer variables after the substitution, which were poisoned beforehand to
prove nothing leaks out of the subshell. The whole file also minifies to
byte-identical output, so every remaining difference is comment text or layout.

Verification. A 23-case differential over pre-edit and post-edit copies of the
formatter compared seven channels per case: both streams, exit code, a hash of
every fixture file afterwards, the argv and working directory the tool was
invoked with, files left in an isolated temporary directory, and the telemetry
envelope. 161 artifacts, zero divergences.

Ten seeded mutants were all caught. Two of them matter especially: the mutants
that delete each disclosure take are caught by exactly one case each, the
fixtures where the tool writes the file and then fails. Without a
write-before-failure case those arms are unobservable, so the arms whose comments
changed here are genuinely covered rather than nominally so.

Both suites pass identically on either side, 51 and 54 assertions.

Three findings outside this group's files, recorded rather than fixed. The
go-format suite silently no-ops when its tool is absent from PATH, and
`affected-tests.sh --run` reports a suite that printed only a skip line as
passing, so the gate cannot distinguish 54 assertions passing from none running.
A shared discovery suite has a load-dependent false failure: two pipelines under
`set -uo pipefail` let a grep close the pipe, the producer dies of SIGPIPE, and
`pipefail` promotes 141, so a present field reports as missing; measured here at
zero spurious in 1500 idle runs and 47 in 1200 under load, every one status 141.
And the selector's own false-coverage direction is live: three files are selected
only by basename collisions that never test them, so the gate exits 0 over zero
real coverage.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LRWf55tWpxcSdpxEMh2eJD
Eight files reviewed; one changed. guardrails 0.31.4 -> 0.31.5.

The suite for the pre-commit content-invariants hook carried a GitHub-PAT-shaped
token at line 74 and an OpenAI-shaped one at line 98 as contiguous literals in its
own source bytes. So `secrets::scan_text` returned rc=1 on the file that tests the
scanner, naming both by line; the hook would refuse a commit staging its own test
file; and the Write-time guard blocked edits to it. Scanning every tracked file
against all twelve patterns in SECRET_PATTERNS found these two hits and no others
in the repository. Both fixtures now assemble at runtime, the discipline the
sibling secret-pattern-detection suite already documents and uses.

Verification. The load-bearing claim is that the assembled values are the same
bytes, and it was checked as bytes rather than read: the assembly was executed in
isolation under set -euo pipefail, none of which the suite itself sets, and the
results compared with cmp against the literals extracted from HEAD. 40 bytes and
23 bytes, identical. Suite stdout and stderr are byte-identical on both sides,
from mirrored trees, with no normalisation needed because the suite emits no
timestamps and no absolute paths.

Seven mutations were run against both versions with identical scores. Two make the
gate wrongly allow (deleting the PAT pattern, blanking the Linux home-path body):
two assertions fail each. Three make it wrongly refuse (deleting the .env.example
allowlist arm, deleting the tests/fixtures arm, and making the hook fall back to
reading the worktree): one assertion fails each. The .env.example flip is what
proves the fixture is not vacuous, because a fixture that had stopped matching
would let that mutant pass. Two equivalence controls, an internal local rename and
a swap of two adjacent allowlist arms, both score ZERO, so the corpus
discriminates rather than firing on everything.

Five claims corrected rather than carried forward.

CI does scan for secrets. The gitleaks lane runs un-gated on every diff. It was
established empirically why it missed this: running gitleaks with this repo's own
config over the pre-fix bytes reports no leaks, while the same config over a
high-entropy PAT reports one. The default github-pat rule carries an entropy floor
that thirty-six identical characters falls under, and the default OpenAI rule
needs more than a bare prefix plus twenty. This repo's own scanner has no entropy
floor, which is why only it fired.

An `ok:` count equal to a PASS count proves nothing about vacuous assertions. The
shared helper prints and increments in one unconditional body, so the two counters
cannot disagree; the identity is tautological. The mutation corpus is the evidence.

`check-purged-em-dashes.sh` does not cover this file. Its positive list carries
zero guardrails entries; it is a list of prose surfaces. Citing its pass for a
shell file is a non-sequitur. The added lines were grepped directly instead.

The allowlist arm count of eight describes the secret predicate only, and spans
twenty globs; the path predicate has seven arms and sixteen globs. Both were
sourced in isolation and both return 1 for this file's path in three spellings.

The comment-residue count of one is scope-dependent and the scope was not named.
The changed file alone scores zero on both sides. The one finding sits in a
different file, and it is a false positive: `git log -L` shows the comment and the
expression it describes landed in the SAME commit, so the prose is a counterfactual
about the code as written, not a narration of history.

Recorded, not fixed. The hook is not installed in this checkout, so nothing was
actually blocked here; it would have bitten anyone who ran the setup skill and then
edited this plugin. And `machine-path-patterns.sh` is named by no suite directly:
its selection set and `hardcoded-path-patterns.sh`'s are byte-identical at 150
suites, which is 38 percent of the repository's 397, so that is hub saturation
rather than coverage.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LRWf55tWpxcSdpxEMh2eJD
Twenty-one files reviewed; eight changed. source-control 0.55.43 -> 0.55.44.

is_owner_repo_pair holds the owner/repo segment rules for the PR-reference and
scope-key parsers, which kept two hand-maintained copies.
record_mutation_ledger_entry merges save_state's two ledger folds.
legacy_check_identity_keys replaces a tuple literal written out four times.
unscoped_queue_run names a predicate spelled at five sites. Two GraphQL walks
now use the package's own dig instead of hand-rolled nested narrowing.

Six tests added, each closing a gap that was silently open.

Verification. The changed code is behaviour-preserving on every path anyone
could construct: three independent differentials totalling about 14,370
invocations with zero divergence. find_open_prs_for_head_ref is only ever mocked
by the suite, proven by two wrong-key mutants surviving all 649 tests, so its
rewrite is carried by 844 row shapes including every raise path, with a wrong-key
control discriminating on 84. The parser extraction ran to 13,475 invocations
against both pre-images, with a traversal-permissive control discriminating on 18
pairs and 72 triples.

Fifteen kill-intent mutations, nine killed; ten equivalence controls, ten
surviving. The decisive check is the counterfactual: applying the same intents to
the pre-change tree, six of them SURVIVED there, and their post-change
counterparts are killed by the new tests specifically. The new tests are what made
both the over-acceptance and over-refusal directions discriminate; ten controls
surviving is what says the corpus is not merely trigger-happy.

The suite went 643 to 649 by test-id inventory diff, not by counting: exactly six
ids added, none removed or renamed.

Two claims are corrected rather than carried.

The segment rule had FOUR copies, not two. babysit_resolve_thread.py spells the
same rule against the same two regexes twice more, and its own comment already
worries about that drift. Both sites are individually tested, so this is a missed
dedup rather than a hole, but the docstring here claimed to be the one place the
rules live and that was false. It now says what is true and names the other two.

Two of the four deduplicated comprehensions were not interchangeable. The two
reading prev wrapped their iterable in json_array(); the two reading checks did
not. Over 17 inputs the current form diverges from the helper on five: None and 5
raise TypeError where the helper yields an empty set, and a string yields
per-character keys. Unreachable in practice, since classify_checks builds those
lists with comprehensions and checks is local to classify_pr. The helper is the
stricter of the two, and the divergence is recorded rather than assumed away.

Three findings outside this diff, recorded rather than fixed.

A routing gap defeats the integration test written to protect the ledger.
affected-tests.sh selects only test_babysit_state.py for babysit_state.py. Under
a ledger-overwrite mutation that suite stays green, and test_integration.py, the
only suite that discriminates, is never selected. The gate reports success over
zero real coverage of exactly that invariant.

Two mutations survive all 649 tests, both pre-existing. Reducing
unscoped_queue_run to `scope is None` makes in_scope_keys the whole file in
non-queue mode; dropping the `if merged:` guard writes an empty ledger entry.
Naming the first is the natural place to test it.

save_state's first ledger fold is undiscriminated but not dead. Deleting it leaves
all 649 tests green, and instrumentation shows seven tests reach it across
thirteen invocations with none of the thirteen changing the ledger, because
persisted_pr_state retains all three fields it reads. It is not migration-era
code: load_state raises on any schema_version other than 1. It is a live self-heal
for a schema-1 file with a missing mutation_ledger key, demonstrated by rebuilding
an entry from an on-disk record.

Formatter churn, so it is not mistaken for hand editing. The ruff-format hook
reflowed four files on save, measured at 404, 168, 127, 14 and 13 changed lines.
Reproduced by running the pinned formatter over the pre-change copies: the
residual is exactly the intended edits and nothing else. Twelve other Python files
in the same directory are formatter-dirty and untouched, which is what confirms
this is edit-triggered rather than a sweep.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LRWf55tWpxcSdpxEMh2eJD
Eighteen files reviewed; ten changed. source-control 0.55.44 -> 0.55.45.

worktree-create.sh documented two exit codes wrongly, and one of them told a
caller the opposite of the truth. Exit 5, the tree was created but `git worktree
lock` failed, has shipped since #2389 and appeared in neither the header table
nor usage(). Exit 4 was described as "not a git repo, or `git worktree add`
failed", but it also fires when the tree WAS created and a .worktreeinclude file
then failed to copy. That case has exit 5's shape, a tree on disk that is not what
the caller asked for, so a caller reading 4 as "nothing was created" is wrong for
it. Both rows now say so.

exec-bit-check.sh's two synopses disagreed with each other and with the file's own
Modes block: usage() was missing both --list0 and --all, the header was missing
--all. Both now list all four modes plus --all.

Six files had past-tense narration rewritten as the rule it encodes. landed-work.sh
gained a nested flush_record for two byte-identical five-line call sites, the same
idiom worktree-claim.sh already uses.

Verification. Comment-only was established mechanically, not by reading: two
quote- and heredoc-aware shell parsers agree across all ten files, and the
comparison was shown to discriminate heredoc body text, numeric literals,
single-quoted string bodies and statement reordering, while returning unchanged
for three comment-reword controls. Two files in this very diff are live positive
controls for it, one heredoc-only and one reordering-only, and both register.

Exit 5 was verified against an unmodified copy with a git shim that fails
`worktree lock`: rc=5, exactly one stdout line, the tree present, and no `locked`
line in `git worktree list --porcelain` where the control has one. The reflowed
paragraph beside it is word-for-word identical, 104 words each way.

flush_record was checked by differential over nine porcelain fixtures, including
an empty input, a missing trailing separator, a detached-first ordering, a
newline-in-path and a bare repository: identical rows in every case. Ten mutations
were run on both sides of the change with identical scores, seven caught including
four over-refusals, and three equivalence controls scoring zero.

Sixteen shell suites pass with per-suite assertion counts matching a pre-change
baseline, six of them additionally compared against a mirror of the unmodified
tree. Total skips across eight suites: three, each printing its reason, and zero
discriminating skips.

One worker claim is corrected. A mutation that drops the "(detached)" label inside
flush_record was reported as an uncaught pre-existing coverage gap. It is not a
gap: the label is re-derived at every read, so the mutant leaves stdout and stderr
byte-identical on a real detached-HEAD worktree, and dropping BOTH sites is caught
by the suite. The conclusion, that the mutant scores the same before and after,
was right; the reason was not.

That correction surfaces a live question this diff deliberately does not settle.
The assignment inside flush_record appears redundant, and a tidy pass would delete
it rather than hoist it into a named helper. Whether a bare or non-git row, which
returns early before the re-derivation, can observe it is unresolved between two
readings of the control flow, so it stays until someone settles it rather than
being deleted on an unverified one.

Recorded, not fixed: neither the exit 5 contract nor the new help line is
test-covered. The suite asserts nothing about exit 5, the lock failure, or the
"created but not locked" wording. The contract is documented but unpinned.

Also left alone deliberately: fetch-all-pr-comments.sh's if/then/fi merge block,
which looks like it wants to be `&&`. The `&&` form returns 1 on an empty last
surface and fails the pipeline under pipefail, which is the defect that file's
Case 14 pins. Confirmed empirically, including that only the LAST surface matters,
and that reverting the three arms fails exactly three assertions.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LRWf55tWpxcSdpxEMh2eJD
#3706 merged as 1bde828 and its branch was auto-deleted. This brings the branch
onto the new base so the remaining tidy waves land in a successor PR rather than
stacking on already-merged history. origin/main also gained #3707, a miro
dependency bump and bundle rebuild.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LRWf55tWpxcSdpxEMh2eJD

# Conflicts:
#	plugins/source-control/.claude-plugin/plugin.json
#	plugins/source-control/CHANGELOG.md
Seventeen files reviewed; two changed. work-items 0.39.54 -> 0.39.55.

The gitea repo-label pagination walk re-ran `jq 'length'` over the response body
to get the row count the previous iteration had already recorded. LABEL_GOT is
now seeded from page 1 and read directly: one fewer jq process per label page,
and three spellings of "page length" collapsed to one. Two jq calls fed a
here-string of `null` to programs that never read `.`; they now use -n, the idiom
the same file uses three lines later.

Verification. Behaviour was established two independent ways, not by reading. An
end-to-end differential ran 23 scenarios against both versions on the same mock,
comparing exit code, stdout, stderr AND the full recorded request log: zero
divergence, across an exactly-full last page, an empty page, a single page, a
label on the last page, four page sizes, a total-count header present and
unsatisfiable, an unseeded page 2, a non-array page 2, duplicate names across
pages, and a four-page walk. Then an instrumented probe recomputed the OLD value
at the top of every iteration and compared it to the new one: 22 iterations
across 12 scenarios, zero divergences, including the first iteration in every one,
which is where a seeding error would live. The claimed spawn saving was measured
with a counting shim: 45 total jq spawns and 8 `jq 'length'` before, 42 and 5
after.

The -n change was checked by reading both programs in full for any indirect read
of `.` and then empirically over five argument values crossed with six stdin
values. The only divergence is on empty stdin, which the here-string form cannot
produce.

The new test case is load-bearing, and this was measured rather than asserted:
instrumenting the loop body shows it executed ZERO times across the suite's 49
cases before, and once after. Three mutations of the changed line all survive the
old suite. Twelve mutations and five equivalence controls in total, killed and
surviving 8 to 4, with every control scoring zero.

One defect is corrected rather than shipped. The worker rewrote a past-tense
comment into the present tense, and the present-tense form is FALSE: it said an
empty jq length makes the page-length arm an arithmetic error and the walk
continues into page 2. That was true only of the pre-#3651 inline spelling. The
current arm reads an assigned variable, where empty evaluates to 0 and the break
fires; driven directly, page 2 is never requested. This same diff deletes the
#3651 fix line, so a reader would have had nothing nearby to recover the true
reading from, and the comment would have told them a closed bug was live. The
clause is deleted rather than re-tensed, which is the residue skill's own
treatment of history narration.

Recorded, not fixed. Dropping -n from the label-id jq call is caught by NO test:
it leaves the id list empty, the payload build dies on invalid JSON, and the
issue is POSTed with no body at all while the suite still reports success. So the
-n change is carried by the static proof above, not by the test net. Relatedly,
mock.sh records only method and URL and never a request body, so no assertion in
any gitea suite observes the POST payload; label-name to label-id resolution, the
whole purpose of that block, is asserted only through the exit code. Three further
mutations adjacent to the walk also survive, all pre-existing.

Coverage, quantified rather than labelled. create-item.sh selects 202 suites, of
which exactly ONE exercises it. One more references it only as a file-exists
check that would pass against an empty file. Three are same-named suites in
sibling adapter directories that each run their own copy. About 197 are transitive
fan-out on generic basenames, and ten of the 202 mention gitea at all.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LRWf55tWpxcSdpxEMh2eJD
Seventeen files reviewed; five changed. repo-fleet-hygiene 0.23.15 -> 0.23.16,
prototype 0.10.1 -> 0.10.2.

audit-fleet.sh had three copies of an order-preserving linear scan and a
duplicated porcelain-record flush; they are now array_contains and
push_worktree_record. apply-plan.sh had one env prefix spelled out at five
mutation sites; they share git_mutate. Both allowed-tools-pairing.test.sh copies
in this group were shfmt-formatted in step.

The test the worker removed is restored, because removing it was wrong.
Instrumenting every array_contains call shows 152 calls across the suite with
exactly TWO hits, both from the single fixture that puts two findings on one
target. So the dedupe IS exercised, and an over-firing dedupe emits that target's
whole thirteen-line block twice while every count the suite checks still matches.
The worker measured only the repo_verdict site, found zero hits there, and
generalised that to all three. It is right about that one site: over-firing it
alone is a genuinely equivalent mutant with zero difference in any artefact the
suite writes. It is wrong about the other two.

The restored assertion is whole-line, which matters: the roll-up prints the same
path with a "(N linked)" suffix, and a substring match counts that as a second
block and fails on correct code. That is not theoretical, it is what the first
version of this assertion did. It now passes on the real code and fails on the
over-firing mutant with the expected count of two.

Verification. Formatting-only was established mechanically: two independent
quote- and heredoc-aware shell parsers agree on both files, with sensitivity
proven by nine seeded semantic mutations, every one caught by both parsers,
against two controls that correctly do not fire. Comment lines are identical at
35 each. Runtime output from a full plugin tree is byte-identical at nine lines,
exit 0.

array_contains was diffed against the pre-image over fifteen adversarial inputs
including glob metacharacters, empty strings in every position, embedded
newlines, a leading -n and interleaved duplicates: fifteen of fifteen identical,
order preserved. git_mutate's five sites were confirmed to carry ONE spelling of
the prefix by extraction, not by reading. A live fleet of two repos with merged
branches and linked, prunable and nested worktrees produces a 121-line report
differing only on the lines carrying $0 and the plan path, with byte-identical
plan JSON and identical stderr.

Twelve mutations and four controls. Killed: array_contains always-true at 65
failures, its shift removed at 65, push_worktree_record's guard inverted at 28,
a parallel-array misalignment at 95, git_mutate as a no-op at 3.

Two corrections carried from verification rather than the worker's numbers. The
guard-inverted mutant is 28 failures against the shipped suite, not 27; 27 was
the count before the new assertion existed. And drift across the five-copy
pairing family fell in NET, 252 differing lines to 228, with four copies now
differing pairwise by exactly their SKILLS= line, but it GREW by twelve lines
against the repo-hygiene copy, which is a genuine superset and now the family's
only unformatted member. "Drift reduced, not widened" was true in net and false
against that copy. No cross-plugin consolidation was made, and the drift gate
still reports the family as differing rather than identical, which is what keeps
it green.

A hazard-list correction worth carrying beyond this group: prototype does NOT
have three copies of a detector. scripts/detect-ecosystems.sh is the detector;
the two skill-level files are seventeen-line exec wrappers whose executable
bodies are byte-identical, differing only in a comment naming the sibling skill,
and their suites assert exactly that byte-identity plus the header's rationale
phrases. Nothing was drifting and nothing was consolidatable.

Recorded, not fixed. The worktree-lane OID-drift gates have NO coverage at either
end: disabling the plan-time and execution-time gates together leaves the suite
at 23 passing, 0 failing. The branch lane is covered by that same pairing, at 2
failures. This is pre-existing and is the first time it was measured. Separately,
a prefix-glob comparison in array_contains escapes the suite undetected.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LRWf55tWpxcSdpxEMh2eJD
… (G55)

Twenty-one files reviewed; three changed plus one test added.
source-control 0.55.45 -> 0.55.46.

babysit_resolve_thread.py spelled the owner/repo segment rule out twice against
the same two regexes the shared helper wraps. Both now call is_owner_repo_pair,
and the file's own comment at the second site, "keeps one rule instead of two",
is finally literally true. babysit_merge.py's seven refusal sites share one
_refuse closure, the idiom the sibling guarded CLI already uses at thirteen sites.

A test is added because the refactor CREATED a failure mode, and that is the
important part of this commit. Passing the pair in the wrong order is something
the previous spelling could not express: it named the two regexes explicitly.
Once both go through one helper, a one-token transposition silently weakens a
path-traversal guard whose result is interpolated into a gh api URL, and it was
killed by ZERO of the 649 tests. Every existing head-repo fixture uses a name and
an owner that both regexes accept or both reject, so the transposition was
invisible to all of them. A dot is legal in a repository name and illegal in an
owner, so a my.repo fixture clears validation only when the arguments are the
right way round. Against a scratch baseline the transposed build differs from the
control by exactly this one test and nothing else.

Verification. Both pre-images were compared character by character against the
helper's body: identical, including short-circuit order under the enclosing not().
The refusal contract was checked over a 77-shape corpus comparing exit code and
stdout and stderr bytes, with zero divergence, covering all seven routed sites,
empty envelopes, JSON-escaping payloads with quotes, backslashes, control
characters, CJK and emoji, and nine shapes that trip two sites at once. Exactly
three stdout key orders exist and both versions produce the same one per shape.
Each site's exit code was flipped individually; all seven are guarded. Fifteen
mutations reproduce the worker's numbers exactly, with four equivalence controls
scoring zero.

One claim is corrected. The parse-failure refusal was described as the one with
no usable pr value; args.pr does exist and holds the raw string. The exception is
nonetheless necessary, shown by the differing output: routing it through _refuse
adds a pr field naming a PR nobody can resolve.

The selector finding this group inherited is worse than reported, and it lands on
this package hardest. affected-tests.sh seeds its reverse lookup with the basename
INCLUDING .py, so a suite that says `import foo` rather than naming `foo.py` is
invisible. babysit_review_trigger.py maps to zero suites while two suites exercise
it, one calling ten distinct symbols. No test imports refresh_pr_branch.py at all;
its only coverage is a guard-contract row enforced by read_text plus assertIn,
which is a source-text grep and never an execution. Two sibling suites for the
same module differ purely because one spells the literal filename in a subprocess
argv. And babysit_gh.py selects ONE suite despite twelve importing modules and
three importing suites, because the only file in the repository containing the
string "babysit_gh.py" is a changelog. Had this group trusted the selector for
that file, it would have missed the very suite whose eighteen kills cover the two
new call sites.

Recorded, not fixed: reordering the envelope keys, dropping the pr key, and
routing the parse failure through the helper each survive the full suite. All
three were equally unprotected before, but the refactor concentrates the risk
from seven edit sites to one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LRWf55tWpxcSdpxEMh2eJD
Eleven files reviewed; six changed. source-control 0.55.46 -> 0.55.47.

fetch-annotations.sh --help sliced its header with a hardcoded sed -n '2,20p'.
The header runs to line 23, so the banner stopped three lines short: it printed
"Exit codes:" and "0 success" and then ended, never showing 1, 2 or 5. Measured
rather than inferred, by running both versions from clean extractions: the old
form printed 19 lines and exactly one exit code, the new blank-line-terminated
form prints 23 and all four. That form is already used by three other scripts
here and removes the line-number coupling that caused the drift.

Three assertions were added beyond what the worker wrote, each closing a gap the
verifier proved was open by mutation.

fetch-failed-logs.sh had NO --help coverage at all. An under-slice dropping exit
codes, an over-slice leaking `set -uo pipefail` and the whole variable block, and
a usage() emitting zero bytes each left that suite green at 14 of 14. It now has
the same two-direction guard, and both the under-slice and over-slice mutants
fail it.

check_run_name had no value assertion. The schema check used has(), which is
presence-only, so dropping .name from the jq projection scored zero across all
ten assertions while every emitted record silently carried the string "null". The
field is emitted and read by nothing else, so this was the only place its value
could be pinned. The mutant now fails with "expected test got null".

Verification. The jq shorthand was checked byte-identical including key order over
ten object shapes, among them absent keys, nulls, nested objects, duplicate keys,
bignums and escape sequences, plus four non-objects where the error text also
matches. Every gh and curl invocation line is identical to the pre-image, so no
request method, URL, header or body changed. Mutations cover both gate directions,
including one that drops a single exit code from the header and one that inserts a
blank line INSIDE the header, which is the failure mode the new slice form could
plausibly have introduced; both are caught. Two equivalence controls score zero.

A landmine was avoided and is worth recording. nesting-invariant-ssot.test.sh has
explicit FAILED=0 and CASE_NUM=0 that look redundant against the shared helper's
`: "${FAILED:=0}"`. The plugin's own changelog records that exact removal being
proposed, refuted and reverted: the helper form preserves an environment-inherited
value, the explicit form forces a reset, and they are not the same. Left alone.

Three findings recorded rather than fixed.

The same truncation bug survives in two more scripts, the byte-identical
resolve-convention-pattern.sh pair, whose sed -n '2,40p' cuts a 43-line header and
drops all three of their exit codes. The fix here is NOT transplantable: those
headers have no blank line before the code, so the terminator used here would leak
executable lines into their banner. They are also a registered sync-cluster pair,
so they need one deliberate change applied to both.

fetch-failed-logs.sh's help output was byte-identical before and after only
because the hardcoded 41 happened to land exactly on the blank line. That was luck
the next header edit could have taken away.

And the completed dispatch table still lists generic routes before specific ones
while the code matches specific-first, so read top-down as a dispatch order it
gives the wrong answer for two fixtures. Pre-existing, and the completion
preserved rather than introduced it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LRWf55tWpxcSdpxEMh2eJD
Nineteen files reviewed; six changed. performance 0.1.0 -> 0.1.1. This plugin
arrived on main mid-run, so it was not in the original inventory and its file
list was enumerated fresh and verified against the tree.

spawn-census.test.sh called assert_not_contains at two sites and never defined
it. The suite defines five functions and that is not one of them, and it sources
no helper file that could have supplied it. Both calls died as `command not
found` on stderr, incremented no counter, and the suite still exited 0 with 28
PASS lines against 30 call sites.

The two dead assertions guarded exactly the false green this plugin exists to
refuse: a census line printed for a subject that never ran. That is not an
argument, it is measured. Emitting `spawns=0 rc=127 []` before the never-ran
refusal leaves the OLD suite at exit 0 with zero failures, and fails the fixed
suite twice, naming both the 127 and the 126 arm.

The added helper was checked against its call sites rather than assumed: argument
order is label, needle, haystack, matching this file's assert_contains and NOT the
opposite order used by a sibling suite elsewhere in the repo, and both call sites
are paired with a preceding positive pin so neither can pass vacuously on empty
output.

Also fixed: an assertion label that contradicted its own expectation, reading "an
unresolvable denominator still exits 0" while asserting 2. Exit 2 is a refusal.
Plus a dead mkdir for a fixture directory nothing references, proven unreferenced
beyond the file itself, and duplicate section-header numbers in two suites.

The formatter reflow, recorded so it is not mistaken for hand editing.
spawn-census.sh carries a 100-line diff from the bash-format hook, which runs
shfmt without -ci while .editorconfig sets no switch_case_indent, so case arms
de-indent from four spaces to two. It is provably semantics-free, and the proof
matters because the obvious one is invalid: a double space inside a string
literal is a real content change that BOTH `git diff -w` and a whitespace-stripped
hash miss. Three orthogonal checks agree the file is unchanged in meaning, and
that combination was shown sensitive by six seeded mutations it catches against
three controls it correctly ignores. The suite's output is byte-identical against
both versions.

Two findings recorded rather than fixed.

The spawn instrument has four undocumented blind spots. Counting via
PATH-prepended shims is sound for indirect spawns, verified over a subshell, a
command substitution, a pipeline, xargs, a nested script and a backgrounded job.
But a subject invoking an absolute path, resetting PATH, running under `env -i`,
or forking without exec is counted as ZERO. Three of those emit a tidy
`spawns=0 rc=0 []`, which is the confidently-wrong-number shape this script's own
header exists to refuse, and nothing in the plugin's documentation mentions it.

pathfix.py is under-selected by the test mapper: four modules import it and none
of their suites is selected, because the selector seeds its reverse lookup with
the basename including .py while an `import pathfix` reference carries no
extension. Latent rather than live, since mutating it is still caught by its own
co-located suite, which drives the whole public surface.

The registered sync-cluster copy plugins/performance/lib/spawn_noise.py was not
touched. Its canonical is plugins/claude-ops/lib/spawn_noise.py, not the path an
earlier note in this run gave; both sync checks are green.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LRWf55tWpxcSdpxEMh2eJD
Sixteen files reviewed; five changed. typos-format 0.6.37 -> 0.6.38,
skill-quality 0.20.11 -> 0.20.12, songwriting 1.4.21 -> 1.4.22,
testing 0.7.12 -> 0.7.13.

The typos-format suite measured process spawns by exporting PS4 and reading the
trace. Bash overwrites and re-exports PS4 at startup, so the tracer matched 0 of
802 lines: `env PS4='SENTINEL ' bash -c 'declare -p PS4'` reports
`declare -x PS4="+ "`, and prefix assignment, env(1) and export are discarded
alike. dash honours it; bash does not. Four assertions were therefore measuring
an empty word list and passing, and the fifth failed. PS4 now arrives through a
BASH_ENV preload, which the traced shell performs itself and which preserves the
${FUNCNAME[0]} attribution the tracer depends on, where `source` would not.
765 of 803 lines are now marked, and a new assertion fails loudly if that ever
returns to zero.

With the instrument live the jq expectation was wrong: it asserted 2 and the true
count is 1. That was established with a counting jq shim on PATH, deliberately NOT
with the repaired tracer, so a tracer bug could not replace one wrong number with
another. Only hook::buffer_stdin's `jq -e .` runs; hook::read_file_path takes the
builtin fast path on a single-chunk payload and never reaches its jq fallback.

This corrects a claim this run repeated for hours, and it was wrong in both
halves. The failure was the broken instrument, not the container. And CI never
ran the block at all: the suite gates on a real typos binary, the typos action
lives in the lint job while the plugin contract tests run in test-linux, separate
jobs with no shared PATH, and typos is in neither the CI Python requirements nor
the npm devDependencies. Measured by stripping typos from PATH: 87 of 144
assertions run, so 57 have never executed in CI, including telemetry, config
precedence, symlinked roots and the write-mode allowlist. The dark region is the
whole real-binary tail, not just the trace block.

check-skill.sh loses a line that could never change anything: the preceding
expansion already yields exactly SKILL.md, since SKILL_MD is $SKILL_DIR/SKILL.md
and both are assigned once. Proven three ways rather than read: 374 adversarial
root and name pairs replaying both real call sites with zero divergence, a
differential over ten real skills with byte-identical output, and the symmetric
check that re-inserting the line changes nothing.

datamuse.sh was reflowed in full by the bash-format hook, which is hook output
rather than a hand edit; the file was not shfmt-clean before. The live API
contract is untouched, verified four ways including bash's own parse-tree
re-serialiser and a 36-case harness with argv-capturing curl and jq shims driving
adversarial words, with zero divergence in exit code, output or argv multiset.

Mutation evidence, in the direction that matters: the pre-change test scores
IDENTICALLY on every one of four mutants, catching none, while the repaired suite
catches all four. Two equivalence controls score zero. A third candidate control
was correctly rejected by the worker for scoring 1, and the reason is worth
keeping: the suite reads that constant out of the hook by literal source text,
not by behaviour.

Three findings recorded rather than fixed. The repaired tracer is still blind to a
`command`-prefixed external, because the traced word is `command`, whose type is
builtin, so even the ceiling misses it; latent, since the hook's only use is
`command -v`. A result-cap mutation in datamuse.sh's syllables arm survives its
suite. And the coverage numbers here are mostly fiction: check-skill.sh selects
155 suites of which 2 execute it, three name it only in a comment, and 76 of the
155 selection reasons name some other file entirely.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LRWf55tWpxcSdpxEMh2eJD
Twenty-three files reviewed; seven changed. work-items 0.39.55 -> 0.39.56.

validate.mjs cited source locations for the queries it checks, and eleven of
eighteen were stale: eight into common.sh, three into create-item.sh. Every one
landed on a comment, a bare closing brace, or unrelated code. issueCreate claimed
line 151, which is a `while :; do`, against an actual 216. All eleven are
corrected AND the seven that were already right were confirmed right rather than
churned, because a pointer "corrected" to a new wrong line looks fresh and is
harder to catch next time. Both edited hunks are two lines for two, so nothing
shifted underneath another pointer.

reclaim.test.sh had two probes wrapped in `2>/dev/null || echo 0` around a jq
count whose expected value IS 0. A probe broken outright therefore scored exactly
like a passing assertion. With the jq program deliberately broken, the old form
reports 39 passing and exits 0; the new form reports two failures and exits 1. The
legitimate empty and missing cases still yield 0, so nothing over-rejects.

The cross-group handoff was re-proved rather than inherited. A sibling adapter
replaced `jq … <<<'null'` with `jq -n` after proving equivalence for ITS programs;
these are different programs. Neither reads `.` even indirectly, the only
reachable identity being select's pass-through which the following `| $n`
discards. Output is identical across a 560-pair matrix of arguments and stdin
values including invalid JSON, a closed stdin, a blocking FIFO with a live writer,
and a 64 MiB stream. All 297 recorded GraphQL request bodies across the nine verb
suites are byte-identical before and after, with a working one-field tamper
control proving that comparison can fail.

The re-tensing caution was applied in BOTH directions and each decision settled by
mutation, not by reading. One clause was DELETED: removing the guard it describes
now produces a loud failure at exit 1, not the silent success-with-no-output the
text claimed, so a present-tense rewrite would have shipped a false statement. One
was RE-TENSED: reducing the guard to a name-only compare fails exactly three cases,
so the counterfactual is true.

The schema lane was actually executed, not assumed: a 1.31 MB Linear SDL fetched
into scratch, graphql installed there, and validate.mjs at 18 of 18, negative.mjs
at 10 of 10, fidelity.sh at 17 verbatim and 0 mismatched, byte-identical to the
pre-change baseline. Nothing was written into the repository to make it work.

One correction to the worker's bookkeeping. It reported executed-equals-contained
across nine suites; it holds for eight. get-item.test.sh has 39 static call sites
and executes 44, because two `for` loops multiply single sites. The stated
derivation never mentioned loop expansion, and that is the one suite where it
matters. It is not among the changed files.

Recorded, not fixed. Four assertions in claim.test.sh share a residual blind spot
with the two probes repaired here: an assertion whose expected value equals what an
EMPTY probe yields cannot tell "nothing was written" from "the right thing was
written". The suites as wholes do fail under that mutation, so it is a
per-assertion gap rather than a blind suite, and a positive control asserting that
some update WAS recorded would close it. It is the natural next step of the same
reasoning this commit acts on.

Also recorded: linear's mock DOES record the request body and 33 assertions across
eight of nine verb suites read it, unlike the gitea and github mocks. What nothing
here records is the HTTP method, the header set including the API key, and the
curl flag set. And nothing guards the loc: pointers, which is how eleven of
eighteen went stale.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LRWf55tWpxcSdpxEMh2eJD
Twenty-three files reviewed; ten changed. work-items 0.39.56 -> 0.39.57.

Seven `printf '%s\n' "$X" | jq …` pipelines became `jq … <<<"$X"`, the idiom the
file and every seam lib already use, plus one merged `local` declaration and one
stub variable dropped from a line where it is unreachable. The `case`-body
re-indentation in seven files is the repo's own formatter output: bash-format.sh
runs shfmt with no layout flags, driven by .editorconfig.

Byte safety, and a hypothesis I put to the verifier that turned out to be wrong.
I briefed it to expect divergence when the value already ends in a newline, is
empty, or is a lone newline, on the theory that a herestring adds a newline only
if one is missing. That is not how bash behaves: a herestring appends exactly one
newline unconditionally, so all three cases match `printf '%s\n'` byte for byte.
Seventeen payload shapes compared at the level of the bytes reaching jq's stdin,
seventeen identical. NUL is unreachable because all seven sites take a value from
a command substitution, which strips it. No site reads PIPESTATUS or depends on an
assignment surviving the pipeline, and the pipeline form carried a latent pipefail
plus SIGPIPE hazard that the herestring removes. Driving the real verbs against a
stub over eight payload shapes gives byte-identical stdout and identical exit
codes in 32 of 32 cases, with eleven distinct output checksums proving the matrix
can tell outputs apart.

The manual delta was isolated mechanically rather than eyeballed: running shfmt
over the pre-change files and diffing against the working tree leaves exactly the
seven rewrites, the local merge, the dropped variable and the header edit, and
nothing else. That is 22 insertions and 18 deletions across six files, out of a
headline 95 and 91; four of the ten files are whitespace-only.

Coverage was measured, not reasoned. Poisoning each file with an early exit and
counting which suites notice: common.sh is exercised by 12 suites, reclaim.sh by
2, list-items.sh by 1. The selection is 202 for each, of which 193 are runnable
shell suites, so for common.sh 181 of 193 selections are false. The method also
detects a suite that SOURCES the file rather than running it: common.test.sh
exits with the poison code itself, distinctly from the child-process suites.

Three claims are corrected rather than carried. "github was the only adapter out
of shfmt line" is false: local-markdown has four dirty files, at HEAD and still.
The seven dirty github files were exactly the seven re-indented here, so the
justification stands, but the comparison did not. The 202 denominator is the
selection count, not the runnable count, which is 193. And the redundancy found in
`wit_map_gh_error` is SYMMETRIC: deleting the "HTTP 404" arm is uncaught, and so
is deleting the "Not Found" arm, because one fixture string contains both. Killing
the whole arm IS caught, so the assertion is live and the cause is masking rather
than absent coverage. The finding is twice the size reported.

Every uncaught mutant was proven pre-existing by running the same battery against
a clean pre-change tree: fourteen mutants, identical scores on both sides. This
change introduces no coverage regression.

Recorded, not fixed, and the first is the one that matters.

The lease protocol's three writing verbs have NO assertion on what they write. The
stub logs a PATCH id and a bare POST_COMMENT token and never records the request
body, so renew-lease can PATCH back a body whose renewed_at was never bumped,
which is the verb's entire purpose, and reclaim can leave a reclaimed lease
without superseded_at, active forever. Both mutations pass every suite.

Two suites lose assertions silently without jq: common.test.sh drops 28 to 16 and
create-item.test.sh 10 to 4, both still exiting 0, while a sibling in the same
directory fails loudly under the same condition. Neither emits the repo's SKIP
convention, so --strict-skips cannot see them, and check-silent-skips.sh cannot
either: it scans hooks rather than suites, and only the negated `if ! command -v`
form.

And conformance/bindings/github.test.sh is selected by none of these ten files. A
change to a sibling adapter's common.sh pulls in four conformance bindings; a
github adapter edit pulls in none, and that suite deliberately does not run the
abstract suite anyway. The github adapter has no conformance lane reachable from a
github adapter edit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LRWf55tWpxcSdpxEMh2eJD
Eleven files reviewed; seven changed. work-items 0.39.57 -> 0.39.58.

preflight.sh loses a normalize_path wrapper with two call sites, replaced by the
fork-free norm_path/$NORM_OUT protocol ten other sites already use. The loop it
sat in was paying exactly the fork the function's own header says it exists to
avoid. Checked over 30 hostile inputs: 27 identical, and the 3 that differ are
trailing-newline shapes the loop cannot produce, since read -r strips them. Across
24 newline shapes there is no fail-open case in either direction; the new code
only ever reports MORE gaps.

Three assertions were added, each closing a mutant proven live against an isolated
pre-change tree: the needs-confirmation path's stdout was unasserted, the
generated upper-cased provider spelling was unpinned, and a want-from-NORM_OUT
detachment was invisible. All three exit 0 before and 1 after.

Two tidies are REVERTED before shipping, on a precedent this plugin already set.
Version 0.39.24 records a printf-pipe conversion in
evaluate-schedule-precondition.sh being refuted and reverted because it shifted a
line number into a stderr diagnostic on a reachable error path. Both of these were
the same class, in that same file family.

Dropping the `printf '%s\n' "$( … )"` wrapper is NOT newline-neutral, though it
was reported as identical. Command substitution collapses trailing newlines to
one; the direct pipeline passes them through and puts a blank line before the
needs-confirmation marker. Measured over seven prompt shapes, two diverge. The
wrapper is restored with a comment recording that it is load-bearing.

And `$(cat "$file")` to `$(<"$file")` changes the missing-template diagnostic from
one naming only the template path to a bash error carrying THIS script's line
number. That is precisely what 0.39.24 rejected. Restored, with the precedent
named in the comment so the next sweep does not re-propose it.

Verification. The three reformatted files are semantics-free, established with two
independent parsers plus a comment-stream diff, and the combination was proven
sensitive by eight seeded mutants including a double space inside a string
literal, which `git diff -w` cannot see. Isolating the manual delta that way
matters here: `git diff -w` also cannot see semicolon-separated one-liners being
split, which is most of one file's diff.

The generator's output is contract-identical across 20 spec shapes including seven
rejection paths: generated tree bytes, stdout, stderr and exit codes all match.
Templates were not touched.

Three of the worker's claims are corrected rather than carried. normalize_path had
TWO call sites, not one. The preflight differential's "0 differences" holds for
167 fixtures but not for 240: two trailing-newline roots diverge, which is the
shape the smaller matrix omitted. And the suites do not call skip_suite; they
hand-roll an echo-and-exit-0, which lands in the same blind spot.

Recorded, not fixed: preflight.test.sh and generate-adapter.test.sh degrade
silently without jq, and check-silent-skips.sh cannot see them because it scans
hooks and top-level scripts rather than plugin suites. CI installs no jq
explicitly, so their coverage rests on the runner image. Separately, preflight.sh
selects three suites and one exercises it; the other two are basename collisions
with repo-hygiene's own preflight.sh.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LRWf55tWpxcSdpxEMh2eJD
Twelve files reviewed; four changed. work-items 0.39.58 -> 0.39.59.

Two concurrent conformance runs on one host clobbered each other. The overlay
case derived its path from `dirname` of the binding file, and every binding
mktemps that file straight into TMPDIR, so the overlay resolved to one fixed path
shared by every run on the machine. The binding is now re-homed into a run-private
`mktemp -d` right after cb_setup, and the trap removes it.

Measured on separate extractions of both trees. Eight jira plus eight
local-markdown in lockstep: 9 of 16 red before, 0 of 16 after. Ten jittered pairs:
19 of 20 red before, 0 after. A 25-round reproduction of the CI shape: 15 of 50
red before, 0 after. A 40-way stress run: 0 red. 130 runs on the fixed tree, none
red, case counts always 34 and 81. Planting a poisoned overlay at the shared path
makes it deterministic: 38 cases with 9 failed before, 34 with 0 after.

The failure is whole-suite poisoning, not the two cases first reported. Once one
run leaves a foreign provider at the shared path, every tracker invocation in
every concurrent run exits 3, the provider resolves empty, and verb_supported
reads every verb false, which CHANGES the reported case count. So matching case
counts is not by itself a sufficient regression guard.

The attribution is narrowed, because the first wording would have put a false
statement into a bug tracker. scripts/run-plugin-tests-serial.txt lists two
entries as unexplained under --jobs 4: plugins/discovery/agents/tool-honesty.test.sh
and this plugin's bindings/jira.test.sh. It does NOT list local-markdown.test.sh
anywhere. This mechanism explains the JIRA entry only, and the evidence for that
is an asymmetry the first pass left on the table: under the CI shape jira loses
the race 13 times in 25 while local-markdown loses 2, so the mechanism predicts
which of the pair got listed. tool-honesty.test.sh is a markdown contract test
with no reference to the tracker, no mktemp and no shared path; this cannot
explain it. The record's own rule is that BOTH entries come off the list when the
cause is found, so #3694 stays open.

Also worth stating plainly: the collision cannot fire in the lane as configured
today, because jira.test.sh is serial-listed and never runs beside the only other
runner-invoker. This is a precondition for delisting it, not a repair of a
currently red lane.

Fix correctness, checked independently. No consumer resolves the binding by its
old path; the only binding-relative resolution in the seam takes an absolute value
from every conformance binding. Nothing leaks: teardown still removes the original
and the trap removes the copy, with /tmp entry counts unchanged across 40
concurrent runs. Trap coverage was measured by signalling a parked instrumented
copy: SIGTERM, SIGINT, SIGHUP, normal exit and failure exit all clean; SIGKILL
leaks one directory, uncatchable, and the pre-change code leaks its own temp file
there identically. Every other write in the runner was audited for the same shape
and all were already unique, so this was the only fixed shared path.

Fifteen mutants plus controls. Two prove the new lines are load-bearing rather
than cosmetically dead: emptying the copy and forcing the new status capture to
zero both fail the suites outright. Two more show the private directory and its
export are each individually load-bearing under concurrency. Three equivalence
controls score zero, and reverting the fix is killed only in parallel, which is
exactly why this went undiagnosed.

Recorded, not fixed, and the last one is sharp. e2e-probe.sh's 16 assertions have
never been executed by any suite, and the reclaim block's 13 never run in CI,
both confirmed by planting an early exit and watching nothing notice.
bindings/github.sh's success path is likewise unexecuted; its suite asserts only
that setup refuses without a target. The consequence: redirecting e2e-probe.sh's
`gh issue close` to a DIFFERENT REPOSITORY leaves every automated suite green.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LRWf55tWpxcSdpxEMh2eJD
Twenty-one files reviewed; ten changed. work-items 0.39.59 -> 0.39.60. This is
the last of seventy groups in the repo-wide sweep.

renew-lease.sh reported a renewal it had not persisted. When the store rewrite
could not run, mktemp failure left the temp path empty, the redirect failed, `&&`
short-circuited past the move, and the script's status came from the trailing jq.
Reproduced with a failing mktemp: exit 0, a renewed_at on stdout, and a store
still holding the older timestamp. It now exits 1, the code CONTRACT.md defines as
internal and that claim.sh already used for this same class. The conformance suite
is byte-identical at 81 cases, so this moves toward the contract rather than away.

Its suite had an assertion comparing two empty greps. It derived the item path
from the outer store variable, and when that path is wrong both greps return
empty and "marker unchanged" compares "" to "". Pointing it at a nonexistent file
leaves the pre-change suite green at exit 0 with eight passing lines. Paths now
come from each item's own reported url. Stated precisely rather than as the worker
put it: the class can no longer pass silently because a sentinel assertion fails
first, but the trailing comparison is still empty-versus-empty under a forced bad
path, so this guards the suite rather than making that one assertion
self-sufficient.

A leading-zero item name broke allocation. `08.md` passes the item filter and bash
read the bare 08 as an invalid octal literal. This was NOISY, not silent as
reported: the walk emitted an arithmetic error on stderr and skipped the file, so
a store holding 08.md and 7.md allocated 8 and wrote 8.md beside it, two files
with one numeric identity and one unreachable. Base 10 is forced now, and the
maximum derives from the walk's numeric tail, which cannot collide by
construction. The old skipping was the collision hazard, not a protection against
one.

Two defects in the group's OWN new code, both found at verification and fixed
here rather than shipped.

The new cleanup line was `rm -rf "$STORAGE" "$(dirname "$LIVE_FILE")"`. LIVE_FILE
comes from the created item's reported url, so any upstream failure leaves it
empty or the string null, and dirname of either is ".". Unguarded, that asks rm to
delete the runner's working directory, and only GNU rm's own refusal to remove "."
stood in the way. It is now guarded on an absolute existing path, with the reason
recorded.

And the new store-walk assertion could not detect what it claimed to pin.
Allocation derives its maximum from the tail of the walk, so numeric ordering is
load-bearing, but the fixture's lexical and numeric orders coincided and a
degraded sort scored zero. My first correction was wrong in the same way: {1,10}
also agrees in both orders. The fixture is now {2,10}, which under a lexical sort
returns 1,10,2 and allocates 3, an existing item. Verified in both directions: the
real code passes, the degraded sort fails both assertions with exactly those
values.

Verification. Twenty-two added assertions across five suites, each closing a
mutant confirmed to survive the pre-change tree. The claim record rewrite is
byte-identical on stdout including key order across 28 flag and value shapes,
among them a session id containing the marker terminator, a ttl above 2^53,
newlines, tabs and unicode. Coverage measured by poisoning: common.sh is exercised
by 13 of its 202 selected suites, so 189 selections are false; because it is
sourced, the poison surfaces both as exit 99 in the sourcing suites and exit 1 in
the verb-running ones. Two surviving gate mutants were shown to be genuinely
masked rather than uncovered, by checking every adapter-reachable input yields an
identical exit code. Three equivalence controls score zero. All eleven adapter
suites green, conformance 81 of 81.

Three of the worker's framings are corrected rather than carried: there were three
copies of the name filter and four of the store walk, not two, and two filter
copies remain; the octal behaviour was noisy rather than silent; and
superseded_at is not written only by reclaim, the accurate premise being that no
local-markdown script writes it at all.

Recorded, not fixed. renew-lease reports a record that computes as EXPIRED while
its own store computes LIVE, because its projection hand-lists fields and drops
ttl_minutes, which claim reports and the store keeps. Pre-existing, but CONTRACT.md
says renew-lease emits the same shape as claim and this falsifies it. The same
unchecked-write shape as the headline fix survives in add-sub-item.sh, which
reports linked true after an unchecked write, and in link-blocks.sh. And one suite
came back red once in a 202-suite baseline, green in an identical re-run, and
could not be reproduced in 440 targeted runs under contention; it is recorded
rather than attributed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LRWf55tWpxcSdpxEMh2eJD
guardrails 0.31.5 -> 0.31.6. Cross-group resolution, not a group of its own.

G57's verifier found this while checking an unrelated diff: the same hardcoded
header slice that truncated fetch-annotations.sh survives in
resolve-convention-pattern.sh, whose `sed -n '2,40p'` cuts a 43-line header. Its
--help ended on the words "Exit status:" and never showed 0, 1 or 2. Reproduced
before the change and after: three exit-code lines now print and no executable
line leaks.

The fix is deliberately NOT the one used on the sibling scripts. Those terminate
the slice on the first blank line; this header runs straight into `set -uo
pipefail` with no blank line between, so that terminator would leak code into the
banner. The verifier flagged exactly that, which is why this was left for a
considered fix rather than a transplant. The slice now stops at the first
non-comment line, which cannot drift as the header grows.

The pair is a registered sync cluster. The canonical was edited and
scripts/sync-resolve-convention-pattern.sh propagated it; the two are
byte-identical and the drift gate is clean. All four covering suites pass:
resolve-convention-pattern, block-convention-violation, require-jq-posture and
commit-msg-convention.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LRWf55tWpxcSdpxEMh2eJD
@kyle-sexton kyle-sexton changed the title refactor: repo-wide tidy sweep, continued (waves 13+) refactor: complete the repo-wide tidy sweep (all 70 groups) Sep 4, 2026
@kyle-sexton
kyle-sexton marked this pull request as ready for review September 4, 2026 09:15
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 4, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-04T09:22:43.356426Z ee0509d Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@claude

claude Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Claude finished @kyle-sexton's task in 9m 6s —— View job


Claude finished the /review:code-review pass

  • Load /review:code-review skill instructions
  • Check skip gate (PR is open, not draft, has real behavior changes → proceed)
  • Gather diff via gh pr diff 3710
  • Review changed files for correctness/maintainability findings (5 parallel passes covering all 100 changed files)
  • Post inline comments for anchorable findings (none met the high-signal bar)
  • Post summary with cross-file findings and permalinks

Summary

Reviewed the full diff (100 files) against the /review:code-review high-signal bar: real correctness bugs, logic errors, or maintainability regressions introduced by this diff, excluding anything a linter/formatter would catch and pre-existing issues on untouched lines.

I specifically re-verified, independently of the PR description's own claims, the two headline behavior fixes:

  • Conformance overlay path collisionrun-conformance.sh: BINDING_DIR="$(mktemp -d)" now mints a fresh directory per run, and the overlay path derives from that directory rather than the shared $TMPDIR. Confirmed this actually fixes the collision for all three bindings (github, gitea, local-markdown) that mktemp a binding file straight into $TMPDIR. Correct.
  • renew-lease.sh swallowing a failed writerenew-lease.sh:79-85: the rewrite is now grouped and tested with !, so a mktemp/awk/mv failure exits 1 before the reporting jq (which previously ran unconditionally and reported success from stale in-memory state) can execute. Correct, and the new test exercises it with a mktemp-failure PATH shim.

Also spot-checked the --help truncation fixes in fetch-annotations.sh / fetch-failed-logs.sh (source-control) and the awk-based header slicer in lib/resolve-convention-pattern.sh / plugins/guardrails/hooks/resolve-convention-pattern.sh — both correctly print the full exit-code block now, and the two copies stay byte-identical as their "registered sync-cluster pair" status requires.

No correctness bugs or maintainability regressions found across the remaining tidyings (case-statement reindents, printf | jqjq <<< conversions, dead-variable removal, predicate extraction in audit-fleet.sh/apply-plan.sh/babysit_*.py, the typos-format.test.sh PS4/BASH_ENV tracer repair, preflight.sh's norm_path refactor, and local-markdown/common.sh's wit_next_number rewrite, which incidentally also fixes a latent octal-parsing hazard on zero-padded filenames). None had a call site, edge case, or ordering assumption that the diff broke.

One minor, non-blocking documentation note (cross-file, not anchorable to a single changed line): plugins/source-control/CHANGELOG.md:48 says the --help truncation bug "survives in two more scripts" and "need[s] a different terminator" (future tense, unresolved) — but this same PR's diff already ships that exact fix, via the awk terminator in lib/resolve-convention-pattern.sh:66, documented as resolved in plugins/guardrails/CHANGELOG.md:6 (0.31.6). Reads correctly if you know the two changelog entries were written at different points in a 53-commit sweep, but a reader of the merged source-control/CHANGELOG.md alone would be told a bug is still open when it isn't. Not a code defect — no action required unless you want the wording tightened.

No inline PR comments posted; nothing found met the bar for a line-anchored finding.

@claude

claude Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Claude finished @kyle-sexton's task in 2m 19s —— View job


Security review complete

  • Gather context (PR diff, changed files, CLAUDE.md conventions)
  • Apply skip gate — PR is open and has security-relevant surface (GitHub API path construction, gh api/curl calls, lease/claim writers, git worktree mutation), so a full review was warranted
  • Review diff for security-relevant surface (injection, auth, secrets, Actions workflow risk)
  • Adversarial validation of any candidate findings
  • Post findings (inline + summary) or report clean

No security issues found in this diff.

Notable, already fixed correctly within the PR itself: the consolidation of owner/repo validation into is_owner_repo_pair() in babysit_gh.py preserves the repo not in {".", ".."} path-traversal guard at both call sites in babysit_resolve_thread.py (verify_fix_commit and verify_tracker_item, both of which interpolate repo into a gh api repos/{repo}/... call). The PR description notes this guard was transiently weakened mid-refactor and caught by the fresh-context verifier before this commit; the version in this diff has the guard intact.

Areas specifically checked and found clean:

  • GitHub/Gitea/Linear work-item adapters (create-item.sh, common.sh, list-items.sh, lease/claim/renew-lease scripts): argument handling and jq --arg/--argjson usage properly parameterized, no raw interpolation into shell or JSON.
  • fetch-annotations.sh / fetch-failed-logs.sh: only --help banner-slicing and a jq projection changed; no change to gh api argument construction.
  • apply-plan.sh: the new git_mutate() helper dedupes GIT_TERMINAL_PROMPT=0 GIT_OPTIONAL_LOCKS=0 git ... call sites (branch delete, worktree remove/prune) without changing arguments or trust boundaries.
  • babysit_merge.py: the new _refuse() helper dedupes refusal-envelope construction (owner allowlist, --expected-head SHA-prefix check, autopilot-tier gating); check order and exit codes are unchanged.
  • No .github/workflows/** files are touched in this PR, so no Actions permission/trigger surface to review.

Per the skill's scope, this excludes style/naming/coverage commentary (that's /review:code-review) and defers unpinned-action/permission/template-injection classes to zizmor's static lane.

@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Last security-reviewed head: ee0509d6b7911517db3c0e69430ec9a711da6fec. On the next push, the relevance gate compares only the commits since this SHA; delete this comment to force a full re-review.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ee0509d6b7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread plugins/work-items/skills/onboard-adapter/scripts/generate-adapter.sh Outdated
@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Claude has reviewed this PR 1 time. The lane skips further automatic reviews after 5; deleting this comment resets the count.

An earlier commit on this branch replaced `tr '[:lower:]' '[:upper:]'` with
`${PROVIDER_FUNC^^}` in onboard-adapter/generate-adapter.sh. The case-folding
expansions are bash 4.0+, the script carries no version gate to keep one
behind, and its shebang is `/usr/bin/env bash` - so on a stock macOS, where
that resolves to the system bash 3.2, `^^` is a fatal expansion error and
every valid spec aborts there before an adapter is written.

Reported by an automated reviewer on #3710 as a P1. Verified rather than
taken on trust:

- The rule is the repo's own, not the reviewer's. scripts/check-drive-root-litter.sh
  states it at line 143 and gates its `${var,,}` folds behind a Windows host
  check for exactly this reason; lib/hook-utils.sh documents 3.2+ support.
  plugins/work-items/README.md describes the skills' mechanics as POSIX-shell
  and declares no bash-4 floor.
- Audited the whole branch diff for the same class rather than just the one
  line reported: case-folding, `declare -A`, mapfile/readarray, `&>>`, the
  `${var@X}` transforms, negative array indices, coproc, globstar, `wait -n`,
  `read -N`, `printf '%()T'`. Three case-folding hits, of which two
  (preflight.sh `${p,,}`) are pre-existing - they show as additions only
  because an shfmt reindent moved the whole `case` block, and both appear
  unchanged on the deletion side. One genuine regression, the one reported.

The code is now byte-identical to what it replaced. A comment names the
constraint so a later tidy pass does not re-apply it, matching the note
already in this file for `$(<"$file")`.

Recorded as a Known issue rather than fixed here: check-shell-portability.sh
reasons about GNU-vs-BSD userland, not bash version, so `${var^^}` and
`${var,,}` pass it. That blind spot is why this went green through every
lane. Widening that gate changes the gate's contract, which is not this
PR's to do.

Verification: generate-adapter.test.sh 141/141; affected-tests.sh --run
232 shell suites pass (exit 3, NOT-RUN lanes only, none touched by this
delta); shellcheck, shfmt -d, editorconfig-checker, check-shell-portability
against origin/main, em-dash purge, and all four changelog-parity modes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LRWf55tWpxcSdpxEMh2eJD
`source-control/CHANGELOG.md` carried a Known-issues bullet saying the
`--help` truncation bug "survives in two more scripts" and that they "need a
different terminator" - written mid-sweep, before the fix existed. The fix
shipped later on this same branch: the awk first-non-comment-line terminator
in `lib/resolve-convention-pattern.sh`, propagated to the guardrails copy and
recorded under guardrails 0.31.6. A reader of the merged source-control
changelog alone would have been told an open bug was still open.

Verified before changing it: the bullet names exactly the sync-cluster pair
that 0.31.6 fixes, and its stated reason the blank-line terminator was not
transplantable is the same reason 0.31.6 gives for using awk instead.

Rewritten as past tense and folded into the Fixed entry it belongs to, so the
discovery is still recorded without the stale warning. It is not claimed as a
source-control fix, because it is not one - the cross-reference names where it
landed. No version bump: this edits prose in an entry this branch added, which
has not shipped.

Reported as a non-blocking documentation note by the /review:code-review lane
on #3710, whose broader pass found no correctness or maintainability defects.

Verification: all four changelog-parity modes, em-dash purge,
editorconfig-checker. The file is on the affected-tests no-suite allowlist
(a non-shell CI lane covers it), so no suite maps to it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LRWf55tWpxcSdpxEMh2eJD
@kyle-sexton
kyle-sexton merged commit da70c87 into main Sep 4, 2026
11 checks passed
@kyle-sexton
kyle-sexton deleted the claude/hello-yuwqst branch September 4, 2026 14:56
kyle-sexton pushed a commit that referenced this pull request Sep 4, 2026
…iation

main advanced again while this branch was running: #3710, the competing
repo-wide tidy sweep, merged in full. Far smaller than the first
reconciliation, which is what convergence looks like: 15 overlapping files
against 69 last time, and 10 real conflicts (7 CHANGELOG, 1 plugin.json,
2 source) against 52.

Version and changelog conflicts resolved to main's side again, on the same
verified premise as before: this branch's only edit to any plugin.json is the
"version" line, so nothing of main's is lost. The renumbering follows in the
next commit, because main has since published versions this branch was already
carrying.

The two source conflicts:

- repo-fleet-hygiene/skills/audit/scripts/audit-fleet.test.sh: both sweeps
  independently wrote a comment explaining that the _file assert forms are the
  general ones and the two-argument forms the common case. Same content, two
  placements. Took main's, per the standing rule that equivalent restatements
  resolve to the published base. Resolved the two hunks in place rather than
  taking the whole file, so this branch's earlier content elsewhere survives.

- source-control/skills/pull-request/scripts/fetch-annotations.sh: genuinely
  two-sided. Ours (G85) is one jq pass that projects and filters together; main
  kept two passes with an early exit when there are no check-runs. Took ours,
  because it is the verified side: G85 drove it over 240 payloads against the
  two-pass form it replaced with 0 mismatches, plus a positive control showing
  the dropped intermediate was dead on the --failed path and live on the other.
  The empty case is still handled, one guard lower, and its comment is the more
  accurate of the two ("--failed can legitimately match none"). Adopted main's
  `{id, name, conclusion, status}` shorthand, which jq expands identically.

Worth recording: main independently FIXED the --help truncation defect G85 had
found and deferred in that same file. Its `usage()` now terminates the header
slice on the first blank line (`sed -n '2,/^$/p'`) instead of a hardcoded line
20, so the banner stops swallowing exit codes 1, 2 and 5, and main added two
tests pinning it. That fix sat outside the conflict and merged cleanly.
Verified after resolving: `--help` prints all four documented exit codes and
both new tests pass. The Known-issues entry this branch wrote about it is now
false and is dropped in the renumbering commit rather than left to mislead.

Verification: no conflict markers anywhere in the staged tree; shellcheck and
shfmt -d clean on both resolved files; fetch-annotations.test.sh green
including main's two new --help assertions.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XsxC7nPL8mhm3JXL1rrjNJ
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.

2 participants