From d7703657530bd0281299e5e89dcea2b0347ca616 Mon Sep 17 00:00:00 2001 From: Tom Boucher Date: Sun, 2 Aug 2026 14:02:58 -0400 Subject: [PATCH] docs(#2999): document the takeover process for a capability, reviewer lane, or EoS integration (#3000) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * docs(#2999): document the capability / reviewer-lane / EoS takeover process The capability ecosystem documented a complete forward lifecycle — develop, publish, version, import, update, remove, turn off — but nothing covering a change of maintainer for an entry that already exists. Adds docs/how-to/take-over-a-capability-or-eos.md defining four takeover modes (consensual handoff, adoption fork, first-party absorption, retirement), the PR shape each takes, a per-surface snapshot of the inherited user-visible contract, and an install-continuity checklist. Also corrects .github/PULL_REQUEST_TEMPLATE/registry-entry.md, which directed contributors to a 'Registry' Discussions category that does not exist — the category is named 'EoS Registry' per docs/registries/README.md, and because 'discussion' is a required field the thread must exist before the PR is opened, so the wrong name stalled contributors at the first required step. Closes #2999 Co-Authored-By: Claude Opus 5 * chore(#2999): backfill changeset pr number to 3000 Co-Authored-By: Claude Opus 5 --------- Co-authored-by: sim Co-authored-by: Claude Opus 5 --- .../2999-capability-eos-takeover-process.md | 11 ++ .../PULL_REQUEST_TEMPLATE/registry-entry.md | 2 +- docs/README.md | 1 + docs/how-to/take-over-a-capability-or-eos.md | 141 ++++++++++++++++++ 4 files changed, 154 insertions(+), 1 deletion(-) create mode 100644 .changeset/2999-capability-eos-takeover-process.md create mode 100644 docs/how-to/take-over-a-capability-or-eos.md diff --git a/.changeset/2999-capability-eos-takeover-process.md b/.changeset/2999-capability-eos-takeover-process.md new file mode 100644 index 000000000..8d2628a05 --- /dev/null +++ b/.changeset/2999-capability-eos-takeover-process.md @@ -0,0 +1,11 @@ +--- +type: Added +pr: 3000 +--- +**New how-to: [Take over a capability, reviewer lane, or EoS integration](../docs/how-to/take-over-a-capability-or-eos.md).** The capability ecosystem documented a complete forward lifecycle — develop, publish, version, import, update, remove, turn off — but nothing covering a change of *maintainer* for an entry that already exists. There is no `gsd capability transfer` command and no rename tooling, and `docs/registries/README.md` specifies submission and the narrow removal policy but never transfer, so a would-be adopter had no documented path and a reviewing maintainer had no stated bar. + +The guide defines four takeover modes and the PR shape each one takes. **T1 — consensual handoff** keeps the `id` and the entry, changes only `repo` / `author` / `install` / `uninstall`, and requires a permalink to the outgoing author's public handoff comment in the entry's Discussion. That permalink is mandatory rather than advisory because entry-update authorship is not verified anywhere: `scripts/registry-schema.cjs` and `npm run validate:registry` check an entry's shape, not who is changing it, and the registry-entry PR template's "`repo` links to a repository I own" is a self-attestation — so a PR repointing `repo` and `author` at an unrelated account passes every automated gate, and the reviewing maintainer is the only control. **T2 — adoption fork** takes a new `id`, opens a new Discussion, and leaves the original entry untouched, because the narrow removal policy removes an entry only for illegal content, malware, spam, or a dead link and never for staleness or abandonment: an abandoned-but-working entry can never be reclaimed, so adoption is always additive and the original `id` stays taken. **T3 — first-party absorption** routes through `approved-feature` plus an ADR, lands under `capabilities//capability.json` per ADR-894, annotates rather than deletes the registry entry, and requires a migration note telling existing users to `gsd capability remove ` first — config keys are exclusive to one capability and skill/agent stems must be unique, so a first-party capability that collides with an installed overlay wins *silently*, leaving the user running code they did not think they were running. **T4 — retirement** is restricted to the four narrow grounds with evidence in the PR body. + +Around the modes the guide adds an evidence pack, license and reserved-prefix and consent gates, a per-surface snapshot of the inherited user-visible contract (`loopExtensionPoints` / `hookKinds` / `configKeys` / `requires` / `runtimeCompat` for Feature Capabilities; `slug` / `flags` / `reviewsSection` uniqueness across the merged first-party and overlay set for reviewer lanes; `protocolVersion`, `interfacePoints`, `profile` and the eight ADR-1239 axes for EoS integrations), and an install-continuity checklist covering the failure modes that break existing consumers — `id` continuity, since consent is stored per `(realpath(projectRoot), capability id)` and an `id` change re-prompts every installed project and orphans the update path; re-stating `integrity` and `provenance` after a rebuild under new ownership; holding the executable-surface set steady so the handoff is not itself a consent event; and not narrowing `engines.gsd` without a matching `compatVersions` row. Post-takeover obligations note that a Release must be cut under the new repo, since there is no re-registration and both the shields badge and the `releases/latest` permalink render live from `repo`. The two enforcement gaps — unverified entry-update authorship, and the absence of any `id` migration path — are stated explicitly in the guide so the process is not mistaken for something CI verifies. + +**Fixed alongside:** `.github/PULL_REQUEST_TEMPLATE/registry-entry.md` directed contributors to file their Discussion in a `Registry` category that does not exist. `docs/registries/README.md` names the category `EoS Registry` and explicitly notes the name is misleading because it carries threads for all three catalogs. Because `discussion` is a required field, the thread must exist *before* the entry's PR is opened — so a contributor following the template stalled at the first required step of the submission process. (#2999) diff --git a/.github/PULL_REQUEST_TEMPLATE/registry-entry.md b/.github/PULL_REQUEST_TEMPLATE/registry-entry.md index e9baa3601..4e7820a42 100644 --- a/.github/PULL_REQUEST_TEMPLATE/registry-entry.md +++ b/.github/PULL_REQUEST_TEMPLATE/registry-entry.md @@ -72,7 +72,7 @@ Full schema and process: [docs/registries/README.md](../../docs/registries/READM ## Checklist - [ ] `npm run validate:registry` passes locally against my entry -- [ ] `discussion` links to a GitHub Discussion in the `Registry` category (or notes that one will be created on merge, per [docs/registries/README.md](../../docs/registries/README.md)) +- [ ] `discussion` links to a GitHub Discussion in the `EoS Registry` category — which, despite its name, carries threads for all three registries (or notes that one will be created on merge, per [docs/registries/README.md](../../docs/registries/README.md)) - [ ] `.changeset/` fragment added with an `Added` type describing the new listing --- diff --git a/docs/README.md b/docs/README.md index 54183964c..6c4576110 100644 --- a/docs/README.md +++ b/docs/README.md @@ -38,6 +38,7 @@ Language versions: [English](README.md) · [Português (pt-BR)](pt-BR/README.md) - [Develop a Capability for GSD 1.5+](how-to/develop-a-capability.md) — add feature Capabilities, hook fragments, and registry entries - [Ship a reviewer lane in your capability](how-to/ship-a-reviewer-lane.md) — declare a `reviewer` body so `/gsd-review` discovers, invokes, and renders your external review CLI or model endpoint - [List your reviewer lane in the registry](how-to/list-your-reviewer-lane.md) — publish a lane you have built to the Reviewer Lane Registry so other people can find and install it +- [Take over a capability or EoS integration](how-to/take-over-a-capability-or-eos.md) — assume maintainership of an existing third-party capability, reviewer lane, or EoS host integration through a handoff, an adoption fork, first-party absorption, or a de-listing - [Add or update a host's integration](how-to/add-or-update-a-host-integration.md) — set a host's documentation-sourced `runtime.hostIntegration` axes (ADR-1239 Phase A), with the `undocumented` sentinel rule - [Turn a capability off (and keep it off)](how-to/turn-a-capability-off.md) — disable a capability via the surface, or gate individual hooks off without removing the capability - [Drive GSD from a tracker issue](how-to/drive-gsd-from-a-tracker-issue.md) — start a phase from a GitHub, Linear, or Jira issue diff --git a/docs/how-to/take-over-a-capability-or-eos.md b/docs/how-to/take-over-a-capability-or-eos.md new file mode 100644 index 000000000..8e90459b5 --- /dev/null +++ b/docs/how-to/take-over-a-capability-or-eos.md @@ -0,0 +1,141 @@ +# How to take over a capability, reviewer lane, or EoS integration + +A **takeover** changes who maintains something that already exists and is already listed — a third-party Feature Capability, a reviewer lane, or an Embeddable Orchestration System (EoS) host integration. + +There is no `gsd capability transfer` command and no rename tooling. A takeover is a governance process executed entirely through registry pull requests and the entry's GitHub Discussion thread. Everything below is manual and auditable by design. + +## Decide what you are taking over + +| Surface | Manifest | Registry source | Ships in | +|---|---|---|---| +| First-party capability | `capabilities//capability.json` | none — generated registry only | the `gsd-core` release | +| Third-party Feature Capability | `capability.json`, `role: "feature"` | `docs/registries/capabilities.json` | the author's own repo | +| Third-party reviewer lane | `capability.json`, `role: "reviewer"` | `docs/registries/reviewers.json` | the author's own repo | +| Third-party EoS host integration | ADR-1239 host plugin (no `capability.json` in `gsd-core`) | `docs/registries/eos.json` | the author's own repo | + +A first-party capability has no registry entry and no external owner — changing who works on it is ordinary contribution, not a takeover. Use this guide only when an external `repo` and `author` are recorded somewhere under `docs/registries/`. + +## Pick the takeover mode + +| Mode | Entry condition | End state | +|---|---|---| +| **T1 — Consensual handoff** | The current author agrees, in public, to hand the project off | Same `id`, same entry, new `repo` and `author` | +| **T2 — Adoption fork** | The author is unreachable but the linked repo still works | **New** `id`, new entry, new Discussion; the original entry stays | +| **T3 — First-party absorption** | GSD absorbs the surface into `capabilities//` | In-repo capability; the registry entry is annotated, not deleted | +| **T4 — Retirement** | Illegal content, malware, spam, or a dead link | Entry removed | + +> **The narrow removal policy constrains every mode.** A merged entry is removed only for illegal content, malware, spam, or a repo that is dead or completely non-functional. It is **never** removed for staleness, quality, abandonment, or a maintainer's disagreement with its design. An abandoned-but-working entry therefore cannot be reclaimed — T2 is always additive, and the original `id` stays taken forever. + +## Step 1 — Build the evidence pack + +Collect all of this before opening anything. Every later step reads from it. + +- The current registry object, copied verbatim from `capabilities.json`, `reviewers.json`, or `eos.json`. +- The upstream `capability.json` (or, for EoS, the host plugin's own manifest), copied verbatim. +- `id`, `author`, `repo`, `license`, `enginesGsd`, and — for EoS — `protocolVersion`. +- The entry's `discussion` URL and its full comment history. +- A liveness probe of `repo`: is the default branch reachable and non-empty? If the link is dead, the correct mode is **T4**, not T2. +- The upstream release history — the shields badge and the update channel both read from `repo`, so a repo with no releases has no update channel at all. + +## Step 2 — Verify you are allowed to take it over + +**License gate.** T2 and T3 both require a fork or a derivative. If `license` is `UNLICENSED` or `Proprietary`, stop — neither mode is available without written permission from the rights holder. + +**Reserved-prefix gate.** The `gsd-`, `gsd-core-`, and `anthropic-` prefixes are reserved for first-party, and a third-party id claiming one is rejected at the conformance gate. You may only move into a reserved prefix as part of **T3**. + +**Consent gate (T1 only).** The handoff must be a public comment in the entry's Discussion thread, posted from the GitHub account that owns `repo`. A DM, an email, or a comment from any other account is not sufficient. The reason is structural: the registry-entry PR template's ownership statement is self-attested and no automated gate checks who is changing `repo` or `author`, so the Discussion permalink is the only auditable record that the handoff happened. + +**Unreachability record (T2 only).** Document at least three contact attempts across at least 30 days — an issue on the upstream repo, a comment on the entry's Discussion, and one more public channel — each dated and linkable. Paste these into the T2 PR body. + +## Step 3 — Snapshot the inherited contract + +These fields are the user-visible contract. A takeover **preserves** them. If you intend to change them, do that afterward as a normal version bump under your own maintainership — never inside the ownership-change PR. + +**Feature Capability** — `interactions.loopExtensionPoints` (which of the 12 Loop Extension Points it registers on), `hookKinds`, `configKeys`, `requires`, `runtimeCompat`, `produces`, `consumes`; plus the manifest's `skills` and `agents` stems, which must stay unique across the merged first-party and overlay set. + +**Reviewer lane** — `interactions.slug`, `flags`, `transport`, `evidenceClass`, `reviewsSection`, `requiresBinaries`, `configKeys`. The `slug`, `flags`, and `reviewsSection` must remain unique across the merged first-party and overlay set; a collision breaks `/gsd-review` for anyone running both lanes. + +**EoS host integration** — `protocolVersion`, `interactions.interfacePoints` (the subset of `command`, `dispatch`, `model`, `hooks`, `state`, `artifact` it binds), `interactions.profile`, and all eight required axes: `embeddingMode`, `commandSurface`, `dispatch`, `modelMode`, `hookBus`, `stateIO`, `transport`, `runtime` — plus `effortSurface` if the entry already carries it. + +## Step 4 — Work the continuity checklist + +Each item below breaks an existing install if you get it wrong. + +- **Keep the `id`.** Consent is stored per `(realpath(projectRoot), capability id)`. Changing the `id` re-prompts consent in every project that has the capability installed, and orphans the old `gsd capability update ` path. +- **Re-state `integrity`.** The `sha512-` hash is computed over the published artifact. A rebuild under new ownership produces a new hash, and anyone who pinned with `--integrity` will fail verification until you publish and communicate the new value. +- **Re-state `provenance`.** `sourceRepo` and `commit` must point at the new repository and the exact commit you published. +- **Re-consent on executable-surface change.** If the set of hooks, MCP servers, or command modules changes, installers are re-prompted. Keep it identical through the takeover so the handoff itself is not a consent event. +- **Do not narrow `engines.gsd`.** If you raise the floor, add the corresponding `compatVersions` row so older GSD versions can still resolve a working version. +- **Make `install` and `uninstall` copy-pasteable against the new repo.** They are exact commands, not descriptions, for capability and reviewer entries. +- **Do not drop `protocolVersion`** on an EoS entry. It must stay an integer of 1 or greater and must still name the ADR-1239 protocol the integration actually implements. + +## Step 5 — Execute the mode + +All registry work follows the standard submission process: fork, edit exactly one JSON object, run `npm run gen:registry`, commit **both** the JSON source and the regenerated markdown, and open one PR from a `docs/-` branch using the registry-entry PR template. Never hand-edit the generated `.md` — the `gen:registry --check` drift gate rejects it. **One entry, one PR**, always. + +### T1 — Consensual handoff + +1. Change only `repo`, `author`, `install`, and `uninstall` — plus `homepage` and `license` if they genuinely changed. +2. Leave `id`, `discussion`, and the entire `interactions` object byte-identical. +3. Put the permalink to the outgoing author's public handoff comment in the PR body. Without it the PR is indistinguishable from an entry hijack and a maintainer should decline it. +4. Run `npm run validate:registry` locally. +5. Add a `.changeset/` fragment typed `Changed`. + +### T2 — Adoption fork + +1. Fork upstream under your own account, honoring the license. +2. Choose a **new** `id`. The original is permanently taken. +3. Open a new Discussion in the `EoS Registry` category — which, despite its name, carries threads for all three registries. The `discussion` field is required, so the thread must exist before the PR. +4. State in `description` that this is a maintained fork of the original id, and link it. +5. Leave the original entry completely untouched. Its install command keeps resolving to the original repo forever; that is the intended behavior, not a bug to route around. +6. Add a `.changeset/` fragment typed `Added`. + +### T3 — First-party absorption + +Absorption is a feature-scale change, so the contribution rules apply before any code: an issue carrying `approved-feature`, and an ADR recording the decision. + +1. Land the capability as `capabilities//capability.json` per ADR-894, with `role`, `tier`, and a `requires` list that is acyclic and tier-monotone. Declare `runtimeCompat`. +2. Reserved prefixes are now available to you, and first-party wins every collision — id, skill and agent stems, config keys, command families. +3. Regenerate the capability registry with `scripts/gen-capability-registry.cjs --write`. +4. Update `CONTEXT.md` domain terms, `docs/README.md`, and the surface docs the capability touches. +5. **Do not delete the registry entry.** Absorption is not one of the four removal grounds. Note the supersession in `description` through a separate `Changed` PR. +6. **Publish a migration note telling existing users to run `gsd capability remove ` first.** Config keys are exclusive to one capability and skill/agent stems must be unique; a first-party capability that collides with an installed overlay wins silently, leaving the user running code they did not think they were running. + +### T4 — Retirement + +1. Cite exactly one of the four grounds — illegal, malware, spam, or dead/non-functional link — and put the evidence in the PR body. +2. Remove the single object, regenerate, and commit both files. +3. Add a `.changeset/` fragment typed `Removed`. + +Staleness, low quality, an unmaintained-but-working project, or a design you would have built differently are **not** grounds. If that is your situation, the mode is T2. + +## Step 6 — Post-takeover obligations + +- **Cut a GitHub Release under the new repo.** There is no re-registration on new versions: the shields badge and the `releases/latest` permalink render live from `repo`, so releases are the update channel forever. A takeover with no release leaves consumers with a badge that never moves. +- **Bump `version`** in the manifest, and add a `compatVersions` row if you raised the `engines.gsd` floor. +- **Publish the new `integrity` hash and `provenance` object.** +- **Keep the Discussion thread.** It is the continuity record across maintainers, and it carries the community's upvotes and experience reports. + +## What a takeover must not do + +- Change `interactions` in the same PR as the ownership change. Split them. +- Repoint `repo` at a fork while the original is alive and maintained. That is an entry hijack, not a takeover. +- Reclaim an `id` that is still listed. +- Remove a working entry in order to make room for a replacement. +- Rename into `gsd-`, `gsd-core-`, or `anthropic-` outside of T3. +- Bundle the change with any other registry entry, code change, or unrelated docs edit. + +## Known gaps in the enforcement path + +Two parts of this process rest on human judgment rather than an automated gate. Both are worth knowing before you rely on the process. + +1. **Entry-update authorship is unverified.** `scripts/registry-schema.cjs` and `npm run validate:registry` check the shape of an entry, not who is changing it. A PR that repoints `repo` and `author` at an unrelated account passes every automated gate. The reviewing maintainer is the only control, which is why the public handoff permalink in Step 5 is mandatory rather than advisory. +2. **There is no `id` migration path.** With no transfer or rename tooling, `id` continuity is manual, and an `id` change is a hard break for every installed consumer — a consent re-prompt, a broken update path, and an orphaned ledger entry. + +## Related + +- [Develop a Capability for GSD 1.5+](develop-a-capability.md) +- [How to publish a capability so others can install it](publish-a-capability.md) +- [Version a capability](version-a-capability.md) +- [Remove a capability](remove-a-capability.md) +- [GSD Registries: schema and submission process](../registries/README.md)