refactor(scripts): define the list-file format once, and close a third twin fail-open - #3181
Merged
Merged
Conversation
…d twin fail-open Route-lane finding 7 of 7 from the /coupling:reduce dry-run over scripts/ (#2914, tracked as #3161). The premise needed correcting first The finding described "one line format, five bespoke parsers". Scoping it found EIGHT parsers in FOUR spellings across TWO genuinely different semantics, so a single read_active_lines would have been actively wrong: inline `sed -E 's/#.*//'` (check-docs-only, check-orphaned-fixtures, check-shell-portability's skill-md baseline) and the equivalent bash `${line%%#*}` (check-changelog-parity, affected-tests). A `#` ANYWHERE starts a comment. leading the awk `FNR == NR` loaders in both portability scanners, plus check-hook-userconfig-argv. A `#` comments only at line start. `leading` is not a bug. Token-list entries are EREs and a regex may legitimately contain `#`; applying the inline rule to a token list truncates such a pattern, and a truncated or emptied pattern is a gate enforcing less than it reports. So `--comments` is required and has no default, the same call-site-explicit shape #3144 used for `--include-deleted`. Measured before the change: no active line in any of the fifteen data files carries a non-leading `#`, so the divergence was LATENT, not live. This makes it stay that way. check-hook-userconfig-argv.sh turned out to disagree with ITSELF: `allowed()` grep-matched raw lines with no comment stripping and no trimming, while the stale-entry guard below it skipped leading-`#` lines and stripped a CR. The allowlist is now read once and both consumers share the result. The third twin fail-open, found while migrating and fixed here check-shell-portability.sh has refused an empty active pattern set since #1513 (`np == 0 && ncls == 0` -> exit 2). check-skill-portability.sh had no such guard. Measured, with a discriminating control on the same fixture: check-skill-portability real tokens=1 (caught) all-comments list=0 <-- gates nothing check-shell-portability real tokens=1 (caught) all-comments list=2 That is the #1513 shape a third time, after the awk-operand guard (#1513) and the token-list operand guard (#2914 finding 2): a protection added to one twin and never propagated. Resolving the list in the shell puts the guard in one place for both. Both suites now assert it, the skill-portability one with a control proving the fixture really does flag under a real list. Scope scripts/lib/read-list.sh, sourced-only, nameref out-array, every internal local `_rl_`-prefixed for the correctness reason #3144 measured. All eight parsers migrated. Both awk loaders now receive a pre-filtered list and keep only their scanner-specific logic (check-shell-portability's `!class` dispatch). Tests New scripts/lib/read-list.test.sh, 29 assertions, wired into the plugin-gate lane. The two modes are each asserted against the OTHER mode's answer on the same input, so a change that quietly collapses them fails rather than passing. Three suites gained the stage_libs fixture helper they now need; three existing ones stage the new lib. 647 assertions green: 335 + 92 + 82 + 44 + 29 + 21 + 19 + 13 + 13. shellcheck (repo rcfile), shfmt, actionlint, typos, editorconfig clean; every gate touched here run against its own diff; exec-bit gate replicated repo-wide. Refs #3161 Refs #2914 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q6Xm8Swd6ELYid3fiDCRq3
Contributor
|
🔒 Lane claim (melo-lap-001-merge-20260823T045045Z, autopilot, PR-queue coordinator): checking readiness and merge-gate now. Head pinned; will yield if it moves. No recent foreign activity observed. |
7 tasks
This was referenced Aug 23, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #3161
Summary
Route-lane finding 7 of 7 from the
/coupling:reducedry-run overscripts/(#2914). Extracts the list-file format intoscripts/lib/read-list.sh— and, while migrating, found and fixed a live fail-open incheck-skill-portability.sh.Two things differ from the filed finding, both because scoping it turned up more than the ledger recorded. Details below.
Fix
The premise needed correcting: two formats, not one
The finding described "one
# comment/blank-strippedline format, five bespoke parsers". There are eight parsers in four spellings across two genuinely different semantics:#anywhere starts a commentsed -E 's/#.*//'check-docs-only,check-orphaned-fixtures,check-shell-portability(skill-md baseline)${line%%#*}check-changelog-parity,affected-tests#comments only at line startFNR == NRcheck-shell-portability,check-skill-portability(token lists)check-hook-userconfig-argvleadingis not a bug. Token-list entries are EREs, and a regex may legitimately contain#; applying the inline rule to a token list truncates such a pattern, and a truncated or emptied pattern is a gate enforcing less than it reports. A singleread_active_lineswould therefore have been actively wrong.--commentsis required with no default — the same call-site-explicit shape #3144 used for--include-deleted.Measured before changing anything: no active line in any of the fifteen data files carries a non-leading
#, so the divergence was latent, not live. Stated plainly rather than oversold — the value here is that the two semantics currently exist only inside their parsers.check-hook-userconfig-argv.shturned out to disagree with itself:allowed()grep-matched raw lines with no comment stripping and no trimming (so an entry with trailing whitespace silently never matched), while the stale-entry guard below it skipped leading-#lines and stripped a CR. The allowlist is now read once and both consumers share the result.A third twin fail-open, found while migrating
check-shell-portability.shhas refused an empty active pattern set since #1513 (np == 0 && ncls == 0→ exit 2).check-skill-portability.shhad no such guard. Measured, with a discriminating control on the same fixture:That is the #1513 shape a third time — after the awk-operand guard (#1513) and the token-list operand guard (#2914 finding 2): a protection added to one twin and never propagated to the other. Resolving the list in the shell puts the guard in one place for both.
Scope
scripts/lib/read-list.shis sourced-only, returns through a nameref, and prefixes every internal local_rl_— the correctness requirement #3144 measured, not a naming style. All eight parsers migrated. Both awk loaders now receive a pre-filtered list and keep only their scanner-specific logic (check-shell-portability's!classdispatch).Deliberately no escape syntax: a
\#escape in inline mode was considered and rejected — no current file needs it, adding it would silently change how an existing entry parses, and the leading mode already covers "the#is data". Recorded in the file, per the issue's acceptance criteria.Verification
The fail-open, before and after — same fixture, discriminating control:
New suite —
scripts/lib/read-list.test.sh, 29 assertions, wired into theplugin-gatelane. The two modes are each asserted against the other mode's answer on the same input, so a change that quietly collapses them fails rather than passing:It also asserts the nameref hazard (an out-array named
file,mode,line, orout), loud failure on an unreadable file, and — as a liveness check — that the real shipped token list and docs-only allowlist still yield non-empty results under the mode their consumer uses.Both portability suites gained an empty-token-list regression test, the skill-portability one with a control proving the fixture really does flag under a real list.
Three suites gained the
stage_libsfixture helper they now need; three existing ones stage the new lib. That consequence was predicted in #3160 when these items were filed.647 assertions green:
check-shell-portability.test.shcheck-skill-portability.test.shcheck-changelog-parity.test.shaffected-tests.test.shscripts/lib/read-list.test.sh(new)check-docs-only.test.shscripts/lib/changed-files.test.shcheck-orphaned-fixtures.test.shcheck-changed-skills.test.shcheck-hook-userconfig-argv.test.shshellcheck --rcfile=.shellcheckrc -x,shfmt -d,actionlint,typos,editorconfig-checkerall clean on the staged diff. Every gate modified here was run against its own diff. The exec-bit gate was replicated repo-wide before pushing (0 violations) — it caught me on the previous PR.No plugin manifest is touched, so no version bump or CHANGELOG entry applies;
--check-bumpconfirms.Related
scripts/lib/precedent, the nameref-prefix requirement, and thestage_libsfixture pattern.Generated by Claude Code