fix(#3309): restore W027's active-worktree exclusion

The migrated checkW027 (stale worktree) dropped the pre-migration
exclusion of the CLI's own current worktree, since a Rule.check(snapshot)
has no cwd access (§8.1 rule 1 forbids ambient I/O) — flagged as a
disclosed regression during this phase's own design work, then
confirmed as a real, fixable gap by the Spec-axis orthogonal review
rather than an inherent limitation.

Fixes it properly instead of accepting the regression: buildPlanningSnapshot(cwd)
already receives cwd as its own input, so exposing it as snapshot.cwd
is not new ambient I/O, just surfacing an existing parameter — fully
consistent with §8.1 rule 2's "parsed value" allowance. checkW027 now
excludes the entry matching snapshot.cwd before flagging, matching the
original verify.cts:2233-2242 behavior exactly.
This commit is contained in:
sim
2026-08-13 02:56:23 -04:00
parent 42729b21fa
commit 1255f960db
3 changed files with 85 additions and 63 deletions

View File

@@ -306,17 +306,13 @@ describe('W027 — stale git worktree', () => {
assert.deepEqual(ruleFor('W027').check(snapshot), []);
});
// GAP (documented in the rule module's own header comment, gap 2): the
// pre-migration `process.cwd()` exclusion of the ACTIVE session's own
// worktree cannot be reproduced here — `Rule.check(snapshot)` has no
// ambient cwd access, and `PlanningSnapshot` carries no "which entry is
// the active worktree" field. This test recreates the real-world shape the
// module doc calls out: the active session's cwd is a LINKED (non-first)
// `git worktree list` entry, not the main repo root — `buildPlanningSnapshot(cwd)`
// is called with `cwd` itself listed as entry index 1 (not the dropped
// index-0 "main" entry) and made stale. W027 fires for it anyway,
// demonstrating the gap rather than silently passing.
test('GAP: fires for the active session\'s own (stale) worktree — no cwd-based exclusion is possible from snapshot alone', (t) => {
// Regression proof (restores `verify.cts:2233-2242`'s pre-migration
// behavior via `PlanningSnapshot.cwd`, see the rule module's own header
// comment): the active session's cwd is a LINKED (non-first) `git worktree
// list` entry, not the main repo root — `buildPlanningSnapshot(cwd)` is
// called with `cwd` itself listed as entry index 1 (not the dropped
// index-0 "main" entry) and made stale. W027 must NOT fire for it.
test('excludes the active session\'s own (stale) worktree — matches snapshot.cwd exactly', (t) => {
const cwd = createTempDir('gsd-3309-w027-3-');
t.after(() => cleanup(cwd));
fs.mkdirSync(planningDirOf(cwd), { recursive: true });
@@ -327,10 +323,52 @@ describe('W027 — stale git worktree', () => {
mockGitWorktreeListOk(t, buildPorcelain(['/fake/main-repo', cwd]));
const snapshot = buildPlanningSnapshot(cwd);
assert.equal(snapshot.cwd, path.resolve(cwd), 'snapshot.cwd must be the resolved active cwd');
const diagnostics = ruleFor('W027').check(snapshot);
assert.deepEqual(
diagnostics,
[],
'the active worktree must be excluded from stale-worktree diagnostics, matching pre-migration behavior',
);
});
test('excludes the active session\'s own (stale) worktree when cwd is NESTED under the worktree path — mirrors verify.cts:2239-2241\'s startsWith check', (t) => {
const cwd = createTempDir('gsd-3309-w027-4-');
t.after(() => cleanup(cwd));
const worktreePath = path.join(cwd, 'wt-active');
const nestedCwd = path.join(worktreePath, 'sub', 'dir');
fs.mkdirSync(nestedCwd, { recursive: true });
fs.mkdirSync(planningDirOf(nestedCwd), { recursive: true });
fs.utimesSync(worktreePath, new Date(), new Date(Date.now() - 2 * 60 * 60 * 1000));
mockGitWorktreeListOk(t, buildPorcelain(['/fake/main-repo', worktreePath]));
const snapshot = buildPlanningSnapshot(nestedCwd);
const diagnostics = ruleFor('W027').check(snapshot);
assert.equal(diagnostics.length, 1, 'the active worktree is NOT excluded — this is the documented gap');
assert.deepEqual(
diagnostics,
[],
'a worktree that is an ancestor of the active cwd must also be excluded',
);
});
test('a DIFFERENT stale worktree (not the active cwd, not an ancestor of it) still fires', (t) => {
const cwd = createTempDir('gsd-3309-w027-5-');
t.after(() => cleanup(cwd));
fs.mkdirSync(planningDirOf(cwd), { recursive: true });
const otherStalePath = path.join(cwd, 'wt-other-stale');
fs.mkdirSync(otherStalePath, { recursive: true });
fs.utimesSync(otherStalePath, new Date(), new Date(Date.now() - 2 * 60 * 60 * 1000));
mockGitWorktreeListOk(t, buildPorcelain(['/fake/main-repo', otherStalePath]));
const snapshot = buildPlanningSnapshot(cwd);
const diagnostics = ruleFor('W027').check(snapshot);
assert.equal(diagnostics.length, 1, 'a stale worktree distinct from the active cwd must still be flagged');
assert.equal(diagnostics[0].code, 'W027');
assert.ok(diagnostics[0].message.includes(cwd));
});
});