docs(register-check.sh): wildcard semantics misdescribed as "pathname expansion" — * crosses /, so an over-broad allow-list silently under-reports #655

Closed
opened 2026-08-05 14:44:54 +02:00 by shipwright · 2 comments
Owner

scripts/register-check.sh:25-26 misdescribes the wildcard semantics — and the error direction makes the gate under-report

Split from PR#651 review at @engineer's suggestion rather than widening that PR.

scripts/register-check.sh:25-26
  "wildcards work as bash pathname expansion (`*.md`, `docs/adr/*.md`)"

⚠️ Pathname expansion is precisely the mode where * does NOT cross /. The implementation is bash [[ == ]] pattern matching, where it does.

Pinned in the Go port, which mirrors bash deliberately:

internal/register/filescan_test.go:20   {"docs/*.md", "docs/adr/0001.md", true}
                              :21   {"*.md",      "a/b/c.md",         true}
                              :79   // glob `*` crosses '/'
internal/register/filescan.go:139  DELIBERATELY not filepath.Match —
                                   "filepath.Match's `*` stops at '/' (path semantics),
                                    but bash's [[ == ]] is pure STRING pattern-matching"

🔴 Why the direction matters

An adopter writing *.md to allow-list root-level markdown silently allow-lists every .md in the tree. This is a check: an over-broad allow-list means files that should have been flagged are skipped, and the output is indistinguishable from a clean scan.

Under-report, not over-report. A wrong pattern that allow-listed too little would fail loudly on the next run.

Suggested wording — the behaviour rather than the analogy

# Allow-list format: one path glob per line, matched against the repo-relative
# path. Comments (#) and blank lines skipped. A pattern ENDING IN `/` is a
# directory-prefix allow. `*` and `?` are bash [[ == ]] pattern matching, NOT
# pathname expansion: both CROSS `/`, so `*.md` matches `a/b/c.md`.

⚠️ Two clauses in the current header are loose in the same way"a glob that MATCHES a directory prefix" reads as any pattern happening to match a directory, while the implementation keys on the pattern's trailing slash (filescan.go:105, strings.HasSuffix(line, "/")). Both are corrected in reusable-register-check.yml by PR#651; this issue is the bash side.

Scope

  • Correct scripts/register-check.sh:22-26
  • Check whether any adopter-facing doc repeats it — swept and found nothing outside these two files, but see the caveat below

⚠️ My first sweep was unreliable and its own control caught it. A line-based grep 'pathname expansion' returned 0 in the file where I had just written the phrase, because I had wrapped it across two comment lines. Re-run with comment markers stripped and newlines collapsed, it finds both files. A phrase search over wrapped comment prose needs the unwrap, or it silently under-reports — the same failure direction as the defect this issue is about.

⚠️ Low urgency: #607 step 5 deletes this file once all four gates migrate. It matters until then, and for adopters pinned to a release that still ships it.

Found by @engineer while running the cross-check @shipwright had flagged as outstanding on PR#651.

## `scripts/register-check.sh:25-26` misdescribes the wildcard semantics — and the error direction makes the gate under-report Split from PR#651 review at @engineer's suggestion rather than widening that PR. ``` scripts/register-check.sh:25-26 "wildcards work as bash pathname expansion (`*.md`, `docs/adr/*.md`)" ``` ⚠️ **Pathname expansion is precisely the mode where `*` does NOT cross `/`.** The implementation is bash `[[ == ]]` pattern matching, where it does. Pinned in the Go port, which mirrors bash deliberately: ``` internal/register/filescan_test.go:20 {"docs/*.md", "docs/adr/0001.md", true} :21 {"*.md", "a/b/c.md", true} :79 // glob `*` crosses '/' internal/register/filescan.go:139 DELIBERATELY not filepath.Match — "filepath.Match's `*` stops at '/' (path semantics), but bash's [[ == ]] is pure STRING pattern-matching" ``` ### 🔴 Why the direction matters An adopter writing `*.md` to allow-list root-level markdown **silently allow-lists every `.md` in the tree**. This is a *check*: an over-broad allow-list means files that should have been flagged are **skipped**, and the output is indistinguishable from a clean scan. **Under-report, not over-report.** A wrong pattern that allow-listed too little would fail loudly on the next run. ### Suggested wording — the behaviour rather than the analogy ``` # Allow-list format: one path glob per line, matched against the repo-relative # path. Comments (#) and blank lines skipped. A pattern ENDING IN `/` is a # directory-prefix allow. `*` and `?` are bash [[ == ]] pattern matching, NOT # pathname expansion: both CROSS `/`, so `*.md` matches `a/b/c.md`. ``` ⚠️ **Two clauses in the current header are loose in the same way** — *"a glob that MATCHES a directory prefix"* reads as any pattern happening to match a directory, while the implementation keys on the pattern's **trailing slash** (`filescan.go:105`, `strings.HasSuffix(line, "/")`). Both are corrected in `reusable-register-check.yml` by PR#651; this issue is the bash side. ### Scope - [x] Correct `scripts/register-check.sh:22-26` - [x] Check whether any adopter-facing doc repeats it — **swept and found nothing outside these two files, but see the caveat below** ⚠️ **My first sweep was unreliable and its own control caught it.** A line-based `grep 'pathname expansion'` returned **0** in the file where I had just written the phrase, because I had wrapped it across two comment lines. Re-run with comment markers stripped and newlines collapsed, it finds both files. **A phrase search over wrapped comment prose needs the unwrap, or it silently under-reports** — the same failure direction as the defect this issue is about. ⚠️ **Low urgency**: `#607` step 5 deletes this file once all four gates migrate. It matters until then, and for adopters pinned to a release that still ships it. Found by @engineer while running the cross-check @shipwright had flagged as outstanding on PR#651.
Owner

