feat(#1430): versioned capability manifest + native stamping (ADR-1244 Phase 1) (#1436)

* feat(#1430): versioned capability manifest + native stamping (ADR-1244 Phase 1)

Make the capability manifest versioned — the data substrate the Capability
Ecosystem (ADR-1244) keys off:

- capability.json gains a REQUIRED semver `version` plus the optional
  ecosystem envelope (`engines.gsd`, `compatVersions`, `integrity`,
  `provenance`); the build-time conformance validator enforces them via a new
  `validateVersionEnvelope()` (exported for the Phase 2 runtime overlay).
- All 32 native capabilities stamped with `version` (= package version,
  lockstep) + `engines.gsd`; `sync-manifest-versions.cjs` gains a glob sweep
  that keeps them in sync, and the issue-844 regression guard is extended.
- Strict SemVer 2.0.0 grammar blocks metacharacter/space/unicode smuggling in
  version strings; range/integrity fields are shape-validated (satisfaction
  and the load-time gate are deferred to Phase 2/4).
- Capability rel-paths emitted forward-slash for cross-platform git correctness.

Closes #1430

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

* docs(#1430): add changeset for versioned capability manifest

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 10:45:53 -04:00
committed by GitHub
parent 8040a6bac0
commit 2421cf1b4a
40 changed files with 1321 additions and 115 deletions

View File

@@ -0,0 +1,5 @@
---
type: Added
pr: 1436
---
**Capability manifests are now versioned** — every `capability.json` carries a required semver `version`, plus optional `engines.gsd`, `compatVersions`, `integrity` and `provenance` fields, enforced by the capability conformance validator. First-party capabilities are version-stamped in lockstep with the GSD release. Foundation (ADR-1244 Phase 1) for installing, upgrading, and removing capabilities in later releases.

View File

@@ -1,12 +1,23 @@
{
"id": "ai-integration",
"role": "feature",
"version": "1.5.1-dev.0",
"title": "AI design contract",
"description": "AI-SPEC design contract workflow for phases that build AI systems; owns the AI integration command, agents, and workflow.ai_integration_phase activation key.",
"tier": "full",
"requires": [],
"runtimeCompat": { "supported": ["*"], "unsupported": [] },
"skills": ["ai-integration-phase"],
"engines": {
"gsd": ">=1.6.0"
},
"runtimeCompat": {
"supported": [
"*"
],
"unsupported": []
},
"skills": [
"ai-integration-phase"
],
"agents": [
"gsd-framework-selector",
"gsd-ai-researcher",
@@ -24,9 +35,15 @@
"steps": [
{
"point": "plan:pre",
"ref": { "skill": "ai-integration-phase" },
"produces": ["AI-SPEC.md"],
"consumes": ["CONTEXT.md"],
"ref": {
"skill": "ai-integration-phase"
},
"produces": [
"AI-SPEC.md"
],
"consumes": [
"CONTEXT.md"
],
"when": "workflow.ai_integration_phase",
"onError": "skip"
}

View File

@@ -1,17 +1,27 @@
{
"id": "antigravity",
"role": "runtime",
"version": "1.5.1-dev.0",
"title": "Antigravity",
"description": "Google Antigravity IDE — nested under ~/.gemini/antigravity; probed across 1.x and 2.x layouts; Gemini hook event dialect; nested skill layout; tier-1 support.",
"tier": "core",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtime": {
"configHome": {
"kind": "dot-home-nested",
"name": "antigravity",
"parent": ".gemini",
"env": ["ANTIGRAVITY_CONFIG_DIR"],
"probe": ["antigravity", "antigravity-ide", "antigravity-cli"]
"env": [
"ANTIGRAVITY_CONFIG_DIR"
],
"probe": [
"antigravity",
"antigravity-ide",
"antigravity-cli"
]
},
"configFormat": "settings-json",
"artifactLayout": {

View File

@@ -1,11 +1,20 @@
{
"id": "audit",
"role": "feature",
"version": "1.5.1-dev.0",
"title": "Audit",
"description": "Open-artifact audit and UAT-gap audit for milestone close gates; exposes `gsd-tools audit-uat` (cross-phase UAT outstanding items) and `gsd-tools audit-open` (structured open-artifact scan across debug, tasks, threads, todos, seeds, UAT, verification, context-questions).",
"tier": "full",
"requires": [],
"runtimeCompat": { "supported": ["*"], "unsupported": [] },
"engines": {
"gsd": ">=1.6.0"
},
"runtimeCompat": {
"supported": [
"*"
],
"unsupported": []
},
"skills": [],
"agents": [],
"config": {},

View File

@@ -1,15 +1,21 @@
{
"id": "augment",
"role": "runtime",
"version": "1.5.1-dev.0",
"title": "Augment Code",
"description": "Augment Code CLI — commands + nested-skill artifact layout; settings-json hook surface; Claude hook event dialect; tier-2 support.",
"tier": "core",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtime": {
"configHome": {
"kind": "dot-home",
"name": ".augment",
"env": ["AUGMENT_CONFIG_DIR"]
"env": [
"AUGMENT_CONFIG_DIR"
]
},
"configFormat": "settings-json",
"artifactLayout": {

View File

@@ -1,15 +1,21 @@
{
"id": "claude",
"role": "runtime",
"version": "1.5.1-dev.0",
"title": "Claude Code",
"description": "Anthropic Claude Code — primary development runtime; tier-1 support with full hook surface and skills-based global install.",
"tier": "core",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtime": {
"configHome": {
"kind": "dot-home",
"name": ".claude",
"env": ["CLAUDE_CONFIG_DIR"]
"env": [
"CLAUDE_CONFIG_DIR"
]
},
"configFormat": "settings-json",
"artifactLayout": {
@@ -50,6 +56,11 @@
"installSurface": "settings-json",
"writesSharedSettings": true,
"permissionWriter": null,
"extendedHookEvents": ["SubagentStop", "Stop", "PreCompact", "FileChanged"]
"extendedHookEvents": [
"SubagentStop",
"Stop",
"PreCompact",
"FileChanged"
]
}
}

View File

@@ -1,15 +1,21 @@
{
"id": "cline",
"role": "runtime",
"version": "1.5.1-dev.0",
"title": "Cline",
"description": "Cline (VS Code extension) — global-only nested-skill layout; cline-rules hook surface (.clinerules); no hook events emitted; tier-2 support.",
"tier": "core",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtime": {
"configHome": {
"kind": "dot-home",
"name": ".cline",
"env": ["CLINE_CONFIG_DIR"]
"env": [
"CLINE_CONFIG_DIR"
]
},
"configFormat": "markdown-dir",
"artifactLayout": {

View File

@@ -1,13 +1,27 @@
{
"id": "code-review",
"role": "feature",
"version": "1.5.1-dev.0",
"title": "Code review",
"description": "Source-file code review and review-fix workflow support for completed execution work.",
"tier": "full",
"requires": [],
"runtimeCompat": { "supported": ["*"], "unsupported": [] },
"skills": ["code-review"],
"agents": ["gsd-code-reviewer", "gsd-code-fixer"],
"engines": {
"gsd": ">=1.6.0"
},
"runtimeCompat": {
"supported": [
"*"
],
"unsupported": []
},
"skills": [
"code-review"
],
"agents": [
"gsd-code-reviewer",
"gsd-code-fixer"
],
"hooks": [],
"config": {
"workflow.code_review": {
@@ -17,7 +31,11 @@
},
"workflow.code_review_depth": {
"type": "enum",
"values": ["quick", "standard", "deep"],
"values": [
"quick",
"standard",
"deep"
],
"default": "standard",
"description": "Default depth for code review when no --depth override is supplied."
}
@@ -25,9 +43,15 @@
"steps": [
{
"point": "execute:post",
"ref": { "skill": "code-review" },
"produces": ["REVIEW.md"],
"consumes": ["SUMMARY.md"],
"ref": {
"skill": "code-review"
},
"produces": [
"REVIEW.md"
],
"consumes": [
"SUMMARY.md"
],
"when": "workflow.code_review",
"onError": "skip"
}

View File

@@ -1,15 +1,21 @@
{
"id": "codebuddy",
"role": "runtime",
"version": "1.5.1-dev.0",
"title": "CodeBuddy",
"description": "CodeBuddy (Tencent) — converted commands + skills artifact layout; settings-json hook surface; Claude hook event dialect; tier-2 support.",
"tier": "core",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtime": {
"configHome": {
"kind": "dot-home",
"name": ".codebuddy",
"env": ["CODEBUDDY_CONFIG_DIR"]
"env": [
"CODEBUDDY_CONFIG_DIR"
]
},
"configFormat": "settings-json",
"artifactLayout": {

View File

@@ -1,15 +1,21 @@
{
"id": "codex",
"role": "runtime",
"version": "1.5.1-dev.0",
"title": "OpenAI Codex CLI",
"description": "OpenAI Codex CLI — shell-var command style; per-agent sandbox tiers; config.toml + hooks.json hook surface; tier-1 support.",
"tier": "core",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtime": {
"configHome": {
"kind": "dot-home",
"name": ".codex",
"env": ["CODEX_HOME"]
"env": [
"CODEX_HOME"
]
},
"configFormat": "toml",
"artifactLayout": {

View File

@@ -1,15 +1,22 @@
{
"id": "copilot",
"role": "runtime",
"version": "1.5.1-dev.0",
"title": "GitHub Copilot",
"description": "GitHub Copilot (VS Code) — markdown config format; copilot-inline hook surface; no hook events emitted; flat skill nesting (unconfirmed recursive loader); tier-2 support.",
"tier": "core",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtime": {
"configHome": {
"kind": "dot-home",
"name": ".copilot",
"env": ["COPILOT_CONFIG_DIR", "COPILOT_HOME"]
"env": [
"COPILOT_CONFIG_DIR",
"COPILOT_HOME"
]
},
"configFormat": "markdown",
"artifactLayout": {

View File

@@ -1,15 +1,21 @@
{
"id": "cursor",
"role": "runtime",
"version": "1.5.1-dev.0",
"title": "Cursor",
"description": "Cursor IDE — skills + converted commands artifact layout; hooks.json surface; Claude hook event dialect; recursive skill loader (flat nesting); tier-2 support.",
"tier": "core",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtime": {
"configHome": {
"kind": "dot-home",
"name": ".cursor",
"env": ["CURSOR_CONFIG_DIR"]
"env": [
"CURSOR_CONFIG_DIR"
]
},
"configFormat": "none",
"artifactLayout": {

View File

@@ -1,11 +1,20 @@
{
"id": "drift",
"role": "feature",
"version": "1.5.1-dev.0",
"title": "Drift detection gates",
"description": "Post-execution drift detection gates that run after each wave completes. Provides two gates at execute:wave:post: a blocking schema drift gate (detects schema files changed without a database push) and a non-blocking codebase drift gate (detects structural additions not reflected in STRUCTURE.md).",
"tier": "full",
"requires": [],
"runtimeCompat": { "supported": ["*"], "unsupported": [] },
"engines": {
"gsd": ">=1.6.0"
},
"runtimeCompat": {
"supported": [
"*"
],
"unsupported": []
},
"skills": [],
"agents": [],
"hooks": [],
@@ -17,7 +26,10 @@
},
"workflow.drift_action": {
"type": "enum",
"values": ["warn", "auto-remap"],
"values": [
"warn",
"auto-remap"
],
"default": "warn",
"description": "Action taken by the codebase drift gate when the threshold is exceeded: warn (advisory message) or auto-remap (spawn gsd-codebase-mapper agent to refresh STRUCTURE.md)."
},
@@ -32,14 +44,18 @@
"gates": [
{
"point": "execute:wave:post",
"check": { "query": "verify.schema-drift" },
"check": {
"query": "verify.schema-drift"
},
"when": "workflow.schema_drift_gate",
"blocking": true,
"onError": "skip"
},
{
"point": "execute:wave:post",
"check": { "query": "verify.codebase-drift" },
"check": {
"query": "verify.codebase-drift"
},
"when": "workflow.schema_drift_gate",
"blocking": false,
"onError": "skip"

View File

@@ -1,11 +1,20 @@
{
"id": "gap-analysis",
"role": "feature",
"version": "1.5.1-dev.0",
"title": "Post-planning gap analysis",
"description": "Proactive, non-blocking post-planning coverage report. After all PLAN.md files are generated, cross-references every REQ-ID and D-ID from REQUIREMENTS.md and CONTEXT.md against plan bodies. Emits a Source | Item | Status table. Does not block phase advancement.",
"tier": "standard",
"requires": [],
"runtimeCompat": { "supported": ["*"], "unsupported": [] },
"engines": {
"gsd": ">=1.6.0"
},
"runtimeCompat": {
"supported": [
"*"
],
"unsupported": []
},
"skills": [],
"agents": [],
"hooks": [],

View File

@@ -1,15 +1,21 @@
{
"id": "gemini",
"role": "runtime",
"version": "1.5.1-dev.0",
"title": "Gemini CLI",
"description": "Google Gemini CLI — commands-only artifact layout (TOML); Gemini hook event dialect; settings-json hook surface; tier-2 support.",
"tier": "core",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtime": {
"configHome": {
"kind": "dot-home",
"name": ".gemini",
"env": ["GEMINI_CONFIG_DIR"]
"env": [
"GEMINI_CONFIG_DIR"
]
},
"configFormat": "settings-json",
"artifactLayout": {
@@ -42,6 +48,10 @@
"installSurface": "settings-json",
"writesSharedSettings": true,
"permissionWriter": null,
"extendedHookEvents": ["BeforeAgent", "AfterAgent", "BeforeModel"]
"extendedHookEvents": [
"BeforeAgent",
"AfterAgent",
"BeforeModel"
]
}
}

View File

@@ -1,12 +1,23 @@
{
"id": "graphify",
"role": "feature",
"version": "1.5.1-dev.0",
"title": "Knowledge graph",
"description": "Build, query, and inspect the project knowledge graph in `.planning/graphs/`; exposes graphify CLI subcommands (build, query, status, diff) and the /gsd-graphify skill.",
"tier": "full",
"requires": [],
"runtimeCompat": { "supported": ["*"], "unsupported": [] },
"skills": ["graphify"],
"engines": {
"gsd": ">=1.6.0"
},
"runtimeCompat": {
"supported": [
"*"
],
"unsupported": []
},
"skills": [
"graphify"
],
"agents": [],
"activationKey": "graphify.enabled",
"config": {

View File

@@ -1,15 +1,21 @@
{
"id": "hermes",
"role": "runtime",
"version": "1.5.1-dev.0",
"title": "Hermes Agent",
"description": "Hermes Agent (NousResearch) — skills nest under skills/gsd/ category bucket; nested skill layout; settings-json hook surface; Claude hook event dialect; tier-2 support.",
"tier": "core",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtime": {
"configHome": {
"kind": "dot-home",
"name": ".hermes",
"env": ["HERMES_HOME"]
"env": [
"HERMES_HOME"
]
},
"configFormat": "settings-json",
"artifactLayout": {

View File

@@ -1,11 +1,20 @@
{
"id": "intel",
"role": "feature",
"version": "1.5.1-dev.0",
"title": "Codebase intelligence",
"description": "Code-intelligence store for codebase querying, diff, snapshot, and API-surface extraction; exposes `gsd-tools intel` subcommands (query, status, update, diff, snapshot, patch-meta, validate, extract-exports, api-surface) and backs `/gsd-map-codebase` and `gsd-intel-updater`.",
"tier": "full",
"requires": [],
"runtimeCompat": { "supported": ["*"], "unsupported": [] },
"engines": {
"gsd": ">=1.6.0"
},
"runtimeCompat": {
"supported": [
"*"
],
"unsupported": []
},
"skills": [],
"agents": [],
"activationKey": "intel.enabled",
@@ -27,8 +36,12 @@
"steps": [
{
"point": "plan:pre",
"ref": { "command": "intel api-surface" },
"produces": [".planning/intel/API-SURFACE.md"],
"ref": {
"command": "intel api-surface"
},
"produces": [
".planning/intel/API-SURFACE.md"
],
"consumes": [],
"when": "intel.enabled",
"onError": "skip"

View File

@@ -1,15 +1,23 @@
{
"id": "kilo",
"role": "runtime",
"version": "1.5.1-dev.0",
"title": "Kilo Code",
"description": "Kilo Code — XDG-based config dir; global skills at ~/.kilo/skills (separate from XDG config); flat command/ + skills artifact layout; no lifecycle hook registration; tier-2 support.",
"tier": "core",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtime": {
"configHome": {
"kind": "xdg",
"name": "kilo",
"env": ["KILO_CONFIG_DIR", "KILO_CONFIG", "XDG_CONFIG_HOME"],
"env": [
"KILO_CONFIG_DIR",
"KILO_CONFIG",
"XDG_CONFIG_HOME"
],
"skillsHome": {
"kind": "dot-home",
"name": ".kilo",

View File

@@ -1,16 +1,25 @@
{
"id": "kimi",
"role": "runtime",
"version": "1.5.1-dev.0",
"title": "Kimi CLI",
"description": "Kimi CLI (Moonshot AI) — generic agents root at ~/.config/agents; skills + kimi-agents artifact layout; no hook surface; no hook events; tier-2 support.",
"tier": "core",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtime": {
"configHome": {
"kind": "generic-agents-root",
"name": "agents",
"env": ["KIMI_CONFIG_DIR"],
"probe": ["~/.config/agents", "~/.agents"],
"env": [
"KIMI_CONFIG_DIR"
],
"probe": [
"~/.config/agents",
"~/.agents"
],
"probeExists": "skills"
},
"configFormat": "none",

View File

@@ -1,13 +1,27 @@
{
"id": "mempalace",
"role": "feature",
"version": "1.5.1-dev.0",
"title": "MemPalace memory",
"description": "Cross-session, cross-project memory: deliberate recall before discuss/plan and verbatim capture + temporal-KG sync at phase boundaries, via the MemPalace MCP server and CLI.",
"tier": "full",
"requires": [],
"runtimeCompat": { "supported": ["*"], "unsupported": [] },
"skills": ["mempalace-recall", "mempalace-capture"],
"agents": ["gsd-mempalace-curator"],
"engines": {
"gsd": ">=1.6.0"
},
"runtimeCompat": {
"supported": [
"*"
],
"unsupported": []
},
"skills": [
"mempalace-recall",
"mempalace-capture"
],
"agents": [
"gsd-mempalace-curator"
],
"hooks": [],
"config": {
"mempalace.enabled": {
@@ -17,7 +31,11 @@
},
"mempalace.memory_mode": {
"type": "enum",
"values": ["augment", "kg_backend", "replace"],
"values": [
"augment",
"kg_backend",
"replace"
],
"default": "augment",
"description": "How MemPalace relates to GSD native memory. Only 'augment' (additive) is implemented today; 'kg_backend' and 'replace' are forward-declared (routing seam not yet built) and currently behave as 'augment'."
},
@@ -65,41 +83,63 @@
"steps": [
{
"point": "discuss:post",
"ref": { "skill": "mempalace-capture" },
"ref": {
"skill": "mempalace-capture"
},
"produces": [],
"consumes": ["CONTEXT.md"],
"consumes": [
"CONTEXT.md"
],
"when": "mempalace.enabled",
"onError": "skip"
},
{
"point": "plan:pre",
"ref": { "skill": "mempalace-recall" },
"produces": ["MEMORY-RECALL.md"],
"consumes": ["CONTEXT.md"],
"ref": {
"skill": "mempalace-recall"
},
"produces": [
"MEMORY-RECALL.md"
],
"consumes": [
"CONTEXT.md"
],
"when": "mempalace.enabled",
"onError": "skip"
},
{
"point": "plan:post",
"ref": { "skill": "mempalace-capture" },
"ref": {
"skill": "mempalace-capture"
},
"produces": [],
"consumes": ["PLAN.md"],
"consumes": [
"PLAN.md"
],
"when": "mempalace.enabled",
"onError": "skip"
},
{
"point": "verify:post",
"ref": { "skill": "mempalace-capture" },
"ref": {
"skill": "mempalace-capture"
},
"produces": [],
"consumes": ["SUMMARY.md"],
"consumes": [
"SUMMARY.md"
],
"when": "mempalace.enabled",
"onError": "skip"
},
{
"point": "ship:post",
"ref": { "agent": "gsd-mempalace-curator" },
"ref": {
"agent": "gsd-mempalace-curator"
},
"produces": [],
"consumes": ["UAT.md"],
"consumes": [
"UAT.md"
],
"when": "mempalace.enabled",
"onError": "skip"
}
@@ -108,7 +148,9 @@
{
"point": "discuss:pre",
"into": "orchestrator",
"fragment": { "path": "fragments/recall-discuss.md" },
"fragment": {
"path": "fragments/recall-discuss.md"
},
"produces": [],
"consumes": [],
"when": "mempalace.enabled",
@@ -117,7 +159,9 @@
{
"point": "execute:wave:post",
"into": "verifier",
"fragment": { "path": "fragments/capture-problems.md" },
"fragment": {
"path": "fragments/capture-problems.md"
},
"produces": [],
"consumes": [],
"when": "mempalace.enabled",

View File

@@ -1,13 +1,26 @@
{
"id": "nyquist",
"role": "feature",
"version": "1.5.1-dev.0",
"title": "Nyquist validation",
"description": "Validation coverage audit that maps executed work back to tests and manual-only evidence.",
"tier": "full",
"requires": [],
"runtimeCompat": { "supported": ["*"], "unsupported": [] },
"skills": ["validate-phase"],
"agents": ["gsd-nyquist-auditor"],
"engines": {
"gsd": ">=1.6.0"
},
"runtimeCompat": {
"supported": [
"*"
],
"unsupported": []
},
"skills": [
"validate-phase"
],
"agents": [
"gsd-nyquist-auditor"
],
"hooks": [],
"config": {
"workflow.nyquist_validation": {
@@ -19,9 +32,15 @@
"steps": [
{
"point": "verify:post",
"ref": { "skill": "validate-phase" },
"produces": ["VALIDATION.md"],
"consumes": ["SUMMARY.md"],
"ref": {
"skill": "validate-phase"
},
"produces": [
"VALIDATION.md"
],
"consumes": [
"SUMMARY.md"
],
"when": "workflow.nyquist_validation",
"onError": "halt"
}

View File

@@ -1,15 +1,23 @@
{
"id": "opencode",
"role": "runtime",
"version": "1.5.1-dev.0",
"title": "OpenCode",
"description": "OpenCode — XDG-based config dir; flat command/ + skills artifact layout; settings-json config format; no lifecycle hook registration; tier-2 support.",
"tier": "core",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtime": {
"configHome": {
"kind": "xdg",
"name": "opencode",
"env": ["OPENCODE_CONFIG_DIR", "OPENCODE_CONFIG", "XDG_CONFIG_HOME"]
"env": [
"OPENCODE_CONFIG_DIR",
"OPENCODE_CONFIG",
"XDG_CONFIG_HOME"
]
},
"configFormat": "settings-json",
"artifactLayout": {

View File

@@ -1,13 +1,26 @@
{
"id": "pattern-mapper",
"role": "feature",
"version": "1.5.1-dev.0",
"title": "Pattern mapping",
"description": "Optional codebase-pattern mapping before planning; owns the pattern mapper agent and workflow.pattern_mapper activation key.",
"tier": "full",
"requires": ["research"],
"runtimeCompat": { "supported": ["*"], "unsupported": [] },
"requires": [
"research"
],
"engines": {
"gsd": ">=1.6.0"
},
"runtimeCompat": {
"supported": [
"*"
],
"unsupported": []
},
"skills": [],
"agents": ["gsd-pattern-mapper"],
"agents": [
"gsd-pattern-mapper"
],
"hooks": [],
"config": {
"workflow.pattern_mapper": {
@@ -19,10 +32,18 @@
"steps": [
{
"point": "plan:pre",
"ref": { "agent": "gsd-pattern-mapper" },
"fragment": { "path": "fragments/plan-pre.md" },
"produces": ["PATTERNS.md"],
"consumes": ["RESEARCH.md"],
"ref": {
"agent": "gsd-pattern-mapper"
},
"fragment": {
"path": "fragments/plan-pre.md"
},
"produces": [
"PATTERNS.md"
],
"consumes": [
"RESEARCH.md"
],
"when": "workflow.pattern_mapper",
"onError": "skip"
}

View File

@@ -1,13 +1,26 @@
{
"id": "profile-pipeline",
"role": "feature",
"version": "1.5.1-dev.0",
"title": "Developer profiling pipeline",
"description": "Developer behavioral profiling from Claude Code session history; scans session JSONL files, extracts and samples user messages, and generates profile artifacts (USER-PROFILE.md, dev-preferences.md, CLAUDE.md sections). Exposes eight `gsd-tools` commands: scan-sessions, extract-messages, profile-sample (pipeline phase) and write-profile, profile-questionnaire, generate-dev-preferences, generate-claude-profile, generate-claude-md (output phase). Backs the /gsd-profile-user skill and gsd-user-profiler agent.",
"tier": "full",
"requires": [],
"runtimeCompat": { "supported": ["*"], "unsupported": [] },
"skills": ["profile-user"],
"agents": ["gsd-user-profiler"],
"engines": {
"gsd": ">=1.6.0"
},
"runtimeCompat": {
"supported": [
"*"
],
"unsupported": []
},
"skills": [
"profile-user"
],
"agents": [
"gsd-user-profiler"
],
"config": {
"profile-pipeline.enabled": {
"type": "boolean",

View File

@@ -1,15 +1,21 @@
{
"id": "qwen",
"role": "runtime",
"version": "1.5.1-dev.0",
"title": "Qwen Code",
"description": "Qwen Code (Alibaba) — nested-skill artifact layout; settings-json hook surface; Claude hook event dialect; tier-2 support.",
"tier": "core",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtime": {
"configHome": {
"kind": "dot-home",
"name": ".qwen",
"env": ["QWEN_CONFIG_DIR"]
"env": [
"QWEN_CONFIG_DIR"
]
},
"configFormat": "settings-json",
"artifactLayout": {
@@ -42,6 +48,10 @@
"installSurface": "settings-json",
"writesSharedSettings": true,
"permissionWriter": null,
"extendedHookEvents": ["SubagentStop", "Stop", "PreCompact"]
"extendedHookEvents": [
"SubagentStop",
"Stop",
"PreCompact"
]
}
}

View File

@@ -1,13 +1,24 @@
{
"id": "research",
"role": "feature",
"version": "1.5.1-dev.0",
"title": "Phase research",
"description": "Optional phase research before planning; owns the phase researcher agent and workflow.research activation key.",
"tier": "standard",
"requires": [],
"runtimeCompat": { "supported": ["*"], "unsupported": [] },
"engines": {
"gsd": ">=1.6.0"
},
"runtimeCompat": {
"supported": [
"*"
],
"unsupported": []
},
"skills": [],
"agents": ["gsd-phase-researcher"],
"agents": [
"gsd-phase-researcher"
],
"hooks": [],
"config": {
"workflow.research": {
@@ -19,10 +30,18 @@
"steps": [
{
"point": "plan:pre",
"ref": { "agent": "gsd-phase-researcher" },
"fragment": { "path": "fragments/plan-pre.md" },
"produces": ["RESEARCH.md"],
"consumes": ["CONTEXT.md"],
"ref": {
"agent": "gsd-phase-researcher"
},
"fragment": {
"path": "fragments/plan-pre.md"
},
"produces": [
"RESEARCH.md"
],
"consumes": [
"CONTEXT.md"
],
"when": "workflow.research",
"onError": "skip"
}

View File

@@ -1,11 +1,20 @@
{
"id": "schema-gate",
"role": "feature",
"version": "1.5.1-dev.0",
"title": "Schema push detection gate",
"description": "Detects ORM schema-relevant files in the phase scope during planning and injects a mandatory [BLOCKING] schema push task into the plan. Prevents false-positive verification where build/types pass because TypeScript types come from config, not the live database.",
"tier": "full",
"requires": [],
"runtimeCompat": { "supported": ["*"], "unsupported": [] },
"engines": {
"gsd": ">=1.6.0"
},
"runtimeCompat": {
"supported": [
"*"
],
"unsupported": []
},
"skills": [],
"agents": [],
"hooks": [],
@@ -21,9 +30,13 @@
{
"point": "plan:pre",
"into": "planner",
"fragment": { "path": "fragments/plan-pre.md" },
"fragment": {
"path": "fragments/plan-pre.md"
},
"produces": [],
"consumes": ["CONTEXT.md"],
"consumes": [
"CONTEXT.md"
],
"when": "workflow.schema_push_detection",
"onError": "skip"
}

View File

@@ -1,13 +1,26 @@
{
"id": "security",
"role": "feature",
"version": "1.5.1-dev.0",
"title": "Security enforcement",
"description": "Threat mitigation verification and ship-time security blocking for phases with security enforcement enabled.",
"tier": "full",
"requires": [],
"runtimeCompat": { "supported": ["*"], "unsupported": [] },
"skills": ["secure-phase"],
"agents": ["gsd-security-auditor"],
"engines": {
"gsd": ">=1.6.0"
},
"runtimeCompat": {
"supported": [
"*"
],
"unsupported": []
},
"skills": [
"secure-phase"
],
"agents": [
"gsd-security-auditor"
],
"hooks": [],
"config": {
"workflow.security_enforcement": {
@@ -22,7 +35,13 @@
},
"workflow.security_block_on": {
"type": "enum",
"values": ["critical", "high", "medium", "low", "none"],
"values": [
"critical",
"high",
"medium",
"low",
"none"
],
"default": "high",
"description": "Minimum open threat severity that blocks advancement."
}
@@ -30,9 +49,15 @@
"steps": [
{
"point": "verify:post",
"ref": { "skill": "secure-phase" },
"produces": ["SECURITY.md"],
"consumes": ["SUMMARY.md"],
"ref": {
"skill": "secure-phase"
},
"produces": [
"SECURITY.md"
],
"consumes": [
"SUMMARY.md"
],
"when": "workflow.security_enforcement",
"onError": "halt"
}
@@ -49,7 +74,9 @@
"security_block_on": "workflow.security_block_on"
},
"produces": [],
"consumes": ["CONTEXT.md"],
"consumes": [
"CONTEXT.md"
],
"when": "workflow.security_enforcement"
}
],

View File

@@ -1,11 +1,20 @@
{
"id": "tdd",
"role": "feature",
"version": "1.5.1-dev.0",
"title": "Test-driven development",
"description": "Injects TDD heuristics into the planner and enforces RED/GREEN gate compliance on type:tdd plans after execution. Owns workflow.tdd_mode; the --tdd CLI flag is the ephemeral override.",
"tier": "full",
"requires": [],
"runtimeCompat": { "supported": ["*"], "unsupported": [] },
"engines": {
"gsd": ">=1.6.0"
},
"runtimeCompat": {
"supported": [
"*"
],
"unsupported": []
},
"skills": [],
"agents": [],
"hooks": [],

View File

@@ -1,15 +1,21 @@
{
"id": "trae",
"role": "runtime",
"version": "1.5.1-dev.0",
"title": "Trae IDE",
"description": "Trae IDE — nested-skill artifact layout; no hook surface (profile-marker-only config); tier-2 support.",
"tier": "core",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtime": {
"configHome": {
"kind": "dot-home",
"name": ".trae",
"env": ["TRAE_CONFIG_DIR"]
"env": [
"TRAE_CONFIG_DIR"
]
},
"configFormat": "none",
"artifactLayout": {

View File

@@ -1,23 +1,95 @@
{
"id": "ui", "role": "feature", "title": "UI design contracts",
"id": "ui",
"role": "feature",
"version": "1.5.1-dev.0",
"title": "UI design contracts",
"description": "UI-SPEC design contract + retrospective UI audit for frontend phases.",
"tier": "full", "requires": [],
"runtimeCompat": { "supported": ["*"], "unsupported": [] },
"skills": ["ui-phase", "ui-review"],
"agents": ["gsd-ui-checker", "gsd-ui-auditor"],
"tier": "full",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtimeCompat": {
"supported": [
"*"
],
"unsupported": []
},
"skills": [
"ui-phase",
"ui-review"
],
"agents": [
"gsd-ui-checker",
"gsd-ui-auditor"
],
"hooks": [],
"config": {
"workflow.ui_phase": { "type": "boolean", "default": true, "description": "Enable the UI design-contract gate during planning." },
"workflow.ui_review": { "type": "boolean", "default": true, "description": "Enable the retrospective UI audit." },
"workflow.ui_safety_gate": { "type": "boolean", "default": true, "description": "Block execution on unmet UI-SPEC contracts." }
"workflow.ui_phase": {
"type": "boolean",
"default": true,
"description": "Enable the UI design-contract gate during planning."
},
"workflow.ui_review": {
"type": "boolean",
"default": true,
"description": "Enable the retrospective UI audit."
},
"workflow.ui_safety_gate": {
"type": "boolean",
"default": true,
"description": "Block execution on unmet UI-SPEC contracts."
}
},
"steps": [
{ "point": "plan:pre", "ref": { "skill": "ui-phase" }, "produces": ["UI-SPEC.md"], "consumes": ["CONTEXT.md"], "when": "workflow.ui_phase", "onError": "skip" },
{ "point": "verify:post", "ref": { "skill": "ui-review" }, "produces": ["UI-REVIEW.md"], "consumes": ["UI-SPEC.md"], "when": "workflow.ui_review", "onError": "skip" }
{
"point": "plan:pre",
"ref": {
"skill": "ui-phase"
},
"produces": [
"UI-SPEC.md"
],
"consumes": [
"CONTEXT.md"
],
"when": "workflow.ui_phase",
"onError": "skip"
},
{
"point": "verify:post",
"ref": {
"skill": "ui-review"
},
"produces": [
"UI-REVIEW.md"
],
"consumes": [
"UI-SPEC.md"
],
"when": "workflow.ui_review",
"onError": "skip"
}
],
"contributions": [],
"gates": [
{ "point": "plan:pre", "check": { "query": "ui.plan-gate" }, "when": "workflow.ui_safety_gate", "blocking": true, "onError": "halt" },
{ "point": "execute:wave:post", "check": { "query": "ui.safety-gate" }, "when": "workflow.ui_safety_gate", "blocking": true, "onError": "halt" }
{
"point": "plan:pre",
"check": {
"query": "ui.plan-gate"
},
"when": "workflow.ui_safety_gate",
"blocking": true,
"onError": "halt"
},
{
"point": "execute:wave:post",
"check": {
"query": "ui.safety-gate"
},
"when": "workflow.ui_safety_gate",
"blocking": true,
"onError": "halt"
}
]
}

View File

@@ -1,16 +1,22 @@
{
"id": "windsurf",
"role": "runtime",
"version": "1.5.1-dev.0",
"title": "Windsurf",
"description": "Windsurf (Codeium) — nested under ~/.codeium/windsurf; skills-only artifact layout; no hook surface; no hook events; tier-2 support.",
"tier": "core",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtime": {
"configHome": {
"kind": "dot-home-nested",
"name": "windsurf",
"parent": ".codeium",
"env": ["WINDSURF_CONFIG_DIR"]
"env": [
"WINDSURF_CONFIG_DIR"
]
},
"configFormat": "none",
"artifactLayout": {

View File

@@ -28,6 +28,8 @@ Set the version in your manifest before every release:
}
```
> **First-party capabilities are versioned automatically.** The native capabilities shipped inside GSD (`capabilities/<id>/capability.json`) are stamped in lockstep with the GSD package version at release time by `scripts/sync-manifest-versions.cjs` — their `version` always equals the GSD version, so per-capability semver and `compatVersions` only carry independent signal for **third-party** capabilities. As an author of a third-party capability, you own your own version line; the lockstep rule does not apply to you.
### Decide when to raise `engines.gsd`
The `engines.gsd` range expresses which GSD host versions your capability is compatible with. GSD enforces this as a hard gate at install time and again at load time.
@@ -137,5 +139,5 @@ If the new version of a capability requires a GSD version newer than what you ha
- [How to remove or disable a capability](remove-a-capability.md)
- [Develop a Capability for GSD 1.5+](develop-a-capability.md)
- [Capability manifest reference](../reference/capability-matrix.md)
- [Capability manifest reference](../reference/capability-manifest.md)
- [Turn a capability off (and keep it off)](turn-a-capability-off.md)

View File

@@ -10,10 +10,14 @@ const capabilities = {
"ai-integration": {
"id": "ai-integration",
"role": "feature",
"version": "1.5.1-dev.0",
"title": "AI design contract",
"description": "AI-SPEC design contract workflow for phases that build AI systems; owns the AI integration command, agents, and workflow.ai_integration_phase activation key.",
"tier": "full",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtimeCompat": {
"supported": [
"*"
@@ -59,10 +63,14 @@ const capabilities = {
"antigravity": {
"id": "antigravity",
"role": "runtime",
"version": "1.5.1-dev.0",
"title": "Antigravity",
"description": "Google Antigravity IDE — nested under ~/.gemini/antigravity; probed across 1.x and 2.x layouts; Gemini hook event dialect; nested skill layout; tier-1 support.",
"tier": "core",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtime": {
"configHome": {
"kind": "dot-home-nested",
@@ -114,10 +122,14 @@ const capabilities = {
"audit": {
"id": "audit",
"role": "feature",
"version": "1.5.1-dev.0",
"title": "Audit",
"description": "Open-artifact audit and UAT-gap audit for milestone close gates; exposes `gsd-tools audit-uat` (cross-phase UAT outstanding items) and `gsd-tools audit-open` (structured open-artifact scan across debug, tasks, threads, todos, seeds, UAT, verification, context-questions).",
"tier": "full",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtimeCompat": {
"supported": [
"*"
@@ -147,10 +159,14 @@ const capabilities = {
"augment": {
"id": "augment",
"role": "runtime",
"version": "1.5.1-dev.0",
"title": "Augment Code",
"description": "Augment Code CLI — commands + nested-skill artifact layout; settings-json hook surface; Claude hook event dialect; tier-2 support.",
"tier": "core",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtime": {
"configHome": {
"kind": "dot-home",
@@ -212,10 +228,14 @@ const capabilities = {
"claude": {
"id": "claude",
"role": "runtime",
"version": "1.5.1-dev.0",
"title": "Claude Code",
"description": "Anthropic Claude Code — primary development runtime; tier-1 support with full hook surface and skills-based global install.",
"tier": "core",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtime": {
"configHome": {
"kind": "dot-home",
@@ -274,10 +294,14 @@ const capabilities = {
"cline": {
"id": "cline",
"role": "runtime",
"version": "1.5.1-dev.0",
"title": "Cline",
"description": "Cline (VS Code extension) — global-only nested-skill layout; cline-rules hook surface (.clinerules); no hook events emitted; tier-2 support.",
"tier": "core",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtime": {
"configHome": {
"kind": "dot-home",
@@ -313,10 +337,14 @@ const capabilities = {
"code-review": {
"id": "code-review",
"role": "feature",
"version": "1.5.1-dev.0",
"title": "Code review",
"description": "Source-file code review and review-fix workflow support for completed execution work.",
"tier": "full",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtimeCompat": {
"supported": [
"*"
@@ -370,10 +398,14 @@ const capabilities = {
"codebuddy": {
"id": "codebuddy",
"role": "runtime",
"version": "1.5.1-dev.0",
"title": "CodeBuddy",
"description": "CodeBuddy (Tencent) — converted commands + skills artifact layout; settings-json hook surface; Claude hook event dialect; tier-2 support.",
"tier": "core",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtime": {
"configHome": {
"kind": "dot-home",
@@ -435,10 +467,14 @@ const capabilities = {
"codex": {
"id": "codex",
"role": "runtime",
"version": "1.5.1-dev.0",
"title": "OpenAI Codex CLI",
"description": "OpenAI Codex CLI — shell-var command style; per-agent sandbox tiers; config.toml + hooks.json hook surface; tier-1 support.",
"tier": "core",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtime": {
"configHome": {
"kind": "dot-home",
@@ -484,10 +520,14 @@ const capabilities = {
"copilot": {
"id": "copilot",
"role": "runtime",
"version": "1.5.1-dev.0",
"title": "GitHub Copilot",
"description": "GitHub Copilot (VS Code) — markdown config format; copilot-inline hook surface; no hook events emitted; flat skill nesting (unconfirmed recursive loader); tier-2 support.",
"tier": "core",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtime": {
"configHome": {
"kind": "dot-home",
@@ -533,10 +573,14 @@ const capabilities = {
"cursor": {
"id": "cursor",
"role": "runtime",
"version": "1.5.1-dev.0",
"title": "Cursor",
"description": "Cursor IDE — skills + converted commands artifact layout; hooks.json surface; Claude hook event dialect; recursive skill loader (flat nesting); tier-2 support.",
"tier": "core",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtime": {
"configHome": {
"kind": "dot-home",
@@ -598,10 +642,14 @@ const capabilities = {
"drift": {
"id": "drift",
"role": "feature",
"version": "1.5.1-dev.0",
"title": "Drift detection gates",
"description": "Post-execution drift detection gates that run after each wave completes. Provides two gates at execute:wave:post: a blocking schema drift gate (detects schema files changed without a database push) and a non-blocking codebase drift gate (detects structural additions not reflected in STRUCTURE.md).",
"tier": "full",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtimeCompat": {
"supported": [
"*"
@@ -658,10 +706,14 @@ const capabilities = {
"gap-analysis": {
"id": "gap-analysis",
"role": "feature",
"version": "1.5.1-dev.0",
"title": "Post-planning gap analysis",
"description": "Proactive, non-blocking post-planning coverage report. After all PLAN.md files are generated, cross-references every REQ-ID and D-ID from REQUIREMENTS.md and CONTEXT.md against plan bodies. Emits a Source | Item | Status table. Does not block phase advancement.",
"tier": "standard",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtimeCompat": {
"supported": [
"*"
@@ -695,10 +747,14 @@ const capabilities = {
"gemini": {
"id": "gemini",
"role": "runtime",
"version": "1.5.1-dev.0",
"title": "Gemini CLI",
"description": "Google Gemini CLI — commands-only artifact layout (TOML); Gemini hook event dialect; settings-json hook surface; tier-2 support.",
"tier": "core",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtime": {
"configHome": {
"kind": "dot-home",
@@ -748,10 +804,14 @@ const capabilities = {
"graphify": {
"id": "graphify",
"role": "feature",
"version": "1.5.1-dev.0",
"title": "Knowledge graph",
"description": "Build, query, and inspect the project knowledge graph in `.planning/graphs/`; exposes graphify CLI subcommands (build, query, status, diff) and the /gsd-graphify skill.",
"tier": "full",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtimeCompat": {
"supported": [
"*"
@@ -785,10 +845,14 @@ const capabilities = {
"hermes": {
"id": "hermes",
"role": "runtime",
"version": "1.5.1-dev.0",
"title": "Hermes Agent",
"description": "Hermes Agent (NousResearch) — skills nest under skills/gsd/ category bucket; nested skill layout; settings-json hook surface; Claude hook event dialect; tier-2 support.",
"tier": "core",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtime": {
"configHome": {
"kind": "dot-home",
@@ -834,10 +898,14 @@ const capabilities = {
"intel": {
"id": "intel",
"role": "feature",
"version": "1.5.1-dev.0",
"title": "Codebase intelligence",
"description": "Code-intelligence store for codebase querying, diff, snapshot, and API-surface extraction; exposes `gsd-tools intel` subcommands (query, status, update, diff, snapshot, patch-meta, validate, extract-exports, api-surface) and backs `/gsd-map-codebase` and `gsd-intel-updater`.",
"tier": "full",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtimeCompat": {
"supported": [
"*"
@@ -882,10 +950,14 @@ const capabilities = {
"kilo": {
"id": "kilo",
"role": "runtime",
"version": "1.5.1-dev.0",
"title": "Kilo Code",
"description": "Kilo Code — XDG-based config dir; global skills at ~/.kilo/skills (separate from XDG config); flat command/ + skills artifact layout; no lifecycle hook registration; tier-2 support.",
"tier": "core",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtime": {
"configHome": {
"kind": "xdg",
@@ -953,10 +1025,14 @@ const capabilities = {
"kimi": {
"id": "kimi",
"role": "runtime",
"version": "1.5.1-dev.0",
"title": "Kimi CLI",
"description": "Kimi CLI (Moonshot AI) — generic agents root at ~/.config/agents; skills + kimi-agents artifact layout; no hook surface; no hook events; tier-2 support.",
"tier": "core",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtime": {
"configHome": {
"kind": "generic-agents-root",
@@ -1005,10 +1081,14 @@ const capabilities = {
"mempalace": {
"id": "mempalace",
"role": "feature",
"version": "1.5.1-dev.0",
"title": "MemPalace memory",
"description": "Cross-session, cross-project memory: deliberate recall before discuss/plan and verbatim capture + temporal-KG sync at phase boundaries, via the MemPalace MCP server and CLI.",
"tier": "full",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtimeCompat": {
"supported": [
"*"
@@ -1175,10 +1255,14 @@ const capabilities = {
"nyquist": {
"id": "nyquist",
"role": "feature",
"version": "1.5.1-dev.0",
"title": "Nyquist validation",
"description": "Validation coverage audit that maps executed work back to tests and manual-only evidence.",
"tier": "full",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtimeCompat": {
"supported": [
"*"
@@ -1221,10 +1305,14 @@ const capabilities = {
"opencode": {
"id": "opencode",
"role": "runtime",
"version": "1.5.1-dev.0",
"title": "OpenCode",
"description": "OpenCode — XDG-based config dir; flat command/ + skills artifact layout; settings-json config format; no lifecycle hook registration; tier-2 support.",
"tier": "core",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtime": {
"configHome": {
"kind": "xdg",
@@ -1287,12 +1375,16 @@ const capabilities = {
"pattern-mapper": {
"id": "pattern-mapper",
"role": "feature",
"version": "1.5.1-dev.0",
"title": "Pattern mapping",
"description": "Optional codebase-pattern mapping before planning; owns the pattern mapper agent and workflow.pattern_mapper activation key.",
"tier": "full",
"requires": [
"research"
],
"engines": {
"gsd": ">=1.6.0"
},
"runtimeCompat": {
"supported": [
"*"
@@ -1337,10 +1429,14 @@ const capabilities = {
"profile-pipeline": {
"id": "profile-pipeline",
"role": "feature",
"version": "1.5.1-dev.0",
"title": "Developer profiling pipeline",
"description": "Developer behavioral profiling from Claude Code session history; scans session JSONL files, extracts and samples user messages, and generates profile artifacts (USER-PROFILE.md, dev-preferences.md, CLAUDE.md sections). Exposes eight `gsd-tools` commands: scan-sessions, extract-messages, profile-sample (pipeline phase) and write-profile, profile-questionnaire, generate-dev-preferences, generate-claude-profile, generate-claude-md (output phase). Backs the /gsd-profile-user skill and gsd-user-profiler agent.",
"tier": "full",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtimeCompat": {
"supported": [
"*"
@@ -1410,10 +1506,14 @@ const capabilities = {
"qwen": {
"id": "qwen",
"role": "runtime",
"version": "1.5.1-dev.0",
"title": "Qwen Code",
"description": "Qwen Code (Alibaba) — nested-skill artifact layout; settings-json hook surface; Claude hook event dialect; tier-2 support.",
"tier": "core",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtime": {
"configHome": {
"kind": "dot-home",
@@ -1463,10 +1563,14 @@ const capabilities = {
"research": {
"id": "research",
"role": "feature",
"version": "1.5.1-dev.0",
"title": "Phase research",
"description": "Optional phase research before planning; owns the phase researcher agent and workflow.research activation key.",
"tier": "standard",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtimeCompat": {
"supported": [
"*"
@@ -1511,10 +1615,14 @@ const capabilities = {
"schema-gate": {
"id": "schema-gate",
"role": "feature",
"version": "1.5.1-dev.0",
"title": "Schema push detection gate",
"description": "Detects ORM schema-relevant files in the phase scope during planning and injects a mandatory [BLOCKING] schema push task into the plan. Prevents false-positive verification where build/types pass because TypeScript types come from config, not the live database.",
"tier": "full",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtimeCompat": {
"supported": [
"*"
@@ -1553,10 +1661,14 @@ const capabilities = {
"security": {
"id": "security",
"role": "feature",
"version": "1.5.1-dev.0",
"title": "Security enforcement",
"description": "Threat mitigation verification and ship-time security blocking for phases with security enforcement enabled.",
"tier": "full",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtimeCompat": {
"supported": [
"*"
@@ -1648,10 +1760,14 @@ const capabilities = {
"tdd": {
"id": "tdd",
"role": "feature",
"version": "1.5.1-dev.0",
"title": "Test-driven development",
"description": "Injects TDD heuristics into the planner and enforces RED/GREEN gate compliance on type:tdd plans after execution. Owns workflow.tdd_mode; the --tdd CLI flag is the ephemeral override.",
"tier": "full",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtimeCompat": {
"supported": [
"*"
@@ -1697,10 +1813,14 @@ const capabilities = {
"trae": {
"id": "trae",
"role": "runtime",
"version": "1.5.1-dev.0",
"title": "Trae IDE",
"description": "Trae IDE — nested-skill artifact layout; no hook surface (profile-marker-only config); tier-2 support.",
"tier": "core",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtime": {
"configHome": {
"kind": "dot-home",
@@ -1745,10 +1865,14 @@ const capabilities = {
"ui": {
"id": "ui",
"role": "feature",
"version": "1.5.1-dev.0",
"title": "UI design contracts",
"description": "UI-SPEC design contract + retrospective UI audit for frontend phases.",
"tier": "full",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtimeCompat": {
"supported": [
"*"
@@ -1836,10 +1960,14 @@ const capabilities = {
"windsurf": {
"id": "windsurf",
"role": "runtime",
"version": "1.5.1-dev.0",
"title": "Windsurf",
"description": "Windsurf (Codeium) — nested under ~/.codeium/windsurf; skills-only artifact layout; no hook surface; no hook events; tier-2 support.",
"tier": "core",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtime": {
"configHome": {
"kind": "dot-home-nested",
@@ -2592,10 +2720,14 @@ const runtimes = {
"antigravity": {
"id": "antigravity",
"role": "runtime",
"version": "1.5.1-dev.0",
"title": "Antigravity",
"description": "Google Antigravity IDE — nested under ~/.gemini/antigravity; probed across 1.x and 2.x layouts; Gemini hook event dialect; nested skill layout; tier-1 support.",
"tier": "core",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtime": {
"configHome": {
"kind": "dot-home-nested",
@@ -2647,10 +2779,14 @@ const runtimes = {
"augment": {
"id": "augment",
"role": "runtime",
"version": "1.5.1-dev.0",
"title": "Augment Code",
"description": "Augment Code CLI — commands + nested-skill artifact layout; settings-json hook surface; Claude hook event dialect; tier-2 support.",
"tier": "core",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtime": {
"configHome": {
"kind": "dot-home",
@@ -2712,10 +2848,14 @@ const runtimes = {
"claude": {
"id": "claude",
"role": "runtime",
"version": "1.5.1-dev.0",
"title": "Claude Code",
"description": "Anthropic Claude Code — primary development runtime; tier-1 support with full hook surface and skills-based global install.",
"tier": "core",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtime": {
"configHome": {
"kind": "dot-home",
@@ -2774,10 +2914,14 @@ const runtimes = {
"cline": {
"id": "cline",
"role": "runtime",
"version": "1.5.1-dev.0",
"title": "Cline",
"description": "Cline (VS Code extension) — global-only nested-skill layout; cline-rules hook surface (.clinerules); no hook events emitted; tier-2 support.",
"tier": "core",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtime": {
"configHome": {
"kind": "dot-home",
@@ -2813,10 +2957,14 @@ const runtimes = {
"codebuddy": {
"id": "codebuddy",
"role": "runtime",
"version": "1.5.1-dev.0",
"title": "CodeBuddy",
"description": "CodeBuddy (Tencent) — converted commands + skills artifact layout; settings-json hook surface; Claude hook event dialect; tier-2 support.",
"tier": "core",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtime": {
"configHome": {
"kind": "dot-home",
@@ -2878,10 +3026,14 @@ const runtimes = {
"codex": {
"id": "codex",
"role": "runtime",
"version": "1.5.1-dev.0",
"title": "OpenAI Codex CLI",
"description": "OpenAI Codex CLI — shell-var command style; per-agent sandbox tiers; config.toml + hooks.json hook surface; tier-1 support.",
"tier": "core",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtime": {
"configHome": {
"kind": "dot-home",
@@ -2927,10 +3079,14 @@ const runtimes = {
"copilot": {
"id": "copilot",
"role": "runtime",
"version": "1.5.1-dev.0",
"title": "GitHub Copilot",
"description": "GitHub Copilot (VS Code) — markdown config format; copilot-inline hook surface; no hook events emitted; flat skill nesting (unconfirmed recursive loader); tier-2 support.",
"tier": "core",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtime": {
"configHome": {
"kind": "dot-home",
@@ -2976,10 +3132,14 @@ const runtimes = {
"cursor": {
"id": "cursor",
"role": "runtime",
"version": "1.5.1-dev.0",
"title": "Cursor",
"description": "Cursor IDE — skills + converted commands artifact layout; hooks.json surface; Claude hook event dialect; recursive skill loader (flat nesting); tier-2 support.",
"tier": "core",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtime": {
"configHome": {
"kind": "dot-home",
@@ -3041,10 +3201,14 @@ const runtimes = {
"gemini": {
"id": "gemini",
"role": "runtime",
"version": "1.5.1-dev.0",
"title": "Gemini CLI",
"description": "Google Gemini CLI — commands-only artifact layout (TOML); Gemini hook event dialect; settings-json hook surface; tier-2 support.",
"tier": "core",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtime": {
"configHome": {
"kind": "dot-home",
@@ -3094,10 +3258,14 @@ const runtimes = {
"hermes": {
"id": "hermes",
"role": "runtime",
"version": "1.5.1-dev.0",
"title": "Hermes Agent",
"description": "Hermes Agent (NousResearch) — skills nest under skills/gsd/ category bucket; nested skill layout; settings-json hook surface; Claude hook event dialect; tier-2 support.",
"tier": "core",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtime": {
"configHome": {
"kind": "dot-home",
@@ -3143,10 +3311,14 @@ const runtimes = {
"kilo": {
"id": "kilo",
"role": "runtime",
"version": "1.5.1-dev.0",
"title": "Kilo Code",
"description": "Kilo Code — XDG-based config dir; global skills at ~/.kilo/skills (separate from XDG config); flat command/ + skills artifact layout; no lifecycle hook registration; tier-2 support.",
"tier": "core",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtime": {
"configHome": {
"kind": "xdg",
@@ -3214,10 +3386,14 @@ const runtimes = {
"kimi": {
"id": "kimi",
"role": "runtime",
"version": "1.5.1-dev.0",
"title": "Kimi CLI",
"description": "Kimi CLI (Moonshot AI) — generic agents root at ~/.config/agents; skills + kimi-agents artifact layout; no hook surface; no hook events; tier-2 support.",
"tier": "core",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtime": {
"configHome": {
"kind": "generic-agents-root",
@@ -3266,10 +3442,14 @@ const runtimes = {
"opencode": {
"id": "opencode",
"role": "runtime",
"version": "1.5.1-dev.0",
"title": "OpenCode",
"description": "OpenCode — XDG-based config dir; flat command/ + skills artifact layout; settings-json config format; no lifecycle hook registration; tier-2 support.",
"tier": "core",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtime": {
"configHome": {
"kind": "xdg",
@@ -3332,10 +3512,14 @@ const runtimes = {
"qwen": {
"id": "qwen",
"role": "runtime",
"version": "1.5.1-dev.0",
"title": "Qwen Code",
"description": "Qwen Code (Alibaba) — nested-skill artifact layout; settings-json hook surface; Claude hook event dialect; tier-2 support.",
"tier": "core",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtime": {
"configHome": {
"kind": "dot-home",
@@ -3385,10 +3569,14 @@ const runtimes = {
"trae": {
"id": "trae",
"role": "runtime",
"version": "1.5.1-dev.0",
"title": "Trae IDE",
"description": "Trae IDE — nested-skill artifact layout; no hook surface (profile-marker-only config); tier-2 support.",
"tier": "core",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtime": {
"configHome": {
"kind": "dot-home",
@@ -3433,10 +3621,14 @@ const runtimes = {
"windsurf": {
"id": "windsurf",
"role": "runtime",
"version": "1.5.1-dev.0",
"title": "Windsurf",
"description": "Windsurf (Codeium) — nested under ~/.codeium/windsurf; skills-only artifact layout; no hook surface; no hook events; tier-2 support.",
"tier": "core",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtime": {
"configHome": {
"kind": "dot-home-nested",

View File

@@ -236,6 +236,96 @@ const VALID_TIERS = new Set(['core', 'standard', 'full']);
const VALID_ON_ERROR = new Set(['skip', 'halt']);
const RUNTIME_COMPAT_WILDCARD = '*';
// ── ADR-1244 D1: versioned-manifest envelope ─────────────────────────────────
// Official strict SemVer 2.0.0 grammar (https://semver.org). Rejects partials
// ("1.0"), prefixes ("v1.0.0"), leading-zero segments ("01.2.3"), numeric
// prerelease identifiers with leading zeros ("1.2.3-01"), empty identifiers
// ("1.2.3-..") and — critically — prerelease/build identifiers containing
// anything outside [0-9A-Za-z-] (so a version can never smuggle shell
// metacharacters, spaces or unicode into a downstream `git tag v<version>` or
// path). Accepts "1.2.3-dev.0", "1.2.3-rc.1", "1.2.3+build.5".
const SEMVER_RE = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/;
// Permissive *shape* check for a semver range (engines.gsd / compatVersions
// values). Range SATISFACTION is enforced by the runtime overlay (ADR-1244 D2);
// here we only reject empty/garbage and shell metacharacters.
const SEMVER_RANGE_RE = /^[0-9A-Za-z.\-+ |<>=~^*()]+$/;
// Subresource-integrity hash: "sha512-" + base64 of a 64-byte digest (86 base64
// chars + "==" padding). Exact length so malformed pins ("sha512-abc") fail.
const SHA512_INTEGRITY_RE = /^sha512-[A-Za-z0-9+/]{86}==$/;
// A syntactically plausible semver range (shape-only — see SEMVER_RANGE_RE).
// Requires a digit or a bare wildcard so pure-alpha garbage ("abcx", "()x") is
// rejected; full range satisfaction is the runtime overlay's job (ADR-1244 D2).
function isPlausibleRange(s) {
if (typeof s !== 'string') return false;
const t = s.trim();
if (t.length === 0 || !SEMVER_RANGE_RE.test(s)) return false;
return /\d/.test(t) || t === '*' || t === 'x' || t === 'X';
}
/**
* ADR-1244 D1: validate the versioned-manifest envelope.
* - version REQUIRED semver string (the registry rejects a manifest
* without one).
* - engines optional object; engines.gsd optional semver-range string.
* - compatVersions optional object mapping a capability version (semver) to a
* gsd version range.
* - integrity optional "sha512-<base64>" string.
* - provenance optional { sourceRepo, commit } strings.
*
* Shape only — range satisfaction and integrity verification are enforced by
* the source resolver / runtime overlay (ADR-1244 D2/D3).
*
* @param {object} cap The parsed JSON object.
* @returns {string[]} Array of error strings; empty = valid.
*/
function validateVersionEnvelope(cap) {
const errors = [];
if (typeof cap.version !== 'string' || !SEMVER_RE.test(cap.version)) {
errors.push('version must be a semver string (e.g. "1.2.3"); got: ' + JSON.stringify(cap.version));
}
if (cap.engines !== undefined) {
if (typeof cap.engines !== 'object' || cap.engines === null || Array.isArray(cap.engines)) {
errors.push('engines must be an object (e.g. { "gsd": ">=1.6.0" })');
} else if (cap.engines.gsd !== undefined && !isPlausibleRange(cap.engines.gsd)) {
errors.push('engines.gsd must be a semver range string; got: ' + JSON.stringify(cap.engines.gsd));
}
}
if (cap.compatVersions !== undefined) {
if (typeof cap.compatVersions !== 'object' || cap.compatVersions === null || Array.isArray(cap.compatVersions)) {
errors.push('compatVersions must be an object mapping capability versions to gsd version ranges');
} else {
for (const [k, v] of Object.entries(cap.compatVersions)) {
if (!SEMVER_RE.test(k)) errors.push('compatVersions key "' + k + '" must be a semver string');
if (!isPlausibleRange(v)) errors.push('compatVersions["' + k + '"] must be a semver range string');
}
}
}
if (cap.integrity !== undefined && (typeof cap.integrity !== 'string' || !SHA512_INTEGRITY_RE.test(cap.integrity))) {
errors.push('integrity must be a "sha512-<base64>" string');
}
if (cap.provenance !== undefined) {
const p = cap.provenance;
if (typeof p !== 'object' || p === null || Array.isArray(p)) {
errors.push('provenance must be an object { sourceRepo, commit }');
} else {
if (typeof p.sourceRepo !== 'string' || p.sourceRepo.length === 0) {
errors.push('provenance.sourceRepo must be a non-empty string');
}
if (typeof p.commit !== 'string' || p.commit.length === 0) {
errors.push('provenance.commit must be a non-empty string');
}
}
}
return errors;
}
/**
* Validate a single capability declaration.
*
@@ -285,6 +375,9 @@ function validateCapability(cap, folderId) {
}
}
// ── Versioned-manifest envelope (ADR-1244 D1) ──────────────────────────────
errors.push(...validateVersionEnvelope(cap));
// ── Role-specific body ────────────────────────────────────────────────────
if (cap.role === 'feature') {
@@ -2580,6 +2673,11 @@ function main() {
module.exports = {
validateCapability,
// ADR-1244 D1: versioned-manifest envelope validation (reused by the runtime overlay, D2)
validateVersionEnvelope,
SEMVER_RE,
SEMVER_RANGE_RE,
SHA512_INTEGRITY_RE,
validateAgainstContract,
validateConsumesGlobal,
validateCrossCapability,

View File

@@ -68,6 +68,66 @@ function findDrift(opts) {
return drift;
}
// ─── ADR-1244 D6: native capability manifests ────────────────────────────────
//
// Native capabilities (capabilities/<id>/capability.json) carry a `version`
// stamped in lockstep with the package version at release. Unlike
// VERSIONED_MANIFESTS (fixed paths), capabilities are discovered by glob so a
// new capability is auto-covered without editing this file. The version-sync
// regression guard (issue #844) treats every swept capability manifest as
// registered.
// Discover capabilities/<id>/capability.json under `root`, sorted for stable
// staging order. Returns [] when there is no capabilities/ directory.
function listCapabilityManifests(opts) {
const root = (opts && opts.root) || ROOT;
const dir = path.join(root, 'capabilities');
let entries;
try {
entries = fs.readdirSync(dir, { withFileTypes: true });
} catch {
return [];
}
return entries
.filter((e) => e.isDirectory())
// Forward-slash rel paths (NOT path.join) so they match `git ls-files`
// output, git pathspecs, and the forward-slash VERSIONED_MANIFESTS on every
// platform — path.join would emit backslashes on Windows and break the
// issue-844 regression guard's ALLOWED-set comparison.
.map((e) => 'capabilities/' + e.name + '/capability.json')
.filter((rel) => fs.existsSync(path.join(root, rel)))
.sort();
}
// Stamp `version` into each native capability manifest. Returns changed rel paths.
function syncCapabilityVersions(opts) {
const root = (opts && opts.root) || ROOT;
const v = (opts && opts.version) != null ? opts.version : getPackageVersion(root);
const changed = [];
for (const rel of listCapabilityManifests({ root })) {
const abs = path.join(root, rel);
const manifest = readJson(abs);
if (manifest.version !== v) {
manifest.version = v;
fs.writeFileSync(abs, JSON.stringify(manifest, null, 2) + '\n');
changed.push(rel);
}
}
return changed;
}
// Native capability manifests whose version != package version.
function findCapabilityDrift(opts) {
const root = (opts && opts.root) || ROOT;
const v = (opts && opts.version) != null ? opts.version : getPackageVersion(root);
const drift = [];
for (const rel of listCapabilityManifests({ root })) {
const found = readJson(path.join(root, rel)).version;
if (found !== v) drift.push({ manifest: rel, found, expected: v });
}
return drift;
}
// Best-effort outside git; fail-closed inside a work tree so a release never
// ships a stale manifest that the working-tree test already accepted.
function stageManifests(opts) {
@@ -83,21 +143,32 @@ function stageManifests(opts) {
console.warn('sync-manifest-versions: not a git work tree; skipping staging.');
return;
}
const toStage = [...VERSIONED_MANIFESTS, ...listCapabilityManifests({ root })];
try {
execFileSync('git', ['add', '--', ...VERSIONED_MANIFESTS], { cwd: root, stdio: ['ignore', 'ignore', 'pipe'] });
execFileSync('git', ['add', '--', ...toStage], { cwd: root, stdio: ['ignore', 'ignore', 'pipe'] });
} catch (err) {
const detail = err && err.stderr ? err.stderr.toString().trim() : (err && err.message) || 'unknown error';
throw new Error(`sync-manifest-versions: failed to git-add manifests inside a work tree: ${detail}`);
}
}
module.exports = { VERSIONED_MANIFESTS, syncManifestVersions, findDrift, getPackageVersion, stageManifests };
module.exports = {
VERSIONED_MANIFESTS,
syncManifestVersions,
findDrift,
getPackageVersion,
stageManifests,
// ADR-1244 D6: native capability version sweep
listCapabilityManifests,
syncCapabilityVersions,
findCapabilityDrift,
};
if (require.main === module) {
const args = process.argv.slice(2);
const version = getPackageVersion();
if (args.includes('--check')) {
const drift = findDrift({ version });
const drift = [...findDrift({ version }), ...findCapabilityDrift({ version })];
if (drift.length) {
for (const d of drift) {
console.error('Manifest ' + d.manifest + ' version ' + d.found + ' != package.json ' + d.expected);
@@ -105,10 +176,11 @@ if (require.main === module) {
console.error('Run `node scripts/sync-manifest-versions.cjs` to fix.');
process.exitCode = 1;
} else {
console.log('All ' + VERSIONED_MANIFESTS.length + ' versioned manifests in sync at ' + version + '.');
const total = VERSIONED_MANIFESTS.length + listCapabilityManifests().length;
console.log('All ' + total + ' versioned manifests in sync at ' + version + '.');
}
} else {
const changed = syncManifestVersions({ version });
const changed = [...syncManifestVersions({ version }), ...syncCapabilityVersions({ version })];
if (changed.length) {
console.log('Stamped ' + version + ' into: ' + changed.join(', '));
} else {

View File

@@ -0,0 +1,295 @@
'use strict';
/**
* Phase 1 (ADR-1244 / issue #1430): versioned capability manifest.
*
* The build-time validator in scripts/gen-capability-registry.cjs must:
* - REQUIRE a semver `version` on every capability (the registry rejects a
* manifest without one — ADR-1244 D1).
* - Shape-validate the optional ecosystem envelope fields `engines`,
* `compatVersions`, `integrity`, `provenance` when present.
*
* Every native capabilities/<id>/capability.json must carry a valid `version`
* and `engines.gsd` (the conformance / parity requirement: the build fails when
* a native manifest lacks a version).
*
* These are behavioral tests against the exported validator + generator
* pipeline — no source-grep. They mirror the harness in
* tests/capability-registry.test.cjs (makeTempCapDir + loadAndValidate +
* buildRegistry).
*/
const { test, describe } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('fs');
const os = require('os');
const path = require('path');
const ROOT = path.resolve(__dirname, '..');
const helpers = require(path.join(__dirname, 'helpers.cjs'));
const {
validateCapability,
loadAndValidate,
buildRegistry,
SEMVER_RE,
} = require(path.join(ROOT, 'scripts', 'gen-capability-registry.cjs'));
const PKG_VERSION = JSON.parse(fs.readFileSync(path.join(ROOT, 'package.json'), 'utf8')).version;
const CAPABILITIES_DIR = path.join(ROOT, 'capabilities');
// Single source of truth: the validator's own strict-semver regex.
const SEMVER = SEMVER_RE;
// ─── Minimal, otherwise-valid fixtures ───────────────────────────────────────
function featureCap(overrides) {
return {
id: 'demo',
role: 'feature',
version: '1.2.3',
title: 'Demo',
description: 'A demo capability.',
tier: 'standard',
requires: [],
engines: { gsd: '>=1.6.0' },
runtimeCompat: { supported: ['*'], unsupported: [] },
skills: [],
agents: [],
hooks: [],
config: {},
steps: [],
contributions: [],
gates: [],
...overrides,
};
}
function runtimeCap(overrides) {
return {
id: 'demo-rt',
role: 'runtime',
version: '1.2.3',
title: 'Demo RT',
description: 'A demo runtime.',
tier: 'standard',
requires: [],
engines: { gsd: '>=1.6.0' },
runtime: {
configHome: { kind: 'dot-home', name: '.demo', env: [] },
configFormat: 'settings-json',
artifactLayout: { global: [], local: [] },
commandStyle: 'slash-hyphen',
hooksSurface: 'settings-json',
sandboxTier: 'none',
supportTier: 2,
installSurface: 'settings-json',
writesSharedSettings: false,
permissionWriter: null,
extendedHookEvents: [],
},
...overrides,
};
}
function makeTempCapDir(capabilities) {
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cap-ver-test-'));
for (const [id, cap] of Object.entries(capabilities)) {
const subDir = path.join(tmpDir, id);
fs.mkdirSync(subDir, { recursive: true });
fs.writeFileSync(path.join(subDir, 'capability.json'), JSON.stringify(cap), 'utf8');
}
return tmpDir;
}
// ─── Sanity: the base fixtures are valid as-is ───────────────────────────────
describe('version envelope — base fixtures are valid', () => {
test('a feature cap with a valid version passes validation', () => {
assert.deepEqual(validateCapability(featureCap(), 'demo'), []);
});
test('a runtime cap with a valid version passes validation', () => {
assert.deepEqual(validateCapability(runtimeCap(), 'demo-rt'), []);
});
});
// ─── version is required + semver ────────────────────────────────────────────
describe('version is required and must be semver', () => {
test('missing version is rejected (feature)', () => {
const { version: _v, ...cap } = featureCap();
const errors = validateCapability(cap, 'demo');
assert.ok(errors.some((e) => e.includes('version')), `expected a version error, got: ${JSON.stringify(errors)}`);
});
test('missing version is rejected (runtime)', () => {
const { version: _v, ...cap } = runtimeCap();
const errors = validateCapability(cap, 'demo-rt');
assert.ok(errors.some((e) => e.includes('version')), `expected a version error, got: ${JSON.stringify(errors)}`);
});
test('empty-string version is rejected', () => {
const errors = validateCapability(featureCap({ version: '' }), 'demo');
assert.ok(errors.some((e) => e.includes('version')));
});
test('whitespace-only version is rejected', () => {
const errors = validateCapability(featureCap({ version: ' ' }), 'demo');
assert.ok(errors.some((e) => e.includes('version')));
});
test('non-semver versions are rejected', () => {
for (const bad of ['1.0', 'v1.0.0', '1.0.0.0', '1', 'latest', '1.x', '1.0.0 ', '01.2.3']) {
const errors = validateCapability(featureCap({ version: bad }), 'demo');
assert.ok(errors.some((e) => e.includes('version')), `expected "${bad}" to be rejected`);
}
});
test('malformed/hostile prerelease & build identifiers are rejected (strict semver)', () => {
// The prerelease/build suffix must be dot-separated [0-9A-Za-z-] identifiers
// with no leading-zero numerics and no empty segments — so a version can
// never smuggle shell metacharacters, spaces or unicode downstream.
for (const bad of ['1.2.3-01', '1.2.3-..', '1.2.3-', '1.2.3+', '1.2.3-foo bar', '1.2.3-$(whoami)', '1.2.3-`id`', '1.2.3-😈', '1.2.3+build meta']) {
const errors = validateCapability(featureCap({ version: bad }), 'demo');
assert.ok(errors.some((e) => e.includes('version')), `expected hostile suffix "${bad}" to be rejected`);
}
});
test('non-string version is rejected', () => {
for (const bad of [123, null, {}, ['1.0.0']]) {
const errors = validateCapability(featureCap({ version: bad }), 'demo');
assert.ok(errors.some((e) => e.includes('version')), `expected ${JSON.stringify(bad)} to be rejected`);
}
});
test('valid semver versions (incl. prerelease/build) pass', () => {
for (const ok of ['1.0.0', '0.0.1', '10.20.30', '1.2.3-dev.0', '1.2.3-rc.1', '1.2.3+build.5', PKG_VERSION]) {
const errors = validateCapability(featureCap({ version: ok }), 'demo');
assert.deepEqual(errors, [], `expected "${ok}" to pass, got: ${JSON.stringify(errors)}`);
}
});
test('hostile version strings are rejected (shell metachars, newline, unicode)', () => {
for (const bad of ['1.0.0; rm -rf /', '1.0.0\n2.0.0', '1.0.0$(whoami)', '१.२.३', '1.0.0`id`']) {
const errors = validateCapability(featureCap({ version: bad }), 'demo');
assert.ok(errors.some((e) => e.includes('version')), `expected hostile "${bad}" to be rejected`);
}
});
});
// ─── engines (optional; shape-validated) ─────────────────────────────────────
describe('engines is optional but shape-validated when present', () => {
test('omitting engines is valid', () => {
const { engines: _e, ...cap } = featureCap();
assert.deepEqual(validateCapability(cap, 'demo'), []);
});
test('engines must be an object', () => {
for (const bad of ['>=1.6.0', 123, ['gsd'], null]) {
const errors = validateCapability(featureCap({ engines: bad }), 'demo');
assert.ok(errors.some((e) => e.includes('engines')), `expected engines=${JSON.stringify(bad)} rejected`);
}
});
test('engines.gsd must be a non-empty range string', () => {
for (const bad of ['', ' ', 123, {}, 'not a range!!', '>=1.0.0; rm -rf', 'abcx', '()x']) {
const errors = validateCapability(featureCap({ engines: { gsd: bad } }), 'demo');
assert.ok(errors.some((e) => e.includes('engines')), `expected engines.gsd=${JSON.stringify(bad)} rejected`);
}
});
test('valid engines.gsd ranges pass', () => {
for (const ok of ['>=1.6.0', '>=1.6.0 <3.0.0', '^1.0.0', '~1.2.0', '1.x', '*', '>=1.6.0 || >=2.0.0']) {
const errors = validateCapability(featureCap({ engines: { gsd: ok } }), 'demo');
assert.deepEqual(errors, [], `expected range "${ok}" to pass, got: ${JSON.stringify(errors)}`);
}
});
});
// ─── compatVersions / integrity / provenance (optional; shape-validated) ──────
describe('optional ecosystem envelope fields are shape-validated', () => {
test('compatVersions must be an object of semver→range strings', () => {
assert.deepEqual(validateCapability(featureCap({ compatVersions: { '1.0.0': '>=1.6.0' } }), 'demo'), []);
for (const bad of ['x', 123, { '1.0.0': 5 }, { 'not-semver': '>=1.6.0' }]) {
const errors = validateCapability(featureCap({ compatVersions: bad }), 'demo');
assert.ok(errors.some((e) => e.includes('compatVersions')), `expected compatVersions=${JSON.stringify(bad)} rejected`);
}
});
test('integrity must be sha512-<base64>', () => {
const good = 'sha512-' + 'a'.repeat(86) + '==';
assert.deepEqual(validateCapability(featureCap({ integrity: good }), 'demo'), []);
for (const bad of ['abc', 'sha256-deadbeef', 'sha512-', 'sha512-abc', 'sha512-' + 'a'.repeat(40) + '==', 123, 'sha512-not base64!!']) {
const errors = validateCapability(featureCap({ integrity: bad }), 'demo');
assert.ok(errors.some((e) => e.includes('integrity')), `expected integrity=${JSON.stringify(bad)} rejected`);
}
});
test('provenance must be { sourceRepo, commit } strings', () => {
assert.deepEqual(validateCapability(featureCap({ provenance: { sourceRepo: 'https://x/y', commit: 'abc123' } }), 'demo'), []);
for (const bad of ['x', 123, { sourceRepo: 5, commit: 'c' }, { sourceRepo: 'r' }, { commit: 'c' }]) {
const errors = validateCapability(featureCap({ provenance: bad }), 'demo');
assert.ok(errors.some((e) => e.includes('provenance')), `expected provenance=${JSON.stringify(bad)} rejected`);
}
});
});
// ─── Registry pass-through ────────────────────────────────────────────────────
describe('generated registry preserves version + engines', () => {
test('buildRegistry carries version and engines onto the capability object', (t) => {
const capDir = makeTempCapDir({ demo: featureCap({ id: 'demo', version: '2.5.0', engines: { gsd: '>=1.6.0 <2.0.0' } }) });
t.after(() => helpers.cleanup(capDir));
const { capMap, errors } = loadAndValidate(new Set(), capDir);
assert.deepEqual(errors, [], `loadAndValidate errors: ${JSON.stringify(errors)}`);
const registry = buildRegistry(capMap);
assert.equal(registry.capabilities.demo.version, '2.5.0');
assert.equal(registry.capabilities.demo.engines.gsd, '>=1.6.0 <2.0.0');
});
test('loadAndValidate rejects a capability dir whose manifest lacks a version', (t) => {
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cap-ver-noversion-'));
t.after(() => helpers.cleanup(tmpDir));
const sub = path.join(tmpDir, 'demo');
fs.mkdirSync(sub, { recursive: true });
const { version: _v, ...noVersion } = featureCap({ id: 'demo' });
fs.writeFileSync(path.join(sub, 'capability.json'), JSON.stringify(noVersion), 'utf8');
const { errors } = loadAndValidate(new Set(), tmpDir);
assert.ok(errors.some((e) => e.includes('version')), `expected a version error, got: ${JSON.stringify(errors)}`);
});
});
// ─── Native manifest conformance (the ADR-1244 parity requirement) ────────────
describe('every native capability.json carries a valid version + engines.gsd', () => {
const ids = fs
.readdirSync(CAPABILITIES_DIR, { withFileTypes: true })
.filter((d) => d.isDirectory())
.map((d) => d.name);
test('there are native capabilities to check', () => {
assert.ok(ids.length >= 30, `expected the full native capability set, found ${ids.length}`);
});
for (const id of ids) {
test(`capabilities/${id}/capability.json has a semver version`, () => {
const cap = JSON.parse(fs.readFileSync(path.join(CAPABILITIES_DIR, id, 'capability.json'), 'utf8'));
assert.equal(typeof cap.version, 'string', `${id}: version must be a string`);
assert.ok(SEMVER.test(cap.version), `${id}: version "${cap.version}" must be semver`);
});
test(`capabilities/${id}/capability.json declares engines.gsd`, () => {
const cap = JSON.parse(fs.readFileSync(path.join(CAPABILITIES_DIR, id, 'capability.json'), 'utf8'));
assert.ok(cap.engines && typeof cap.engines.gsd === 'string' && cap.engines.gsd.length > 0,
`${id}: engines.gsd must be a non-empty string`);
});
test(`capabilities/${id}/capability.json passes validateCapability`, () => {
const cap = JSON.parse(fs.readFileSync(path.join(CAPABILITIES_DIR, id, 'capability.json'), 'utf8'));
assert.deepEqual(validateCapability(cap, id), [], `${id}: native manifest must validate`);
});
}
});

View File

@@ -1470,6 +1470,7 @@ describe('S1: fragment.path traversal guard', () => {
JSON.stringify({
id: 'planning-advice',
role: 'feature',
version: '1.0.0',
title: 'Planning advice',
description: 'Synthetic fixture for fragment path materialization.',
tier: 'full',
@@ -1515,6 +1516,7 @@ describe('S1: fragment.path traversal guard', () => {
JSON.stringify({
id: 'research',
role: 'feature',
version: '1.0.0',
title: 'Research',
description: 'Synthetic fixture for step fragment materialization.',
tier: 'standard',
@@ -1702,7 +1704,7 @@ describe('C3: role:runtime body validation', () => {
// configHome is now an object (Decision 1), artifactLayout is { global, local } (Decision 3),
// commandStyle is closed enum (Decision 4), hooksSurface is closed enum (Decision 5).
const VALID_RUNTIME_CAP = {
id: 'cursor', role: 'runtime', title: 'Cursor', description: 'Cursor IDE runtime',
id: 'cursor', role: 'runtime', version: '1.0.0', title: 'Cursor', description: 'Cursor IDE runtime',
tier: 'standard', requires: [],
runtime: {
configHome: { kind: 'dot-home', name: '.cursor', env: ['CURSOR_CONFIG_DIR'] },
@@ -2886,6 +2888,7 @@ function makeCommandCap(id, commands) {
return {
id,
role: 'feature',
version: '1.0.0',
title: 'Test cap ' + id,
description: 'Synthetic capability for ADR-959 command tests.',
tier: 'full',
@@ -3085,6 +3088,7 @@ function makeRuntimeCap(overrides) {
return {
id: 'test-rt',
role: 'runtime',
version: '1.0.0',
title: 'Test Runtime',
description: 'A synthetic runtime capability for testing.',
tier: 'core',

View File

@@ -27,6 +27,8 @@ const {
syncManifestVersions,
getPackageVersion,
stageManifests,
listCapabilityManifests,
syncCapabilityVersions,
} = require(path.join(ROOT, 'scripts', 'sync-manifest-versions.cjs'));
// ─── A: RED→GREEN repro via temp fixture ─────────────────────────────────────
@@ -172,12 +174,91 @@ describe('B: real manifests match package.json version', () => {
}
});
// ─── B2: native capability manifests track package.json version (ADR-1244 D6) ─
describe('B2: native capability manifests match package.json version', () => {
const pkgVersion = getPackageVersion(ROOT);
const capManifests = listCapabilityManifests({ root: ROOT });
test('there is at least one native capability manifest', () => {
assert.ok(capManifests.length >= 30, `expected the native capability set, found ${capManifests.length}`);
});
for (const rel of capManifests) {
test(`${rel} version === ${pkgVersion}`, () => {
const m = JSON.parse(fs.readFileSync(path.join(ROOT, rel), 'utf8'));
assert.equal(
m.version,
pkgVersion,
`${rel} version (${m.version}) must match package.json version (${pkgVersion}). ` +
'Run `node scripts/sync-manifest-versions.cjs` to fix.'
);
});
}
});
// ─── B3: syncCapabilityVersions stamps + is idempotent (temp fixture) ─────────
describe('B3: syncCapabilityVersions — temp fixture', () => {
test('stamps stale capability manifests to package version, then is idempotent', () => {
const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-844-cap-'));
try {
fs.writeFileSync(
path.join(tmpRoot, 'package.json'),
JSON.stringify({ name: 'x', version: '9.9.9-test.0' }, null, 2) + '\n'
);
// Two stale capability manifests.
for (const id of ['alpha', 'beta']) {
const dir = path.join(tmpRoot, 'capabilities', id);
fs.mkdirSync(dir, { recursive: true });
fs.writeFileSync(
path.join(dir, 'capability.json'),
JSON.stringify({ id, role: 'feature', version: '0.0.0', title: id }, null, 2) + '\n'
);
}
const found = listCapabilityManifests({ root: tmpRoot });
assert.equal(found.length, 2, 'should discover both capability manifests');
const changed = syncCapabilityVersions({ root: tmpRoot });
assert.equal(changed.length, 2, 'both manifests should be stamped on first run');
for (const rel of found) {
const m = JSON.parse(fs.readFileSync(path.join(tmpRoot, rel), 'utf8'));
assert.equal(m.version, '9.9.9-test.0', `${rel} should be stamped`);
assert.equal(m.title, m.id, `${rel} non-version fields preserved`);
}
// Idempotent second run.
assert.deepEqual(syncCapabilityVersions({ root: tmpRoot }), [], 'second run is a no-op');
} finally {
helpers.cleanup(tmpRoot);
}
});
test('listCapabilityManifests returns [] when there is no capabilities/ dir', () => {
const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-844-nocaps-'));
try {
assert.deepEqual(listCapabilityManifests({ root: tmpRoot }), []);
} finally {
helpers.cleanup(tmpRoot);
}
});
});
// ─── C: Regression guard — all version-bearing JSON files are registered ──────
describe('C: regression guard — version-bearing JSON files must be registered', () => {
// package.json is the version source; package-lock.json is npm-managed.
// Both inherently track the version without the sync script.
const ALLOWED = new Set([...VERSIONED_MANIFESTS, 'package.json', 'package-lock.json']);
// Native capability manifests (capabilities/<id>/capability.json) are
// version-swept by syncCapabilityVersions (ADR-1244 D6) — discovered by glob,
// so every one is "registered" without an explicit entry here.
const ALLOWED = new Set([
...VERSIONED_MANIFESTS,
...listCapabilityManifests({ root: ROOT }),
'package.json',
'package-lock.json',
]);
// Semver-ish: matches X.Y.Z with optional pre-release/build metadata.
const SEMVER = /^\d+\.\d+\.\d+(?:[-+].+)?$/;