feat(#1432): capability source resolver + ledger (ADR-1244 Phase 3) (#1443)

* feat(#1432): capability source resolver + ledger (ADR-1244 Phase 3)

ADR-1244 D3 + D4 — additive, testable-in-isolation modules (the /gsd:capability
install command + consent gate are Phase 4). NEW modules only; not yet wired into
bin/install.js.

- src/capability-source.cts → resolveCapabilitySource(spec, opts): one seam, one
  adapter per source kind — local (fs copy), git (execGit clone+checkout, https/ssh/
  git transports only), npm (execNpm pack --ignore-scripts + tar, NEVER npm install),
  tarball (https download + sha512 integrity-before-extraction + tar), registry (stub).
  SECURITY: install never executes capability code (copy/extract only, --ignore-scripts);
  integrity verified before staging; symlink members rejected at interior, source-root,
  AND tar-member (verbose-listing) layers; tar-slip member paths rejected pre-extraction;
  npm specs with shell metacharacters (incl %) rejected (execNpm uses a Windows shell);
  git ext::/file:// transports + leading-dash/metachar refs rejected; atomic staging
  (.staging→rename, restore-on-failure); full Phase 1/2 validator suite + engines.gsd
  pre-check on the fetched manifest. Test seam _setCapabilitySourceHttpGet.
- src/capability-ledger.cts → per-runtime .gsd-capabilities.json install manifest:
  readLedger/writeLedger (atomic via platformWriteSync)/recordInstall (idempotent,
  prototype-pollution-guarded)/removeEntry/reconcile (orphan report, hardened against
  hostile/non-string/'..' files[] members — never throws, never oracles outside runtimeDir).
- 48 new tests (34 source incl. the full security matrix, 14 ledger). Two Codex
  adversarial rounds; all 12 findings fixed + regression-tested.
- .gitignore + eslint.config.mjs: built capability-source/ledger.cjs git+eslint-ignored
  (ADR-457 #551 migration coverage); CONTEXT glossary + INVENTORY rows + manifest regen.

Closes #1432

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(#1432): add changeset for capability source resolver + ledger

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Tom Boucher
2026-06-18 16:23:20 -04:00
committed by GitHub
parent 353f63d170
commit 1abb0d3427
10 changed files with 1973 additions and 0 deletions

View File

@@ -279,8 +279,10 @@
"audit-command-router.cjs",
"audit.cjs",
"capability-activation.cjs",
"capability-ledger.cjs",
"capability-loader.cjs",
"capability-registry.cjs",
"capability-source.cjs",
"capability-state.cjs",
"capability-validator.cjs",
"capability-writer.cjs",

View File

@@ -390,8 +390,10 @@ Full listing: `gsd-core/bin/lib/*.cjs`.
| `audit-command-router.cjs` | ADR-959 capability command router for `gsd-tools audit-uat` and `gsd-tools audit-open` — extracted from hardcoded cases in `gsd-tools.cjs`; dispatches to `uat.cjs:cmdAuditUat` and `audit.cjs:{auditOpenArtifacts,formatAuditReport}`; phase 4d-impl-3 |
| `audit.cjs` | Audit dispatch, audit open sessions, audit storage helpers |
| `capability-activation.cjs` | Capability activation resolver shared by config validation and capability-state consumers — resolves registry-owned config keys from raw runtime config without re-centralizing migrated settings |
| `capability-ledger.cjs` | Per-runtime install ledger (ADR-1244 D4) — atomic read/write of `.gsd-capabilities.json` recording `{ id, version, source, integrity, files[], sharedEdits[] }` per installed capability; exports `readLedger`/`writeLedger`/`recordInstall`/`removeEntry`/`reconcile` (orphan detection); atomic commit point and reconciliation basis for Phase-4 upgrade/remove |
| `capability-loader.cjs` | Runtime Capability Registry overlay (ADR-1244 D2) — `loadRegistry({ includeInstalled })` composes the frozen first-party registry with a validated installed overlay read from `$GSD_HOME/.gsd/capabilities/<id>/` (global) and `<projectRoot>/.gsd/capabilities/<id>/` (project); first-party-wins on id/skill/agent/config collisions, reserved-namespace rejection, load-time `engines.gsd` re-gate (skip-with-warning), and gate-kind fail-closed via `_overlay.blockedGates`; composes through the canonical `buildRegistry` so derived views never drift |
| `capability-registry.cjs` | Generated central Capability Registry — role-partitioned index of all co-located capability declarations (`capabilities/<id>/capability.json`); emitted by `scripts/gen-capability-registry.cjs --write` (ADR-894 §5) |
| `capability-source.cjs` | Capability source resolver (ADR-1244 D3) — `resolveCapabilitySource(spec, opts)` fetches and stages a capability from local path, git (https/ssh/git transports only), npm pack (no lifecycle scripts), tarball (sha512 integrity verify before extraction), or registry (stub); tar-slip/symlink rejection; atomic staging to `$GSD_HOME/.gsd/capabilities/<id>/`; no capability code executes during install |
| `capability-state.cjs` | Unified capability-state resolver (ADR-857 phase 4b/6) — composes install profile, runtime surface, and config activation into one per-capability view consumed by workflow hook rendering; exports pure `resolveCapabilityState`, reusable `resolveCapabilityRuntimeState`, and I/O handler `cmdCapabilityState`; command surface: `gsd-tools capability state [--config-dir <path>]` emitting `{ runtimeConfigDir, capabilities[] }` |
| `capability-validator.cjs` | Shared runtime-callable capability validator (ADR-1244 D2) — extracted from `scripts/gen-capability-registry.cjs` so the build-time generator and the runtime overlay loader share ONE validation implementation (generative-parity guarded); exports `validateCapability`/`validateCrossCapability`/`validateVersionEnvelope`/`validateConsumesGlobal`/… plus the closed-vocabulary sets and `SEMVER_RE` |
| `capability-writer.cjs` | Capability State Writer (ADR-1213) — write-side inverse of the resolver; projects desired per-capability enabled/gates onto surface + config substrates, then re-resolves (assert-and-report); exports `setCapabilityState` and I/O handler `cmdCapabilitySet`; command surface: `gsd-tools capability set <id> [--on\|--off] [--gate <key>=<true\|false>]` |