refactor: drop 12 runtimes, keep Claude, Codex, OpenCode, Cursor, ZCode, Antigravity
Removes kilo, kimi, kimi-code, copilot, windsurf, augment, trae, qwen, hermes, cline, codebuddy and pi end to end: capability descriptors, installer branches and converters (bin/install.js 14.9k -> 11.2k lines), TypeScript converters, hook surfaces and runtime homes, review lanes qwen/kimi-code, the two pi migrations, Kimi payload normalization in the hook guards, dead hostBehaviors vocabulary, launcher home probes, fixtures, runtime-specific tests and the prose that presented them as supported. Installer output for the six kept runtimes is byte-identical to before the prune. The Kimi tool-vocabulary tests in workflow-guard, read-guard and read-injection-scanner are left in place pending a decision.
This commit is contained in:
@@ -174,16 +174,16 @@ Runtime capabilities describe how MSD projects its artefacts onto one host CLI.
|
||||
| Axis | Field | Type summary |
|
||||
|---|---|---|
|
||||
| Config home | `runtime.configHome` | Structured object with `kind` (`dot-home` \| `dot-home-nested` \| `xdg` \| `generic-agents-root`), `name`, optional `parent`, `env[]`, `probe[]`, `probeExists`, `skillsHome`. `probeExists` is an optional sub-path applied to probe candidates: for `generic-agents-root` it is a hard filter (a candidate qualifies only if `<candidate>/<probeExists>` exists); for `dot-home-nested` it is a preference that makes probing pick the candidate MSD owns (e.g. `msd-core/VERSION`) over a bare-existing sibling before falling back — see ADR-1016 and #213/#217. |
|
||||
| Local config dir | `runtime.localConfigDir` | Required dot-prefixed string. The runtime's **local** content-rewrite directory — the `./` target MSD stamps into rewritten artefact bodies (e.g. `./.claude/` → `./<localConfigDir>/`) and the local install dir basename. Backs `getDirName()` (registry-derived, #1679). Usually `.<runtime>` (the runtime's home dot-dir), but **three runtimes diverge** because they read MSD's content from a non-home directory: `copilot` → `.github` (GitHub Copilot reads custom instructions from `.github/copilot-instructions.md` / `.github/instructions/`; see `convertClaudeToCopilotContent` rewrites in `src/runtime-artifact-conversion.cts`), `antigravity` → `.agents` (local agent/workflow dir; see the antigravity rewrites in `src/runtime-artifact-conversion.cts`), `kimi` → `.kimi-code`. Distinct from `configHome.name` (the **global** install home, which for these three is `.copilot` / `antigravity` / `agents`). Byte-parity-proven against the prior hand-maintained mapping by the golden-install-parity harness. |
|
||||
| Local config dir | `runtime.localConfigDir` | Required dot-prefixed string. The runtime's **local** content-rewrite directory — the `./` target MSD stamps into rewritten artefact bodies (e.g. `./.claude/` → `./<localConfigDir>/`) and the local install dir basename. Backs `getDirName()` (registry-derived, #1679). Usually `.<runtime>` (the runtime's home dot-dir), but **one runtime diverges** because it reads MSD's content from a non-home directory: `antigravity` → `.agents` (local agent/workflow dir; see the antigravity rewrites in `src/runtime-artifact-conversion.cts`). Distinct from `configHome.name` (the **global** install home, which for it is `antigravity`). Byte-parity-proven against the prior hand-maintained mapping by the golden-install-parity harness. |
|
||||
| Config format | `runtime.configFormat` | Closed enum: `settings-json` \| `toml` \| `markdown` \| `markdown-dir` \| `none`. |
|
||||
| Artefact layout | `runtime.artifactLayout` | Object with `global` and `local` arrays of `ArtifactKind` (`kind`, `destSubpath`, `prefix`, `nesting`, `recursive`, `stage`). |
|
||||
| Command style | `runtime.commandStyle` | Closed enum: `slash-hyphen` \| `shell-var`. |
|
||||
| Hooks surface | `runtime.hooksSurface` | Closed enum: `settings-json` \| `codex-hooks-json` \| `cursor-hooks-json` \| `copilot-inline` \| `cline-rules` \| `kimi-hooks-toml` \| `none`. |
|
||||
| Hooks surface | `runtime.hooksSurface` | Closed enum: `settings-json` \| `codex-hooks-json` \| `cursor-hooks-json` \| `none`. |
|
||||
| Sandbox tier | `runtime.sandboxTier` | Closed enum: `none` \| `codex-agent-sandbox`. |
|
||||
| Support tier | `runtime.supportTier` | Integer: `1` (fully tested first-party) \| `2` (shipped, lower coverage). |
|
||||
| Install surface | `runtime.installSurface` | Closed enum: `settings-json` \| `codex-toml` \| `copilot-instructions` \| `cline-rules` \| `cursor-hooks-json` \| `profile-marker-only`. |
|
||||
| Install surface | `runtime.installSurface` | Closed enum: `settings-json` \| `codex-toml` \| `cursor-hooks-json` \| `profile-marker-only` \| `none`. |
|
||||
| Shared settings | `runtime.writesSharedSettings` | boolean. Whether the runtime writes a shared `settings.json`. |
|
||||
| Permission writer | `runtime.permissionWriter` | `null` \| `"opencode"` \| `"kilo"` \| `"antigravity"`. The finish-time permissions-sidecar writer. |
|
||||
| Permission writer | `runtime.permissionWriter` | `null` \| `"opencode"` \| `"antigravity"`. The finish-time permissions-sidecar writer. |
|
||||
| Extended hook events | `runtime.extendedHookEvents` | string[] over a closed vocabulary: `SubagentStop`, `Stop`, `PreCompact`, `FileChanged`, `BeforeAgent`, `AfterAgent`, `BeforeModel`, `SubagentStart`. |
|
||||
|
||||
### `hostBehaviors`
|
||||
@@ -240,7 +240,7 @@ All 12 shipped lane declarations carry all 14 fields below.
|
||||
| `slug` | string | Lane identity; grammar `^[a-z0-9][a-z0-9_-]*$`. May use `_` (`lm_studio`, `llama_cpp`) even where the capability *folder id* is kebab-case (`lm-studio`). |
|
||||
| `flags` | string[] | User-facing CLI flags that select this lane. A lane may declare more than one — `antigravity` declares `--antigravity` and `--agy`. 12 lanes declare 13 flags in total. |
|
||||
| `transport` | closed enum | `spawn` \| `openai-http`. |
|
||||
| `probe` | object | Availability check. `probe.kind` is a closed enum: `command-exists` \| `command-capability` \| `http-reachable`. `command-capability` additionally takes `binary`, `needle`, and a **required** `timeoutMs` — it exists because a bare binary name can be ambiguous (`kimi` is claimed by both the Kimi Code CLI and the legacy Python `kimi-cli`), and the timeout bound is mandatory because an unbounded `--help \| grep` probe is this repo's named Unbounded Subprocesses defect. |
|
||||
| `probe` | object | Availability check. `probe.kind` is a closed enum: `command-exists` \| `command-capability` \| `http-reachable`. `command-capability` additionally takes `binary`, `needle`, and a **required** `timeoutMs` — it exists because a bare binary name can be ambiguous (the same binary name can be claimed by more than one CLI), and the timeout bound is mandatory because an unbounded `--help \| grep` probe is this repo's named Unbounded Subprocesses defect. |
|
||||
| `invoke` | object | Shape is selected by `transport`. For `spawn`: `binary`, `args[]`, `promptChannel` (`stdin` \| `argv` \| `argv-file-ref` \| `none`), `outputChannel` (`stdout` \| `file-arg`), `outputArg` (required when `outputChannel` is `file-arg`), `modelArg` (string or `null`), `effortChannel` (`none` \| `argv` \| `env`), `env` (optional; an object of environment name/value pairs, string values only, merged over the inherited environment for that one spawn — keys must match the portable environment-name grammar `[A-Za-z_][A-Za-z0-9_]*`, which is a portability policy rather than an OS limit, and `__proto__` is refused because it would be dropped before reaching the child). For `openai-http`: `hostConfigKey`, `defaultHost`, `path`, `modelDiscovery` (`none` \| `first-from-models-endpoint`), `fallbackModel`, `effortChannel`. `args` supports the `{{model}}`, `{{prompt}}`, `{{effort}}`, and `{{output}}` placeholders. **Every field in this object is disclosed at install and bound to the consent signature** — `env` and `defaultHost` by name in the consent prompt, the rest through a residual, so any change to a declared `invoke` field forces re-consent. `env` additionally **refuses execution-primitive names** — `PATH`, `NODE_OPTIONS`, `LD_PRELOAD`, `DYLD_INSERT_LIBRARIES`, `BASH_ENV`, `PYTHONPATH`, `PERL5OPT`, `RUBYOPT`, `GIT_SSH_COMMAND`, `JAVA_TOOL_OPTIONS` and siblings, matched case-insensitively (Windows environment lookup is). A lane needing a specific executable declares an absolute `binary` rather than reshaping the child's `PATH`. That denylist is defence in depth and not the boundary: it cannot be complete against an arbitrary child, and disclosure runs before validation, so install-time consent — which shows every declared pair and warns on execution-primitive names — is what actually gates them. |
|
||||
| `timeoutFloorMs` | number | Measured per-lane floor. Lane divergence here is real and correct — the descriptor's job is to declare divergence in one place, not to promise uniformity. |
|
||||
| `timeoutConfigKey` | string or `null` | Federated config key holding this lane's outer timeout override, in SECONDS, e.g. `review.timeouts.antigravity`. Falls back to `timeoutFloorMs` when unset or invalid (#3274). |
|
||||
@@ -249,7 +249,7 @@ All 12 shipped lane declarations carry all 14 fields below.
|
||||
| `evidenceClass` | closed enum | `source-grounded` \| `diff-only` (diff-only findings are down-weighted in consensus). |
|
||||
| `requiresBinaries` | string[] | Extra binaries the lane needs beyond `invoke.binary`. |
|
||||
| `promptBudgetKey` | string or `null` | Federated config key bounding prompt size. |
|
||||
| `modelConfigKey` | string or `null` | Federated config key naming the model, e.g. `review.models.kimi-code`. |
|
||||
| `modelConfigKey` | string or `null` | Federated config key naming the model, e.g. `review.models.codex`. |
|
||||
| `handler` | closed enum or `null` | `antigravity` \| `openai-compatible` \| `opencode` \| `null`. |
|
||||
|
||||
**`handler` is a closed enum of first-party handler names, not an open escape hatch.** [ADR-1016](../adr/1016-runtime-capability-descriptor.md) explicitly rejected "arbitrary code in the descriptor"; hard shapes are absorbed by adding a named primitive that is reviewed first-party. The consequence, stated plainly: **a third-party reviewer lane is strictly data-only.** A plugin can ship a lane, but not a quirky lane that needs imperative code — a lane requiring behavior beyond the closed `handler` set is not expressible and must be proposed and merged first-party.
|
||||
|
||||
Reference in New Issue
Block a user