refactor: hard-fork GSD -> MSD (Make Software Done)
Mechanical rename produced by scripts/msd-rename.cjs: gsd/Gsd/GSD -> msd/Msd/MSD across contents and paths, upstream package/repo coordinates -> @golem15/msd-core and golem15com/msd-core. Deep links into upstream history, sibling upstream packages, the GSD-2 import feature, CHANGELOG.md and .changeset/ are kept as-is. Hand edits on top: MSD block-letter banner and logos, LICENSE copyright line, package/plugin identity, regenerated lockfile, install-tree fixtures, derived registries and benchmark baseline; migration checksum baseline re-locked (MSD keeps its own install state, so no install had applied the old sums); sort-order and regex-escaped expectations in tests adjusted.
This commit is contained in:
@@ -25,7 +25,7 @@ const {
|
||||
makeInvalidArgs,
|
||||
makeHandlerRefusal,
|
||||
makeHandlerFailure,
|
||||
} = require('../gsd-core/bin/lib/command-routing-hub.cjs');
|
||||
} = require('../msd-core/bin/lib/command-routing-hub.cjs');
|
||||
|
||||
// ─── Frozen taxonomy lock ─────────────────────────────────────────────────────
|
||||
// #175: SdkDispatchFailed and SdkLoadFailed are removed from the closed enum.
|
||||
@@ -962,11 +962,11 @@ describe('CommandRoutingHub — exitReason? field on InvalidArgs (#1644 / amendm
|
||||
const { test } = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
|
||||
const { runGsdTools, createTempProject, cleanup } = require('./helpers.cjs');
|
||||
const { runMsdTools, createTempProject, cleanup } = require('./helpers.cjs');
|
||||
|
||||
// #4342: this ran gsd-tools with runGsdTools's default cwd — the checkout's own
|
||||
// #4342: this ran msd-tools with runMsdTools's default cwd — the checkout's own
|
||||
// working directory — and an inherited HOME, so the child read the developer's
|
||||
// real .planning/config.json and ~/.gsd/defaults.json. On a machine whose real
|
||||
// real .planning/config.json and ~/.msd/defaults.json. On a machine whose real
|
||||
// project enables workstream mode with no active workstream, `init.progress`
|
||||
// exits non-zero and the FIRST assertion fails, so the routing comparison this
|
||||
// test exists for was never reached. testEnvBase() blanks the config-LOCATION
|
||||
@@ -975,18 +975,18 @@ const { runGsdTools, createTempProject, cleanup } = require('./helpers.cjs');
|
||||
// The routing invariant is about `query <cmd>` and `<cmd>` agreeing, which is
|
||||
// independent of project state — so the fixture only has to be a project whose
|
||||
// state is known and unaffected by the developer's.
|
||||
test('bug #167: query meta-command prefixes direct gsd-tools calls', (t) => {
|
||||
const fixture = createTempProject('gsd-4342-routing-');
|
||||
test('bug #167: query meta-command prefixes direct msd-tools calls', (t) => {
|
||||
const fixture = createTempProject('msd-4342-routing-');
|
||||
t.after(() => cleanup(fixture));
|
||||
// HOME/USERPROFILE point at the fixture so ~/.gsd/defaults.json resolves
|
||||
// HOME/USERPROFILE point at the fixture so ~/.msd/defaults.json resolves
|
||||
// inside it (absent) rather than in the developer's home — the idiom
|
||||
// runGsdTools's own doc comment prescribes for exactly this.
|
||||
// runMsdTools's own doc comment prescribes for exactly this.
|
||||
const sandbox = { HOME: fixture, USERPROFILE: fixture };
|
||||
|
||||
const direct = runGsdTools(['init.progress'], fixture, sandbox);
|
||||
const direct = runMsdTools(['init.progress'], fixture, sandbox);
|
||||
assert.equal(direct.success, true, `init.progress failed: ${direct.error || direct.output}`);
|
||||
|
||||
const meta = runGsdTools(['query', 'init.progress'], fixture, sandbox);
|
||||
const meta = runMsdTools(['query', 'init.progress'], fixture, sandbox);
|
||||
assert.equal(meta.success, true, `query init.progress failed: ${meta.error || meta.output}`);
|
||||
|
||||
const directPayload = JSON.parse(direct.output);
|
||||
@@ -1018,7 +1018,7 @@ test('bug #167: query meta-command prefixes direct gsd-tools calls', (t) => {
|
||||
/**
|
||||
* Regression test for bug #1818, updated for #3019.
|
||||
*
|
||||
* Original #1818 invariant: gsd-tools must NOT silently ignore --help/-h
|
||||
* Original #1818 invariant: msd-tools must NOT silently ignore --help/-h
|
||||
* and proceed with a destructive command — that turned AI-agent
|
||||
* hallucinations into accidental data loss (e.g. `phases clear --help`
|
||||
* deleting phase dirs because the flag was dropped).
|
||||
@@ -1028,13 +1028,13 @@ test('bug #167: query meta-command prefixes direct gsd-tools calls', (t) => {
|
||||
* exit. Now --help → render top-level usage and exit 0 WITHOUT running
|
||||
* the command. Both shapes satisfy the original invariant ("the
|
||||
* destructive command did not execute"); the new shape also restores
|
||||
* subcommand discoverability for `gsd-sdk query <subcommand> --help`.
|
||||
* subcommand discoverability for `msd-sdk query <subcommand> --help`.
|
||||
*
|
||||
* The tests therefore assert two things:
|
||||
* 1. The destructive command did NOT run (anti-hallucination invariant).
|
||||
* 2. The output contains the top-level usage (#3019 discoverability).
|
||||
*
|
||||
* --version remains rejected — it's never a valid gsd-tools flag and has
|
||||
* --version remains rejected — it's never a valid msd-tools flag and has
|
||||
* no discovery use-case.
|
||||
*/
|
||||
|
||||
@@ -1044,7 +1044,7 @@ const { describe, test, beforeEach, afterEach } = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const { runGsdTools, createTempProject, cleanup, isUsageOutput } = require('./helpers.cjs');
|
||||
const { runMsdTools, createTempProject, cleanup, isUsageOutput } = require('./helpers.cjs');
|
||||
|
||||
describe('unknown flag guard (bug #1818, updated for #3019)', () => {
|
||||
let tmpDir;
|
||||
@@ -1065,7 +1065,7 @@ describe('unknown flag guard (bug #1818, updated for #3019)', () => {
|
||||
fs.mkdirSync(phaseDir, { recursive: true });
|
||||
fs.writeFileSync(path.join(phaseDir, 'PLAN.md'), 'sentinel');
|
||||
|
||||
const result = runGsdTools(['phases', 'clear', '--help'], tmpDir);
|
||||
const result = runMsdTools(['phases', 'clear', '--help'], tmpDir);
|
||||
assert.strictEqual(result.success, true, 'help renders, no error exit');
|
||||
assert.ok(isUsageOutput(result.output), `expected top-level usage, got: ${result.output}`);
|
||||
// Anti-hallucination invariant: the destructive command did NOT run.
|
||||
@@ -1074,20 +1074,20 @@ describe('unknown flag guard (bug #1818, updated for #3019)', () => {
|
||||
});
|
||||
|
||||
test('generate-slug hello --help renders usage and does NOT emit a slug', () => {
|
||||
const ok = runGsdTools(['generate-slug', 'hello'], tmpDir);
|
||||
const ok = runMsdTools(['generate-slug', 'hello'], tmpDir);
|
||||
assert.strictEqual(ok.success, true, 'control: generate-slug works without --help');
|
||||
// The control output is just the slug; the help output is the usage.
|
||||
const slugOut = ok.output;
|
||||
assert.ok(slugOut && !isUsageOutput(slugOut), `control should not be usage: ${slugOut}`);
|
||||
|
||||
const result = runGsdTools(['generate-slug', 'hello', '--help'], tmpDir);
|
||||
const result = runMsdTools(['generate-slug', 'hello', '--help'], tmpDir);
|
||||
assert.strictEqual(result.success, true);
|
||||
assert.ok(isUsageOutput(result.output), 'help renders top-level usage');
|
||||
assert.notEqual(result.output, slugOut, 'help output must differ from the slug — generate-slug must not have run');
|
||||
});
|
||||
|
||||
test('phase complete --help renders usage and does NOT mark a phase complete', () => {
|
||||
const result = runGsdTools(['phase', 'complete', '--help'], tmpDir);
|
||||
const result = runMsdTools(['phase', 'complete', '--help'], tmpDir);
|
||||
assert.strictEqual(result.success, true);
|
||||
assert.ok(isUsageOutput(result.output));
|
||||
// success:true + isUsageOutput is sufficient: if the destructive path
|
||||
@@ -1096,7 +1096,7 @@ describe('unknown flag guard (bug #1818, updated for #3019)', () => {
|
||||
});
|
||||
|
||||
test('state load --help renders usage', () => {
|
||||
const result = runGsdTools(['state', 'load', '--help'], tmpDir);
|
||||
const result = runMsdTools(['state', 'load', '--help'], tmpDir);
|
||||
assert.strictEqual(result.success, true);
|
||||
assert.ok(isUsageOutput(result.output));
|
||||
});
|
||||
@@ -1106,14 +1106,14 @@ describe('unknown flag guard (bug #1818, updated for #3019)', () => {
|
||||
test('phases clear -h renders usage and does NOT clear phase dirs', () => {
|
||||
const phaseDir = path.join(tmpDir, '.planning', 'phases', 'phase-42');
|
||||
fs.mkdirSync(phaseDir, { recursive: true });
|
||||
const result = runGsdTools(['phases', 'clear', '-h'], tmpDir);
|
||||
const result = runMsdTools(['phases', 'clear', '-h'], tmpDir);
|
||||
assert.strictEqual(result.success, true);
|
||||
assert.ok(isUsageOutput(result.output));
|
||||
assert.ok(fs.existsSync(phaseDir), 'phase dir must survive');
|
||||
});
|
||||
|
||||
test('generate-slug hello -h renders usage', () => {
|
||||
const result = runGsdTools(['generate-slug', 'hello', '-h'], tmpDir);
|
||||
const result = runMsdTools(['generate-slug', 'hello', '-h'], tmpDir);
|
||||
assert.strictEqual(result.success, true);
|
||||
assert.ok(isUsageOutput(result.output));
|
||||
});
|
||||
@@ -1121,7 +1121,7 @@ describe('unknown flag guard (bug #1818, updated for #3019)', () => {
|
||||
// ── --version is still rejected — no discovery use-case ──────────────────
|
||||
|
||||
test('generate-slug hello --version is rejected', () => {
|
||||
const result = runGsdTools(['generate-slug', 'hello', '--version'], tmpDir);
|
||||
const result = runMsdTools(['generate-slug', 'hello', '--version'], tmpDir);
|
||||
assert.strictEqual(result.success, false);
|
||||
assert.match(result.error, /--version/);
|
||||
});
|
||||
@@ -1129,7 +1129,7 @@ describe('unknown flag guard (bug #1818, updated for #3019)', () => {
|
||||
// ── current-timestamp --help: same as the others ─────────────────────────
|
||||
|
||||
test('current-timestamp --help renders usage', () => {
|
||||
const result = runGsdTools(['current-timestamp', '--help'], tmpDir);
|
||||
const result = runMsdTools(['current-timestamp', '--help'], tmpDir);
|
||||
assert.strictEqual(result.success, true);
|
||||
assert.ok(isUsageOutput(result.output));
|
||||
});
|
||||
@@ -1147,7 +1147,7 @@ describe('unknown flag guard (bug #1818, updated for #3019)', () => {
|
||||
/**
|
||||
* Tests for the --json-errors mode added in #3255.
|
||||
*
|
||||
* When gsd-tools is invoked with --json-errors, all error() calls emit a
|
||||
* When msd-tools is invoked with --json-errors, all error() calls emit a
|
||||
* structured JSON object to stderr:
|
||||
*
|
||||
* { ok: false, reason: "<error_code>", message: "<human text>" }
|
||||
@@ -1162,7 +1162,7 @@ describe('unknown flag guard (bug #1818, updated for #3019)', () => {
|
||||
* 3. Missing required argument → reason: "usage" (--pick without value)
|
||||
* 4. Config key not found → reason: "config_key_not_found"
|
||||
* 5. Unknown subcommand → reason: "sdk_unknown_command"
|
||||
* 6. GSD_JSON_ERRORS=1 env var → same structured output without --flag
|
||||
* 6. MSD_JSON_ERRORS=1 env var → same structured output without --flag
|
||||
* 7. Successful command unaffected
|
||||
* 8. Error object shape is stable ({ok, reason, message})
|
||||
* 9. Single error line per invocation
|
||||
@@ -1173,13 +1173,13 @@ describe('unknown flag guard (bug #1818, updated for #3019)', () => {
|
||||
|
||||
const { describe, test, beforeEach, afterEach } = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const { runGsdTools, createTempProject, cleanup } = require('./helpers.cjs');
|
||||
const { runMsdTools, createTempProject, cleanup } = require('./helpers.cjs');
|
||||
|
||||
// Helper: run gsd-tools with --json-errors and parse the structured stderr.
|
||||
// Helper: run msd-tools with --json-errors and parse the structured stderr.
|
||||
// Returns the parsed object, or throws if stderr is not valid JSON.
|
||||
function runJsonErrors(args, tmpDir, env = {}) {
|
||||
const allArgs = ['--json-errors', ...args];
|
||||
const result = runGsdTools(allArgs, tmpDir, env);
|
||||
const result = runMsdTools(allArgs, tmpDir, env);
|
||||
// Must have failed
|
||||
assert.strictEqual(result.success, false,
|
||||
`Expected failure with --json-errors for args: ${args.join(' ')}\nstdout: ${result.output}\nstderr: ${result.error}`);
|
||||
@@ -1252,7 +1252,7 @@ describe('feat #3255: --json-errors mode emits structured error objects', () =>
|
||||
test('config-get for absent key emits { ok: false, reason: "config_key_not_found" }', () => {
|
||||
// Initialise config.json first so we reach the "key not found" branch
|
||||
// rather than the "no config.json" branch.
|
||||
runGsdTools(['config-ensure-section'], tmpDir);
|
||||
runMsdTools(['config-ensure-section'], tmpDir);
|
||||
|
||||
const parsed = runJsonErrors(['config-get', 'nonexistent_config_key_xyzzy'], tmpDir);
|
||||
|
||||
@@ -1277,14 +1277,14 @@ describe('feat #3255: --json-errors mode emits structured error objects', () =>
|
||||
'message must be a non-empty string');
|
||||
});
|
||||
|
||||
// ── 6. GSD_JSON_ERRORS=1 env var activates structured mode ───────────────
|
||||
// ── 6. MSD_JSON_ERRORS=1 env var activates structured mode ───────────────
|
||||
|
||||
test('GSD_JSON_ERRORS=1 env var produces same structured error as --json-errors flag', () => {
|
||||
test('MSD_JSON_ERRORS=1 env var produces same structured error as --json-errors flag', () => {
|
||||
// Run with env var instead of --json-errors flag
|
||||
const result = runGsdTools(
|
||||
const result = runMsdTools(
|
||||
['totally-unknown-command-xyzzy'],
|
||||
tmpDir,
|
||||
{ GSD_JSON_ERRORS: '1' }
|
||||
{ MSD_JSON_ERRORS: '1' }
|
||||
);
|
||||
assert.strictEqual(result.success, false,
|
||||
'command must fail');
|
||||
@@ -1293,7 +1293,7 @@ describe('feat #3255: --json-errors mode emits structured error objects', () =>
|
||||
parsed = JSON.parse(result.error);
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
`GSD_JSON_ERRORS=1 must emit valid JSON on stderr.\n` +
|
||||
`MSD_JSON_ERRORS=1 must emit valid JSON on stderr.\n` +
|
||||
`stderr: ${result.error}\n` +
|
||||
`parse error: ${e.message}`
|
||||
);
|
||||
@@ -1307,7 +1307,7 @@ describe('feat #3255: --json-errors mode emits structured error objects', () =>
|
||||
// ── 7. Successful commands are unaffected by --json-errors ───────────────
|
||||
|
||||
test('successful command with --json-errors flag still succeeds normally', () => {
|
||||
const result = runGsdTools(
|
||||
const result = runMsdTools(
|
||||
['--json-errors', 'generate-slug', 'hello-world'],
|
||||
tmpDir
|
||||
);
|
||||
@@ -1330,7 +1330,7 @@ describe('feat #3255: --json-errors mode emits structured error objects', () =>
|
||||
// ── 9. Multiple errors in one session: only the first error is emitted ───
|
||||
|
||||
test('only one error JSON line is emitted per invocation (process exits on first error)', () => {
|
||||
const result = runGsdTools(
|
||||
const result = runMsdTools(
|
||||
['--json-errors', 'totally-unknown-command-xyzzy'],
|
||||
tmpDir
|
||||
);
|
||||
@@ -1365,7 +1365,7 @@ describe('feat #3255: --json-errors mode emits structured error objects', () =>
|
||||
__foldDescribe("folded:feat-3310-followup-typed-codes (consolidation epic #1969 B2 #1971)", () => {
|
||||
/**
|
||||
* Follow-up tests for #3310: every remaining `error()` call at a subcommand
|
||||
* boundary or usage check in `gsd-tools.cjs` carries a typed `ERROR_REASON`.
|
||||
* boundary or usage check in `msd-tools.cjs` carries a typed `ERROR_REASON`.
|
||||
*
|
||||
* #3304 wired four representative paths (unknown top-level command, unknown
|
||||
* intel subcommand, missing --pick value, --version flag). The rest fell
|
||||
@@ -1382,22 +1382,22 @@ describe('feat #3255: --json-errors mode emits structured error objects', () =>
|
||||
|
||||
const { describe, test, beforeEach, afterEach } = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const { runGsdTools, createTempProject, cleanup } = require('./helpers.cjs');
|
||||
const { runMsdTools, createTempProject, cleanup } = require('./helpers.cjs');
|
||||
|
||||
// Run gsd-tools with GSD_JSON_ERRORS=1 (env-var activation, exercises the
|
||||
// Run msd-tools with MSD_JSON_ERRORS=1 (env-var activation, exercises the
|
||||
// path #3304 added alongside the --json-errors flag) and parse the
|
||||
// structured stderr. Returns the parsed object; throws if stderr is not JSON.
|
||||
function runJsonErrors(args, tmpDir, env = {}) {
|
||||
const result = runGsdTools(args, tmpDir, { ...env, GSD_JSON_ERRORS: '1' });
|
||||
const result = runMsdTools(args, tmpDir, { ...env, MSD_JSON_ERRORS: '1' });
|
||||
assert.strictEqual(result.success, false,
|
||||
`Expected failure with GSD_JSON_ERRORS=1 for args: ${args.join(' ')}\n` +
|
||||
`Expected failure with MSD_JSON_ERRORS=1 for args: ${args.join(' ')}\n` +
|
||||
`stdout: ${result.output}\nstderr: ${result.error}`);
|
||||
let parsed;
|
||||
try {
|
||||
parsed = JSON.parse(result.error);
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
`GSD_JSON_ERRORS=1 must emit valid JSON on stderr.\n` +
|
||||
`MSD_JSON_ERRORS=1 must emit valid JSON on stderr.\n` +
|
||||
`Args: ${args.join(' ')}\nstderr: ${result.error}\nparse error: ${e.message}`
|
||||
);
|
||||
}
|
||||
@@ -1600,7 +1600,7 @@ describe('feat #3310: typed ERROR_REASON codes on remaining error paths', () =>
|
||||
__foldDescribe("folded:bug-853-bg-dispatch-runtime-gating (consolidation epic #1969 B2 #1971)", () => {
|
||||
'use strict';
|
||||
/**
|
||||
* Regression guard — bug(#853): /gsd-manager and /gsd-autonomous --interactive
|
||||
* Regression guard — bug(#853): /msd-manager and /msd-autonomous --interactive
|
||||
* silently skipped worktree isolation + independent verification because they
|
||||
* dispatched Plan/Execute via Agent(run_in_background=true). On Claude Code a
|
||||
* backgrounded agent has no Agent/Task tool, so it cannot spawn the nested
|
||||
@@ -1610,7 +1610,7 @@ describe('feat #3310: typed ERROR_REASON codes on remaining error paths', () =>
|
||||
* are both true (currently: codex, cursor).
|
||||
*
|
||||
* Phase B (#1708): the prose `RUNTIME === 'codex'` rule is graduated to a typed
|
||||
* `gsd_run query dispatch-should-flatten` query backed by shouldFlattenDispatch()
|
||||
* `msd_run query dispatch-should-flatten` query backed by shouldFlattenDispatch()
|
||||
* from host-integration.cjs and the documentation-sourced capability registry.
|
||||
*/
|
||||
|
||||
@@ -1618,9 +1618,9 @@ const { describe, test } = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const { createTempProject, cleanup: cleanupDir, runGsdTools } = require('./helpers.cjs');
|
||||
const { createTempProject, cleanup: cleanupDir, runMsdTools } = require('./helpers.cjs');
|
||||
|
||||
const WORKFLOWS_DIR = path.join(__dirname, '..', 'gsd-core', 'workflows');
|
||||
const WORKFLOWS_DIR = path.join(__dirname, '..', 'msd-core', 'workflows');
|
||||
// allow-test-rule: source-text-is-the-product (see #1708)
|
||||
const MANAGER = fs.readFileSync(path.join(WORKFLOWS_DIR, 'manager.md'), 'utf8');
|
||||
// allow-test-rule: source-text-is-the-product (see #1708)
|
||||
@@ -1647,12 +1647,12 @@ describe('bug-853 — manager/autonomous gate background dispatch by runtime', (
|
||||
// Inline is the default/else branch for plan — anchored on FLATTEN=true language (not runtime name)
|
||||
assert.match(
|
||||
MANAGER,
|
||||
/Otherwise[\s\S]{0,100}?`FLATTEN`[\s\S]{0,400}?Skill\(skill="gsd-plan-phase"/,
|
||||
/Otherwise[\s\S]{0,100}?`FLATTEN`[\s\S]{0,400}?Skill\(skill="msd-plan-phase"/,
|
||||
);
|
||||
// Inline is the default/else branch for execute — anchored on FLATTEN=true language (not runtime name)
|
||||
assert.match(
|
||||
MANAGER,
|
||||
/Otherwise[\s\S]{0,100}?`FLATTEN`[\s\S]{0,400}?Skill\(skill="gsd-execute-phase"/,
|
||||
/Otherwise[\s\S]{0,100}?`FLATTEN`[\s\S]{0,400}?Skill\(skill="msd-execute-phase"/,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1696,26 +1696,26 @@ describe('bug-853 — manager/autonomous gate background dispatch by runtime', (
|
||||
});
|
||||
|
||||
test('autonomous.md gates interactive background dispatch on FLATTEN=false; runs plan/execute inline otherwise', () => {
|
||||
// Background block: run_in_background=true appears within the FLATTEN=false branch and gsd-plan-phase is nearby
|
||||
// Background block: run_in_background=true appears within the FLATTEN=false branch and msd-plan-phase is nearby
|
||||
// allow-test-rule: source-text-is-the-product (see #1708)
|
||||
assert.match(AUTONOMOUS, /If `FLATTEN` is `false`[\s\S]{0,1200}?run_in_background=true[\s\S]{0,600}?gsd-plan-phase/);
|
||||
// Background block: run_in_background=true appears within the FLATTEN=false branch and gsd-execute-phase is nearby
|
||||
assert.match(AUTONOMOUS, /If `FLATTEN` is `false`[\s\S]{0,3000}?run_in_background=true[\s\S]{0,200}?gsd-execute-phase/);
|
||||
assert.match(AUTONOMOUS, /If `FLATTEN` is `false`[\s\S]{0,1200}?run_in_background=true[\s\S]{0,600}?msd-plan-phase/);
|
||||
// Background block: run_in_background=true appears within the FLATTEN=false branch and msd-execute-phase is nearby
|
||||
assert.match(AUTONOMOUS, /If `FLATTEN` is `false`[\s\S]{0,3000}?run_in_background=true[\s\S]{0,200}?msd-execute-phase/);
|
||||
// Inline is the otherwise/else branch for plan — anchored on FLATTEN=true language (not runtime name).
|
||||
// #2994: the window between `FLATTEN` and the inline Skill() call widened
|
||||
// past manager.md's 400 because this branch now carries a
|
||||
// state:plan-strategy-converge conditional-read stub (converge-dispatch-inline,
|
||||
// gsd-core/workflows/autonomous/steps/converge-dispatch-inline.md) ahead of
|
||||
// msd-core/workflows/autonomous/steps/converge-dispatch-inline.md) ahead of
|
||||
// the local-planning fallback — the stub's full step-file path is more
|
||||
// verbose than the terse `Skill()` call it replaced.
|
||||
assert.match(
|
||||
AUTONOMOUS,
|
||||
/Otherwise[\s\S]{0,100}?`FLATTEN`[\s\S]{0,700}?Skill\(skill="gsd-plan-phase"/,
|
||||
/Otherwise[\s\S]{0,100}?`FLATTEN`[\s\S]{0,700}?Skill\(skill="msd-plan-phase"/,
|
||||
);
|
||||
// Inline is the otherwise/else branch for execute — anchored on FLATTEN=true language (not runtime name)
|
||||
assert.match(
|
||||
AUTONOMOUS,
|
||||
/Otherwise[\s\S]{0,100}?`FLATTEN`[\s\S]{0,400}?Skill\(skill="gsd-execute-phase"/,
|
||||
/Otherwise[\s\S]{0,100}?`FLATTEN`[\s\S]{0,400}?Skill\(skill="msd-execute-phase"/,
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -1736,8 +1736,8 @@ describe('dispatch-should-flatten query — behavioral', () => {
|
||||
test('runtime=codex → shouldFlatten=true (#2939: maxDepth:1 cannot host a bg orchestrator + leaf)', () => {
|
||||
const tmpDir = createTempProject();
|
||||
try {
|
||||
const result = runGsdTools(['query', 'dispatch-should-flatten', '--raw'], tmpDir, {
|
||||
GSD_RUNTIME: 'codex',
|
||||
const result = runMsdTools(['query', 'dispatch-should-flatten', '--raw'], tmpDir, {
|
||||
MSD_RUNTIME: 'codex',
|
||||
});
|
||||
assert.ok(result.success, `Expected success, got error: ${result.error}`);
|
||||
assert.strictEqual(result.output, 'true', `#2939: codex maxDepth:1 should return true (must inline), got: ${result.output}`);
|
||||
@@ -1749,8 +1749,8 @@ describe('dispatch-should-flatten query — behavioral', () => {
|
||||
test('runtime=claude → shouldFlatten=true (must inline)', () => {
|
||||
const tmpDir = createTempProject();
|
||||
try {
|
||||
const result = runGsdTools(['query', 'dispatch-should-flatten', '--raw'], tmpDir, {
|
||||
GSD_RUNTIME: 'claude',
|
||||
const result = runMsdTools(['query', 'dispatch-should-flatten', '--raw'], tmpDir, {
|
||||
MSD_RUNTIME: 'claude',
|
||||
});
|
||||
assert.ok(result.success, `Expected success, got error: ${result.error}`);
|
||||
assert.strictEqual(result.output, 'true', `claude should return true (must inline), got: ${result.output}`);
|
||||
@@ -1762,8 +1762,8 @@ describe('dispatch-should-flatten query — behavioral', () => {
|
||||
test('runtime=cursor → shouldFlatten=false (background dispatch safe)', () => {
|
||||
const tmpDir = createTempProject();
|
||||
try {
|
||||
const result = runGsdTools(['query', 'dispatch-should-flatten', '--raw'], tmpDir, {
|
||||
GSD_RUNTIME: 'cursor',
|
||||
const result = runMsdTools(['query', 'dispatch-should-flatten', '--raw'], tmpDir, {
|
||||
MSD_RUNTIME: 'cursor',
|
||||
});
|
||||
assert.ok(result.success, `Expected success, got error: ${result.error}`);
|
||||
assert.strictEqual(result.output, 'false', `cursor should return false (may background), got: ${result.output}`);
|
||||
@@ -1776,8 +1776,8 @@ describe('dispatch-should-flatten query — behavioral', () => {
|
||||
// An unknown runtime has no registry entry → dispatch is null → fail-closed to true.
|
||||
const tmpDir = createTempProject();
|
||||
try {
|
||||
const result = runGsdTools(['query', 'dispatch-should-flatten', '--raw'], tmpDir, {
|
||||
GSD_RUNTIME: 'unknown-runtime-xyz',
|
||||
const result = runMsdTools(['query', 'dispatch-should-flatten', '--raw'], tmpDir, {
|
||||
MSD_RUNTIME: 'unknown-runtime-xyz',
|
||||
});
|
||||
// The query must succeed (exit 0) even for unknown runtimes — fail-closed not crash-closed.
|
||||
assert.ok(result.success, `Expected success (fail-closed), got error: ${result.error}`);
|
||||
@@ -1790,8 +1790,8 @@ describe('dispatch-should-flatten query — behavioral', () => {
|
||||
test('--json flag returns structured { runtime, shouldFlatten, dispatch }', () => {
|
||||
const tmpDir = createTempProject();
|
||||
try {
|
||||
const result = runGsdTools(['query', 'dispatch-should-flatten', '--json'], tmpDir, {
|
||||
GSD_RUNTIME: 'codex',
|
||||
const result = runMsdTools(['query', 'dispatch-should-flatten', '--json'], tmpDir, {
|
||||
MSD_RUNTIME: 'codex',
|
||||
});
|
||||
assert.ok(result.success, `Expected success, got error: ${result.error}`);
|
||||
let parsed;
|
||||
@@ -1812,9 +1812,9 @@ describe('dispatch-should-flatten query — behavioral', () => {
|
||||
}
|
||||
});
|
||||
|
||||
test('config.runtime takes precedence when GSD_RUNTIME not set', () => {
|
||||
// GSD_RUNTIME > config.runtime > 'claude'
|
||||
// Write config.json with runtime=codex; no GSD_RUNTIME override.
|
||||
test('config.runtime takes precedence when MSD_RUNTIME not set', () => {
|
||||
// MSD_RUNTIME > config.runtime > 'claude'
|
||||
// Write config.json with runtime=codex; no MSD_RUNTIME override.
|
||||
const tmpDir = createTempProject();
|
||||
try {
|
||||
fs.writeFileSync(
|
||||
@@ -1822,18 +1822,18 @@ describe('dispatch-should-flatten query — behavioral', () => {
|
||||
JSON.stringify({ runtime: 'codex' }),
|
||||
'utf-8',
|
||||
);
|
||||
// Override GSD_RUNTIME to '' (empty string) so any ambient value is cleared.
|
||||
// Override MSD_RUNTIME to '' (empty string) so any ambient value is cleared.
|
||||
// resolveRuntimeNameFromCandidates treats empty string as absent (normalizes
|
||||
// to '' which is falsy → skipped → falls through to config.runtime=codex).
|
||||
// This is the only way to suppress an ambient GSD_RUNTIME since runGsdTools
|
||||
// This is the only way to suppress an ambient MSD_RUNTIME since runMsdTools
|
||||
// merges { ...process.env, ...TEST_ENV_BASE, ...env } — passing '' as the
|
||||
// override overwrites the ambient value at the correct merge position.
|
||||
const result = runGsdTools(['query', 'dispatch-should-flatten', '--raw'], tmpDir, {
|
||||
GSD_RUNTIME: '',
|
||||
const result = runMsdTools(['query', 'dispatch-should-flatten', '--raw'], tmpDir, {
|
||||
MSD_RUNTIME: '',
|
||||
});
|
||||
// #2939: config.runtime=codex with GSD_RUNTIME cleared → codex maxDepth:1 → shouldFlatten=true (must inline)
|
||||
// #2939: config.runtime=codex with MSD_RUNTIME cleared → codex maxDepth:1 → shouldFlatten=true (must inline)
|
||||
assert.ok(result.success, `Expected success, got error: ${result.error}`);
|
||||
assert.strictEqual(result.output, 'true', `#2939: config.runtime=codex (GSD_RUNTIME cleared) should return true (must inline, maxDepth:1), got: ${result.output}`);
|
||||
assert.strictEqual(result.output, 'true', `#2939: config.runtime=codex (MSD_RUNTIME cleared) should return true (must inline, maxDepth:1), got: ${result.output}`);
|
||||
} finally {
|
||||
cleanupDir(tmpDir);
|
||||
}
|
||||
@@ -1849,7 +1849,7 @@ describe('dispatch-should-flatten query — behavioral', () => {
|
||||
const { describe: __foldDescribe } = require('node:test');
|
||||
__foldDescribe("folded:bug-3683-command-cross-reference-invariant (consolidation epic #1969 B3 #1972)", () => {
|
||||
// allow-test-rule: source-text-is-the-product (see #3683)
|
||||
// commands/gsd/*.md bodies are the deployed contract — cross-references between
|
||||
// commands/msd/*.md bodies are the deployed contract — cross-references between
|
||||
// them must stay coherent. This test inspects .md source to enforce the invariant.
|
||||
|
||||
'use strict';
|
||||
@@ -1859,7 +1859,7 @@ const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
|
||||
const COMMANDS_DIR = path.resolve(__dirname, '..', 'commands', 'gsd');
|
||||
const COMMANDS_DIR = path.resolve(__dirname, '..', 'commands', 'msd');
|
||||
|
||||
function readKnownTargets() {
|
||||
const commandNames = fs.readdirSync(COMMANDS_DIR)
|
||||
@@ -1873,14 +1873,14 @@ function stripFrontmatter(src) {
|
||||
}
|
||||
|
||||
// Word-boundary lookbehind matching fix-slash-commands.cjs buildColonPattern / buildPattern
|
||||
// Excludes path-y characters (~, ., /) so `~/gsd-workspaces`, `./gsd-foo`, `path/gsd-bar` don't match.
|
||||
// Trailing `(?![\w-]*\/)` rejects filesystem path segments like `${VAR}/gsd-core/bin` (the
|
||||
// runtime-launcher shim) where a non-path char (e.g. `}`) precedes `/gsd-core/` — those are
|
||||
// directory paths to the gsd-core/ runtime, not slash-command references (#604 rename).
|
||||
const REF_PATTERN = /(?<![a-zA-Z0-9_~./-])\/gsd[:-]([a-zA-Z0-9_-]+)(?![\w-]*\/)/g;
|
||||
// Excludes path-y characters (~, ., /) so `~/msd-workspaces`, `./msd-foo`, `path/msd-bar` don't match.
|
||||
// Trailing `(?![\w-]*\/)` rejects filesystem path segments like `${VAR}/msd-core/bin` (the
|
||||
// runtime-launcher shim) where a non-path char (e.g. `}`) precedes `/msd-core/` — those are
|
||||
// directory paths to the msd-core/ runtime, not slash-command references (#604 rename).
|
||||
const REF_PATTERN = /(?<![a-zA-Z0-9_~./-])\/msd[:-]([a-zA-Z0-9_-]+)(?![\w-]*\/)/g;
|
||||
|
||||
describe('bug-3683 command cross-reference invariant', () => {
|
||||
test('all /gsd:<X> and /gsd-<X> body refs resolve to known command base-names', () => {
|
||||
test('all /msd:<X> and /msd-<X> body refs resolve to known command base-names', () => {
|
||||
const { commandNames, knownTargets: knownSet } = readKnownTargets();
|
||||
const mdFiles = commandNames.sort().map(n => path.join(COMMANDS_DIR, `${n}.md`));
|
||||
|
||||
@@ -1902,7 +1902,7 @@ describe('bug-3683 command cross-reference invariant', () => {
|
||||
failures.push({
|
||||
file: relFile,
|
||||
line: idx + 1,
|
||||
ref: `/gsd${sep}${ref}`,
|
||||
ref: `/msd${sep}${ref}`,
|
||||
excerpt: line.trim(),
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user