Skip to content

fix(pr-contract): placeholder detector false-positives on valid prose ('not yet', 'N/A') — anchor ambiguous tokens to line-leading #112

Description

@ArchonVII

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 THISanywhere 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:

  1. placeholder_text body error (validateBody, ~line 199) — body contains "not yet".
  2. empty_summaryhasSubstantiveContent 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).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions