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