Problem
scripts/pr-contract.mjs PLACEHOLDER_RE (line 19) matches ambiguous English-phrase tokens — NOT YET, N/A, NONE YET, TBD, TODO, FIXME, FILL ME, FILL IN, REPLACE THIS — anywhere in the text via \b…\b. These words occur in legitimate completed prose, so the gate false-positives on real Summary/section content.
Observed: a PR whose ## Summary read "hydrology is not yet wired in" failed two ways and cost two extra round-trips:
placeholder_text body error (validateBody, ~line 199) — body contains "not yet".
empty_summary — hasSubstantiveContent runs the same regex (~line 449), so a Summary full of real prose is declared empty.
This is the second occurrence of the class ecosystem-wide; the sibling detector repo-template/scripts/close/lib.mjs PLACEHOLDER_RE was flagged for the same false-positive on 2026-07-11 (repo-template .claude/friction.md) and is being fixed in parallel.
Relationship to #103
Complementary, not conflicting. #103 wants the placeholder-word filler detector broadened (it's too narrow — misses "placeholder summary goes here"). This issue is the opposite axis: the ambiguous status tokens are too broad (anywhere-match). This fix does not touch the placeholder-word branch (hasLiteralPlaceholderFiller), so #103's self-referential-prose constraint is preserved. The line-anchoring framework added here also makes #103 straightforward to implement later.
Proposed fix
Split PLACEHOLDER_RE into two classes:
STRICT_PLACEHOLDER_RE — pure markers, never valid prose, match anywhere: #___, #<…>, <set-before-merge>.
LINE_PLACEHOLDER_RE — ambiguous tokens, match only as the leading marker of a line (after stripping list/checkbox/Label: prefixes via the existing normalizePlaceholderCandidate): TODO, TBD, FIXME, FILL ME, FILL IN, REPLACE THIS, NOT YET, N/A, NONE YET.
Line-leading (not pure whole-line) is required so the template's own TODO: <instruction> leftovers and lazy standalone N/A/TBD fills are still caught, while mid-sentence prose passes.
Acceptance criteria
- Prose containing
not yet / N/A / TBD mid-sentence in Summary/section bodies passes the contract.
- Leading/standalone placeholders still fail:
TODO: …, - [ ] TODO: …, command: TODO, standalone N/A / TBD, TODO: Closes #___.
- Existing "rejects placeholder scaffolds" test still passes.
- New vitest coverage for both the false-positive-now-passes and the still-fails cases.
- Consumers pick the change up via the normal @v1 retag + re-vendor chain (repo-template re-vendor + sha256 manifest bump, archon-setup re-snapshot).
Source: /page-gm incident 2026-07-13 (CallId gm-20260713-113508-0f18a5d4).
Problem
scripts/pr-contract.mjsPLACEHOLDER_RE(line 19) matches ambiguous English-phrase tokens —NOT YET,N/A,NONE YET,TBD,TODO,FIXME,FILL ME,FILL IN,REPLACE THIS— anywhere in the text via\b…\b. These words occur in legitimate completed prose, so the gate false-positives on real Summary/section content.Observed: a PR whose
## Summaryread "hydrology is not yet wired in" failed two ways and cost two extra round-trips:placeholder_textbody error (validateBody, ~line 199) — body contains "not yet".empty_summary—hasSubstantiveContentruns the same regex (~line 449), so a Summary full of real prose is declared empty.This is the second occurrence of the class ecosystem-wide; the sibling detector
repo-template/scripts/close/lib.mjsPLACEHOLDER_REwas flagged for the same false-positive on 2026-07-11 (repo-template.claude/friction.md) and is being fixed in parallel.Relationship to #103
Complementary, not conflicting. #103 wants the
placeholder-word filler detector broadened (it's too narrow — misses "placeholder summary goes here"). This issue is the opposite axis: the ambiguous status tokens are too broad (anywhere-match). This fix does not touch theplaceholder-word branch (hasLiteralPlaceholderFiller), so #103's self-referential-prose constraint is preserved. The line-anchoring framework added here also makes #103 straightforward to implement later.Proposed fix
Split
PLACEHOLDER_REinto two classes:STRICT_PLACEHOLDER_RE— pure markers, never valid prose, match anywhere:#___,#<…>,<set-before-merge>.LINE_PLACEHOLDER_RE— ambiguous tokens, match only as the leading marker of a line (after stripping list/checkbox/Label:prefixes via the existingnormalizePlaceholderCandidate):TODO,TBD,FIXME,FILL ME,FILL IN,REPLACE THIS,NOT YET,N/A,NONE YET.Line-leading (not pure whole-line) is required so the template's own
TODO: <instruction>leftovers and lazy standaloneN/A/TBDfills are still caught, while mid-sentence prose passes.Acceptance criteria
not yet/N/A/TBDmid-sentence in Summary/section bodies passes the contract.TODO: …,- [ ] TODO: …,command: TODO, standaloneN/A/TBD,TODO: Closes #___.Source: /page-gm incident 2026-07-13 (CallId gm-20260713-113508-0f18a5d4).