Skip to content

[finding] scripts/pm/os-regen-merge.sh admits a phantom pathspec — its os-regen list is 18 entries where .gitattributes has 17 real patterns #15497

Description

@claude

Observation-class finding. ⛔ Ungraded and unrouteddomain:*, priority and type are triage's. Filed unassigned.

Surfaced during the merge round on PR #15395 (card #14147) by the dev agent, which deliberately did not file it and handed it to the PM instead, on the reasoning that a defect in PM-owned tooling is a filing judgment its dispatch did not cover. That call was right. Everything below was re-measured by the domain:engine seat at today's origin/main before filing, rather than relayed.

The defect

scripts/pm/os-regen-merge.sh:220 builds its authoritative merge=os-regen path list with:

done < <(grep 'merge=os-regen' .gitattributes | awk '{print $1}')

.gitattributes:36 is a comment that quotes the literal string in prose:

# `merge=os-regen` hands those paths to `scripts/git-merge-regen.mjs`, which does

grep matches it, awk takes its first field, and the list gains a pathspec that is literally #.

Measured at origin/main, reproducing the script's own pipeline:

total entries produced: 18
entries that are literally '#': 1

17 real patterns, reported as 18. The script prints the wrong number at :225
("→ os-regen paths (from .gitattributes, ${#regen_paths[@]} patterns):") and carries # into the
two git diff --name-only … -- "${regen_paths[@]}" pathspecs at :239-240 that decide, per file,
which side step 2 takes.

Why it is worth a row even though this run was unharmed

Harmless here: # matches no tracked path, so both git diffs ignore it and the per-file
decisions were unaffected. The reason to record it is what the script says about this list, at
:125-126:

The os-regen path list is read from .gitattributes AT RUN TIME — the one copy that cannot rot is the one that does not exist.

That claim is about staleness, and it holds. But the list is one entry impure at birth, so the
printed pattern count — the number an operator reads to confirm the script is looking at the right
surface — is off by one, in the one place the design deliberately has no second copy to check
against. ⚠️ A future .gitattributes comment mentioning the literal adds another phantom, and a
comment whose first field happened to be a real path prefix would add a pathspec that matches
something
.

Two repairs, neither of them chosen here

  • anchor the match to a line start: grep '^[^#]' .gitattributes | grep 'merge=os-regen' | …
  • or strip comment lines before the field split.

⚠️ Whoever takes it should decide which, and should re-run bash scripts/pm/os-regen-merge.sh --self-test
— the script ships one, and this list's construction is exactly the kind of thing a self-test can pin
(a fixture .gitattributes carrying a comment that quotes the literal, asserting the pattern count).

Not this

⛔ Not #14671, which is the other known defect in this same script (MIXED-conflict misreporting,
referenced from the domain:services seat post #6021). Different mechanism, different line.

Dedup: ⚠️ /search/issues answers 403 in this container, and a search's empty result is not a
reading — so this was a repo-scoped REST list of 563 open cards plus a local grep. Cards
mentioning os-regen-merge: #6017, #6021, #6024 — all three are PM seat posts, not cards, and
none names this mechanism. os-regen also hits #14977 (a strictness-ledger row, unrelated).
gitattributes hits #6023. The walk's firing control: it returned #15090, a card known to be open.


Generated by Claude Code

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions