test(#3336): fold the installer & runtime surface issue-* cluster — Wave 4 (#3376)

* test(#3336): fold the installer & runtime surface issue-* cluster — Wave 4

Folds 10 legacy issue-*.test.cjs regression files (79 test() blocks) into
their module's main suite, per H3 (#3315) of the test-hygiene epic (#3053).
First of 4 issue-* waves (following the 3 fix-* waves, all merged).

- 1 file with no prior target coverage: renamed (git mv) into
  legacy-cleanup.test.cjs (sole comprehensive suite for that module).
- 9 files merged into 6 pre-existing suites: golden-parity-single-source,
  runtime-artifact-layout-surface, codex-config (4 sources merged jointly
  in one pass per the issue's own instruction, to catch overlap between the
  4 sources themselves, not just against the pre-existing target — zero
  overlap found, all 20 blocks additive), runtime-config-adapter-registry
  (1 of 10 source blocks dropped as a proven subset of existing coverage),
  cline-install, install.test.cjs.

Incidental fixes required to keep this wave's own ratchets green:
- Fixed a stale ADR doc reference (docs/adr/1235) to a folded-away filename.
- scripts/lint-allow-test-rule-refs: pruned 4 stale allowlist entries for
  renamed/merged-away files, cited 2 previously-uncited allow-test-rule
  comments that surfaced as "new" only because their file path changed,
  added 1 fresh allowlist entry for a pre-existing uncited comment that
  predates this PR, and tightened the exemption-file ceiling 309 -> 305
  to match the real post-fold high-water mark.

Zero net test-coverage loss. No production code changed.

* test(#3336): fix orthogonal-review findings — Wave 4 fold

Standards-axis review + Memtrace graph pass found real issues in the
just-folded suites, all fixed here:

- Standardized the fold-wrapper convention (block-scoped __foldDescribe)
  across golden-parity-single-source.test.cjs, runtime-artifact-layout-
  surface.test.cjs, runtime-config-adapter-registry.test.cjs, and
  cline-install.test.cjs to match the pattern already used by
  codex-config.test.cjs and install.test.cjs in this same wave (and by
  earlier folds elsewhere in the epic) — repeats the exact inconsistency
  Wave 3 (#3335) already fixed once in this epic.
- Fixed a stale allowlist entry's alphabetical position (cosmetic, not
  tool-gated, caught by review anyway).
- Fixed two stale test-filename references in PRODUCTION code comments
  (src/capability-writer.cts, src/runtime-config-adapter-registry.cts)
  caught by lint-removed-but-needed — a class of stale reference this
  wave's fold agents didn't check for, since they were scoped to docs/
  and gsd-core/references/ only, not src/. First fix attempt wrongly
  edited the gitignored gsd-core/bin/lib/*.cjs BUILD OUTPUT instead of
  the tracked .cts source; caught and corrected before commit.
- Fixed one remaining stale doc reference in docs/adr/1235 (a prior
  partial fix in this same wave missed it).

No test() count changed in any file. No production code BEHAVIOR
changed — comment-only fixes in src/.

---------

Co-authored-by: sim <sim@local>
This commit is contained in:
Tom Boucher
2026-08-11 23:35:44 -04:00
committed by GitHub
parent 23e6d49929
commit ad07f76a31
21 changed files with 1830 additions and 1856 deletions

View File

@@ -26,6 +26,18 @@ const {
} = require(path.join(ROOT, 'gsd-core', 'bin', 'lib', 'runtime-config-adapter-registry.cjs'));
const registry = require(path.join(ROOT, 'gsd-core', 'bin', 'lib', 'capability-registry.cjs'));
// Folded from tests/issue-57-runtime-install-no-drift.test.cjs (issue #57).
process.env.GSD_TEST_MODE = '1'; // must precede require of bin/install.js
const fs = require('node:fs');
const os = require('node:os');
const { allRuntimes, runtimeMap } = require(path.join(ROOT, 'bin', 'install.js'));
const { resolveRuntimeArtifactLayout } = require(
path.join(ROOT, 'gsd-core', 'bin', 'lib', 'runtime-artifact-layout.cjs'),
);
const { getGlobalConfigDir } = require(path.join(ROOT, 'gsd-core', 'bin', 'lib', 'runtime-homes.cjs'));
const sorted = (iterable) => [...iterable].sort();
// ---------------------------------------------------------------------------
// Source-of-truth table — DERIVED from the capability registry descriptors.
// Each row is the descriptor projection of one runtime's config intent. This is
@@ -373,3 +385,169 @@ describe('resolveInstallPlan — descriptor-projection contract (count-agnostic)
}
});
});
{
const { describe: __foldDescribe } = require('node:test');
__foldDescribe('folded:issue-57-runtime-install-no-drift', () => {
// ---------------------------------------------------------------------------
// Folded from tests/issue-57-runtime-install-no-drift.test.cjs (issue #57,
// H3 Wave 4 consolidation). Protects the Runtime Install Policy Module
// boundary (ADR-58) and the explicit Runtime Config Adapter Registry (#60):
// these guards fail when supported-runtime metadata bypasses the registry
// projection (AC1) or config-mutation dispatch bypasses the explicit adapter
// registry (AC2). Known intentional asymmetry: `grok` appears in
// runtime-homes.cjs's getGlobalConfigDir switch but not in the registry /
// artifact-layout supported sets, and getGlobalConfigDir() falls back to
// ~/.claude for an unknown runtime instead of throwing (deliberately
// liberal) — only the registry and artifact-layout projections are loud
// gates. One source case was dropped as subsumed (see note below).
// ---------------------------------------------------------------------------
describe('issue-57 AC1 — supported-runtime metadata has one projected source of truth', () => {
test('installer allRuntimes, interactive runtimeMap, and registry agree on the supported set', () => {
const installable = sorted(allRuntimes);
assert.deepStrictEqual(
installable,
sorted(Object.values(runtimeMap)),
'Drift: bin/install.js `allRuntimes` and the interactive `runtimeMap` selection menu '
+ 'diverged. A runtime selectable in the prompt but absent from allRuntimes (or vice '
+ 'versa) is a supported-runtime call site that skipped the projection.',
);
assert.deepStrictEqual(
installable,
sorted(ALLOWED_CONFIG_RUNTIMES),
'Drift: bin/install.js `allRuntimes` and `ALLOWED_CONFIG_RUNTIMES` (runtime config '
+ 'adapter registry) diverged. A runtime added to an installer call site without a '
+ 'registry adapter entry bypasses the registry projection — register it in '
+ 'src/runtime-config-adapter-registry.cts.',
);
});
// NOTE: source also asserted `resolveRuntimeConfigIntent(runtime).runtime === runtime`
// for every `allRuntimes` entry — dropped here as subsumed by the
// descriptor-projection contract test above (deep-equal over every registry
// runtime, a strict superset of `allRuntimes` per the equality just asserted).
test('every installable runtime resolves a global config dir through runtime-homes', () => {
for (const runtime of allRuntimes) {
const dir = getGlobalConfigDir(runtime);
assert.equal(typeof dir, 'string', `${runtime} config dir must be a string`);
assert.ok(dir.length > 0, `${runtime} must resolve a non-empty global config dir`);
}
});
});
describe('issue-57 AC2 — config-mutation dispatch is closed over the explicit registry', () => {
test('every config intent uses a registry-declared install surface', () => {
const surfaces = new Set(INSTALL_SURFACES);
for (const runtime of allRuntimes) {
const { installSurface } = resolveRuntimeConfigIntent(runtime);
assert.ok(
surfaces.has(installSurface),
`${runtime} dispatches config via unregistered surface "${installSurface}" — add it `
+ 'to INSTALL_SURFACES in the registry instead of branching on it inline.',
);
}
});
test('every finishInstall permission writer is null or a registry-known runtime', () => {
for (const runtime of allRuntimes) {
const { finishPermissionWriter } = resolveRuntimeConfigIntent(runtime);
assert.ok(
finishPermissionWriter === null || ALLOWED_CONFIG_RUNTIMES.has(finishPermissionWriter),
`${runtime} uses finishPermissionWriter "${finishPermissionWriter}", which is neither `
+ 'null nor a registry-known runtime — route it through a registered adapter.',
);
}
});
test('unknown runtime fails loudly through both strict projections (no silent fallthrough)', () => {
const SENTINEL = '__drift_sentinel_runtime__';
assert.throws(
() => resolveRuntimeConfigIntent(SENTINEL),
TypeError,
'config adapter registry must reject an unknown runtime, not dispatch it silently',
);
assert.throws(
() => resolveRuntimeArtifactLayout(SENTINEL, path.join(os.tmpdir(), 'gsd-57'), 'global'),
TypeError,
'artifact-layout projection must reject an unknown runtime',
);
});
test('registry rejects prototype-chain keys (no proto-pollution dispatch bypass)', () => {
// Overlaps __proto__/constructor/toString with the individually-named
// cases above; folded anyway to keep the 'prototype' key covered (not
// asserted individually elsewhere in this file).
for (const key of ['__proto__', 'constructor', 'prototype', 'toString']) {
assert.throws(
() => resolveRuntimeConfigIntent(key),
TypeError,
`${key} must throw, not resolve via the prototype chain`,
);
}
});
// allow-test-rule: structural-regression-guard (#3336)
// structural guard over bin/install.js source. Behavioral assertions
// cannot observe inline `runtime === '...'` config branching, so this enforces that
// every inline per-runtime branch references a runtime the adapter registry knows
// about — a NEW branch against an unregistered runtime name fails here.
test('every inline `runtime === "..."` branch references a registry-known runtime', () => {
const src = fs.readFileSync(path.join(ROOT, 'bin', 'install.js'), 'utf8');
const literals = new Set(
[...src.matchAll(/runtime === (?:'([a-z][a-z0-9-]*)'|"([a-z][a-z0-9-]*)")/g)]
.map((m) => m[1] ?? m[2]),
);
assert.ok(literals.size > 0, 'expected to find inline runtime comparisons in bin/install.js');
const unregistered = [...literals].filter((r) => !ALLOWED_CONFIG_RUNTIMES.has(r));
assert.deepStrictEqual(
unregistered,
[],
`inline 'runtime === "..."' branch(es) reference runtimes absent from the config adapter `
+ `registry: ${unregistered.join(', ')} — register them in `
+ 'src/runtime-config-adapter-registry.cts or route the logic through '
+ 'resolveRuntimeConfigIntent instead of branching inline.',
);
});
// allow-test-rule: structural-regression-guard (#2103)
// VS Code is a registry runtime but is NEVER CLI-installed (Marketplace/VSIX
// extension); it must stay fully descriptor-driven — bin/install.js must
// never special-case it by name.
test('#2103: bin/install.js has ZERO runtime === "vscode" / isVscode branches (vscode stays fully descriptor-driven)', () => {
const src = fs.readFileSync(path.join(ROOT, 'bin', 'install.js'), 'utf8');
const runtimeComparisons = [...src.matchAll(/runtime === (?:'vscode'|"vscode")/g)];
assert.deepStrictEqual(
runtimeComparisons.map((m) => m[0]),
[],
'bin/install.js must not special-case vscode via `runtime === "vscode"` — vscode has no '
+ 'install surface at all (installSurface: "none") and is never CLI-installed; any '
+ 'vscode-specific behavior belongs in capabilities/vscode/capability.json, not an inline branch.',
);
const isVscodeRefs = [...src.matchAll(/\bisVscode\b/g)];
assert.deepStrictEqual(
isVscodeRefs.map((m) => m[0]),
[],
'bin/install.js must not introduce an isVscode flag — vscode is intentionally excluded '
+ 'from runtimeFlags (Marketplace-distributed, never CLI-installed).',
);
});
// allow-test-rule: structural-regression-guard (#3336)
// delegation-presence guard. Catches wholesale removal of the registry
// dispatch (a regression to scattered per-runtime config branching).
test('bin/install.js requires the config adapter registry and dispatches through it', () => {
const src = fs.readFileSync(path.join(ROOT, 'bin', 'install.js'), 'utf8');
assert.ok(
src.includes('runtime-config-adapter-registry'),
'bin/install.js no longer requires the runtime config adapter registry',
);
assert.ok(
src.includes('resolveInstallPlan('),
'bin/install.js no longer dispatches config through resolveInstallPlan',
);
});
});
});
}