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

@@ -18,9 +18,9 @@ const os = require('node:os');
const path = require('node:path');
const { cleanup } = require('./helpers.cjs');
const lifecycle = require('../gsd-core/bin/lib/capability-lifecycle.cjs');
const ledgerMod = require('../gsd-core/bin/lib/capability-ledger.cjs');
const { escapeRegex } = require('../gsd-core/bin/lib/pattern.cjs');
const lifecycle = require('../msd-core/bin/lib/capability-lifecycle.cjs');
const ledgerMod = require('../msd-core/bin/lib/capability-ledger.cjs');
const { escapeRegex } = require('../msd-core/bin/lib/pattern.cjs');
const { CAP_MARKER } = lifecycle;
// ---------------------------------------------------------------------------
@@ -56,7 +56,7 @@ function declarativeCap(id, version = '1.0.0') {
description: 'test capability',
tier: 'standard',
requires: [],
engines: { gsd: '>=1.0.0' },
engines: { msd: '>=1.0.0' },
runtimeCompat: { supported: ['*'], unsupported: [] },
skills: [],
agents: [],
@@ -88,7 +88,7 @@ function materialize(dir, rel) {
function fakeResolve(manifest, { integrity = null, throwErr = null } = {}) {
return async (spec, opts) => {
if (throwErr) throw new Error(throwErr);
const root = path.join(opts.gsdHome, '.gsd', 'capabilities', '.staging');
const root = path.join(opts.msdHome, '.msd', 'capabilities', '.staging');
fs.mkdirSync(root, { recursive: true });
const dir = path.join(root, `${manifest.id}-${++stageCounter}`);
fs.mkdirSync(dir, { recursive: true });
@@ -108,7 +108,7 @@ function readSettings(dir) {
}
function capManifestVersion(dir, id) {
try {
return JSON.parse(fs.readFileSync(path.join(dir, '.gsd', 'capabilities', id, 'capability.json'), 'utf8')).version;
return JSON.parse(fs.readFileSync(path.join(dir, '.msd', 'capabilities', id, 'capability.json'), 'utf8')).version;
} catch { return null; }
}
/**
@@ -117,7 +117,7 @@ function capManifestVersion(dir, id) {
* (so an ancestor symlink cannot escape). Mirrors confinedBundleScript in the source.
*/
function expectedBundleCommand(dir, id, script) {
const capDir = path.join(dir, '.gsd', 'capabilities', id);
const capDir = path.join(dir, '.msd', 'capabilities', id);
const target = path.resolve(capDir, script);
let parent = path.dirname(target);
try { parent = fs.realpathSync(parent); } catch { /* lexical fallback below */ }
@@ -137,7 +137,7 @@ test('install: declarative capability installs without consent and records the l
const entry = readLedgerEntry(dir, 'decl');
assert.ok(entry, 'ledger entry recorded');
assert.strictEqual(entry.version, '1.0.0');
assert.ok(fs.existsSync(path.join(dir, '.gsd', 'capabilities', 'decl', 'capability.json')));
assert.ok(fs.existsSync(path.join(dir, '.msd', 'capabilities', 'decl', 'capability.json')));
});
test('install: executable capability without consent aborts and writes NOTHING', async () => {
@@ -149,7 +149,7 @@ test('install: executable capability without consent aborts and writes NOTHING',
assert.strictEqual(res.status, 'aborted');
assert.strictEqual(res.requiresConsent, true);
assert.strictEqual(readLedgerEntry(dir, 'exec'), null, 'no ledger entry');
assert.ok(!fs.existsSync(path.join(dir, '.gsd', 'capabilities', 'exec')), 'no install dir');
assert.ok(!fs.existsSync(path.join(dir, '.msd', 'capabilities', 'exec')), 'no install dir');
assert.strictEqual(readSettings(dir), null, 'no settings.json written');
});
@@ -183,17 +183,17 @@ test('install: a disallowed source is blocked BEFORE the resolver runs', async (
test('install: a reserved-namespace capability is blocked', async () => {
const dir = runtime();
const res = await lifecycle.installCapability('./x', {
runtimeDir: dir, hostVersion: '1.6.0', _resolve: fakeResolve(declarativeCap('gsd-evil')),
runtimeDir: dir, hostVersion: '1.6.0', _resolve: fakeResolve(declarativeCap('msd-evil')),
});
assert.strictEqual(res.status, 'blocked');
assert.ok(res.blockReasons.some((r) => /reserved namespace/.test(r)));
assert.ok(!fs.existsSync(path.join(dir, '.gsd', 'capabilities', 'gsd-evil')));
assert.ok(!fs.existsSync(path.join(dir, '.msd', 'capabilities', 'msd-evil')));
});
test('install: engines mismatch is blocked at install WITH a compatVersions downgrade hint', async () => {
const dir = runtime();
const cap = declarativeCap('eng', '3.0.0');
cap.engines = { gsd: '>=2.0.0' };
cap.engines = { msd: '>=2.0.0' };
cap.compatVersions = { '1.4.0': '>=1.5.0 <2.0.0' };
const res = await lifecycle.installCapability('./eng', {
runtimeDir: dir, hostVersion: '1.6.0', _resolve: fakeResolve(cap),
@@ -208,13 +208,13 @@ test('integration: engines-incompatible local cap is blocked by the lifecycle, n
const src = fs.mkdtempSync(path.join(os.tmpdir(), 'cap-src-eng-'));
cleanups.push(src);
const cap = declarativeCap('engreal');
cap.engines = { gsd: '>=99.0.0' };
cap.engines = { msd: '>=99.0.0' };
fs.writeFileSync(path.join(src, 'capability.json'), JSON.stringify(cap), 'utf8');
const res = await lifecycle.installCapability(src, { runtimeDir: dir, hostVersion: '1.6.0' });
assert.strictEqual(res.status, 'blocked', JSON.stringify(res));
assert.ok(res.blockReasons.some((r) => /engines\.gsd/.test(r)));
assert.ok(!fs.existsSync(path.join(dir, '.gsd', 'capabilities', 'engreal')), 'nothing installed');
const staging = path.join(dir, '.gsd', 'capabilities', '.staging');
assert.ok(res.blockReasons.some((r) => /engines\.msd/.test(r)));
assert.ok(!fs.existsSync(path.join(dir, '.msd', 'capabilities', 'engreal')), 'nothing installed');
const staging = path.join(dir, '.msd', 'capabilities', '.staging');
assert.deepStrictEqual(fs.existsSync(staging) ? fs.readdirSync(staging) : [], [], 'staging cleaned');
});
@@ -232,13 +232,13 @@ test('install: re-installing over an existing capability (via install, not upgra
assert.strictEqual(capManifestVersion(dir, 'e'), '2.0.0');
assert.strictEqual(readLedgerEntry(dir, 'e').version, '2.0.0');
assert.ok(!readLedgerEntry(dir, 'e')._pending, 'intent cleared on commit');
assert.deepStrictEqual(fs.readdirSync(path.join(dir, '.gsd', 'capabilities')).filter((n) => n.includes('.upgrading-')), [], 'no backup left');
assert.deepStrictEqual(fs.readdirSync(path.join(dir, '.msd', 'capabilities')).filter((n) => n.includes('.upgrading-')), [], 'no backup left');
assert.strictEqual(readSettings(dir).hooks.PostToolUse.length, 1, 'exactly one stamped hook');
});
test('install: a deadman-stale lock is stolen so a crashed prior holder does not block forever', async () => {
const dir = runtime();
const lockPath = path.join(dir, '.gsd', 'capabilities', '.lock');
const lockPath = path.join(dir, '.msd', 'capabilities', '.lock');
fs.mkdirSync(path.dirname(lockPath), { recursive: true });
// A legacy no-pid body: liveness cannot be verified, so (finding 1) it is reclaimable ONLY by the
// HARD deadman timeout — backdate it past LOCK_DEADMAN_MS (10 min) to simulate a crashed holder the
@@ -289,7 +289,7 @@ test('upgrade: same executable set upgrades without re-consent; bundle + ledger
assert.strictEqual(res.toVersion, '2.0.0');
assert.strictEqual(capManifestVersion(dir, 'e'), '2.0.0');
assert.strictEqual(readLedgerEntry(dir, 'e').version, '2.0.0');
const leftovers = fs.readdirSync(path.join(dir, '.gsd', 'capabilities')).filter((n) => n.includes('.upgrading-'));
const leftovers = fs.readdirSync(path.join(dir, '.msd', 'capabilities')).filter((n) => n.includes('.upgrading-'));
assert.deepStrictEqual(leftovers, [], 'no backup dir left behind');
// Exactly one stamped hook remains (old stripped, new applied).
assert.strictEqual(readSettings(dir).hooks.PostToolUse.length, 1);
@@ -403,7 +403,7 @@ test('#1460 (R): a normal script under a normal prefix emits the quoted absolute
test('#1460 (R): idempotent strip-then-reapply yields the identical quoted command', () => {
const dir = runtime();
const capId = 'e';
const capDirPath = path.join(dir, '.gsd', 'capabilities', capId);
const capDirPath = path.join(dir, '.msd', 'capabilities', capId);
fs.mkdirSync(path.join(capDirPath, 'hooks'), { recursive: true });
fs.writeFileSync(path.join(capDirPath, 'hooks', 'run.js'), '// x', 'utf8');
const manifest = execCap(capId, '1.0.0', { script: 'hooks/run.js' });
@@ -428,7 +428,7 @@ test('#1460 (R): idempotent strip-then-reapply yields the identical quoted comma
test('#1634: a declared matcher is preserved on the emitted shared-config hook entry', () => {
const dir = runtime();
const capId = 'toolkit';
const capDirPath = path.join(dir, '.gsd', 'capabilities', capId);
const capDirPath = path.join(dir, '.msd', 'capabilities', capId);
fs.mkdirSync(path.join(capDirPath, 'hooks'), { recursive: true });
fs.writeFileSync(path.join(capDirPath, 'hooks', 'genfile-guard.cjs'), '// guard\n', 'utf8');
const manifest = declarativeCap(capId, '1.0.0');
@@ -445,7 +445,7 @@ test('#1634: a declared matcher is preserved on the emitted shared-config hook e
test('#1634: an absent matcher is omitted (match-all) so shipped capabilities stay unchanged', () => {
const dir = runtime();
const capId = 'plain';
const capDirPath = path.join(dir, '.gsd', 'capabilities', capId);
const capDirPath = path.join(dir, '.msd', 'capabilities', capId);
fs.mkdirSync(path.join(capDirPath, 'hooks'), { recursive: true });
fs.writeFileSync(path.join(capDirPath, 'hooks', 'run.js'), '// x\n', 'utf8');
const manifest = declarativeCap(capId, '1.0.0');
@@ -460,7 +460,7 @@ test('#1634: an absent matcher is omitted (match-all) so shipped capabilities st
test('#1634: a .cjs hook command is node-prefixed so it runs without the executable bit', () => {
const dir = runtime();
const capId = 'toolkit';
const capDirPath = path.join(dir, '.gsd', 'capabilities', capId);
const capDirPath = path.join(dir, '.msd', 'capabilities', capId);
fs.mkdirSync(path.join(capDirPath, 'hooks'), { recursive: true });
// Stage at 0644 (no +x) — a git/tarball source that lost the executable bit.
fs.writeFileSync(path.join(capDirPath, 'hooks', 'genfile-guard.cjs'), '// guard\n', { encoding: 'utf8', mode: 0o644 });
@@ -484,7 +484,7 @@ test('#1634: a .cjs hook command is node-prefixed so it runs without the executa
test('#1634: matcher + node-prefix together; marker strip round-trip unaffected', () => {
const dir = runtime();
const capId = 'toolkit';
const capDirPath = path.join(dir, '.gsd', 'capabilities', capId);
const capDirPath = path.join(dir, '.msd', 'capabilities', capId);
fs.mkdirSync(path.join(capDirPath, 'hooks'), { recursive: true });
fs.writeFileSync(path.join(capDirPath, 'hooks', 'genfile-guard.cjs'), '// guard\n', 'utf8');
const manifest = declarativeCap(capId, '1.0.0');
@@ -504,7 +504,7 @@ test('#1634: matcher + node-prefix together; marker strip round-trip unaffected'
test('#1460 (R): confinedBundleScript returns null for an unsafe-char script (defense-in-depth)', () => {
const dir = runtime();
const capDirPath = path.join(dir, '.gsd', 'capabilities', 'e');
const capDirPath = path.join(dir, '.msd', 'capabilities', 'e');
fs.mkdirSync(capDirPath, { recursive: true });
// Even when the file literally exists on disk inside the bundle, an unsafe-char script
// name must be refused — so applyCapabilitySharedEdits skips it even if validation were
@@ -522,7 +522,7 @@ test('#1460 (R): confinedBundleScript returns null for an unsafe-char script (de
// ---------------------------------------------------------------------------
function seedCapDir(dir, name, manifest) {
const d = path.join(dir, '.gsd', 'capabilities', name);
const d = path.join(dir, '.msd', 'capabilities', name);
fs.mkdirSync(d, { recursive: true });
fs.writeFileSync(path.join(d, 'capability.json'), JSON.stringify(manifest), 'utf8');
return d;
@@ -530,7 +530,7 @@ function seedCapDir(dir, name, manifest) {
// Record a ledger entry carrying an in-flight INTENT (the commit signal).
function recordPending(dir, id, version, { kind = 'upgrade', backupName, sharedFiles = [], sharedEdits = [] }) {
ledgerMod.recordInstall(dir, {
id, version, source: 's', integrity: '', files: ['.gsd/capabilities/' + id], sharedEdits,
id, version, source: 's', integrity: '', files: ['.msd/capabilities/' + id], sharedEdits,
_pending: { kind, backupName, sharedFiles },
});
}
@@ -543,7 +543,7 @@ test('reconcile: crash before new swapped in (final missing, backup present) rol
assert.ok(report.rolledBack.includes('c'));
assert.strictEqual(capManifestVersion(dir, 'c'), '1.0.0');
assert.ok(!readLedgerEntry(dir, 'c')._pending, 'intent cleared');
assert.deepStrictEqual(fs.readdirSync(path.join(dir, '.gsd', 'capabilities')).filter((n) => n.includes('.upgrading-')), []);
assert.deepStrictEqual(fs.readdirSync(path.join(dir, '.msd', 'capabilities')).filter((n) => n.includes('.upgrading-')), []);
});
test('reconcile: crash after swap before commit (intent present) rolls back to old', () => {
@@ -564,7 +564,7 @@ test('reconcile H3: SAME-version malicious bundle with intent present is rolled
recordPending(dir, 'c', '1.0.0', { backupName: 'c.upgrading-111-222' });
const report = lifecycle.reconcileCapabilities({ runtimeDir: dir });
assert.ok(report.rolledBack.includes('c'), 'must roll back despite equal version strings');
const live = JSON.parse(fs.readFileSync(path.join(dir, '.gsd', 'capabilities', 'c', 'capability.json'), 'utf8'));
const live = JSON.parse(fs.readFileSync(path.join(dir, '.msd', 'capabilities', 'c', 'capability.json'), 'utf8'));
assert.strictEqual(live._evil, false, 'the genuine old bundle is restored, not the uncommitted one');
});
@@ -593,16 +593,16 @@ test('reconcile: committed leftover backup (no intent) rolls forward, dropping t
seedCapDir(dir, 'c', declarativeCap('c', '2.0.0')); // new, committed, live
seedCapDir(dir, 'c.upgrading-111-222', declarativeCap('c', '1.0.0')); // stale backup
// Committed: ledger entry has NO _pendingUpgrade.
ledgerMod.recordInstall(dir, { id: 'c', version: '2.0.0', source: 's', integrity: '', files: ['.gsd/capabilities/c'], sharedEdits: [] });
ledgerMod.recordInstall(dir, { id: 'c', version: '2.0.0', source: 's', integrity: '', files: ['.msd/capabilities/c'], sharedEdits: [] });
const report = lifecycle.reconcileCapabilities({ runtimeDir: dir });
assert.ok(report.rolledForward.includes('c'));
assert.strictEqual(capManifestVersion(dir, 'c'), '2.0.0', 'new kept');
assert.deepStrictEqual(fs.readdirSync(path.join(dir, '.gsd', 'capabilities')).filter((n) => n.includes('.upgrading-')), []);
assert.deepStrictEqual(fs.readdirSync(path.join(dir, '.msd', 'capabilities')).filter((n) => n.includes('.upgrading-')), []);
});
test('reconcile: an AGED staging orphan is swept, a FRESH one (possible in-flight resolve) is spared', () => {
const dir = runtime();
const stagingRoot = path.join(dir, '.gsd', 'capabilities', '.staging');
const stagingRoot = path.join(dir, '.msd', 'capabilities', '.staging');
const aged = path.join(stagingRoot, 'aged-1');
const fresh = path.join(stagingRoot, 'fresh-2');
fs.mkdirSync(aged, { recursive: true });
@@ -630,7 +630,7 @@ test('reconcile H1: a crashed FRESH install (intent kind=install) is rolled back
const report = lifecycle.reconcileCapabilities({ runtimeDir: dir });
assert.ok(report.rolledBack.includes('f'));
assert.strictEqual(readLedgerEntry(dir, 'f'), null, 'half-installed ledger entry removed');
assert.ok(!fs.existsSync(path.join(dir, '.gsd', 'capabilities', 'f')), 'half-installed dir removed');
assert.ok(!fs.existsSync(path.join(dir, '.msd', 'capabilities', 'f')), 'half-installed dir removed');
assert.ok(!readSettings(dir).hooks, 'stranded shared edit stripped');
});
@@ -650,7 +650,7 @@ test('reconcile M5: a tampered ledger key (non-kebab id) is skipped, never used
entries: {
'../../precious': {
id: '../../precious', version: '1.0.0', source: 's', integrity: '',
files: ['.gsd/capabilities/x'], sharedEdits: [],
files: ['.msd/capabilities/x'], sharedEdits: [],
_pending: { kind: 'install', backupName: null, sharedFiles: [] },
},
},
@@ -684,9 +684,9 @@ test('reconcile H2: reinstall rollback restores OLD ledger metadata, not the new
test('remove: a held lock makes remove report "in progress" (does not mutate)', () => {
const dir = runtime();
const lockPath = path.join(dir, '.gsd', 'capabilities', '.lock');
const lockPath = path.join(dir, '.msd', 'capabilities', '.lock');
fs.mkdirSync(path.dirname(lockPath), { recursive: true });
ledgerMod.recordInstall(dir, { id: 'q', version: '1.0.0', source: 's', integrity: '', files: ['.gsd/capabilities/q'], sharedEdits: [] });
ledgerMod.recordInstall(dir, { id: 'q', version: '1.0.0', source: 's', integrity: '', files: ['.msd/capabilities/q'], sharedEdits: [] });
fs.writeFileSync(lockPath, '99999', 'utf8');
try {
const res = lifecycle.removeCapability('q', { runtimeDir: dir });
@@ -700,7 +700,7 @@ test('remove: a held lock makes remove report "in progress" (does not mutate)',
test('reconcile: a held lock makes reconcile defer (no-op) and a mutation report "in progress"', async () => {
const dir = runtime();
// Manually hold the lock (fresh mtime => not stale).
const lockPath = path.join(dir, '.gsd', 'capabilities', '.lock');
const lockPath = path.join(dir, '.msd', 'capabilities', '.lock');
fs.mkdirSync(path.dirname(lockPath), { recursive: true });
fs.writeFileSync(lockPath, '99999', 'utf8');
try {
@@ -734,7 +734,7 @@ test('remove: deletes recorded files, strips marked shared edits, drops the ledg
const res = lifecycle.removeCapability('e', { runtimeDir: dir });
assert.strictEqual(res.status, 'removed');
assert.strictEqual(readLedgerEntry(dir, 'e'), null);
assert.ok(!fs.existsSync(path.join(dir, '.gsd', 'capabilities', 'e')));
assert.ok(!fs.existsSync(path.join(dir, '.msd', 'capabilities', 'e')));
const settings = readSettings(dir);
assert.ok(!settings.hooks, 'empty hooks object pruned');
assert.ok(!settings.mcpServers, 'empty mcpServers object pruned');
@@ -771,7 +771,7 @@ test('remove: tolerates a user having already deleted the shared file and the in
_resolve: fakeResolve(execCap('e', '1.0.0')),
});
cleanup(path.join(dir, 'settings.json'));
cleanup(path.join(dir, '.gsd', 'capabilities', 'e'));
cleanup(path.join(dir, '.msd', 'capabilities', 'e'));
const res = lifecycle.removeCapability('e', { runtimeDir: dir });
assert.strictEqual(res.status, 'removed', 'idempotent despite missing artifacts');
assert.strictEqual(readLedgerEntry(dir, 'e'), null);
@@ -784,11 +784,11 @@ test('remove: a tampered ledger files[] routed through a symlink cannot delete o
const victim = path.join(outside, 'victim.txt');
fs.writeFileSync(victim, 'precious', 'utf8');
// A symlink inside runtimeDir pointing OUT, plus a tampered ledger routing files[] through it.
fs.mkdirSync(path.join(dir, '.gsd'), { recursive: true });
fs.symlinkSync(outside, path.join(dir, '.gsd', 'link'), 'dir');
fs.mkdirSync(path.join(dir, '.msd'), { recursive: true });
fs.symlinkSync(outside, path.join(dir, '.msd', 'link'), 'dir');
ledgerMod.recordInstall(dir, {
id: 'evil', version: '1.0.0', source: 's', integrity: '',
files: ['.gsd/link/victim.txt'], sharedEdits: [],
files: ['.msd/link/victim.txt'], sharedEdits: [],
});
lifecycle.removeCapability('evil', { runtimeDir: dir });
assert.ok(fs.existsSync(victim), 'a file OUTSIDE runtimeDir (reached via symlink) must NOT be deleted');
@@ -800,7 +800,7 @@ test('remove: CAPABILITY_DATA is preserved by default and deleted only on remove
runtimeDir: dir, hostVersion: '1.6.0', consentGranted: true,
_resolve: fakeResolve(declarativeCap('e')),
});
const dataDir = path.join(dir, '.gsd', 'capability-data', 'e');
const dataDir = path.join(dir, '.msd', 'capability-data', 'e');
fs.mkdirSync(dataDir, { recursive: true });
fs.writeFileSync(path.join(dataDir, 'state.json'), '{}', 'utf8');
@@ -888,17 +888,17 @@ test('remove: a corrupt-present ledger fails closed with status=blocked — must
// ---------------------------------------------------------------------------
// Finding 2 (HIGH): remove must run a READ-ONLY corruption preflight BEFORE acquireLock.
// On a corrupt ledger it must NOT create .gsd/capabilities and must NOT create a .lock.
// On a corrupt ledger it must NOT create .msd/capabilities and must NOT create a .lock.
// ---------------------------------------------------------------------------
test('finding-2: removeCapability on a corrupt ledger does NOT acquire a lock or create .gsd/capabilities (preflight precedes acquireLock)', () => {
test('finding-2: removeCapability on a corrupt ledger does NOT acquire a lock or create .msd/capabilities (preflight precedes acquireLock)', () => {
const dir = runtime();
const ledgerPath = path.join(dir, ledgerMod.LEDGER_FILE_NAME);
fs.mkdirSync(dir, { recursive: true });
fs.writeFileSync(ledgerPath, '{ broken json ---');
const capsRoot = path.join(dir, '.gsd', 'capabilities');
assert.ok(!fs.existsSync(capsRoot), 'precondition: .gsd/capabilities must not exist yet');
const capsRoot = path.join(dir, '.msd', 'capabilities');
assert.ok(!fs.existsSync(capsRoot), 'precondition: .msd/capabilities must not exist yet');
const result = lifecycle.removeCapability('some-cap', { runtimeDir: dir });
@@ -909,7 +909,7 @@ test('finding-2: removeCapability on a corrupt ledger does NOT acquire a lock or
// UNCONDITIONAL: the read-only preflight failed BEFORE acquireLock, so no lock dir/file exists.
assert.ok(!fs.existsSync(capsRoot),
'.gsd/capabilities must NOT be created by acquireLock when the corruption preflight blocks first');
'.msd/capabilities must NOT be created by acquireLock when the corruption preflight blocks first');
assert.ok(!fs.existsSync(path.join(capsRoot, '.lock')),
'no .lock file may be created when the corruption preflight blocks before acquireLock');
});
@@ -1029,7 +1029,7 @@ test('#1460 CONF-1: a relative hook script is emitted as the absolute path insid
const dir = runtime();
const capId = 'conf1';
// Make the install dir real so realpath confinement resolves a concrete chain.
const capDir = path.join(dir, '.gsd', 'capabilities', capId);
const capDir = path.join(dir, '.msd', 'capabilities', capId);
fs.mkdirSync(path.join(capDir, 'subdir'), { recursive: true });
fs.writeFileSync(path.join(capDir, 'subdir', 'run.js'), '// hook', 'utf8');
@@ -1053,7 +1053,7 @@ test('#1460 CONF-1: a relative hook script is emitted as the absolute path insid
test('#1460 CONF-1: a script resolving OUTSIDE capDir via a symlinked subdir is NOT written (skipped)', (t) => {
const dir = runtime();
const capId = 'conf1-escape';
const capDir = path.join(dir, '.gsd', 'capabilities', capId);
const capDir = path.join(dir, '.msd', 'capabilities', capId);
fs.mkdirSync(capDir, { recursive: true });
// Plant a victim file outside the bundle and a symlinked subdir inside the bundle
@@ -1139,16 +1139,16 @@ test('reconcileCapabilities: a corrupt-present ledger surfaces a warning in repo
// ---------------------------------------------------------------------------
// Finding 2 (HIGH): reconcile must run a READ-ONLY corruption preflight BEFORE acquireLock.
// On a corrupt ledger it must warn WITHOUT creating .gsd/capabilities or a .lock.
// On a corrupt ledger it must warn WITHOUT creating .msd/capabilities or a .lock.
// ---------------------------------------------------------------------------
test('finding-2: reconcileCapabilities on a corrupt ledger does NOT acquire a lock or create .gsd/capabilities (preflight precedes acquireLock)', () => {
test('finding-2: reconcileCapabilities on a corrupt ledger does NOT acquire a lock or create .msd/capabilities (preflight precedes acquireLock)', () => {
const dir = runtime();
fs.mkdirSync(dir, { recursive: true });
fs.writeFileSync(path.join(dir, ledgerMod.LEDGER_FILE_NAME), '{ broken json ---');
const capsRoot = path.join(dir, '.gsd', 'capabilities');
assert.ok(!fs.existsSync(capsRoot), 'precondition: .gsd/capabilities must not exist yet');
const capsRoot = path.join(dir, '.msd', 'capabilities');
assert.ok(!fs.existsSync(capsRoot), 'precondition: .msd/capabilities must not exist yet');
const report = lifecycle.reconcileCapabilities({ runtimeDir: dir });
@@ -1157,7 +1157,7 @@ test('finding-2: reconcileCapabilities on a corrupt ledger does NOT acquire a lo
// UNCONDITIONAL: the read-only preflight warned BEFORE acquireLock, so no lock dir/file exists.
assert.ok(!fs.existsSync(capsRoot),
'.gsd/capabilities must NOT be created by acquireLock when the corruption preflight warns first');
'.msd/capabilities must NOT be created by acquireLock when the corruption preflight warns first');
assert.ok(!fs.existsSync(path.join(capsRoot, '.lock')),
'no .lock file may be created when the corruption preflight warns before acquireLock');
});
@@ -1297,7 +1297,7 @@ test('integration: install a real, valid, declarative local capability through t
const res = await lifecycle.installCapability(src, { runtimeDir: dir, hostVersion: '1.6.0' });
assert.strictEqual(res.status, 'installed', JSON.stringify(res));
assert.strictEqual(res.id, 'realcap');
assert.ok(fs.existsSync(path.join(dir, '.gsd', 'capabilities', 'realcap', 'capability.json')));
assert.ok(fs.existsSync(path.join(dir, '.msd', 'capabilities', 'realcap', 'capability.json')));
assert.ok(readLedgerEntry(dir, 'realcap'), 'ledger entry recorded via real path');
});
@@ -1357,8 +1357,8 @@ test('remove: finding-3 — mid-remove corruption blocks coherently: capability
const renameMock = mock.method(fs, 'renameSync', function (src, dst) {
renameCount++;
// The first rename may be for staging during install setup; skip it.
// The ledger commit rename will be for a .tmp.<pid>-<nonce> → .gsd-capabilities.json path.
if (typeof dst === 'string' && dst.includes('.gsd-capabilities.json') && renameCount >= 1) {
// The ledger commit rename will be for a .tmp.<pid>-<nonce> → .msd-capabilities.json path.
if (typeof dst === 'string' && dst.includes('.msd-capabilities.json') && renameCount >= 1) {
const err = new Error('EXDEV: cross-device link not permitted');
err.code = 'EXDEV';
throw err;
@@ -1377,11 +1377,11 @@ test('remove: finding-3 — mid-remove corruption blocks coherently: capability
result.blockReasons && result.blockReasons.length > 0,
'must include blockReasons explaining the failure',
);
// The error message must NOT reference a non-existent CLI command ('gsd capability reconcile').
// The error message must NOT reference a non-existent CLI command ('msd capability reconcile').
const reason = result.blockReasons[0] || '';
assert.ok(
!reason.includes('gsd capability reconcile'),
`blockReasons must not reference non-existent CLI subcommand 'gsd capability reconcile'; got: "${reason}"`,
!reason.includes('msd capability reconcile'),
`blockReasons must not reference non-existent CLI subcommand 'msd capability reconcile'; got: "${reason}"`,
);
});
@@ -1394,20 +1394,20 @@ test('root-fix-2: install on a corrupt-present ledger blocks BEFORE resolving so
const dir = runtime();
// Write a corrupt ledger before attempting install.
const ledgerPath = path.join(dir, '.gsd-capabilities.json');
const ledgerPath = path.join(dir, '.msd-capabilities.json');
fs.writeFileSync(ledgerPath, '{ broken json ---', 'utf8');
let resolveCalled = false;
const trackingResolve = async (spec, opts) => {
resolveCalled = true;
// Materialize a staging dir so a regression (resolve before strict read) is observable.
const root = path.join(opts.gsdHome, '.gsd', 'capabilities', '.staging');
const root = path.join(opts.msdHome, '.msd', 'capabilities', '.staging');
fs.mkdirSync(root, { recursive: true });
const staged = path.join(root, 'preflight-test');
fs.mkdirSync(staged, { recursive: true });
fs.writeFileSync(path.join(staged, 'capability.json'), JSON.stringify(
{ id: 'pf', role: 'feature', version: '1.0.0', title: 'pf', description: 'x',
tier: 'standard', requires: [], engines: { gsd: '>=1.0.0' },
tier: 'standard', requires: [], engines: { msd: '>=1.0.0' },
runtimeCompat: { supported: ['*'], unsupported: [] },
skills: [], agents: [], hooks: [], config: {}, steps: [], contributions: [], gates: [] }
), 'utf8');
@@ -1434,7 +1434,7 @@ test('root-fix-2: install on a corrupt-present ledger blocks BEFORE resolving so
'resolver must NOT be called when the ledger is corrupt (strict read precedes _resolve)');
// And because resolve never ran, NO staging dir was created.
const stagingRoot = path.join(dir, '.gsd', 'capabilities', '.staging');
const stagingRoot = path.join(dir, '.msd', 'capabilities', '.staging');
assert.ok(
!fs.existsSync(stagingRoot),
'no .staging dir may be created when the ledger is corrupt (preflight precedes staging)',
@@ -1451,19 +1451,19 @@ test('root-fix-2: upgrade on a corrupt-present ledger blocks BEFORE resolving so
});
// Then corrupt the ledger.
const ledgerPath = path.join(dir, '.gsd-capabilities.json');
const ledgerPath = path.join(dir, '.msd-capabilities.json');
fs.writeFileSync(ledgerPath, '{ broken json ---', 'utf8');
let resolveCalled = false;
const trackingResolve = async (spec, opts) => {
resolveCalled = true;
const root = path.join(opts.gsdHome, '.gsd', 'capabilities', '.staging');
const root = path.join(opts.msdHome, '.msd', 'capabilities', '.staging');
fs.mkdirSync(root, { recursive: true });
const staged = path.join(root, 'preflight-upgrade-test');
fs.mkdirSync(staged, { recursive: true });
fs.writeFileSync(path.join(staged, 'capability.json'), JSON.stringify(
{ id: 'u2', role: 'feature', version: '2.0.0', title: 'u2', description: 'x',
tier: 'standard', requires: [], engines: { gsd: '>=1.0.0' },
tier: 'standard', requires: [], engines: { msd: '>=1.0.0' },
runtimeCompat: { supported: ['*'], unsupported: [] },
skills: [], agents: [], hooks: [], config: {}, steps: [], contributions: [], gates: [] }
), 'utf8');
@@ -1472,7 +1472,7 @@ test('root-fix-2: upgrade on a corrupt-present ledger blocks BEFORE resolving so
// Snapshot the .staging dir's prior contents (the successful install above may have left none,
// but be precise): the corrupt-upgrade attempt must add NOTHING.
const stagingRoot = path.join(dir, '.gsd', 'capabilities', '.staging');
const stagingRoot = path.join(dir, '.msd', 'capabilities', '.staging');
const before = fs.existsSync(stagingRoot) ? fs.readdirSync(stagingRoot).sort() : [];
const result = await lifecycle.upgradeCapability('./u2-v2', {
@@ -1502,13 +1502,13 @@ test('root-fix-2: corrupt-ledger EXECUTABLE install WITHOUT --yes blocks on CORR
const dir = runtime();
// Write a corrupt ledger before attempting install.
const ledgerPath = path.join(dir, '.gsd-capabilities.json');
const ledgerPath = path.join(dir, '.msd-capabilities.json');
fs.writeFileSync(ledgerPath, '{ broken json ---', 'utf8');
let resolveCalled = false;
const trackingResolve = async (spec, opts) => {
resolveCalled = true;
const root = path.join(opts.gsdHome, '.gsd', 'capabilities', '.staging');
const root = path.join(opts.msdHome, '.msd', 'capabilities', '.staging');
fs.mkdirSync(root, { recursive: true });
const staged = path.join(root, 'exec-corrupt-test');
fs.mkdirSync(staged, { recursive: true });
@@ -1549,13 +1549,13 @@ test('root-fix-3: installCapability rejects unsafe id (constructor) before stagi
const result = await lifecycle.installCapability('./evil', {
runtimeDir: dir, hostVersion: '1.6.0',
_resolve: async (spec, opts) => {
const root = path.join(opts.gsdHome, '.gsd', 'capabilities', '.staging');
const root = path.join(opts.msdHome, '.msd', 'capabilities', '.staging');
fs.mkdirSync(root, { recursive: true });
const staged = path.join(root, 'unsafe-id-test');
fs.mkdirSync(staged, { recursive: true });
fs.writeFileSync(path.join(staged, 'capability.json'), JSON.stringify(
{ id: 'constructor', role: 'feature', version: '1.0.0', title: 'evil',
description: 'evil', tier: 'standard', requires: [], engines: { gsd: '>=1.0.0' },
description: 'evil', tier: 'standard', requires: [], engines: { msd: '>=1.0.0' },
runtimeCompat: { supported: ['*'], unsupported: [] },
skills: [], agents: [], hooks: [], config: {}, steps: [], contributions: [], gates: [] }
), 'utf8');
@@ -1566,10 +1566,10 @@ test('root-fix-3: installCapability rejects unsafe id (constructor) before stagi
assert.strictEqual(result.status, 'blocked',
`installCapability must block a capability with id='constructor'; got: ${result.status}`);
assert.ok(result.blockReasons && result.blockReasons.length > 0, 'must have blockReasons');
// Must NOT have installed a bundle at .gsd/capabilities/constructor
// Must NOT have installed a bundle at .msd/capabilities/constructor
assert.ok(
!fs.existsSync(path.join(dir, '.gsd', 'capabilities', 'constructor')),
'no .gsd/capabilities/constructor bundle must be promoted',
!fs.existsSync(path.join(dir, '.msd', 'capabilities', 'constructor')),
'no .msd/capabilities/constructor bundle must be promoted',
);
// Must NOT have a ledger entry for 'constructor'
const l = ledgerMod.readLedger(dir);
@@ -1584,13 +1584,13 @@ test('root-fix-3: installCapability rejects __proto__ and prototype ids', async
const res = await lifecycle.installCapability('./evil', {
runtimeDir: dir, hostVersion: '1.6.0',
_resolve: async (spec, opts) => {
const root = path.join(opts.gsdHome, '.gsd', 'capabilities', '.staging');
const root = path.join(opts.msdHome, '.msd', 'capabilities', '.staging');
fs.mkdirSync(root, { recursive: true });
const staged = path.join(root, `unsafe-${unsafeId}`);
fs.mkdirSync(staged, { recursive: true });
fs.writeFileSync(path.join(staged, 'capability.json'), JSON.stringify(
{ id: unsafeId, role: 'feature', version: '1.0.0', title: 'evil',
description: 'evil', tier: 'standard', requires: [], engines: { gsd: '>=1.0.0' },
description: 'evil', tier: 'standard', requires: [], engines: { msd: '>=1.0.0' },
runtimeCompat: { supported: ['*'], unsupported: [] },
skills: [], agents: [], hooks: [], config: {}, steps: [], contributions: [], gates: [] }
), 'utf8');
@@ -1615,13 +1615,13 @@ test('root-fix-3: upgradeCapability rejects unsafe id (constructor) before stagi
const result = await lifecycle.upgradeCapability('./evil-upgrade', {
runtimeDir: dir, hostVersion: '1.6.0',
_resolve: async (spec, opts) => {
const root = path.join(opts.gsdHome, '.gsd', 'capabilities', '.staging');
const root = path.join(opts.msdHome, '.msd', 'capabilities', '.staging');
fs.mkdirSync(root, { recursive: true });
const staged = path.join(root, 'unsafe-upgrade-test');
fs.mkdirSync(staged, { recursive: true });
fs.writeFileSync(path.join(staged, 'capability.json'), JSON.stringify(
{ id: 'constructor', role: 'feature', version: '2.0.0', title: 'evil',
description: 'evil', tier: 'standard', requires: [], engines: { gsd: '>=1.0.0' },
description: 'evil', tier: 'standard', requires: [], engines: { msd: '>=1.0.0' },
runtimeCompat: { supported: ['*'], unsupported: [] },
skills: [], agents: [], hooks: [], config: {}, steps: [], contributions: [], gates: [] }
), 'utf8');
@@ -1631,16 +1631,16 @@ test('root-fix-3: upgradeCapability rejects unsafe id (constructor) before stagi
assert.strictEqual(result.status, 'blocked',
`upgradeCapability must block unsafe id='constructor'; got: ${result.status}`);
assert.ok(!fs.existsSync(path.join(dir, '.gsd', 'capabilities', 'constructor')),
'no .gsd/capabilities/constructor bundle must be promoted on upgrade');
assert.ok(!fs.existsSync(path.join(dir, '.msd', 'capabilities', 'constructor')),
'no .msd/capabilities/constructor bundle must be promoted on upgrade');
});
// ---------------------------------------------------------------------------
// FIX 5: removeCapability failure guidance must NOT reference 'gsd capability reconcile'
// FIX 5: removeCapability failure guidance must NOT reference 'msd capability reconcile'
// (a non-existent CLI subcommand).
// ---------------------------------------------------------------------------
test('fix-5: removeCapability commit-fail guidance does not reference nonexistent "gsd capability reconcile" subcommand', async (t) => {
test('fix-5: removeCapability commit-fail guidance does not reference nonexistent "msd capability reconcile" subcommand', async (t) => {
const dir = runtime();
// Install a capability.
@@ -1653,7 +1653,7 @@ test('fix-5: removeCapability commit-fail guidance does not reference nonexisten
const { mock } = require('node:test');
const realRename = fs.renameSync.bind(fs);
const renameMock = mock.method(fs, 'renameSync', function (src, dst) {
if (typeof dst === 'string' && dst.includes('.gsd-capabilities.json')) {
if (typeof dst === 'string' && dst.includes('.msd-capabilities.json')) {
const err = new Error('EXDEV: cross-device link not permitted');
err.code = 'EXDEV';
throw err;
@@ -1667,12 +1667,12 @@ test('fix-5: removeCapability commit-fail guidance does not reference nonexisten
const reason = (result.blockReasons || []).join(' ');
assert.ok(
!reason.includes('gsd capability reconcile'),
`Failure guidance must not reference non-existent "gsd capability reconcile"; got: "${reason}"`,
!reason.includes('msd capability reconcile'),
`Failure guidance must not reference non-existent "msd capability reconcile"; got: "${reason}"`,
);
// Must still mention a useful recovery action (inspect/restore the ledger file).
assert.ok(
reason.includes('ledger') || reason.includes('.gsd-capabilities.json') || reason.includes('remove'),
reason.includes('ledger') || reason.includes('.msd-capabilities.json') || reason.includes('remove'),
`Failure guidance must mention the ledger or re-run remove; got: "${reason}"`,
);
});
@@ -1709,7 +1709,7 @@ function lockBody({ pid = process.pid, host = os.hostname(), ts = Date.now(), st
}
function writeLock(dir, body, ageMs) {
const lockPath = path.join(dir, '.gsd', 'capabilities', '.lock');
const lockPath = path.join(dir, '.msd', 'capabilities', '.lock');
fs.mkdirSync(path.dirname(lockPath), { recursive: true });
// Finding 1: age now binds to the BODY's own `ts` for a JSON body (not the file mtime). So a lock
// that is `ageMs` old must carry a `ts` that is `ageMs` in the past — backdate BOTH the body ts (for
@@ -1957,7 +1957,7 @@ test('finding-1: a JSON lock with a RECENT body `ts` but an artificially-OLD mti
const dir = runtime();
// pid "dead" so a mtime-derived STALE age would steal it; only ts-bound freshness can protect it.
withLockProbes(t, { alive: false, startTime: 'START-A' });
const lockPath = path.join(dir, '.gsd', 'capabilities', '.lock');
const lockPath = path.join(dir, '.msd', 'capabilities', '.lock');
fs.mkdirSync(path.dirname(lockPath), { recursive: true });
// Body `ts` is NOW (fresh replacement); file mtime is backdated 2 minutes (would look stale).
const freshBody = JSON.stringify({ token: `${process.pid}-${Date.now()}-1`, pid: process.pid, hostname: os.hostname(), startTime: 'START-A', ts: Date.now() });
@@ -1992,7 +1992,7 @@ test('finding-1: an inode change between the steal-decision and the rename cause
const dir = runtime();
const { mock } = require('node:test');
withLockProbes(t, { alive: false, startTime: 'START-A' }); // dead pid → otherwise steal-eligible
const lockPath = path.join(dir, '.gsd', 'capabilities', '.lock');
const lockPath = path.join(dir, '.msd', 'capabilities', '.lock');
fs.mkdirSync(path.dirname(lockPath), { recursive: true });
// Stale legacy body (no ts → mtime age), backdated past the deadman so the steal branch is reached.
const body = 'legacy-token-no-pid';
@@ -2044,7 +2044,7 @@ test('finding-1: a body `ts` change between the steal-decision and the rename ca
const dir = runtime();
const { mock } = require('node:test');
withLockProbes(t, { alive: false, startTime: 'START-A' }); // dead pid → steal-eligible if stale
const lockPath = path.join(dir, '.gsd', 'capabilities', '.lock');
const lockPath = path.join(dir, '.msd', 'capabilities', '.lock');
fs.mkdirSync(path.dirname(lockPath), { recursive: true });
// DECISION body: OLD ts (stale → steal-eligible with a dead pid). RECHECK body: fresh ts (B's swap).
const oldTs = Date.now() - 2 * 60 * 1000;
@@ -2122,7 +2122,7 @@ test('finding-3: a body `ts` going NULL between the steal-decision and the renam
const dir = runtime();
const { mock } = require('node:test');
withLockProbes(t, { alive: false, startTime: 'START-A' }); // dead pid → steal-eligible if stale
const lockPath = path.join(dir, '.gsd', 'capabilities', '.lock');
const lockPath = path.join(dir, '.msd', 'capabilities', '.lock');
fs.mkdirSync(path.dirname(lockPath), { recursive: true });
// DECISION body: a JSON body with a non-null OLD ts (stale → steal-eligible with a dead pid).
// RECHECK body: a no-`ts` legacy/garbage body on the SAME inode (B replaced the body content).
@@ -2199,7 +2199,7 @@ test('finding-3: a body `ts` going NULL between the steal-decision and the renam
test('finding-2: a FIFO lock body does NOT hang acquireLock — treated as unparseable (deadman policy)', (t) => {
const dir = runtime();
withLockProbes(t, { alive: true, startTime: 'START-A' });
const lockPath = path.join(dir, '.gsd', 'capabilities', '.lock');
const lockPath = path.join(dir, '.msd', 'capabilities', '.lock');
fs.mkdirSync(path.dirname(lockPath), { recursive: true });
if (!tryMkfifoLife(lockPath)) { t.skip('mkfifo unavailable on this platform'); return; }
const old = new Date(Date.now() - 2 * 60 * 1000); // stale, under the deadman
@@ -2227,7 +2227,7 @@ test('finding-2: a FIFO lock body does NOT hang acquireLock — treated as unpar
test('finding-3/2: a body-write (writeFileSync) failure after the exclusive create leaves NO orphan .lock behind', (t) => {
const dir = runtime();
const { mock } = require('node:test');
const lockPath = path.join(dir, '.gsd', 'capabilities', '.lock');
const lockPath = path.join(dir, '.msd', 'capabilities', '.lock');
fs.mkdirSync(path.dirname(lockPath), { recursive: true });
// Let the exclusive create succeed (real openSync), then force the body write to fail. The body MUST
@@ -2259,7 +2259,7 @@ test('finding-3/2: a body-write (writeFileSync) failure after the exclusive crea
test('finding-3: a closeSync failure after the exclusive create+write leaves NO orphan .lock behind', (t) => {
const dir = runtime();
const { mock } = require('node:test');
const lockPath = path.join(dir, '.gsd', 'capabilities', '.lock');
const lockPath = path.join(dir, '.msd', 'capabilities', '.lock');
fs.mkdirSync(path.dirname(lockPath), { recursive: true });
// Let the exclusive create AND the body write succeed; force ONLY the .lock fd's closeSync to fail.
@@ -2303,7 +2303,7 @@ test('finding-1: a lock whose JSON body `ts` is far in the FUTURE is still recla
// Different host + past the deadman by MTIME so the deadman branch reclaims it once the future ts is
// distrusted. (A future ts under the trusting code would compute a negative age → never stale.)
withLockProbes(t, { alive: true, startTime: 'START-A' });
const lockPath = path.join(dir, '.gsd', 'capabilities', '.lock');
const lockPath = path.join(dir, '.msd', 'capabilities', '.lock');
fs.mkdirSync(path.dirname(lockPath), { recursive: true });
// Body ts is 1 hour in the FUTURE; file mtime is backdated 11 min (past the deadman).
const futureBody = JSON.stringify({
@@ -2340,7 +2340,7 @@ test('finding-1: a lock whose JSON body `ts` is far in the FUTURE is still recla
// token re-check (body token != handle.token) is what prevents the delete.
test('finding-4: releaseLock does NOT delete a SUCCESSOR lock (different token) at the same path', () => {
const dir = runtime();
const lockPath = path.join(dir, '.gsd', 'capabilities', '.lock');
const lockPath = path.join(dir, '.msd', 'capabilities', '.lock');
// The original holder acquires (captures its token + dev/ino in the handle).
const handle = lifecycle.acquireLock(dir);
@@ -2369,7 +2369,7 @@ test('finding-4: releaseLock does NOT delete a SUCCESSOR lock (different token)
// over-strict (the dev/ino captured at acquire matches the unchanged on-disk lock → rmSync runs).
test('finding-4: releaseLock STILL deletes our own unchanged lock (inode guard is not over-strict)', () => {
const dir = runtime();
const lockPath = path.join(dir, '.gsd', 'capabilities', '.lock');
const lockPath = path.join(dir, '.msd', 'capabilities', '.lock');
const handle = lifecycle.acquireLock(dir);
assert.ok(handle, 'must acquire');
assert.ok(fs.existsSync(lockPath), 'lock present before release');
@@ -2389,7 +2389,7 @@ test('finding-4: releaseLock STILL deletes our own unchanged lock (inode guard i
test('CONC-2: acquireLock returns null on contention exhaustion WITHOUT a stack overflow (bounded loop)', (t) => {
const dir = runtime();
const { mock } = require('node:test');
const lockPath = path.join(dir, '.gsd', 'capabilities', '.lock');
const lockPath = path.join(dir, '.msd', 'capabilities', '.lock');
fs.mkdirSync(path.dirname(lockPath), { recursive: true });
// TV-06/07/11/17: a FAITHFUL pathological-contention sim. Write a REAL, stale, DEAD-pid JSON lock body
@@ -2573,7 +2573,7 @@ test('DUR-3: promoteStagingToFinal fsyncs the parent directory after BOTH rename
_resolve: fakeResolve(declarativeCap('e', '1.0.0')),
});
const capsRoot = path.join(dir, '.gsd', 'capabilities');
const capsRoot = path.join(dir, '.msd', 'capabilities');
const { mock } = require('node:test');
const realOpen = fs.openSync.bind(fs);
const realFsync = fs.fsyncSync.bind(fs);
@@ -2646,7 +2646,7 @@ function withLifecycleDirFsyncError(t, capsRoot, errno) {
// this "must be blocked" assertion fails.
test('finding-4: a NON-tolerated dir-fsync errno (EIO) during install promotion surfaces as blocked (durability not silently claimed)', async (t) => {
const dir = runtime();
const capsRoot = path.join(dir, '.gsd', 'capabilities');
const capsRoot = path.join(dir, '.msd', 'capabilities');
fs.mkdirSync(capsRoot, { recursive: true });
const { closed } = withLifecycleDirFsyncError(t, capsRoot, 'EIO');
@@ -2665,7 +2665,7 @@ test('finding-4: a NON-tolerated dir-fsync errno (EIO) during install promotion
// would block the install, so this 'installed' assertion fails.
test('finding-4: a TOLERATED dir-fsync errno (EISDIR) during install promotion is ignored (install succeeds)', async (t) => {
const dir = runtime();
const capsRoot = path.join(dir, '.gsd', 'capabilities');
const capsRoot = path.join(dir, '.msd', 'capabilities');
fs.mkdirSync(capsRoot, { recursive: true });
const { closed } = withLifecycleDirFsyncError(t, capsRoot, 'EISDIR');
@@ -2702,14 +2702,14 @@ test('DUR-6: reconcile upgrade-rollback renames backup→final atomically (no rm
// order is restored, finalDir is rmSync'd first; the new order must rename the backup over
// finalDir directly (renameSync replaces atomically), so no rmSync of finalDir occurs.
const { mock } = require('node:test');
const finalDir = path.join(dir, '.gsd', 'capabilities', 'c');
const finalDir = path.join(dir, '.msd', 'capabilities', 'c');
const realRm = fs.rmSync.bind(fs);
const realRename = fs.renameSync.bind(fs); // capture BEFORE mocking
let finalDirRmBeforeRename = false;
let backupRenamedToFinal = false;
const renameMock = mock.method(fs, 'renameSync', function (src, dst, ...rest) {
if (typeof src === 'string' && typeof dst === 'string'
&& path.resolve(src) === path.resolve(path.join(dir, '.gsd', 'capabilities', backupName))
&& path.resolve(src) === path.resolve(path.join(dir, '.msd', 'capabilities', backupName))
&& path.resolve(dst) === path.resolve(finalDir)) {
backupRenamedToFinal = true;
}
@@ -2804,7 +2804,7 @@ test('W-6: a throwing per-entry mutation does not abort reconcile (warns and con
const { mock } = require('node:test');
const realExists = fs.existsSync.bind(fs);
const badBackupPath = path.join(dir, '.gsd', 'capabilities', badBackup);
const badBackupPath = path.join(dir, '.msd', 'capabilities', badBackup);
// existsSync(backupDir) is a direct per-entry call in reconcile's step-1 loop (outside the inner
// restore try/catch) — making it throw for bad-cap exercises the W-6 per-entry catch.
const existsMock = mock.method(fs, 'existsSync', function (p) {
@@ -2833,13 +2833,13 @@ test('W-6: a throwing per-entry mutation does not abort reconcile (warns and con
});
// ---------------------------------------------------------------------------
// W-3 / DUR-5 (LOW): reconcile step-2 must sweep stale `.gsd-capabilities.json.tmp.*`
// W-3 / DUR-5 (LOW): reconcile step-2 must sweep stale `.msd-capabilities.json.tmp.*`
// orphan temp files (older than a threshold) from the runtime dir.
// Revert-fails: remove the stale-temp sweep → the old orphan temp file remains
// after reconcile, so the "orphan removed" assertion fails.
// ---------------------------------------------------------------------------
test('W-3/DUR-5: reconcile sweeps a stale .gsd-capabilities.json.tmp.* orphan from the runtime dir', () => {
test('W-3/DUR-5: reconcile sweeps a stale .msd-capabilities.json.tmp.* orphan from the runtime dir', () => {
const dir = runtime();
fs.mkdirSync(dir, { recursive: true });
// A committed, valid ledger so reconcile proceeds past the corruption preflight.
@@ -2863,8 +2863,8 @@ test('W-3/DUR-5: reconcile sweeps a stale .gsd-capabilities.json.tmp.* orphan fr
// Consent store binding on project install / upgrade / remove (#1459)
// ---------------------------------------------------------------------------
const consentMod = require('../gsd-core/bin/lib/capability-consent.cjs');
const trustMod = require('../gsd-core/bin/lib/capability-trust.cjs');
const consentMod = require('../msd-core/bin/lib/capability-consent.cjs');
const trustMod = require('../msd-core/bin/lib/capability-trust.cjs');
/** A consent home OUTSIDE the project tree (user-owned). */
function consentHome() {
@@ -2875,11 +2875,11 @@ function consentHome() {
/**
* #1459 CB-1/CB-2: the SECURITY binding `hasProjectConsent` checks is the RECOMPUTED full-bundle
* content hash over the INSTALLED capDir (`<runtimeDir>/.gsd/capabilities/<id>`) — exactly what the
* content hash over the INSTALLED capDir (`<runtimeDir>/.msd/capabilities/<id>`) — exactly what the
* loader recomputes at load. Tests assert consent presence by recomputing the same hash here.
*/
function installedCapDir(runtimeDir, id) {
return path.join(runtimeDir, '.gsd', 'capabilities', id);
return path.join(runtimeDir, '.msd', 'capabilities', id);
}
function installedContentHash(runtimeDir, id) {
return consentMod.bundleContentHash(installedCapDir(runtimeDir, id));
@@ -2900,7 +2900,7 @@ test('install (project scope, consented): writes a consent record under the cons
// manifest while consent still matched; the contentHash binds the whole bundle.
assert.strictEqual(
consentMod.hasProjectConsent({
gsdHome: home, projectRoot: dir, id: 'proj-decl',
msdHome: home, projectRoot: dir, id: 'proj-decl',
contentHash: installedContentHash(dir, 'proj-decl'),
}),
true,
@@ -2908,7 +2908,7 @@ test('install (project scope, consented): writes a consent record under the cons
);
// It is under the consent HOME, not under the project runtimeDir.
assert.ok(fs.existsSync(consentMod.consentStorePath(home)), 'store under consent home');
assert.ok(!fs.existsSync(path.join(dir, '.gsd', 'consent.json')), 'NOT written inside the project');
assert.ok(!fs.existsSync(path.join(dir, '.msd', 'consent.json')), 'NOT written inside the project');
});
test('install (project scope, executable + consented): consent record matches the executable disclosure signature', async () => {
@@ -2926,7 +2926,7 @@ test('install (project scope, executable + consented): consent record matches th
// deactivates (loader test). The stored disclosureSignature (incl. env) remains for the UX layer.
assert.strictEqual(
consentMod.hasProjectConsent({
gsdHome: home, projectRoot: dir, id: 'proj-exec',
msdHome: home, projectRoot: dir, id: 'proj-exec',
contentHash: installedContentHash(dir, 'proj-exec'),
}),
true,
@@ -2961,14 +2961,14 @@ test('remove (project scope): revokes the consent record', async () => {
});
const rmHash = installedContentHash(dir, 'proj-rm');
assert.strictEqual(
consentMod.hasProjectConsent({ gsdHome: home, projectRoot: dir, id: 'proj-rm', contentHash: rmHash }),
consentMod.hasProjectConsent({ msdHome: home, projectRoot: dir, id: 'proj-rm', contentHash: rmHash }),
true,
'consent present after install',
);
const rm = lifecycle.removeCapability('proj-rm', { runtimeDir: dir, scope: 'project', consentStoreDir: home });
assert.strictEqual(rm.status, 'removed');
assert.strictEqual(
consentMod.hasProjectConsent({ gsdHome: home, projectRoot: dir, id: 'proj-rm', contentHash: rmHash }),
consentMod.hasProjectConsent({ msdHome: home, projectRoot: dir, id: 'proj-rm', contentHash: rmHash }),
false,
'remove fully revokes the consent record',
);
@@ -2978,7 +2978,7 @@ test('remove (project scope): revokes the consent record', async () => {
// (round-3). removeCapability must NOT silently swallow that throw and still report a clean
// 'removed' — that leaves a STALE consent record a byte-identical re-drop + forged ledger could
// reactivate against. The revoke failure must be SURFACED (a stderr warning naming the record AND
// a flag in the returned result) so the user knows to clear it (`gsd capability trust revoke`).
// a flag in the returned result) so the user knows to clear it (`msd capability trust revoke`).
function plantFreshConsentLockLife(home) {
const lockPath = consentMod.consentLockPath(home);
fs.mkdirSync(path.dirname(lockPath), { recursive: true });
@@ -3004,7 +3004,7 @@ test('remove (project scope): a revoke-on-lock-failure is SURFACED, not swallowe
});
const rmHash = installedContentHash(dir, 'proj-rm-lk');
assert.strictEqual(
consentMod.hasProjectConsent({ gsdHome: home, projectRoot: dir, id: 'proj-rm-lk', contentHash: rmHash }),
consentMod.hasProjectConsent({ msdHome: home, projectRoot: dir, id: 'proj-rm-lk', contentHash: rmHash }),
true, 'consent present after install',
);
// Hold the consent-store lock so the revoke inside remove cannot acquire it → revokeProjectConsent throws.
@@ -3024,7 +3024,7 @@ test('remove (project scope): a revoke-on-lock-failure is SURFACED, not swallowe
);
// The consent record is STALE (could not be revoked) — confirming the failure was real, not a no-op.
assert.strictEqual(
consentMod.hasProjectConsent({ gsdHome: home, projectRoot: dir, id: 'proj-rm-lk', contentHash: rmHash }),
consentMod.hasProjectConsent({ msdHome: home, projectRoot: dir, id: 'proj-rm-lk', contentHash: rmHash }),
true, 'the consent record is left STALE (revoke was blocked by the held lock) — the user must clear it',
);
});
@@ -3052,7 +3052,7 @@ test('upgrade (project scope, consented): re-records the consent for the new ver
const v2Hash = installedContentHash(dir, 'proj-up');
assert.notStrictEqual(v1Hash, v2Hash, 'precondition: the v1 and v2 bundles hash differently');
assert.strictEqual(
consentMod.hasProjectConsent({ gsdHome: home, projectRoot: dir, id: 'proj-up', contentHash: v2Hash }),
consentMod.hasProjectConsent({ msdHome: home, projectRoot: dir, id: 'proj-up', contentHash: v2Hash }),
true,
'consent re-recorded against the upgraded bundle content hash',
);
@@ -3060,7 +3060,7 @@ test('upgrade (project scope, consented): re-records the consent for the new ver
// OLD version may linger. revert-fails: if the upgrade ADDED a new record instead of overwriting (or
// left the v1 binding around), the store would carry 2 records and/or the OLD hash would still match.
assert.strictEqual(
consentMod.hasProjectConsent({ gsdHome: home, projectRoot: dir, id: 'proj-up', contentHash: v1Hash }),
consentMod.hasProjectConsent({ msdHome: home, projectRoot: dir, id: 'proj-up', contentHash: v1Hash }),
false,
'the OLD-version content hash no longer matches (no stale consent record)',
);
@@ -3073,7 +3073,7 @@ test('upgrade (project scope, consented): re-records the consent for the new ver
// looks up via consentProjectRoot = realpath(findProjectRoot(cwd))) finds it from any descendant.
// ---------------------------------------------------------------------------
const { loadRegistry } = require('../gsd-core/bin/lib/capability-loader.cjs');
const { loadRegistry } = require('../msd-core/bin/lib/capability-loader.cjs');
test('D (IC-01/CB-4): install --scope project from a SUBDIR → cap is ACTIVE (record key matches loader lookup)', async () => {
// revert-fails: if the RECORD site bound consent to realpath(subdir) and the loader looked it up at
@@ -3097,7 +3097,7 @@ test('D (IC-01/CB-4): install --scope project from a SUBDIR → cap is ACTIVE (r
assert.strictEqual(res.status, 'installed');
// Load the registry FROM the nested subdir, pointing the consent home at the same store. The loader
// resolves the project root (findProjectRoot finds the .planning/ marker) and finds the record.
const reg = loadRegistry({ includeInstalled: true, gsdHome: home, cwd: subdir, hostVersion: '1.6.0' });
const reg = loadRegistry({ includeInstalled: true, msdHome: home, cwd: subdir, hostVersion: '1.6.0' });
assert.ok(reg.capabilities && reg.capabilities['subdir-cap'], 'cap ACTIVE when loaded from a subdir (no install-then-inactive)');
assert.strictEqual(reg.bySkill['subdir-skill'], 'subdir-cap', 'skill surface present from the subdir');
});
@@ -3126,35 +3126,35 @@ test('IC-03: reconcile rollback of a deleted project bundle revokes consent →
assert.strictEqual(installed.status, 'installed');
const consentedHash = installedContentHash(dir, 'redrop-cap');
assert.strictEqual(
consentMod.hasProjectConsent({ gsdHome: home, projectRoot: dir, id: 'redrop-cap', contentHash: consentedHash }),
consentMod.hasProjectConsent({ msdHome: home, projectRoot: dir, id: 'redrop-cap', contentHash: consentedHash }),
true, 'consent present after install',
);
// 2. Simulate a crashed/interrupted state: mark the entry as an in-flight (uncommitted) install and
// delete its on-disk bundle dir. reconcile's install-rollback path then drops the entry.
recordPending(dir, 'redrop-cap', '1.0.0', { kind: 'install', backupName: null, sharedFiles: [] });
cleanup(path.join(dir, '.gsd', 'capabilities', 'redrop-cap')); // delete the on-disk bundle dir (helpers.cleanup: Windows-EBUSY retry budget)
cleanup(path.join(dir, '.msd', 'capabilities', 'redrop-cap')); // delete the on-disk bundle dir (helpers.cleanup: Windows-EBUSY retry budget)
// 3. Reconcile WITH the consent context — the rollback must revoke the stale consent.
const report = lifecycle.reconcileCapabilities({ runtimeDir: dir, scope: 'project', consentStoreDir: home });
assert.ok(report.rolledBack.includes('redrop-cap'), 'the deleted-bundle entry is rolled back');
assert.strictEqual(
consentMod.hasProjectConsent({ gsdHome: home, projectRoot: dir, id: 'redrop-cap', contentHash: consentedHash }),
consentMod.hasProjectConsent({ msdHome: home, projectRoot: dir, id: 'redrop-cap', contentHash: consentedHash }),
false, 'reconcile rollback revoked the now-stale consent record',
);
// 4. Re-drop the BYTE-IDENTICAL bundle + a committed (forged) project ledger — no new consent.
const reDir = path.join(dir, '.gsd', 'capabilities', 'redrop-cap');
const reDir = path.join(dir, '.msd', 'capabilities', 'redrop-cap');
fs.mkdirSync(reDir, { recursive: true });
fs.writeFileSync(path.join(reDir, 'capability.json'), JSON.stringify(cap), 'utf8');
assert.strictEqual(consentMod.bundleContentHash(reDir), consentedHash, 'precondition: the re-drop is byte-identical (same hash)');
fs.writeFileSync(path.join(dir, '.gsd-capabilities.json'), JSON.stringify({
fs.writeFileSync(path.join(dir, '.msd-capabilities.json'), JSON.stringify({
version: '1', updatedAt: '2026-01-01T00:00:00Z',
entries: { 'redrop-cap': { id: 'redrop-cap', version: '1.0.0', source: 's', integrity: '', files: [], sharedEdits: [] } },
}), 'utf8');
// 5. The loader must NOT re-activate the identical re-drop — consent was revoked.
const reg = loadRegistry({ includeInstalled: true, gsdHome: home, cwd: dir, hostVersion: '1.6.0' });
const reg = loadRegistry({ includeInstalled: true, msdHome: home, cwd: dir, hostVersion: '1.6.0' });
assert.ok(reg.capabilities['redrop-cap'] === undefined, 'an identical re-drop stays INACTIVE after the rollback revoked consent');
});
@@ -3216,7 +3216,7 @@ test('IC-05/WIN-2: a consent-store write failure leaves the install status:insta
}
void realRecord;
assert.strictEqual(res.status, 'installed', 'a consent-store IO error must NOT fail an otherwise-successful install');
assert.ok(fs.existsSync(path.join(dir, '.gsd', 'capabilities', 'rofs-cap', 'capability.json')), 'the bundle is committed on disk');
assert.ok(fs.existsSync(path.join(dir, '.msd', 'capabilities', 'rofs-cap', 'capability.json')), 'the bundle is committed on disk');
assert.match(buf, /capability consent:/i, 'a consent diagnostic was written to stderr');
assert.match(buf, /could not write the consent record/i, 'the warning explains the write failure');
assert.match(buf, new RegExp(escapeRegex(home)), 'the warning names the consent store path');
@@ -3230,7 +3230,7 @@ test('IC-05/WIN-2: a consent-store write failure leaves the install status:insta
function plantEntry(dir, id, version, source) {
ledgerMod.recordInstall(dir, {
id, version, source, integrity: '',
files: [`.gsd/capabilities/${id}`], sharedEdits: [],
files: [`.msd/capabilities/${id}`], sharedEdits: [],
});
}
/** A git ls-remote --tags line for a tag. */
@@ -3298,7 +3298,7 @@ test('#1463 outdated: npm newer → outdated; npm peek error → unknown, other
test('#1463 outdated: tarball → manual; registry → unknown', () => {
const dir = runtime();
plantEntry(dir, 'tarcap', '1.0.0', 'https://host/path/cap-1.0.0.tgz');
plantEntry(dir, 'regcap', '1.0.0', 'my-cap@gsd-registry');
plantEntry(dir, 'regcap', '1.0.0', 'my-cap@msd-registry');
const recs = lifecycle.outdatedCapabilities({ runtimeDir: dir });
const byId = Object.fromEntries(recs.map((r) => [r.id, r]));
assert.strictEqual(byId.tarcap.status, 'manual');