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:
@@ -32,10 +32,10 @@ const {
|
||||
ROLE_FAMILY,
|
||||
} = require('../scripts/gen-loop-host-contract.cjs');
|
||||
|
||||
const { LOOP_HOST_CONTRACT } = require('../gsd-core/bin/lib/loop-host-contract.cjs');
|
||||
const { LOOP_HOST_CONTRACT } = require('../msd-core/bin/lib/loop-host-contract.cjs');
|
||||
|
||||
const ROOT = path.resolve(__dirname, '..');
|
||||
const CONTRACT_PATH = path.join(ROOT, 'gsd-core', 'bin', 'lib', 'loop-host-contract.cjs');
|
||||
const CONTRACT_PATH = path.join(ROOT, 'msd-core', 'bin', 'lib', 'loop-host-contract.cjs');
|
||||
|
||||
// ─── Helper: write a temporary workflows directory ────────────────────────────
|
||||
|
||||
@@ -55,7 +55,7 @@ function makeWorkflow(step, points, roles, produces, consumes, extraContent) {
|
||||
const producesList = produces.join(', ');
|
||||
const consumesList = consumes.join(', ');
|
||||
return (
|
||||
'<!-- gsd:loop-host\n' +
|
||||
'<!-- msd:loop-host\n' +
|
||||
'step: ' + step + '\n' +
|
||||
'points: ' + pointsList + '\n' +
|
||||
'agent-roles: ' + rolesList + '\n' +
|
||||
@@ -75,10 +75,10 @@ function makeValidWorkflowFiles() {
|
||||
['researcher', 'planner', 'checker'],
|
||||
['PLAN.md'], ['CONTEXT.md'],
|
||||
// Cross-check content: must contain the agent names
|
||||
'gsd-phase-researcher gsd-planner gsd-plan-checker',
|
||||
'msd-phase-researcher msd-planner msd-plan-checker',
|
||||
),
|
||||
'quick.md': [
|
||||
'PLAN_PRE_HOOKS_JSON=$(gsd_run loop render-hooks plan:pre --raw)',
|
||||
'PLAN_PRE_HOOKS_JSON=$(msd_run loop render-hooks plan:pre --raw)',
|
||||
'For each active entry where `kind == "contribution"` and `into == "planner"`: inject.',
|
||||
'',
|
||||
].join('\n'),
|
||||
@@ -86,7 +86,7 @@ function makeValidWorkflowFiles() {
|
||||
'execute', ['execute:pre', 'execute:wave:pre', 'execute:wave:post', 'execute:post'],
|
||||
['executor', 'verifier'],
|
||||
['SUMMARY.md'], ['PLAN.md'],
|
||||
'gsd-executor gsd-verifier',
|
||||
'msd-executor msd-verifier',
|
||||
),
|
||||
'verify-work.md': makeWorkflow('verify', ['verify:pre', 'verify:post'], ['orchestrator'], ['UAT.md'], ['SUMMARY.md']),
|
||||
'ship.md': makeWorkflow('ship', ['ship:pre', 'ship:post'], ['orchestrator'], [], ['UAT.md']),
|
||||
@@ -131,13 +131,13 @@ describe('parseLoopHostBlock', () => {
|
||||
test('throws when block is missing', () => {
|
||||
assert.throws(
|
||||
() => parseLoopHostBlock('no block here\n<purpose>hello</purpose>', 'test.md'),
|
||||
/missing.*gsd:loop-host/,
|
||||
/missing.*msd:loop-host/,
|
||||
);
|
||||
});
|
||||
|
||||
test('throws when step field is missing from block', () => {
|
||||
const content =
|
||||
'<!-- gsd:loop-host\n' +
|
||||
'<!-- msd:loop-host\n' +
|
||||
'points: plan:pre, plan:post\n' +
|
||||
'agent-roles: orchestrator\n' +
|
||||
'produces:\n' +
|
||||
@@ -151,7 +151,7 @@ describe('parseLoopHostBlock', () => {
|
||||
|
||||
test('throws when points field is empty', () => {
|
||||
const content =
|
||||
'<!-- gsd:loop-host\n' +
|
||||
'<!-- msd:loop-host\n' +
|
||||
'step: plan\n' +
|
||||
'points:\n' +
|
||||
'agent-roles: orchestrator\n' +
|
||||
@@ -186,20 +186,20 @@ describe('crossCheckRoles', () => {
|
||||
});
|
||||
|
||||
test('passes when agent name is present in content', () => {
|
||||
const content = 'Agent(subagent_type="gsd-planner") Agent(subagent_type="gsd-phase-researcher") gsd-plan-checker';
|
||||
const content = 'Agent(subagent_type="msd-planner") Agent(subagent_type="msd-phase-researcher") msd-plan-checker';
|
||||
const errors = crossCheckRoles(content, ['researcher', 'planner', 'checker'], 'plan-phase.md');
|
||||
assert.deepEqual(errors, []);
|
||||
});
|
||||
|
||||
test('fails when declared role has no agent reference in content', () => {
|
||||
const content = 'gsd-phase-researcher gsd-plan-checker'; // planner missing
|
||||
const content = 'msd-phase-researcher msd-plan-checker'; // planner missing
|
||||
const errors = crossCheckRoles(content, ['researcher', 'planner', 'checker'], 'plan-phase.md');
|
||||
assert.strictEqual(errors.length, 1, 'expected exactly 1 error for missing planner');
|
||||
assert.match(errors[0], /planner.*gsd-planner/);
|
||||
assert.match(errors[0], /planner.*msd-planner/);
|
||||
});
|
||||
|
||||
test('fails when declared role is unknown (not in ROLE_TO_AGENT)', () => {
|
||||
const content = 'gsd-executor gsd-verifier';
|
||||
const content = 'msd-executor msd-verifier';
|
||||
const errors = crossCheckRoles(content, ['executor', 'nonexistent-role'], 'execute-phase.md');
|
||||
assert.ok(errors.some((e) => e.includes('nonexistent-role') && e.includes('ROLE_TO_AGENT')));
|
||||
});
|
||||
@@ -413,7 +413,7 @@ describe('assertPointsCoverage', () => {
|
||||
|
||||
describe('buildContract from real workflows', () => {
|
||||
test('produces a contract matching the inline LOOP_HOST_CONTRACT shape', () => {
|
||||
const contract = buildContract(); // reads real gsd-core/workflows/
|
||||
const contract = buildContract(); // reads real msd-core/workflows/
|
||||
|
||||
// Must be an array of 5 entries
|
||||
assert.strictEqual(contract.length, 5, 'contract must have 5 step entries');
|
||||
@@ -512,7 +512,7 @@ describe('buildContract cross-check drift guard', () => {
|
||||
const files = makeValidWorkflowFiles();
|
||||
// Override plan-phase.md to declare a "phantom" role with no agent reference
|
||||
files['plan-phase.md'] =
|
||||
'<!-- gsd:loop-host\n' +
|
||||
'<!-- msd:loop-host\n' +
|
||||
'step: plan\n' +
|
||||
'points: plan:pre, plan:post\n' +
|
||||
'agent-roles: researcher, planner, checker, phantom\n' +
|
||||
@@ -520,7 +520,7 @@ describe('buildContract cross-check drift guard', () => {
|
||||
'consumes: CONTEXT.md\n' +
|
||||
'-->\n' +
|
||||
// Include real agents but NOT the phantom role's agent (phantom is not in ROLE_TO_AGENT)
|
||||
'gsd-phase-researcher gsd-planner gsd-plan-checker\n';
|
||||
'msd-phase-researcher msd-planner msd-plan-checker\n';
|
||||
|
||||
const tmpDir = makeTempWorkflowsDir(files);
|
||||
try {
|
||||
@@ -534,10 +534,10 @@ describe('buildContract cross-check drift guard', () => {
|
||||
});
|
||||
|
||||
test('rejects a block declaring a role whose agent is absent from the workflow', () => {
|
||||
// plan-phase.md declares 'researcher' but does NOT mention gsd-phase-researcher
|
||||
// plan-phase.md declares 'researcher' but does NOT mention msd-phase-researcher
|
||||
const files = makeValidWorkflowFiles();
|
||||
files['plan-phase.md'] =
|
||||
'<!-- gsd:loop-host\n' +
|
||||
'<!-- msd:loop-host\n' +
|
||||
'step: plan\n' +
|
||||
'points: plan:pre, plan:post\n' +
|
||||
'agent-roles: researcher, planner, checker\n' +
|
||||
@@ -545,13 +545,13 @@ describe('buildContract cross-check drift guard', () => {
|
||||
'consumes: CONTEXT.md\n' +
|
||||
'-->\n' +
|
||||
// Only planner and checker present, researcher's agent is absent
|
||||
'gsd-planner gsd-plan-checker\n';
|
||||
'msd-planner msd-plan-checker\n';
|
||||
|
||||
const tmpDir = makeTempWorkflowsDir(files);
|
||||
try {
|
||||
assert.throws(
|
||||
() => buildContract(tmpDir),
|
||||
/gsd-phase-researcher.*not referenced|researcher.*gsd-phase-researcher/,
|
||||
/msd-phase-researcher.*not referenced|researcher.*msd-phase-researcher/,
|
||||
);
|
||||
} finally {
|
||||
cleanup(tmpDir);
|
||||
@@ -571,7 +571,7 @@ describe('normalizeLineEndings', () => {
|
||||
// The committed-loop-host-contract.cjs freshness guard moved to
|
||||
// `npm run lint:generated-sync` (gen-loop-host-contract.cjs --check), where it
|
||||
// runs against the committed file in both local and CI lint lanes — instead of
|
||||
// being masked by gsd-test's `npm run build` leg regenerating the artifact.
|
||||
// being masked by msd-test's `npm run build` leg regenerating the artifact.
|
||||
});
|
||||
|
||||
// ─── 7. STEP_WORKFLOWS and CANONICAL_POINTS exported constants ────────────────
|
||||
@@ -617,7 +617,7 @@ describe('Quick auxiliary loop-host contract (#3778)', () => {
|
||||
function makeQuickWorkflow({ includePoint = true, includeKind = true, into = 'planner' } = {}) {
|
||||
const target = into === null ? '' : ` and \`into == "${into}"\``;
|
||||
return [
|
||||
includePoint ? 'PLAN_PRE_HOOKS_JSON=$(gsd_run loop render-hooks plan:pre --raw)' : '',
|
||||
includePoint ? 'PLAN_PRE_HOOKS_JSON=$(msd_run loop render-hooks plan:pre --raw)' : '',
|
||||
includeKind ? `For each active entry where \`kind == "contribution"\`${target}: inject.` : '',
|
||||
'',
|
||||
].join('\n');
|
||||
@@ -757,7 +757,7 @@ describe('assertPointsCoverage per-step ownership (FIX 1)', () => {
|
||||
'plan', ['discuss:pre', 'plan:post'],
|
||||
['researcher', 'planner', 'checker'],
|
||||
['PLAN.md'], ['CONTEXT.md'],
|
||||
'gsd-phase-researcher gsd-planner gsd-plan-checker',
|
||||
'msd-phase-researcher msd-planner msd-plan-checker',
|
||||
);
|
||||
const tmpDir = makeTempWorkflowsDir(files);
|
||||
let cleaned = false;
|
||||
@@ -778,9 +778,9 @@ describe('assertPointsCoverage per-step ownership (FIX 1)', () => {
|
||||
// ─── 9. Regression: FIX 2 — multiple blocks + duplicate keys ─────────────────
|
||||
|
||||
describe('parseLoopHostBlock multiple-block and duplicate-key detection (FIX 2)', () => {
|
||||
test('throws when a file has two gsd:loop-host marker blocks', () => {
|
||||
test('throws when a file has two msd:loop-host marker blocks', () => {
|
||||
const block =
|
||||
'<!-- gsd:loop-host\n' +
|
||||
'<!-- msd:loop-host\n' +
|
||||
'step: discuss\n' +
|
||||
'points: discuss:pre, discuss:post\n' +
|
||||
'agent-roles: orchestrator\n' +
|
||||
@@ -790,13 +790,13 @@ describe('parseLoopHostBlock multiple-block and duplicate-key detection (FIX 2)'
|
||||
const content = block + '\nSome prose.\n\n' + block;
|
||||
assert.throws(
|
||||
() => parseLoopHostBlock(content, 'discuss-phase.md'),
|
||||
/expected exactly one gsd:loop-host marker block, found 2/,
|
||||
/expected exactly one msd:loop-host marker block, found 2/,
|
||||
);
|
||||
});
|
||||
|
||||
test('throws when a block has a duplicate "points" key', () => {
|
||||
const content =
|
||||
'<!-- gsd:loop-host\n' +
|
||||
'<!-- msd:loop-host\n' +
|
||||
'step: discuss\n' +
|
||||
'points: discuss:pre, discuss:post\n' +
|
||||
'points: discuss:pre\n' + // duplicate
|
||||
@@ -806,13 +806,13 @@ describe('parseLoopHostBlock multiple-block and duplicate-key detection (FIX 2)'
|
||||
'-->\n';
|
||||
assert.throws(
|
||||
() => parseLoopHostBlock(content, 'discuss-phase.md'),
|
||||
/duplicate key 'points' in gsd:loop-host marker/,
|
||||
/duplicate key 'points' in msd:loop-host marker/,
|
||||
);
|
||||
});
|
||||
|
||||
test('throws when a block has a duplicate "step" key', () => {
|
||||
const content =
|
||||
'<!-- gsd:loop-host\n' +
|
||||
'<!-- msd:loop-host\n' +
|
||||
'step: discuss\n' +
|
||||
'step: plan\n' + // duplicate
|
||||
'points: discuss:pre, discuss:post\n' +
|
||||
@@ -822,14 +822,14 @@ describe('parseLoopHostBlock multiple-block and duplicate-key detection (FIX 2)'
|
||||
'-->\n';
|
||||
assert.throws(
|
||||
() => parseLoopHostBlock(content, 'discuss-phase.md'),
|
||||
/duplicate key 'step' in gsd:loop-host marker/,
|
||||
/duplicate key 'step' in msd:loop-host marker/,
|
||||
);
|
||||
});
|
||||
|
||||
test('buildContract with a two-block file throws with "found 2" error', () => {
|
||||
const files = makeValidWorkflowFiles();
|
||||
const singleBlock =
|
||||
'<!-- gsd:loop-host\n' +
|
||||
'<!-- msd:loop-host\n' +
|
||||
'step: discuss\n' +
|
||||
'points: discuss:pre, discuss:post\n' +
|
||||
'agent-roles: orchestrator\n' +
|
||||
@@ -852,43 +852,43 @@ describe('parseLoopHostBlock multiple-block and duplicate-key detection (FIX 2)'
|
||||
// ─── 10. Regression: FIX 3 — word-boundary agent cross-check ─────────────────
|
||||
|
||||
describe('crossCheckRoles word-boundary match (FIX 3)', () => {
|
||||
test('gsd-plan-checker-v2 does NOT satisfy required gsd-plan-checker reference', () => {
|
||||
// Content has gsd-plan-checker-v2 but NOT bare gsd-plan-checker
|
||||
const content = 'Agent("gsd-phase-researcher") Agent("gsd-planner") gsd-plan-checker-v2';
|
||||
test('msd-plan-checker-v2 does NOT satisfy required msd-plan-checker reference', () => {
|
||||
// Content has msd-plan-checker-v2 but NOT bare msd-plan-checker
|
||||
const content = 'Agent("msd-phase-researcher") Agent("msd-planner") msd-plan-checker-v2';
|
||||
const errors = crossCheckRoles(content, ['researcher', 'planner', 'checker'], 'plan-phase.md');
|
||||
assert.strictEqual(errors.length, 1, 'expected exactly 1 error for checker missing bare reference');
|
||||
assert.match(errors[0], /gsd-plan-checker/);
|
||||
assert.match(errors[0], /msd-plan-checker/);
|
||||
});
|
||||
|
||||
test('gsd-plan-checker (bare) still satisfies the checker role', () => {
|
||||
const content = 'Agent("gsd-phase-researcher") Agent("gsd-planner") gsd-plan-checker something-else';
|
||||
test('msd-plan-checker (bare) still satisfies the checker role', () => {
|
||||
const content = 'Agent("msd-phase-researcher") Agent("msd-planner") msd-plan-checker something-else';
|
||||
const errors = crossCheckRoles(content, ['researcher', 'planner', 'checker'], 'plan-phase.md');
|
||||
assert.deepEqual(errors, []);
|
||||
});
|
||||
|
||||
test('gsd-plan-checker immediately followed by newline satisfies the checker role', () => {
|
||||
const content = 'gsd-phase-researcher\ngsd-planner\ngsd-plan-checker\n';
|
||||
test('msd-plan-checker immediately followed by newline satisfies the checker role', () => {
|
||||
const content = 'msd-phase-researcher\nmsd-planner\nmsd-plan-checker\n';
|
||||
const errors = crossCheckRoles(content, ['researcher', 'planner', 'checker'], 'plan-phase.md');
|
||||
assert.deepEqual(errors, []);
|
||||
});
|
||||
|
||||
test('buildContract rejects workflow referencing only -v2 agent variant', () => {
|
||||
const files = makeValidWorkflowFiles();
|
||||
// plan-phase.md refers to gsd-plan-checker-v2 but not gsd-plan-checker
|
||||
// plan-phase.md refers to msd-plan-checker-v2 but not msd-plan-checker
|
||||
files['plan-phase.md'] =
|
||||
'<!-- gsd:loop-host\n' +
|
||||
'<!-- msd:loop-host\n' +
|
||||
'step: plan\n' +
|
||||
'points: plan:pre, plan:post\n' +
|
||||
'agent-roles: researcher, planner, checker\n' +
|
||||
'produces: PLAN.md\n' +
|
||||
'consumes: CONTEXT.md\n' +
|
||||
'-->\n' +
|
||||
'gsd-phase-researcher gsd-planner gsd-plan-checker-v2\n';
|
||||
'msd-phase-researcher msd-planner msd-plan-checker-v2\n';
|
||||
const tmpDir = makeTempWorkflowsDir(files);
|
||||
try {
|
||||
assert.throws(
|
||||
() => buildContract(tmpDir),
|
||||
/gsd-plan-checker.*not referenced|checker.*gsd-plan-checker/,
|
||||
/msd-plan-checker.*not referenced|checker.*msd-plan-checker/,
|
||||
);
|
||||
} finally {
|
||||
cleanup(tmpDir);
|
||||
@@ -944,7 +944,7 @@ describe('--check full-content comparison (FIX 4)', () => {
|
||||
|
||||
describe('temp-dir lifecycle', () => {
|
||||
test('makeTempWorkflowsDir creates a directory that can be cleaned up', () => {
|
||||
const files = { 'dummy.md': '<!-- gsd:loop-host\nstep: discuss\n-->' };
|
||||
const files = { 'dummy.md': '<!-- msd:loop-host\nstep: discuss\n-->' };
|
||||
const tmpDir = makeTempWorkflowsDir(files);
|
||||
assert.ok(fs.existsSync(tmpDir), 'temp dir must exist after creation');
|
||||
cleanup(tmpDir);
|
||||
|
||||
Reference in New Issue
Block a user