From 4036470ca0899cfd35c2946384be44e842b74350 Mon Sep 17 00:00:00 2001 From: sim Date: Wed, 12 Aug 2026 07:40:34 -0400 Subject: [PATCH] fix(#3339): consolidated helper silently overwrote an unrelated module-scope function The runVerifiedPhaseComplete(args, tmpDir) consolidation in the prior commit hoisted a `function` declaration into a bare (sloppy-mode) block. Annex B legacy hoisting semantics mean a block-scoped function declaration in sloppy mode also reassigns any enclosing var of the same name the instant the block executes -- and this file already had an unrelated module-scope runVerifiedPhaseComplete(args, tmpDir, env) at line 54, used by ~50 other call sites throughout the file. The block ran before any test() body did, so every one of those 50 call sites was silently pointed at the consolidated helper's different phase-matching logic (parseInt-based, loses dotted decimal sub-phase segments) instead of the real one (normalizePhaseToken/phaseTokenFromDirName-based), breaking multi-level decimal phases like 03.2.1. Fixed by wrapping the consolidated helper in a strict-mode IIFE, which disables Annex B hoisting and restores real block scoping. Caught by a genuine gsd-test failure (2 unique failures, both throw- class, both in phase-completion verification-gate behavior) -- root- caused via diff against the pre-consolidation commit and a scoped local node --test run confirming the fix, not asserted from a hunch. No test() count changed. No production code touched. --- tests/phase.test.cjs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/tests/phase.test.cjs b/tests/phase.test.cjs index b596966a1..9f639b8a5 100644 --- a/tests/phase.test.cjs +++ b/tests/phase.test.cjs @@ -10495,7 +10495,18 @@ describe('#2572: phase complete warns when a SUMMARY claims files that never lan // Outer scope shared by the two fold blocks below (deliberately NOT module-scope: // this file already declares a distinct, 3-arg `runVerifiedPhaseComplete(args, tmpDir, env)` // at line 54 used throughout the rest of the file; nesting here avoids shadowing it). -{ +// +// IIFE, not a bare `{ }` block: this file has no top-level 'use strict', so a plain +// block is sloppy-mode and Annex B function-hoisting semantics apply — a `function` +// declared directly inside a bare block still leaks out and REASSIGNS the enclosing +// (module-scope) `runVerifiedPhaseComplete` var the moment this block runs, clobbering +// the real one at line 54 for every call site in the file (test() bodies are deferred +// and all run after this synchronous top-level code, so every caller ends up hitting +// this one). Wrapping in a strict-mode function expression suppresses Annex B leakage, +// matching how the two original un-consolidated copies were each scoped inside a +// strict-mode `describe(() => { 'use strict'; ... })` arrow function body. +(function () { +'use strict'; /** * Write a passed-VERIFICATION marker for the phase, then run `phase complete N`. * Mirrors phase.test.cjs's writePassedVerificationForPhase: a `-VERIFICATION.md` @@ -10846,4 +10857,4 @@ describe('phase complete stage-3 sentinel filter (#2949)', () => { }); }); } -} +})();