fix(#1571): resolve schema-drift phase by token, not substring (#1640)

* fix(#1571): resolve schema-drift phase by token, not substring

verify schema-drift <phase> resolved the phase directory with a naive
entry.name.includes(phaseArg) test, so a non-existent phase could
silently match a different phase whose directory name merely contained
the requested token (e.g. "1" matched "11-expansion"), running the drift
gate against the wrong phase. Use the canonical phaseTokenMatches +
normalizePhaseName, matching find-phase, verify phase-completeness, and
this file's own unstarted-phase check.

Regression coverage folded into tests/schema-drift.test.cjs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore(#1571): add changeset for schema-drift token-match fix

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Behruz Nassre Esfahani
2026-06-24 14:03:01 -07:00
committed by GitHub
parent 51c5497460
commit 3870fafe74
3 changed files with 78 additions and 1 deletions

View File

@@ -2055,10 +2055,17 @@ function cmdVerifySchemaDrift(
return;
}
// Resolve the phase directory with the canonical phase-token matcher
// (phase-id.cjs), not a naive substring test. A bare `.includes(phaseArg)`
// lets a non-existent phase silently match a different phase whose directory
// name merely contains the requested token (e.g. "1" matching "11-expansion"),
// making the drift gate inspect the wrong phase. This mirrors find-phase /
// verify phase-completeness, which both use phaseTokenMatches. (#1571)
let phaseDir: string | null = null;
const normalizedPhase = normalizePhaseName(phaseArg);
const entries = fs.readdirSync(phasesDir, { withFileTypes: true });
for (const entry of entries) {
if (entry.isDirectory() && entry.name.includes(phaseArg)) {
if (entry.isDirectory() && phaseTokenMatches(entry.name, normalizedPhase)) {
phaseDir = path.join(phasesDir, entry.name);
break;
}