* fix(#373): replace unquoted $GSD_SDK with space-safe gsd_run launcher Workflow bash blocks resolved the runtime as GSD_SDK="node $GSD_TOOLS" and invoked it unquoted ($GSD_SDK query ...). On install paths containing spaces (e.g. /Volumes/Mini Me/...) the unquoted expansion word-split into `node /Volumes/Mini gsd-tools.cjs ...`, failing with "Cannot find module '/Volumes/Mini'" and getting masked by `2>/dev/null || echo "{}"` into a silent empty state. Replace the string variable with a single-line shell launcher that defines a gsd_run function, invokes the runtime with a fully-quoted path and "$@", and preserves the local-cjs / installed-gsd-tools-on-PATH fallback (#3668) plus the loud not-found error and install hint. The launcher uses _GSD_SHIM_NAME indirection so no workflow emits the /gsd-tools substring that the do.md dispatcher-parity scanner would misread, and is single-line to stay within the per-file progressive-disclosure line budgets (#2551). The canonical launcher lives in get-shit-done/workflows/_runtime-launcher.snippet.sh, is propagated once per file by scripts/sync-runtime-launcher.cjs, and is locked by tests/runtime-launcher-parity.test.cjs (fails CI on drift, on a reappearing $GSD_SDK token, or on a /gsd-tools substring). Dependent workflow-assertion tests are updated from $GSD_SDK to gsd_run, and the runtime launcher is registered in CONTEXT.md. Fixes #373 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * ci(#373): add changeset fragment and allow-test-rule for parity guard The runtime-launcher parity test is a structural drift guard that reads workflow markdown to assert the canonical launcher is present and the retired $GSD_SDK / /gsd-tools tokens are absent; annotate it with allow-test-rule per the no-source-grep lint escape hatch. Add the required .changeset fragment for this user-facing fix. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * test(#373): make parity PATH-fallback assertion cross-platform (Windows) Subtest (E) compared GSD_TOOLS against the Node-side absolute temp path, but the value originates from git-bash which reports the POSIX form, so the prefix comparison failed on windows-latest while the launcher itself worked (the installed stub was invoked). Assert the resolved binary by normalized suffix (/bin/gsd-tools, not .cjs) instead of the absolute prefix; the behavioral stub-invocation assertion is unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
80 lines
3.4 KiB
JavaScript
80 lines
3.4 KiB
JavaScript
'use strict';
|
|
|
|
/**
|
|
* Regression test for #2384.
|
|
*
|
|
* During execute-phase, the orchestrator merges per-plan worktree branches into
|
|
* main. The pre-merge deletion check (git diff --diff-filter=D HEAD...WT_BRANCH)
|
|
* only catches files deleted on the worktree branch. A post-merge audit is also
|
|
* required to catch deletions that made it into the merge commit (e.g., files
|
|
* that were in the common ancestor but deleted by the merged worktree) and to
|
|
* provide a revert safety net.
|
|
*
|
|
* After #3797: execute-phase.md delegates worktree cleanup to the SDK's
|
|
* worktree.cleanup-wave command, which implements pre-merge deletion checks
|
|
* (diff --diff-filter=D) internally via executeWorktreeWaveCleanupPlan.
|
|
* The manual post-merge shell audit (MERGE_DEL_COUNT, git reset --hard) has
|
|
* been removed from the workflow — it was part of the SDK-absence fallback.
|
|
*/
|
|
|
|
const { test, describe } = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
const EXECUTE_PHASE = path.join(
|
|
__dirname, '..', 'get-shit-done', 'workflows', 'execute-phase.md'
|
|
);
|
|
|
|
/**
|
|
* Parse execute-phase.md into a structured contract object.
|
|
* Returns typed boolean fields so tests can assert on structure
|
|
* rather than raw text.
|
|
*/
|
|
function parseExecutePhaseContract(filePath) {
|
|
const lines = fs.readFileSync(filePath, 'utf-8').split(/\r?\n/);
|
|
return {
|
|
// Does the workflow call the worktree.cleanup-wave SDK command?
|
|
delegatesToCleanupWave: lines.some(l => l.includes('worktree.cleanup-wave')),
|
|
// Does the cleanup-wave invocation use || exit 1 (fail-closed)?
|
|
cleanupWaveFailClosed: lines.some(
|
|
l => /gsd_run query worktree\.cleanup-wave.*\|\| exit 1/.test(l),
|
|
),
|
|
// Does the workflow export/reference WAVE_WORKTREE_MANIFEST for the SDK?
|
|
passesWaveManifest: lines.some(l => l.includes('WAVE_WORKTREE_MANIFEST')),
|
|
};
|
|
}
|
|
|
|
describe('execute-phase.md — post-merge deletion audit (#2384)', () => {
|
|
const contract = parseExecutePhaseContract(EXECUTE_PHASE);
|
|
|
|
test('execute-phase delegates to worktree.cleanup-wave (which handles deletion audit)', () => {
|
|
// After #3797: worktree.cleanup-wave in worktree-safety.cjs performs
|
|
// diff --diff-filter=D checks (blocks branches with deletions) before merge.
|
|
// The workflow delegates to the SDK rather than duplicating the check inline.
|
|
assert.ok(
|
|
contract.delegatesToCleanupWave,
|
|
'execute-phase.md must delegate to gsd_run query worktree.cleanup-wave (#2384/#3797)',
|
|
);
|
|
});
|
|
|
|
test('execute-phase cleanup-wave uses || exit 1 (fail-closed for blocked deletions)', () => {
|
|
// If worktree.cleanup-wave detects deletions, it exits 1 (blocked).
|
|
// The || exit 1 in the workflow propagates that refusal rather than swallowing it.
|
|
assert.ok(
|
|
contract.cleanupWaveFailClosed,
|
|
'execute-phase.md must use || exit 1 so deletion-blocked cleanups surface to the orchestrator',
|
|
);
|
|
});
|
|
|
|
test('execute-phase still has pre-merge deletion check (via guard before worktree.cleanup-wave)', () => {
|
|
// The primary deletion guard is now in worktree-safety.cjs (SDK).
|
|
// The workflow must still enforce WAVE_WORKTREE_MANIFEST so the SDK
|
|
// has the info it needs to validate branches.
|
|
assert.ok(
|
|
contract.passesWaveManifest,
|
|
'execute-phase.md must pass WAVE_WORKTREE_MANIFEST to worktree.cleanup-wave',
|
|
);
|
|
});
|
|
});
|