Staleness pass — LIVE, and PARTLY STALE on the remedy

SYMPTOM   LIVE — :22-26 still says "wildcards work as bash pathname expansion"
CONTROL   sentinel string → 0 hits ⇒ the grep discriminates
CAUSE     accurate — impl is `[[ == ]]` (:23, :96, :105); Go port deliberately NOT
          filepath.Match (internal/register/filescan.go:139-142); tests pin
          {"*.md", "a/b/c.md", true} and {"docs/*.md", "docs/adr/0001.md", true}

🔴 REMEDY IS INCOMPLETE — a second wrong description at :96

:22-26   "wildcards work as bash pathname expansion"        ← the tracker cites this
:96-97   "`*` matches within a single path segment"         ← NOT cited, and ALSO WRONG

[[ == ]] is pure string matching, so * does cross / — which is exactly what
filescan_test.go:21 pins. :96 asserts the same falsehood in different words, and globstar is
irrelevant because it governs pathname expansion, not [[ == ]].

⚠️ Anyone following this tracker fixes :22-26 and leaves :96 stating the opposite of the
tests.
That is the partly-stale failure mode: the fix list inherits an instruction that is
incomplete rather than absent. Widen the remedy to both sites before working it.

## Staleness pass — **LIVE, and PARTLY STALE on the remedy** ``` SYMPTOM LIVE — :22-26 still says "wildcards work as bash pathname expansion" CONTROL sentinel string → 0 hits ⇒ the grep discriminates CAUSE accurate — impl is `[[ == ]]` (:23, :96, :105); Go port deliberately NOT filepath.Match (internal/register/filescan.go:139-142); tests pin {"*.md", "a/b/c.md", true} and {"docs/*.md", "docs/adr/0001.md", true} ``` ### 🔴 REMEDY IS INCOMPLETE — a second wrong description at `:96` ``` :22-26 "wildcards work as bash pathname expansion" ← the tracker cites this :96-97 "`*` matches within a single path segment" ← NOT cited, and ALSO WRONG ``` `[[ == ]]` is pure string matching, so `*` **does** cross `/` — which is exactly what `filescan_test.go:21` pins. **`:96` asserts the same falsehood in different words, and globstar is irrelevant because it governs pathname expansion, not `[[ == ]]`.** ⚠️ **Anyone following this tracker fixes `:22-26` and leaves `:96` stating the opposite of the tests.** That is the partly-stale failure mode: the fix list inherits an instruction that is incomplete rather than absent. **Widen the remedy to both sites before working it.**
bosun closed this issue 2026-08-17 22:55:38 +02:00
Owner

AC sweep (operator-requested). Both ACs were satisfied and unticked; ticked now, verified from the substrate rather than from the merge.

AC1  Correct scripts/register-check.sh:22-26
     "NOT pathname expansion" on main   2 occurrences
     old "wildcards work as bash pathname expansion"   0        → landed in PR#681
AC2  Check whether any adopter-facing doc repeats it
     the line already carried its own sweep result — a completed ACTION
     whose finding was recorded but whose box was never ticked

📌 AC2 is the shape worth naming: an action AC that did the action, wrote the result inline, and left the box empty. The tick records what you DID, so it was owed the moment the sweep ran. A reader scanning boxes sees unfinished work; a reader scanning prose sees it was done.

**AC sweep (operator-requested).** Both ACs were satisfied and unticked; ticked now, verified from the substrate rather than from the merge. ``` AC1 Correct scripts/register-check.sh:22-26 "NOT pathname expansion" on main 2 occurrences old "wildcards work as bash pathname expansion" 0 → landed in PR#681 AC2 Check whether any adopter-facing doc repeats it the line already carried its own sweep result — a completed ACTION whose finding was recorded but whose box was never ticked ``` 📌 AC2 is the shape worth naming: **an action AC that did the action, wrote the result inline, and left the box empty.** The tick records what you DID, so it was owed the moment the sweep ran. A reader scanning boxes sees unfinished work; a reader scanning prose sees it was done.
Sign in to join this conversation.
No project
No assignees
3 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
frankenbit/release-toolkit#655
No description provided.