feat(#1943): extensionEvents vocabulary — separate from hookEvents (extension-system surface) (#1946)

* feat(#1943): extensionEvents vocabulary — separate from hookEvents (extension-system surface)

* fix(#1943): re-export VALID_EXTENSION_EVENTS from gen-capability-registry (test import path)

* fix(#1943): regenerate capability-registry.cjs + add changeset fragment

* fix(#1943): import VALID_EXTENSION_EVENTS from validator, not gen-capability-registry (golden parity)
This commit is contained in:
Tom Boucher
2026-07-02 21:45:35 -04:00
committed by GitHub
parent d3d689a5e9
commit b51cbf96cf
11 changed files with 157 additions and 47 deletions

View File

@@ -3200,6 +3200,11 @@ const {
VALID_ARTIFACT_NESTINGS,
} = require('../scripts/gen-capability-registry.cjs');
// VALID_EXTENSION_EVENTS is imported from the validator directly (not from
// gen-capability-registry) to avoid changing gen-capability-registry.cjs — that
// file is an installed artifact captured by golden-install-parity (#1943).
const VALID_EXTENSION_EVENTS = capValidatorModule.VALID_EXTENSION_EVENTS;
const RUNTIME_IDS = [
'claude', 'codex', 'antigravity', 'gemini', 'cursor', 'opencode',
'kilo', 'copilot', 'augment', 'trae', 'qwen', 'hermes',
@@ -3386,6 +3391,14 @@ describe('ADR-1016 phase 5a: sample axis value assertions', () => {
);
});
test('opencode: extensionEvents === opencode (the extension-system event dialect — #1943)', () => {
const { capMap } = loadAndValidate(new Set());
const registry = buildRegistry(capMap);
const rt = registry.runtimes['opencode'].runtime;
assert.strictEqual(rt.extensionEvents, 'opencode',
'opencode declares the extension-system event subset via extensionEvents (NOT hookEvents)');
});
test('kilo: hooksSurface === none, no hookEvents (registers zero lifecycle hooks)', () => {
const { capMap } = loadAndValidate(new Set());
const registry = buildRegistry(capMap);
@@ -3952,12 +3965,22 @@ describe('ADR-1016 phase 5a: closed-vocab set exports', () => {
assert.strictEqual(VALID_HOOKS_SURFACES.size, 6);
});
test('VALID_HOOK_EVENTS has exactly 3 values', () => {
test('VALID_HOOK_EVENTS has exactly 2 managed-hook dialects (claude/gemini)', () => {
assert.ok(VALID_HOOK_EVENTS instanceof Set);
for (const v of ['claude', 'gemini', 'opencode-subset']) {
for (const v of ['claude', 'gemini']) {
assert.ok(VALID_HOOK_EVENTS.has(v), 'VALID_HOOK_EVENTS must contain "' + v + '"');
}
assert.strictEqual(VALID_HOOK_EVENTS.size, 3);
assert.strictEqual(VALID_HOOK_EVENTS.size, 2);
assert.ok(!VALID_HOOK_EVENTS.has('opencode-subset'),
'opencode-subset is NOT a hookEvents value — it is the extensionEvents vocabulary (#1943)');
});
test('VALID_EXTENSION_EVENTS has the extension-system dialects (opencode/pi/none — #1943)', () => {
assert.ok(VALID_EXTENSION_EVENTS instanceof Set);
for (const v of ['opencode', 'pi', 'none']) {
assert.ok(VALID_EXTENSION_EVENTS.has(v), 'VALID_EXTENSION_EVENTS must contain "' + v + '"');
}
assert.strictEqual(VALID_EXTENSION_EVENTS.size, 3);
});
test('VALID_SANDBOX_TIERS has exactly 2 values', () => {