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

@@ -2,7 +2,7 @@
/**
* Tests for the user-owned capability CONSENT STORE — issue #1459 (capability trust model
* bypassable). The consent store lives OUTSIDE any repo, at ${GSD_HOME||homedir()}/.gsd/consent.json,
* bypassable). The consent store lives OUTSIDE any repo, at ${MSD_HOME||homedir()}/.msd/consent.json,
* and binds each project-scope third-party capability activation to a user decision made on THIS
* machine. A forged/cloned project ledger can no longer activate anything — activation requires a
* matching consent record the user wrote here.
@@ -12,7 +12,7 @@
* hook script, or an empty-integrity local install all change the hash and fail to match. `integrity`
* and `disclosureSignature` are kept on the record for the disclosure/re-consent UX, NOT the binding.
*
* Covers: path resolution (GSD_HOME honored, never under a repo), bundleContentHash (deterministic,
* Covers: path resolution (MSD_HOME honored, never under a repo), bundleContentHash (deterministic,
* tamper-sensitive, symlink/non-regular rejected, bounded), non-throwing bounded read, prototype-
* pollution-safe keys, atomic round-trip, the contentHash match, revoke, concurrency (CONSENT-
* CONCURRENCY-1), MAX_RECORDS at WRITE (CONSENT-MAXRECORDS-WRITE-1), and the WIN-3 space-boundary
@@ -29,7 +29,7 @@ const crypto = require('node:crypto');
const { cleanup } = require('./helpers.cjs');
const { runHook } = require('./helpers/process-seam.cjs');
const { throwIfFailed } = require('./helpers/git-fixture.cjs');
const consent = require('../gsd-core/bin/lib/capability-consent.cjs');
const consent = require('../msd-core/bin/lib/capability-consent.cjs');
// #3145: class-norm timeout, not a per-suite value — see helpers/timeouts.cjs.
const { PROBE_TIMEOUT_MS } = require('./helpers/timeouts.cjs');
@@ -66,34 +66,34 @@ function makeBundle(opts) {
// consentStorePath
// ---------------------------------------------------------------------------
test('consentStorePath: honors an explicit gsdHome (store under <home>/.gsd/consent.json)', () => {
test('consentStorePath: honors an explicit msdHome (store under <home>/.msd/consent.json)', () => {
const home = tmpDir();
try {
assert.strictEqual(consent.consentStorePath(home), path.join(home, '.gsd', 'consent.json'));
assert.strictEqual(consent.consentStorePath(home), path.join(home, '.msd', 'consent.json'));
} finally {
cleanup(home);
}
});
test('consentStorePath: honors GSD_HOME env when no arg is given', () => {
test('consentStorePath: honors MSD_HOME env when no arg is given', () => {
const home = tmpDir();
const prev = process.env.GSD_HOME;
const prev = process.env.MSD_HOME;
try {
process.env.GSD_HOME = home;
assert.strictEqual(consent.consentStorePath(), path.join(home, '.gsd', 'consent.json'));
process.env.MSD_HOME = home;
assert.strictEqual(consent.consentStorePath(), path.join(home, '.msd', 'consent.json'));
} finally {
if (prev === undefined) delete process.env.GSD_HOME; else process.env.GSD_HOME = prev;
if (prev === undefined) delete process.env.MSD_HOME; else process.env.MSD_HOME = prev;
cleanup(home);
}
});
test('consentStorePath: falls back to homedir() when neither arg nor GSD_HOME is set', () => {
const prev = process.env.GSD_HOME;
test('consentStorePath: falls back to homedir() when neither arg nor MSD_HOME is set', () => {
const prev = process.env.MSD_HOME;
try {
delete process.env.GSD_HOME;
assert.strictEqual(consent.consentStorePath(), path.join(os.homedir(), '.gsd', 'consent.json'));
delete process.env.MSD_HOME;
assert.strictEqual(consent.consentStorePath(), path.join(os.homedir(), '.msd', 'consent.json'));
} finally {
if (prev === undefined) delete process.env.GSD_HOME; else process.env.GSD_HOME = prev;
if (prev === undefined) delete process.env.MSD_HOME; else process.env.MSD_HOME = prev;
}
});
@@ -375,7 +375,7 @@ test('bundleContentHash (finding 4): two files whose NAMES differ only in invali
// This requires a filesystem that PERMITS arbitrary (invalid-UTF-8) filename bytes. Linux ext4/tmpfs
// do; macOS APFS/HFS+ REJECT illegal byte sequences at create time (EILSEQ). When the fs refuses the
// create, the vulnerable path is unreachable on this fs — skip rather than fail (the defect is fs-
// observable only where such filenames can exist; gsd-test's Linux docker leg covers it).
// observable only where such filenames can exist; msd-test's Linux docker leg covers it).
const dirA = fs.realpathSync(tmpDir('cap-consent-pathA-'));
const dirB = fs.realpathSync(tmpDir('cap-consent-pathB-'));
try {
@@ -426,7 +426,7 @@ test('readConsentStore: missing store returns an empty records map (non-throwing
test('readConsentStore: corrupt JSON returns an empty records map (non-throwing)', () => {
const home = tmpDir();
try {
fs.mkdirSync(path.join(home, '.gsd'), { recursive: true });
fs.mkdirSync(path.join(home, '.msd'), { recursive: true });
fs.writeFileSync(consent.consentStorePath(home), '{ not valid json', 'utf8');
assert.deepStrictEqual(consent.readConsentStore(home), { records: {} });
} finally {
@@ -437,7 +437,7 @@ test('readConsentStore: corrupt JSON returns an empty records map (non-throwing)
test('readConsentStore: wrong-shape store (records not an object) returns an empty map', () => {
const home = tmpDir();
try {
fs.mkdirSync(path.join(home, '.gsd'), { recursive: true });
fs.mkdirSync(path.join(home, '.msd'), { recursive: true });
fs.writeFileSync(consent.consentStorePath(home), JSON.stringify({ version: '1', records: [1, 2, 3] }), 'utf8');
assert.deepStrictEqual(consent.readConsentStore(home), { records: {} });
} finally {
@@ -448,7 +448,7 @@ test('readConsentStore: wrong-shape store (records not an object) returns an emp
test('readConsentStore: a record missing contentHash is dropped (fail closed)', () => {
const home = tmpDir();
try {
fs.mkdirSync(path.join(home, '.gsd'), { recursive: true });
fs.mkdirSync(path.join(home, '.msd'), { recursive: true });
// A legacy/tampered record with no contentHash binding must be treated as invalid.
const onDisk = { version: '1', records: { '{"r":"/p","i":"cap"}': { projectRoot: '/p', id: 'cap', scope: 'project', integrity: 'i', disclosureSignature: 's', consentedAt: '2026-01-01T00:00:00Z' } } };
fs.writeFileSync(consent.consentStorePath(home), JSON.stringify(onDisk), 'utf8');
@@ -461,7 +461,7 @@ test('readConsentStore: a record missing contentHash is dropped (fail closed)',
test('readConsentStore: oversized store is refused (returns empty), never read whole', () => {
const home = tmpDir();
try {
fs.mkdirSync(path.join(home, '.gsd'), { recursive: true });
fs.mkdirSync(path.join(home, '.msd'), { recursive: true });
const big = '{"version":"1","records":{}' + ' '.repeat(16 * 1024 * 1024) + '}';
fs.writeFileSync(consent.consentStorePath(home), big, 'utf8');
assert.deepStrictEqual(consent.readConsentStore(home), { records: {} });
@@ -492,8 +492,8 @@ test('TV-12: a store of EXACTLY CONSENT_MAX_BYTES is accepted (parsed); MAX+1 is
const homeAccept = tmpDir();
const homeRefuse = tmpDir();
try {
fs.mkdirSync(path.join(homeAccept, '.gsd'), { recursive: true });
fs.mkdirSync(path.join(homeRefuse, '.gsd'), { recursive: true });
fs.mkdirSync(path.join(homeAccept, '.msd'), { recursive: true });
fs.mkdirSync(path.join(homeRefuse, '.msd'), { recursive: true });
const atMax = consentStoreOfExactBytes(CONSENT_MAX_BYTES);
assert.strictEqual(Buffer.byteLength(atMax, 'utf8'), CONSENT_MAX_BYTES, 'precondition: exactly MAX bytes');
fs.writeFileSync(consent.consentStorePath(homeAccept), atMax, 'utf8');
@@ -513,7 +513,7 @@ test('TV-12: a store of EXACTLY CONSENT_MAX_BYTES is accepted (parsed); MAX+1 is
test('readConsentStore: a FIFO at the store path does not block; returns empty', { skip: process.platform === 'win32' }, () => {
const home = tmpDir();
try {
fs.mkdirSync(path.join(home, '.gsd'), { recursive: true });
fs.mkdirSync(path.join(home, '.msd'), { recursive: true });
mkfifo(consent.consentStorePath(home));
assert.deepStrictEqual(consent.readConsentStore(home), { records: {} });
} finally {
@@ -524,7 +524,7 @@ test('readConsentStore: a FIFO at the store path does not block; returns empty',
test('readConsentStore: caps the number of records (a hostile store with too many is refused)', () => {
const home = tmpDir();
try {
fs.mkdirSync(path.join(home, '.gsd'), { recursive: true });
fs.mkdirSync(path.join(home, '.msd'), { recursive: true });
const records = {};
for (let i = 0; i < 5000; i++) {
records[`{"r":"/p${i}","i":"cap${i}"}`] = { projectRoot: `/p${i}`, id: `cap${i}`, scope: 'project', integrity: 'i', disclosureSignature: 's', contentHash: 'sha512-x', consentedAt: '2026-01-01T00:00:00Z' };
@@ -539,7 +539,7 @@ test('readConsentStore: caps the number of records (a hostile store with too man
// Build a store on disk with exactly `n` valid records (distinct kebab ids + roots).
function seedStoreWithRecords(home, n) {
fs.mkdirSync(path.join(home, '.gsd'), { recursive: true });
fs.mkdirSync(path.join(home, '.msd'), { recursive: true });
const records = {};
for (let i = 0; i < n; i++) {
records[`{"r":"/p${i}","i":"cap-${i}"}`] = { projectRoot: `/p${i}`, id: `cap-${i}`, scope: 'project', integrity: 'i', disclosureSignature: 's', contentHash: 'sha512-x', consentedAt: '2026-01-01T00:00:00Z' };
@@ -573,9 +573,9 @@ test('record then has: a recorded consent matches on the EXACT contentHash', ()
const home = tmpDir();
const projectRoot = realProject();
try {
consent.recordProjectConsent({ gsdHome: home, projectRoot, id: 'deploy-gate', integrity: 'sha512-abc', disclosureSignature: 'sig-1', contentHash: 'sha512-bundle-1' });
consent.recordProjectConsent({ msdHome: home, projectRoot, id: 'deploy-gate', integrity: 'sha512-abc', disclosureSignature: 'sig-1', contentHash: 'sha512-bundle-1' });
assert.strictEqual(
consent.hasProjectConsent({ gsdHome: home, projectRoot, id: 'deploy-gate', contentHash: 'sha512-bundle-1' }),
consent.hasProjectConsent({ msdHome: home, projectRoot, id: 'deploy-gate', contentHash: 'sha512-bundle-1' }),
true,
);
} finally {
@@ -590,7 +590,7 @@ test('record requires a non-empty contentHash (the security binding) — throws
const home = tmpDir();
const projectRoot = realProject();
try {
assert.throws(() => consent.recordProjectConsent({ gsdHome: home, projectRoot, id: 'cap', integrity: 'i', disclosureSignature: 's', contentHash: '' }), /contentHash/);
assert.throws(() => consent.recordProjectConsent({ msdHome: home, projectRoot, id: 'cap', integrity: 'i', disclosureSignature: 's', contentHash: '' }), /contentHash/);
assert.deepStrictEqual(consent.readConsentStore(home), { records: {} }, 'nothing written');
} finally {
cleanup(home);
@@ -598,13 +598,13 @@ test('record requires a non-empty contentHash (the security binding) — throws
}
});
test('record writes a well-formed record + lands the store under GSD_HOME (never the project)', () => {
test('record writes a well-formed record + lands the store under MSD_HOME (never the project)', () => {
const home = tmpDir();
const projectRoot = realProject();
try {
consent.recordProjectConsent({ gsdHome: home, projectRoot, id: 'deploy-gate', integrity: 'sha512-abc', disclosureSignature: 'sig-1', contentHash: 'sha512-bundle-1' });
assert.ok(fs.existsSync(consent.consentStorePath(home)), 'store written under GSD_HOME');
assert.ok(!fs.existsSync(path.join(projectRoot, '.gsd', 'consent.json')), 'NOT written under the project root');
consent.recordProjectConsent({ msdHome: home, projectRoot, id: 'deploy-gate', integrity: 'sha512-abc', disclosureSignature: 'sig-1', contentHash: 'sha512-bundle-1' });
assert.ok(fs.existsSync(consent.consentStorePath(home)), 'store written under MSD_HOME');
assert.ok(!fs.existsSync(path.join(projectRoot, '.msd', 'consent.json')), 'NOT written under the project root');
const onDisk = JSON.parse(fs.readFileSync(consent.consentStorePath(home), 'utf8'));
assert.strictEqual(onDisk.version, '1');
// WIN-3: the on-disk key is the unambiguous JSON-object form {"r":<root>,"i":<id>}.
@@ -628,9 +628,9 @@ test('has: a contentHash mismatch is rejected (the binding is the bundle hash)',
const home = tmpDir();
const projectRoot = realProject();
try {
consent.recordProjectConsent({ gsdHome: home, projectRoot, id: 'cap', integrity: 'sha512-good', disclosureSignature: 'sig-good', contentHash: 'sha512-bundle-good' });
assert.strictEqual(consent.hasProjectConsent({ gsdHome: home, projectRoot, id: 'cap', contentHash: 'sha512-bundle-DIFFERENT' }), false, 'contentHash mismatch rejected');
assert.strictEqual(consent.hasProjectConsent({ gsdHome: home, projectRoot, id: 'cap', contentHash: 'sha512-bundle-good' }), true);
consent.recordProjectConsent({ msdHome: home, projectRoot, id: 'cap', integrity: 'sha512-good', disclosureSignature: 'sig-good', contentHash: 'sha512-bundle-good' });
assert.strictEqual(consent.hasProjectConsent({ msdHome: home, projectRoot, id: 'cap', contentHash: 'sha512-bundle-DIFFERENT' }), false, 'contentHash mismatch rejected');
assert.strictEqual(consent.hasProjectConsent({ msdHome: home, projectRoot, id: 'cap', contentHash: 'sha512-bundle-good' }), true);
} finally {
cleanup(home);
cleanup(projectRoot);
@@ -642,8 +642,8 @@ test('has: a different project root does NOT match (consent is per-project, on T
const projectRoot = realProject();
const otherProject = realProject();
try {
consent.recordProjectConsent({ gsdHome: home, projectRoot, id: 'cap', integrity: 'i', disclosureSignature: 's', contentHash: 'sha512-h' });
assert.strictEqual(consent.hasProjectConsent({ gsdHome: home, projectRoot: otherProject, id: 'cap', contentHash: 'sha512-h' }), false);
consent.recordProjectConsent({ msdHome: home, projectRoot, id: 'cap', integrity: 'i', disclosureSignature: 's', contentHash: 'sha512-h' });
assert.strictEqual(consent.hasProjectConsent({ msdHome: home, projectRoot: otherProject, id: 'cap', contentHash: 'sha512-h' }), false);
} finally {
cleanup(home);
cleanup(projectRoot);
@@ -656,7 +656,7 @@ test('has: returns false (never throws) for an unsafe capability id', () => {
const projectRoot = realProject();
try {
for (const bad of ['__proto__', 'constructor', 'prototype', 'Not-Kebab', 'with space', '../escape']) {
assert.strictEqual(consent.hasProjectConsent({ gsdHome: home, projectRoot, id: bad, contentHash: 'sha512-h' }), false, `unsafe id ${bad} → false`);
assert.strictEqual(consent.hasProjectConsent({ msdHome: home, projectRoot, id: bad, contentHash: 'sha512-h' }), false, `unsafe id ${bad} → false`);
}
} finally {
cleanup(home);
@@ -668,7 +668,7 @@ test('record: rejects an unsafe capability id (prototype-pollution-safe), nothin
const home = tmpDir();
const projectRoot = realProject();
try {
assert.throws(() => consent.recordProjectConsent({ gsdHome: home, projectRoot, id: '__proto__', integrity: 'i', disclosureSignature: 's', contentHash: 'sha512-h' }));
assert.throws(() => consent.recordProjectConsent({ msdHome: home, projectRoot, id: '__proto__', integrity: 'i', disclosureSignature: 's', contentHash: 'sha512-h' }));
const store = consent.readConsentStore(home);
assert.deepStrictEqual(Object.keys(store.records), []);
assert.strictEqual({}.polluted, undefined);
@@ -682,14 +682,14 @@ test('record is idempotent: re-recording the same key overwrites in place (one r
const home = tmpDir();
const projectRoot = realProject();
try {
consent.recordProjectConsent({ gsdHome: home, projectRoot, id: 'cap', integrity: 'i1', disclosureSignature: 's1', contentHash: 'sha512-h1' });
consent.recordProjectConsent({ gsdHome: home, projectRoot, id: 'cap', integrity: 'i2', disclosureSignature: 's2', contentHash: 'sha512-h2' });
consent.recordProjectConsent({ msdHome: home, projectRoot, id: 'cap', integrity: 'i1', disclosureSignature: 's1', contentHash: 'sha512-h1' });
consent.recordProjectConsent({ msdHome: home, projectRoot, id: 'cap', integrity: 'i2', disclosureSignature: 's2', contentHash: 'sha512-h2' });
const onDisk = JSON.parse(fs.readFileSync(consent.consentStorePath(home), 'utf8'));
assert.strictEqual(Object.keys(onDisk.records).length, 1);
const key = JSON.stringify({ r: projectRoot, i: 'cap' });
assert.strictEqual(onDisk.records[key].contentHash, 'sha512-h2');
assert.strictEqual(consent.hasProjectConsent({ gsdHome: home, projectRoot, id: 'cap', contentHash: 'sha512-h2' }), true);
assert.strictEqual(consent.hasProjectConsent({ gsdHome: home, projectRoot, id: 'cap', contentHash: 'sha512-h1' }), false);
assert.strictEqual(consent.hasProjectConsent({ msdHome: home, projectRoot, id: 'cap', contentHash: 'sha512-h2' }), true);
assert.strictEqual(consent.hasProjectConsent({ msdHome: home, projectRoot, id: 'cap', contentHash: 'sha512-h1' }), false);
} finally {
cleanup(home);
cleanup(projectRoot);
@@ -700,10 +700,10 @@ test('record preserves OTHER existing records (atomic round-trip across multiple
const home = tmpDir();
const projectRoot = realProject();
try {
consent.recordProjectConsent({ gsdHome: home, projectRoot, id: 'cap-a', integrity: 'ia', disclosureSignature: 'sa', contentHash: 'sha512-a' });
consent.recordProjectConsent({ gsdHome: home, projectRoot, id: 'cap-b', integrity: 'ib', disclosureSignature: 'sb', contentHash: 'sha512-b' });
assert.strictEqual(consent.hasProjectConsent({ gsdHome: home, projectRoot, id: 'cap-a', contentHash: 'sha512-a' }), true);
assert.strictEqual(consent.hasProjectConsent({ gsdHome: home, projectRoot, id: 'cap-b', contentHash: 'sha512-b' }), true);
consent.recordProjectConsent({ msdHome: home, projectRoot, id: 'cap-a', integrity: 'ia', disclosureSignature: 'sa', contentHash: 'sha512-a' });
consent.recordProjectConsent({ msdHome: home, projectRoot, id: 'cap-b', integrity: 'ib', disclosureSignature: 'sb', contentHash: 'sha512-b' });
assert.strictEqual(consent.hasProjectConsent({ msdHome: home, projectRoot, id: 'cap-a', contentHash: 'sha512-a' }), true);
assert.strictEqual(consent.hasProjectConsent({ msdHome: home, projectRoot, id: 'cap-b', contentHash: 'sha512-b' }), true);
} finally {
cleanup(home);
cleanup(projectRoot);
@@ -714,12 +714,12 @@ test('revoke removes a record (has → false afterward); no-op when absent', ()
const home = tmpDir();
const projectRoot = realProject();
try {
consent.recordProjectConsent({ gsdHome: home, projectRoot, id: 'cap', integrity: 'i', disclosureSignature: 's', contentHash: 'sha512-h' });
assert.strictEqual(consent.hasProjectConsent({ gsdHome: home, projectRoot, id: 'cap', contentHash: 'sha512-h' }), true);
consent.revokeProjectConsent({ gsdHome: home, projectRoot, id: 'cap' });
assert.strictEqual(consent.hasProjectConsent({ gsdHome: home, projectRoot, id: 'cap', contentHash: 'sha512-h' }), false, 'record removed by revoke');
assert.doesNotThrow(() => consent.revokeProjectConsent({ gsdHome: home, projectRoot, id: 'cap' }));
assert.doesNotThrow(() => consent.revokeProjectConsent({ gsdHome: home, projectRoot, id: 'never' }));
consent.recordProjectConsent({ msdHome: home, projectRoot, id: 'cap', integrity: 'i', disclosureSignature: 's', contentHash: 'sha512-h' });
assert.strictEqual(consent.hasProjectConsent({ msdHome: home, projectRoot, id: 'cap', contentHash: 'sha512-h' }), true);
consent.revokeProjectConsent({ msdHome: home, projectRoot, id: 'cap' });
assert.strictEqual(consent.hasProjectConsent({ msdHome: home, projectRoot, id: 'cap', contentHash: 'sha512-h' }), false, 'record removed by revoke');
assert.doesNotThrow(() => consent.revokeProjectConsent({ msdHome: home, projectRoot, id: 'cap' }));
assert.doesNotThrow(() => consent.revokeProjectConsent({ msdHome: home, projectRoot, id: 'never' }));
} finally {
cleanup(home);
cleanup(projectRoot);
@@ -730,11 +730,11 @@ test('revoke leaves OTHER records intact', () => {
const home = tmpDir();
const projectRoot = realProject();
try {
consent.recordProjectConsent({ gsdHome: home, projectRoot, id: 'cap-a', integrity: 'ia', disclosureSignature: 'sa', contentHash: 'sha512-a' });
consent.recordProjectConsent({ gsdHome: home, projectRoot, id: 'cap-b', integrity: 'ib', disclosureSignature: 'sb', contentHash: 'sha512-b' });
consent.revokeProjectConsent({ gsdHome: home, projectRoot, id: 'cap-a' });
assert.strictEqual(consent.hasProjectConsent({ gsdHome: home, projectRoot, id: 'cap-a', contentHash: 'sha512-a' }), false);
assert.strictEqual(consent.hasProjectConsent({ gsdHome: home, projectRoot, id: 'cap-b', contentHash: 'sha512-b' }), true, 'sibling record preserved');
consent.recordProjectConsent({ msdHome: home, projectRoot, id: 'cap-a', integrity: 'ia', disclosureSignature: 'sa', contentHash: 'sha512-a' });
consent.recordProjectConsent({ msdHome: home, projectRoot, id: 'cap-b', integrity: 'ib', disclosureSignature: 'sb', contentHash: 'sha512-b' });
consent.revokeProjectConsent({ msdHome: home, projectRoot, id: 'cap-a' });
assert.strictEqual(consent.hasProjectConsent({ msdHome: home, projectRoot, id: 'cap-a', contentHash: 'sha512-a' }), false);
assert.strictEqual(consent.hasProjectConsent({ msdHome: home, projectRoot, id: 'cap-b', contentHash: 'sha512-b' }), true, 'sibling record preserved');
} finally {
cleanup(home);
cleanup(projectRoot);
@@ -754,12 +754,12 @@ test('two concurrent cross-project consent writes both survive (CONSENT-CONCURRE
const projA = realProject();
const projB = realProject();
try {
const modPath = path.resolve('gsd-core/bin/lib/capability-consent.cjs');
const modPath = path.resolve('msd-core/bin/lib/capability-consent.cjs');
// Run the two record writes in genuinely separate processes that hit the cross-process O_EXCL
// lock concurrently (an in-process Promise.all would not exercise the file lock at all).
const writeIn = (proj, id, hash) => new Promise((resolve, reject) => {
const code = `require(${JSON.stringify(modPath)}).recordProjectConsent(` +
`{gsdHome:${JSON.stringify(home)},projectRoot:${JSON.stringify(proj)},id:${JSON.stringify(id)},` +
`{msdHome:${JSON.stringify(home)},projectRoot:${JSON.stringify(proj)},id:${JSON.stringify(id)},` +
`integrity:'i',disclosureSignature:'s',contentHash:${JSON.stringify(hash)}})`;
const child = spawn(process.execPath, ['-e', code], { stdio: 'ignore' });
child.on('error', reject);
@@ -769,8 +769,8 @@ test('two concurrent cross-project consent writes both survive (CONSENT-CONCURRE
writeIn(projA, 'cap-a', 'sha512-a'),
writeIn(projB, 'cap-b', 'sha512-b'),
]);
assert.strictEqual(consent.hasProjectConsent({ gsdHome: home, projectRoot: projA, id: 'cap-a', contentHash: 'sha512-a' }), true, 'project A record survived');
assert.strictEqual(consent.hasProjectConsent({ gsdHome: home, projectRoot: projB, id: 'cap-b', contentHash: 'sha512-b' }), true, 'project B record survived (no lost update)');
assert.strictEqual(consent.hasProjectConsent({ msdHome: home, projectRoot: projA, id: 'cap-a', contentHash: 'sha512-a' }), true, 'project A record survived');
assert.strictEqual(consent.hasProjectConsent({ msdHome: home, projectRoot: projB, id: 'cap-b', contentHash: 'sha512-b' }), true, 'project B record survived (no lost update)');
} finally {
cleanup(home);
cleanup(projA);
@@ -809,7 +809,7 @@ test('finding-3: recordProjectConsent THROWS when the consent lock cannot be acq
try {
plantFreshConsentLock(home);
assert.throws(
() => consent.recordProjectConsent({ gsdHome: home, projectRoot, id: 'cap', integrity: 'i', disclosureSignature: 's', contentHash: 'sha512-h' }),
() => consent.recordProjectConsent({ msdHome: home, projectRoot, id: 'cap', integrity: 'i', disclosureSignature: 's', contentHash: 'sha512-h' }),
/lock/i,
'record must throw (lock-acquire failure) rather than write unlocked',
);
@@ -829,15 +829,15 @@ test('finding-3: revokeProjectConsent THROWS when the consent lock cannot be acq
const projectRoot = realProject();
try {
// Seed a real record FIRST (under a free lock), then plant the fresh lock to block the revoke.
consent.recordProjectConsent({ gsdHome: home, projectRoot, id: 'cap', integrity: 'i', disclosureSignature: 's', contentHash: 'sha512-h' });
consent.recordProjectConsent({ msdHome: home, projectRoot, id: 'cap', integrity: 'i', disclosureSignature: 's', contentHash: 'sha512-h' });
plantFreshConsentLock(home);
assert.throws(
() => consent.revokeProjectConsent({ gsdHome: home, projectRoot, id: 'cap' }),
() => consent.revokeProjectConsent({ msdHome: home, projectRoot, id: 'cap' }),
/lock/i,
'revoke must throw (lock-acquire failure) rather than delete unlocked',
);
// The record must STILL be present — the blocked revoke did not mutate the store.
assert.strictEqual(consent.hasProjectConsent({ gsdHome: home, projectRoot, id: 'cap', contentHash: 'sha512-h' }), true, 'record preserved (revoke blocked, no unlocked delete)');
assert.strictEqual(consent.hasProjectConsent({ msdHome: home, projectRoot, id: 'cap', contentHash: 'sha512-h' }), true, 'record preserved (revoke blocked, no unlocked delete)');
} finally {
cleanup(home);
cleanup(projectRoot);
@@ -883,7 +883,7 @@ test('finding-4: the consent lock does NOT stale-steal a VERIFIED-LIVE same-host
ageConsentLock(lockPath, 2 * 60 * 1000, body); // 2 min old (past the 60s stale window)
const original = fs.readFileSync(lockPath, 'utf8');
assert.throws(
() => consent.recordProjectConsent({ gsdHome: home, projectRoot, id: 'cap', integrity: 'i', disclosureSignature: 's', contentHash: 'sha512-h' }),
() => consent.recordProjectConsent({ msdHome: home, projectRoot, id: 'cap', integrity: 'i', disclosureSignature: 's', contentHash: 'sha512-h' }),
/lock/i,
'a verified-live holder must NOT be stolen (record cannot acquire → throws)',
);
@@ -907,10 +907,10 @@ test('finding-4: the consent lock RECLAIMS a dead same-host holder (fast local r
fs.mkdirSync(path.dirname(lockPath), { recursive: true });
ageConsentLock(lockPath, 2 * 60 * 1000, consentLockBody({ startTime: 'CSTART' })); // stale (>60s), dead pid
assert.doesNotThrow(
() => consent.recordProjectConsent({ gsdHome: home, projectRoot, id: 'cap', integrity: 'i', disclosureSignature: 's', contentHash: 'sha512-h' }),
() => consent.recordProjectConsent({ msdHome: home, projectRoot, id: 'cap', integrity: 'i', disclosureSignature: 's', contentHash: 'sha512-h' }),
'a dead holder must be reclaimed so record proceeds',
);
assert.strictEqual(consent.hasProjectConsent({ gsdHome: home, projectRoot, id: 'cap', contentHash: 'sha512-h' }), true, 'record written after reclaiming the dead holder');
assert.strictEqual(consent.hasProjectConsent({ msdHome: home, projectRoot, id: 'cap', contentHash: 'sha512-h' }), true, 'record written after reclaiming the dead holder');
} finally {
cleanup(home);
cleanup(projectRoot);
@@ -933,7 +933,7 @@ test('exactly MAX_RECORDS records can be written; the (MAX+1)th NEW key is refus
// applies via consentProjectRoot (realpathSync fallback → path.resolve). On Windows,
// path.resolve('/p0') === 'C:\\p0', so a raw '/p0' key would NOT match the production
// lookup and the re-record below would be treated as a NEW key → false cap-full throw.
fs.mkdirSync(path.join(home, '.gsd'), { recursive: true });
fs.mkdirSync(path.join(home, '.msd'), { recursive: true });
const records = {};
for (let i = 0; i < MAX; i++) {
const r = path.resolve(`/p${i}`);
@@ -943,11 +943,11 @@ test('exactly MAX_RECORDS records can be written; the (MAX+1)th NEW key is refus
assert.strictEqual(Object.keys(consent.readConsentStore(home).records).length, MAX, 'store seeded at the cap');
// A re-record of an EXISTING key does NOT grow the store → allowed even at the cap.
const existingProj = path.resolve('/p0');
assert.doesNotThrow(() => consent.recordProjectConsent({ gsdHome: home, projectRoot: existingProj, id: 'cap0', integrity: 'i', disclosureSignature: 's', contentHash: 'sha512-new' }));
assert.doesNotThrow(() => consent.recordProjectConsent({ msdHome: home, projectRoot: existingProj, id: 'cap0', integrity: 'i', disclosureSignature: 's', contentHash: 'sha512-new' }));
// Adding a NEW key when already at the cap is refused with a clear 'full' error.
const fresh = realProject();
try {
assert.throws(() => consent.recordProjectConsent({ gsdHome: home, projectRoot: fresh, id: 'overflow', integrity: 'i', disclosureSignature: 's', contentHash: 'sha512-of' }), /full|maximum/i);
assert.throws(() => consent.recordProjectConsent({ msdHome: home, projectRoot: fresh, id: 'overflow', integrity: 'i', disclosureSignature: 's', contentHash: 'sha512-of' }), /full|maximum/i);
} finally {
cleanup(fresh);
}
@@ -971,15 +971,15 @@ test('WIN-3: roots containing spaces are keyed unambiguously on disk (no collisi
try {
const r1 = '/tmp/space root one'; // path containing spaces (Windows-style)
const r2 = '/tmp/space root one x'; // a DIFFERENT root extending r1 past a space boundary
consent.recordProjectConsent({ gsdHome: home, projectRoot: r1, id: 'cap-a', integrity: 'i', disclosureSignature: 's', contentHash: 'sha512-1' });
consent.recordProjectConsent({ gsdHome: home, projectRoot: r2, id: 'cap-b', integrity: 'i', disclosureSignature: 's', contentHash: 'sha512-2' });
consent.recordProjectConsent({ msdHome: home, projectRoot: r1, id: 'cap-a', integrity: 'i', disclosureSignature: 's', contentHash: 'sha512-1' });
consent.recordProjectConsent({ msdHome: home, projectRoot: r2, id: 'cap-b', integrity: 'i', disclosureSignature: 's', contentHash: 'sha512-2' });
const onDisk = JSON.parse(fs.readFileSync(consent.consentStorePath(home), 'utf8'));
assert.strictEqual(Object.keys(onDisk.records).length, 2, 'two distinct records, no disk-key collision');
assert.ok(onDisk.records[JSON.stringify({ r: path.resolve(r1), i: 'cap-a' })], 'r1 (space path) record keyed unambiguously');
assert.ok(onDisk.records[JSON.stringify({ r: path.resolve(r2), i: 'cap-b' })], 'r2 (space path) record keyed unambiguously');
// Both are independently retrievable (the lookup re-keys via the canonical NUL key).
assert.strictEqual(consent.hasProjectConsent({ gsdHome: home, projectRoot: r1, id: 'cap-a', contentHash: 'sha512-1' }), true);
assert.strictEqual(consent.hasProjectConsent({ gsdHome: home, projectRoot: r2, id: 'cap-b', contentHash: 'sha512-2' }), true);
assert.strictEqual(consent.hasProjectConsent({ msdHome: home, projectRoot: r1, id: 'cap-a', contentHash: 'sha512-1' }), true);
assert.strictEqual(consent.hasProjectConsent({ msdHome: home, projectRoot: r2, id: 'cap-b', contentHash: 'sha512-2' }), true);
} finally {
cleanup(home);
}