feat(#1049): phase 5d — drive artifactLayout from the runtime descriptor (retire the 128-LOC switch) (#1053)

resolveRuntimeArtifactLayout now builds Layout from
registry.runtimes[id].runtime.artifactLayout[scope] — a loop dispatching each
ArtifactKind through the SAME 5 builders (commandsKind/agentsKind/skillsKind/
convertedCommandsKind/kimiAgentsKind, unchanged) by (kind, converter, nesting) —
replacing the hardcoded switch(runtime). Equivalence-preserving for all 16 runtimes
× {global, local} (Codex-verified, no divergence). -43 LOC; bin/install.js + the
converters + the install loop untouched. getInstallExports()[converterName]
resolution, configDir threading, scope default, unknown-runtime guard all preserved.

Driving the local scope surfaced a 5a gap: the old switch had no scope branch for 13
runtimes (cursor/gemini/codex/copilot/antigravity/windsurf/augment/trae/qwen/hermes/
codebuddy/opencode/kilo) → local == global for them, but 5a authored local:[].
Backfilled local=global for those 13 (descriptor-faithful; a fall-through shim would
wrongly give cline/kimi local=global). claude/cline/kimi scope-gating untouched.

validateArtifactKindEntry tightened: destSubpath/prefix/nesting/converter required
(ConverterName enum still open — 5e). New 39-case deep-equal golden equivalence test.

Closes #1049

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Tom Boucher
2026-06-11 14:08:46 -04:00
committed by GitHub
parent cec7e704d6
commit 58ed55683e
18 changed files with 946 additions and 171 deletions

View File

@@ -581,21 +581,21 @@ function validateArtifactKindEntry(capId, entry, prefix) {
errors.push(ctx + '.destSubpath must be a non-empty string');
}
// nesting — optional; if present must be in closed vocab
if (entry.nesting !== undefined) {
if (!VALID_ARTIFACT_NESTINGS.has(entry.nesting)) {
errors.push(
ctx + '.nesting must be one of: ' + [...VALID_ARTIFACT_NESTINGS].join(', ') +
' (got: ' + JSON.stringify(entry.nesting) + ')',
);
}
// nesting — required; must be in closed vocab (ADR-857 §5d: now drives install)
if (entry.nesting === undefined || entry.nesting === null) {
errors.push(ctx + '.nesting is required and must be one of: ' + [...VALID_ARTIFACT_NESTINGS].join(', '));
} else if (!VALID_ARTIFACT_NESTINGS.has(entry.nesting)) {
errors.push(
ctx + '.nesting must be one of: ' + [...VALID_ARTIFACT_NESTINGS].join(', ') +
' (got: ' + JSON.stringify(entry.nesting) + ')',
);
}
// prefix — optional; if present must be a string
if (entry.prefix !== undefined) {
if (typeof entry.prefix !== 'string') {
errors.push(ctx + '.prefix must be a string if present (got: ' + typeof entry.prefix + ')');
}
// prefix — required; must be a string (may be empty string '')
if (entry.prefix === undefined || entry.prefix === null) {
errors.push(ctx + '.prefix is required (must be a string, may be empty)');
} else if (typeof entry.prefix !== 'string') {
errors.push(ctx + '.prefix must be a string (got: ' + typeof entry.prefix + ')');
}
// recursive — optional; if present must be a boolean
@@ -605,11 +605,11 @@ function validateArtifactKindEntry(capId, entry, prefix) {
}
}
// converter — optional; if present must be a string or null (closed ConverterName enum in phase 5e)
if (entry.converter !== undefined) {
if (entry.converter !== null && typeof entry.converter !== 'string') {
errors.push(ctx + '.converter must be a string or null if present (got: ' + typeof entry.converter + ')');
}
// converter — required; must be a string or null (closed ConverterName enum in phase 5e)
if (!Object.prototype.hasOwnProperty.call(entry, 'converter')) {
errors.push(ctx + '.converter is required (must be a string or null)');
} else if (entry.converter !== null && typeof entry.converter !== 'string') {
errors.push(ctx + '.converter must be a string or null (got: ' + typeof entry.converter + ')');
}
return errors;

View File

@@ -79,6 +79,7 @@
},
"runtime-artifact-layout": {
"files": [
"runtime-artifact-layout-descriptor-drive.test.cjs",
"runtime-artifact-layout-install-profiles.test.cjs",
"runtime-artifact-layout-surface.test.cjs",
"runtime-artifact-layout.test.cjs"