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

@@ -17,9 +17,9 @@ const {
VALID_PROFILES,
formatAgentToModelMapAsTable,
getAgentToModelMapForProfile,
} = require('../gsd-core/bin/lib/model-profiles.cjs');
} = require('../msd-core/bin/lib/model-profiles.cjs');
const { resolveModelInternal } = require('../gsd-core/bin/lib/model-resolver.cjs');
const { resolveModelInternal } = require('../msd-core/bin/lib/model-resolver.cjs');
const { createTempProject, cleanup } = require('./helpers.cjs');
const { listAgentFiles } = require('./helpers/agent-roster.cjs');
@@ -36,8 +36,8 @@ function writeConfig(tmpDir, obj) {
// ─── MODEL_PROFILES data integrity ────────────────────────────────────────────
describe('MODEL_PROFILES', () => {
test('contains every shipped gsd agent file on disk (#3229)', () => {
// Canonical source roster (sorted gsd-* basenames without .md) — shared helper.
test('contains every shipped msd agent file on disk (#3229)', () => {
// Canonical source roster (sorted msd-* basenames without .md) — shared helper.
const expectedAgents = listAgentFiles();
const actualAgents = Object.keys(MODEL_PROFILES).sort();
assert.deepStrictEqual(actualAgents, expectedAgents);
@@ -82,7 +82,7 @@ describe('VALID_PROFILES', () => {
});
test('includes all MODEL_PROFILES keys plus inherit', () => {
const fromData = Object.keys(MODEL_PROFILES['gsd-planner']);
const fromData = Object.keys(MODEL_PROFILES['msd-planner']);
for (const profile of fromData) {
assert.ok(VALID_PROFILES.includes(profile), `VALID_PROFILES should include ${profile}`);
}
@@ -95,41 +95,41 @@ describe('VALID_PROFILES', () => {
describe('getAgentToModelMapForProfile', () => {
test('returns correct models for balanced profile', () => {
const map = getAgentToModelMapForProfile('balanced');
assert.strictEqual(map['gsd-planner'], 'opus');
assert.strictEqual(map['gsd-codebase-mapper'], 'haiku');
assert.strictEqual(map['gsd-verifier'], 'sonnet');
assert.strictEqual(map['msd-planner'], 'opus');
assert.strictEqual(map['msd-codebase-mapper'], 'haiku');
assert.strictEqual(map['msd-verifier'], 'sonnet');
});
test('returns correct models for budget profile', () => {
const map = getAgentToModelMapForProfile('budget');
assert.strictEqual(map['gsd-planner'], 'sonnet');
assert.strictEqual(map['gsd-phase-researcher'], 'haiku');
assert.strictEqual(map['msd-planner'], 'sonnet');
assert.strictEqual(map['msd-phase-researcher'], 'haiku');
});
test('returns correct models for quality profile', () => {
const map = getAgentToModelMapForProfile('quality');
assert.strictEqual(map['gsd-planner'], 'opus');
assert.strictEqual(map['gsd-executor'], 'opus');
assert.strictEqual(map['msd-planner'], 'opus');
assert.strictEqual(map['msd-executor'], 'opus');
});
test('returns correct models for adaptive profile', () => {
const map = getAgentToModelMapForProfile('adaptive');
assert.strictEqual(map['gsd-planner'], 'opus', 'planner should use opus in adaptive');
assert.strictEqual(map['gsd-debugger'], 'opus', 'debugger should use opus in adaptive');
assert.strictEqual(map['gsd-executor'], 'sonnet', 'executor should use sonnet in adaptive');
assert.strictEqual(map['gsd-codebase-mapper'], 'haiku', 'mapper should use haiku in adaptive');
assert.strictEqual(map['gsd-plan-checker'], 'haiku', 'checker should use haiku in adaptive');
assert.strictEqual(map['msd-planner'], 'opus', 'planner should use opus in adaptive');
assert.strictEqual(map['msd-debugger'], 'opus', 'debugger should use opus in adaptive');
assert.strictEqual(map['msd-executor'], 'sonnet', 'executor should use sonnet in adaptive');
assert.strictEqual(map['msd-codebase-mapper'], 'haiku', 'mapper should use haiku in adaptive');
assert.strictEqual(map['msd-plan-checker'], 'haiku', 'checker should use haiku in adaptive');
});
// ─── resolution order: override > profile > default ─────────────────────────
// Uses gsd-phase-researcher because it has visibly distinct values at every
// Uses msd-phase-researcher because it has visibly distinct values at every
// level: balanced (default) = sonnet, budget (profile) = haiku, override = opus.
// Each tier must beat the one below it; the test goes RED if resolveModelInternal
// ignores model_overrides (returns 'haiku') or conflates default with profile
// (returns 'sonnet' instead of 'haiku' for budget).
describe('resolution order: override > profile > default', () => {
// agent under test — must have three distinct model values across tiers
const AGENT = 'gsd-phase-researcher';
const AGENT = 'msd-phase-researcher';
const EXPECTED_DEFAULT = 'sonnet'; // balanced profile (no config)
const EXPECTED_PROFILE = 'haiku'; // budget profile
const EXPECTED_OVERRIDE = 'opus'; // explicit model_overrides entry
@@ -193,12 +193,12 @@ describe('getAgentToModelMapForProfile', () => {
describe('formatAgentToModelMapAsTable', () => {
test('produces a table with header and separator', () => {
const map = { 'gsd-planner': 'opus', 'gsd-executor': 'sonnet' };
const map = { 'msd-planner': 'opus', 'msd-executor': 'sonnet' };
const table = formatAgentToModelMapAsTable(map);
assert.ok(table.includes('Agent'), 'should have Agent header');
assert.ok(table.includes('Model'), 'should have Model header');
assert.ok(table.includes('─'), 'should have separator line');
assert.ok(table.includes('gsd-planner'), 'should list agent');
assert.ok(table.includes('msd-planner'), 'should list agent');
assert.ok(table.includes('opus'), 'should list model');
});
@@ -235,12 +235,12 @@ describe('formatAgentToModelMapAsTable', () => {
* agent_runtime was not surfaced at all.
*
* After the fix:
* - GSD_RUNTIME=opencode + OPENCODE_CONFIG_DIR pointing at a temp dir →
* - MSD_RUNTIME=opencode + OPENCODE_CONFIG_DIR pointing at a temp dir →
* agents_installed=true, agent_runtime='opencode', agents_dir under the
* opencode config dir
* - No GSD_RUNTIME + GSD_AGENTS_DIR pointing at a temp dir →
* - No MSD_RUNTIME + MSD_AGENTS_DIR pointing at a temp dir →
* agents_installed=true, agent_runtime='claude'
* - GSD_RUNTIME=opencode but agents dir empty →
* - MSD_RUNTIME=opencode but agents dir empty →
* agents_installed=false, agent_runtime='opencode'
*/
@@ -249,9 +249,9 @@ const assert = require('node:assert/strict');
const fs = require('fs');
const path = require('path');
const os = require('os');
const { runGsdTools, createTempProject, cleanup } = require('./helpers.cjs');
const { runMsdTools, createTempProject, cleanup } = require('./helpers.cjs');
const MODEL_PROFILES = require('../gsd-core/bin/lib/model-profiles.cjs').MODEL_PROFILES;
const MODEL_PROFILES = require('../msd-core/bin/lib/model-profiles.cjs').MODEL_PROFILES;
const EXPECTED_AGENTS = Object.keys(MODEL_PROFILES);
/**
@@ -277,7 +277,7 @@ describe('bug #384 — getAgentsDir() is runtime-aware', () => {
beforeEach(() => {
tmpDir = createTempProject();
// Separate temp dir to act as the opencode global config dir
opencodeConfigDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-test-opencode-'));
opencodeConfigDir = fs.mkdtempSync(path.join(os.tmpdir(), 'msd-test-opencode-'));
});
afterEach(() => {
@@ -287,21 +287,21 @@ describe('bug #384 — getAgentsDir() is runtime-aware', () => {
// ── Test 1: opencode runtime resolves the opencode agents path ──────────────
test('GSD_RUNTIME=opencode finds agents under OPENCODE_CONFIG_DIR/agents', () => {
test('MSD_RUNTIME=opencode finds agents under OPENCODE_CONFIG_DIR/agents', () => {
// Place agents under the opencode config dir that getGlobalConfigDir('opencode')
// will return when OPENCODE_CONFIG_DIR is set.
const agentsDir = createAgentsInConfigDir(opencodeConfigDir);
const result = runGsdTools(
const result = runMsdTools(
['init', 'quick', 'test description', '--raw'],
tmpDir,
{
GSD_RUNTIME: 'opencode',
MSD_RUNTIME: 'opencode',
OPENCODE_CONFIG_DIR: opencodeConfigDir,
// Ensure the process HOME does NOT have a conflicting ~/.claude/agents
// that might accidentally produce a false positive via GSD_AGENTS_DIR
// (we must NOT set GSD_AGENTS_DIR here — the whole point is that the fix
// uses the runtime-aware path without needing GSD_AGENTS_DIR).
// that might accidentally produce a false positive via MSD_AGENTS_DIR
// (we must NOT set MSD_AGENTS_DIR here — the whole point is that the fix
// uses the runtime-aware path without needing MSD_AGENTS_DIR).
}
);
assert.ok(result.success, `Command failed: ${result.error}`);
@@ -313,27 +313,27 @@ describe('bug #384 — getAgentsDir() is runtime-aware', () => {
`agents_dir=${output.agents_dir}, agent_runtime=${output.agent_runtime}`);
assert.strictEqual(output.agent_runtime, 'opencode',
'agent_runtime must be "opencode" when GSD_RUNTIME=opencode');
'agent_runtime must be "opencode" when MSD_RUNTIME=opencode');
assert.strictEqual(output.agents_dir, agentsDir,
`agents_dir must point at the opencode agents dir (${agentsDir}), got: ${output.agents_dir}`);
});
// ── Test 2: claude fallback via GSD_AGENTS_DIR ──────────────────────────────
// ── Test 2: claude fallback via MSD_AGENTS_DIR ──────────────────────────────
test('default runtime (no GSD_RUNTIME) with GSD_AGENTS_DIR → agents_installed=true, agent_runtime=claude', () => {
// Classic GSD_AGENTS_DIR override: no runtime set, use the env shortcut
test('default runtime (no MSD_RUNTIME) with MSD_AGENTS_DIR → agents_installed=true, agent_runtime=claude', () => {
// Classic MSD_AGENTS_DIR override: no runtime set, use the env shortcut
createAgentsInConfigDir(tmpDir);
// GSD_AGENTS_DIR points directly at the agents dir (not the config dir)
// MSD_AGENTS_DIR points directly at the agents dir (not the config dir)
const directAgentsDir = path.join(tmpDir, 'agents');
const result = runGsdTools(
const result = runMsdTools(
['init', 'quick', 'test description', '--raw'],
tmpDir,
{
GSD_AGENTS_DIR: directAgentsDir,
// Explicitly unset GSD_RUNTIME so no runtime override applies
GSD_RUNTIME: '',
MSD_AGENTS_DIR: directAgentsDir,
// Explicitly unset MSD_RUNTIME so no runtime override applies
MSD_RUNTIME: '',
}
);
assert.ok(result.success, `Command failed: ${result.error}`);
@@ -341,28 +341,28 @@ describe('bug #384 — getAgentsDir() is runtime-aware', () => {
const output = JSON.parse(result.output);
assert.strictEqual(output.agents_installed, true,
`agents_installed must be true when GSD_AGENTS_DIR points at a populated agents dir. ` +
`agents_installed must be true when MSD_AGENTS_DIR points at a populated agents dir. ` +
`agents_dir=${output.agents_dir}`);
assert.strictEqual(output.agent_runtime, 'claude',
'agent_runtime must be "claude" when no GSD_RUNTIME is set');
'agent_runtime must be "claude" when no MSD_RUNTIME is set');
assert.strictEqual(output.agents_dir, directAgentsDir,
`agents_dir must match GSD_AGENTS_DIR override`);
`agents_dir must match MSD_AGENTS_DIR override`);
});
// ── Test 3 (negative): opencode runtime, empty agents dir ───────────────────
test('GSD_RUNTIME=opencode with empty agents dir → agents_installed=false, agent_runtime still surfaced', () => {
test('MSD_RUNTIME=opencode with empty agents dir → agents_installed=false, agent_runtime still surfaced', () => {
// Create the opencode config dir but leave agents/ empty (no files)
const emptyAgentsDir = path.join(opencodeConfigDir, 'agents');
fs.mkdirSync(emptyAgentsDir, { recursive: true });
const result = runGsdTools(
const result = runMsdTools(
['init', 'quick', 'test description', '--raw'],
tmpDir,
{
GSD_RUNTIME: 'opencode',
MSD_RUNTIME: 'opencode',
OPENCODE_CONFIG_DIR: opencodeConfigDir,
}
);
@@ -429,7 +429,7 @@ describe('bug #384 — getAgentsDir() is runtime-aware', () => {
'use strict';
process.env.GSD_TEST_MODE = '1';
process.env.MSD_TEST_MODE = '1';
const { test, describe, beforeEach, afterEach } = require('node:test');
const assert = require('node:assert/strict');
@@ -438,17 +438,17 @@ const path = require('node:path');
const {
resolveModelInternal,
resolveModelForTier,
} = require('../gsd-core/bin/lib/model-resolver.cjs');
} = require('../msd-core/bin/lib/model-resolver.cjs');
const {
AGENT_DEFAULT_TIERS,
VALID_AGENT_TIERS,
MODEL_PROFILES,
nextTier,
} = require('../gsd-core/bin/lib/model-profiles.cjs');
const { isValidConfigKey } = require('../gsd-core/bin/lib/config-schema.cjs');
} = require('../msd-core/bin/lib/model-profiles.cjs');
const { isValidConfigKey } = require('../msd-core/bin/lib/config-schema.cjs');
const { createTempDir, cleanup } = require('./helpers.cjs');
const makeTmp = (prefix) => createTempDir(`gsd-3024-${prefix}-`);
const makeTmp = (prefix) => createTempDir(`msd-3024-${prefix}-`);
function writeConfig(dir, config) {
const planningDir = path.join(dir, '.planning');
fs.mkdirSync(planningDir, { recursive: true });
@@ -516,8 +516,8 @@ describe('#3024 resolveModelForTier: disabled mode is a no-op (acceptance criter
test('with no dynamic_routing block, falls back to resolveModelInternal', () => {
writeConfig(projectDir, { model_profile: 'balanced' });
// resolveModelForTier with attempt=0 must match resolveModelInternal.
const baseline = resolveModelInternal(projectDir, 'gsd-phase-researcher');
assert.equal(resolveModelForTier(projectDir, 'gsd-phase-researcher', 0), baseline);
const baseline = resolveModelInternal(projectDir, 'msd-phase-researcher');
assert.equal(resolveModelForTier(projectDir, 'msd-phase-researcher', 0), baseline);
});
test('with dynamic_routing.enabled=false, attempt argument is ignored — same as resolveModelInternal', () => {
@@ -528,10 +528,10 @@ describe('#3024 resolveModelForTier: disabled mode is a no-op (acceptance criter
tier_models: { light: 'haiku', standard: 'sonnet', heavy: 'opus' },
},
});
const baseline = resolveModelInternal(projectDir, 'gsd-phase-researcher');
const baseline = resolveModelInternal(projectDir, 'msd-phase-researcher');
// attempt=0 and attempt=1 both ignored when disabled
assert.equal(resolveModelForTier(projectDir, 'gsd-phase-researcher', 0), baseline);
assert.equal(resolveModelForTier(projectDir, 'gsd-phase-researcher', 1), baseline);
assert.equal(resolveModelForTier(projectDir, 'msd-phase-researcher', 0), baseline);
assert.equal(resolveModelForTier(projectDir, 'msd-phase-researcher', 1), baseline);
});
});
@@ -550,16 +550,16 @@ describe('#3024 resolveModelForTier: enabled mode picks tier_models[default_tier
tier_models: { light: 'haiku', standard: 'sonnet', heavy: 'opus' },
},
});
// gsd-codebase-mapper has light default tier per AGENT_DEFAULT_TIERS.
// msd-codebase-mapper has light default tier per AGENT_DEFAULT_TIERS.
// CR nitpick (#3031): assert preconditions explicitly so a tier
// re-mapping in AGENT_DEFAULT_TIERS surfaces as a test failure
// instead of a silent skip.
assert.equal(AGENT_DEFAULT_TIERS['gsd-codebase-mapper'], 'light',
'gsd-codebase-mapper expected to be light tier');
assert.equal(resolveModelForTier(projectDir, 'gsd-codebase-mapper', 0), 'haiku');
assert.equal(AGENT_DEFAULT_TIERS['gsd-planner'], 'heavy',
'gsd-planner expected to be heavy tier');
assert.equal(resolveModelForTier(projectDir, 'gsd-planner', 0), 'opus');
assert.equal(AGENT_DEFAULT_TIERS['msd-codebase-mapper'], 'light',
'msd-codebase-mapper expected to be light tier');
assert.equal(resolveModelForTier(projectDir, 'msd-codebase-mapper', 0), 'haiku');
assert.equal(AGENT_DEFAULT_TIERS['msd-planner'], 'heavy',
'msd-planner expected to be heavy tier');
assert.equal(resolveModelForTier(projectDir, 'msd-planner', 0), 'opus');
});
test('attempt=1 escalates to next tier up (acceptance criterion 3)', () => {
@@ -690,15 +690,15 @@ describe('#3024 precedence: per-agent override > dynamic_routing > models > prof
test('per-agent model_overrides beats dynamic_routing (acceptance criterion: override wins)', () => {
writeConfig(projectDir, {
model_profile: 'balanced',
model_overrides: { 'gsd-codebase-mapper': 'openai/gpt-5' },
model_overrides: { 'msd-codebase-mapper': 'openai/gpt-5' },
dynamic_routing: {
enabled: true,
tier_models: { light: 'haiku', standard: 'sonnet', heavy: 'opus' },
},
});
// Per-agent override always wins, even at escalated attempt.
assert.equal(resolveModelForTier(projectDir, 'gsd-codebase-mapper', 0), 'openai/gpt-5');
assert.equal(resolveModelForTier(projectDir, 'gsd-codebase-mapper', 1), 'openai/gpt-5');
assert.equal(resolveModelForTier(projectDir, 'msd-codebase-mapper', 0), 'openai/gpt-5');
assert.equal(resolveModelForTier(projectDir, 'msd-codebase-mapper', 1), 'openai/gpt-5');
});
test('dynamic_routing beats phase-type models (#3023)', () => {
@@ -710,10 +710,10 @@ describe('#3024 precedence: per-agent override > dynamic_routing > models > prof
tier_models: { light: 'haiku', standard: 'sonnet', heavy: 'opus' },
},
});
// gsd-codebase-mapper is research phase-type; phase-type would give 'opus',
// msd-codebase-mapper is research phase-type; phase-type would give 'opus',
// but dynamic routing (light default → haiku) wins.
if (AGENT_DEFAULT_TIERS['gsd-codebase-mapper'] === 'light') {
assert.equal(resolveModelForTier(projectDir, 'gsd-codebase-mapper', 0), 'haiku');
if (AGENT_DEFAULT_TIERS['msd-codebase-mapper'] === 'light') {
assert.equal(resolveModelForTier(projectDir, 'msd-codebase-mapper', 0), 'haiku');
}
});
});