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:
@@ -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}`);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user