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:
Jakub Zych
2026-10-06 01:47:40 +02:00
parent fe069b2a56
commit a9a7a328e6
2763 changed files with 78465 additions and 78434 deletions

View File

@@ -3,7 +3,7 @@
* audit-command-cutover.test.cjs — ADR-959 phase 4d-impl-3 equivalence tests.
*
* Verifies that `audit-uat` and `audit-open`, after cutover from the hardcoded
* `case 'audit-uat':` and `case 'audit-open':` arms in gsd-tools.cjs to the
* `case 'audit-uat':` and `case 'audit-open':` arms in msd-tools.cjs to the
* capability registry dispatch path (default → dispatchCapabilityCommand →
* audit-command-router.cjs → routeAuditUat | routeAuditOpen), behave
* identically to the old inline cases.
@@ -19,11 +19,11 @@
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');
const registry = require('../gsd-core/bin/lib/capability-registry.cjs');
const { routeAuditUat, routeAuditOpen } = require('../gsd-core/bin/lib/audit-command-router.cjs');
const { scanFencedBlocks } = require('../gsd-core/bin/lib/markdown-sectionizer.cjs');
const registry = require('../msd-core/bin/lib/capability-registry.cjs');
const { routeAuditUat, routeAuditOpen } = require('../msd-core/bin/lib/audit-command-router.cjs');
const { scanFencedBlocks } = require('../msd-core/bin/lib/markdown-sectionizer.cjs');
// ─── helpers ─────────────────────────────────────────────────────────────────
@@ -147,7 +147,7 @@ describe('audit cutover: dispatch path (default-case → capability registry)',
let tmpDir;
beforeEach(() => {
tmpDir = createTempProject('gsd-audit-cutover-');
tmpDir = createTempProject('msd-audit-cutover-');
});
afterEach(() => {
@@ -155,7 +155,7 @@ describe('audit cutover: dispatch path (default-case → capability registry)',
});
test('audit-uat dispatches via capability registry (no "Unknown command" error)', () => {
const result = runGsdTools(['audit-uat'], tmpDir);
const result = runMsdTools(['audit-uat'], tmpDir);
// audit-uat with a minimal project may succeed or fail on file-not-found;
// the key assertion is it never emits "Unknown command: audit-uat"
const isUnknownCmd = (result.error || '').includes('Unknown command: audit-uat');
@@ -166,7 +166,7 @@ describe('audit cutover: dispatch path (default-case → capability registry)',
});
test('audit-open dispatches via capability registry (no "Unknown command" error)', () => {
const result = runGsdTools(['audit-open'], tmpDir);
const result = runMsdTools(['audit-open'], tmpDir);
const isUnknownCmd = (result.error || '').includes('Unknown command: audit-open');
assert.strictEqual(isUnknownCmd, false,
`Must not emit "Unknown command: audit-open". stderr: ${result.error}`);
@@ -175,7 +175,7 @@ describe('audit cutover: dispatch path (default-case → capability registry)',
});
test('audit-open --json dispatches via capability registry', () => {
const result = runGsdTools(['audit-open', '--json'], tmpDir);
const result = runMsdTools(['audit-open', '--json'], tmpDir);
const isUnknownCmd = (result.error || '').includes('Unknown command: audit-open');
assert.strictEqual(isUnknownCmd, false,
`Must not emit "Unknown command: audit-open" with --json. stderr: ${result.error}`);
@@ -195,7 +195,7 @@ describe('audit cutover: output shape equivalence', () => {
let tmpDir;
beforeEach(() => {
tmpDir = createTempProject('gsd-audit-behavior-');
tmpDir = createTempProject('msd-audit-behavior-');
});
afterEach(() => {
@@ -203,7 +203,7 @@ describe('audit cutover: output shape equivalence', () => {
});
test('audit-open (text) succeeds and produces non-empty output', () => {
const result = runGsdTools(['audit-open'], tmpDir);
const result = runMsdTools(['audit-open'], tmpDir);
assert.ok(result.success,
`audit-open must succeed. stderr: ${result.error}`);
assert.ok(result.output && result.output.length > 0,
@@ -216,7 +216,7 @@ describe('audit cutover: output shape equivalence', () => {
});
test('audit-open --json produces valid JSON with expected shape', () => {
const result = runGsdTools(['audit-open', '--json'], tmpDir);
const result = runMsdTools(['audit-open', '--json'], tmpDir);
assert.ok(result.success,
`audit-open --json must succeed. stderr: ${result.error}`);
let parsed;
@@ -234,7 +234,7 @@ describe('audit cutover: output shape equivalence', () => {
});
test('audit-open (text) report title present as standalone line', () => {
const result = runGsdTools(['audit-open'], tmpDir);
const result = runMsdTools(['audit-open'], tmpDir);
assert.ok(result.success,
`audit-open must succeed. stderr: ${result.error}`);
const lines = result.output.split('\n').map(l => l.trim()).filter(Boolean);
@@ -245,7 +245,7 @@ describe('audit cutover: output shape equivalence', () => {
});
test('audit-uat succeeds and produces non-empty stdout', () => {
const result = runGsdTools(['audit-uat'], tmpDir);
const result = runMsdTools(['audit-uat'], tmpDir);
assert.ok(result.success,
`audit-uat must succeed. stderr: ${result.error}`);
assert.ok(result.output && result.output.length > 0,
@@ -253,44 +253,44 @@ describe('audit cutover: output shape equivalence', () => {
});
test('audit-uat --raw flag passes through (does not break dispatch)', () => {
const result = runGsdTools(['audit-uat', '--raw'], tmpDir);
// --raw is a gsd-tools global flag; it modifies output encoding but
const result = runMsdTools(['audit-uat', '--raw'], tmpDir);
// --raw is a msd-tools global flag; it modifies output encoding but
// the command must still succeed and produce output
assert.ok(result.success,
`audit-uat --raw must succeed. stderr: ${result.error}`);
});
});
// ─── 4. JSON-ERRORS — GSD_JSON_ERRORS mode passes through cleanly ────────────
// ─── 4. JSON-ERRORS — MSD_JSON_ERRORS mode passes through cleanly ────────────
describe('audit cutover: GSD_JSON_ERRORS mode (both commands succeed without structured error)', () => {
describe('audit cutover: MSD_JSON_ERRORS mode (both commands succeed without structured error)', () => {
let tmpDir;
beforeEach(() => {
tmpDir = createTempProject('gsd-audit-jsonerr-');
tmpDir = createTempProject('msd-audit-jsonerr-');
});
afterEach(() => {
cleanup(tmpDir);
});
test('audit-open --json with GSD_JSON_ERRORS=1 succeeds (no spurious error payload)', () => {
test('audit-open --json with MSD_JSON_ERRORS=1 succeeds (no spurious error payload)', () => {
// Successful commands must not emit JSON error payloads; verify exit 0.
const result = runGsdTools(['audit-open', '--json'], tmpDir, { GSD_JSON_ERRORS: '1' });
const result = runMsdTools(['audit-open', '--json'], tmpDir, { MSD_JSON_ERRORS: '1' });
assert.ok(result.success,
`audit-open --json must succeed even with GSD_JSON_ERRORS=1; stderr: ${result.error}`);
`audit-open --json must succeed even with MSD_JSON_ERRORS=1; stderr: ${result.error}`);
});
test('audit-open text with GSD_JSON_ERRORS=1 succeeds (no spurious error payload)', () => {
const result = runGsdTools(['audit-open'], tmpDir, { GSD_JSON_ERRORS: '1' });
test('audit-open text with MSD_JSON_ERRORS=1 succeeds (no spurious error payload)', () => {
const result = runMsdTools(['audit-open'], tmpDir, { MSD_JSON_ERRORS: '1' });
assert.ok(result.success,
`audit-open text mode must succeed even with GSD_JSON_ERRORS=1; stderr: ${result.error}`);
`audit-open text mode must succeed even with MSD_JSON_ERRORS=1; stderr: ${result.error}`);
});
test('audit-uat with GSD_JSON_ERRORS=1 succeeds (no spurious error payload)', () => {
const result = runGsdTools(['audit-uat'], tmpDir, { GSD_JSON_ERRORS: '1' });
test('audit-uat with MSD_JSON_ERRORS=1 succeeds (no spurious error payload)', () => {
const result = runMsdTools(['audit-uat'], tmpDir, { MSD_JSON_ERRORS: '1' });
assert.ok(result.success,
`audit-uat must succeed even with GSD_JSON_ERRORS=1; stderr: ${result.error}`);
`audit-uat must succeed even with MSD_JSON_ERRORS=1; stderr: ${result.error}`);
});
});
@@ -393,7 +393,7 @@ describe('audit cutover: registry entries correct', () => {
/**
* Regression test for #2659.
*
* The `audit-open` dispatch case in bin/gsd-tools.cjs previously called bare
* The `audit-open` dispatch case in bin/msd-tools.cjs previously called bare
* `output(...)` on both the --json and text branches. `output` is never in
* local scope — the entire core module is imported as `const core`, so every
* other case uses `core.output(...)`. The bare calls therefore crashed with
@@ -407,13 +407,13 @@ describe('audit cutover: registry entries correct', () => {
const { test, describe, 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');
describe('audit-open — does not crash with ReferenceError (#2659)', () => {
let tmpDir;
beforeEach(() => {
tmpDir = createTempProject('gsd-bug-2659-');
tmpDir = createTempProject('msd-bug-2659-');
});
afterEach(() => {
@@ -421,7 +421,7 @@ describe('audit-open — does not crash with ReferenceError (#2659)', () => {
});
test('audit-open (text output) succeeds and produces stdout', () => {
const result = runGsdTools('audit-open', tmpDir);
const result = runMsdTools('audit-open', tmpDir);
assert.ok(
result.success,
`audit-open must not crash. stderr: ${result.error}`
@@ -437,7 +437,7 @@ describe('audit-open — does not crash with ReferenceError (#2659)', () => {
});
test('audit-open --json succeeds and produces stdout', () => {
const result = runGsdTools(['audit-open', '--json'], tmpDir);
const result = runMsdTools(['audit-open', '--json'], tmpDir);
assert.ok(
result.success,
`audit-open --json must not crash. stderr: ${result.error}`
@@ -476,7 +476,7 @@ describe('audit-open — does not crash with ReferenceError (#2659)', () => {
/**
* Regression test for #2911.
*
* Two bugs in the `audit-open` dispatch case in bin/gsd-tools.cjs:
* Two bugs in the `audit-open` dispatch case in bin/msd-tools.cjs:
*
* 1. Bare `output(...)` calls (only `core.output` is in scope) → ReferenceError.
* 2. Even after switching to `core.output(formatted, raw)`, the human-readable
@@ -502,7 +502,7 @@ const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const { runGsdTools, createTempProject, cleanup } = require('./helpers.cjs');
const { runMsdTools, createTempProject, cleanup } = require('./helpers.cjs');
// ─── #3458 fixtures: phase-scoped scanners must also see archived phases ─────
//
@@ -625,7 +625,7 @@ describe('audit-open — output shape (#2911)', () => {
let tmpDir;
beforeEach(() => {
tmpDir = createTempProject('gsd-bug-2911-');
tmpDir = createTempProject('msd-bug-2911-');
});
afterEach(() => {
@@ -633,7 +633,7 @@ describe('audit-open — output shape (#2911)', () => {
});
test('text mode emits the formatted report as raw text (not JSON-encoded)', () => {
const result = runGsdTools('audit-open', tmpDir);
const result = runMsdTools('audit-open', tmpDir);
assert.ok(
result.success,
`audit-open must not crash. stderr: ${result.error}`
@@ -666,7 +666,7 @@ describe('audit-open — output shape (#2911)', () => {
});
test('--json mode emits parseable JSON matching auditOpenArtifacts shape', () => {
const result = runGsdTools(['audit-open', '--json'], tmpDir);
const result = runMsdTools(['audit-open', '--json'], tmpDir);
assert.ok(
result.success,
`audit-open --json must not crash. stderr: ${result.error}`
@@ -681,7 +681,7 @@ describe('audit-open — output shape (#2911)', () => {
assert.equal(typeof parsed, 'object', 'parsed payload must be an object');
assert.ok(parsed !== null, 'parsed payload must not be null');
// Shape contract from auditOpenArtifacts() in gsd-core/bin/lib/audit.cjs.
// Shape contract from auditOpenArtifacts() in msd-core/bin/lib/audit.cjs.
assert.equal(typeof parsed.scanned_at, 'string', 'must include scanned_at ISO timestamp');
assert.equal(typeof parsed.has_open_items, 'boolean', 'must include has_open_items boolean');
assert.equal(typeof parsed.counts, 'object', 'must include counts object');
@@ -730,7 +730,7 @@ describe('audit-open — output shape (#2911)', () => {
const archivedPhaseDir = path.join(tmpDir, '.planning', 'milestones', 'v1.0-phases', '01-alpha');
writePhaseArtifacts(archivedPhaseDir, '01', UNRESOLVED_ARTIFACTS);
const result = runGsdTools(['audit-open', '--json'], tmpDir);
const result = runMsdTools(['audit-open', '--json'], tmpDir);
assert.ok(result.success, `audit-open --json must not crash. stderr: ${result.error}`);
const parsed = JSON.parse(result.output);
@@ -748,7 +748,7 @@ describe('audit-open — output shape (#2911)', () => {
const archivedPhaseDir = path.join(tmpDir, '.planning', 'milestones', 'v1.0-phases', '02-beta');
writePhaseArtifacts(archivedPhaseDir, '02', UNRESOLVED_ARTIFACTS);
const result = runGsdTools(['audit-open', '--json'], tmpDir);
const result = runMsdTools(['audit-open', '--json'], tmpDir);
assert.ok(result.success, `audit-open --json must not crash. stderr: ${result.error}`);
const parsed = JSON.parse(result.output);
@@ -763,7 +763,7 @@ describe('audit-open — output shape (#2911)', () => {
const activePhaseDir = path.join(tmpDir, '.planning', 'phases', '01-alpha');
writePhaseArtifacts(activePhaseDir, '01', UNRESOLVED_ARTIFACTS);
const result = runGsdTools(['audit-open', '--json'], tmpDir);
const result = runMsdTools(['audit-open', '--json'], tmpDir);
assert.ok(result.success, `audit-open --json must not crash. stderr: ${result.error}`);
const parsed = JSON.parse(result.output);
@@ -781,7 +781,7 @@ describe('audit-open — output shape (#2911)', () => {
const archivedPhaseDir = path.join(tmpDir, '.planning', 'milestones', 'v1.0-phases', '01-alpha');
writePhaseArtifacts(archivedPhaseDir, '01', RESOLVED_ARTIFACTS);
const result = runGsdTools(['audit-open', '--json'], tmpDir);
const result = runMsdTools(['audit-open', '--json'], tmpDir);
assert.ok(result.success, `audit-open --json must not crash. stderr: ${result.error}`);
const parsed = JSON.parse(result.output);
@@ -834,7 +834,7 @@ describe('audit-open — output shape (#2911)', () => {
if (!tryMkdirForgedName(t, forgedPhaseDir)) return;
fs.writeFileSync(path.join(forgedPhaseDir, 'deferred-items.md'), DEFERRED_ITEM_UNRESOLVED);
const result = runGsdTools('audit-open', tmpDir);
const result = runMsdTools('audit-open', tmpDir);
assert.ok(result.success, `audit-open must not crash. stderr: ${result.error}`);
const lines = result.output.split('\n').map(l => l.trim());
@@ -849,7 +849,7 @@ describe('audit-open — output shape (#2911)', () => {
if (!tryMkdirForgedName(t, forgedPhaseDir)) return;
fs.writeFileSync(path.join(forgedPhaseDir, 'deferred-items.md'), DEFERRED_ITEM_UNRESOLVED);
const result = runGsdTools('audit-open', tmpDir);
const result = runMsdTools('audit-open', tmpDir);
assert.ok(result.success, `audit-open must not crash. stderr: ${result.error}`);
assert.ok(
@@ -867,7 +867,7 @@ describe('audit-open — output shape (#2911)', () => {
const archivedPhaseDir = path.join(tmpDir, '.planning', 'milestones', 'v1.0-phases', '02-beta');
writePhaseArtifacts(archivedPhaseDir, '02', UNRESOLVED_ARTIFACTS);
const result = runGsdTools(['audit-open', '--json'], tmpDir);
const result = runMsdTools(['audit-open', '--json'], tmpDir);
assert.ok(result.success, `audit-open --json must not crash. stderr: ${result.error}`);
const parsed = JSON.parse(result.output);
@@ -889,7 +889,7 @@ describe('audit-open — output shape (#2911)', () => {
fs.rmSync(path.join(tmpDir, '.planning', 'phases'), { recursive: true, force: true });
fs.writeFileSync(path.join(tmpDir, '.planning', 'phases'), 'not a directory');
const result = runGsdTools(['audit-open', '--json'], tmpDir);
const result = runMsdTools(['audit-open', '--json'], tmpDir);
assert.ok(result.success, `audit-open --json must not crash. stderr: ${result.error}`);
const parsed = JSON.parse(result.output);
@@ -909,7 +909,7 @@ describe('audit-open — output shape (#2911)', () => {
// getArchivedPhaseDirs's readdirSync throws (ENOTDIR).
fs.writeFileSync(path.join(tmpDir, '.planning', 'milestones'), 'not a directory');
const result = runGsdTools(['audit-open', '--json'], tmpDir);
const result = runMsdTools(['audit-open', '--json'], tmpDir);
assert.ok(result.success, `audit-open --json must not crash. stderr: ${result.error}`);
const parsed = JSON.parse(result.output);
@@ -936,7 +936,7 @@ describe('audit-open — output shape (#2911)', () => {
const archivedPhaseDir = path.join(tmpDir, '.planning', 'milestones', 'v1.0-phases', '01-alpha');
writePhaseArtifacts(archivedPhaseDir, '01', UNRESOLVED_ARTIFACTS);
const jsonResult = runGsdTools(['audit-open', '--json'], tmpDir);
const jsonResult = runMsdTools(['audit-open', '--json'], tmpDir);
assert.ok(jsonResult.success, `audit-open --json must not crash. stderr: ${jsonResult.error}`);
const parsed = JSON.parse(jsonResult.output);
@@ -947,7 +947,7 @@ describe('audit-open — output shape (#2911)', () => {
assert.deepEqual(archivedFlags, [false, true],
'exactly one of the two duplicate-named entries must carry archived_milestone');
const textResult = runGsdTools(['audit-open'], tmpDir);
const textResult = runMsdTools(['audit-open'], tmpDir);
assert.ok(textResult.success, `audit-open (text) must not crash. stderr: ${textResult.error}`);
const uatLines = textResult.output.split('\n').filter(l => l.includes('01-UAT.md'));
assert.equal(uatLines.length, 2,
@@ -963,7 +963,7 @@ describe('audit-open — output shape (#2911)', () => {
writePhaseArtifacts(path.join(tmpDir, '.planning', 'milestones', 'v1.9-phases', '01-b'), '01', UNRESOLVED_ARTIFACTS);
writePhaseArtifacts(path.join(tmpDir, '.planning', 'milestones', 'v1.10-phases', '01-c'), '01', UNRESOLVED_ARTIFACTS);
const result = runGsdTools(['audit-open', '--json'], tmpDir);
const result = runMsdTools(['audit-open', '--json'], tmpDir);
assert.ok(result.success, `audit-open --json must not crash. stderr: ${result.error}`);
const parsed = JSON.parse(result.output);
@@ -986,7 +986,7 @@ describe('audit-open — output shape (#2911)', () => {
const forgedQuickDir = path.join(tmpDir, '.planning', 'quick', FORGED_QUICK_TASK_DIR_NAME);
if (!tryMkdirForgedName(t, forgedQuickDir)) return;
const result = runGsdTools('audit-open', tmpDir);
const result = runMsdTools('audit-open', tmpDir);
assert.ok(result.success, `audit-open must not crash. stderr: ${result.error}`);
const lines = result.output.split('\n').map(l => l.trim());
@@ -1000,7 +1000,7 @@ describe('audit-open — output shape (#2911)', () => {
const forgedQuickDir = path.join(tmpDir, '.planning', 'quick', FORGED_QUICK_TASK_DIR_NAME);
if (!tryMkdirForgedName(t, forgedQuickDir)) return;
const result = runGsdTools('audit-open', tmpDir);
const result = runMsdTools('audit-open', tmpDir);
assert.ok(result.success, `audit-open must not crash. stderr: ${result.error}`);
assert.ok(
@@ -1041,7 +1041,7 @@ const fs = require('node:fs');
const path = require('node:path');
const os = require('node:os');
const auditModule = require('../gsd-core/bin/lib/audit.cjs');
const auditModule = require('../msd-core/bin/lib/audit.cjs');
const { auditOpenArtifacts } = auditModule;
const { cleanup } = require('./helpers.cjs');
@@ -1050,17 +1050,17 @@ function mkTmp() {
}
describe('bug #2836: audit-open quick-task summary filename + UAT terminal status', () => {
// Ensure GSD env vars do not redirect planningDir() away from our fixture.
// Ensure MSD env vars do not redirect planningDir() away from our fixture.
let prevProject, prevWorkstream;
before(() => {
prevProject = process.env.GSD_PROJECT;
prevWorkstream = process.env.GSD_WORKSTREAM;
delete process.env.GSD_PROJECT;
delete process.env.GSD_WORKSTREAM;
prevProject = process.env.MSD_PROJECT;
prevWorkstream = process.env.MSD_WORKSTREAM;
delete process.env.MSD_PROJECT;
delete process.env.MSD_WORKSTREAM;
});
after(() => {
if (prevProject !== undefined) process.env.GSD_PROJECT = prevProject;
if (prevWorkstream !== undefined) process.env.GSD_WORKSTREAM = prevWorkstream;
if (prevProject !== undefined) process.env.MSD_PROJECT = prevProject;
if (prevWorkstream !== undefined) process.env.MSD_WORKSTREAM = prevWorkstream;
});
test('quick task with ${quick_id}-SUMMARY.md is recognized as complete (not missing)', () => {
@@ -1276,7 +1276,7 @@ describe('bug #2836: workflows/help.md one-liner reconciliation', () => {
test('help.md quick-task one-liner uses ${quick_id}-SUMMARY.md pattern', () => {
// After #3039, help content moved into help/modes/full.md.
const helpPath = path.resolve(
__dirname, '..', 'gsd-core', 'workflows', 'help', 'modes', 'full.md'
__dirname, '..', 'msd-core', 'workflows', 'help', 'modes', 'full.md'
);
const content = fs.readFileSync(helpPath, 'utf-8');
@@ -1321,7 +1321,7 @@ describe('bug #2836: workflows/help.md one-liner reconciliation', () => {
*
* Primary guard: behavioral audit-scanner tests (tasks read by scanQuickTasks)
* Secondary guard: template-contract text checks (template text IS the runtime contract)
* Writer-path guard: contract assertions on quick.md + gsd-executor.md (source-text-is-the-product)
* Writer-path guard: contract assertions on quick.md + msd-executor.md (source-text-is-the-product)
*/
'use strict';
@@ -1332,13 +1332,13 @@ const fs = require('node:fs');
const path = require('node:path');
const os = require('node:os');
const auditModule = require('../gsd-core/bin/lib/audit.cjs');
const auditModule = require('../msd-core/bin/lib/audit.cjs');
const { auditOpenArtifacts } = auditModule;
const { cleanup } = require('./helpers.cjs');
const TEMPLATES_DIR = path.resolve(__dirname, '..', 'gsd-core', 'templates');
const QUICK_MD = path.resolve(__dirname, '..', 'gsd-core', 'workflows', 'quick.md');
const EXECUTOR_MD = path.resolve(__dirname, '..', 'agents', 'gsd-executor.md');
const TEMPLATES_DIR = path.resolve(__dirname, '..', 'msd-core', 'templates');
const QUICK_MD = path.resolve(__dirname, '..', 'msd-core', 'workflows', 'quick.md');
const EXECUTOR_MD = path.resolve(__dirname, '..', 'agents', 'msd-executor.md');
function mkTmp() {
return fs.mkdtempSync(path.join(os.tmpdir(), 'bug-950-'));
@@ -1378,17 +1378,17 @@ function extractFrontmatter(content) {
}
describe('bug #950: quick-task SUMMARY must carry status: complete', () => {
// Ensure GSD env vars do not redirect planningDir() away from our fixture.
// Ensure MSD env vars do not redirect planningDir() away from our fixture.
let prevProject, prevWorkstream;
before(() => {
prevProject = process.env.GSD_PROJECT;
prevWorkstream = process.env.GSD_WORKSTREAM;
delete process.env.GSD_PROJECT;
delete process.env.GSD_WORKSTREAM;
prevProject = process.env.MSD_PROJECT;
prevWorkstream = process.env.MSD_WORKSTREAM;
delete process.env.MSD_PROJECT;
delete process.env.MSD_WORKSTREAM;
});
after(() => {
if (prevProject !== undefined) process.env.GSD_PROJECT = prevProject;
if (prevWorkstream !== undefined) process.env.GSD_WORKSTREAM = prevWorkstream;
if (prevProject !== undefined) process.env.MSD_PROJECT = prevProject;
if (prevWorkstream !== undefined) process.env.MSD_WORKSTREAM = prevWorkstream;
});
// ── Behavioral: scanner recognizes complete quick tasks ───────────────────
@@ -1535,11 +1535,11 @@ describe('bug #950: quick-task SUMMARY must carry status: complete', () => {
const fm = extractFrontmatter(content);
assert.ok(
fm !== null,
'gsd-core/templates/summary.md: could not locate a YAML frontmatter block (leading --- or fenced ```markdown --- block)'
'msd-core/templates/summary.md: could not locate a YAML frontmatter block (leading --- or fenced ```markdown --- block)'
);
assert.ok(
/^status:\s*complete\s*$/m.test(fm),
`gsd-core/templates/summary.md: \`status: complete\` not found in the frontmatter block.\n` +
`msd-core/templates/summary.md: \`status: complete\` not found in the frontmatter block.\n` +
`Frontmatter extracted:\n${fm}`
);
});
@@ -1549,11 +1549,11 @@ describe('bug #950: quick-task SUMMARY must carry status: complete', () => {
const fm = extractFrontmatter(content);
assert.ok(
fm !== null,
'gsd-core/templates/summary-minimal.md: could not locate a leading YAML frontmatter block'
'msd-core/templates/summary-minimal.md: could not locate a leading YAML frontmatter block'
);
assert.ok(
/^status:\s*complete\s*$/m.test(fm),
`gsd-core/templates/summary-minimal.md: \`status: complete\` not found in the frontmatter block.\n` +
`msd-core/templates/summary-minimal.md: \`status: complete\` not found in the frontmatter block.\n` +
`Frontmatter extracted:\n${fm}`
);
});
@@ -1563,11 +1563,11 @@ describe('bug #950: quick-task SUMMARY must carry status: complete', () => {
const fm = extractFrontmatter(content);
assert.ok(
fm !== null,
'gsd-core/templates/summary-standard.md: could not locate a leading YAML frontmatter block'
'msd-core/templates/summary-standard.md: could not locate a leading YAML frontmatter block'
);
assert.ok(
/^status:\s*complete\s*$/m.test(fm),
`gsd-core/templates/summary-standard.md: \`status: complete\` not found in the frontmatter block.\n` +
`msd-core/templates/summary-standard.md: \`status: complete\` not found in the frontmatter block.\n` +
`Frontmatter extracted:\n${fm}`
);
});
@@ -1577,17 +1577,17 @@ describe('bug #950: quick-task SUMMARY must carry status: complete', () => {
const fm = extractFrontmatter(content);
assert.ok(
fm !== null,
'gsd-core/templates/summary-complex.md: could not locate a leading YAML frontmatter block'
'msd-core/templates/summary-complex.md: could not locate a leading YAML frontmatter block'
);
assert.ok(
/^status:\s*complete\s*$/m.test(fm),
`gsd-core/templates/summary-complex.md: \`status: complete\` not found in the frontmatter block.\n` +
`msd-core/templates/summary-complex.md: \`status: complete\` not found in the frontmatter block.\n` +
`Frontmatter extracted:\n${fm}`
);
});
// ── Writer-path contract checks ───────────────────────────────────────────
// Guards quick.md and gsd-executor.md so a future edit removing `status: complete`
// Guards quick.md and msd-executor.md so a future edit removing `status: complete`
// from the SUMMARY-creation instructions would fail the suite before the bug recurs.
// (source-text-is-the-product: the deployed .md text IS the runtime contract for agents)
@@ -1595,16 +1595,16 @@ describe('bug #950: quick-task SUMMARY must carry status: complete', () => {
const content = fs.readFileSync(QUICK_MD, 'utf-8');
assert.ok(
/status:\s*complete/.test(content),
'gsd-core/workflows/quick.md must instruct the executor to write `status: complete` in the SUMMARY frontmatter. ' +
'msd-core/workflows/quick.md must instruct the executor to write `status: complete` in the SUMMARY frontmatter. ' +
'The <constraints> block must contain the `status: complete` requirement so a future edit cannot silently drop it.'
);
});
test('[WRITER-PATH] gsd-executor.md frontmatter spec requires status: complete', () => {
test('[WRITER-PATH] msd-executor.md frontmatter spec requires status: complete', () => {
const content = fs.readFileSync(EXECUTOR_MD, 'utf-8');
assert.ok(
/status[\s\S]{0,40}complete/.test(content),
'agents/gsd-executor.md must document `status: complete` as a required SUMMARY frontmatter field. ' +
'agents/msd-executor.md must document `status: complete` as a required SUMMARY frontmatter field. ' +
'The Frontmatter section must include `status: complete` so the executor always emits it.'
);
});
@@ -1628,7 +1628,7 @@ describe('bug #950: quick-task SUMMARY must carry status: complete', () => {
{
const fs = require('node:fs');
const path = require('node:path');
const { splitLines } = require('../gsd-core/bin/lib/text-lines.cjs');
const { splitLines } = require('../msd-core/bin/lib/text-lines.cjs');
function readJson(result) {
assert.ok(result.success, `command must succeed. stdout: ${result.output}\nstderr: ${result.error}`);
@@ -1636,17 +1636,17 @@ describe('bug #950: quick-task SUMMARY must carry status: complete', () => {
}
function ack(tmpDir, args) {
return runGsdTools(['audit-open', 'acknowledge', ...args, '--json'], tmpDir);
return runMsdTools(['audit-open', 'acknowledge', ...args, '--json'], tmpDir);
}
function audit(tmpDir) {
return readJson(runGsdTools(['audit-open', '--json'], tmpDir));
return readJson(runMsdTools(['audit-open', '--json'], tmpDir));
}
describe('audit-open acknowledge — suppression seam (#3458 follow-up)', () => {
let tmpDir;
beforeEach(() => { tmpDir = createTempProject('gsd-3458-ack-'); });
beforeEach(() => { tmpDir = createTempProject('msd-3458-ack-'); });
afterEach(() => { cleanup(tmpDir); });
function planningPath(...segs) {
@@ -2450,7 +2450,7 @@ describe('bug #950: quick-task SUMMARY must carry status: complete', () => {
assert.ok(ack(tmpDir, ['--category', 'uat_gaps', '--phase', '01', '--file', '01-UAT.md', '--milestone', 'v1.0', '--at', '2026-08-15']).success);
// All-clear case: the only item left is a previously-acknowledged one.
const clearReport = runGsdTools(['audit-open'], tmpDir);
const clearReport = runMsdTools(['audit-open'], tmpDir);
assert.ok(clearReport.success, `stderr: ${clearReport.error}`);
assert.match(clearReport.output, /1 previously acknowledged item/i, 'all-clear footer must disclose the suppressed item');
@@ -2461,7 +2461,7 @@ describe('bug #950: quick-task SUMMARY must carry status: complete', () => {
fs.mkdirSync(debugDir, { recursive: true });
fs.writeFileSync(path.join(debugDir, 'investigate.md'), '---\nstatus: open\n---\n## Current Focus\ndigging\n');
const openReport = runGsdTools(['audit-open'], tmpDir);
const openReport = runMsdTools(['audit-open'], tmpDir);
assert.ok(openReport.success, `stderr: ${openReport.error}`);
assert.match(openReport.output, /previously acknowledged item/i, 'footer must still disclose the acknowledged item while other items are open');
});
@@ -2586,17 +2586,17 @@ describe('bug #950: quick-task SUMMARY must carry status: complete', () => {
}
function auditJson(tmpDir) {
return readJson4546(runGsdTools(['audit-open', '--json'], tmpDir));
return readJson4546(runMsdTools(['audit-open', '--json'], tmpDir));
}
function ack4546(tmpDir, args) {
return runGsdTools(['audit-open', 'acknowledge', ...args, '--json'], tmpDir);
return runMsdTools(['audit-open', 'acknowledge', ...args, '--json'], tmpDir);
}
describe('audit seed identity agrees with list-seeds (#4378)', () => {
let tmpDir;
beforeEach(() => { tmpDir = createTempProject('gsd-4378-seedident-'); });
beforeEach(() => { tmpDir = createTempProject('msd-4378-seedident-'); });
afterEach(() => { cleanup(tmpDir); });
function planningPath(...segs) {
@@ -2681,7 +2681,7 @@ describe('#4802: acknowledge refuses targets whose frontmatter fails to parse',
const path = require('node:path');
let tmpDir;
beforeEach(() => { tmpDir = createTempProject('gsd-4802-'); });
beforeEach(() => { tmpDir = createTempProject('msd-4802-'); });
afterEach(() => { cleanup(tmpDir); });
function planningPath(...segs) {
@@ -2705,7 +2705,7 @@ describe('#4802: acknowledge refuses targets whose frontmatter fails to parse',
].join('\n');
function ack(tmpDir, args) {
return runGsdTools(['audit-open', 'acknowledge', ...args, '--json'], tmpDir);
return runMsdTools(['audit-open', 'acknowledge', ...args, '--json'], tmpDir);
}
test('threads: an unparseable-frontmatter target is refused, file byte-identical', () => {