Files
msd-core/tests/surface-empty-manifest-agents.test.cjs
Jakub Zych a9a7a328e6 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.
2026-10-06 01:47:40 +02:00

69 lines
3.4 KiB
JavaScript

'use strict';
// Regression guard for #2018 — applySurface over an empty/unresolvable manifest
// silently deleted every msd-* agent because the agent-prune loop in _syncMsdDir
// didn't check manifest membership (unlike pruneSkillDirs which conservatively
// preserves everything when the manifest is empty). The fix skips the agent-prune
// loop when the manifest is empty/absent so agents are never bulk-deleted.
const { describe, test, beforeEach, afterEach } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('fs');
const os = require('os');
const path = require('path');
const { _syncMsdDir } = require('../msd-core/bin/lib/surface.cjs');
const { cleanup } = require('./helpers.cjs');
function tmp() { return fs.mkdtempSync(path.join(os.tmpdir(), 'msd-surface-empty-')); }
describe('#2018 — empty manifest must not delete msd-* agents', () => {
let dest, staged;
beforeEach(() => {
dest = tmp(); staged = tmp();
// Simulate existing agents in dest (what a real install has)
for (const name of ['msd-executor.md', 'msd-planner.md', 'msd-verifier.md']) {
fs.writeFileSync(path.join(dest, name), `# ${name}\n`);
}
// Staged dir is EMPTY (unresolvable manifest → nothing staged)
});
afterEach(() => { cleanup(dest); cleanup(staged); });
test('empty manifest → agents preserved (not deleted)', () => {
_syncMsdDir(staged, dest, 'agents', new Map());
const remaining = fs.readdirSync(dest).filter((f) => f.startsWith('msd-'));
assert.deepEqual(remaining.sort(), ['msd-executor.md', 'msd-planner.md', 'msd-verifier.md'],
'all msd-* agents must survive an empty manifest');
});
test('undefined manifest → agents preserved', () => {
_syncMsdDir(staged, dest, 'agents', undefined);
const remaining = fs.readdirSync(dest).filter((f) => f.startsWith('msd-'));
assert.ok(remaining.length === 3, 'all agents preserved with undefined manifest');
});
test('non-empty manifest + empty staged → agents still preserved (staged set empty but manifest exists)', () => {
// A populated manifest (even with a different set) is trusted; but staged is empty.
// The manifest gate only fires on EMPTY manifest. With a non-empty manifest but
// empty staged, the prune loop SHOULD run (agents not in staged get pruned).
// This test documents the boundary: non-empty manifest → prune happens.
const manifest = new Map([['msd-old-agent', []]]);
_syncMsdDir(staged, dest, 'agents', manifest);
const remaining = fs.readdirSync(dest).filter((f) => f.startsWith('msd-'));
// With a non-empty manifest, staged is empty → all agents pruned (this is the
// existing behavior for a genuine "all agents were un-surfaced" scenario, NOT
// the empty-manifest bug).
assert.deepEqual(remaining, [], 'non-empty manifest + empty staged → agents pruned (expected behavior)');
});
test('empty manifest but new agents staged → new agents added, existing preserved', () => {
// Stage a new agent
fs.writeFileSync(path.join(staged, 'msd-new-agent.md'), '# New\n');
_syncMsdDir(staged, dest, 'agents', new Map());
const remaining = fs.readdirSync(dest).filter((f) => f.startsWith('msd-'));
assert.ok(remaining.includes('msd-new-agent.md'), 'new staged agent must be copied');
assert.ok(remaining.includes('msd-executor.md'), 'existing agent must be preserved');
assert.ok(remaining.length === 4, '3 existing + 1 new = 4 agents');
});
});