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:
@@ -6,8 +6,8 @@
|
||||
* #2874 (epic #2866 Phase 5), governed by ADR-58
|
||||
* (docs/adr/58-runtime-install-policy-module.md).
|
||||
*
|
||||
* Design: .gsd/phase/feat-2874-executed-plan-return/40-design.md
|
||||
* Test matrix: .gsd/phase/feat-2874-executed-plan-return/50-test-matrix.md
|
||||
* Design: .msd/phase/feat-2874-executed-plan-return/40-design.md
|
||||
* Test matrix: .msd/phase/feat-2874-executed-plan-return/50-test-matrix.md
|
||||
*
|
||||
* This file implements the Red-first order's rows 1-3 from 50-test-matrix.md:
|
||||
* - E3 (section E, "Executed-plan return shape"): the opencode-family
|
||||
@@ -24,7 +24,7 @@
|
||||
* — this package is tests only.
|
||||
*/
|
||||
|
||||
process.env.GSD_TEST_MODE = '1';
|
||||
process.env.MSD_TEST_MODE = '1';
|
||||
|
||||
const { test, describe } = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
@@ -37,16 +37,16 @@ const fc = require('fast-check');
|
||||
|
||||
const { createTempDir, cleanup } = require('./helpers.cjs');
|
||||
|
||||
const { installRuntimeArtifacts, hasExistingSymlinkBetween } = require('../gsd-core/bin/lib/install-engine.cjs');
|
||||
const registry = require('../gsd-core/bin/lib/capability-registry.cjs');
|
||||
const { loadSkillsManifest, resolveProfile } = require('../gsd-core/bin/lib/install-profiles.cjs');
|
||||
const runtimeArtifactLayout = require('../gsd-core/bin/lib/runtime-artifact-layout.cjs');
|
||||
const runtimeArtifactInstallPlan = require('../gsd-core/bin/lib/runtime-artifact-install-plan.cjs');
|
||||
const { withInstallFs } = require('../gsd-core/bin/lib/install-fs-adapter.cjs');
|
||||
const commandRoster = require('../gsd-core/bin/lib/command-roster.cjs');
|
||||
const { installRuntimeArtifacts, hasExistingSymlinkBetween } = require('../msd-core/bin/lib/install-engine.cjs');
|
||||
const registry = require('../msd-core/bin/lib/capability-registry.cjs');
|
||||
const { loadSkillsManifest, resolveProfile } = require('../msd-core/bin/lib/install-profiles.cjs');
|
||||
const runtimeArtifactLayout = require('../msd-core/bin/lib/runtime-artifact-layout.cjs');
|
||||
const runtimeArtifactInstallPlan = require('../msd-core/bin/lib/runtime-artifact-install-plan.cjs');
|
||||
const { withInstallFs } = require('../msd-core/bin/lib/install-fs-adapter.cjs');
|
||||
const commandRoster = require('../msd-core/bin/lib/command-roster.cjs');
|
||||
const slashCommandTransformer = require('../scripts/fix-slash-commands.cjs');
|
||||
|
||||
const REAL_COMMANDS_DIR = path.join(__dirname, '..', 'commands', 'gsd');
|
||||
const REAL_COMMANDS_DIR = path.join(__dirname, '..', 'commands', 'msd');
|
||||
const REAL_PACKAGE_ROOT = path.resolve(__dirname, '..');
|
||||
const MANIFEST = loadSkillsManifest(REAL_COMMANDS_DIR);
|
||||
const RESOLVED_CORE = resolveProfile({ modes: ['core'], manifest: MANIFEST });
|
||||
@@ -68,7 +68,7 @@ const { sandboxHome } = require('./helpers.cjs');
|
||||
|
||||
describe('installRuntimeArtifacts — E3: opencode-family early return', () => {
|
||||
test('family install still returns a plan', (t) => {
|
||||
const configDir = createTempDir('gsd-e3-opencode-');
|
||||
const configDir = createTempDir('msd-e3-opencode-');
|
||||
t.after(() => cleanup(configDir));
|
||||
sandboxHome(t, configDir);
|
||||
|
||||
@@ -95,7 +95,7 @@ describe('installRuntimeArtifacts — E13: no runtime returns undefined', () =>
|
||||
|
||||
for (const runtime of RUNTIMES) {
|
||||
test(`${runtime}: installRuntimeArtifacts does not return undefined`, (t) => {
|
||||
const configDir = createTempDir(`gsd-e13-${runtime}-`);
|
||||
const configDir = createTempDir(`msd-e13-${runtime}-`);
|
||||
t.after(() => cleanup(configDir));
|
||||
sandboxHome(t, configDir);
|
||||
|
||||
@@ -117,7 +117,7 @@ describe('installRuntimeArtifacts — E13: no runtime returns undefined', () =>
|
||||
// The full write+read surface installRuntimeArtifacts's call tree is known to
|
||||
// reach once every gap named in the #2874 follow-up round is closed: the
|
||||
// direct mkdirSync/existsSync/rmSync calls, _copyStaged's readdirSync/cpSync/
|
||||
// copyFileSync/mkdirSync, _removeGsdEntries's directory scan+delete,
|
||||
// copyFileSync/mkdirSync, _removeMsdEntries's directory scan+delete,
|
||||
// _snapshotDir/_restoreDir's read/write of preserved skill dirs, the
|
||||
// symlink-escape guard's lstatSync/realpathSync probes, commonjs-marker.cts's
|
||||
// lstatSync/writeFileSync/unlinkSync, and installer-migrations.cts's
|
||||
@@ -136,8 +136,8 @@ describe('installRuntimeArtifacts — E13: no runtime returns undefined', () =>
|
||||
// never reaches this poison. If this ever fires, `current`'s identity check
|
||||
// broke, not a documented gap.
|
||||
//
|
||||
// One exception this poison list does NOT cover: readGsdCommandNames
|
||||
// (command-roster.cts) reads the PACKAGE'S OWN commands/gsd/ source tree via
|
||||
// One exception this poison list does NOT cover: readMsdCommandNames
|
||||
// (command-roster.cts) reads the PACKAGE'S OWN commands/msd/ source tree via
|
||||
// real fs.readdirSync — deliberately unrouted (see install-fs-adapter.cts's
|
||||
// module doc, "DELIBERATELY NOT ROUTED"). `poisonRealFsAgainstDestination`
|
||||
// below allows real calls scoped to that known package-source root and
|
||||
@@ -151,7 +151,7 @@ const REAL_FS_WRITE_SURFACE = [
|
||||
];
|
||||
|
||||
// The provider resolver probes upward through the executing package before it
|
||||
// reaches commands/gsd and agents. All read-only package-tree probes are valid
|
||||
// reaches commands/msd and agents. All read-only package-tree probes are valid
|
||||
// real IO while a fake DESTINATION adapter is active; destination paths remain
|
||||
// poisoned below.
|
||||
const PACKAGE_SOURCE_ROOTS = [path.resolve(__dirname, '..')];
|
||||
@@ -167,9 +167,9 @@ function isPackageSourcePath(resolvedPath) {
|
||||
* limits"/install-fs-adapter.cts's module doc) rather than aligned with it by
|
||||
* coincidence: a real fs call against the install DESTINATION is a failure —
|
||||
* the seam exists precisely so a fake adapter can intercept those — but a
|
||||
* real call against the package's OWN source tree (commands/gsd/, agents/)
|
||||
* real call against the package's OWN source tree (commands/msd/, agents/)
|
||||
* is expected and allowed, because that read is deliberately unrouted
|
||||
* (readGsdCommandNames et al.). Poisoning every real fs method wholesale,
|
||||
* (readMsdCommandNames et al.). Poisoning every real fs method wholesale,
|
||||
* regardless of path, makes a correct install fail for the wrong reason.
|
||||
*
|
||||
* Returns a Map<method, count> of package-source hits, so a caller can
|
||||
@@ -358,7 +358,7 @@ function createFakeInstallFs(seed = []) {
|
||||
// itself rather than assuming it.
|
||||
describe('createFakeInstallFs — wx exclusive-create emulation', () => {
|
||||
test('refuses an exclusive create against an existing path (EEXIST)', () => {
|
||||
const target = path.join(os.tmpdir(), 'gsd-fake-wx-existing.txt');
|
||||
const target = path.join(os.tmpdir(), 'msd-fake-wx-existing.txt');
|
||||
const fakeFs = createFakeInstallFs([[target, { type: 'file', content: 'original' }]]);
|
||||
|
||||
assert.throws(
|
||||
@@ -374,7 +374,7 @@ describe('createFakeInstallFs — wx exclusive-create emulation', () => {
|
||||
});
|
||||
|
||||
test('allows an exclusive create against an absent path', () => {
|
||||
const target = path.join(os.tmpdir(), 'gsd-fake-wx-absent.txt');
|
||||
const target = path.join(os.tmpdir(), 'msd-fake-wx-absent.txt');
|
||||
const fakeFs = createFakeInstallFs();
|
||||
|
||||
fakeFs.writeFileSync(target, 'created', { flag: 'wx' });
|
||||
@@ -390,16 +390,16 @@ function sha256Hex(content) {
|
||||
}
|
||||
|
||||
function fakeInstalledCorpusSeed(configDir) {
|
||||
const coreDir = path.join(configDir, 'gsd-core');
|
||||
const coreDir = path.join(configDir, 'msd-core');
|
||||
const commandsDir = path.join(coreDir, 'commands');
|
||||
const commandsGsdDir = path.join(commandsDir, 'gsd');
|
||||
const commandsMsdDir = path.join(commandsDir, 'msd');
|
||||
const agentsDir = path.join(coreDir, 'agents');
|
||||
return [
|
||||
[configDir, { type: 'dir' }],
|
||||
[coreDir, { type: 'dir' }],
|
||||
[commandsDir, { type: 'dir' }],
|
||||
[commandsGsdDir, { type: 'dir' }],
|
||||
[path.join(commandsGsdDir, 'fixture.md'), { type: 'file', content: '# Fixture command\n' }],
|
||||
[commandsMsdDir, { type: 'dir' }],
|
||||
[path.join(commandsMsdDir, 'fixture.md'), { type: 'file', content: '# Fixture command\n' }],
|
||||
[agentsDir, { type: 'dir' }],
|
||||
[path.join(agentsDir, 'fixture-agent.md'), { type: 'file', content: '# Fixture agent\n' }],
|
||||
];
|
||||
@@ -407,8 +407,8 @@ function fakeInstalledCorpusSeed(configDir) {
|
||||
|
||||
function fakeInstalledCorpusManifestFiles() {
|
||||
return {
|
||||
'gsd-core/commands/gsd/fixture.md': sha256Hex('# Fixture command\n'),
|
||||
'gsd-core/agents/fixture-agent.md': sha256Hex('# Fixture agent\n'),
|
||||
'msd-core/commands/msd/fixture.md': sha256Hex('# Fixture command\n'),
|
||||
'msd-core/agents/fixture-agent.md': sha256Hex('# Fixture agent\n'),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -421,7 +421,7 @@ describe('installRuntimeArtifacts — F2: fake-adapter install touches no real f
|
||||
// try/finally inside test bodies").
|
||||
const packageSourceHits = poisonRealFsAgainstDestination(t, '');
|
||||
|
||||
const configDir = path.join(os.tmpdir(), `gsd-f2-must-not-exist-${crypto.randomUUID()}`);
|
||||
const configDir = path.join(os.tmpdir(), `msd-f2-must-not-exist-${crypto.randomUUID()}`);
|
||||
const fakeFs = createFakeInstallFs(fakeInstalledCorpusSeed(configDir));
|
||||
|
||||
// configDir exists only in the fake store — F2 asserts nothing real ever
|
||||
@@ -446,7 +446,7 @@ describe('installRuntimeArtifacts — F2: fake-adapter install touches no real f
|
||||
// touch the poisoned surface.
|
||||
assert.ok(
|
||||
(packageSourceHits.get('readdirSync') ?? 0) > 0,
|
||||
'F2: readGsdCommandNames must have read the real, unrouted commands/gsd/ package-source ' +
|
||||
'F2: readMsdCommandNames must have read the real, unrouted commands/msd/ package-source ' +
|
||||
'tree at least once — proving the poison boundary was exercised, not merely tolerated',
|
||||
);
|
||||
});
|
||||
@@ -454,14 +454,14 @@ describe('installRuntimeArtifacts — F2: fake-adapter install touches no real f
|
||||
test('fake-adapter install touches no real filesystem (opencode-family legacy command/ dir migration)', (t) => {
|
||||
poisonRealFsAgainstDestination(t, ' (opencode legacy migration)');
|
||||
|
||||
const configDir = path.join(os.tmpdir(), `gsd-f2-opencode-legacy-${crypto.randomUUID()}`);
|
||||
const configDir = path.join(os.tmpdir(), `msd-f2-opencode-legacy-${crypto.randomUUID()}`);
|
||||
const legacyDir = path.join(configDir, 'command');
|
||||
const legacyFile = path.join(legacyDir, 'gsd-old-cmd.md');
|
||||
const legacyFile = path.join(legacyDir, 'msd-old-cmd.md');
|
||||
const content = '# stale legacy command\n';
|
||||
const manifestPath = path.join(configDir, 'gsd-file-manifest.json');
|
||||
const manifestPath = path.join(configDir, 'msd-file-manifest.json');
|
||||
const manifestJson = JSON.stringify({ files: {
|
||||
...fakeInstalledCorpusManifestFiles(),
|
||||
'command/gsd-old-cmd.md': sha256Hex(content),
|
||||
'command/msd-old-cmd.md': sha256Hex(content),
|
||||
} });
|
||||
|
||||
const fakeFs = createFakeInstallFs([
|
||||
@@ -502,13 +502,13 @@ describe('installRuntimeArtifacts — F2: fake-adapter install touches no real f
|
||||
// the poisoned list (see install-fs-adapter.cts's module doc — it is
|
||||
// deliberately unrouted, real-fs-only, package-source introspection), so
|
||||
// resolving this here is safe even after poisoning existsSync et al.
|
||||
const commandsGsdDir = REAL_COMMANDS_DIR;
|
||||
const repoRoot = path.dirname(path.dirname(commandsGsdDir));
|
||||
const commandsMsdDir = REAL_COMMANDS_DIR;
|
||||
const repoRoot = path.dirname(path.dirname(commandsMsdDir));
|
||||
const nativePlugin = registry.runtimes.pi.runtime.hostBehaviors.nativePlugin;
|
||||
assert.ok(nativePlugin && nativePlugin.source, 'pi must declare hostBehaviors.nativePlugin.source (registry drifted)');
|
||||
const pluginSrc = path.join(repoRoot, nativePlugin.source);
|
||||
|
||||
const configDir = path.join(os.tmpdir(), `gsd-f2-pi-nativeplugin-${crypto.randomUUID()}`);
|
||||
const configDir = path.join(os.tmpdir(), `msd-f2-pi-nativeplugin-${crypto.randomUUID()}`);
|
||||
const fakeFs = createFakeInstallFs([
|
||||
[pluginSrc, { type: 'file', content: '// fake plugin adapter\n' }],
|
||||
]);
|
||||
@@ -541,13 +541,13 @@ describe('installRuntimeArtifacts — F2: fake-adapter install touches no real f
|
||||
assert.ok(Array.isArray(retired) && retired.length > 0, 'cursor must declare hostBehaviors.retiredArtifacts (registry drifted)');
|
||||
const { destSubpath, prefix, suffix } = retired[0];
|
||||
|
||||
const configDir = path.join(os.tmpdir(), `gsd-f2-cursor-retired-${crypto.randomUUID()}`);
|
||||
const configDir = path.join(os.tmpdir(), `msd-f2-cursor-retired-${crypto.randomUUID()}`);
|
||||
const destDir = path.resolve(configDir, destSubpath);
|
||||
const staleName = `${prefix}retired-probe${suffix}`;
|
||||
const staleFile = path.join(destDir, staleName);
|
||||
const content = '# stale retired artifact\n';
|
||||
const relPath = `${destSubpath.replace(/\\/g, '/')}/${staleName}`;
|
||||
const manifestPath = path.join(configDir, 'gsd-file-manifest.json');
|
||||
const manifestPath = path.join(configDir, 'msd-file-manifest.json');
|
||||
const manifestJson = JSON.stringify({ files: {
|
||||
...fakeInstalledCorpusManifestFiles(),
|
||||
[relPath]: sha256Hex(content),
|
||||
@@ -576,7 +576,7 @@ describe('installRuntimeArtifacts — F2: fake-adapter install touches no real f
|
||||
);
|
||||
assert.ok(
|
||||
(packageSourceHits.get('readdirSync') ?? 0) > 0,
|
||||
'F2 (retiredArtifacts): readGsdCommandNames must have read the real, unrouted commands/gsd/ ' +
|
||||
'F2 (retiredArtifacts): readMsdCommandNames must have read the real, unrouted commands/msd/ ' +
|
||||
'package-source tree at least once — proving the poison boundary was exercised, not merely tolerated',
|
||||
);
|
||||
});
|
||||
@@ -592,7 +592,7 @@ describe('installRuntimeArtifacts — F2: fake-adapter install touches no real f
|
||||
|
||||
describe('installRuntimeArtifacts — E1: claude global, normal install', () => {
|
||||
test('returns an executed plan for a normal install', (t) => {
|
||||
const configDir = createTempDir('gsd-e1-claude-global-');
|
||||
const configDir = createTempDir('msd-e1-claude-global-');
|
||||
t.after(() => cleanup(configDir));
|
||||
sandboxHome(t, configDir);
|
||||
|
||||
@@ -609,7 +609,7 @@ describe('installRuntimeArtifacts — E1: claude global, normal install', () =>
|
||||
|
||||
describe('installRuntimeArtifacts — E2: claude local', () => {
|
||||
test('executed plan records the scope', (t) => {
|
||||
const configDir = createTempDir('gsd-e2-claude-local-');
|
||||
const configDir = createTempDir('msd-e2-claude-local-');
|
||||
t.after(() => cleanup(configDir));
|
||||
sandboxHome(t, configDir);
|
||||
|
||||
@@ -621,7 +621,7 @@ describe('installRuntimeArtifacts — E2: claude local', () => {
|
||||
|
||||
describe('installRuntimeArtifacts — E4: kilo (second family member)', () => {
|
||||
test('kilo family install still returns a plan', (t) => {
|
||||
const configDir = createTempDir('gsd-e4-kilo-');
|
||||
const configDir = createTempDir('msd-e4-kilo-');
|
||||
t.after(() => cleanup(configDir));
|
||||
sandboxHome(t, configDir);
|
||||
|
||||
@@ -647,7 +647,7 @@ describe('installRuntimeArtifacts — E4: kilo (second family member)', () => {
|
||||
|
||||
describe('installRuntimeArtifacts — E5/E7: empty layout + nativePlugin post-step (pi)', () => {
|
||||
test('empty layout returns an empty plan', (t) => {
|
||||
const configDir = createTempDir('gsd-e5-pi-');
|
||||
const configDir = createTempDir('msd-e5-pi-');
|
||||
t.after(() => cleanup(configDir));
|
||||
sandboxHome(t, configDir);
|
||||
|
||||
@@ -658,7 +658,7 @@ describe('installRuntimeArtifacts — E5/E7: empty layout + nativePlugin post-st
|
||||
});
|
||||
|
||||
test('native plugin post-step is recorded', (t) => {
|
||||
const configDir = createTempDir('gsd-e7-pi-');
|
||||
const configDir = createTempDir('msd-e7-pi-');
|
||||
t.after(() => cleanup(configDir));
|
||||
sandboxHome(t, configDir);
|
||||
|
||||
@@ -673,7 +673,7 @@ describe('installRuntimeArtifacts — E5/E7: empty layout + nativePlugin post-st
|
||||
|
||||
describe('installRuntimeArtifacts — E6: hermes post-step is recorded', () => {
|
||||
test('hermes post-step is recorded', (t) => {
|
||||
const configDir = createTempDir('gsd-e6-hermes-');
|
||||
const configDir = createTempDir('msd-e6-hermes-');
|
||||
t.after(() => cleanup(configDir));
|
||||
sandboxHome(t, configDir);
|
||||
|
||||
@@ -688,10 +688,10 @@ describe('installRuntimeArtifacts — E6: hermes post-step is recorded', () => {
|
||||
|
||||
describe('installRuntimeArtifacts — E8: preserved user skill dirs are recorded', () => {
|
||||
test('preserved user skill dirs are recorded', (t) => {
|
||||
const configDir = createTempDir('gsd-e8-claude-');
|
||||
const configDir = createTempDir('msd-e8-claude-');
|
||||
t.after(() => cleanup(configDir));
|
||||
sandboxHome(t, configDir);
|
||||
const preservedSkillDir = path.join(configDir, 'skills', 'gsd-dev-preferences');
|
||||
const preservedSkillDir = path.join(configDir, 'skills', 'msd-dev-preferences');
|
||||
fs.mkdirSync(preservedSkillDir, { recursive: true });
|
||||
fs.writeFileSync(path.join(preservedSkillDir, 'SKILL.md'), '# my custom prefs\n');
|
||||
|
||||
@@ -700,8 +700,8 @@ describe('installRuntimeArtifacts — E8: preserved user skill dirs are recorded
|
||||
const skillsKind = result.kinds.find((k) => k.kind === 'skills');
|
||||
assert.ok(skillsKind, 'E8 precondition: claude global must write a skills kind');
|
||||
assert.deepStrictEqual(
|
||||
skillsKind.preserved, ['gsd-dev-preferences'],
|
||||
'E8: the plan must record gsd-dev-preferences as preserved',
|
||||
skillsKind.preserved, ['msd-dev-preferences'],
|
||||
'E8: the plan must record msd-dev-preferences as preserved',
|
||||
);
|
||||
assert.strictEqual(
|
||||
fs.readFileSync(path.join(preservedSkillDir, 'SKILL.md'), 'utf8'),
|
||||
@@ -713,7 +713,7 @@ describe('installRuntimeArtifacts — E8: preserved user skill dirs are recorded
|
||||
|
||||
describe('installRuntimeArtifacts — E9: non-skills kind records its writes', () => {
|
||||
test('non-skills kind records its writes', (t) => {
|
||||
const configDir = createTempDir('gsd-e9-claude-local-');
|
||||
const configDir = createTempDir('msd-e9-claude-local-');
|
||||
t.after(() => cleanup(configDir));
|
||||
sandboxHome(t, configDir);
|
||||
|
||||
@@ -731,7 +731,7 @@ describe('installRuntimeArtifacts — E9: non-skills kind records its writes', (
|
||||
|
||||
describe('installRuntimeArtifacts — E10: plan item naming a kind absent from layout.kinds', () => {
|
||||
test('unknown kind still throws', (t) => {
|
||||
const configDir = createTempDir('gsd-e10-claude-');
|
||||
const configDir = createTempDir('msd-e10-claude-');
|
||||
t.after(() => cleanup(configDir));
|
||||
sandboxHome(t, configDir);
|
||||
|
||||
@@ -763,7 +763,7 @@ describe('installRuntimeArtifacts — E10: plan item naming a kind absent from l
|
||||
|
||||
describe('installRuntimeArtifacts — E11: plan is not shared across calls', () => {
|
||||
test('plan is not shared across calls', (t) => {
|
||||
const configDir = createTempDir('gsd-e11-claude-');
|
||||
const configDir = createTempDir('msd-e11-claude-');
|
||||
t.after(() => cleanup(configDir));
|
||||
sandboxHome(t, configDir);
|
||||
|
||||
@@ -788,7 +788,7 @@ describe('installRuntimeArtifacts — E11: plan is not shared across calls', ()
|
||||
|
||||
describe('installRuntimeArtifacts — E12: executed plan key set is locked', () => {
|
||||
test('executed plan key set is locked', (t) => {
|
||||
const configDir = createTempDir('gsd-e12-claude-');
|
||||
const configDir = createTempDir('msd-e12-claude-');
|
||||
t.after(() => cleanup(configDir));
|
||||
sandboxHome(t, configDir);
|
||||
|
||||
@@ -846,14 +846,14 @@ function createRealDelegatingFs(overrides = {}) {
|
||||
|
||||
describe('installRuntimeArtifacts — F4: adapter errors propagate, cleanup still runs', () => {
|
||||
test('adapter errors propagate, cleanup still runs', (t) => {
|
||||
const configDir = createTempDir('gsd-f4-augment-');
|
||||
const configDir = createTempDir('msd-f4-augment-');
|
||||
t.after(() => cleanup(configDir));
|
||||
sandboxHome(t, configDir);
|
||||
|
||||
let capturedCleanupDir;
|
||||
const fakeFs = createRealDelegatingFs({
|
||||
writeFileSync: (p, data, opts) => {
|
||||
if (String(p).includes('gsd-cmd-rewrites-') && capturedCleanupDir === undefined) {
|
||||
if (String(p).includes('msd-cmd-rewrites-') && capturedCleanupDir === undefined) {
|
||||
capturedCleanupDir = path.dirname(p);
|
||||
}
|
||||
fs.writeFileSync(p, data, opts);
|
||||
@@ -880,7 +880,7 @@ describe('installRuntimeArtifacts — F4: adapter errors propagate, cleanup stil
|
||||
|
||||
describe('installRuntimeArtifacts — F5: fake existsSync drives the same branch', () => {
|
||||
test('fake existsSync drives the same branch', () => {
|
||||
const configDir = path.join(os.tmpdir(), `gsd-f5-must-not-exist-${crypto.randomUUID()}`);
|
||||
const configDir = path.join(os.tmpdir(), `msd-f5-must-not-exist-${crypto.randomUUID()}`);
|
||||
const skillsDest = path.join(configDir, 'skills');
|
||||
// Seed ONLY the skills destDir as a pre-existing (empty) directory in the
|
||||
// fake store — configDir is never created for real, so existsSync(dest)
|
||||
@@ -912,13 +912,13 @@ describe('installRuntimeArtifacts — F6: incomplete adapter fails loudly, never
|
||||
// user-artifact-staging.cts's `stageUserArtifacts` calling
|
||||
// `installFs().rmSync(entryDir)` unconditionally, where a test fake
|
||||
// missing `rmSync` would silently delete the real
|
||||
// `<configDir>/.gsd-staging/<key>` on disk. Falling through to real fs is
|
||||
// `<configDir>/.msd-staging/<key>` on disk. Falling through to real fs is
|
||||
// exactly how a fake-adapter test can end up performing real, uncontrolled
|
||||
// IO — the bug, not a feature. The guarded contract instead throws
|
||||
// immediately, naming the missing method, the moment the exercised path
|
||||
// reaches it: never a silent no-op AND never a silent real-fs write.
|
||||
test('incomplete adapter fails loudly (never silently skips the write)', () => {
|
||||
const configDir = path.join(os.tmpdir(), `gsd-f6-must-not-exist-${crypto.randomUUID()}`);
|
||||
const configDir = path.join(os.tmpdir(), `msd-f6-must-not-exist-${crypto.randomUUID()}`);
|
||||
|
||||
// Deliberately incomplete: only mkdirSync is implemented, to prove the
|
||||
// FIRST other method the call path reaches throws immediately instead of
|
||||
@@ -941,7 +941,7 @@ describe('installRuntimeArtifacts — F6: incomplete adapter fails loudly, never
|
||||
describe('installRuntimeArtifacts — G2: bin/install.js production call site unchanged', () => {
|
||||
test('installer call site unchanged', (t) => {
|
||||
const binInstall = require('../bin/install.js');
|
||||
const tmpDir = createTempDir('gsd-g2-');
|
||||
const tmpDir = createTempDir('msd-g2-');
|
||||
const previousCwd = process.cwd();
|
||||
process.chdir(tmpDir);
|
||||
t.after(() => { process.chdir(previousCwd); cleanup(tmpDir); });
|
||||
@@ -966,8 +966,8 @@ describe('installRuntimeArtifacts — G2: bin/install.js production call site un
|
||||
|
||||
describe('installRuntimeArtifacts — H1: symlink escape still refuses', () => {
|
||||
test('symlink escape still refuses', (t) => {
|
||||
const configDir = createTempDir('gsd-h1-');
|
||||
const outsideDir = createTempDir('gsd-h1-outside-');
|
||||
const configDir = createTempDir('msd-h1-');
|
||||
const outsideDir = createTempDir('msd-h1-outside-');
|
||||
t.after(() => { cleanup(configDir); cleanup(outsideDir); });
|
||||
sandboxHome(t, configDir);
|
||||
// Pre-create the skills destDir AS a symlink pointing outside configDir —
|
||||
@@ -976,7 +976,7 @@ describe('installRuntimeArtifacts — H1: symlink escape still refuses', () => {
|
||||
|
||||
assert.throws(
|
||||
() => installRuntimeArtifacts('claude', configDir, 'global', RESOLVED_CORE),
|
||||
/GSD_ALLOW_SYMLINKED_DEST/,
|
||||
/MSD_ALLOW_SYMLINKED_DEST/,
|
||||
'H1: a destDir that is itself a symlink pointing outside the install root must be refused',
|
||||
);
|
||||
});
|
||||
@@ -984,17 +984,17 @@ describe('installRuntimeArtifacts — H1: symlink escape still refuses', () => {
|
||||
|
||||
describe('installRuntimeArtifacts — H2: opt-in still follows', () => {
|
||||
test('opt-in still follows', (t) => {
|
||||
const configDir = createTempDir('gsd-h2-');
|
||||
const outsideDir = createTempDir('gsd-h2-outside-');
|
||||
const configDir = createTempDir('msd-h2-');
|
||||
const outsideDir = createTempDir('msd-h2-outside-');
|
||||
t.after(() => { cleanup(configDir); cleanup(outsideDir); });
|
||||
sandboxHome(t, configDir);
|
||||
fs.symlinkSync(outsideDir, path.join(configDir, 'skills'), 'dir');
|
||||
|
||||
const savedOptIn = process.env.GSD_ALLOW_SYMLINKED_DEST;
|
||||
process.env.GSD_ALLOW_SYMLINKED_DEST = '1';
|
||||
const savedOptIn = process.env.MSD_ALLOW_SYMLINKED_DEST;
|
||||
process.env.MSD_ALLOW_SYMLINKED_DEST = '1';
|
||||
t.after(() => {
|
||||
if (savedOptIn === undefined) delete process.env.GSD_ALLOW_SYMLINKED_DEST;
|
||||
else process.env.GSD_ALLOW_SYMLINKED_DEST = savedOptIn;
|
||||
if (savedOptIn === undefined) delete process.env.MSD_ALLOW_SYMLINKED_DEST;
|
||||
else process.env.MSD_ALLOW_SYMLINKED_DEST = savedOptIn;
|
||||
});
|
||||
|
||||
const result = installRuntimeArtifacts('claude', configDir, 'global', RESOLVED_CORE);
|
||||
@@ -1018,7 +1018,7 @@ describe('installRuntimeArtifacts — H3: fake adapter cannot bypass the symlink
|
||||
// invariant directly: even a fake adapter that lies "nothing exists,
|
||||
// nothing is a symlink" everywhere cannot make this refusal pass for an
|
||||
// escaping path, because this branch never asks the adapter anything.
|
||||
const root = path.join(os.tmpdir(), 'gsd-h3-fake-root');
|
||||
const root = path.join(os.tmpdir(), 'msd-h3-fake-root');
|
||||
const escapingPath = path.join(root, '..', '..', 'etc', 'passwd');
|
||||
const lyingFs = {
|
||||
existsSync: () => false,
|
||||
@@ -1063,7 +1063,7 @@ describe('installRuntimeArtifacts — H5: nul byte in dest is rejected', () => {
|
||||
|
||||
describe('installRuntimeArtifacts — I1: successful cleanup is recorded', () => {
|
||||
test('successful cleanup is recorded', (t) => {
|
||||
const configDir = createTempDir('gsd-i1-augment-');
|
||||
const configDir = createTempDir('msd-i1-augment-');
|
||||
t.after(() => cleanup(configDir));
|
||||
sandboxHome(t, configDir);
|
||||
|
||||
@@ -1080,13 +1080,13 @@ describe('installRuntimeArtifacts — I1: successful cleanup is recorded', () =>
|
||||
|
||||
describe('installRuntimeArtifacts — I2: failed cleanup is visible, not silent', () => {
|
||||
test('failed cleanup is visible, not silent', (t) => {
|
||||
const configDir = createTempDir('gsd-i2-augment-');
|
||||
const configDir = createTempDir('msd-i2-augment-');
|
||||
t.after(() => cleanup(configDir));
|
||||
sandboxHome(t, configDir);
|
||||
|
||||
const fakeFs = createRealDelegatingFs({
|
||||
rmSync: (p, opts) => {
|
||||
if (String(p).includes('gsd-cmd-rewrites-')) {
|
||||
if (String(p).includes('msd-cmd-rewrites-')) {
|
||||
throw new Error('I2: simulated cleanup failure');
|
||||
}
|
||||
// eslint-disable-next-line local/no-raw-rmsync-in-tests -- delegate, not test cleanup
|
||||
@@ -1107,7 +1107,7 @@ describe('installRuntimeArtifacts — I2: failed cleanup is visible, not silent'
|
||||
|
||||
describe('installRuntimeArtifacts — I3: no cleanup dirs is an empty array', () => {
|
||||
test('no cleanup dirs is an empty array', (t) => {
|
||||
const configDir = createTempDir('gsd-i3-claude-');
|
||||
const configDir = createTempDir('msd-i3-claude-');
|
||||
t.after(() => cleanup(configDir));
|
||||
sandboxHome(t, configDir);
|
||||
|
||||
@@ -1123,7 +1123,7 @@ describe('installRuntimeArtifacts — I3: no cleanup dirs is an empty array', ()
|
||||
|
||||
describe('installRuntimeArtifacts — I4: stage failure before any item cleans up and throws', () => {
|
||||
test('stage failure cleans up and throws', (t) => {
|
||||
const configDir = createTempDir('gsd-i4-');
|
||||
const configDir = createTempDir('msd-i4-');
|
||||
t.after(() => cleanup(configDir));
|
||||
sandboxHome(t, configDir);
|
||||
|
||||
@@ -1138,21 +1138,21 @@ describe('installRuntimeArtifacts — I4: stage failure before any item cleans u
|
||||
|
||||
describe('installRuntimeArtifacts — I5: rewrite failure mid-plan cleans up and throws', () => {
|
||||
test('rewrite failure cleans up and throws', (t) => {
|
||||
const configDir = createTempDir('gsd-i5-augment-');
|
||||
const configDir = createTempDir('msd-i5-augment-');
|
||||
t.after(() => cleanup(configDir));
|
||||
sandboxHome(t, configDir);
|
||||
|
||||
let capturedCleanupDir;
|
||||
const fakeFs = createRealDelegatingFs({
|
||||
mkdirSync: (p, opts) => {
|
||||
if (String(p).includes('gsd-profile-runtime-skills-')) {
|
||||
if (String(p).includes('msd-profile-runtime-skills-')) {
|
||||
throw new Error('I5: simulated skills-stage failure AFTER commands already rewrote+registered a cleanup dir');
|
||||
}
|
||||
fs.mkdirSync(p, opts);
|
||||
return undefined;
|
||||
},
|
||||
writeFileSync: (p, data, opts) => {
|
||||
if (String(p).includes('gsd-cmd-rewrites-') && capturedCleanupDir === undefined) {
|
||||
if (String(p).includes('msd-cmd-rewrites-') && capturedCleanupDir === undefined) {
|
||||
capturedCleanupDir = path.dirname(p);
|
||||
}
|
||||
fs.writeFileSync(p, data, opts);
|
||||
@@ -1192,29 +1192,29 @@ function walkFilesRecursively(root) {
|
||||
describe('installRuntimeArtifacts — K3: real install before/after, full recursive diff', () => {
|
||||
test('writes are byte-identical', (t) => {
|
||||
for (const runtime of ['claude', 'qwen']) {
|
||||
const dirA = createTempDir(`gsd-k3-${runtime}-a-`);
|
||||
const dirB = createTempDir(`gsd-k3-${runtime}-b-`);
|
||||
const dirA = createTempDir(`msd-k3-${runtime}-a-`);
|
||||
const dirB = createTempDir(`msd-k3-${runtime}-b-`);
|
||||
// Two SEQUENTIAL sandboxes for one test: sandboxHome()'s per-call
|
||||
// t.after hooks each save the env as they found it, so the second call
|
||||
// saves the FIRST sandbox as its "original" — hook ordering then leaves
|
||||
// HOME pointing at dirA after the test, leaking into later tests in the
|
||||
// shard (observed on the windows matrix: a leaked gsd-k3-qwen-* home
|
||||
// shard (observed on the windows matrix: a leaked msd-k3-qwen-* home
|
||||
// made the L2 property's antigravity/global run refuse via the
|
||||
// #3712 real-home guard). Manage the env directly with ONE restore.
|
||||
const savedHome = process.env.HOME;
|
||||
const savedUserProfile = process.env.USERPROFILE;
|
||||
const savedMarker = process.env.GSD_TEST_HOME_SANDBOX;
|
||||
const savedMarker = process.env.MSD_TEST_HOME_SANDBOX;
|
||||
t.after(() => {
|
||||
if (savedHome === undefined) delete process.env.HOME; else process.env.HOME = savedHome;
|
||||
if (savedUserProfile === undefined) delete process.env.USERPROFILE; else process.env.USERPROFILE = savedUserProfile;
|
||||
if (savedMarker === undefined) delete process.env.GSD_TEST_HOME_SANDBOX; else process.env.GSD_TEST_HOME_SANDBOX = savedMarker;
|
||||
if (savedMarker === undefined) delete process.env.MSD_TEST_HOME_SANDBOX; else process.env.MSD_TEST_HOME_SANDBOX = savedMarker;
|
||||
cleanup(dirA); cleanup(dirB);
|
||||
});
|
||||
|
||||
for (const dir of [dirA, dirB]) {
|
||||
process.env.HOME = dir;
|
||||
process.env.USERPROFILE = dir;
|
||||
process.env.GSD_TEST_HOME_SANDBOX = dir;
|
||||
process.env.MSD_TEST_HOME_SANDBOX = dir;
|
||||
installRuntimeArtifacts(runtime, dir, 'global', RESOLVED_FULL);
|
||||
}
|
||||
|
||||
@@ -1225,15 +1225,15 @@ describe('installRuntimeArtifacts — K3: real install before/after, full recurs
|
||||
`K3 (${runtime}): the file sets written by two independent installs must match`,
|
||||
);
|
||||
for (const [relPath, contentA] of filesA) {
|
||||
if (relPath === '.gsd-source') {
|
||||
if (relPath === '.msd-source') {
|
||||
assert.strictEqual(
|
||||
contentA.toString('utf8'),
|
||||
`${path.join(dirA, 'gsd-core', 'commands', 'gsd')}\n`,
|
||||
`${path.join(dirA, 'msd-core', 'commands', 'msd')}\n`,
|
||||
`K3 (${runtime}): first marker must target its own installed corpus`,
|
||||
);
|
||||
assert.strictEqual(
|
||||
filesB.get(relPath).toString('utf8'),
|
||||
`${path.join(dirB, 'gsd-core', 'commands', 'gsd')}\n`,
|
||||
`${path.join(dirB, 'msd-core', 'commands', 'msd')}\n`,
|
||||
`K3 (${runtime}): second marker must target its own installed corpus`,
|
||||
);
|
||||
continue;
|
||||
@@ -1268,7 +1268,7 @@ describe('installRuntimeArtifacts — L1: plan kinds mirror layout kinds (proper
|
||||
// the failing/shrunk (runtime, scope) pair fast-check found).
|
||||
fc.assert(
|
||||
fc.property(RUNTIME_ARB, SCOPE_ARB, (runtime, scope) => {
|
||||
const configDir = createTempDir(`gsd-l1-${runtime}-`);
|
||||
const configDir = createTempDir(`msd-l1-${runtime}-`);
|
||||
createdDirs.push(configDir);
|
||||
process.env.HOME = configDir;
|
||||
process.env.USERPROFILE = configDir;
|
||||
@@ -1335,7 +1335,7 @@ describe('installRuntimeArtifacts — L2: plan is deterministic (property)', ()
|
||||
fc.property(RUNTIME_ARB, SCOPE_ARB, (runtime, scope) => {
|
||||
// configDir is never created for real — both calls run against fresh,
|
||||
// independent fake adapters, so no real fs cleanup is needed here.
|
||||
const configDir = path.join(os.tmpdir(), `gsd-l2-${runtime}-${crypto.randomUUID()}`);
|
||||
const configDir = path.join(os.tmpdir(), `msd-l2-${runtime}-${crypto.randomUUID()}`);
|
||||
fs.mkdirSync(configDir, { recursive: true });
|
||||
createdL2Dirs.push(configDir);
|
||||
// #3738: a home-override runtime (antigravity → <HOME>/.gemini/config)
|
||||
@@ -1371,31 +1371,31 @@ describe('installRuntimeArtifacts — L2: plan is deterministic (property)', ()
|
||||
});
|
||||
});
|
||||
|
||||
// ─── readGsdCommandNames — single-source parity ──────────────────────────────
|
||||
// ─── readMsdCommandNames — single-source parity ──────────────────────────────
|
||||
//
|
||||
// command-roster.cts's readGsdCommandNames reimplements
|
||||
// command-roster.cts's readMsdCommandNames reimplements
|
||||
// scripts/fix-slash-commands.cjs's readCmdNames' directory-scan rule against
|
||||
// the injectable install-fs seam instead of delegating to it — see
|
||||
// command-roster.cts's module comment for why (readCmdNames is deliberately
|
||||
// a zero-dependency standalone CLI/library with no build-order dependency on
|
||||
// gsd-core/bin/lib, so it cannot itself require the compiled
|
||||
// msd-core/bin/lib, so it cannot itself require the compiled
|
||||
// install-fs-adapter.cjs). Two implementations of one filtering rule is this
|
||||
// repo's recorded Generative Fix Divergence class; this test is the
|
||||
// enforcement the coordinator required in exchange for keeping the
|
||||
// reimplementation: it fails the moment the two disagree about which stems
|
||||
// commands/gsd/ contains.
|
||||
// commands/msd/ contains.
|
||||
|
||||
describe('readGsdCommandNames — single-source parity (command-roster.cts vs scripts/fix-slash-commands.cjs)', () => {
|
||||
test('both implementations report the identical stem set for commands/gsd/', () => {
|
||||
const fromCommandRoster = [...commandRoster.readGsdCommandNames()].sort();
|
||||
describe('readMsdCommandNames — single-source parity (command-roster.cts vs scripts/fix-slash-commands.cjs)', () => {
|
||||
test('both implementations report the identical stem set for commands/msd/', () => {
|
||||
const fromCommandRoster = [...commandRoster.readMsdCommandNames()].sort();
|
||||
const fromSlashCommandTransformer = [...slashCommandTransformer.readCmdNames()].sort();
|
||||
assert.deepStrictEqual(
|
||||
fromCommandRoster,
|
||||
fromSlashCommandTransformer,
|
||||
'command-roster.cts readGsdCommandNames() and scripts/fix-slash-commands.cjs readCmdNames() ' +
|
||||
'command-roster.cts readMsdCommandNames() and scripts/fix-slash-commands.cjs readCmdNames() ' +
|
||||
'diverged — these are two implementations of the SAME directory-scan rule (Generative Fix ' +
|
||||
'Divergence); fix the one that is wrong, do not just silence this test',
|
||||
);
|
||||
assert.ok(fromCommandRoster.length > 0, 'sanity: commands/gsd/ must contain at least one command');
|
||||
assert.ok(fromCommandRoster.length > 0, 'sanity: commands/msd/ must contain at least one command');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user