From 2bd1d123693611288e5d360cfea0408d985df5ff Mon Sep 17 00:00:00 2001 From: sim Date: Wed, 12 Aug 2026 22:54:30 -0400 Subject: [PATCH] fix(#3308): compare guard-produced file fields against POSIX-normalized paths in Windows CI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR #3402's real Windows CI (windows-latest node22/24) caught what gsd-test's Linux-only lanes structurally cannot: tests/planning-snapshot-bypass-drift.test.cjs compared the guard's own POSIX-normalized output (findSnapshotBypassDrift's `file` field, dedupeViolationsForBaseline/sortEntries entries, a written baseline read back from disk) against REGISTERED_FILE, which is built via path.join('src', 'verify.cts') and is therefore backslash-separated on Windows. The guard always normalizes its OUTPUT to POSIX via toPosixRel regardless of the input separator form, so the comparison only ever coincidentally passed on POSIX. Adds REGISTERED_FILE_POSIX for every assertion against a guard-PRODUCED value (including baseline fixtures fed into diffAgainstBaseline, which are matched by exact string key against the guard's normalized output). REGISTERED_FILE itself is unchanged and still used, correctly, everywhere it is the relPath INPUT to findSnapshotBypassDrift or a DIAGNOSTIC_RULE_FUNCTIONS Map-key lookup — both need the platform-native form to match the guard's own Map key, which is also path.join-constructed. No behavior change on POSIX (both constants are byte-identical there). --- tests/planning-snapshot-bypass-drift.test.cjs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/tests/planning-snapshot-bypass-drift.test.cjs b/tests/planning-snapshot-bypass-drift.test.cjs index 1fff7fe2b..140e82345 100644 --- a/tests/planning-snapshot-bypass-drift.test.cjs +++ b/tests/planning-snapshot-bypass-drift.test.cjs @@ -40,6 +40,14 @@ const fs = require('node:fs'); const path = require('node:path'); const REGISTERED_FILE = path.join('src', 'verify.cts'); +// The guard's OWN output (found/file fields, baseline entries) is always +// POSIX-normalized via toPosixRel, regardless of the separator form its +// `relPath` input used — comparing against the platform-native +// REGISTERED_FILE is correct for the guard's INPUT (the DIAGNOSTIC_RULE_FUNCTIONS +// Map key is also path.join-constructed, so an exact Map.has() lookup needs +// this form) but wrong for anything the guard PRODUCED, which this constant +// is for. +const REGISTERED_FILE_POSIX = REGISTERED_FILE.replace(/\\/g, '/'); const REGISTERED_FN = 'cmdValidateHealth'; // A minimal fixture carrying one registered diagnostic-rule-shaped function @@ -72,12 +80,12 @@ describe('findSnapshotBypassDrift — G1/G3: registered function raw-read detect assert.strictEqual(out[0].line, 2); assert.strictEqual(out[0].found, 'platformReadSync('); assert.strictEqual(out[0].text, 'const raw = platformReadSync(x);'); - assert.strictEqual(out[0].file, REGISTERED_FILE); + assert.strictEqual(out[0].file, REGISTERED_FILE_POSIX); }); test('G1: a registered-function violation present in the baseline is KNOWN — neither fresh nor stale', () => { const violations = findSnapshotBypassDrift(fixtureSource(), REGISTERED_FILE); - const baseline = [{ file: REGISTERED_FILE, text: 'const raw = platformReadSync(x);' }]; + const baseline = [{ file: REGISTERED_FILE_POSIX, text: 'const raw = platformReadSync(x);' }]; const { fresh, stale } = diffAgainstBaseline(violations, baseline); assert.deepStrictEqual(fresh, []); assert.deepStrictEqual(stale, []); @@ -116,7 +124,7 @@ describe('findSnapshotBypassDrift — G2: function-scoped detection (not whole-f describe('diffAgainstBaseline — G4: stale entries', () => { test('a baseline entry whose (file, text) pair no longer appears in a fresh scan is reported under stale', () => { - const baseline = [{ file: REGISTERED_FILE, text: 'const raw = platformReadSync(oldSite);' }]; + const baseline = [{ file: REGISTERED_FILE_POSIX, text: 'const raw = platformReadSync(oldSite);' }]; const violations = findSnapshotBypassDrift(fixtureSource(), REGISTERED_FILE); const { fresh, stale } = diffAgainstBaseline(violations, baseline); // The baseline's own (unmatched) entry is stale... @@ -136,7 +144,7 @@ describe('writeBaseline / dedupeViolationsForBaseline — G5: regeneration match const deduped = dedupeViolationsForBaseline(violations); const sorted = sortEntries(deduped); assert.strictEqual(sorted.length, 1); - assert.strictEqual(sorted[0].file, REGISTERED_FILE); + assert.strictEqual(sorted[0].file, REGISTERED_FILE_POSIX); assert.strictEqual(sorted[0].text, 'const raw = platformReadSync(x);'); assert.strictEqual(sorted[0].count, 1); // A freshly-written baseline must itself be immediately KNOWN (not @@ -155,7 +163,7 @@ describe('writeBaseline / dedupeViolationsForBaseline — G5: regeneration match const writtenPath = path.join(root, 'scripts', 'baselines', 'planning-snapshot-bypass-baseline.json'); const written = JSON.parse(fs.readFileSync(writtenPath, 'utf8')); assert.strictEqual(written.entries.length, 1); - assert.strictEqual(written.entries[0].file, REGISTERED_FILE); + assert.strictEqual(written.entries[0].file, REGISTERED_FILE_POSIX); assert.strictEqual(written.entries[0].text, 'const raw = platformReadSync(x);'); assert.strictEqual(written.entries[0].count, 1); // The written baseline immediately reconciles with the pass that produced