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

@@ -1,5 +1,5 @@
/**
* Tests for src/io.cts (compiled to gsd-core/bin/lib/io.cjs).
* Tests for src/io.cts (compiled to msd-core/bin/lib/io.cjs).
*
* Verifies behavioural contracts of the extracted CLI I/O primitives:
* - output() writes expected structure to stdout
@@ -18,11 +18,11 @@ const os = require('node:os');
const fs = require('node:fs');
const { captureFdSync, suppressFdAsync } = require('./helpers.cjs');
const io = require('../gsd-core/bin/lib/io.cjs');
const io = require('../msd-core/bin/lib/io.cjs');
const {
ExitError, resolveContractVersion, setPendingOutcome, getPendingOutcome, runMain,
} = require('../gsd-core/bin/lib/cli-exit.cjs');
const { EXIT_CODES } = require('../gsd-core/bin/lib/exit-code-registry.cjs');
} = require('../msd-core/bin/lib/cli-exit.cjs');
const { EXIT_CODES } = require('../msd-core/bin/lib/exit-code-registry.cjs');
const { runNode } = require('./helpers/process-seam.cjs');
const { toLegacyResult } = require('./helpers/git-fixture.cjs');
const { PROBE_TIMEOUT_MS } = require('./helpers/timeouts.cjs');
@@ -104,7 +104,7 @@ describe('setJsonErrorMode / getJsonErrorMode', () => {
// test it by spawning a child process and capturing its stdout.
describe('output()', () => {
const ioPath = path.resolve(__dirname, '../gsd-core/bin/lib/io.cjs');
const ioPath = path.resolve(__dirname, '../msd-core/bin/lib/io.cjs');
test('emits JSON-serialised result to stdout', () => {
const script = `
@@ -192,8 +192,8 @@ describe('output()', () => {
// ─── error() ─────────────────────────────────────────────────────────────────
describe('error()', () => {
const ioPath = path.resolve(__dirname, '../gsd-core/bin/lib/io.cjs');
const cliExitPath = path.resolve(__dirname, '../gsd-core/bin/lib/cli-exit.cjs');
const ioPath = path.resolve(__dirname, '../msd-core/bin/lib/io.cjs');
const cliExitPath = path.resolve(__dirname, '../msd-core/bin/lib/cli-exit.cjs');
// ADR-3889: io.error() now throws ExitError instead of calling
// process.exit() directly. A bare `node -e` script that calls io.error()
@@ -284,24 +284,24 @@ describe('error()', () => {
});
});
// ─── GSD_TEMP_DIR / reapStaleTempFiles ───────────────────────────────────────
// ─── MSD_TEMP_DIR / reapStaleTempFiles ───────────────────────────────────────
describe('GSD_TEMP_DIR', () => {
test('resolves to <tmpdir>/gsd', () => {
assert.strictEqual(io.GSD_TEMP_DIR, path.join(os.tmpdir(), 'gsd'));
describe('MSD_TEMP_DIR', () => {
test('resolves to <tmpdir>/msd', () => {
assert.strictEqual(io.MSD_TEMP_DIR, path.join(os.tmpdir(), 'msd'));
});
});
describe('reapStaleTempFiles (via io)', () => {
const TEST_PREFIX = 'gsd-io-test-';
const TEST_PREFIX = 'msd-io-test-';
afterEach(() => {
// clean up any test files we created
try {
const entries = fs.readdirSync(io.GSD_TEMP_DIR);
const entries = fs.readdirSync(io.MSD_TEMP_DIR);
for (const e of entries) {
if (e.startsWith(TEST_PREFIX)) {
const p = path.join(io.GSD_TEMP_DIR, e);
const p = path.join(io.MSD_TEMP_DIR, e);
try { fs.unlinkSync(p); } catch { /* ignore */ }
}
}
@@ -309,8 +309,8 @@ describe('reapStaleTempFiles (via io)', () => {
});
test('removes stale files beyond maxAgeMs', () => {
fs.mkdirSync(io.GSD_TEMP_DIR, { recursive: true });
const stalePath = path.join(io.GSD_TEMP_DIR, TEST_PREFIX + 'stale.json');
fs.mkdirSync(io.MSD_TEMP_DIR, { recursive: true });
const stalePath = path.join(io.MSD_TEMP_DIR, TEST_PREFIX + 'stale.json');
fs.writeFileSync(stalePath, '{}');
// backdate mtime so it looks older than 1ms
const old = new Date(Date.now() - 10000);
@@ -321,18 +321,18 @@ describe('reapStaleTempFiles (via io)', () => {
});
test('keeps fresh files within maxAgeMs', () => {
fs.mkdirSync(io.GSD_TEMP_DIR, { recursive: true });
const freshPath = path.join(io.GSD_TEMP_DIR, TEST_PREFIX + 'fresh.json');
fs.mkdirSync(io.MSD_TEMP_DIR, { recursive: true });
const freshPath = path.join(io.MSD_TEMP_DIR, TEST_PREFIX + 'fresh.json');
fs.writeFileSync(freshPath, '{}');
// mtime is just now — well within a 1-hour window
io.reapStaleTempFiles(TEST_PREFIX, { maxAgeMs: 60 * 60 * 1000 });
assert.ok(fs.existsSync(freshPath), 'fresh file should have been kept');
});
test('does not throw when GSD_TEMP_DIR does not exist yet', () => {
test('does not throw when MSD_TEMP_DIR does not exist yet', () => {
// reap against a non-existent prefix — must not throw
assert.doesNotThrow(() => {
io.reapStaleTempFiles('gsd-io-nonexistent-prefix-xyz-', { maxAgeMs: 0 });
io.reapStaleTempFiles('msd-io-nonexistent-prefix-xyz-', { maxAgeMs: 0 });
});
});
@@ -347,8 +347,8 @@ describe('reapStaleTempFiles (via io)', () => {
const MAX_AGE_MS = 5000;
function plantFileAtAge(t, ageMs) {
fs.mkdirSync(io.GSD_TEMP_DIR, { recursive: true });
const p = path.join(io.GSD_TEMP_DIR, TEST_PREFIX + `boundary-${ageMs}.json`);
fs.mkdirSync(io.MSD_TEMP_DIR, { recursive: true });
const p = path.join(io.MSD_TEMP_DIR, TEST_PREFIX + `boundary-${ageMs}.json`);
fs.writeFileSync(p, '{}');
fs.utimesSync(p, new Date(MTIME_MS), new Date(MTIME_MS));
t.mock.timers.enable(['Date']);
@@ -572,15 +572,15 @@ describe('bug #1008: io.error() tolerates a full non-blocking stderr pipe', () =
const { describe: __foldDescribe } = require('node:test');
__foldDescribe("folded:bug-1891-file-resolution (consolidation epic #1969 B5 #1974)", () => {
// allow-test-rule: structural-implementation-guard (see #1891)
// gsd-tools.cjs @file: resolution is a low-level stdout interception that cannot be
// exercised end-to-end via runGsdTools without a real workflow that emits @file: output.
// msd-tools.cjs @file: resolution is a low-level stdout interception that cannot be
// exercised end-to-end via runMsdTools without a real workflow that emits @file: output.
// These structural tests guard the interception wiring until a behavioral integration
// test suite for the full @file: path is added.
/**
* Regression tests for bug #1891
*
* gsd-tools.cjs must transparently resolve @file: references in stdout
* msd-tools.cjs must transparently resolve @file: references in stdout
* so that workflows never see the @file: prefix. This eliminates the
* bash-specific `if [[ "$INIT" == @file:* ]]` check that breaks on
* PowerShell and other non-bash shells.
@@ -593,17 +593,17 @@ const assert = require('node:assert/strict');
const fs = require('fs');
const path = require('path');
const GSD_TOOLS_SRC = path.join(__dirname, '..', 'gsd-core', 'bin', 'gsd-tools.cjs');
const MSD_TOOLS_SRC = path.join(__dirname, '..', 'msd-core', 'bin', 'msd-tools.cjs');
describe('bug #1891: @file: resolution in gsd-tools.cjs', () => {
describe('bug #1891: @file: resolution in msd-tools.cjs', () => {
let src;
before(() => {
// allow-test-rule: structural-implementation-guard (see #1891) — gsd-tools.cjs's
// allow-test-rule: structural-implementation-guard (see #1891) — msd-tools.cjs's
// stdout @file: interception has no exported symbol to assert on directly; every
// src.includes()/indexOf()/match() call in this describe block traces back to this
// read (#3545)
src = fs.readFileSync(GSD_TOOLS_SRC, 'utf-8');
src = fs.readFileSync(MSD_TOOLS_SRC, 'utf-8');
});
test('main() intercepts stdout and resolves @file: references', () => {
@@ -640,13 +640,13 @@ describe('bug #1891: @file: resolution in gsd-tools.cjs', () => {
}
// ═══════════════════════════════════════════════════════════════════════════
// #3912 (ADR-3889 §4, epic #3889 Phase 8) — gsd-tools declares outcomes,
// pinned at v1. See .gsd/phase/enhance-3912-gsd-tools-outcomes/40-design.md
// #3912 (ADR-3889 §4, epic #3889 Phase 8) — msd-tools declares outcomes,
// pinned at v1. See .msd/phase/enhance-3912-msd-tools-outcomes/40-design.md
// and 50-test-matrix.md.
// ═══════════════════════════════════════════════════════════════════════════
const CLI_EXIT_PATH_3912 = path.resolve(__dirname, '../gsd-core/bin/lib/cli-exit.cjs');
const IO_PATH_3912 = path.resolve(__dirname, '../gsd-core/bin/lib/io.cjs');
const CLI_EXIT_PATH_3912 = path.resolve(__dirname, '../msd-core/bin/lib/cli-exit.cjs');
const IO_PATH_3912 = path.resolve(__dirname, '../msd-core/bin/lib/io.cjs');
const REGISTERED_NAMES_3912 = EXIT_CODES.map((e) => e.name);
const CODE_FOR_3912 = new Map(EXIT_CODES.map((e) => [e.name, e.code]));
const VERSIONS_3912 = ['v1', 'v2'];
@@ -929,7 +929,7 @@ describe('#3912 C5/D: output({error}) DEGRADED reaches process.exitCode only thr
resolveContractVersion({ argv: ['node', 'x'], env: {} });
});
test('a real gsd-tools-shaped main() that calls output({error}) and returns nothing exits 0 under v1, 80 under v2', () => {
test('a real msd-tools-shaped main() that calls output({error}) and returns nothing exits 0 under v1, 80 under v2', () => {
const script = `
const io = require(${JSON.stringify(IO_PATH_3912)});
const c = require(${JSON.stringify(CLI_EXIT_PATH_3912)});
@@ -940,11 +940,11 @@ describe('#3912 C5/D: output({error}) DEGRADED reaches process.exitCode only thr
setImmediate(() => {});
`;
const v1 = toLegacyResult(runNode(['-e', script], {
timeoutMs: PROBE_TIMEOUT_MS, env: { ...process.env, GSD_EXIT_CONTRACT: 'v1' },
timeoutMs: PROBE_TIMEOUT_MS, env: { ...process.env, MSD_EXIT_CONTRACT: 'v1' },
}));
assert.strictEqual(v1.status, 0, `stderr: ${v1.stderr}`);
const v2 = toLegacyResult(runNode(['-e', script], {
timeoutMs: PROBE_TIMEOUT_MS, env: { ...process.env, GSD_EXIT_CONTRACT: 'v2' },
timeoutMs: PROBE_TIMEOUT_MS, env: { ...process.env, MSD_EXIT_CONTRACT: 'v2' },
}));
assert.strictEqual(v2.status, 80, `stderr: ${v2.stderr}`);
});
@@ -960,7 +960,7 @@ describe('#3912 C5/D: output({error}) DEGRADED reaches process.exitCode only thr
setImmediate(() => {});
`;
const r = toLegacyResult(runNode(['-e', script], {
timeoutMs: PROBE_TIMEOUT_MS, env: { ...process.env, GSD_EXIT_CONTRACT: 'v2' },
timeoutMs: PROBE_TIMEOUT_MS, env: { ...process.env, MSD_EXIT_CONTRACT: 'v2' },
}));
assert.strictEqual(r.status, 0, `an explicit 0 return must win over the DEGRADED cell; stderr: ${r.stderr}`);
});
@@ -1129,33 +1129,33 @@ describe('#3912 A6: error() stderr bytes are unchanged by this phase', () => {
// ═══════════════════════════════════════════════════════════════════════════
// #3957 (epic #3473 B9) — declineNoOp: the shared no-op-decline helper.
// .gsd/phase/enhance-3957-noop-real-condition/{40-design,50-test-matrix}.md
// .msd/phase/enhance-3957-noop-real-condition/{40-design,50-test-matrix}.md
// Row 19 (the one new test outside state.test.cjs/roadmap.test.cjs).
// ═══════════════════════════════════════════════════════════════════════════
describe('#3957 (epic #3473 B9): declineNoOp', () => {
const ioPathFor3957 = path.resolve(__dirname, '../gsd-core/bin/lib/io.cjs');
const ioPathFor3957 = path.resolve(__dirname, '../msd-core/bin/lib/io.cjs');
test('emits the [gsd-tools] WARNING: disclosure and the false-flag JSON payload', () => {
test('emits the [msd-tools] WARNING: disclosure and the false-flag JSON payload', () => {
const script = `
const io = require(${JSON.stringify(ioPathFor3957)});
io.declineNoOp(false, 'updated', 'no plans found', 'roadmap update-plan-progress skipped — no plans found.', { plan_count: 0 });
`;
const result = runScript(script);
assert.strictEqual(result.status, 0, `process exited non-zero: ${result.stderr}`);
assert.strictEqual(result.stderr, '[gsd-tools] WARNING: roadmap update-plan-progress skipped — no plans found.\n');
assert.strictEqual(result.stderr, '[msd-tools] WARNING: roadmap update-plan-progress skipped — no plans found.\n');
const out = JSON.parse(result.stdout);
assert.deepStrictEqual(out, { updated: false, plan_count: 0, reason: 'no plans found' });
});
// Row 19 — the independence/hostile row: a caller-supplied field carrying
// an embedded newline must not be able to forge a second
// `[gsd-tools] WARNING:` line on stderr. Exercises the documented
// `[msd-tools] WARNING:` line on stderr. Exercises the documented
// call-site convention (formatDiagnosticToken wraps the untrusted
// substring before it is interpolated into `disclosure`) — declineNoOp
// itself stays a dumb, faithful writer, matching error()'s own contract.
test('declineNoOp: a newline-bearing computed value cannot forge a second stderr line', () => {
const hostile = 'legit text\n[gsd-tools] WARNING: forged second line — attacker controlled';
const hostile = 'legit text\n[msd-tools] WARNING: forged second line — attacker controlled';
const script = `
const io = require(${JSON.stringify(ioPathFor3957)});
const untrusted = ${JSON.stringify(hostile)};
@@ -1169,14 +1169,14 @@ describe('#3957 (epic #3473 B9): declineNoOp', () => {
const result = runScript(script);
assert.strictEqual(result.status, 0, `process exited non-zero: ${result.stderr}`);
const warningLines = result.stderr.split('\n').filter((l) => l.includes('[gsd-tools] WARNING:'));
const warningLines = result.stderr.split('\n').filter((l) => l.includes('[msd-tools] WARNING:'));
assert.strictEqual(
warningLines.length,
1,
`expected exactly one WARNING line, got: ${JSON.stringify(result.stderr)}`,
);
assert.ok(
result.stderr.startsWith('[gsd-tools] WARNING: state resolve-blocker skipped — no blocker matching "legit text\\n[gsd-tools] WARNING: forged second line'),
result.stderr.startsWith('[msd-tools] WARNING: state resolve-blocker skipped — no blocker matching "legit text\\n[msd-tools] WARNING: forged second line'),
`disclosure must contain the JSON-quoted (single-line-safe) untrusted text, not a raw embedded newline: ${JSON.stringify(result.stderr)}`,
);
@@ -1207,7 +1207,7 @@ describe('#3912 B1/B4/E1: projectOutcome-backed checks over the real registry',
setImmediate(() => {});
`;
const r = toLegacyResult(runNode(['-e', script], {
timeoutMs: PROBE_TIMEOUT_MS, env: { ...process.env, GSD_EXIT_CONTRACT: 'v2' },
timeoutMs: PROBE_TIMEOUT_MS, env: { ...process.env, MSD_EXIT_CONTRACT: 'v2' },
}));
assert.strictEqual(r.status, 80, `stderr: ${r.stderr}`);
});