fix(#1629): copy Windsurf command bodies so workflow delegation targets exist
PR #1622 (issue #1615) shipped Windsurf /gsd-* workflow wrappers that delegate to command bodies at <targetDir>/.windsurf/gsd-core/commands/gsd/X.md via a hardcoded @~/.claude/gsd-core/commands/gsd/ path. The path-rewrite pipeline correctly substitutes ~/.claude/ to the install target. But the source gsd-core/ dir does not ship with commands/ — the canonical command source lives at the package root (commands/gsd/). Without this copy, every /gsd-* workflow in Cascade references a file that does not exist. The slash commands appear in the / menu but silently fail when invoked because the LLM is told to read a missing file. None of the original reviews caught this: not the security review, not Codex's adversarial orthogonal review (gpt-5.5/high), not Memtrace's graph-backed review. It was surfaced by a #1629 regression test that verifies 'every workflow @-reference target exists on disk after install' — the test failed, revealing the bug. Fix: for Windsurf local installs, copy commands/gsd/*.md into <targetDir>/gsd-core/commands/gsd/ via copyWithPathReplacement (applies the same path+brand rewrites as the rest of the install). Guarded on isWindsurf && !isGlobal since global Windsurf workflow install is an explicit no-op. Documented as DEFECT.WORKFLOW-DELEGATION-TARGET-NOT-INSTALLED in CONTEXT.md so the pattern is locked in: any new converter emitting a wrapper that delegates to another file MUST verify the delegation target is actually installed.
This commit is contained in:
@@ -1330,6 +1330,33 @@ describe('windsurf local install writes workflow slash commands (#1615)', () =>
|
||||
}
|
||||
});
|
||||
|
||||
// #1629 Finding A: every workflow's @-reference target must exist on disk
|
||||
// after install. Pre-fix, gsd-core/ was copied AFTER workflows were written;
|
||||
// a throw or kill in that window left workflows pointing at missing files.
|
||||
// Post-fix, gsd-core/ is copied first. This behavioral invariant catches
|
||||
// any ordering regression that leaves a workflow target absent.
|
||||
test('every workflow @-reference target exists on disk after install (#1629 Finding A)', () => {
|
||||
install(false, 'windsurf');
|
||||
const workflowsDir = path.join(tmpDir, '.windsurf', 'workflows');
|
||||
const workflowEntries = fs.readdirSync(workflowsDir, { withFileTypes: true })
|
||||
.filter(e => e.isFile() && e.name.startsWith('gsd-') && e.name.endsWith('.md'));
|
||||
assert.ok(workflowEntries.length > 0, 'pre-condition: at least one gsd-* workflow must be installed');
|
||||
|
||||
const commandsGsdDir = path.join(tmpDir, '.windsurf', 'gsd-core', 'commands', 'gsd');
|
||||
assert.ok(fs.existsSync(commandsGsdDir),
|
||||
`gsd-core/commands/gsd/ must exist at ${commandsGsdDir} so workflows can delegate to it`);
|
||||
|
||||
for (const workflowEntry of workflowEntries) {
|
||||
// Workflow naming convention: gsd-<stem>.md → delegates to commands/gsd/<stem>.md
|
||||
const stem = workflowEntry.name.replace(/^gsd-/, '').replace(/\.md$/, '');
|
||||
const targetFile = path.join(commandsGsdDir, `${stem}.md`);
|
||||
assert.ok(
|
||||
fs.existsSync(targetFile),
|
||||
`${workflowEntry.name} delegates to commands/gsd/${stem}.md, but that file does not exist at ${targetFile}`,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test('global windsurf install does not write unsupported workflows or skills', () => {
|
||||
const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-ws-global-'));
|
||||
const savedHome = process.env.HOME;
|
||||
|
||||
Reference in New Issue
Block a user