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

@@ -2,8 +2,8 @@
/**
* commit_docs bypass guard (#1783; superseded/widened by #3585)
*
* #1783: when users set commit_docs: false during /gsd-new-project,
* .planning/ files should never be staged or committed. The gsd-tools.cjs
* #1783: when users set commit_docs: false during /msd-new-project,
* .planning/ files should never be staged or committed. The msd-tools.cjs
* commit wrapper already checks this flag, but three locations in
* execute-phase.md and quick.md used raw `git add .planning/` commands that
* bypassed it.
@@ -154,7 +154,7 @@ describe('commit_docs bypass guard (#1783, repo-wide per #3585)', () => {
test('C1: a guard enclosing the add is guarded (no offender)', () => {
const { offenders } = scanText('fixture.md', fenced([
'COMMIT_DOCS=$(gsd_run query config-get commit_docs --default true)',
'COMMIT_DOCS=$(msd_run query config-get commit_docs --default true)',
'if [ "$COMMIT_DOCS" != "false" ]; then',
' git add .planning/STATE.md',
'fi',
@@ -209,7 +209,7 @@ describe('commit_docs bypass guard (#1783, repo-wide per #3585)', () => {
test('C6: a nested if inside the guard stays guarded', () => {
const { offenders } = scanText('fixture.md', fenced([
'COMMIT_DOCS=$(gsd_run query config-get commit_docs --default true)',
'COMMIT_DOCS=$(msd_run query config-get commit_docs --default true)',
'if [ "$COMMIT_DOCS" != "false" ]; then',
' if [ -f somefile ]; then',
' git add .planning/STATE.md',
@@ -235,7 +235,7 @@ describe('commit_docs bypass guard (#1783, repo-wide per #3585)', () => {
test('C8: CRLF line endings behave identically to LF (the C1 fixture, \\r\\n joined)', () => {
const { offenders } = scanText('fixture.md', fenced([
'COMMIT_DOCS=$(gsd_run query config-get commit_docs --default true)',
'COMMIT_DOCS=$(msd_run query config-get commit_docs --default true)',
'if [ "$COMMIT_DOCS" != "false" ]; then',
' git add .planning/STATE.md',
'fi',
@@ -245,7 +245,7 @@ describe('commit_docs bypass guard (#1783, repo-wide per #3585)', () => {
test('C9: the = "true" polarity is a guard too (both polarities accepted)', () => {
const { offenders } = scanText('fixture.md', fenced([
'COMMIT_DOCS=$(gsd_run query config-get commit_docs --default true)',
'COMMIT_DOCS=$(msd_run query config-get commit_docs --default true)',
'if [ "$COMMIT_DOCS" = "true" ]; then',
' git add .planning/STATE.md',
'fi',
@@ -276,13 +276,13 @@ describe('commit_docs bypass guard (#1783, repo-wide per #3585)', () => {
});
});
describe('D: gsd-scan-ignore declaration handling', () => {
describe('D: msd-scan-ignore declaration handling', () => {
const fenced = (line) => ['```bash', line, '```'].join('\n');
test('D1: declared with a tracked issue (#3585) is exempt', () => {
const { offenders, untracked } = scanText(
'fixture.md',
fenced('git add -A # gsd-scan-ignore: #3585 demonstrating the unscoped shape'),
fenced('git add -A # msd-scan-ignore: #3585 demonstrating the unscoped shape'),
);
assert.deepStrictEqual(offenders, []);
assert.deepStrictEqual(untracked, []);
@@ -291,7 +291,7 @@ describe('commit_docs bypass guard (#1783, repo-wide per #3585)', () => {
test('D2: an empty reason is malformed, not exempt', () => {
const { offenders, untracked } = scanText(
'fixture.md',
fenced('git add -A # gsd-scan-ignore:'),
fenced('git add -A # msd-scan-ignore:'),
);
assert.strictEqual(offenders.length, 1, 'not exempted — still an offender');
assert.strictEqual(untracked.length, 1, 'and reported as a malformed declaration');
@@ -300,7 +300,7 @@ describe('commit_docs bypass guard (#1783, repo-wide per #3585)', () => {
test('D3: "just a note" (no tracking reference) is malformed, not exempt', () => {
const { offenders, untracked } = scanText(
'fixture.md',
fenced('git add -A # gsd-scan-ignore: just a note'),
fenced('git add -A # msd-scan-ignore: just a note'),
);
assert.strictEqual(offenders.length, 1, 'not exempted — still an offender');
assert.strictEqual(untracked.length, 1, 'and reported as a malformed declaration');
@@ -309,7 +309,7 @@ describe('commit_docs bypass guard (#1783, repo-wide per #3585)', () => {
test('D4: a marker surviving as an argv token declares nothing', () => {
const { offenders, untracked } = scanText(
'fixture.md',
fenced('git add .planning/STATE.md "gsd-scan-ignore: #3585"'),
fenced('git add .planning/STATE.md "msd-scan-ignore: #3585"'),
);
assert.strictEqual(offenders.length, 1, 'plain unguarded offender');
assert.deepStrictEqual(untracked, [], 'the marker never reached comment position, so no declaration was attempted');
@@ -365,7 +365,7 @@ describe('commit_docs bypass guard (#1783, repo-wide per #3585)', () => {
assert.deepStrictEqual(
untracked,
[],
'gsd-scan-ignore: declarations without a tracking reference. Add a #NNN issue '
'msd-scan-ignore: declarations without a tracking reference. Add a #NNN issue '
+ 'number or an http(s):// URL to the reason, per ADR-456:\n'
+ untracked.map((u) => `${u.file}:${u.line}: ${u.text}`).join('\n'),
);
@@ -374,7 +374,7 @@ describe('commit_docs bypass guard (#1783, repo-wide per #3585)', () => {
offenders,
[],
'git add invocations that can reach .planning/ without a commit_docs guard or a '
+ 'tracked gsd-scan-ignore declaration (#1783 / #3585):\n\n'
+ 'tracked msd-scan-ignore declaration (#1783 / #3585):\n\n'
+ offenders.map((o) => `${o.file}:${o.line}: ${o.text}`).join('\n'),
);
});
@@ -405,11 +405,11 @@ const assert = require('node:assert/strict');
const fs = require('fs');
const path = require('path');
const PLAN_PHASE_PATH = path.join(__dirname, '..', 'gsd-core', 'workflows', 'plan-phase.md');
const PLAN_PHASE_PATH = path.join(__dirname, '..', 'msd-core', 'workflows', 'plan-phase.md');
describe('plan-phase commit_docs support (#2399)', () => {
test('plan-phase.md exists', () => {
assert.ok(fs.existsSync(PLAN_PHASE_PATH), 'gsd-core/workflows/plan-phase.md must exist');
assert.ok(fs.existsSync(PLAN_PHASE_PATH), 'msd-core/workflows/plan-phase.md must exist');
});
test('plan-phase.md has a commit step for plan artifacts', () => {
@@ -455,12 +455,12 @@ describe('plan-phase commit_docs support (#2399)', () => {
);
});
test('plan-phase.md uses gsd-sdk query commit for the plan commit', () => {
test('plan-phase.md uses msd-sdk query commit for the plan commit', () => {
const content = fs.readFileSync(PLAN_PHASE_PATH, 'utf-8');
// Must use gsd-sdk query commit (not raw git) so commit_docs guard in gsd-tools is respected
// Must use msd-sdk query commit (not raw git) so commit_docs guard in msd-tools is respected
assert.ok(
content.includes('gsd-sdk query commit') || content.includes('gsd-tools') || content.includes('gsd-sdk'),
'plan-phase.md plan commit step must use gsd-sdk query commit (not raw git commit)'
content.includes('msd-sdk query commit') || content.includes('msd-tools') || content.includes('msd-sdk'),
'plan-phase.md plan commit step must use msd-sdk query commit (not raw git commit)'
);
});
});
@@ -475,21 +475,21 @@ describe('plan-phase commit_docs support (#2399)', () => {
__foldDescribe("folded:bug-3678-executor-commit-docs-respect (consolidation epic #1969 B6 #1975)", () => {
// allow-test-rule: source-text-is-the-product (see #3678)
// Three of the assertions in this file (A1, A2, C) inspect agent / workflow
// `.md` bodies. Those files ARE the runtime contract that GSD loads into agent
// `.md` bodies. Those files ARE the runtime contract that MSD loads into agent
// prompts at run time, so source-text inspection is exactly what the
// `source-text-is-the-product` exception covers.
//
// The remaining assertions (B1, B2, B3) are behavioral — they invoke
// `gsd-tools commit` against a temp project and assert on its structured
// `msd-tools commit` against a temp project and assert on its structured
// JSON return envelope plus the git index state. No raw-text matching on
// rendered output.
/**
* Regression for #3678 — gsd-executor force-commits .planning/ files when
* Regression for #3678 — msd-executor force-commits .planning/ files when
* commit_docs is false.
*
* Root cause: the executor agent prompt (agents/gsd-executor.md) tells the
* agent to call `gsd-sdk query commit "docs(...)" --files .planning/...`
* Root cause: the executor agent prompt (agents/msd-executor.md) tells the
* agent to call `msd-sdk query commit "docs(...)" --files .planning/...`
* in the per-plan final_commit block, but the prompt says nothing about
* what to do when the SDK returns `{committed: false, skipped: true,
* reason: 'skipped_commit_docs_false'}`. With no explicit instruction, the
@@ -502,7 +502,7 @@ describe('plan-phase commit_docs support (#2399)', () => {
* 2. SDK envelope: add `skipped: true` field so agents see "skipped" as a
* first-class success signal, not "committed is missing, must improvise."
* 3. Structural guard: ban `git add -f` / `git add --force` from agent and
* workflow bodies entirely (no GSD-managed surface should force-stage
* workflow bodies entirely (no MSD-managed surface should force-stage
* gitignored content).
*/
@@ -512,23 +512,23 @@ 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 { createTempGitProject, cleanup, runGsdTools } = require('./helpers.cjs');
const { createTempGitProject, cleanup, runMsdTools } = require('./helpers.cjs');
const { gitOrThrow } = require('./helpers/git-fixture.cjs');
// #3145: class-norm timeout, not a per-suite value — see helpers/timeouts.cjs.
const { GIT_TIMEOUT_MS } = require('./helpers/timeouts.cjs');
// Repo root resolution. This test file lives in `<repo>/tests/`. Use a single
// parent reference (the established repo-wide pattern, e.g. tests/helpers.cjs
// `path.resolve(__dirname, '..', 'gsd-core', ...)`). A `.git`-anchored
// `path.resolve(__dirname, '..', 'msd-core', ...)`). A `.git`-anchored
// walker is not portable because the docker test mirror at `/work` strips the
// `.git/` directory before running tests.
const REPO_ROOT = path.resolve(__dirname, '..');
const EXECUTOR_AGENT = path.join(REPO_ROOT, 'agents', 'gsd-executor.md');
const EXECUTOR_AGENT = path.join(REPO_ROOT, 'agents', 'msd-executor.md');
// Frozen reason enum mirrors the SDK source. Both members are now
// behaviorally pinned against the live envelope returned by `cmdCommit` in
// gsd-core/bin/lib/commands.cjs, not kept in sync by hand-reading the
// msd-core/bin/lib/commands.cjs, not kept in sync by hand-reading the
// source: SKIPPED_COMMIT_DOCS_FALSE by describe block B (B1-B3, explicit
// `commit_docs: false`) and SKIPPED_GITIGNORED by describe block G (G1-G3,
// gitignore auto-detect). A production rename of either string now fails
@@ -556,8 +556,8 @@ describe('bug #3678 — executor must respect commit_docs:false', () => {
const mentionsSkippedTrue = /skipped:\s*true/i.test(body);
assert.ok(
mentionsSkipReason || mentionsCommittedFalse || mentionsSkippedTrue,
'agents/gsd-executor.md must teach the agent how to recognize the '
+ 'skipped envelope from `gsd-sdk query commit` (one of: '
'agents/msd-executor.md must teach the agent how to recognize the '
+ 'skipped envelope from `msd-sdk query commit` (one of: '
+ `'${COMMIT_REASON.SKIPPED_COMMIT_DOCS_FALSE}', 'committed: false', `
+ "'skipped: true').",
);
@@ -572,7 +572,7 @@ describe('bug #3678 — executor must respect commit_docs:false', () => {
const forbidsFallbackText = /(do not|must not|never)\s+(fall back|fallback|use .*git add|run .*git commit|force[- ]?add)/i;
assert.ok(
forbidsFallbackText.test(body),
'agents/gsd-executor.md must contain an explicit "do not fall back to '
'agents/msd-executor.md must contain an explicit "do not fall back to '
+ 'raw git" instruction tied to the commit_docs:false / skipped envelope. '
+ 'Without it, the agent improvises raw `git add` / `git add -f` to '
+ 'fulfill its "complete plan" goal.',
@@ -606,11 +606,11 @@ describe('bug #3678 — executor must respect commit_docs:false', () => {
afterEach(() => cleanup(tmpDir));
test('B1: commit returns committed:false with skipped envelope', () => {
const result = runGsdTools(
const result = runMsdTools(
'commit "docs(test): noop" --files .planning/STATE.md',
tmpDir,
);
assert.ok(result.success, `gsd-tools commit should exit 0 even when skipped: ${result.error || ''}`);
assert.ok(result.success, `msd-tools commit should exit 0 even when skipped: ${result.error || ''}`);
const envelope = JSON.parse(result.output);
assert.strictEqual(envelope.committed, false, 'committed must be false when commit_docs is false');
assert.strictEqual(
@@ -626,7 +626,7 @@ describe('bug #3678 — executor must respect commit_docs:false', () => {
});
test('B2: commit_docs:false leaves the git index empty (no .planning/ staged)', () => {
runGsdTools(
runMsdTools(
'commit "docs(test): noop" --files .planning/STATE.md',
tmpDir,
);
@@ -644,7 +644,7 @@ describe('bug #3678 — executor must respect commit_docs:false', () => {
test('B3: commit_docs:false produces no new commits', () => {
const headBefore = git(['rev-parse', 'HEAD'], tmpDir).trim();
runGsdTools(
runMsdTools(
'commit "docs(test): noop" --files .planning/STATE.md',
tmpDir,
);
@@ -691,11 +691,11 @@ describe('bug #3678 — executor must respect commit_docs:false', () => {
afterEach(() => cleanup(tmpDir));
test('G1: commit returns skipped:true with reason SKIPPED_GITIGNORED (parity anchor for the enum member the comment claims is kept in sync)', () => {
const result = runGsdTools(
const result = runMsdTools(
'commit "docs(test): noop" --files .planning/STATE.md',
tmpDir,
);
assert.ok(result.success, `gsd-tools commit should exit 0 even when skipped: ${result.error || ''}`);
assert.ok(result.success, `msd-tools commit should exit 0 even when skipped: ${result.error || ''}`);
const envelope = JSON.parse(result.output);
assert.strictEqual(envelope.skipped, true, 'envelope must carry skipped:true for the gitignore auto-detect path');
assert.strictEqual(
@@ -707,7 +707,7 @@ describe('bug #3678 — executor must respect commit_docs:false', () => {
});
test('G2: gitignore auto-detect skip leaves the git index empty (no .planning/ staged)', () => {
runGsdTools(
runMsdTools(
'commit "docs(test): noop" --files .planning/STATE.md',
tmpDir,
);
@@ -725,7 +725,7 @@ describe('bug #3678 — executor must respect commit_docs:false', () => {
test('G3: gitignore auto-detect skip produces no new commits', () => {
const headBefore = git(['rev-parse', 'HEAD'], tmpDir).trim();
runGsdTools(
runMsdTools(
'commit "docs(test): noop" --files .planning/STATE.md',
tmpDir,
);
@@ -745,7 +745,7 @@ describe('bug #3678 — executor must respect commit_docs:false', () => {
.find(line => /Final metadata commit made/.test(line));
assert.ok(
checklistLine,
'agents/gsd-executor.md must contain a "Final metadata commit made" checklist line',
'agents/msd-executor.md must contain a "Final metadata commit made" checklist line',
);
assert.ok(
checklistLine.includes('Final metadata commit'),
@@ -759,7 +759,7 @@ describe('bug #3678 — executor must respect commit_docs:false', () => {
);
});
describe('C — structural ban on raw force-add in GSD-managed bodies', () => {
describe('C — structural ban on raw force-add in MSD-managed bodies', () => {
function scanForForceAdd(rootDir) {
const offenders = [];
function walk(dir) {
@@ -802,7 +802,7 @@ describe('bug #3678 — executor must respect commit_docs:false', () => {
});
test('C2: no workflow body contains `git add -f` / `git add --force`', () => {
const offenders = scanForForceAdd(path.join(REPO_ROOT, 'gsd-core', 'workflows'));
const offenders = scanForForceAdd(path.join(REPO_ROOT, 'msd-core', 'workflows'));
assert.deepStrictEqual(
offenders,
[],
@@ -829,7 +829,7 @@ describe('bug #3678 — executor must respect commit_docs:false', () => {
* A tech lead can mark a single phase's docs to be committed (or suppressed)
* independently of the project-wide `commit_docs` setting, via the dynamic
* config key `phase_commit_docs.<phase-id>`. See
* `.gsd/phase/feat-3587-per-phase-commit-docs/40-design.md` for the
* `.msd/phase/feat-3587-per-phase-commit-docs/40-design.md` for the
* precedence chain (phase > config > gitignore > default) and
* `50-test-matrix.md` for the matrix this file implements (A/B/C/D/F —
* E lives in this file's own 'E' describe block below).
@@ -849,13 +849,13 @@ const { seedPhase } = require('./fixtures/index.cjs');
const { gitOrThrow } = require('./helpers/git-fixture.cjs');
const { GIT_TIMEOUT_MS } = require('./helpers/timeouts.cjs');
const commands = require('../gsd-core/bin/lib/commands.cjs');
const configCli = require('../gsd-core/bin/lib/config.cjs');
const { loadConfigResolved, CONFIG_DEFAULTS } = require('../gsd-core/bin/lib/config-loader.cjs');
const io = require('../gsd-core/bin/lib/io.cjs');
const { ExitError } = require('../gsd-core/bin/lib/cli-exit.cjs');
const { PHASE_NUMBER_TOKEN_SOURCE } = require('../gsd-core/bin/lib/phase-id.cjs');
const { DYNAMIC_KEY_PATTERNS } = require('../gsd-core/bin/lib/config-schema.cjs');
const commands = require('../msd-core/bin/lib/commands.cjs');
const configCli = require('../msd-core/bin/lib/config.cjs');
const { loadConfigResolved, CONFIG_DEFAULTS } = require('../msd-core/bin/lib/config-loader.cjs');
const io = require('../msd-core/bin/lib/io.cjs');
const { ExitError } = require('../msd-core/bin/lib/cli-exit.cjs');
const { PHASE_NUMBER_TOKEN_SOURCE } = require('../msd-core/bin/lib/phase-id.cjs');
const { DYNAMIC_KEY_PATTERNS } = require('../msd-core/bin/lib/config-schema.cjs');
const {
detectPhaseNumberFromFiles,
@@ -1057,7 +1057,7 @@ describe('#3587 per-phase commit_docs override', () => {
// a commit whose `--files` spans two phase directories resolves the tier-1
// override against whichever phase appears first, not the majority. A pinned,
// named behavior is not a bug; an unpinned surprise is. See "Known limits" in
// `.gsd/phase/feat-3587-per-phase-commit-docs/40-design.md` and
// `.msd/phase/feat-3587-per-phase-commit-docs/40-design.md` and
// `docs/CONFIGURATION.md`'s per-phase section.
test('multiPhaseFilesResolvesAgainstFirstPhase — --files spanning two phase directories with DIFFERENT phase_commit_docs values resolves against the FIRST phase, not the majority', () => {
const files = [
@@ -1143,7 +1143,7 @@ describe('#3587 per-phase commit_docs override', () => {
assert.notStrictEqual(envelope.reason, 'skipped_commit_docs_false');
});
test('D2: existingReasonStringsUnchanged — the two pre-existing reason strings are unchanged (agents/gsd-executor.md pattern-matches on them)', () => {
test('D2: existingReasonStringsUnchanged — the two pre-existing reason strings are unchanged (agents/msd-executor.md pattern-matches on them)', () => {
assert.strictEqual(COMMIT_DOCS_SKIP_REASON.config, 'skipped_commit_docs_false');
assert.strictEqual(COMMIT_DOCS_SKIP_REASON.gitignore, 'skipped_gitignored');
});