fix(#2128): address review — migrate 9 mis-allowlisted sites, harden scanner + guards

Correctness review of the Phase 4 guard found the allowlist over-broad and the
scanner/guards evadable. Fixed all findings:

- Migrate 9 sites that were wrongly sanctioned: their regex is the PURE canonical
  token (`\d+[A-Z]?(?:\.\d+)*`, no variant), byte-identical to already-migrated
  siblings. The old justification argued against swapping to the extractPhaseToken()
  FUNCTION (behavior-risky) — but the guard only wants the same regex built from
  the SOURCE string (byte-equal, zero risk). Coverage is now 32 migrated / 5
  sanctioned, not the overstated 23 / 14 (audit.cts x3, uat.cts, init.cts x4,
  roadmap-upgrade.cts). Each conversion proven byte-equal (.source + .flags).
- Harden the drift detector: also catch the `[0-9]`-in-place-of-`\d` variant;
  document the accepted limits (cross-line split, semantic restructuring —
  covered by the identity guard + review, not a text scan).
- Sanction robustness: a `phase-id-owner:` marker now counts only inside a `//`
  comment (a bare substring in a string no longer suppresses a real flag), and
  the preceding-line window skips blank lines (an auto-formatter's blank line no
  longer reactivates the flag).
- roadmap-parser.cts:462 comment: corrected — that regex carries no /i flag, so
  its [A-Za-z] class does real case work (matches state.cts:1409's rationale).
- Identity guard: surface require failures instead of silently skipping, and
  floor coverage at >75% of consumer modules (inspects 156/157).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Tom Boucher
2026-07-10 09:14:15 -04:00
parent dfad3a7510
commit e2eaa5b046
7 changed files with 67 additions and 36 deletions

View File

@@ -175,8 +175,7 @@ function extractPhaseNumFromDir(dirName: string): string | null {
const stripped = stripProjectCodePrefix(dirName);
// Matches: digits + optional letter + optional decimal suffix, followed by '-' or end.
// e.g. "02.1-hotfix" → "02.1", "01-setup" → "01"
// phase-id-owner: strips a leading phase number from a dir name; extractPhaseToken returns the project-code-prefixed token, so it is not a behavior-preserving drop-in.
const m = stripped.match(/^(\d+[A-Z]?(?:\.\d+)*)(?:-|$)/i);
const m = stripped.match(new RegExp(`^(${PHASE_NUMBER_TOKEN_SOURCE})(?:-|$)`, 'i'));
return m ? m[1] : null;
}