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 @@
// allow-test-rule: source-text-is-the-product see #2568
// agents/gsd-debug-session-manager.md is executed instruction text: the orchestrator
// agents/msd-debug-session-manager.md is executed instruction text: the orchestrator
// follows it verbatim, so WHERE the commit step sits relative to the terminal vs
// non-terminal summary shapes IS the contract. The commit_docs gate it relies on is
// real code and is exercised behaviorally through the CLI below.
@@ -9,11 +9,11 @@ const { test } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const { runGsdTools, createTempGitProject, cleanup } = require('./helpers.cjs');
const { runMsdTools, createTempGitProject, cleanup } = require('./helpers.cjs');
const ROOT = path.resolve(__dirname, '..');
const MANAGER = path.join(ROOT, 'agents', 'gsd-debug-session-manager.md');
const DEBUGGER = path.join(ROOT, 'agents', 'gsd-debugger.md');
const MANAGER = path.join(ROOT, 'agents', 'msd-debug-session-manager.md');
const DEBUGGER = path.join(ROOT, 'agents', 'msd-debugger.md');
const manager = () => fs.readFileSync(MANAGER, 'utf8');
@@ -31,26 +31,26 @@ test('#2568: the manager commits the session doc before a terminal summary', ()
// manager-driven path ever consults commit_docs.
assert.match(
body,
/gsd_run query commit/,
/msd_run query commit/,
'the manager owns the terminal path and must commit the session doc via the CLI — ' +
'the step existed only in gsd-debugger.md, which does not reach the end of a ' +
'the step existed only in msd-debugger.md, which does not reach the end of a ' +
'manager-driven session (#2568)',
);
// Resolved and abandoned shapes each need their own doc committed.
assert.match(
body,
/gsd_run query commit[^\n]*resolved/,
/msd_run query commit[^\n]*resolved/,
'the resolved session doc (.planning/debug/resolved/{slug}.md) must be committed',
);
assert.match(
body,
/gsd_run query commit[^\n]*(debug_file_path|checkpoint)/,
/msd_run query commit[^\n]*(debug_file_path|checkpoint)/,
'the abandoned/checkpoint session doc must be committed',
);
// Ordering: the commit instruction must PRECEDE the terminal summary it guards.
const firstCommit = offsetOf(body, 'gsd_run query commit', 'commit step');
const firstCommit = offsetOf(body, 'msd_run query commit', 'commit step');
const terminal = offsetOf(body, '## DEBUG SESSION COMPLETE', 'terminal summary');
assert.ok(
firstCommit < terminal,
@@ -82,7 +82,7 @@ test('#2568: the manager must NOT commit on the non-terminal CONTINUE_REQUIRED p
// Structural: the commit block must sit after the CONTINUE_REQUIRED fence, so an
// orchestrator taking the early-return path never reaches it.
const continueFence = offsetOf(body, '## CONTINUE_REQUIRED', 'CONTINUE_REQUIRED shape');
const firstCommit = offsetOf(body, 'gsd_run query commit', 'commit step');
const firstCommit = offsetOf(body, 'msd_run query commit', 'commit step');
assert.ok(
firstCommit > continueFence,
'the commit step must come after the CONTINUE_REQUIRED early-return block, not before it',
@@ -132,7 +132,7 @@ test('#2568: the fix does not touch agent frontmatter', () => {
const end = body.indexOf('\n---', 4);
assert.ok(end > 0, 'frontmatter terminates');
const fm = body.slice(0, end);
assert.match(fm, /^name:\s*gsd-debug-session-manager$/m, 'name key intact');
assert.match(fm, /^name:\s*msd-debug-session-manager$/m, 'name key intact');
assert.doesNotMatch(fm, /commit/i, 'the commit instruction must live in the body, not frontmatter');
});
@@ -143,14 +143,14 @@ test('#2568: the debugger keeps its own commit step for the single-spawn path',
const body = fs.readFileSync(DEBUGGER, 'utf8');
assert.match(
body,
/gsd_run query commit[^\n]*resolved/,
'gsd-debugger.md must retain its own doc-commit step',
/msd_run query commit[^\n]*resolved/,
'msd-debugger.md must retain its own doc-commit step',
);
});
test('#2568: every token substituted into a commit command is a declared session parameter', () => {
// The first cut of this fix pasted `{debug_dir}` straight from the issue's suggested
// patch. That variable is real in gsd-core/workflows/debug.md — the ORCHESTRATOR
// patch. That variable is real in msd-core/workflows/debug.md — the ORCHESTRATOR
// receives it from init JSON — but this agent never does: <session_parameters>
// declares only slug, debug_file_path, symptoms_prefilled, tdd_mode, goal and
// specialist_dispatch_enabled. An unbound token makes --files resolve to a
@@ -163,7 +163,7 @@ test('#2568: every token substituted into a commit command is a declared session
const declared = new Set([...block.matchAll(/^- `([a-z_]+)`/gm)].map((m) => m[1]));
assert.ok(declared.size >= 5, `precondition: session parameters parsed, got ${declared.size}`);
const commitLines = [...body.matchAll(/gsd_run query commit[^\n]*/g)].map((m) => m[0]);
const commitLines = [...body.matchAll(/msd_run query commit[^\n]*/g)].map((m) => m[0]);
assert.ok(commitLines.length >= 2, `expected the resolved + checkpoint commits, got ${commitLines.length}`);
for (const line of commitLines) {
@@ -178,29 +178,29 @@ test('#2568: every token substituted into a commit command is a declared session
});
test('#2568: the commit call is covered by the single canonical preamble', () => {
// The repo invariant (tests/…B-agents…) is that each agent .md using gsd_run carries
// EXACTLY ONE canonical preamble, placed before the first gsd_run call — the runtime
// The repo invariant (tests/…B-agents…) is that each agent .md using msd_run carries
// EXACTLY ONE canonical preamble, placed before the first msd_run call — the runtime
// establishes it once. An earlier cut of this fix pasted a second preamble into the
// commit block on the theory that shell state does not persist; that broke the
// invariant and five suites with it. The correct property is coverage, not locality.
const body = manager();
const preambles = body.match(/^\s*_GSD_SHIM_NAME=/gm) || [];
const preambles = body.match(/^\s*_MSD_SHIM_NAME=/gm) || [];
assert.equal(
preambles.length,
1,
`exactly one canonical gsd_run preamble per agent file, got ${preambles.length}`,
`exactly one canonical msd_run preamble per agent file, got ${preambles.length}`,
);
const preambleAt = body.search(/^\s*_GSD_SHIM_NAME=/m);
const commitAt = body.indexOf('gsd_run query commit');
const preambleAt = body.search(/^\s*_MSD_SHIM_NAME=/m);
const commitAt = body.indexOf('msd_run query commit');
assert.notEqual(commitAt, -1, 'precondition: the commit call exists');
assert.ok(
preambleAt < commitAt,
'the canonical preamble must precede the commit call that relies on gsd_run',
'the canonical preamble must precede the commit call that relies on msd_run',
);
});
test('#2568: the in-session fix commit is idempotent', () => {
// gsd-debugger.md's archive_session already commits the fix on the confirmed-checkpoint
// msd-debugger.md's archive_session already commits the fix on the confirmed-checkpoint
// path, which is the standard find_and_fix flow. A bare `git commit` with nothing
// staged exits non-zero and would abort this step before the summary is returned.
const body = manager();
@@ -230,11 +230,11 @@ function writeConfig(dir, extra) {
}
test('#2568: query commit honors commit_docs true', () => {
const dir = createTempGitProject('gsd-2568-on-');
const dir = createTempGitProject('msd-2568-on-');
try {
writeConfig(dir, { commit_docs: true });
const rel = seedDoc(dir);
const res = runGsdTools(['query', 'commit', 'docs(debug): resolve demo session', '--files', rel], dir);
const res = runMsdTools(['query', 'commit', 'docs(debug): resolve demo session', '--files', rel], dir);
assert.ok(res.success, `commit failed: ${res.error}`);
const parsed = JSON.parse(res.output);
assert.equal(parsed.committed, true, `expected a commit, got ${res.output}`);
@@ -247,11 +247,11 @@ test('#2568: query commit no-ops when commit_docs is false', () => {
// This is the load-bearing assumption: the manager calls the CLI unconditionally
// and correctness follows from the gate, rather than the agent re-implementing a
// config check it could get wrong.
const dir = createTempGitProject('gsd-2568-off-');
const dir = createTempGitProject('msd-2568-off-');
try {
writeConfig(dir, { commit_docs: false });
const rel = seedDoc(dir);
const res = runGsdTools(['query', 'commit', 'docs(debug): resolve demo session', '--files', rel], dir);
const res = runMsdTools(['query', 'commit', 'docs(debug): resolve demo session', '--files', rel], dir);
assert.ok(res.success, `commit invocation failed: ${res.error}`);
const parsed = JSON.parse(res.output);
assert.equal(parsed.committed, false, 'must not commit when commit_docs is false');
@@ -263,11 +263,11 @@ test('#2568: query commit no-ops when commit_docs is false', () => {
});
test('#2568: query commit behavior with commit_docs absent is explicit, not assumed', () => {
const dir = createTempGitProject('gsd-2568-default-');
const dir = createTempGitProject('msd-2568-default-');
try {
writeConfig(dir, {});
const rel = seedDoc(dir);
const res = runGsdTools(['query', 'commit', 'docs(debug): resolve demo session', '--files', rel], dir);
const res = runMsdTools(['query', 'commit', 'docs(debug): resolve demo session', '--files', rel], dir);
assert.ok(res.success, `commit invocation failed: ${res.error}`);
const parsed = JSON.parse(res.output);
// Pin whatever the shipped default resolves to, so a silent change to