Files
msd-core/.github/PULL_REQUEST_TEMPLATE/registry-entry.md
Tom Boucher 9624167eec fix(#2810): accept the documented effortSurface axis on EoS registry entries (#2813)
* fix(#2810): accept the documented effortSurface axis on EoS registry entries

The EoS registry schema required an exact eight-key `interactions.axes`
object, while `docs/registries/README.md` and `CONTEXT.md` both documented
nine keys including `effortSurface`. An entry that faithfully mirrored its
upstream descriptor's `effortSurface` key was rejected outright.

`effortSurface` reached the runtime-descriptor vocabulary through ADR-1239
amendment #2481 (`HOST_INTEGRATION_AXES`), but the registry's hand-maintained
copy of that vocabulary never picked it up. The runtime-descriptor surface is
guarded by tests/host-integration-validator-parity.test.cjs; the registry copy
had no equivalent guard, which is what let the two drift.

Accept `effortSurface` as an OPTIONAL ninth axis validated against the
canonical ['argv','none'] rather than a required one: registry entries mirror
their upstream registry/eos-entry.json byte-for-byte, so requiring it would
retroactively invalidate every entry published before the amendment.

Adds tests/registry-axes-parity.test.cjs, which asserts that every key shared
between the registry vocabulary and HOST_INTEGRATION_AXES has an identical
enum array, plus limit-1/limit/limit+1 boundary coverage on the axes key set.

Closes #2810

* test(#2810): fail when a canonical axis is added but never mirrored

The enum-equality assertion compares only keys the registry and
HOST_INTEGRATION_AXES already share, so it is blind to the exact drift that
produced #2810: a new canonical axis appears and the registry copy is never
told. Verified by simulation — mutating an enum is caught, adding a new
canonical key is not.

Assert instead that every HOST_INTEGRATION_AXES key is either modeled by the
registry or named in an explicit NOT_MODELLED allowlist (subagentToolkit and
isolation, both dispatch sub-fields the registry collapses into its free-form
dispatch summary). Adding a canonical axis now fails until someone decides
which bucket it belongs in. The allowlist is itself guarded against going
stale.

Refs #2810

* fix(#2810): harden the axis value lookup with the CodeQL barrier pattern

Both orthogonal reviews flagged the same line: `AXES[key] !== undefined`
is not an own-property test, and the bracket reads are shaped like a
prototype-pollution sink even though the unknown-key gate above provably
makes them unreachable.

Switch the presence test to `Object.hasOwn` and add the repo's inline
literal guards (`capability-state.cts:146-155`, "Prototype-pollution guard
(inline literal, CodeQL barrier)"), which CodeQL can follow where it cannot
follow the `.includes()` filter that actually does the work.

Behavior is unchanged — re-verified all five axes key-count shapes plus a
genuine own `__proto__` property built through JSON.parse (the shape a
third-party registry PR would submit): it is rejected as an unknown key and
Object.prototype is untouched.

Refs #2810

* chore(#2810): backfill changeset PR number
2026-07-29 07:00:35 -04:00

4.5 KiB

Registry Entry PR

Using the wrong template? — Bug fix: use fix.md — New feature (not a registry listing): use feature.md — Enhancement to existing behavior: use enhancement.md

Full schema and process: docs/registries/README.md.


Registry type

  • Capability Registry entry — adds/updates one object in docs/registries/capabilities.json
  • EoS Registry entry — adds/updates one object in docs/registries/eos.json

The entry

{
  "id": "",
  "name": "",
  "type": "",
  "repo": "",
  "description": "",
  "author": "",
  "license": "",
  "enginesGsd": "",
  "install": "",
  "uninstall": "",
  "interactions": {},
  "discussion": ""
}

Required-field checklist

  • id, name, type, repo, description, author, license, enginesGsd, install, uninstall, interactions, discussion are all present and non-empty
  • (Capability entries only) interactions.loopExtensionPoints is a non-empty subset of the 12 Loop Extension Points, interactions.hookKinds ⊆ {step, contribution, gate}, and interactions.configKeys / requires / runtimeCompat / produces / consumes are present (empty arrays are fine where nothing applies)
  • (EoS entries only) protocolVersion is an integer ≥ 1, interactions.interfacePoints is a non-empty subset of the six interface points, interactions.profile is one of programmatic-cli / declarative-cli / ide, and interactions.axes has exactly the eight required axis keys plus, optionally, effortSurface (argv / none)

Ownership & non-endorsement

  • repo links to a repository I own or am the primary maintainer of — not a fork, mirror, or someone else's project
  • I understand that inclusion in this registry means only that a maintainer merged this PR — it is not an endorsement, and GSD has not reviewed, tested, audited, or verified my solution or its claimed GSD interactions
  • I understand this entry is removed only for illegal content, malware, spam, or a dead/non-functional link — never for quality — and a maintainer may remove it on that narrow basis without further notice

One entry, one PR

  • This PR adds or updates exactly one entry, in exactly one of capabilities.json / eos.json
  • I have not bundled any other registry entry, code change, or unrelated docs change into this PR

Generated file in sync

  • I ran npm run gen:registry after editing the JSON source, and this PR includes the regenerated docs/registries/capability-registry.md or docs/registries/eos-registry.md
  • I did not hand-edit the generated .md file directly — all edits were made to the JSON source

Documentation

CI enforces lint:docs for any changeset fragment typed Added / Changed / Deprecated / Removed — it must also touch a file under docs/. The JSON source and its regenerated markdown, both under docs/registries/, satisfy this.

  • This PR includes both the JSON source file and the regenerated markdown file under docs/registries/

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)
  • .changeset/ fragment added with an Added type describing the new listing

Example filled entry

{
  "id": "linear-issue-sync",
  "name": "Linear Issue Sync",
  "type": "capability",
  "repo": "some-org/gsd-cap-linear-sync",
  "description": "Mirrors ROADMAP.md items to Linear issues as a ship:post contribution.",
  "author": "Some Org <hello@some-org.example>",
  "license": "MIT",
  "enginesGsd": ">=1.6.0",
  "install": "gsd capability install https://github.com/some-org/gsd-cap-linear-sync.git#v1.0.0",
  "uninstall": "gsd capability remove linear-issue-sync",
  "interactions": {
    "loopExtensionPoints": ["ship:post"],
    "hookKinds": ["contribution"],
    "configKeys": ["linear-issue-sync.enabled"],
    "requires": [],
    "runtimeCompat": ["all"],
    "produces": ["linear-issue-links"],
    "consumes": ["ROADMAP.md"]
  },
  "discussion": "https://github.com/open-gsd/gsd-core/discussions/1234"
}