Files
msd-core/tests/bug-621-plan-phase-gap-analysis-gsd-run.test.cjs
Tom Boucher 2815720aad fix(#621): route plan-phase post-planning-gaps through gsd_run launcher (#635)
* fix(#621): route plan-phase post-planning-gaps through gsd_run launcher

The post-planning-gaps step in gsd-core/workflows/plan-phase.md invoked
gsd-tools via a hardcoded `node "$HOME/.claude/gsd-core/bin/gsd-tools.cjs"`
path twice on one line — for the gap-analysis call and its nested
init.plan-phase phase_req_ids query — bypassing the gsd_run launcher that
every other call in the workflow uses. On non-default install/runtime layouts
(relocated/global installs, non-Claude runtimes) the hardcoded path does not
resolve, so the assistant reported the gap-analysis tool as "not found" and
fell back to a frontmatter-only coverage check even when a working install
existed. #3668 fixed this class earlier in the file but missed this block.

Route both invocations through gsd_run, matching the rest of the workflow.
No hardcoded $HOME gsd-tools path remains in plan-phase.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* chore(#621): add changeset for plan-phase gsd_run gap-analysis fix

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* test(#621): update bug-2851 §13e guard for the gsd_run gap-analysis form

The #621 fix migrates plan-phase.md's post-planning-gaps gap-analysis call
from the hardcoded node "$HOME/.claude/gsd-core/bin/gsd-tools.cjs" form to the
gsd_run launcher (the canonical resolvable form every other call in the file
uses). bug-2851's §13e subtest pinned that line to the absolute-$HOME form and
now asserts stale behavior.

Update the §13e assertion to require `gsd_run gap-analysis` (still rejecting a
regression to the hardcoded $HOME path), retitle it, and note the migration in
the file header. The generic bare-`gsd-tools` sweeper test is unchanged —
gsd_run is a resolvable launcher, not a bare gsd-tools call.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 23:02:25 -04:00

85 lines
3.1 KiB
JavaScript

// allow-test-rule: source-text-is-the-product
// The post-planning-gaps gap-analysis invocation is deployed workflow text the
// runtime executes; the contract is that it routes through the gsd_run launcher,
// not a hardcoded $HOME path (#621).
/**
* Regression test for #621: plan-phase gap-analysis must route through gsd_run
*
* Prior to the fix, line 1631 of plan-phase.md hardcoded:
* node "$HOME/.claude/gsd-core/bin/gsd-tools.cjs" gap-analysis ...
* twice on the same line, breaking non-default install layouts.
*
* After the fix, both invocations route through gsd_run (the launcher defined
* at line ~34 of the same file that resolves gsd-tools.cjs against
* RUNTIME_DIR / git-toplevel / PATH / $HOME in order).
*/
const { test, describe } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('fs');
const path = require('path');
const WORKFLOW_PATH = path.join(
__dirname,
'..',
'gsd-core',
'workflows',
'plan-phase.md'
);
// ─── Fixture ──────────────────────────────────────────────────────────────────
const workflow = fs.readFileSync(WORKFLOW_PATH, 'utf8');
// ─── #621 regression: gap-analysis routes through gsd_run ────────────────────
describe('plan-phase workflow: post-planning-gaps gap-analysis uses gsd_run launcher (#621)', () => {
test('gap-analysis call routes through gsd_run, not hardcoded node path', () => {
assert.ok(
workflow.includes('gsd_run gap-analysis'),
'workflow must invoke gap-analysis via gsd_run, not a hardcoded node path'
);
});
test('inner phase_req_ids query also routes through gsd_run', () => {
assert.ok(
workflow.includes('gsd_run query init.plan-phase'),
'workflow must invoke the inner phase_req_ids query via gsd_run launcher'
);
});
test('no hardcoded node "$HOME/.claude/gsd-core/bin/gsd-tools.cjs" invocations remain (#621)', () => {
const hardcodedCount = (
workflow.match(/node "\$HOME\/\.claude\/gsd-core\/bin\/gsd-tools\.cjs"/g) || []
).length;
assert.strictEqual(
hardcodedCount,
0,
[
'#621 regression: workflow must not contain any hardcoded',
'node "$HOME/.claude/gsd-core/bin/gsd-tools.cjs" invocations;',
`found ${hardcodedCount}`,
].join(' ')
);
});
test('post-planning-gaps block still gates on workflow.post_planning_gaps and preserves required args', () => {
const hasGate = workflow.includes('workflow.post_planning_gaps');
const hasPhaseDir = workflow.includes('--phase-dir "${PHASE_DIR}"');
const hasPickArg = workflow.includes('--pick phase_req_ids');
assert.ok(
hasGate,
'workflow must still gate the gap-analysis step on workflow.post_planning_gaps config key'
);
assert.ok(
hasPhaseDir,
'gap-analysis invocation must still pass --phase-dir "${PHASE_DIR}"'
);
assert.ok(
hasPickArg,
'inner query must still pass --pick phase_req_ids to extract phase requirement IDs'
);
});
});