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

@@ -3,7 +3,7 @@
// Claude Code loads at runtime. Checking text content IS checking the deployed contract.
/**
* GSD Agent Frontmatter Tests
* MSD Agent Frontmatter Tests
*
* Validates that all agent .md files have correct frontmatter fields:
* - Anti-heredoc instruction present in file-writing agents
@@ -19,8 +19,8 @@ const path = require('path');
const { listAgentFiles } = require('./helpers/agent-roster.cjs');
const AGENTS_DIR = path.join(__dirname, '..', 'agents');
const WORKFLOWS_DIR = path.join(__dirname, '..', 'gsd-core', 'workflows');
const COMMANDS_DIR = path.join(__dirname, '..', 'commands', 'gsd');
const WORKFLOWS_DIR = path.join(__dirname, '..', 'msd-core', 'workflows');
const COMMANDS_DIR = path.join(__dirname, '..', 'commands', 'msd');
// Sorted basenames (without `.md`); reads below re-add `.md` via `name + '.md'`.
const ALL_AGENTS = listAgentFiles(AGENTS_DIR);
@@ -107,11 +107,11 @@ describe('HOOK: hooks frontmatter pattern', () => {
describe('SPAWN: spawn type consistency', () => {
// #1689: `subagent_type="{TOKEN}"` is a workflow-bound placeholder for
// parameterized executor dispatch (resolved at runtime via `gsd-tools
// resolve-agent`, defaulting to gsd-executor), not a concrete agent name.
// parameterized executor dispatch (resolved at runtime via `msd-tools
// resolve-agent`, defaulting to msd-executor), not a concrete agent name.
// The static spawn-type checks below skip these — the effective value is
// validated at dispatch time, and execute-phase.md still documents the
// built-in roster (incl. gsd-executor) in <available_agent_types>.
// built-in roster (incl. msd-executor) in <available_agent_types>.
const PARAMETERIZED_SPAWN_TYPE = /^\{[^}]+\}$/;
test('no "First, read agent .md" workaround pattern remains', () => {
@@ -121,7 +121,7 @@ describe('SPAWN: spawn type consistency', () => {
const files = fs.readdirSync(dir).filter(f => f.endsWith('.md'));
for (const file of files) {
const content = fs.readFileSync(path.join(dir, file), 'utf-8');
const hasWorkaround = content.includes('First, read ~/.claude/agents/gsd-');
const hasWorkaround = content.includes('First, read ~/.claude/agents/msd-');
assert.ok(
!hasWorkaround,
`${file} still has "First, read agent .md" workaround — use named subagent_type instead`
@@ -156,13 +156,13 @@ describe('SPAWN: spawn type consistency', () => {
}
});
test('diagnose-issues uses gsd-debugger (not general-purpose)', () => {
test('diagnose-issues uses msd-debugger (not general-purpose)', () => {
const content = fs.readFileSync(
path.join(WORKFLOWS_DIR, 'diagnose-issues.md'), 'utf-8'
);
assert.ok(
content.includes('subagent_type="gsd-debugger"'),
'diagnose-issues should spawn gsd-debugger, not general-purpose'
content.includes('subagent_type="msd-debugger"'),
'diagnose-issues should spawn msd-debugger, not general-purpose'
);
});
@@ -171,7 +171,7 @@ describe('SPAWN: spawn type consistency', () => {
// context. Without an <available_agent_types> section, the orchestrator may
// fall back to general-purpose, silently breaking agent capabilities.
// PR #1139 added this to plan-phase and execute-phase but missed all other
// workflows that spawn named GSD agents.
// workflows that spawn named MSD agents.
const dirs = [WORKFLOWS_DIR, COMMANDS_DIR];
for (const dir of dirs) {
if (!fs.existsSync(dir)) continue;
@@ -249,7 +249,7 @@ describe('AGENT: required frontmatter fields', () => {
// ─── Model resolution uniformity (#3895) ─────────────────────────────────────
describe('MODEL: no agent hardcodes a model frontmatter pin', () => {
// Exactly one shipped agent (gsd-mempalace-curator) carried `model: sonnet`
// Exactly one shipped agent (msd-mempalace-curator) carried `model: sonnet`
// while the other 33 resolved through the model-profile system. The pin
// intercepted #2517's deliberate inherit case — the ship:post dispatch OMITS
// model= on inherit so the agent inherits the orchestrator's model, but the
@@ -270,9 +270,9 @@ describe('MODEL: no agent hardcodes a model frontmatter pin', () => {
});
test('the curator keeps its catalog entry (deleting the pin must not orphan the agent)', () => {
const catalog = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'gsd-core', 'bin', 'shared', 'model-catalog.json'), 'utf-8'));
const entry = catalog.agents && catalog.agents['gsd-mempalace-curator'];
assert.ok(entry, 'catalog entry for gsd-mempalace-curator must exist');
const catalog = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'msd-core', 'bin', 'shared', 'model-catalog.json'), 'utf-8'));
const entry = catalog.agents && catalog.agents['msd-mempalace-curator'];
assert.ok(entry, 'catalog entry for msd-mempalace-curator must exist');
assert.equal(entry.golden, 'sonnet', 'golden profile preserves the pinned behavior');
assert.equal(entry.balanced, 'sonnet', 'balanced profile preserves the pinned behavior');
});
@@ -305,44 +305,44 @@ describe('COLOR: color frontmatter must be a documented named color', () => {
// ─── CLAUDE.md Compliance ───────────────────────────────────────────────────
describe('CLAUDEMD: CLAUDE.md compliance enforcement', () => {
test('gsd-plan-checker has Dimension 10: CLAUDE.md Compliance', () => {
const content = fs.readFileSync(path.join(AGENTS_DIR, 'gsd-plan-checker.md'), 'utf-8');
test('msd-plan-checker has Dimension 10: CLAUDE.md Compliance', () => {
const content = fs.readFileSync(path.join(AGENTS_DIR, 'msd-plan-checker.md'), 'utf-8');
assert.ok(
content.includes('Dimension 10: CLAUDE.md Compliance'),
'gsd-plan-checker must have Dimension 10 for CLAUDE.md compliance checking'
'msd-plan-checker must have Dimension 10 for CLAUDE.md compliance checking'
);
assert.ok(
content.includes('claude_md_compliance'),
'gsd-plan-checker must use claude_md_compliance as dimension identifier'
'msd-plan-checker must use claude_md_compliance as dimension identifier'
);
});
test('gsd-phase-researcher has CLAUDE.md enforcement directive', () => {
const content = fs.readFileSync(path.join(AGENTS_DIR, 'gsd-phase-researcher.md'), 'utf-8');
test('msd-phase-researcher has CLAUDE.md enforcement directive', () => {
const content = fs.readFileSync(path.join(AGENTS_DIR, 'msd-phase-researcher.md'), 'utf-8');
assert.ok(
content.includes('CLAUDE.md enforcement'),
'gsd-phase-researcher must enforce CLAUDE.md directives during research'
'msd-phase-researcher must enforce CLAUDE.md directives during research'
);
assert.ok(
content.includes('Project Constraints (from CLAUDE.md)'),
'gsd-phase-researcher must output a Project Constraints section from CLAUDE.md'
'msd-phase-researcher must output a Project Constraints section from CLAUDE.md'
);
});
test('gsd-executor has CLAUDE.md enforcement directive', () => {
const content = fs.readFileSync(path.join(AGENTS_DIR, 'gsd-executor.md'), 'utf-8');
test('msd-executor has CLAUDE.md enforcement directive', () => {
const content = fs.readFileSync(path.join(AGENTS_DIR, 'msd-executor.md'), 'utf-8');
assert.ok(
content.includes('CLAUDE.md enforcement'),
'gsd-executor must enforce CLAUDE.md directives during execution'
'msd-executor must enforce CLAUDE.md directives during execution'
);
assert.ok(
content.includes('CLAUDE.md rule — it takes precedence over plan instructions'),
'gsd-executor must specify CLAUDE.md precedence over plan instructions'
'msd-executor must specify CLAUDE.md precedence over plan instructions'
);
});
test('all three agents read CLAUDE.md in project_context', () => {
const agents = ['gsd-plan-checker', 'gsd-phase-researcher', 'gsd-executor'];
const agents = ['msd-plan-checker', 'msd-phase-researcher', 'msd-executor'];
for (const agent of agents) {
const content = fs.readFileSync(path.join(AGENTS_DIR, agent + '.md'), 'utf-8');
assert.ok(
@@ -356,36 +356,36 @@ describe('CLAUDEMD: CLAUDE.md compliance enforcement', () => {
// ─── Verification Data-Flow and Environment Audit (#1245) ────────────────────
describe('VERIFY: data-flow trace, environment audit, and behavioral spot-checks', () => {
test('gsd-verifier has Step 4b: Data-Flow Trace', () => {
const content = fs.readFileSync(path.join(AGENTS_DIR, 'gsd-verifier.md'), 'utf-8');
test('msd-verifier has Step 4b: Data-Flow Trace', () => {
const content = fs.readFileSync(path.join(AGENTS_DIR, 'msd-verifier.md'), 'utf-8');
assert.ok(
content.includes('Step 4b: Data-Flow Trace'),
'gsd-verifier must have Step 4b for data-flow tracing'
'msd-verifier must have Step 4b for data-flow tracing'
);
assert.ok(
content.includes('HOLLOW'),
'gsd-verifier must define HOLLOW status for wired-but-disconnected artifacts'
'msd-verifier must define HOLLOW status for wired-but-disconnected artifacts'
);
assert.ok(
content.includes('DISCONNECTED'),
'gsd-verifier must define DISCONNECTED status for missing data sources'
'msd-verifier must define DISCONNECTED status for missing data sources'
);
});
test('gsd-verifier has Step 7b: Behavioral Spot-Checks', () => {
const content = fs.readFileSync(path.join(AGENTS_DIR, 'gsd-verifier.md'), 'utf-8');
test('msd-verifier has Step 7b: Behavioral Spot-Checks', () => {
const content = fs.readFileSync(path.join(AGENTS_DIR, 'msd-verifier.md'), 'utf-8');
assert.ok(
content.includes('Step 7b: Behavioral Spot-Checks'),
'gsd-verifier must have Step 7b for behavioral spot-checks'
'msd-verifier must have Step 7b for behavioral spot-checks'
);
assert.ok(
content.includes('SKIP'),
'gsd-verifier spot-checks must support SKIP status for untestable items'
'msd-verifier spot-checks must support SKIP status for untestable items'
);
});
test('gsd-verifier VERIFICATION.md template includes data-flow and spot-check sections', () => {
const content = fs.readFileSync(path.join(AGENTS_DIR, 'gsd-verifier.md'), 'utf-8');
test('msd-verifier VERIFICATION.md template includes data-flow and spot-check sections', () => {
const content = fs.readFileSync(path.join(AGENTS_DIR, 'msd-verifier.md'), 'utf-8');
assert.ok(
content.includes('Data-Flow Trace (Level 4)'),
'VERIFICATION.md template must include Data-Flow Trace section'
@@ -396,8 +396,8 @@ describe('VERIFY: data-flow trace, environment audit, and behavioral spot-checks
);
});
test('gsd-verifier success criteria include data-flow and spot-checks', () => {
const content = fs.readFileSync(path.join(AGENTS_DIR, 'gsd-verifier.md'), 'utf-8');
test('msd-verifier success criteria include data-flow and spot-checks', () => {
const content = fs.readFileSync(path.join(AGENTS_DIR, 'msd-verifier.md'), 'utf-8');
assert.ok(
content.includes('Data-flow trace (Level 4)'),
'success criteria must include data-flow trace step'
@@ -408,20 +408,20 @@ describe('VERIFY: data-flow trace, environment audit, and behavioral spot-checks
);
});
test('gsd-phase-researcher has Step 2.6: Environment Availability Audit', () => {
const content = fs.readFileSync(path.join(AGENTS_DIR, 'gsd-phase-researcher.md'), 'utf-8');
test('msd-phase-researcher has Step 2.6: Environment Availability Audit', () => {
const content = fs.readFileSync(path.join(AGENTS_DIR, 'msd-phase-researcher.md'), 'utf-8');
assert.ok(
content.includes('Step 2.6: Environment Availability Audit'),
'gsd-phase-researcher must have Step 2.6 for environment availability auditing'
'msd-phase-researcher must have Step 2.6 for environment availability auditing'
);
assert.ok(
content.includes('Environment Availability'),
'gsd-phase-researcher must include Environment Availability section in RESEARCH.md template'
'msd-phase-researcher must include Environment Availability section in RESEARCH.md template'
);
});
test('gsd-phase-researcher success criteria include environment audit', () => {
const content = fs.readFileSync(path.join(AGENTS_DIR, 'gsd-phase-researcher.md'), 'utf-8');
test('msd-phase-researcher success criteria include environment audit', () => {
const content = fs.readFileSync(path.join(AGENTS_DIR, 'msd-phase-researcher.md'), 'utf-8');
assert.ok(
content.includes('Environment availability audited'),
'success criteria must include environment availability audit step'
@@ -454,7 +454,7 @@ describe('DISCUSS: discussion log generation', () => {
});
test('discussion-log template exists', () => {
const templatePath = path.join(__dirname, '..', 'gsd-core', 'templates', 'discussion-log.md');
const templatePath = path.join(__dirname, '..', 'msd-core', 'templates', 'discussion-log.md');
assert.ok(
fs.existsSync(templatePath),
'discussion-log.md template must exist'
@@ -473,16 +473,16 @@ describe('EDITWRITE: section-writer agents must have both Write and Edit in tool
// These agents perform in-place section edits on shared/existing files (e.g.
// AI-SPEC.md). Without Edit in tools:, the "Edit-only" discipline in their
// spawn-prompt is unenforceable — they fall back to whole-file Write and
// clobber sibling sections. Same bug class as #571/#575 (fixed gsd-doc-writer).
// clobber sibling sections. Same bug class as #571/#575 (fixed msd-doc-writer).
// Issue #581.
const SECTION_WRITER_AGENTS = [
'gsd-eval-planner',
'gsd-ai-researcher',
'gsd-domain-researcher',
'gsd-phase-researcher',
'gsd-ui-researcher',
'gsd-debug-session-manager',
'gsd-planner', // #973: planner lacked Edit; whole-file Write truncated ROADMAP.md
'msd-eval-planner',
'msd-ai-researcher',
'msd-domain-researcher',
'msd-phase-researcher',
'msd-ui-researcher',
'msd-debug-session-manager',
'msd-planner', // #973: planner lacked Edit; whole-file Write truncated ROADMAP.md
];
for (const agent of SECTION_WRITER_AGENTS) {
@@ -509,7 +509,7 @@ describe('COMPAT: agents must not use runtime-specific frontmatter keys', () =>
// permissionMode is Claude Code-specific and breaks Gemini CLI agent loading.
// It also has no effect on subagent Write permissions in Claude Code (blocked
// at runtime level regardless). See #1522, #1387.
const AGENTS_WITH_WRITE = ['gsd-executor', 'gsd-debugger'];
const AGENTS_WITH_WRITE = ['msd-executor', 'msd-debugger'];
for (const agent of AGENTS_WITH_WRITE) {
test(`${agent} does not have permissionMode (breaks Gemini CLI)`, () => {
@@ -534,10 +534,10 @@ describe('COMPAT: agents must not use runtime-specific frontmatter keys', () =>
/**
* Regression tests for bug #2346
*
* Multiple GSD agents (gsd-ui-checker, gsd-planner) entered unbounded Read
* Multiple MSD agents (msd-ui-checker, msd-planner) entered unbounded Read
* loops — re-reading the same file hundreds of times in a single run. Root
* cause: no explicit no-re-read rule or tool-budget cap in the agent prompts.
* gsd-pattern-mapper was fixed in #2312; this covers the remaining agents.
* msd-pattern-mapper was fixed in #2312; this covers the remaining agents.
*
* Fix: add <critical_rules> block to each affected agent with:
* 1. No-re-read constraint
@@ -559,18 +559,18 @@ const AGENTS_DIR = path.join(__dirname, '..', 'agents');
// prevents unbounded Read loops. There is no behavioral equivalent without a live LLM run.
describe('bug #2346: agent read loop guards', () => {
describe('gsd-ui-checker', () => {
const agentPath = path.join(AGENTS_DIR, 'gsd-ui-checker.md');
describe('msd-ui-checker', () => {
const agentPath = path.join(AGENTS_DIR, 'msd-ui-checker.md');
const content = fs.readFileSync(agentPath, 'utf-8');
test('agent file exists', () => {
assert.ok(fs.existsSync(agentPath), 'agents/gsd-ui-checker.md must exist');
assert.ok(fs.existsSync(agentPath), 'agents/msd-ui-checker.md must exist');
});
test('has <critical_rules> block', () => {
assert.ok(
content.includes('<critical_rules>'),
'gsd-ui-checker.md must have a <critical_rules> block to prevent unbounded read loops (#2346)'
'msd-ui-checker.md must have a <critical_rules> block to prevent unbounded read loops (#2346)'
);
});
@@ -596,18 +596,18 @@ describe('bug #2346: agent read loop guards', () => {
});
});
describe('gsd-planner', () => {
const agentPath = path.join(AGENTS_DIR, 'gsd-planner.md');
describe('msd-planner', () => {
const agentPath = path.join(AGENTS_DIR, 'msd-planner.md');
const content = fs.readFileSync(agentPath, 'utf-8');
test('agent file exists', () => {
assert.ok(fs.existsSync(agentPath), 'agents/gsd-planner.md must exist');
assert.ok(fs.existsSync(agentPath), 'agents/msd-planner.md must exist');
});
test('has <critical_rules> block', () => {
assert.ok(
content.includes('<critical_rules>'),
'gsd-planner.md must have a <critical_rules> block to prevent unbounded read loops (#2346)'
'msd-planner.md must have a <critical_rules> block to prevent unbounded read loops (#2346)'
);
});
@@ -652,12 +652,12 @@ describe('bug #2346: agent read loop guards', () => {
/**
* Bug #3605: Stale slash command references in 5 agent files
*
* After #3042 deleted /gsd-research-phase (replaced by
* /gsd-plan-phase --research-phase <N>) and v1.40.0 consolidated /gsd-insert-phase
* into /gsd-phase insert, six occurrences survived in agents/*.md because none of
* After #3042 deleted /msd-research-phase (replaced by
* /msd-plan-phase --research-phase <N>) and v1.40.0 consolidated /msd-insert-phase
* into /msd-phase insert, six occurrences survived in agents/*.md because none of
* the consolidation passes (#3029, #3044, #3131) included agents/ in their per-name
* scrub scope. scripts/fix-slash-commands.cjs lists agents/ in SEARCH_DIRS but only
* runs the /gsd- → /gsd: namespace transform, not retired-name replacement.
* runs the /msd- → /msd: namespace transform, not retired-name replacement.
*
* This guard fails when any retired command name reappears in agents/*.md.
*/
@@ -672,11 +672,11 @@ const path = require('node:path');
const AGENTS_DIR = path.join(__dirname, '..', 'agents');
const RETIRED_COMMANDS = [
'/gsd-research-phase',
'/gsd-insert-phase',
'/gsd-add-phase',
'/gsd-remove-phase',
'/gsd-analyze-dependencies',
'/msd-research-phase',
'/msd-insert-phase',
'/msd-add-phase',
'/msd-remove-phase',
'/msd-analyze-dependencies',
];
// Not the shared listAgentFiles() helper: this returns ABSOLUTE paths (consumed
@@ -748,7 +748,7 @@ describe('bug #3605: agent contracts must not reference retired slash commands',
/**
* Tests for #2427 — prompt-level sycophancy hardening of audit-class agents.
* Verifies the four required changes are present in each agent file:
* 1. Third-person framing (no "You are a GSD X" opening in <role>)
* 1. Third-person framing (no "You are a MSD X" opening in <role>)
* 2. FORCE adversarial stance block
* 3. Explicit failure modes list
* 4. BLOCKER/WARNING classification requirement
@@ -762,15 +762,15 @@ const path = require('node:path');
const AGENTS_DIR = path.join(__dirname, '../agents');
const AUDIT_AGENTS = [
'gsd-plan-checker.md',
'gsd-code-reviewer.md',
'gsd-security-auditor.md',
'gsd-verifier.md',
'gsd-eval-auditor.md',
'gsd-nyquist-auditor.md',
'gsd-ui-auditor.md',
'gsd-integration-checker.md',
'gsd-doc-verifier.md',
'msd-plan-checker.md',
'msd-code-reviewer.md',
'msd-security-auditor.md',
'msd-verifier.md',
'msd-eval-auditor.md',
'msd-nyquist-auditor.md',
'msd-ui-auditor.md',
'msd-integration-checker.md',
'msd-doc-verifier.md',
];
function readAgent(agentsDir, filename) {
@@ -797,13 +797,13 @@ describe('enh-2427 — sycophancy hardening: audit-class agents', () => {
assert.ok(content.length > 0, `${filename} should not be empty`);
});
test('(1) third-person framing — <role> does not open with "You are a GSD"', () => {
test('(1) third-person framing — <role> does not open with "You are a MSD"', () => {
content = content || readAgent(AGENTS_DIR, filename);
role = role || extractRole(content);
const firstSentence = role.trim().slice(0, 80);
assert.ok(
!firstSentence.startsWith('You are a GSD'),
`${filename}: <role> must not open with "You are a GSD" — use third-person submission framing. Got: "${firstSentence}"`
!firstSentence.startsWith('You are a MSD'),
`${filename}: <role> must not open with "You are a MSD" — use third-person submission framing. Got: "${firstSentence}"`
);
});
@@ -856,7 +856,7 @@ describe('enh-2427 — sycophancy hardening: audit-class agents', () => {
/**
* Regression tests for bug #571
*
* gsd-doc-writer in fix mode used the Write tool (whole-file replace) instead
* msd-doc-writer in fix mode used the Write tool (whole-file replace) instead
* of the Edit tool (surgical replacement) when correcting specific failing
* claims. When the target doc was generated but not yet committed, Write could
* truncate the file to a single line with no git recovery path.
@@ -879,23 +879,23 @@ const fs = require('fs');
const path = require('path');
const AGENTS_DIR = path.join(__dirname, '..', 'agents');
const WORKFLOWS_DIR = path.join(__dirname, '..', 'gsd-core', 'workflows');
const WORKFLOWS_DIR = path.join(__dirname, '..', 'msd-core', 'workflows');
const AGENT_PATH = path.join(AGENTS_DIR, 'gsd-doc-writer.md');
const AGENT_PATH = path.join(AGENTS_DIR, 'msd-doc-writer.md');
const WORKFLOW_PATH = path.join(WORKFLOWS_DIR, 'docs-update.md');
// ─── Agent fix: Edit in tools frontmatter ────────────────────────────────────
describe('bug #571: gsd-doc-writer agent', () => {
describe('bug #571: msd-doc-writer agent', () => {
const content = fs.readFileSync(AGENT_PATH, 'utf-8');
test('agent file exists', () => {
assert.ok(fs.existsSync(AGENT_PATH), 'agents/gsd-doc-writer.md must exist');
assert.ok(fs.existsSync(AGENT_PATH), 'agents/msd-doc-writer.md must exist');
});
test('tools frontmatter includes Edit', () => {
const toolsMatch = content.match(/^tools:\s*(.+)$/m);
assert.ok(toolsMatch, 'gsd-doc-writer.md must have a tools: frontmatter line');
assert.ok(toolsMatch, 'msd-doc-writer.md must have a tools: frontmatter line');
assert.ok(
toolsMatch[1].includes('Edit'),
'tools: frontmatter must include Edit so fix mode can make surgical replacements (#571)'
@@ -965,7 +965,7 @@ describe('bug #571: docs-update workflow fix_loop', () => {
const content = fs.readFileSync(WORKFLOW_PATH, 'utf-8');
test('workflow file exists', () => {
assert.ok(fs.existsSync(WORKFLOW_PATH), 'gsd-core/workflows/docs-update.md must exist');
assert.ok(fs.existsSync(WORKFLOW_PATH), 'msd-core/workflows/docs-update.md must exist');
});
describe('fix_loop step', () => {
@@ -1077,15 +1077,15 @@ const REPO_ROOT = path.resolve(__dirname, '..');
// same truncation-resilient write contract added for bug #214. OpenCode shares
// OUTPUT_TOKEN_MAX=32000 with the thinking budget (upstream opencode#18108), so
// an oversized single `write` tool call is truncated mid-payload, yielding
// `JSON Parse error: Expected '}'`, and OpenCode then doom-loops. gsd-phase-researcher
// `JSON Parse error: Expected '}'`, and OpenCode then doom-loops. msd-phase-researcher
// is locked by its own bug-214 test; this locks the other large-file writers.
const WRITER_AGENTS = [
'gsd-research-synthesizer',
'gsd-planner',
'gsd-executor',
'gsd-domain-researcher',
'gsd-project-researcher',
'gsd-ui-researcher',
'msd-research-synthesizer',
'msd-planner',
'msd-executor',
'msd-domain-researcher',
'msd-project-researcher',
'msd-ui-researcher',
];
function readAgent(name) {
@@ -1120,7 +1120,7 @@ describe('bug #214: large-file writer agents must survive write-tool truncation'
test('defines the continuation sentinel', () => {
assert.match(
prompt,
/<!-- gsd:write-continue -->/,
/<!-- msd:write-continue -->/,
`${name}: must define the continuation sentinel for incremental writes.`
);
});
@@ -1178,7 +1178,7 @@ const fs = require('node:fs');
const path = require('node:path');
const REPO_ROOT = path.resolve(__dirname, '..');
const RESEARCHER_PATH = path.join(REPO_ROOT, 'agents', 'gsd-phase-researcher.md');
const RESEARCHER_PATH = path.join(REPO_ROOT, 'agents', 'msd-phase-researcher.md');
function readResearcherPrompt() {
return fs.readFileSync(RESEARCHER_PATH, 'utf8');
@@ -1200,7 +1200,7 @@ describe('bug #214: phase researcher must survive OpenCode write-tool truncation
);
assert.match(
prompt,
/<!-- gsd:write-continue -->/,
/<!-- msd:write-continue -->/,
'Step 6 must define the continuation sentinel for incremental writes.'
);
assert.match(
@@ -1243,7 +1243,7 @@ describe('bug #214: phase researcher must survive OpenCode write-tool truncation
'use strict';
// allow-test-rule: source-text-is-the-product (see #2990)
// agents/gsd-code-fixer.md is the deployed agent definition the runtime
// agents/msd-code-fixer.md is the deployed agent definition the runtime
// loads. Parsing its bash code blocks into structured invocation records
// (extractCleanupGitInvocations + the recovery-block parsers below) IS
// testing the runtime contract — what command sequence the agent
@@ -1251,17 +1251,17 @@ describe('bug #214: phase researcher must survive OpenCode write-tool truncation
// fields from a known-shape product file, then assertions go against
// those typed fields, not against the raw markdown text.
// Consolidation #1969: scope GSD_TEST_MODE to this folded block so it does not
// Consolidation #1969: scope MSD_TEST_MODE to this folded block so it does not
// leak into sibling folded suites in the shared file (was process-isolated when
// standalone). This unit suite only parses agent markdown, but keep the flag set
// for its own duration to preserve the origin behaviour, and restore it after.
const { before: __gtmBefore, after: __gtmAfter } = require('node:test');
const __savedGsdTestMode = process.env.GSD_TEST_MODE;
__gtmBefore(() => { process.env.GSD_TEST_MODE = '1'; });
__gtmAfter(() => { if (__savedGsdTestMode === undefined) delete process.env.GSD_TEST_MODE; else process.env.GSD_TEST_MODE = __savedGsdTestMode; });
const __savedMsdTestMode = process.env.MSD_TEST_MODE;
__gtmBefore(() => { process.env.MSD_TEST_MODE = '1'; });
__gtmAfter(() => { if (__savedMsdTestMode === undefined) delete process.env.MSD_TEST_MODE; else process.env.MSD_TEST_MODE = __savedMsdTestMode; });
/**
* Bug #2990: gsd-code-fixer worktree setup fails when current branch
* Bug #2990: msd-code-fixer worktree setup fails when current branch
* is already checked out in the main repo.
*
* The original agent definition called `git worktree add "$wt" "$branch"`,
@@ -1269,7 +1269,7 @@ __gtmAfter(() => { if (__savedGsdTestMode === undefined) delete process.env.GSD_
* to check out the same branch in two worktrees by default, so the setup
* failed before the agent could do any work.
*
* Fix: create a NEW branch `gsd-reviewfix/${padded_phase}-$$` and attach
* Fix: create a NEW branch `msd-reviewfix/${padded_phase}-$$` and attach
* the worktree to it via `git worktree add -b "$reviewfix_branch" "$wt"
* "$branch"`. The cleanup tail then fast-forwards `$branch` to
* `$reviewfix_branch` so the user's branch captures the agent's commits.
@@ -1280,7 +1280,7 @@ const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const AGENT_PATH = path.join(__dirname, '..', 'agents', 'gsd-code-fixer.md');
const AGENT_PATH = path.join(__dirname, '..', 'agents', 'msd-code-fixer.md');
function parseWorktreeAddInvocations(markdown) {
// Pull `git worktree add ...` calls and classify each into structured
@@ -1310,13 +1310,13 @@ function parseWorktreeAddInvocations(markdown) {
return invocations;
}
describe('Bug #2990: gsd-code-fixer worktree attaches to a NEW branch, not the user-checked-out one', () => {
describe('Bug #2990: msd-code-fixer worktree attaches to a NEW branch, not the user-checked-out one', () => {
const md = fs.readFileSync(AGENT_PATH, 'utf-8');
const invocations = parseWorktreeAddInvocations(md);
test('sanity: at least one git-worktree-add invocation exists in the agent definition', () => {
assert.ok(invocations.length > 0,
'expected gsd-code-fixer.md to document at least one git worktree add invocation');
'expected msd-code-fixer.md to document at least one git worktree add invocation');
});
test('every git-worktree-add invocation uses -b $reviewfix_branch (not bare $branch)', () => {
@@ -1336,7 +1336,7 @@ describe('Bug #2990: gsd-code-fixer worktree attaches to a NEW branch, not the u
});
/**
* Bug #2647: gsd-code-fixer hand-rolled its worktree at a hardcoded `/tmp/sv-...`
* Bug #2647: msd-code-fixer hand-rolled its worktree at a hardcoded `/tmp/sv-...`
* mktemp path, which on Windows/Git Bash landed OUTSIDE the project tree —
* outside the agent session's permission allowlist, so every Read inside the
* worktree prompted — and mktemp's MAX_PATH-avoidance substitute produced an
@@ -1350,7 +1350,7 @@ describe('Bug #2990: gsd-code-fixer worktree attaches to a NEW branch, not the u
* and assert the path is repo-relative under `.claude/worktrees/`, never an
* absolute `/tmp` path, and concurrency-unique.
*/
describe('Bug #2647: gsd-code-fixer worktree path is repo-relative, not a hardcoded /tmp path', () => {
describe('Bug #2647: msd-code-fixer worktree path is repo-relative, not a hardcoded /tmp path', () => {
const md = fs.readFileSync(AGENT_PATH, 'utf-8');
// Extract `wt=...` shell assignments from fenced bash blocks (skip inline
@@ -1381,7 +1381,7 @@ describe('Bug #2647: gsd-code-fixer worktree path is repo-relative, not a hardco
test('the worktree path is NOT an absolute /tmp path', () => {
const wtAssigns = parseWtAssignments(md);
assert.ok(wtAssigns.length > 0, 'expected at least one wt= assignment in gsd-code-fixer.md');
assert.ok(wtAssigns.length > 0, 'expected at least one wt= assignment in msd-code-fixer.md');
const tmpViolations = wtAssigns.filter(v => /\/tmp\/sv-|mktemp -d "\/tmp\//.test(v));
assert.deepEqual(
tmpViolations,
@@ -1392,7 +1392,7 @@ describe('Bug #2647: gsd-code-fixer worktree path is repo-relative, not a hardco
test('the worktree path is repo-relative under .claude/worktrees/', () => {
const wtAssigns = parseWtAssignments(md);
assert.ok(wtAssigns.length > 0, 'expected at least one wt= assignment in gsd-code-fixer.md');
assert.ok(wtAssigns.length > 0, 'expected at least one wt= assignment in msd-code-fixer.md');
const nonRelative = wtAssigns.filter(v => !v.includes('.claude/worktrees/rf-'));
assert.deepEqual(
nonRelative,
@@ -1403,7 +1403,7 @@ describe('Bug #2647: gsd-code-fixer worktree path is repo-relative, not a hardco
test('the worktree path is concurrency-unique (PID + epoch suffix)', () => {
const wtAssigns = parseWtAssignments(md);
assert.ok(wtAssigns.length > 0, 'expected at least one wt= assignment in gsd-code-fixer.md');
assert.ok(wtAssigns.length > 0, 'expected at least one wt= assignment in msd-code-fixer.md');
// The path must carry BOTH a PID (`$$`) AND a time component (`$(date +%s)`)
// so concurrent runs for the same phase do not collide — the property
// mktemp's XXXXXX provided before #2647. Requiring both (not either) is
@@ -1470,7 +1470,7 @@ describe('Bug #2990: cleanup tail fast-forwards $branch and deletes the temp bra
const records = extractCleanupGitInvocations(md);
test('cleanup tail bash block exists and is parseable', () => {
assert.notEqual(records, null, 'expected to find a "Cleanup tail" bash block in agents/gsd-code-fixer.md');
assert.notEqual(records, null, 'expected to find a "Cleanup tail" bash block in agents/msd-code-fixer.md');
assert.ok(records.length > 0, 'expected at least one git invocation in the cleanup tail');
});
@@ -1552,7 +1552,7 @@ describe('Bug #2990 (#3001 CR): recovery code reads reviewfix_branch from sentin
/**
* Regression test for bug #2686
*
* The gsd-code-fixer agent (spawned by /gsd-code-review-fix) operated directly
* The msd-code-fixer agent (spawned by /msd-code-review-fix) operated directly
* against the main working tree. When it ran concurrently with a foreground
* session both processes raced for HEAD, the index, and on-disk files. The
* foreground session's next commit could land on the wrong branch (whichever
@@ -1562,14 +1562,14 @@ describe('Bug #2990 (#3001 CR): recovery code reads reviewfix_branch from sentin
* FIRST git operation, run ALL subsequent git operations inside that worktree
* path, and call `git worktree remove` for cleanup when done.
*
* This mirrors the pattern already used by every other per-issue GSD agent at
* This mirrors the pattern already used by every other per-issue MSD agent at
* /private/tmp/sv-<n>.
*/
'use strict';
// allow-test-rule: source-text-is-the-product (see #2686)
// The gsd-code-fixer agent's working instructions ARE the product — Claude
// The msd-code-fixer agent's working instructions ARE the product — Claude
// executes them literally at runtime. Testing the text content tests the
// deployed contract: if the instruction is absent, the isolation guarantee
// is absent.
@@ -1583,8 +1583,8 @@ describe('bug-2686: review-fix agent worktree isolation', () => {
let agentContent;
before(() => {
const agentPath = path.join(__dirname, '..', 'agents', 'gsd-code-fixer.md');
assert.ok(fs.existsSync(agentPath), 'agents/gsd-code-fixer.md must exist');
const agentPath = path.join(__dirname, '..', 'agents', 'msd-code-fixer.md');
assert.ok(fs.existsSync(agentPath), 'agents/msd-code-fixer.md must exist');
agentContent = fs.readFileSync(agentPath, 'utf-8');
});
@@ -1593,7 +1593,7 @@ describe('bug-2686: review-fix agent worktree isolation', () => {
assert.ok(
worktreePos !== -1,
'gsd-code-fixer.md must include a "git worktree add" instruction to isolate operations from the main working tree (#2686)'
'msd-code-fixer.md must include a "git worktree add" instruction to isolate operations from the main working tree (#2686)'
);
// `git checkout -- {file}` is a file-restore within the worktree — safe, not a branch switch.
@@ -1609,8 +1609,8 @@ describe('bug-2686: review-fix agent worktree isolation', () => {
}
// commit command must come after worktree setup — the fixer may use
// either `git commit` directly or `gsd-sdk query commit`
const commitMatch = /(?:git commit|gsd-sdk query commit)/.exec(agentContent);
// either `git commit` directly or `msd-sdk query commit`
const commitMatch = /(?:git commit|msd-sdk query commit)/.exec(agentContent);
if (commitMatch) {
const commitPos = commitMatch.index;
assert.ok(
@@ -1623,13 +1623,13 @@ describe('bug-2686: review-fix agent worktree isolation', () => {
test('agent instructions include worktree cleanup after completion', () => {
assert.ok(
agentContent.includes('git worktree remove') || agentContent.includes('worktree remove'),
'gsd-code-fixer.md must include worktree cleanup (git worktree remove) to avoid leaking tmp directories (#2686)'
'msd-code-fixer.md must include worktree cleanup (git worktree remove) to avoid leaking tmp directories (#2686)'
);
});
test('agent instructions use a repo-relative worktree path under .claude/worktrees/ (not a hardcoded /tmp path)', () => {
// #2647: the original #2686 fix placed the worktree at a hardcoded `/tmp/sv-`
// mktemp path to match sibling GSD agents. On Windows/Git Bash that landed
// mktemp path to match sibling MSD agents. On Windows/Git Bash that landed
// OUTSIDE the project tree — outside the agent session's permission allowlist,
// so every Read inside the worktree prompted — and mktemp's MAX_PATH-avoidance
// substitute produced an un-removable `C:/mvwtNN` path. The worktree must now
@@ -1641,11 +1641,11 @@ describe('bug-2686: review-fix agent worktree isolation', () => {
/\bwt\s*=\s*["']?\/tmp\/sv-/.test(agentContent);
assert.ok(
!hasTmpWorktreePath,
'gsd-code-fixer.md must NOT define a worktree variable at a /tmp/sv-... path (#2647 — use a repo-relative .claude/worktrees/ path instead)'
'msd-code-fixer.md must NOT define a worktree variable at a /tmp/sv-... path (#2647 — use a repo-relative .claude/worktrees/ path instead)'
);
assert.ok(
/\.claude\/worktrees\//.test(agentContent),
'gsd-code-fixer.md must define the worktree under .claude/worktrees/ (repo-relative, gitignored, inside the permission scope) (#2647)'
'msd-code-fixer.md must define the worktree under .claude/worktrees/ (repo-relative, gitignored, inside the permission scope) (#2647)'
);
});
});
@@ -1660,11 +1660,11 @@ describe('bug-2686: review-fix agent worktree isolation', () => {
const { describe: __foldDescribe } = require('node:test');
__foldDescribe("folded:enh-2500-codebase-mapper-arch-rich-format (consolidation epic #1969 B7 #1976)", () => {
/**
* Enhancement #2500: gsd-codebase-mapper (arch focus) rich architecture output
* Enhancement #2500: msd-codebase-mapper (arch focus) rich architecture output
*
* The codebase/ARCHITECTURE.md produced by gsd-codebase-mapper was a sparse
* The codebase/ARCHITECTURE.md produced by msd-codebase-mapper was a sparse
* structural inventory — file listings and module relationships. After a major
* refactor, research/ARCHITECTURE.md (created at /gsd-new-project) goes stale
* refactor, research/ARCHITECTURE.md (created at /msd-new-project) goes stale
* with no refresh command. This enhancement enriches the codebase mapper's
* arch-focus template to match the richness of the research version:
* - ASCII system overview diagram
@@ -1681,7 +1681,7 @@ describe('bug-2686: review-fix agent worktree isolation', () => {
'use strict';
// allow-test-rule: source-text-is-the-product (see #2500)
// The gsd-codebase-mapper ARCHITECTURE.md template is the instruction set
// The msd-codebase-mapper ARCHITECTURE.md template is the instruction set
// executed by the LLM at runtime. Testing its text content tests whether the
// deployed agent will produce rich architecture docs as required by #2500.
@@ -1690,14 +1690,14 @@ const assert = require('node:assert/strict');
const fs = require('fs');
const path = require('path');
const AGENT_PATH = path.join(__dirname, '..', 'agents', 'gsd-codebase-mapper.md');
const AGENT_PATH = path.join(__dirname, '..', 'agents', 'msd-codebase-mapper.md');
describe('enh-2500: gsd-codebase-mapper arch focus — rich architecture output', () => {
describe('enh-2500: msd-codebase-mapper arch focus — rich architecture output', () => {
let agentContent;
let archTemplate;
before(() => {
assert.ok(fs.existsSync(AGENT_PATH), 'agents/gsd-codebase-mapper.md must exist');
assert.ok(fs.existsSync(AGENT_PATH), 'agents/msd-codebase-mapper.md must exist');
agentContent = fs.readFileSync(AGENT_PATH, 'utf-8');
// Isolate the ARCHITECTURE.md template section from the agent file.