ADR-1239 Phase B (parent #1679). getDirName was a hand-maintained 15-branch if-chain mapping each runtime to its local content-rewrite dot-dir. Relocate those values into a documented runtime.localConfigDir descriptor field; derive getDirName from registry.runtimes[id].runtime.localConfigDir (fallback .claude). - 16 capability.json gain runtime.localConfigDir (byte-identical values) - capability-validator.cjs requires it (non-empty dot-dir); registry regenerated - docs/reference/capability-manifest.md documents the field + the three divergent values (copilot=.github, antigravity=.agents, kimi=.kimi-code) - drift-guard test: golden value map + key-set equality both ways Byte-identical install output for all 16 runtimes (golden-parity harness #1730). Closes #1756 Co-authored-by: review-bot <review-bot@gsd> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
94 lines
4.0 KiB
JavaScript
94 lines
4.0 KiB
JavaScript
'use strict';
|
|
/**
|
|
* Drift-guard: getDirName must be derived from the capability registry.
|
|
* Verifies:
|
|
* 1. For every known runtime id, getDirName(id) equals the hardcoded golden
|
|
* expected map — a pinned oracle that catches BOTH formula bugs AND
|
|
* unintended registry drift (adding/removing a runtime or changing its
|
|
* localConfigDir forces a deliberate golden-map update here).
|
|
* 2. getDirName('unknown') and getDirName('') fall back to '.claude'.
|
|
* 3. Every registry runtime entry has a non-empty dot-dir localConfigDir string —
|
|
* cross-check from a different angle than the production derivation formula.
|
|
*
|
|
* ADR-1239 Phase B (#1679).
|
|
* Behavioral tests only: assert on returned values, no source-grep.
|
|
*/
|
|
|
|
const { test } = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
const runtimeNamePolicy = require('../gsd-core/bin/lib/runtime-name-policy.cjs');
|
|
const registry = require('../gsd-core/bin/lib/capability-registry.cjs');
|
|
|
|
const { getDirName } = runtimeNamePolicy;
|
|
|
|
// Golden oracle: hardcoded expected map of all 16 runtime ids to their local config dir.
|
|
// A pinned expected value in a TEST is correct — the test IS the oracle (non-circular).
|
|
// Only PRODUCTION code should derive dynamically from the registry.
|
|
// If this map diverges from getDirName output, either the formula is wrong
|
|
// OR the registry changed — both require a deliberate golden-map update here.
|
|
const GOLDEN_DIR_MAP = {
|
|
claude: '.claude',
|
|
copilot: '.github',
|
|
opencode: '.opencode',
|
|
gemini: '.gemini',
|
|
kilo: '.kilo',
|
|
codex: '.codex',
|
|
antigravity: '.agents',
|
|
cursor: '.cursor',
|
|
windsurf: '.windsurf',
|
|
augment: '.augment',
|
|
trae: '.trae',
|
|
qwen: '.qwen',
|
|
hermes: '.hermes',
|
|
kimi: '.kimi-code',
|
|
codebuddy: '.codebuddy',
|
|
cline: '.cline',
|
|
};
|
|
|
|
test('getDirName: golden map matches for all 16 known runtime ids', () => {
|
|
for (const [id, expected] of Object.entries(GOLDEN_DIR_MAP)) {
|
|
const actual = getDirName(id);
|
|
assert.strictEqual(
|
|
actual,
|
|
expected,
|
|
`getDirName('${id}') diverged from golden.\n` +
|
|
` actual: ${JSON.stringify(actual)}\n` +
|
|
` expected: ${JSON.stringify(expected)}`,
|
|
);
|
|
}
|
|
});
|
|
|
|
test('drift guard: registry runtime id set EXACTLY equals the golden map (adding/removing a runtime forces a golden update)', () => {
|
|
// Without this, a newly-added runtime would pass (its value never checked) and
|
|
// removing `claude` could pass via the .claude fallback. Pin the set both ways.
|
|
const registryIds = Object.keys(registry.runtimes).sort();
|
|
const goldenIds = Object.keys(GOLDEN_DIR_MAP).sort();
|
|
assert.deepEqual(registryIds, goldenIds,
|
|
'registry.runtimes id set must exactly match GOLDEN_DIR_MAP — update the golden map when adding/removing a runtime');
|
|
});
|
|
|
|
test('getDirName fallback: unknown runtime returns ".claude"', () => {
|
|
assert.strictEqual(getDirName('unknown'), '.claude',
|
|
'getDirName("unknown") must return ".claude" (default fallback)');
|
|
});
|
|
|
|
test('getDirName fallback: empty string returns ".claude"', () => {
|
|
assert.strictEqual(getDirName(''), '.claude',
|
|
'getDirName("") must return ".claude" (empty-input fallback)');
|
|
});
|
|
|
|
test('registry cross-check: every runtimes[id].runtime.localConfigDir is a non-empty dot-dir string', () => {
|
|
for (const [id, entry] of Object.entries(registry.runtimes)) {
|
|
if (!entry || typeof entry !== 'object') continue;
|
|
const runtimeBlock = entry.runtime;
|
|
if (!runtimeBlock || typeof runtimeBlock !== 'object') continue;
|
|
const dir = runtimeBlock.localConfigDir;
|
|
assert.strictEqual(typeof dir, 'string',
|
|
`registry.runtimes['${id}'].runtime.localConfigDir must be a string (got: ${typeof dir})`);
|
|
assert.ok(dir.length > 0,
|
|
`registry.runtimes['${id}'].runtime.localConfigDir must be non-empty`);
|
|
assert.ok(dir.startsWith('.'),
|
|
`registry.runtimes['${id}'].runtime.localConfigDir must start with '.' (got: ${JSON.stringify(dir)})`);
|
|
}
|
|
});
|