fix(pm): drop the phantom # pathspec from os-regen-merge.sh's .gitattributes reader - #15700
Conversation
… reader
`grep 'merge=os-regen' .gitattributes | awk '{print $1}'` also matched the
header comment that quotes the literal in prose and took ITS first field, so
the run-time path list carried a pathspec that was literally `#`. Harmless by
luck — `#` matches no tracked path, so both `git diff --name-only … --
"${regen_paths[@]}"` calls ignored it — but the pattern COUNT printed to the
operator is the only check that the script is reading the right surface, and it
was off by one in the one place the design deliberately keeps no second copy.
Measured on origin/main 95d5cbb: 19 entries produced, 1 literally `#`, 18
real patterns. The repaired reader answers 18, matching both the 18 rows in the
file and `scripts/git-merge-regen.mjs`'s independent reader.
The grammar was measured, not assumed (git 2.43.0, scratch repos): a leading-`#`
line is a comment even when indented, and there is NO inline trailing comment —
git rejects such a row whole and routes nothing. Hence comment lines are dropped
before the field split, and `merge=os-regen` is anchored as a whitespace-
delimited token.
Pinned in the script's own `--self-test`: a bait `.gitattributes` with prose
quoting the literal plus three real rows, asserting the exact printed COUNT (so
an empty population reds too), the git-grammar measurement with a firing
control, and a discriminating mutation that disables the comment skip and
watches the phantom come back.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
|
ACCEPT — PM seat Reviewed against the three-dot diff at What the diff does: Measured by this seat on the PR head (plain Not measured here: the derived family (dev: 23 commands, all exit 0 after install; Fences held: one file; Generated by Claude Code |
Fixes #15497
scripts/pm/os-regen-merge.shbuilt its authoritative run-time path list withgrep 'merge=os-regen' .gitattributes | awk '{print $1}'. That also matched the headercomment which quotes the literal in prose, took ITS first field, and put a pathspec that was
literally
#into the list — carried on into the twogit diff --name-onlycalls that decide,per file, which side step 2 takes, and into the pattern COUNT the script prints to the operator.
Counts, re-derived at pickup (⛔ neither 18 nor 19 from the thread is current)
Measured on
origin/mainat95d5cbb31, running the script's own pipeline verbatim:#merge=os-regenrows in the filediffof the two lists is exactly one line: the leading#. Two independent corroborations of18 — the 18 rows at
.gitattributes:140-157, andnode scripts/git-merge-regen.mjs --self-test,whose own reader is separate from this one:
✓ .gitattributes ↔ regen-artifacts.mjs agree on 18 path(s).The grammar, MEASURED not assumed (git 2.43.0, scratch repos)
Triage asked for the repair to be decided against the file's real grammar rather than against the
two lines that happen to exist today. Three readings:
#line is a comment, and indenting does not undo that.# foo barassignsnothing to a file named
#; the escaped-pattern control\# foo baron that same file assignsfooandbar. So comment lines must be dropped before the field split, and the strip hasto tolerate leading whitespace.
x merge=os-regen # trailing notedoes not routexwith a note on the end — git rejects the whole row:
# is not a valid attribute name: .gitattributes:1on stderr, andgit check-attr merge -- xthen answersunspecified. Sothere is nothing to strip: such a row can never be live. This is the reading the dispatch left
conditional ("and inline trailing comments if admitted"); measured, they are not admitted.
merge=os-regen-v2is a different driver(git confirms:
x: merge: os-regen-v2), not this one.The repair chosen, and why
One
awkin place ofgrep | awk, doing both jobs in the tool that already does the field split:grep '^[^#]': reading 1 says an indented#is still a comment, and
^[^#]admits it.admits a different driver's rows.
in the self-test against real git, so the day git grows one, this decision reddens instead of
rotting.
[ \t]rather than[[:space:]]: the bash 3.2 floor documented a few lines above is a macOSfloor, and that host's awk is not gawk.
scripts/git-merge-regen.mjs's own reader (reconcileAttributes) alreadyapplies — that reader was already comment-safe, so this is the shell side catching up, not a
new convention.
The printed count at what is now
:265therefore equals the number of real patterns: 18.Self-test growth
bash scripts/pm/os-regen-merge.sh --self-test, before and after, both verdicts quoted:✓ os-regen-merge self-test: all cases pass.✓ os-regen-merge self-test: all cases pass.Twelve new cases, in three groups:
.gitattributescarrying prose that quotes the literal (twoshapes: backticked, as the real file's line 36 has it, and whitespace-delimited, the near
neighbour a future edit adds for free) plus exactly three real rows. The assertion is the
exact printed COUNT (
3), never "no entry equals#" — as triage required, a count alsoreds the day the read silently matches ZERO lines, which an absence assertion passes happily.
A companion case shows the fixture really is bait: the pre-repair spelling over-counts that same
file at 5.
with a firing control (the same row without the note DOES route) so a git that stopped reading
.gitattributesat all could not pass the two absence assertions.perl/\Q..\Eliteral-replacement shape case 6balready uses: disable the reader's comment-line skip in a copy of the script, run it against the
bait fixture, and assert the phantom is back and the count is one too high.
Ablation — the pin can fail
Trap-guarded (
trap ... EXIT INT TERM), absolute paths resolved fromgit rev-parse --show-toplevel, mutation proven on disk in both directions:Verdict under ablation —
✗ os-regen-merge self-test: 4 case(s) failed.:Restore leg, verified rather than trusted: worktree blob back to
562c20f19ea10a21ac9e9770e57a36492eed027f, equal to the HEAD blob, andgit diff HEADempty.Gates
Derived on the final head with
node scripts/pm/dispatch-gates.mjs --changed --commands --repo objectstack-ai/objectstack(which re-derives its own change set from the merge base — nohand-built path list): 23 commands, all exit 0. Plus
pnpm check:pm-dispatch-gates→✓ dispatch-gates self-test: 1445 cases pass.(exit 0),pnpm check:nul-bytes(exit 0, and aseparate
grep -naPcontrol-byte scan of the edited file returned exit 1 = none), andnode scripts/git-merge-regen.mjs --self-test(exit 0) as the sibling-reader corroboration.Every exit code was captured before any pipe.
CI's own wiring for this script —
.github/workflows/lint.yml:1364,bash scripts/pm/os-regen-merge.sh --self-test— is exactly the command run above.NOT MEASURED, by name:
pnpm installhad ever run in this worktree:six of them answered
PREREQUISITE NOT MET/ERR_MODULE_NOT_FOUND(exit 3 and 1). Those arereported here as NOT MEASURED, never as reds; all 23 were re-run green after the install.
VERDICT queue-timeout (exit 99) · never acquired— nothing was built, tested or decided by them.
dispatch-gatesfamilies whose argv takes a value from the workflow(
check-cross-package-test-inputs --union-into "$RUNNER_TEMP/...",check-shard-attestation,check-test-completeness) — the tool prints them as⊘ NOT MEASUREDand they belong to CI.
silentverdict is a fact about a list rather than about these paths.
Out of fence
here. One observation for the PM rather than a change: this script's self-test cases 6 and 6b —
which assert the MIXED classification is correct — pass on today's main, both before and
after this PR, so that card's premise is worth re-checking before it is dispatched.
scripts/git-merge-regen.mjswas read and left alone: its reader is not a shared copy of thisone-line construction, it already skips comment lines, and this branch has no reason to touch it.
A latent over-admission in it is filed separately rather than ridden along; see the report.
No changeset: this changes no published package, only PM merge tooling.
skip-changesetapplied.🤖 Generated with Claude Code
https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
Generated by Claude Code