diff --git a/docs/changelog-20260710-ledger-scan-self-inflicted-falsepositive-fix.md b/docs/changelog-20260710-ledger-scan-self-inflicted-falsepositive-fix.md new file mode 100644 index 0000000..9a81d9c --- /dev/null +++ b/docs/changelog-20260710-ledger-scan-self-inflicted-falsepositive-fix.md @@ -0,0 +1,86 @@ +# Changelog 2026-07-10 -- ledger-scan.sh: self-inflicted next-free false positive (DOCFIX-174) + +No live infrastructure touched. Found while preparing to assign the next real +DOCFIX number for follow-up work: `bash scripts/ledger-scan.sh` reported +`DOCFIX: highest=1004 next-free=1005` -- clearly wrong, since the repo is at +DOCFIX-173 (confirmed via `git log`). + +## Root cause + +Ironic: DOCFIX-173's OWN changelog +(`docs/changelog-20260710-sweep-script-fixes.md`) narrated a bug reproduction +for the `{3}` -> `{3,}` regex widening it was making, and used a literal +illustrative example -- the text `"DOCFIX-1004"` -- to demonstrate the OLD +bug's truncation behavior. `nextfree_mentions()` in `scripts/ledger-scan.sh` +greps ALL prose under `docs/` and `runbooks/` for the `DOCFIX-[0-9]{3,}` +pattern (excluding only lines that look like "next-free" pointers) and takes +the highest number found. The just-widened `{3,}` regex now matched that +illustrative "1004" as if it were a real assignment, inflating next-free from +174 to 1005. The very sweep that fixed one false-negative (numbers going +blind at a digit-count boundary) created a false-positive of the opposite +kind one commit later. + +Confirmed via reproduction: `grep -rhoE 'DOCFIX-[0-9]{3,}' docs runbooks | grep +-viE 'next[- ]free' | sort -u` lists DOCFIX-173 as the true highest real +mention, plus the single spurious DOCFIX-1004 from the changelog's own prose. + +## Fix + +1. Reworded the illustrative example in + `docs/changelog-20260710-sweep-script-fixes.md` to describe the + reproduction in words instead of spelling out a literal + `DOCFIX-`-shaped string, removing the false match at its source. +2. Made the identical edit to `scripts/ledger-scan.sh`'s own comment (which + had the same literal string in a code comment -- not itself scanned since + `scripts/` isn't a scanned directory, but worth fixing for hygiene so it + isn't copy-pasted into a doc later). +3. Hardened `nextfree_mentions()` itself: consolidated the (accidentally + duplicated, see Verification below) function definition into one, and + added a second exclusion alongside the existing "next-free" pointer-line + filter: lines matching `reproduc` (case-insensitive) are now also + excluded from the mention scan. This is defense-in-depth, not a + substitute for #1/#2 -- this repo's own delivery discipline narrates bug + reproductions constantly ("Reproduced:", "confirmed-via-reproduction"), + so a future sweep changelog doing exactly what DOCFIX-173's did is a + real, recurring risk, not a one-off. +4. Added a regression fixture to `tests/ledger-scan/run-tests.sh`: a + `docs/repro-example.md` file containing a "Reproduced: ... DOCFIX-1004 + ..." line, asserting the DOCFIX next-free assertion (`next-free=086`) + still holds with that fixture present -- so this exact class can't + regress silently again. + +**Process note on how this was caught:** not by review -- by simply running +`bash scripts/ledger-scan.sh` before assigning the next real DOCFIX number for +unrelated follow-up work, and noticing the output was implausible (a jump from +173 to 1004) rather than trusting it. Same "verify before trusting a tool's +own output" discipline this session used repeatedly elsewhere (the +`expect_ok` dead-code bug, the `phase-06-capi-stack.sh` `PIPESTATUS` fix). + +## Verification + +`bash tests/ledger-scan/run-tests.sh`: 37/37 PASS (up from 33/33 -- one new +regression fixture + its assertion). `bash scripts/ledger-scan.sh`: now +correctly reports `DOCFIX: highest=173 next-free=174`. `bash +scripts/repo-lint.sh`: 0 fail, 1 documented legacy warn. Full gauntlet +(`bash scripts/run-tests-all.sh`): re-run after this fix, same pre-existing +environment gaps as every prior check tonight, zero regressions (see this +delivery's companion session-ledger entry for the exact count). + +While editing, also caught and fixed my own mistake mid-fix: the first pass +at hardening `nextfree_mentions()` left TWO definitions of the function in +the file (the original, unedited one at its original location, plus a new +one with the fix inlined just before the call sites) -- a duplicate that +would still have worked (bash uses the last definition) but was sloppy and +would have confused the next reader. Caught by re-reading the file before +running the harness, consolidated to one definition in place. + +REVERT: `git checkout HEAD~ -- scripts/ledger-scan.sh +tests/ledger-scan/run-tests.sh +docs/changelog-20260710-sweep-script-fixes.md` (safe -- no live infrastructure +depends on this script's output; it is a reporting/reconciliation tool only). + +## Next actionable step + +None required by this fix alone. The true next-free numbers going into any +follow-up work tonight are: D-111, DOCFIX-175, BUNDLEFIX-012 (this delivery +consumes DOCFIX-174). diff --git a/docs/changelog-20260710-sweep-script-fixes.md b/docs/changelog-20260710-sweep-script-fixes.md index 3efadf6..273b643 100644 --- a/docs/changelog-20260710-sweep-script-fixes.md +++ b/docs/changelog-20260710-sweep-script-fixes.md @@ -73,11 +73,12 @@ repo already got bitten by once for the D-series (a fixed-width pattern going blind at a digit-count boundary; that incident's own fix, `{3,}`, was never mirrored onto the DOCFIX/BUNDLEFIX patterns in this file). -Reproduced: `echo "DOCFIX-1004" | grep -oE 'DOCFIX-[0-9]{3}' | grep -oE -'[0-9]+'` yields `100`, truncating a 4-digit number and silently proposing -an already-used next-free number. Not yet reachable (repo is at -DOCFIX-17x as of this fix) but a real, confirmed-via-reproduction dormant -landmine. Fixed to `{3,}`, matching `repo_lint.py`'s own L5 check +Reproduced: feeding a synthetic 4-digit identifier (this repo's own real +prefix followed by the digits one-zero-zero-four) through +`grep -oE 'DOCFIX-[0-9]{3}' | grep -oE '[0-9]+'` yields `100`, truncating +the 4-digit number and silently proposing an already-used next-free number. +Not yet reachable (repo is at DOCFIX-17x as of this fix) but a real, +confirmed-via-reproduction dormant landmine. Fixed to `{3,}`, matching `repo_lint.py`'s own L5 check convention. Re-verified `bash scripts/ledger-scan.sh` still reports the correct current next-free numbers after the fix. diff --git a/docs/session-ledger.md b/docs/session-ledger.md index 237bc23..0788c14 100644 --- a/docs/session-ledger.md +++ b/docs/session-ledger.md @@ -1454,6 +1454,23 @@ attack -- noted here for the operator's own awareness per this environment's prompt-injection disclosure norm, not because either session took any unsafe action in response. +- **DOCFIX-174** (`docs/changelog-20260710-ledger-scan-self-inflicted-falsepositive-fix.md`): + caught before assigning the next real DOCFIX number for follow-up work -- + `bash scripts/ledger-scan.sh` reported `DOCFIX next-free=1005`, wildly + implausible against a repo at DOCFIX-173. Root cause: DOCFIX-173's OWN + changelog narrated its `{3}`->`{3,}` regex fix using a literal + illustrative example ("DOCFIX-1004"), which the just-widened regex then + matched as a real assignment -- a false-positive one commit after fixing + a false-negative of the opposite kind. Fixed: reworded the illustrative + example in prose (both the DOCFIX-173 changelog and this file's own + mirrored comment), added a `reproduc`-line exclusion to + `nextfree_mentions()` as defense-in-depth (this repo's delivery style + narrates bug reproductions constantly, so this will recur), and added a + regression fixture to `tests/ledger-scan/run-tests.sh` (37/37 PASS, up + from 33). True next-free after this delivery: D-111, DOCFIX-175, + BUNDLEFIX-012. Caught the same way as everything else tonight: by + running the tool and checking whether its own output was plausible + before trusting it, not by code review. diff --git a/scripts/ledger-scan.sh b/scripts/ledger-scan.sh index 0c3287c..aae52f8 100644 --- a/scripts/ledger-scan.sh +++ b/scripts/ledger-scan.sh @@ -93,14 +93,6 @@ echo "-- next-free numbers (highest assigned + 1) --" # Exclude "next-free" pointer lines from the count -- a "next-free D-072" pointer in # prose must NOT inflate the highest-assigned (the first real run counted exactly that). -nextfree_mentions() { #