* fix(#4132): verify durable runtime surface sources * chore(#4132): record PR number in changeset * test(#4132): cover rejected commands source alias * fix(#4132): reject aliased package fallback * test(#4132): cover rejected agents source alias * test(#4132): cover partially aliased marker provider * fix(#4132): reject partially aliased source providers * test(#4132): cover routed source identity probes * fix(#4132): route installed source identity probes * refactor(#4132): tighten installer source metadata * test(#4132): cover corpus trust boundary attacks * fix(#4132): close installed corpus trust gaps * refactor(#4132): keep installer authority private * fix(#4132): preserve private installer fallback * test(#4132): preserve fixture source authority * fix(#4132): reject overlapping source fallback * fix(#4132): avoid redundant installed corpus reads * refactor(#4132): simplify provider resolution * test(#4132): sync install tree fixtures after rebase --------- Co-authored-by: Tom Boucher <trekkie@nomorestars.com>
This commit is contained in:
@@ -16,7 +16,8 @@ const { writeSurface, readSurface, resolveSurface, listSurface, applySurface } =
|
||||
const { loadSkillsManifest, writeActiveProfile, resolveProfile } = require('../gsd-core/bin/lib/install-profiles.cjs');
|
||||
const { resolveRuntimeArtifactLayout } = require('../gsd-core/bin/lib/runtime-artifact-layout.cjs');
|
||||
const { CLUSTERS, allClusteredSkills } = require('../gsd-core/bin/lib/clusters.cjs');
|
||||
const { createTempDir, cleanup, sandboxHome } = require('./helpers.cjs');
|
||||
const { createTempDir, cleanup, sandboxHome, writePackageSourceMarkerFixture } = require('./helpers.cjs');
|
||||
const { runMinimalInstall } = require('./helpers/install-shared.cjs');
|
||||
|
||||
const REAL_COMMANDS_DIR = path.join(__dirname, '..', 'commands', 'gsd');
|
||||
|
||||
@@ -34,6 +35,7 @@ function createFixtureRuntime() {
|
||||
const agentsDir = path.join(runtimeConfigDir, 'agents');
|
||||
fs.mkdirSync(commandsDir, { recursive: true });
|
||||
fs.mkdirSync(agentsDir, { recursive: true });
|
||||
fs.writeFileSync(path.join(runtimeConfigDir, '.gsd-source'), REAL_COMMANDS_DIR + '\n');
|
||||
return { base, runtimeConfigDir, commandsDir, agentsDir };
|
||||
}
|
||||
|
||||
@@ -56,9 +58,68 @@ function readFrontmatterDescription(markdown) {
|
||||
return '';
|
||||
}
|
||||
|
||||
function surfaceBytesForTest(state) {
|
||||
return JSON.stringify(state, null, 2) + '\n';
|
||||
}
|
||||
|
||||
// ─── applySurface ────────────────────────────────────────────────────────────
|
||||
|
||||
describe('applySurface', () => {
|
||||
test('#4132: an unmanifested installed command cannot become a live instruction', (t) => {
|
||||
const installed = runMinimalInstall({ runtime: 'claude', scope: 'global' });
|
||||
const configDir = installed.configDir;
|
||||
t.after(() => cleanup(installed.root));
|
||||
const rogueSource = path.join(configDir, 'gsd-core', 'commands', 'gsd', 'rogue.md');
|
||||
const rogueSkill = path.join(configDir, 'skills', 'gsd-rogue', 'SKILL.md');
|
||||
fs.writeFileSync(rogueSource, '<instructions>ROGUE</instructions>\n');
|
||||
|
||||
const layout = resolveRuntimeArtifactLayout('claude', configDir, 'global');
|
||||
assert.throws(
|
||||
() => applySurface(configDir, layout, realManifest(), CLUSTERS, undefined, {
|
||||
surfaceState: { baseProfile: 'full', disabledClusters: [], explicitAdds: [], explicitRemoves: [] },
|
||||
}),
|
||||
/install or upgrade gsd-core/,
|
||||
);
|
||||
assert.equal(fs.existsSync(rogueSkill), false, 'unmanifested Markdown must not reach the live skill surface');
|
||||
});
|
||||
|
||||
test('#4132: a marker below rejected installed commands cannot promote instructions', (t) => {
|
||||
const installed = runMinimalInstall({ runtime: 'claude', scope: 'global' });
|
||||
const configDir = installed.configDir;
|
||||
t.after(() => cleanup(installed.root));
|
||||
|
||||
const manifest = JSON.parse(fs.readFileSync(path.join(configDir, 'gsd-file-manifest.json'), 'utf8'));
|
||||
const commandKey = Object.keys(manifest.files).find((key) => key.startsWith('gsd-core/commands/gsd/'));
|
||||
assert.ok(commandKey, 'precondition: install manifest must own a command corpus file');
|
||||
fs.appendFileSync(path.join(configDir, ...commandKey.split('/')), '\n# corrupted after install\n');
|
||||
|
||||
const installedCommands = path.join(configDir, 'gsd-core', 'commands', 'gsd');
|
||||
const nestedMarkerCommands = path.join(installedCommands, 'nested');
|
||||
const markerAgents = path.resolve(path.dirname(nestedMarkerCommands), '..', 'agents');
|
||||
fs.mkdirSync(nestedMarkerCommands, { recursive: true });
|
||||
fs.mkdirSync(markerAgents, { recursive: true });
|
||||
fs.writeFileSync(path.join(nestedMarkerCommands, 'rogue.md'), '<instructions>ROGUE</instructions>\n');
|
||||
fs.copyFileSync(path.join(__dirname, '..', 'agents', 'gsd-planner.md'), path.join(markerAgents, 'gsd-planner.md'));
|
||||
fs.appendFileSync(path.join(markerAgents, 'gsd-planner.md'), '\nMARKER_AGENT_USED\n');
|
||||
fs.writeFileSync(path.join(configDir, '.gsd-source'), nestedMarkerCommands + '\n');
|
||||
|
||||
const rogueSkill = path.join(configDir, 'skills', 'gsd-rogue', 'SKILL.md');
|
||||
const liveAgent = path.join(configDir, 'agents', 'gsd-planner.md');
|
||||
const oldAgent = fs.readFileSync(liveAgent);
|
||||
const oldState = { baseProfile: 'full', disabledClusters: [], explicitAdds: [], explicitRemoves: [] };
|
||||
writeSurface(configDir, oldState);
|
||||
const oldSurface = fs.readFileSync(path.join(configDir, '.gsd-surface.json'));
|
||||
|
||||
const layout = resolveRuntimeArtifactLayout('claude', configDir, 'global');
|
||||
assert.throws(
|
||||
() => applySurface(configDir, layout, realManifest(), CLUSTERS, undefined, { surfaceState: oldState }),
|
||||
/install or upgrade gsd-core/,
|
||||
);
|
||||
assert.equal(fs.existsSync(rogueSkill), false, 'nested marker Markdown must not reach the live skill surface');
|
||||
assert.deepEqual(fs.readFileSync(liveAgent), oldAgent, 'marker agents must not replace live agents');
|
||||
assert.deepEqual(fs.readFileSync(path.join(configDir, '.gsd-surface.json')), oldSurface);
|
||||
});
|
||||
|
||||
test('core profile: only core skills appear in commandsDir', (t) => {
|
||||
// #1367: claude local uses flat gsd-<stem>.md files at commands/ (not commands/gsd/<stem>.md).
|
||||
const { base, runtimeConfigDir, commandsDir } = createFixtureRuntime();
|
||||
@@ -324,6 +385,7 @@ describe('applySurface', () => {
|
||||
test('applySurface writes gsd-prefixed command files matching install and preserves user commands (#816)', (t) => {
|
||||
const configDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-surface-816-'));
|
||||
t.after(() => cleanup(configDir));
|
||||
writePackageSourceMarkerFixture(configDir);
|
||||
|
||||
writeActiveProfile(configDir, 'standard');
|
||||
writeSurface(configDir, {
|
||||
@@ -406,6 +468,7 @@ describe('applySurface', () => {
|
||||
const installDir = fs.mkdtempSync(path.join(os.tmpdir(), `gsd-816-install-${runtime}-`));
|
||||
const surfaceDir = fs.mkdtempSync(path.join(os.tmpdir(), `gsd-816-surface-${runtime}-`));
|
||||
t.after(() => { cleanup(installDir); cleanup(surfaceDir); });
|
||||
writePackageSourceMarkerFixture(surfaceDir);
|
||||
|
||||
// --- Install path ---
|
||||
installRuntimeArtifacts(runtime, installDir, 'global', resolvedProfile);
|
||||
@@ -531,6 +594,118 @@ describe('applySurface', () => {
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
test('#4132: a later kind staging failure leaves exact all-old state and artifacts', (t) => {
|
||||
const root = createTempDir('gsd-surface-stage-failure-');
|
||||
t.after(() => cleanup(root));
|
||||
const firstDest = path.join(root, 'first');
|
||||
const secondDest = path.join(root, 'second');
|
||||
fs.mkdirSync(firstDest, { recursive: true });
|
||||
fs.mkdirSync(secondDest, { recursive: true });
|
||||
fs.writeFileSync(path.join(firstDest, 'gsd-alpha.md'), 'old alpha\n');
|
||||
fs.writeFileSync(path.join(secondDest, 'gsd-beta.md'), 'old beta\n');
|
||||
const stagedFirst = path.join(root, 'staged-first');
|
||||
fs.mkdirSync(stagedFirst);
|
||||
fs.writeFileSync(path.join(stagedFirst, 'alpha.md'), 'new alpha\n');
|
||||
const oldSurface = surfaceBytesForTest({ baseProfile: 'full', disabledClusters: [], explicitAdds: [], explicitRemoves: [] });
|
||||
fs.writeFileSync(path.join(root, '.gsd-surface.json'), oldSurface);
|
||||
const layout = {
|
||||
runtime: 'claude', configDir: root, scope: 'local', kinds: [
|
||||
{ kind: 'commands', destSubpath: 'first', prefix: 'gsd-', stage: () => stagedFirst },
|
||||
{ kind: 'commands', destSubpath: 'second', prefix: 'gsd-', stage: () => { throw new Error('later stage failed'); } },
|
||||
],
|
||||
};
|
||||
|
||||
assert.throws(() => applySurface(root, layout, new Map([['alpha', []], ['beta', []]])), /later stage failed/);
|
||||
assert.equal(fs.readFileSync(path.join(firstDest, 'gsd-alpha.md'), 'utf8'), 'old alpha\n');
|
||||
assert.equal(fs.readFileSync(path.join(secondDest, 'gsd-beta.md'), 'utf8'), 'old beta\n');
|
||||
assert.equal(fs.readFileSync(path.join(root, '.gsd-surface.json'), 'utf8'), oldSurface);
|
||||
});
|
||||
|
||||
test('#4132: a hash-mismatched installed corpus leaves exact all-old state and artifacts', (t) => {
|
||||
const root = createTempDir('gsd-surface-corrupt-corpus-');
|
||||
t.after(() => cleanup(root));
|
||||
const installedCommands = path.join(root, 'gsd-core', 'commands', 'gsd');
|
||||
const installedAgents = path.join(root, 'gsd-core', 'agents');
|
||||
fs.mkdirSync(installedCommands, { recursive: true });
|
||||
fs.mkdirSync(installedAgents, { recursive: true });
|
||||
fs.writeFileSync(path.join(installedCommands, 'help.md'), '# corrupted command\n');
|
||||
fs.writeFileSync(path.join(installedAgents, 'gsd-planner.md'), '# corrupted agent\n');
|
||||
fs.writeFileSync(path.join(root, 'gsd-file-manifest.json'), JSON.stringify({ files: {
|
||||
'gsd-core/commands/gsd/help.md': '0'.repeat(64),
|
||||
'gsd-core/agents/gsd-planner.md': '0'.repeat(64),
|
||||
} }));
|
||||
|
||||
const skillPath = path.join(root, 'skills', 'gsd-help', 'SKILL.md');
|
||||
const agentPath = path.join(root, 'agents', 'gsd-planner.md');
|
||||
fs.mkdirSync(path.dirname(skillPath), { recursive: true });
|
||||
fs.mkdirSync(path.dirname(agentPath), { recursive: true });
|
||||
fs.writeFileSync(skillPath, 'old skill\n');
|
||||
fs.writeFileSync(agentPath, 'old agent\n');
|
||||
const oldState = { baseProfile: 'full', disabledClusters: [], explicitAdds: [], explicitRemoves: [] };
|
||||
const candidate = { ...oldState, explicitRemoves: ['help'] };
|
||||
const oldSurface = surfaceBytesForTest(oldState);
|
||||
fs.writeFileSync(path.join(root, '.gsd-surface.json'), oldSurface);
|
||||
|
||||
const layout = resolveRuntimeArtifactLayout('claude', root, 'global');
|
||||
assert.throws(
|
||||
() => applySurface(root, layout, realManifest(), CLUSTERS, undefined, { surfaceState: candidate }),
|
||||
/install or upgrade gsd-core/,
|
||||
);
|
||||
assert.equal(fs.readFileSync(skillPath, 'utf8'), 'old skill\n');
|
||||
assert.equal(fs.readFileSync(agentPath, 'utf8'), 'old agent\n');
|
||||
assert.equal(fs.readFileSync(path.join(root, '.gsd-surface.json'), 'utf8'), oldSurface);
|
||||
});
|
||||
|
||||
test('#4132: candidate surface state is published after materialization', (t) => {
|
||||
const root = createTempDir('gsd-surface-state-last-');
|
||||
t.after(() => cleanup(root));
|
||||
const dest = path.join(root, 'commands');
|
||||
const staged = path.join(root, 'staged');
|
||||
fs.mkdirSync(dest, { recursive: true });
|
||||
fs.mkdirSync(staged);
|
||||
fs.writeFileSync(path.join(dest, 'gsd-alpha.md'), 'old alpha\n');
|
||||
fs.writeFileSync(path.join(staged, 'alpha.md'), 'new alpha\n');
|
||||
const oldState = { baseProfile: 'full', disabledClusters: [], explicitAdds: [], explicitRemoves: [] };
|
||||
const newState = { ...oldState, disabledClusters: ['ui'] };
|
||||
writeSurface(root, oldState);
|
||||
const layout = {
|
||||
runtime: 'claude', configDir: root, scope: 'local', kinds: [
|
||||
{ kind: 'commands', destSubpath: 'commands', prefix: 'gsd-', stage: () => {
|
||||
assert.deepEqual(readSurface(root), oldState, 'candidate must not be visible while staging');
|
||||
return staged;
|
||||
} },
|
||||
],
|
||||
};
|
||||
|
||||
applySurface(root, layout, new Map([['alpha', []]]), undefined, undefined, { surfaceState: newState });
|
||||
|
||||
assert.equal(fs.readFileSync(path.join(dest, 'gsd-alpha.md'), 'utf8'), 'new alpha\n');
|
||||
assert.deepEqual(readSurface(root), newState);
|
||||
});
|
||||
|
||||
test('#4132: reset removes surface state after materialization', (t) => {
|
||||
const root = createTempDir('gsd-surface-reset-state-last-');
|
||||
t.after(() => cleanup(root));
|
||||
const dest = path.join(root, 'commands');
|
||||
const staged = path.join(root, 'staged');
|
||||
fs.mkdirSync(dest, { recursive: true });
|
||||
fs.mkdirSync(staged);
|
||||
fs.writeFileSync(path.join(staged, 'alpha.md'), 'reset alpha\n');
|
||||
writeActiveProfile(root, 'full');
|
||||
writeSurface(root, { baseProfile: 'core', disabledClusters: ['ui'], explicitAdds: [], explicitRemoves: [] });
|
||||
const layout = {
|
||||
runtime: 'claude', configDir: root, scope: 'local', kinds: [
|
||||
{ kind: 'commands', destSubpath: 'commands', prefix: 'gsd-', stage: () => staged },
|
||||
],
|
||||
};
|
||||
|
||||
applySurface(root, layout, new Map([['alpha', []]]), undefined, undefined, { surfaceState: null });
|
||||
|
||||
assert.equal(fs.existsSync(path.join(root, '.gsd-surface.json')), false);
|
||||
assert.equal(fs.readFileSync(path.join(dest, 'gsd-alpha.md'), 'utf8'), 'reset alpha\n');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
// ─── resolveSurface ──────────────────────────────────────────────────────────
|
||||
@@ -1201,6 +1376,7 @@ describe('skills-kind destination parity: installer vs surface-apply (#2911)', (
|
||||
for (const runtime of RUNTIME_IDS) {
|
||||
const configDir = fs.mkdtempSync(path.join(os.tmpdir(), `gsd-2911-parity-${runtime}-${scope}-`));
|
||||
t.after(() => cleanup(configDir));
|
||||
writePackageSourceMarkerFixture(configDir);
|
||||
|
||||
withFakeHome(fakeHome, () => {
|
||||
let layout;
|
||||
@@ -1295,6 +1471,7 @@ describe('codex skills-kind destination: home override (#2911)', () => {
|
||||
t.after(() => cleanup(fakeHome));
|
||||
const codexHome = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-2911-codex-home-dir-'));
|
||||
t.after(() => cleanup(codexHome));
|
||||
writePackageSourceMarkerFixture(codexHome);
|
||||
|
||||
withFakeHome(fakeHome, () => {
|
||||
const manifest = loadSkillsManifest(REAL_COMMANDS_DIR);
|
||||
@@ -1387,6 +1564,7 @@ describe('installOpencodeFamilySkills destination parity (#2911 sibling coverage
|
||||
const configDir = fs.mkdtempSync(path.join(os.tmpdir(), `gsd-2911-ocfs-${runtime}-`));
|
||||
const fakeHomeOverride = fs.mkdtempSync(path.join(os.tmpdir(), `gsd-2911-ocfs-home-${runtime}-`));
|
||||
t.after(() => { cleanup(configDir); cleanup(fakeHomeOverride); });
|
||||
writePackageSourceMarkerFixture(configDir);
|
||||
// #3712 — this row drives a skills-kind `home` override on purpose, which is
|
||||
// exactly what the test-home guard exists to police, so it has to declare the
|
||||
// sandbox rather than rely on the destination happening to sit outside the
|
||||
@@ -1458,15 +1636,12 @@ describe('installOpencodeFamilySkills destination parity (#2911 sibling coverage
|
||||
// flat layout is preserved (not re-nested) rather than a nested one.
|
||||
describe('issue-69: applySurface preserves nested skill layout (no re-flatten)', () => {
|
||||
test('cline global full: applySurface keeps 6 router dirs and nested gsd-ns-manage/skills/help/SKILL.md', (t) => {
|
||||
process.env.GSD_TEST_MODE = '1';
|
||||
const { installRuntimeArtifacts } = require('../gsd-core/bin/lib/install-engine.cjs');
|
||||
const dir = tmpDir('gsd-69-surface-');
|
||||
t.after(() => { try { cleanup(dir); } catch { /* best-effort */ } });
|
||||
const installed = runMinimalInstall({ runtime: 'cline', scope: 'global' });
|
||||
const dir = installed.configDir;
|
||||
t.after(() => { try { cleanup(installed.root); } catch { /* best-effort */ } });
|
||||
|
||||
// Step 1: full install
|
||||
const manifest = loadSkillsManifest(REAL_COMMANDS_DIR);
|
||||
const resolved = resolveProfile({ modes: ['full'], manifest });
|
||||
installRuntimeArtifacts('cline', dir, 'global', resolved);
|
||||
|
||||
const skillsDir = path.join(dir, 'skills');
|
||||
|
||||
@@ -1510,14 +1685,11 @@ describe('issue-69: applySurface preserves nested skill layout (no re-flatten)',
|
||||
|
||||
// #924 companion: Claude must use FLAT layout and applySurface must NOT re-nest it.
|
||||
test('claude global full: install produces flat layout and applySurface preserves it (#924)', (t) => {
|
||||
process.env.GSD_TEST_MODE = '1';
|
||||
const { installRuntimeArtifacts } = require('../gsd-core/bin/lib/install-engine.cjs');
|
||||
const dir = tmpDir('gsd-924-69-');
|
||||
t.after(() => { try { cleanup(dir); } catch { /* best-effort */ } });
|
||||
const installed = runMinimalInstall({ runtime: 'claude', scope: 'global' });
|
||||
const dir = installed.configDir;
|
||||
t.after(() => { try { cleanup(installed.root); } catch { /* best-effort */ } });
|
||||
|
||||
const manifest = loadSkillsManifest(REAL_COMMANDS_DIR);
|
||||
const resolved = resolveProfile({ modes: ['full'], manifest });
|
||||
installRuntimeArtifacts('claude', dir, 'global', resolved);
|
||||
|
||||
const skillsDir = path.join(dir, 'skills');
|
||||
|
||||
|
||||
Reference in New Issue
Block a user