feat(#1431): runtime capability registry overlay (ADR-1244 Phase 2) (#1440)

* feat(#1431): runtime capability registry overlay (ADR-1244 Phase 2)

Promote the registry from a frozen data file to loadRegistry({includeInstalled}),
composing the first-party registry with a validated installed overlay (ADR-1244 D2):

- Extract the conformance validator to a shared runtime-callable module
  (gsd-core/bin/lib/capability-validator.cjs); the generator re-exports it
  verbatim, guarded by a generative-parity test (no build-time/runtime drift).
- capability-loader.cts: loadRegistry({includeInstalled}) composes first-party
  ∪ validated overlay from $GSD_HOME/.gsd/capabilities (global) and
  <root>/.gsd/capabilities (project) via the canonical buildRegistry. First-party
  always wins (id/skill/agent/config/command-family + reserved gsd-/anthropic-
  prefixes); full merged-set cross-capability validation; engines.gsd load-time
  re-gate (skip-with-warning); gate-kind capabilities FAIL CLOSED; fragment-path
  escapes rejected.
- semverSatisfies (hand-written, no dep) for the engines.gsd gate, fail-closed.
- Wire surface/state + loop to the overlay; loop injects a blocking gate for each
  skipped gate-kind overlay (fail-closed).
- cwd-aware overlay config-key federation: config-loader _federatedConfigSchema(cwd)
  + config-schema isValidConfigKey(key, cwd) compose the overlay per loadConfig/
  config-set call (never eager at module load, never wrong-cwd); first-party path
  unchanged with no cwd.
- run-tests.cjs sandboxes GSD_HOME (idempotent — nested spawns reuse it) for test
  hermeticity; capability-loader.cjs git+eslint-ignored (tsc artifact);
  capability-validator.cjs stays linted (#551 migration coverage).

Closes #1431

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(#1431): add changeset for runtime capability registry overlay

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* test(#1431): kill config-schema cwd-aware federation mutants (Stryker ≥52)

The cwd-aware overlay config-key federation added to config-schema.cts
(_capabilityConfigSchema(cwd) + isCapabilityConfigKey/isValidConfigKey cwd
threading) introduced mutable surface uncovered by config-schema's mutation
test set, dropping its score to 39.58% (below the 52 break threshold). Add a
real-overlay-fixture describe block exercising every branch (cwd guard, overlay
loadRegistry, found-branch, first-party fallback, cwd threading); local Stryker
score 39.58% -> 77.08%.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Tom Boucher
2026-06-18 14:41:20 -04:00
committed by GitHub
parent 2421cf1b4a
commit 353f63d170
25 changed files with 3389 additions and 1929 deletions

View File

@@ -1719,3 +1719,53 @@ describe('isCapabilityActive cross-runtime detection (GSD_RUNTIME → config.run
}
});
});
// ─── ADR-1244 D2 overlay wiring — capability-state sees installed overlays ───
describe('ADR-1244 D2: overlay-aware registry wiring in capability-state', () => {
// Verifies that resolveCapabilityRuntimeState uses loadRegistry({includeInstalled:true})
// so a valid installed overlay capability appears in the capabilities list.
// The overlay cap has no activationKey so it activates freely.
const { resolveCapabilityRuntimeState } = require('../gsd-core/bin/lib/capability-state.cjs');
test('valid overlay capability appears in runtime state capabilities list', () => {
const overlayHome = fs.mkdtempSync(path.join(os.tmpdir(), 'cap-state-overlay-'));
const prevGsdHome = process.env.GSD_HOME;
try {
// Write a valid overlay capability manifest
const capDir = path.join(overlayHome, '.gsd', 'capabilities', 'my-overlay-cap');
fs.mkdirSync(capDir, { recursive: true });
const capManifest = {
id: 'my-overlay-cap',
role: 'feature',
version: '1.0.0',
title: 'My Overlay Cap',
description: 'ADR-1244 D2 wiring test overlay',
tier: 'standard',
requires: [],
engines: { gsd: '>=0.0.0' },
runtimeCompat: { supported: ['*'], unsupported: [] },
skills: [], agents: [], hooks: [], config: {}, steps: [], contributions: [], gates: [],
};
fs.writeFileSync(path.join(capDir, 'capability.json'), JSON.stringify(capManifest), 'utf8');
// Point GSD_HOME to the overlay home so loadRegistry finds it
process.env.GSD_HOME = overlayHome;
// Use a non-existent cwd so no project-scope overlay is scanned — pure global
const nonExistentCwd = path.join(os.tmpdir(), 'cap-state-overlay-cwd-' + Date.now());
const result = resolveCapabilityRuntimeState(nonExistentCwd, undefined);
const overlayEntry = result.capabilities.find((c) => c.id === 'my-overlay-cap');
assert.ok(
overlayEntry !== undefined,
'overlay capability "my-overlay-cap" must appear in resolveCapabilityRuntimeState results ' +
'(ADR-1244 D2: capability-state must use overlay-aware loadRegistry)',
);
} finally {
if (prevGsdHome === undefined) delete process.env.GSD_HOME;
else process.env.GSD_HOME = prevGsdHome;
cleanup(overlayHome);
}
});
});