From 61b29d2af615723743f574ad106372071a297b9f Mon Sep 17 00:00:00 2001 From: Tom Boucher Date: Wed, 10 Jun 2026 21:00:37 -0400 Subject: [PATCH] docs(#1016): ADR-1016 runtime capability descriptor (ADR-857 phase 5 design) (#1019) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Realize ADR-857 Branch 8 (host-CLI support as role:runtime Capabilities) + materialize ADR-58's InstallPlan. Closes the 6-axis descriptor vocabulary, absorbs the hard-case runtimes as data, and stages the install migration as a 5a→5g ladder (4/6 axes already modularized; InstallPlan is the 5g capstone, reachable by collection, not a big-bang rewrite). Amended after a plugin-side design grill (rubber-duck + grill-with-docs vs #956 MemPalace / #999 Impeccable): - New term Connected Capability (CONTEXT.md) — a Capability whose integration shape brings its own external process/service/state; orthogonal to authorship. Named, tracked gap (vehicle #956); current schema does not express it. - Narrowed the dogfood claim: the descriptor dogfoods the runtime interface only, not the feature-plugin/Connected path. - Structural "off means off" rule (CONTEXT.md RULESET.CAPABILITY.off-means-off): the host derives shared outputs from active hooks; a hook adds/is-counted, never mutates host source. Ratify in ADR-894; proven by spike #1018. - Hand-waves resolved by code: sandboxTier real-but-thin; model-catalog orthogonal (not a 7th axis); converters closed into a ConverterName enum + added the kimi-agents artifact kind; configHome is pure read-only. - Hook-firing path is unproven (render-hooks built, never consumed) → spike #1018 must prove render-hooks→live-workflow execution before phase-5 build. Design-only; no code. Status: Proposed. Closes #1016 Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 --- CONTEXT.md | 7 +- .../adr/1016-runtime-capability-descriptor.md | 141 ++++++++++++++++++ docs/adr/README.md | 1 + 3 files changed, 148 insertions(+), 1 deletion(-) create mode 100644 docs/adr/1016-runtime-capability-descriptor.md diff --git a/CONTEXT.md b/CONTEXT.md index 1ae3be6c4..bc42b712f 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -164,7 +164,12 @@ ADR-857 phase 4b unified resolver that composes the three toggle systems (instal ADR-959 (phase 4d) — a CLI command family (a top-level `gsd-tools` command and its subcommands) owned by a Capability via a new optional `commands: [{ family, module, router }]` field on the `feature` role. The Capability declares the `family` name, a first-party in-tree `module` (under `gsd-core/bin/lib/`), and the exported `router` — a standard `route*Command({ args, cwd, raw, error })` function identical in shape to the 12 existing host routers (so it routes through the stateless CommandRoutingHub via `routeCjsCommandFamily`, owning its own subcommand list and arg parsing). The registry materializes a `commandFamilies` index (`family → { capId, module, router }`); the formerly-dead `_dispatchNonFamily` shim is replaced by a real `dispatchCapabilityCommand` (exported from `gsd-core/bin/gsd-tools.cjs`) consulted in `runCommand`'s **`default` case** — an unmigrated command hits its hardcoded `case`; a migrated command's `case` is removed so it reaches `default` → registry → router, making collision structurally impossible. The registry *discovers* a router (it does not rebuild a handler table). First-party only; third-party command loading deferred. **Mechanism built (4d-impl-1):** `commands` schema + validator + single-family-ownership cross-check in `gen-capability-registry.cjs`; `commandFamilies` index emitted in the generated `capability-registry.cjs` (currently `{}` — no capability declares commands yet); `dispatchCapabilityCommand` wired into `runCommand`'s `default` case (behavior-preserving today). **Pilot complete (4d-impl-2):** `graphify` cut over as the first real capability command family — `capabilities/graphify/capability.json` bundles the command (`family: graphify`, `module: graphify-command-router.cjs`, `router: routeGraphifyCommand`), skill (`graphify`), config gate (`graphify.enabled`), and `tier: full`; the `case 'graphify':` arm removed from `gsd-tools.cjs`; dispatch flows `default → dispatchCapabilityCommand → commandFamilies.graphify → graphify-command-router.cjs → routeGraphifyCommand`; behavior proven equivalent (all subcommands: build, query, status, diff, build snapshot, unknown subcommand error, usage error, disabled gate). Template for phase-6 per-feature cutovers. **Audit cutover (4d-impl-3):** `audit-uat` and `audit-open` cut over as the second capability command family pair — `capabilities/audit/capability.json` declares two commands (`family: audit-uat`, `module: audit-command-router.cjs`, `router: routeAuditUat`) and (`family: audit-open`, `module: audit-command-router.cjs`, `router: routeAuditOpen`); the `case 'audit-uat':` and `case 'audit-open':` arms removed from `gsd-tools.cjs`; `commandFamilies` now holds `audit-uat`, `audit-open`, and `graphify`; dispatch flows `default → dispatchCapabilityCommand → commandFamilies["audit-uat"|"audit-open"] → audit-command-router.cjs → routeAuditUat|routeAuditOpen`; behavior equivalence proven by existing regression tests (bug-2659, bug-2911, uat.test.cjs) plus new cutover tests. Confirms hyphenated family names pass registry validator (no format restriction beyond non-empty + non-reserved). **Intel cutover (4d-impl-4, last first-party cutover):** `intel` cut over — `capabilities/intel/capability.json` declares the command (`family: intel`, `module: intel-command-router.cjs`, `router: routeIntelCommand`) and the existing config gate (`intel.enabled`, default false); the `case 'intel':` arm removed from `gsd-tools.cjs`; `commandFamilies` now holds `intel`, `audit-uat`, `audit-open`, and `graphify`; dispatch flows `default → dispatchCapabilityCommand → commandFamilies.intel → intel-command-router.cjs → routeIntelCommand`; all 9 subcommands (query, status, update, diff, snapshot, patch-meta, validate, extract-exports, api-surface) and both usage-error paths preserved; non-raw `timeAgo` transform on `status.files[*].updated_at` preserved exactly. `intel.enabled` declared in capability config (`pending-migration` warning expected during staged 3a-impl cutover). Completes the initial 4d capability command cutover batch. ### Runtime Capability [Planned] -A `role: runtime` variant of a Capability (a Capability carries `role: feature | runtime`) that projects GSD's produced artifacts (skills/agents/hooks/commands) onto one host CLI's conventions — config-surface format, artifact-layout kinds, command template, hooks manifest, sandbox tier. It is a declarative descriptor over a fixed first-party primitive vocabulary (not a code adapter); install composes active Feature Capabilities × the chosen Runtime Capability at the InstallPlan seam (ADR-0058). First-party runtimes are authored through the same descriptor a third party would write (dogfooding the interface); tier-1 (Claude Code, Codex, Antigravity) is fully tested, the other existing runtimes ship lower-tier, none dropped. Third-party runtime loading is deferred to a purely additive external loader + trust gate. +A `role: runtime` variant of a Capability (a Capability carries `role: feature | runtime`) that projects GSD's produced artifacts (skills/agents/hooks/commands) onto one host CLI's conventions — config-surface format, artifact-layout kinds, command template, hooks manifest, sandbox tier. It is a declarative descriptor over a fixed first-party primitive vocabulary (not a code adapter); install composes active Feature Capabilities × the chosen Runtime Capability at the InstallPlan seam (ADR-0058). First-party runtimes are authored through the same descriptor a third party would write (dogfooding the interface); tier-1 (Claude Code, Codex, Antigravity) is fully tested, the other existing runtimes ship lower-tier, none dropped. Third-party runtime loading is deferred to a purely additive external loader + trust gate. Note: "third-party" here is the authorship/distribution axis (who wrote/ships it), distinct from the integration-shape axis (in-host vs Connected Capability). + +### Connected Capability [Planned — deferred design] +A Capability whose integration shape brings its own external process, service, or persistent state — for example an MCP server plus a backing database — rather than running entirely within the host's process and trust boundary as declarative artifacts and in-tree first-party code referenced by closed name. Orthogonal to authorship: a Connected Capability may be first-party (e.g. MemPalace, issue #956) or third-party. Contrast with a plain Capability (declarative artifacts + in-host-trust code) and a Runtime Capability (closed-vocabulary projection descriptor), both of which run within host trust. The Connected Capability contract — external-process/MCP-server/backend-provider contributions plus a trust and load gate — is deferred design (ADR-857 §7); vehicle issue #956. It is NOT expressed by the current capability schema. + +`RULESET.CAPABILITY.off-means-off=the host derives shared outputs from the ACTIVE hook set (via loop.render-hooks); a hook may ADD a labeled block or be COUNTED into a host-computed aggregate (e.g. a score denominator), but NEVER mutates host source — so a disabled capability yields the base output by construction, not by authoring discipline. Ratify in ADR-894; proven by spike #1018.` ### Runtime Config Adapter Registry Module owning the explicit per-runtime config-mutation dispatch table for the installer. `resolveRuntimeConfigIntent(runtime)` projects a typed config intent — `installSurface` (`settings-json` | `codex-toml` | `copilot-instructions` | `cline-rules` | `cursor-hooks-json` | `profile-marker-only`), `writesSharedSettings` (the `finishInstall` shared-settings write gate), and `finishPermissionWriter` (`opencode` | `kilo` | none) — that `bin/install.js` dispatches on instead of inline `runtime === '...'` branching. Owns adapter selection only: it performs no filesystem IO and does not execute config mutations (the install/finishInstall handlers and the per-runtime writers do that). Unknown runtimes fail loudly with a `TypeError`, guarded by an `Object.hasOwn` own-property check so prototype-chain keys (`__proto__`, `constructor`) also throw. Realizes the adapter-selection half of the Runtime Install Policy Module boundary. Source: `gsd-core/bin/lib/runtime-config-adapter-registry.cjs`. See ADR-58, #60. diff --git a/docs/adr/1016-runtime-capability-descriptor.md b/docs/adr/1016-runtime-capability-descriptor.md new file mode 100644 index 000000000..7015fe2ae --- /dev/null +++ b/docs/adr/1016-runtime-capability-descriptor.md @@ -0,0 +1,141 @@ +# ADR-1016: Runtime Capability Descriptor + +- **Status:** Proposed +- **Date:** 2026-06-10 +- **Issue:** [#1016](https://github.com/open-gsd/gsd-core/issues/1016) +- **Epic:** [#857](https://github.com/open-gsd/gsd-core/issues/857) (Capability system) — rollout phase 5 +- **Realizes:** [ADR-857](857-capability-system.md) Branch 8 (host-CLI support as `role: runtime` Capabilities) +- **Materializes:** [ADR-58](58-runtime-install-policy-module.md) (the typed `InstallPlan` projection) +- **Builds on:** [ADR-3660](3660-runtime-artifact-layout-module.md) (artifact layout), [ADR-894](894-capability-declaration-format.md) (the `role: runtime` body, already validated) + +## Context + +GSD installs into 16 host CLIs (claude, codex, gemini, opencode, kilo, cursor, copilot, antigravity, windsurf, augment, trae, qwen, hermes, codebuddy, cline, kimi). Today the per-runtime differences are encoded as **~287 conditional branches + 15 `is` boolean flags** in `bin/install.js`, plus scattered per-runtime switches in `runtime-homes.cts`, `runtime-config-adapter-registry.cts`, `runtime-artifact-layout.cts`, `runtime-slash.cts`, `runtime-name-policy.cts`, and `model-catalog.json`. **Adding one runtime touches ~18 hardcoded sites.** This is exactly the feature-scattering ADR-857 set out to end — for runtimes rather than features. + +ADR-857 Branch 8 decided: host-CLI support becomes a `role: runtime` variant of the unified Capability. Feature Capabilities *produce* artifacts; Runtime Capabilities *project* them onto one host's conventions. Install composes **active Features × the chosen Runtime** at ADR-58's `InstallPlan` seam. A runtime descriptor is a **declarative value over a CLOSED named-primitive vocabulary** — not a code adapter; a genuinely new shape requires adding a first-party primitive (code + enum value), which is the dogfood path a future third-party would also use. + +Two enabling pieces already exist: + +- **The descriptor schema is authored and validated.** `gen-capability-registry.cjs` `validateRuntimeBody` validates a `role: runtime` capability's `runtime: { configHome, configFormat, artifactLayout, commandStyle, hooksSurface, sandboxTier, supportTier }`, forbids feature-only fields (`skills`/`agents`/`steps`/`contributions`/`gates`/`hooks`), and the registry exposes a `runtimes` index (currently `{}` — no descriptor authored yet). +- **ADR-58 defines the `InstallPlan`** as a pure typed projection (placements + command text + config intentions → adapters execute) but it is **not materialized**; `runtime-config-adapter-registry.cts` realizes only the adapter-selection half. + +What is missing, and what this ADR fixes: only `configFormat` is a real closed enum (5 values); `commandStyle`/`hooksSurface`/`sandboxTier` are loose strings and `artifactLayout` is an unconstrained array. This ADR **closes the vocabulary for all six axes**, decides how the seven hard-case runtimes are absorbed as data, and fixes the staged migration that drives `install.js` from descriptors. + +## Decision + +**Core principle:** every per-runtime difference is expressed as a value over a closed primitive vocabulary on six axes. A runtime that needs a shape no existing primitive expresses is supported by adding a first-party primitive (a new enum member + the code that honors it) — never by embedding arbitrary code or an open escape hatch in the descriptor. All 16 current runtimes MUST be expressible as data under the vocabulary below; any residue that genuinely cannot be is named explicitly as retained first-party code (§ Decision 8). + +### 1. `configHome` — a structured value, not a path string + +``` +configHome: { + kind: 'dot-home' | 'dot-home-nested' | 'xdg' | 'generic-agents-root', + name: string, // e.g. 'claude', 'opencode' + parent?: string, // for dot-home-nested: e.g. '.gemini' (antigravity), '.codeium' (windsurf) + env: string[], // ordered override env vars, e.g. ['CLAUDE_CONFIG_DIR'] + probe?: string[], // ordered candidate subpaths; first existing wins (antigravity, kimi) + skillsHome?: { kind, name, ... } // override when the skills dir ≠ config dir (kilo only) +} +``` + +- `dot-home` → `~/.{name}` (claude, cursor, codex, copilot, augment, trae, qwen, hermes, codebuddy, cline). +- `dot-home-nested` → `~/{parent}/{name}` (antigravity `~/.gemini/antigravity` + probe; windsurf `~/.codeium/windsurf`). +- `xdg` → `$XDG_CONFIG_HOME/{name}` ?? `~/.config/{name}` (opencode, kilo). +- `generic-agents-root` → the shared `~/.config/agents` / `~/.agents` first-existing root (kimi). + +This absorbs kilo (`skillsHome` split), kimi & antigravity (`probe`), windsurf (`dot-home-nested`). `runtime-homes.cts` becomes a pure interpreter of this value. + +`configHome` resolution is **pure and read-only** (a first-existing probe; no `mkdirSync` — verified in `runtime-homes.cts`). Any directory creation at install time is the `configFormat` permissions-writer's responsibility (opencode/kilo), never the descriptor-resolution step — so `configHome` carries no `createIfMissing` flag. + +### 2. `configFormat` — the existing closed enum (unchanged) + +`settings-json | toml | markdown | markdown-dir | none`. Cursor is `none` (it writes no settings file — its only managed file is the hooks manifest, captured by `hooksSurface`, not `configFormat`). opencode/kilo are `settings-json` with a JSONC **permissions sidecar** expressed by an optional `permissions: 'opencode-jsonc' | 'kilo-jsonc' | 'none'` sub-field (the only two runtimes that write one). + +Claude's `permissions.allow/deny` (a tool-approval allowlist) rides as a sub-field of the `settings-json` variant — it is **not** a sandbox tier (see Decision 6). + +### 3. `artifactLayout` — ADR-3660 `kinds[]`, keyed by scope + +``` +artifactLayout: { + global: ArtifactKind[], + local: ArtifactKind[] +} +ArtifactKind = { kind: 'commands'|'agents'|'skills'|'kimi-agents', destSubpath: string, prefix: string, + nesting: 'flat'|'nested', recursive: boolean, stage: string } +``` + +Scope-keying is required generally, not just for the hard case: **claude itself differs** (global = `skills/`, local = `commands/gsd/` + `agents/`), and **cline** differs (global = skills + `.clinerules/`, local = `.clinerules/` only). ADR-3660 already absorbs hermes (`destSubpath: 'skills/gsd'`), opencode/kilo (`destSubpath: 'command'` singular), and gemini (commands-only, `.toml`) via `destSubpath`/`prefix`. The `nesting`/`recursive` flags carry the ns-* router nesting + non-recursive-loader facts (#924/#28266). The descriptor's `artifactLayout` IS the ADR-3660 layout, declared per runtime; `runtime-artifact-layout.cts` becomes its lookup table. + +Each `ArtifactKind` names its per-runtime body converter (e.g. `convertClaudeCommandToGeminiToml`, the codex agent-TOML emitter) via a **closed `ConverterName` enum** — verified as 15 named first-party functions covering the 16 runtimes (three share Claude's). The converter is referenced by closed name, never embedded; closing it into a union type (vs today's open string) is what makes the "closed vocabulary" claim type-enforced rather than convention. + +### 4. `commandStyle` — closed enum (2 values) + +`slash-hyphen` (`/gsd-`, 15 runtimes) | `shell-var` (`$gsd-`, codex only). Gemini's own `gsd:`-namespaced TOML routing is a property of its command artifacts (a `commands/gsd/*.toml` layout fact), not GSD's emission style — it stays `slash-hyphen`. + +### 5. `hooksSurface` — closed enum + a hook-event dialect + +``` +hooksSurface: 'settings-json' | 'codex-hooks-json' | 'cursor-hooks-json' + | 'copilot-inline' | 'cline-rules' | 'none', +hookEvents?: 'claude' // SessionStart/PreToolUse/PostToolUse + | 'gemini' // BeforeTool/AfterTool (gemini, antigravity) + | 'opencode-subset' // settings-json surface but SessionStart/PostToolUse skipped +``` + +`settings-json` covers claude, gemini, antigravity, augment, qwen, hermes, codebuddy, opencode; the event-name and registration-subset differences ride on `hookEvents` rather than splitting the surface enum. `none` = windsurf, trae, kimi, kilo. + +### 6. `sandboxTier` — the agent-sandbox primitive (closed enum) + +`none` (default — no per-agent sandbox) | `codex-agent-sandbox` (codex's `CODEX_AGENT_SANDBOX` map of agent → `workspace-write` | `read-only`, baked into each agent `.toml`). Codex is the **only** runtime with a non-`none` value today, so this is the thinnest axis — kept distinct from `supportTier` (coverage) and from model-catalog routing. + +**Explicitly NOT on this axis** (verified, to prevent conflation): Claude's `permissions.allow/deny` is a *tool-approval allowlist* → it belongs on `configFormat`'s `settings-json` variant (Decision 2). The opencode/kilo permissions sidecar is a *filesystem read-grant* → it belongs on `configFormat`'s `permissions` sub-field (Decision 2). These three are categorically different mechanisms; only codex's per-agent sandbox mode is `sandboxTier`. + +### 7. `supportTier` — GSD coverage tier (unchanged: 1 | 2) + +`1` = fully tested first-party (claude, codex, antigravity); `2` = shipped, lower-tier (the other 13). None dropped. Drives the cross-runtime test matrix, not behavior. + +### 8. Staged consumption — author registry-only, then drive install one axis at a time + +The migration is staged the way phases 3–4 were (registry-only → consume incrementally → equivalence-proven no-op → retire the hardcoded branch). **Four of the six axes already live in dedicated modules** that `install.js` merely consumes, so driving them from the descriptor is per-axis and low-risk; the rest is staged behind a prerequisite and assembled last — **no big-bang `install()` rewrite**. + +1. **5a — author the 16 descriptors** (`capabilities//capability.json`, `role: runtime`) registry-only; nothing consumes them. The generator already validates them; the `runtimes` index populates. +2. **5b — drive `configHome`** ← descriptor (`runtime-homes.cts` already centralizes it; swap its switch for a descriptor lookup). Smallest blast radius. +3. **5c — drive `commandStyle`** ← descriptor (`runtime-slash.cts`, 2 values). Trivial. +4. **5d — drive `artifactLayout`** ← descriptor (`runtime-artifact-layout.cts`, ADR-3660; this is ADR-3660's Phase 2 / #3664 — the largest LOC reduction). +5. **5e — drive `configFormat`** ← descriptor (`runtime-config-adapter-registry.cts`) **and close the `ConverterName` enum** (Decision 3). Model-catalog routing stays orthogonal — referenced by descriptor `name`, not an axis; codex's install-time model-embedding is an implementation detail of its converter, not a 7th axis. +6. **5f — extract `hooksSurface` into its own module, then drive it** ← descriptor. `hooks-surface` is the one axis still scattered across `install.js`; its module extraction is a prerequisite, exactly as ADR-3660 was for `artifactLayout`. +7. **5g — materialize the `InstallPlan`** (ADR-58): collect the now-descriptor-driven per-axis projections into one typed value, so `install()` becomes *resolve chosen Runtime descriptor × active Feature Capabilities → `InstallPlan` → adapters execute*. This is **reachable by collection, not a rewrite** — the interleaving that made `install.js` look monolithic is already dissolved by 5b–5f *before* the plan is assembled. It is a phase-5 **deliverable**, not an aspiration. + +Each rung is its own approved-enhancement + PR + equivalence proof (`sandbox-tier`, codex-only, is tiny and rides along in 5e/5g). **Irreducible first-party code, named not hidden:** the artifact converter *functions* remain first-party code, selected by the descriptor's closed `ConverterName` — the descriptor never embeds them. + +### 9. Two axes: the descriptor dogfoods the *runtime* interface only + +Two orthogonal axes were conflated under "third-party" in earlier ADRs (grilled against #956/#999): + +- **Authorship / distribution** — *who wrote it & how it ships*: built-in (in-repo) vs third-party (installed). The welcoming, common word "plugin / third-party plugin" stays here, untouched. +- **Integration shape** — *where the code runs*: an in-host **Capability** (declarative artifacts + in-tree first-party code referenced by closed name) or a **Connected Capability** (brings its own external process / service / state — e.g. MemPalace's MCP server + database, #956). + +All 16 runtimes are authored through the same descriptor (dogfooding) — but the descriptor dogfoods the **runtime-descriptor interface only**. It does **not** validate the third-party *feature-plugin* interface, because the hard feature plugins third parties actually write are **Connected Capabilities**, whose contract — MCP-server / external-process / backend-provider contributions + a §7 trust/load gate — **does not exist yet**. Third-party *runtime* loading stays purely additive (a loader + light trust gate over the descriptor: schema validation + write-confinement to the declared `configHome` + opt-in). The third-party *feature-plugin / Connected Capability* path is a **named, tracked gap**, not de-risked by this work; #956 is its design vehicle. + +## Alternatives considered + +1. **Keep per-runtime branches; just extract helpers** — rejected: leaves the ~18-site add-a-runtime tax and the scattering ADR-857 exists to end; no path to third-party runtimes. +2. **Open escape hatch / arbitrary code in the descriptor** (a `customInstall(fn)`) — rejected: violates the closed-vocabulary principle, reintroduces code-in-data, and breaks the third-party trust story. Hard shapes are absorbed by *adding a named primitive*, reviewed first-party. +3. **Big-bang `InstallPlan` materialization** (drive all axes at once) — rejected: `install.js` is ~287 branches; a single cutover can't be equivalence-proven incrementally. Per-axis staging (Decision 8) keeps every step a provable no-op, matching phases 3–4. +4. **A single `configHome` path string** — rejected: cannot express kilo's config≠skills split, antigravity/kimi probes, or env overrides without re-scattering logic; the structured value (Decision 1) is the minimal shape that absorbs all 16. + +## Consequences + +**Positive:** adding a runtime becomes authoring one `capability.json` (no `install.js` surgery); per-runtime knowledge lives in one declarative place; the third-party door is additive; `install.js` shrinks substantially (ADR-3660 alone projects ~250 lines off the artifact axis); the descriptor dogfoods the third-party *runtime* interface (it does **not** validate the third-party *feature-plugin* / Connected Capability interface — a separate, undesigned contract). + +**Negative:** the closed vocabulary must grow (reviewed) when a genuinely new host shape appears — intentional friction, the trust boundary. The staged migration is many small PRs. The `sandboxTier` axis is thin (only codex non-`none`) and may feel speculative until a second sandboxed runtime appears. + +**Neutral:** behavior is unchanged throughout (every axis cutover is equivalence-proven); model-catalog routing stays where it is, referenced by descriptor `name`. + +## Out of scope + +Authoring the 16 descriptors and the per-axis install cutovers (the impl phases); third-party runtime loading (its own additive ADR + trust gate); the per-feature loop-hook wiring (phase-6 cleanup); moving feature `*.enabled` keys out of the central config-schema (phase-6 cleanup). + +- The **Connected Capability** contract (MCP-server / external-process / backend-provider contributions + the §7 trust/load gate) — future design, vehicle #956. +- The **hook-firing spike** — proving `loop.render-hooks` → workflow execution end-to-end with a *host-computed aggregate* (a phase-6-flavored de-risk that should land **before** phase-5 build, since #956/#999 both depend on it). +- The structural **"off means off"** rule (the host derives shared outputs from the active hook set; hooks add or are counted, never mutate host source) — an ADR-894 contribution-model concern, recorded there, not in this ADR. diff --git a/docs/adr/README.md b/docs/adr/README.md index e91265d49..f54419c10 100644 --- a/docs/adr/README.md +++ b/docs/adr/README.md @@ -56,6 +56,7 @@ See **[CONTRIBUTING.md — "Proposing an ADR or PRD"](../../CONTRIBUTING.md#prop | [660-release-from-next-head.md](660-release-from-next-head.md) | Release from the head of next; immutable release tags; @next dist-tag as the RC surface | Proposed | | [58-runtime-install-policy-module.md](58-runtime-install-policy-module.md) | Runtime Install Policy Module owns the typed install-plan projection | Accepted | | [766-claude-code-plugin-manifest-module.md](766-claude-code-plugin-manifest-module.md) | Claude Code Plugin Manifest Module owns the projection of gsd-core surfaces onto the Claude Code plugin contract | Accepted | +| [1016-runtime-capability-descriptor.md](1016-runtime-capability-descriptor.md) | Runtime Capability Descriptor | Proposed | ## Seam map