Files
msd-core/gsd-core/bin/lib/capability-registry.cjs
Tom Boucher b901d1e06f feat(#1953): complexity-triggered refactor extension point (execute:post) (#3261)
* test(#1953): failing-first suite for the complexity-triggered refactor hook

60 behavioral cases against src/complexity-trigger.cts, which does not exist yet:
decision-point counting, the comment/literal stripping leak surface, threshold and
jump-delta boundaries at limit-1/limit/limit+1, stable-anchor baseline semantics,
and fs fault injection via mock.method. Two fast-check properties assert that
stripping never manufactures a decision point and that comments and string
literals are score-neutral.

Also registers the refactor-trigger capability manifest (inert until
refactor.trigger_enabled) and regenerates the capability registry and matrix.

Verified RED on the remote runner before any implementation exists.

* feat(#1953): complexity-triggered refactor extension point

Adds the opt-in refactor-trigger capability. After a phase executes, an
execute:post step measures per-function complexity for the files the phase
touched and writes a scoped refactor proposal when a function crosses the
configured threshold or drifts past its recorded anchor.

Design notes worth carrying:

- The signal is computed in-core (decision-point counting over comment- and
  literal-stripped source, Node builtins only) rather than via Memtrace or a
  shelled-out analyzer. The hook fires as a deterministic CLI, not an agent
  with MCP tools, and core takes no external dependencies — this is the only
  option a behavioral test can bind to. The metric sits behind a seam.
- The baseline is a stable anchor, not a rolling value: set on first
  observation, moved only on disposition. A rolling baseline makes the delta
  the single-phase change, so a function creeping +2 per phase never trips a
  delta of 5 and the jump check adds nothing over the absolute threshold.
- Strict mode records an open deviation window in the broken-windows ledger
  rather than declaring its own ship:pre gate. ship.md has no generic ship:pre
  gate dispatch — only two hardcoded branches — so a third gate of any kind
  would be declared and never evaluated.
- The gate clears on the proposal being dispositioned, never on the score
  improving. A blocking complexity number is one an executor can satisfy by
  splitting a coherent function in two.

execute-phase.md gains a generic execute:post step-dispatch contract; it
previously matched only ref.skill == "code-review", so any other step
registered there was declared and never run. The code-review branch is
unchanged.

Full rationale in ADR-1953.

Closes #1953

* fix(#1953): close git option injection and symlink escape in the refactor hook

Three findings from the isolated security review, all fixed inline.

HIGH — changedFilesSince interpolated the --since value into a revision
token placed before the -- separator. A -- only stops PATHSPEC parsing of
arguments after it; git still option-parses what comes before. So
--since '--output=/tmp/x' became --output=/tmp/x..HEAD, which git accepts
as --output=<file> and uses to redirect diff output — an arbitrary write.
Fixed with --end-of-options before the revision range plus a conservative
ref validator. The validator deliberately permits ~ ^ @ { } because those
are legitimate git REVISION syntax (HEAD~1, main@{yesterday}) as distinct
from ref-NAME syntax; --end-of-options is the actual barrier. The doc
comment asserting the trailing -- was sufficient was wrong and is corrected.

MEDIUM — resolveConfinedPath confined by string prefix only, so a symlink
committed inside the repo passed the check (its own path is under cwd) and
readFileSync then followed it outside the root. Now lstat-checks for a
regular file and skips anything else with REFACTOR_FILE_UNREADABLE, so one
bad path skips one file and the run continues.

LOW — the new execute:post dispatch contract showed the gsd_run example
before the rule requiring ref.command be validated first. That prose is
executed by an agent, so textual order is execution order. Reordered.

Refs #1953

* fix(#1953): make the analyzer able to see TypeScript at all

Found by running the shipped analyzer over its own source: it reported
functions=1 for a 940-line module with 24 function forms. A return-type
annotation or a generic parameter list made a function invisible —
`function f(a): number {}` and `function f<T>(a: T): T {}` both detected as
zero. Since gsd-core is written in .cts and the capability declares
.ts/.cts/.mts analyzable, the feature silently found nothing in this repo's
own primary language while reporting success. A safety net that reports
"all clear" because it cannot see is worse than no safety net.

All 98 tests passed over this, because every fixture was plain JS — the
exact failure the test matrix's own "assert against the shape production
uses" warning describes. Adds a TypeScript-shapes suite covering return
types (including unions, generics, object literals and type predicates),
generic parameter lists (constrained and defaulted), export/async/generator
combinations, annotated arrows, class-method modifiers, and optional/
default/rest params — plus the two traps: an overload signature has no body
and must not count, and `a < b && c > d` is a comparison, not a generic.
Detection now reports 24/37/21 functions for the three source files, which
matches a hand count exactly.

Also from review:

- The strict-mode ledger dedup identified entries by parsing a prose
  description string. That is banned by CONTRIBUTING's raw-text-matching
  rule and was a real bug: the "exactly one window per untriaged proposal"
  guarantee rested on prose matching, so rewording a description or editing
  WINDOWS.md by hand silently produced duplicates. Now matches structurally
  on kind + phase + file + line.
- A property test asserted on the stripper's output text. Reframed to
  assert the same invariant through analyzeSource's score.
- nextBaseline's `candidates` parameter has been dead since the anchor
  change; removed from the signature and all call sites.
- Extracted the duplicated require-or-degrade and capability-check
  boilerplate.
- ADR-1953's Implementation bullet still named a `refactor.ship-gate` in
  check-command-router.cts — a leftover from the design cut D6 rejects.
  That file is untouched and no such gate exists. Removed.

Refs #1953

* fix(#1953): keep execute-phase.md under its byte ceiling; un-vacuum the large-file test

Five of the seven remote-runner failures were one cause: the execute:post
dispatch contract, written out inline, grew execute-phase.md 1876 bytes
(93,400 -> 95,276) against a frozen PRE_PHASE6 ceiling of 93,600. A drift-ack
does not clear that — tests/phase6-capstone-conformance.test.cjs and
tests/fix-2285-claude-orchestration-wiring.test.cjs assert the file is
literally under the cap.

The contract now lives in gsd-core/references/loop-hook-dispatch.md, which
already claimed to be the point-agnostic dispatch reference and already
documented ref.skill and ref.agent. It gains the ref.command shape, its
in-context validation rule, the advisory-by-construction statement, and a
note that a point whose workflow hand-rolls one kind is not implementing
this contract. execute-phase.md now defers to it in one line: 145 bytes of
growth, 55 B of headroom under the cap. Better placement than the first cut
— the reference was overstating its coverage, and this makes the claim true
rather than duplicating prose next to it.

Acknowledged by appending to tests/emitted-drift-acks/2930-*.json rather
than a new 1953-*.json: two ack sources may never name the same path, and
that fragment is already the accumulating ack for this file.

Sixth and seventh failures: analyzesLargeFileWithinBounds tripped its own
vacuity guard — the fixture generated ~480 KB against a `> 500000` assert,
so the guard fired and the three assertions after it never ran. The test
has been vacuous since it was written. The matrix row specifies ~1 MB, so
N goes 8000 -> 20000 (1.17 MB, 17% margin) and the guard to > 1_000_000.
Verified by reproducing the exact body against the compiled module: 1168888
bytes, 118 ms, all four assertions hold.

Refs #1953

* fix(#1953): fold the execute:post step deferral into the existing resolve line

The remaining two failures were one test: execute-phase.md carries a SECOND,
tighter assertion than the 93,600 ceiling — `<=93400`, which is exactly its
current size. The file cannot grow by a single byte. My previous fix got it
under 93,600 but not under 93,400, so it still failed. ("H." in the report is
just the parent describe of that same test, not a separate defect.)

Rather than add a paragraph, the deferral now REPLACES the existing hook
resolution line. It read:

  Resolve active step hooks from `EXECUTE_POST_HOOKS_JSON` where
  `kind == "step"` and `ref.skill == "code-review"`.

which is the bug itself written down — only code-review was ever dispatched.
It now reads:

  Dispatch each `kind == "step"` hook per
  @gsd-core/references/loop-hook-dispatch.md. For `code-review`:

The following prose already begins "If no active code-review step hook
exists", so it reads correctly and the code-review handling is untouched.
Net effect on the file is -11 bytes: 93,400 -> 93,389, under the margin
assertion rather than merely under the ceiling.

That also removes the need for a drift-ack: the file shrank, so there is no
growth to acknowledge, and the append to the shared 2930-*.json fragment is
reverted. Leaving it would have shipped a claim of "145 bytes of growth"
that is no longer true, on a file six other issues share.

The test's own comment states the principle this ended up honoring: "the host
loop must stay small — optional-feature detail belongs in the capability
fragment, not the host workflow." Putting the dispatch contract in the
reference rather than inline is that rule, applied.

Refs #1953

* fix(#1953): keep the code-review hook literal the workflow test requires

tests/code-review.test.cjs extracts the <step name="code_review_gate"> block
and asserts it contains `ref.skill == "code-review"` verbatim. The previous
commit replaced the line carrying that literal, so the token vanished and the
test went red — a fair assertion: code-review IS the bespoke branch there and
the workflow should still name it.

Restored inside the same one-line deferral, which now reads:

  Dispatch `kind == "step"` hooks per @gsd-core/references/loop-hook-dispatch.md.
  `ref.skill == "code-review"`:

93,396 bytes — still under the `<=93400` margin assertion and 4 bytes below
the base, so the file continues to shrink rather than grow.

Because three consecutive runs were each reddened by a different assertion on
this one file, this change was verified by sweeping ALL of them at once rather
than one run at a time: every test under tests/ that reads execute-phase.md or
references/loop-hook-dispatch.md was located by resolving its path constants,
and each content/size assertion was evaluated directly against the working
tree — 22 assertions, plus two real executions (gen-section-manifest --check,
and emitted-attribution's full real-tree differential). All pass.

That sweep also confirms the earlier judgement call: the net change to
execute-phase.md is a SHRINK, and the size ratchet only gates growth, so
reverting the append to the shared 2930-*.json ack fragment was correct — an
ack would have been both unnecessary and factually wrong.

Refs #1953

* chore(#1953): backfill changeset pr number to 3261

* docs(#1953): add the missing how-to for acting on a refactor proposal

Reference and explanation shipped (COMMANDS.md, CONFIGURATION.md,
FEATURES.md 159, ADR-1953) but the Diataxis how-to quadrant did not, and
that is the one a user reaches for. CONTRIBUTING's required-docs table is
'new command -> COMMANDS.md + FEATURES.md', so CI was green on a gap.

Enabling this feature is genuinely multi-step and no single page walked it:
turn it on, tune the threshold, understand advisory vs strict, discover
that strict needs a SECOND toggle on a DIFFERENT capability, and know what
to do when a proposal appears. The two-toggle subtlety in particular was a
footnote in a config table; here it is a section with both commands.

Follows the shape of its closest siblings, resolve-edge-coverage-findings
and resolve-prohibition-findings — both 'the loop surfaced a finding, here
is what to do with it'. Includes a reason-code table for the silent cases,
since the analyzer is deliberately quiet in six situations and a user who
expected a proposal needs to tell 'nothing to report' from 'could not look'.

Indexed from docs/README.md beside the other loop how-tos.

Docs-only: exempt from the push gate, no re-verification, pass marker on
2af188b4 untouched.

Refs #1953

* feat(#1953): warn when strict mode is on but nothing will actually block

Closes acceptance criterion 5, which I had wrongly marked satisfied.

refactor.trigger_strict records an untriaged proposal as an open deviation
window, but a ship only STOPS if workflow.windows_enforce is also on — a
toggle owned by the broken-windows capability that this feature neither sets
nor requires. So a user could enable strict, believe ship was gated, and find
out otherwise at ship time.

The split itself stays: requires:["broken-windows"] would force-install the
ledger on advisory users who never enable strict, and a ship:pre gate of our
own would never fire because ship.md has no generic ship:pre gate dispatch.
What was missing was discoverability, so that is what this fixes.

`refactor evaluate` now emits a typed REFACTOR_STRICT_NOT_ENFORCING warning,
naming the exact remediation command, whenever strict is on and either
workflow.windows_enforce is off or broken-windows is unavailable. It fires
only on a run that produced a candidate — with nothing to block on there is
nothing to warn about, and warning every run would be noise.

Reads workflow.windows_enforce through the same resolveConfigKey walk the
router already uses for its own keys rather than a second config reader.
Four tests cover the matrix: strict+enforce-off warns, strict+enforce-on does
not, strict+ledger-absent warns, strict-off never warns.

Also corrects a user-facing message in this same file that told the user to
run `gsd-tools config-set` — the wrong form. docs/CONFIGURATION.md and the
broken-windows capability both use `gsd config-set`, and gsd-tools is invoked
as `node gsd-tools.cjs`, so the bare form may not resolve. The two adjacent
messages in this file now agree.

Refs #1953

---------

Co-authored-by: sim <sim@local>
2026-08-09 19:52:47 -04:00

7185 lines
279 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use strict';
/**
* capability-registry.cjs — generated by scripts/gen-capability-registry.cjs
* DO NOT EDIT BY HAND. Run: node scripts/gen-capability-registry.cjs --write
* ADR-894 §5 — role-partitioned Capability Registry.
*/
const capabilities = {
"ai-integration": {
"id": "ai-integration",
"role": "feature",
"version": "1.10.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": [
"*"
],
"unsupported": []
},
"skills": [
"ai-integration-phase"
],
"agents": [
"gsd-framework-selector",
"gsd-ai-researcher",
"gsd-domain-researcher",
"gsd-eval-planner"
],
"hooks": [],
"config": {
"workflow.ai_integration_phase": {
"type": "boolean",
"default": true,
"description": "Prompt for an AI-SPEC design contract before planning phases that involve AI systems."
},
"workflow.api_coverage_gate": {
"type": "boolean",
"default": true,
"description": "Require an explicit API-coverage decision (full-by-default, opt-out-not-opt-in) before a phase that integrates an external API/SDK/service can seal. At plan:pre the planner is prompted to enumerate the API surface into COVERAGE.md; at verify:pre a blocking gate fails the seal unless the matrix exists with every non-integrated capability an explicit, reasoned opt-out. Independent of ai_integration_phase (applies to any external-API integration, not only AI)."
}
},
"steps": [
{
"point": "plan:pre",
"ref": {
"skill": "ai-integration-phase"
},
"produces": [
"AI-SPEC.md"
],
"consumes": [
"CONTEXT.md"
],
"when": "workflow.ai_integration_phase",
"onError": "skip"
}
],
"contributions": [
{
"point": "plan:pre",
"into": "planner",
"fragment": {
"path": "fragments/api-coverage-plan-pre.md",
"inline": "# API Coverage Decision Checkpoint\n\n> Full API Coverage by Default — Opt Out, Never Opt In. Fires when a phase\n> integrates an external API / SDK / service. Most non-API phases will not fire\n> it — that is the point.\n\n## Why this exists\n\n\"We integrated the API\" too often silently means \"we integrated whatever the\nfirst use case exercised.\" Every un-built capability is then an invisible hole,\ndiscovered later by a user who reasonably expected it to work. The phase sealed\ngreen because its tasks completed; nobody decided the gaps were acceptable,\nbecause nobody enumerated them. This checkpoint makes the surface **visible and\ndecided** before the phase can seal.\n\n## Detect whether this phase integrates an external API\n\nThe detector is a deterministic scan over the phase scope. It strips fenced\ncode blocks first, so a trigger term inside a code snippet does not fire. It\nreturns a typed result: `{ detected, signals[], terms }`. Run it on the phase\nscope (the concatenation of this phase's ROADMAP section + the PLAN body):\n\n```bash\nSCOPE=\"$(cat \"${PHASE_DIR}\"/*-PLAN.md 2>/dev/null) $(gsd_run query roadmap.get-phase \"${PHASE}\" 2>/dev/null || true)\"\nAPI_COVERAGE_JSON=$(printf '%s' \"$SCOPE\" | node gsd-core/bin/lib/api-coverage.cjs --json 2>/dev/null || echo '{\"detected\":false,\"signals\":[]}')\n```\n\nRead `API_COVERAGE_JSON.detected`. Act on it only — do **not** pattern-match the\nprose yourself.\n\n**If `detected` is `false`:** this phase does not integrate an external API. Skip\nthe checkpoint entirely and continue planning. Do not raise it with the user.\n\n**If `detected` is `true`:** an external-API integration is in scope. You MUST\nproduce a **coverage matrix** before the plan is finalized.\n\n**If `detected` is `true` but the phase genuinely integrates no external API**\n(the detector is deterministic, not infallible — confirm by re-reading the phase\nscope, not by preference): do NOT fabricate a matrix row for a capability that\ndoes not exist. Write a reasoned declaration to `${PHASE_DIR}/COVERAGE.md`\ninstead:\n\n```markdown\nNo external API integration: <one-line reason — what the phase touches instead>.\n```\n\nThe reason is required, exactly like an `OPT-OUT` reason. The seal-time gate\naccepts this declaration in place of a matrix.\n\n## Produce the coverage matrix\n\nEnumerate the external API's full **capability surface** — the verb/endpoint/method\nlist (e.g. for a music service: `search`, `play`, `pause`, `skip`, `set_volume`,\n`get_playlist`, `create_playlist`, `add_to_playlist`, …). For each capability\nrecord a decision, starting from **full coverage** as the default:\n\n| capability | decision | reason |\n|---|---|---|\n| `<capability-id>` | `INTEGRATE` \\| `OPT-OUT` | `<one-line reason if OPT-OUT>` |\n\nRules:\n\n- **`INTEGRATE` is the default.** Every capability starts as INTEGRATE; the\n matrix is the *subtraction record*.\n- **Every `OPT-OUT` MUST carry a one-line reason** (`not needed`, `not needed\n yet`, `explicitly out of scope`, …). An opt-out without a reason is an\n un-decided hole — the exact failure mode this gate exists to close.\n- **A second integration against the same need** (e.g. a second platform for the\n same capability) starts from the **same full-coverage baseline** as the first.\n Do not carry over the first integration's opt-outs silently — re-decide each\n capability for the new surface, so a first-class/fallback asymmetry cannot\n accumulate.\n\nWrite the matrix to `${PHASE_DIR}/COVERAGE.md` (canonical markdown-table form):\n\n```markdown\n# API Coverage — <service>\n\n> Full coverage by default. Opt-outs are explicit, reasoned decisions.\n\n| capability | decision | reason |\n|---|---|---|\n| search | INTEGRATE | |\n| playlists | INTEGRATE | |\n| skip | OPT-OUT | not needed yet — tracked for follow-up phase |\n```\n\nA fenced ` ```coverage ` JSON block is also accepted for machine-generated\nmatrices; the markdown table is preferred (human-editable, diff-friendly).\n\n## The seal-time gate\n\nThis checkpoint is enforced. At `verify:pre` the `api-coverage.verify-pre` gate\nruns `check api-coverage.verify-pre <phase-dir>`:\n\n- If `COVERAGE.md` exists, it is validated — every row needs a valid decision and\n every `OPT-OUT` a reason. A malformed/partial matrix **blocks the seal**. A\n reasoned `No external API integration: …` declaration (and no rows) passes.\n- If `COVERAGE.md` is absent, the detector runs again over the phase scope. If a\n strong external-API-integration signal is found, the seal is **blocked** until a\n matrix is produced. If no signal is found, the phase is treated as a non-API\n phase and the seal proceeds.\n\nSo: an API-integrating phase cannot seal without a decided matrix. Produce it at\nplan time; do not leave it for seal time.\n\n## Tuning the vocabulary (optional)\n\nThe trigger vocabulary is a curated, additive-only set in\n`gsd-core/bin/lib/api-coverage.cjs` (`DEFAULT_API_COVERAGE_TERMS`). To widen it\nfor a project, override at the call site:\n\n```bash\nprintf '%s' \"$SCOPE\" | node gsd-core/bin/lib/api-coverage.cjs --json \\\n --verbs integrate,wrap,connect,embed --nouns api,sdk,rest,grpc,webhook,plugin\n```\n\nThe whole checkpoint is toggleable via `workflow.api_coverage_gate` in\n`.planning/config.json`.\n"
},
"produces": [
"COVERAGE.md"
],
"consumes": [
"CONTEXT.md"
],
"when": "workflow.api_coverage_gate",
"onError": "skip"
}
],
"gates": [
{
"point": "verify:pre",
"check": {
"query": "api-coverage.verify-pre"
},
"when": "workflow.api_coverage_gate",
"blocking": true,
"onError": "halt"
}
]
},
"antigravity": {
"id": "antigravity",
"role": "runtime",
"version": "1.10.0",
"title": "Antigravity",
"description": "Google Antigravity IDE — nested under ~/.gemini/antigravity; probed across 1.x and 2.x layouts; Gemini hook event dialect; flat 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"
],
"probeExists": "gsd-core/VERSION"
},
"localConfigDir": ".agents",
"configFormat": "settings-json",
"artifactLayout": {
"global": [
{
"kind": "skills",
"destSubpath": "skills",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": "convertClaudeCommandToAntigravitySkill"
},
{
"kind": "agents",
"destSubpath": "agents",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": "convertClaudeAgentToAntigravityAgent"
}
],
"local": [
{
"kind": "skills",
"destSubpath": "skills",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": "convertClaudeCommandToAntigravitySkill"
},
{
"kind": "agents",
"destSubpath": "agents",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": "convertClaudeAgentToAntigravityAgent"
}
]
},
"commandStyle": "slash-hyphen",
"hooksSurface": "settings-json",
"hookEvents": "gemini",
"sandboxTier": "none",
"supportTier": 1,
"installSurface": "settings-json",
"writesSharedSettings": true,
"permissionWriter": "antigravity",
"extendedHookEvents": [],
"hostIntegration": {
"embeddingMode": "declarative",
"commandSurface": "slash-file",
"dispatch": {
"namedDispatch": "undocumented",
"nested": "undocumented",
"maxDepth": "undocumented",
"background": true,
"subagentToolkit": "full",
"backgroundDispatch": "undocumented",
"isolation": "undocumented"
},
"modelMode": "passive",
"hookBus": "host",
"stateIO": "filesystem",
"transport": "mcp",
"runtime": "go",
"effortSurface": "undocumented"
},
"hostBehaviors": {
"projectInstructionFile": "GEMINI.md",
"noPathRewrite": true,
"hookPathStyle": "raw",
"globalDirResolver": "antigravity"
}
},
"reviewer": {
"slug": "antigravity",
"flags": [
"--antigravity",
"--agy"
],
"transport": "spawn",
"probe": {
"kind": "command-exists",
"binary": "agy"
},
"invoke": {
"binary": "agy",
"args": [
"--print-timeout",
"540s",
"{{model}}",
"-p",
"{{prompt}}"
],
"promptChannel": "argv-file-ref",
"outputChannel": "stdout",
"modelArg": "--model",
"effortChannel": "none"
},
"timeoutFloorMs": 600000,
"emptyOutput": "handler-owned",
"reviewsSection": "Antigravity",
"evidenceClass": "source-grounded",
"requiresBinaries": [],
"promptBudgetKey": null,
"modelConfigKey": "review.models.agy",
"handler": "antigravity"
},
"config": {
"review.models.agy": {
"type": "string",
"default": "",
"description": "Model passed to the Antigravity reviewer lane. The key suffix is the lane binary/flag alias `agy`, not the slug `antigravity` — preserved verbatim so existing .planning/config.json files keep working."
}
}
},
"assumption-delta": {
"id": "assumption-delta",
"role": "feature",
"version": "1.10.0",
"title": "Assumption-delta architecture checkpoint",
"description": "Rarely-firing advisory checkpoint that triggers when a phase makes something plural, optional, or chosen that used to be singular, required, or derived. Surfaces one identity-model question (promote the new general representation to primary, or add it alongside?) so a silent primary-key drift does not accumulate into a later user-facing bug. Non-blocking; fires only on a detected signal.",
"tier": "full",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtimeCompat": {
"supported": [
"*"
],
"unsupported": []
},
"skills": [],
"agents": [],
"hooks": [],
"config": {
"workflow.assumption_delta": {
"type": "boolean",
"default": true,
"description": "Enable the assumption-delta architecture checkpoint during planning. When a pluralization/optional/chosen signal is detected in the phase scope, the planner is prompted to re-ask whether the primary key / identity model still names the right thing. Advisory (non-blocking)."
}
},
"steps": [],
"contributions": [
{
"point": "plan:pre",
"into": "planner",
"fragment": {
"path": "fragments/plan-pre.md",
"inline": "# Assumption-Delta Architecture Checkpoint\n\n> Advisory, non-blocking. Fires **only** when the phase scope shows a singular→plural / required→optional / derived→chosen transition. When it fires, it surfaces ONE identity-model question before the plan is finalized. Most phases will not fire it — that is the point.\n\n## Why this exists\n\nMost quietly-imported architectural debt does not come from a missing upfront design phase. It comes at the *seam*: a later phase introduces a second case (a second platform, auth method, tenant, region, source of truth) and nobody re-asks whether the original abstraction still names the right thing. The phase that adds the second case is exactly the 20-minute conversation that prevents an afternoon of later cleanup.\n\n## Run the detector\n\nThe detector is a deterministic scan over the phase scope text. It strips fenced code blocks first, so a trigger word that appears only inside a code snippet does not fire. It returns a typed result: `{ detected, signals[], terms }`. Resolve it through the `assumption-delta scan` query (same phase-section resolver as `roadmap.get-phase`):\n\n```bash\nASSUMPTION_DELTA_JSON=$(gsd_run query assumption-delta scan \"${PHASE}\" --json 2>/dev/null || echo '{\"detected\":false,\"signals\":[],\"terms\":{}}')\n```\n\n> If the phase section cannot be resolved (no `ROADMAP.md` / unknown phase), the query emits `{ \"detected\": false, ... }` — the checkpoint does not fire. Do not block on it.\n>\n> Optional tuning — pass `--terms <comma-list>` to replace the curated pluralization cues for this project (the `optional`/`chosen` cues keep their defaults): `gsd_run query assumption-delta scan \"${PHASE}\" --json --terms second,alternative,fallback`.\n\n## Decision branch\n\nRead `ASSUMPTION_DELTA_JSON`. Act on `detected` only — do **not** pattern-match the human prose.\n\n**If `detected` is `false`:** this phase does not change a core assumption. Skip the checkpoint entirely and continue planning. Do not raise it with the user.\n\n**If `detected` is `true`:** a core assumption may have lost its monopoly. The `signals[]` array tells you which family fired:\n\n| `kind` | What changed | The question to answer |\n|---|---|---|\n| `pluralization` | A second X was introduced where there was one (second platform / auth method / tenant / region / source of truth) | Does the current primary key / identity model still name the right noun? |\n| `optional` | A required / `only` field became optional | Is the field still the right anchor, or has the anchor moved? |\n| `chosen` | A derived value became chosen, or a constant became a parameter | Has a configuration decision become a modeling decision? |\n\nBefore finalizing the plan, answer this for the user and record the decision explicitly:\n\n> **Promote vs. add-alongside.** The usual correct move when a generalization occurs is to **promote** the new general representation to the primary and **demote** the old specific one to a detail of one variant — *not* to add the new one alongside the still-required old one. Adding alongside silently contradicts the generalized intent (a later variant that does not fit the old primary can be stored but never confirmed as a default).\n\nRecord the outcome in the PLAN.md front matter / a `<assumption_delta_decision>` block:\n\n- The **noun** that is now primary (the generalized identity).\n- The **decision**: `promote` | `add-alongside` | `no-change`, with a one-line rationale.\n- If `add-alongside`: call it out as accepted debt and note what would force a later promote.\n\n## Optional companion: an invariant test\n\nWhen `detected` is `true`, suggest (do not require) a contract/invariant test that encodes the now-generalized intent — e.g. *\"every confirmed default round-trips through the primary use-path, for every supported variant.\"* That test goes red the instant a future phase reintroduces the singular assumption, so the regression cannot land silently. If the user accepts, add the test as a task in the plan.\n\n## Tuning the vocabulary (optional)\n\nThe trigger vocabulary is a curated, additive-only set in `gsd-core/bin/lib/assumption-delta.cjs` (`DEFAULT_ASSUMPTION_DELTA_TERMS`). Bare \"or\" is intentionally excluded — it is too common in prose and would make the gate fire constantly. To widen or narrow the cues for a project, override at the call site with `--terms <comma-list>` (replaces the pluralization cues; `optional`/`chosen` keep defaults). The whole checkpoint is toggleable via `workflow.assumption_delta` in `.planning/config.json`.\n\nThis checkpoint is advisory: it informs and records; it never blocks the phase.\n"
},
"produces": [],
"consumes": [
"CONTEXT.md"
],
"when": "workflow.assumption_delta",
"onError": "skip"
}
],
"gates": []
},
"audit": {
"id": "audit",
"role": "feature",
"version": "1.10.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": [
"*"
],
"unsupported": []
},
"skills": [],
"agents": [],
"config": {},
"commands": [
{
"family": "audit-uat",
"module": "audit-command-router.cjs",
"router": "routeAuditUat"
},
{
"family": "audit-open",
"module": "audit-command-router.cjs",
"router": "routeAuditOpen"
}
],
"hooks": [],
"steps": [],
"contributions": [],
"gates": []
},
"augment": {
"id": "augment",
"role": "runtime",
"version": "1.10.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"
]
},
"localConfigDir": ".augment",
"configFormat": "settings-json",
"artifactLayout": {
"global": [
{
"kind": "commands",
"destSubpath": "commands",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": null
},
{
"kind": "skills",
"destSubpath": "skills",
"prefix": "gsd-",
"nesting": "nested",
"recursive": false,
"converter": "convertClaudeCommandToAugmentSkill"
},
{
"kind": "agents",
"destSubpath": "agents",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": "convertClaudeAgentToAugmentAgent"
}
],
"local": [
{
"kind": "commands",
"destSubpath": "commands",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": null
},
{
"kind": "skills",
"destSubpath": "skills",
"prefix": "gsd-",
"nesting": "nested",
"recursive": false,
"converter": "convertClaudeCommandToAugmentSkill"
},
{
"kind": "agents",
"destSubpath": "agents",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": "convertClaudeAgentToAugmentAgent"
}
]
},
"commandStyle": "slash-hyphen",
"hooksSurface": "settings-json",
"hookEvents": "claude",
"sandboxTier": "none",
"supportTier": 2,
"installSurface": "settings-json",
"writesSharedSettings": true,
"permissionWriter": null,
"extendedHookEvents": [],
"hostBehaviors": {
"commandBodyConverter": "convertClaudeToAugmentMarkdown",
"mcpCompanion": "settings-json"
},
"hostIntegration": {
"embeddingMode": "declarative",
"commandSurface": "slash-file",
"dispatch": {
"namedDispatch": true,
"nested": "undocumented",
"maxDepth": "undocumented",
"background": true,
"subagentToolkit": "full",
"backgroundDispatch": "undocumented",
"isolation": "undocumented"
},
"modelMode": "passive",
"hookBus": "host",
"stateIO": "filesystem",
"transport": "mcp",
"runtime": "node",
"effortSurface": "undocumented"
}
}
},
"broken-windows": {
"id": "broken-windows",
"role": "feature",
"version": "1.10.0",
"title": "Broken-windows ledger",
"description": "Cross-phase defect register accumulating stubs, TODOs, skipped tests, unrun verifies, and unmet truths into .planning/WINDOWS.md. When enforcement is enabled, it blocks /gsd-ship while any window is open unless explicitly waived with a recorded reason. Operationalizes GSD's no-defer discipline as a tracked artifact (issue #1950).",
"tier": "full",
"requires": [],
"engines": {
"gsd": ">=1.7.0"
},
"runtimeCompat": {
"supported": [
"*"
],
"unsupported": []
},
"skills": [],
"agents": [],
"hooks": [],
"config": {
"workflow.windows_enforce": {
"type": "boolean",
"default": false,
"description": "Enable the blocking ship:pre gate for the broken-windows ledger. When true (opt-in), /gsd-ship blocks while .planning/WINDOWS.md has any open entry. When false (default), windows are still tracked (the executor and verifier still populate WINDOWS.md via gsd-tools windows append) but ship does not block — teams can adopt tracking before enforcement. Issue #1950."
}
},
"steps": [],
"contributions": [],
"gates": [
{
"point": "ship:pre",
"check": {
"predicate": {
"kind": "artifact-frontmatter-equals",
"artifact": "WINDOWS.md",
"field": "open_count",
"equals": 0
}
},
"when": "workflow.windows_enforce",
"blocking": true,
"onError": "halt"
}
]
},
"claude": {
"id": "claude",
"role": "runtime",
"version": "1.10.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"
]
},
"localConfigDir": ".claude",
"configFormat": "settings-json",
"artifactLayout": {
"global": [
{
"kind": "skills",
"destSubpath": "skills",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": "convertClaudeCommandToClaudeSkill"
}
],
"local": [
{
"kind": "commands",
"destSubpath": "commands",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": null
},
{
"kind": "agents",
"destSubpath": "agents",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": null
}
]
},
"commandStyle": "slash-hyphen",
"hooksSurface": "settings-json",
"hookEvents": "claude",
"sandboxTier": "none",
"supportTier": 1,
"installSurface": "settings-json",
"writesSharedSettings": true,
"permissionWriter": null,
"extendedHookEvents": [
"SubagentStop",
"Stop",
"PreCompact",
"FileChanged"
],
"hostIntegration": {
"embeddingMode": "imperative",
"commandSurface": "slash-file",
"dispatch": {
"namedDispatch": true,
"nested": true,
"maxDepth": 5,
"background": true,
"subagentToolkit": "full",
"backgroundDispatch": false,
"isolation": "harness-worktree"
},
"modelMode": "passive",
"hookBus": "host",
"stateIO": "filesystem",
"transport": "mcp",
"runtime": "node",
"effortSurface": "argv"
},
"harnessIsolationFlag": "isolation=\"worktree\"",
"hostBehaviors": {
"attributionSource": "settings-json-commit",
"authorsCanonicalWorkflow": true,
"localInstallStyle": "legacy-flat",
"permissionsSchema": "claude",
"settingsFileByScope": {
"local": "settings.local.json",
"global": "settings.json"
},
"sourceMarkerFile": ".gsd-source",
"agentFrontmatterExtensions": [
"effort"
],
"ownsClaudePaths": true,
"nativeModelAliases": true,
"skillsGlobalOnboarding": true,
"legacyCommandsGsdInstallMigration": true,
"legacyCommandsGsdUninstall": "global",
"hyphenNameAgentBody": true
}
},
"reviewer": {
"slug": "claude",
"flags": [
"--claude"
],
"transport": "spawn",
"probe": {
"kind": "command-exists",
"binary": "claude"
},
"invoke": {
"binary": "claude",
"args": [
"{{model}}",
"{{effort}}",
"-p",
"-"
],
"promptChannel": "stdin",
"outputChannel": "stdout",
"modelArg": "--model",
"effortChannel": "argv"
},
"timeoutFloorMs": 1200000,
"emptyOutput": "stub-with-stderr",
"reviewsSection": "Claude",
"evidenceClass": "source-grounded",
"requiresBinaries": [],
"promptBudgetKey": null,
"modelConfigKey": "review.models.claude",
"handler": null
},
"config": {
"review.models.claude": {
"type": "string",
"default": "",
"description": "Model passed to the Claude reviewer lane."
}
}
},
"claude-orchestration": {
"id": "claude-orchestration",
"role": "feature",
"version": "1.10.0",
"title": "Claude orchestration (Workflow backend)",
"description": "Default-off, BETA, claude-only capability that adopts Claude Code's Workflow tool (the engine behind /effort ultracode) as an optional parallel-execution backend for the GSD loop. When the runtime exposes the Workflow tool and claude_orchestration.execution_backend resolves to 'workflow', execute-phase emits a generated Workflow script (waves -> parallel() barriers, plans -> agent({ agentType: 'gsd-executor', isolation: 'worktree' }), files_modified overlap -> separate sequential stages, resumeFromRunId wired to the phase run id, shared token budget) that composes the SAME gsd-executor agent and worktree isolation the inline path uses, restoring the wave parallelism the #853 backgrounded-agent nesting limitation forces inline on Claude Code. (The plan-checker and verifier remain inline until separately wired — this capability delivers the parallel-execution backend, not those gates.) Also folds the ultraplan plan-offload under one runtime gate (plan:* surface). On any runtime lacking the Workflow tool, or when the capability is disabled, behaviour is byte-identical to today (inline/manual dispatch). Detection + emission live in gsd-core/bin/lib/claude-orchestration.cjs (pure, fail-closed). Mirrors the existing gsd-ultraplan-phase BETA-isolation posture.",
"tier": "full",
"requires": [],
"engines": {
"gsd": ">=1.7.0"
},
"runtimeCompat": {
"supported": [
"claude"
],
"unsupported": []
},
"skills": [],
"agents": [],
"hooks": [],
"commands": [
{
"family": "claude-orchestration",
"module": "claude-orchestration-command-router.cjs",
"router": "routeClaudeOrchestrationCommand",
"subcommands": [
"detect-backend",
"emit-workflow",
"resolve-wave-dispatch"
]
}
],
"activationKey": "claude_orchestration.enabled",
"config": {
"claude_orchestration.enabled": {
"type": "boolean",
"default": false,
"description": "Master toggle for the Claude orchestration capability. Default-off + BETA: the Workflow-tool execution backend and the ultraplan plan-offload surface are inert unless this is true. When false, loop behaviour is byte-identical to a non-Claude runtime (inline/manual dispatch)."
},
"claude_orchestration.execution_backend": {
"type": "enum",
"values": [
"auto",
"workflow",
"inline"
],
"default": "auto",
"description": "Which execute-phase dispatch backend to use when the capability is enabled. 'auto' (default) activates the Workflow backend only when the runtime is Claude AND the Workflow tool is detected AND the Agent SDK meets claude_orchestration.min_agent_sdk_version; otherwise it falls back to inline. 'workflow' forces the Workflow backend when the tool is present AND the Agent SDK meets the floor (still fails closed to inline if the tool is absent or the SDK is too old — the floor applies in both modes). 'inline' forces today's manual one-agent-per-message dispatch regardless of tool availability."
},
"claude_orchestration.min_agent_sdk_version": {
"type": "string",
"default": "0.3.149",
"description": "Minimum Agent SDK version required to activate the Workflow backend under execution_backend='auto'. Defaults to 0.3.149 (the release that introduced the Workflow tool). Raise to pin a higher floor; the detection seam fails closed to inline for any runtime reporting an older or unknown version."
}
},
"steps": [],
"contributions": [
{
"point": "execute:wave:pre",
"into": "executor",
"fragment": {
"path": "fragments/execute-wave-pre.md",
"inline": "# Claude orchestration — Workflow execution backend (BETA)\n\n> Injected at `execute:wave:pre` `into: executor` only when\n> `claude_orchestration.enabled` is true. Default-off; `onError: skip`.\n\n## When this contribution is active\n\nThe Claude orchestration capability is **default-off and BETA**. It activates only\nwhen ALL of the following hold:\n\n1. `claude_orchestration.enabled` is `true` in `.planning/config.json`, AND\n2. the active runtime is **Claude Code** (the Workflow tool is Claude / Agent\n SDK-specific), AND\n3. `claude_orchestration.execution_backend` resolves to `workflow` — either\n explicitly, or via `auto` — **and** the Agent SDK version is\n `>= claude_orchestration.min_agent_sdk_version` (default `0.3.149`). The SDK\n floor applies in both `auto` and `workflow` modes (fail-closed: a pre-release\n or older SDK never activates the preview backend).\n\nDetection is fail-closed: any miss degrades to **inline, manual, one-agent-per-\nmessage dispatch** — exactly today's behaviour. On a non-Claude runtime this\ncontribution is a no-op.\n\n## Why `execute:wave:pre` (not `execute:wave:post`)\n\nThis is a **dispatch-backend selector** — it decides HOW a wave's executor agents\nare spawned. That decision has to be made BEFORE the wave's `Agent()` calls in\n`execute-phase.md` step 3, not after the wave has already finished (#2285). The\ncapability previously registered at `execute:wave:post`, which fires only after\nworktree merge/post-merge tests/tracking updates — by then the wave was already\ndispatched inline, so the contribution was structurally unable to change how\ndispatch happened. This fragment is injected at the point that actually precedes\ndispatch.\n\n## What the orchestrator does when the Workflow backend is active\n\nBefore spawning executor agents for the current wave (execute-phase.md step 3),\nresolve the dispatch backend through the single composed CLI seam:\n\n```bash\ngsd-tools claude-orchestration resolve-wave-dispatch \\\n --waves \"$WAVE_MANIFEST_PATH\" --run-id \"$PHASE_RUN_ID\" \\\n --runtime \"$RUNTIME\" \\\n --phase-dir \"$PHASE_DIR\" --raw\n```\n\n`--agent-sdk-version` is no longer passed here (#2590). The router resolves the\ninstalled Agent SDK version itself; see **Agent SDK version** below. The former\n`${AGENT_SDK_VERSION:+--agent-sdk-version \"$AGENT_SDK_VERSION\"}` line was also\n**shell-dependent**: zsh does not word-split unquoted parameter expansions, so it\ncollapsed to a SINGLE argv element there, `argValue()` never matched, and the run\nfailed into `agent_sdk_version_unknown` — indistinguishable from genuinely\nunknown. Pass `--agent-sdk-version <ver>` explicitly only to pin a version.\n\nThis composes `detectWorkflowBackend` (the gate ladder above) with\n`emitWorkflowScript` (the wave→plan mapping below) in ONE call — the pure\nfunction backing it is `resolveWaveDispatch` in\n`gsd-core/bin/lib/claude-orchestration.cjs`. Response shape:\n`{ backend: 'inline'|'workflow', reason, script?, summary? }`.\n\n### Manifest construction (`$WAVE_MANIFEST_PATH`, `$PHASE_RUN_ID`, `$PHASE_DIR`)\n\nThese are NOT pre-existing execute-phase.md variables — the orchestrator builds\nthem at this step, from data it already has in-context from `discover_and_group_plans`\n(the `PLAN_INDEX` JSON) and step 2.5 (the per-plan `USE_WORKTREES_FOR_PLAN` decision):\n\n1. **`$PHASE_DIR`** — reuse `{phase_dir}` from the `INIT` bundle (already loaded\n in the `initialize` step). No new value needed.\n\n2. **`$PHASE_RUN_ID`** — a stable identifier for THIS phase-execution attempt, so\n `resumeFromRunId` can resume an interrupted run without re-dispatching plans\n the Workflow tool already completed. Construct it deterministically —\n `execute-{phase_number}-{phase_slug}` — from `INIT`'s `phase_number`/`phase_slug`\n (both are already validated identifiers used elsewhere in this workflow, so\n they satisfy `emitWorkflowScript`'s `isScriptableIdentifier` check). Do NOT\n mint a new random id per wave — the SAME `$PHASE_RUN_ID` is reused for every\n wave in the phase so the Workflow tool can correctly track cross-wave resume\n state.\n\n3. **`$WAVE_MANIFEST_PATH`** — a fresh temp file for THIS wave's manifest (one\n wave = one `waves` array with a single entry, matching the wave-by-wave\n dispatch loop; do not batch multiple waves into one manifest — waves are\n dispatched in wave order, not all at once):\n\n ```bash\n WAVE_MANIFEST_PATH=$(mktemp \"${TMPDIR:-/tmp}/gsd-wave-dispatch-XXXXXX\") && mv \"$WAVE_MANIFEST_PATH\" \"$WAVE_MANIFEST_PATH.json\" && WAVE_MANIFEST_PATH=\"$WAVE_MANIFEST_PATH.json\"\n ```\n\n Then **use the Write tool** (not a bash/jq pipeline — the orchestrator already\n has every field parsed in-context) to write the manifest JSON to\n `$WAVE_MANIFEST_PATH`:\n\n ```json\n {\n \"waves\": [\n {\n \"id\": \"wave-{N}\",\n \"plans\": [\n {\n \"id\": \"{plan_id}\",\n \"brief\": \"{the SAME <objective>...<success_criteria> prompt block step 3 builds for this plan's inline Agent() call}\",\n \"files_modified\": [\"{from PLAN_INDEX.plans[].files_modified for this plan}\"],\n \"use_worktree\": {true unless step 2.5 set USE_WORKTREES_FOR_PLAN=false for this plan}\n }\n ]\n }\n ]\n }\n ```\n\n - **`id`** — the plan id from `PLAN_INDEX`, e.g. `\"01-01\"`.\n - **`brief`** — MUST carry the same task content as step 3's inline `Agent()`\n prompt (the `<objective>`/`<execution_context>`/`<files_to_read>`/\n `<success_criteria>` block, with `{plan_number}`/`{phase_number}`/\n `{phase_name}` substituted) — a short summary here would NOT reproduce\n step 3's behavior and would violate the \"identical artifacts\" contract.\n - **`files_modified`** — copy verbatim from the plan's `PLAN_INDEX` entry.\n - **`use_worktree`** — `true` for every plan UNLESS step 2.5's per-plan\n worktree gate (`execute-phase/steps/per-plan-worktree-gate.md`) set\n `USE_WORKTREES_FOR_PLAN=false` for that plan (submodule-touching plan, or\n project-level `USE_WORKTREES=false`) — in which case pass `false` here so\n `emitWorkflowScript` omits `isolation: \"worktree\"` for that plan (#2772 /\n #2285 finding 1). **Never** hardcode `true` — that would force worktree\n isolation on a plan the inline path explicitly keeps out of worktrees.\n\n4. **`$AGENT_SDK_VERSION`** — no longer built here; the router resolves it.\n\n**Agent SDK version:** the orchestrator has no *bash-computable* way to\nintrospect the live Agent SDK version — but the router runs in Node, so it\nresolves the version itself (#2590), in this order:\n\n1. an explicit `--agent-sdk-version <ver>` (pin a version),\n2. `GSD_AGENT_SDK_VERSION`,\n3. the **installed** `@anthropic-ai/claude-agent-sdk` package version, read from\n its `package.json` on disk by walking `node_modules` up the tree. (Read\n directly rather than via `require.resolve`: the SDK's `exports` map does not\n expose `./package.json`, so `require.resolve` throws\n `ERR_PACKAGE_PATH_NOT_EXPORTED`.)\n\nPreviously nothing computed this at all, so gate 5 returned\n`agent_sdk_version_unknown` on **every** automated run and the Workflow backend\ncould never activate — while `gsd-tools capability state` still reported the\ncapability `active: true`. Fail-closed is preserved: when no version can be\nresolved, gate 5 still declines to `inline`. What changed is that a resolvable\nversion is now actually found, so a genuinely-too-old SDK reports\n`agent_sdk_version_below_floor` — the truthful reason — instead of `unknown`.\n\n**If `backend == \"workflow\"`:** run the emitted `script` via the Workflow tool\nfor THIS wave instead of the per-message `Agent()` loop in step 3. The script\ncomposes the SAME `gsd-executor` agent type the inline path uses, with\nworktree isolation applied PER PLAN from the manifest's `use_worktree` field\n(see `emitWorkflowScript`):\n\n- **waves → one or more sequential `parallel()` barriers** — each wave is a\n barrier group; when plans within a wave share `files_modified`, they are split\n into separate sequential stages within that wave's barrier.\n- **plans → `agent(brief, { agentType: 'gsd-executor', isolation: 'worktree' })`**\n when `use_worktree` is not `false`, or `agent(brief, { agentType: 'gsd-executor' })`\n (no isolation) when it is — so the produced `SUMMARY.md` and commits are\n identical to inline dispatch, INCLUDING the inline path's submodule safety\n gate (#2772 / #2285 finding 1).\n- **`files_modified` overlap → separate sequential stages** — the same overlap\n rule execute-phase already applies inline (step 1 of the wave loop).\n- **`resumeFromRunId`** — **pass `summary.resumeRunId` as the Workflow tool's\n `resumeFromRunId` INPUT when you invoke the tool.** It is a tool parameter,\n not a script function; the script deliberately does not call it (#2590 — doing\n so threw \"resumeFromRunId is not defined\" and rejected the entire script).\n Omitting it from the tool invocation silently regresses phase-resume to a\n no-op: an interrupted phase re-runs completed plans.\n\nThe orchestrator still runs steps 4–5.8 (wait for completion, worktree cleanup,\npost-merge gate, tracking update) exactly as it does for inline dispatch — the\nWorkflow backend only replaces HOW agents are spawned for this wave, not what\nhappens after they return.\n\n**If `backend == \"inline\"`** (any gate miss, or `resolve-wave-dispatch` itself\nunavailable/erroring): proceed to step 3's standard per-message `Agent()`\ndispatch — the default, byte-identical-to-today path. `onError: skip` on this\ncontribution means a `resolve-wave-dispatch` command failure is treated exactly\nlike an `inline` result, never as a fatal wave error.\n\n## Fallback contract\n\nDetection is fail-closed end-to-end: capability disabled, non-Claude runtime,\n`execution_backend:\"inline\"`, missing/incapable host descriptor, unknown or\nbelow-floor Agent SDK version, or an `emitWorkflowScript` failure on a malformed\nwave manifest — ANY of these degrades to `backend:\"inline\"` and execute-phase's\nstandard inline dispatch (step 3) runs unmodified. The Workflow backend never\npartially activates; the executor MUST NOT assume parallelism, a shared budget,\nor resume-from-run-id semantics when `backend == \"inline\"`.\n"
},
"produces": [],
"consumes": [
"PLAN.md"
],
"when": "claude_orchestration.enabled",
"onError": "skip"
},
{
"point": "plan:post",
"into": "planner",
"fragment": {
"path": "fragments/plan-post.md",
"inline": "# Claude orchestration — ultraplan plan-offload ownership (BETA)\n\n> Injected at `plan:post` `into: planner` only when\n> `claude_orchestration.enabled` is true. Default-off; `onError: skip`.\n\n## Ownership declaration\n\nThe `gsd-ultraplan-phase` plan-offload surface (offloading GSD's plan phase to\nClaude Code's ultraplan cloud) is **owned by this capability**, not by a\nstandalone BETA skill. Both surfaces share one runtime gate\n(`claude_orchestration.enabled`), one BETA boundary, and one Claude-Code-only\ndetection seam.\n\n## When the planner should consider ultraplan offload\n\nWhen this contribution is active (capability enabled, Claude Code runtime), the\nplanner MAY offer the `/gsd-ultraplan-phase` path as an alternative to local\n`/gsd-plan-phase` for phases where cloud-assisted planning adds value. This is\nadvisory, not mandatory — the stable local planner remains the default.\n\n## Fallback contract\n\nIf the capability is disabled, or the runtime is not Claude Code, ultraplan\noffload is **not surfaced** and the planner proceeds with the standard local\n`/gsd-plan-phase`. The `gsd-ultraplan-phase` command itself remains installed\n(its own runtime gate already no-ops on non-Claude runtimes); this contribution\nonly governs whether the capability manifest advertises it as part of the\norchestration surface.\n"
},
"produces": [],
"consumes": [
"CONTEXT.md"
],
"when": "claude_orchestration.enabled",
"onError": "skip"
}
],
"gates": []
},
"cline": {
"id": "cline",
"role": "runtime",
"version": "1.10.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"
]
},
"localConfigDir": ".cline",
"configFormat": "markdown-dir",
"artifactLayout": {
"global": [
{
"kind": "skills",
"destSubpath": "skills",
"prefix": "gsd-",
"nesting": "nested",
"recursive": false,
"converter": "convertClaudeCommandToClineSkill"
}
],
"local": []
},
"commandStyle": "slash-hyphen",
"hooksSurface": "cline-rules",
"sandboxTier": "none",
"supportTier": 2,
"installSurface": "cline-rules",
"writesSharedSettings": false,
"permissionWriter": null,
"extendedHookEvents": [],
"hostIntegration": {
"embeddingMode": "imperative",
"commandSurface": "slash-file",
"dispatch": {
"namedDispatch": true,
"nested": false,
"maxDepth": 1,
"background": true,
"subagentToolkit": "read-only",
"backgroundDispatch": false,
"isolation": "undocumented"
},
"modelMode": "active",
"hookBus": "host",
"stateIO": "filesystem",
"transport": "mcp",
"runtime": "node",
"effortSurface": "undocumented"
},
"hostBehaviors": {
"reapplyCommand": "/gsd-update --reapply",
"frontmatterDialect": "cline",
"skipSharedHooksInstall": true,
"localTargetIsProjectRoot": true,
"clineRulesSurface": true,
"localCommandsViaRules": true
}
}
},
"code-review": {
"id": "code-review",
"role": "feature",
"version": "1.10.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": [
"*"
],
"unsupported": []
},
"skills": [
"code-review"
],
"agents": [
"gsd-code-reviewer",
"gsd-code-fixer"
],
"hooks": [],
"config": {
"workflow.code_review": {
"type": "boolean",
"default": true,
"description": "Enable code-review participation in post-execution review flows."
},
"workflow.code_review_depth": {
"type": "enum",
"values": [
"quick",
"standard",
"deep"
],
"default": "standard",
"description": "Default depth for code review when no --depth override is supplied."
}
},
"steps": [
{
"point": "execute:post",
"ref": {
"skill": "code-review"
},
"produces": [
"REVIEW.md"
],
"consumes": [
"SUMMARY.md"
],
"when": "workflow.code_review",
"onError": "skip"
}
],
"contributions": [],
"gates": []
},
"codebuddy": {
"id": "codebuddy",
"role": "runtime",
"version": "1.10.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"
]
},
"localConfigDir": ".codebuddy",
"configFormat": "settings-json",
"artifactLayout": {
"global": [
{
"kind": "commands",
"destSubpath": "commands",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": "convertClaudeCommandToCodebuddyCommand"
},
{
"kind": "skills",
"destSubpath": "skills",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": "convertClaudeCommandToCodebuddySkill"
},
{
"kind": "agents",
"destSubpath": "agents",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": "convertClaudeAgentToCodebuddyAgent"
}
],
"local": [
{
"kind": "commands",
"destSubpath": "commands",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": "convertClaudeCommandToCodebuddyCommand"
},
{
"kind": "skills",
"destSubpath": "skills",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": "convertClaudeCommandToCodebuddySkill"
},
{
"kind": "agents",
"destSubpath": "agents",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": "convertClaudeAgentToCodebuddyAgent"
}
]
},
"commandStyle": "slash-hyphen",
"hooksSurface": "settings-json",
"hookEvents": "claude",
"sandboxTier": "none",
"supportTier": 2,
"installSurface": "settings-json",
"writesSharedSettings": true,
"permissionWriter": null,
"extendedHookEvents": [
"SubagentStop",
"Stop",
"PreCompact",
"SubagentStart"
],
"hostIntegration": {
"embeddingMode": "declarative",
"commandSurface": "slash-file",
"dispatch": {
"namedDispatch": true,
"nested": false,
"maxDepth": 1,
"background": true,
"subagentToolkit": "full",
"backgroundDispatch": false,
"isolation": "undocumented"
},
"modelMode": "passive",
"hookBus": "host",
"stateIO": "filesystem",
"transport": "mcp",
"runtime": "node",
"effortSurface": "undocumented"
},
"hostBehaviors": {
"reportCommandsDir": true
}
}
},
"coderabbit": {
"id": "coderabbit",
"role": "reviewer",
"version": "1.10.0",
"title": "CodeRabbit",
"description": "CodeRabbit CLI — cross-AI /gsd:review reviewer lane only; not a GSD install target (no runtime body, no artifacts). Reviews the working-tree diff (`coderabbit review --prompt-only`), not the source tree, and accepts neither a prompt nor a model flag; findings are down-weighted in consensus (evidenceClass: diff-only).",
"tier": "full",
"requires": [],
"engines": {
"gsd": ">=1.8.0"
},
"reviewer": {
"slug": "coderabbit",
"flags": [
"--coderabbit"
],
"transport": "spawn",
"probe": {
"kind": "command-exists",
"binary": "coderabbit"
},
"invoke": {
"binary": "coderabbit",
"args": [
"review",
"--prompt-only"
],
"promptChannel": "none",
"outputChannel": "stdout",
"modelArg": null,
"effortChannel": "none"
},
"timeoutFloorMs": 360000,
"emptyOutput": "stub-with-stderr",
"reviewsSection": "CodeRabbit",
"evidenceClass": "diff-only",
"requiresBinaries": [],
"promptBudgetKey": null,
"modelConfigKey": null,
"handler": null
}
},
"codex": {
"id": "codex",
"role": "runtime",
"version": "1.10.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"
]
},
"localConfigDir": ".codex",
"configFormat": "toml",
"artifactLayout": {
"global": [
{
"kind": "skills",
"destSubpath": "skills",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": "convertClaudeCommandToCodexSkill",
"home": ".agents"
}
],
"local": [
{
"kind": "skills",
"destSubpath": "skills",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": "convertClaudeCommandToCodexSkill"
}
]
},
"commandStyle": "shell-var",
"hooksSurface": "codex-hooks-json",
"hookEvents": "claude",
"sandboxTier": "codex-agent-sandbox",
"supportTier": 1,
"installSurface": "codex-toml",
"writesSharedSettings": false,
"permissionWriter": null,
"extendedHookEvents": [
"SubagentStop",
"Stop",
"PreCompact"
],
"hostIntegration": {
"embeddingMode": "declarative",
"commandSurface": "slash-file",
"dispatch": {
"namedDispatch": true,
"nested": true,
"maxDepth": 1,
"background": true,
"subagentToolkit": "full",
"backgroundDispatch": true,
"isolation": "orchestrator-worktree"
},
"modelMode": "passive",
"hookBus": "host",
"stateIO": "filesystem",
"transport": "mcp",
"runtime": "node",
"effortSurface": "argv"
},
"orchestratorExec": {
"command": "codex",
"args": [
"exec"
],
"cwdFlag": "--cd",
"promptFlag": null
},
"hostBehaviors": {
"reapplyCommand": "$gsd-update --reapply",
"tomlConfigInstall": true,
"cleanupSkillSidecars": true,
"agentTomlFiles": true,
"frontmatterDialect": "codex"
}
},
"reviewer": {
"slug": "codex",
"flags": [
"--codex"
],
"transport": "spawn",
"probe": {
"kind": "command-exists",
"binary": "codex"
},
"invoke": {
"binary": "codex",
"args": [
"exec",
"--ephemeral",
"{{model}}",
"{{effort}}",
"--skip-git-repo-check",
"{{output}}",
"-"
],
"promptChannel": "stdin",
"outputChannel": "file-arg",
"outputArg": "-o",
"modelArg": "--model",
"effortChannel": "argv"
},
"timeoutFloorMs": 1200000,
"emptyOutput": "stub-with-stderr",
"reviewsSection": "Codex",
"evidenceClass": "source-grounded",
"requiresBinaries": [],
"promptBudgetKey": null,
"modelConfigKey": "review.models.codex",
"handler": null
},
"config": {
"review.models.codex": {
"type": "string",
"default": "",
"description": "Model passed to the Codex reviewer lane."
}
}
},
"copilot": {
"id": "copilot",
"role": "runtime",
"version": "1.10.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"
]
},
"localConfigDir": ".github",
"configFormat": "markdown",
"artifactLayout": {
"global": [
{
"kind": "skills",
"destSubpath": "skills",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": "convertClaudeCommandToCopilotSkill"
},
{
"kind": "agents",
"destSubpath": "agents",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": "convertClaudeAgentToCopilotAgent"
}
],
"local": [
{
"kind": "skills",
"destSubpath": "skills",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": "convertClaudeCommandToCopilotSkill"
},
{
"kind": "agents",
"destSubpath": "agents",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": "convertClaudeAgentToCopilotAgent"
}
]
},
"commandStyle": "slash-hyphen",
"hooksSurface": "copilot-inline",
"sandboxTier": "none",
"supportTier": 2,
"installSurface": "copilot-instructions",
"writesSharedSettings": false,
"permissionWriter": null,
"extendedHookEvents": [],
"hostIntegration": {
"embeddingMode": "declarative",
"commandSurface": "slash-file",
"dispatch": {
"namedDispatch": true,
"nested": false,
"maxDepth": 1,
"background": true,
"subagentToolkit": "full",
"backgroundDispatch": false,
"isolation": "undocumented"
},
"modelMode": "passive",
"hookBus": "host",
"stateIO": "filesystem",
"transport": "mcp",
"runtime": "undocumented",
"effortSurface": "undocumented"
},
"hostBehaviors": {
"reapplyCommand": "/gsd-update --reapply",
"agentFileExtension": ".agent.md",
"skipSharedHooksInstall": true,
"noPathRewrite": true
}
}
},
"cursor": {
"id": "cursor",
"role": "runtime",
"version": "1.10.0",
"title": "Cursor",
"description": "Cursor IDE — skills-only workflow surface; 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"
]
},
"localConfigDir": ".cursor",
"configFormat": "none",
"artifactLayout": {
"global": [
{
"kind": "skills",
"destSubpath": "skills",
"prefix": "gsd-",
"nesting": "flat",
"recursive": true,
"converter": "convertClaudeCommandToCursorSkill"
},
{
"kind": "agents",
"destSubpath": "agents",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": "convertClaudeAgentToCursorAgent"
}
],
"local": [
{
"kind": "skills",
"destSubpath": "skills",
"prefix": "gsd-",
"nesting": "flat",
"recursive": true,
"converter": "convertClaudeCommandToCursorSkill"
},
{
"kind": "agents",
"destSubpath": "agents",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": "convertClaudeAgentToCursorAgent"
}
]
},
"commandStyle": "slash-hyphen",
"hooksSurface": "cursor-hooks-json",
"hookEvents": "claude",
"sandboxTier": "none",
"supportTier": 2,
"installSurface": "cursor-hooks-json",
"writesSharedSettings": false,
"permissionWriter": null,
"extendedHookEvents": [],
"hostIntegration": {
"embeddingMode": "imperative",
"commandSurface": "slash-file",
"dispatch": {
"namedDispatch": true,
"nested": true,
"maxDepth": 2,
"background": true,
"subagentToolkit": "full",
"backgroundDispatch": true,
"isolation": "harness-worktree"
},
"modelMode": "passive",
"hookBus": "host",
"stateIO": "filesystem",
"transport": "mcp",
"runtime": "node",
"effortSurface": "undocumented"
},
"harnessIsolationFlag": "--worktree",
"hostBehaviors": {
"reapplyCommand": "gsd-update --reapply (mention the skill name)",
"frontmatterDialect": "cursor",
"hooksJsonSurface": true,
"skipSharedHooksInstall": true,
"retiredArtifacts": [
{
"destSubpath": "commands",
"prefix": "gsd-",
"suffix": ".md"
}
],
"skipUpdateBannerCommand": true,
"skipSettingsUi": true,
"managedHookEvents": [
"sessionStart",
"postToolUse",
"preToolUse",
"stop",
"subagentStart",
"subagentStop"
]
}
},
"reviewer": {
"slug": "cursor",
"flags": [
"--cursor"
],
"transport": "spawn",
"probe": {
"kind": "command-exists",
"binary": "cursor-agent"
},
"invoke": {
"binary": "cursor-agent",
"args": [
"-p",
"--mode",
"ask",
"--trust",
"--output-format",
"text",
"{{prompt}}"
],
"promptChannel": "argv-file-ref",
"outputChannel": "stdout",
"modelArg": null,
"effortChannel": "none"
},
"timeoutFloorMs": 900000,
"emptyOutput": "stub-with-stderr",
"reviewsSection": "Cursor",
"evidenceClass": "source-grounded",
"requiresBinaries": [],
"promptBudgetKey": null,
"modelConfigKey": null,
"handler": null
}
},
"drift": {
"id": "drift",
"role": "feature",
"version": "1.10.0",
"title": "Drift detection gates",
"description": "Drift detection gates for the planning loop. 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). At plan:pre: a non-blocking, warn-only codebase drift gate (gated on workflow.plan_drift_precheck) that flags a stale codebase map before planning, so plans are authored against a fresh STRUCTURE.md instead of discovering drift mid-execution.",
"tier": "full",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtimeCompat": {
"supported": [
"*"
],
"unsupported": []
},
"skills": [],
"agents": [],
"hooks": [],
"config": {
"workflow.drift_threshold": {
"type": "number",
"default": 3,
"description": "Minimum number of new structural elements (directories, barrel exports, migrations, routes) before the codebase drift gate triggers a warn or auto-remap action."
},
"workflow.drift_action": {
"type": "enum",
"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)."
},
"workflow.schema_drift_gate": {
"type": "boolean",
"default": true,
"description": "Enable the drift gates at execute:wave:post. When enabled, the schema drift gate blocks verification if schema-relevant files changed during execution but no database push command was executed; the codebase drift gate (non-blocking) warns when structural additions exceed the drift_threshold."
},
"workflow.plan_drift_precheck": {
"type": "boolean",
"default": true,
"description": "Enable the non-blocking codebase drift pre-check at plan:pre, before /gsd:plan-phase spawns the planner. When enabled, a stale STRUCTURE.md (structural additions exceeding drift_threshold) is surfaced up front as a warn-only advisory pointing to /gsd:map-codebase; it never blocks planning and never spawns the mapper agent. Separate from schema_drift_gate so autonomous/CI runs can silence the plan-time advisory while keeping the execute:wave:post gates enabled."
}
},
"steps": [],
"contributions": [],
"gates": [
{
"point": "execute:wave:post",
"check": {
"query": "verify.schema-drift"
},
"when": "workflow.schema_drift_gate",
"blocking": true,
"onError": "skip"
},
{
"point": "execute:wave:post",
"check": {
"query": "verify.codebase-drift"
},
"when": "workflow.schema_drift_gate",
"blocking": false,
"onError": "skip"
},
{
"point": "plan:pre",
"check": {
"query": "verify.codebase-drift"
},
"when": "workflow.plan_drift_precheck",
"blocking": false,
"onError": "skip"
}
]
},
"external-job": {
"id": "external-job",
"role": "feature",
"version": "1.10.0",
"title": "Async external-job scheduler adapter",
"description": "Default-off producer of the async external-job manifest (#1164). At execute:wave:post an executor can externalize long-running compute (SLURM first, scheduler-pluggable), commit a .planning/async-jobs/<job>.json manifest, defer SUMMARY.md, and return external_job_waiting. The core loop (#1165) consumes the manifest; this capability is the only thing that writes it. NOTE on contribution point: #1164 specifies execute:wave:pre, but execute-phase.md only dispatches execute:wave:post today (wave:pre is declared in the loop host contract but not rendered); wiring wave:pre dispatch is a core-loop change #1164 explicitly puts out of scope, so this capability registers at wave:post and the executor honors the runtime_budget classification guidance before running any tagged task. The adapter (scripts/slurm-adapter.cjs) reads external_job.submit_timeout_ms / poll_timeout_ms / artifact_dir through the canonical capability-config seam (env override > config > registry default).",
"tier": "full",
"requires": [],
"engines": {
"gsd": ">=1.7.0"
},
"runtimeCompat": {
"supported": [
"*"
],
"unsupported": []
},
"skills": [],
"agents": [],
"hooks": [],
"config": {
"external_job.enabled": {
"type": "boolean",
"default": false,
"description": "Master toggle for the async external-job producer capability. Default-off: the core loop consumes manifests whether or not this is on, but no manifest is ever written unless an executor opts in here."
},
"external_job.backend": {
"type": "enum",
"values": [
"slurm"
],
"default": "slurm",
"description": "Scheduler backend. SLURM is the first adapter; the field is the pluggability seam for future backends (LSF, PBS, Kubernetes batch). Core never interprets this value."
},
"external_job.artifact_dir": {
"type": "string",
"default": "Artifacts/jobs",
"description": "Root for per-job artifact directories (e.g. Artifacts/jobs/<jobid>/). Avoids fixed log paths and hardcoding a cluster/project layout. Surfaced by the adapter (`slurm-adapter.cjs submit` prints the resolved value); override via GSD_EXTERNAL_JOB_ARTIFACT_DIR."
},
"external_job.submit_timeout_ms": {
"type": "number",
"default": 30000,
"description": "Hard timeout (ms) for the scheduler submit subprocess (e.g. sbatch). Bounded per CLAUDE.md unbounded-subprocess policy. Read by the adapter (env GSD_SLURM_SUBMIT_TIMEOUT_MS overrides)."
},
"external_job.poll_timeout_ms": {
"type": "number",
"default": 15000,
"description": "Hard timeout (ms) for the scheduler poll subprocess (squeue, with sacct fallback). Read by the adapter (env GSD_SLURM_POLL_TIMEOUT_MS overrides)."
}
},
"steps": [],
"contributions": [
{
"point": "execute:wave:post",
"into": "executor",
"fragment": {
"path": "fragments/execute-wave-post.md",
"inline": "<!-- external-job capability — execute:wave:post fragment, injected into the executor (#1164).\n\n Why wave:post, not wave:pre (#1164 refinement A): execute-phase.md only\n dispatches execute:wave:post today — wave:pre is declared in the loop host\n contract but not rendered. Wiring wave:pre dispatch is a core-loop change\n #1164 puts out of scope. The executor therefore honors this classification\n guidance BEFORE running any task tagged <runtime_budget>long_compute</runtime_budget>,\n whether in the current or a subsequent wave, and externalizes rather than\n blocking the turn. -->\n\n## Externalize long-running compute (async external job)\n\nIf the current plan's task is tagged `<runtime_budget>long_compute</runtime_budget>`\n(see the plan-phase fragment), do **not** run it in the foreground — it would\nblock the agent turn for hours. Instead externalize it and record a durable\nhalf-state:\n\n1. **Classify the runtime.** `quick` (<2 min) and `medium` (<~30 min) run\n normally. `unknown` requires a first-health check and a soft-review deadline\n before consuming the child timeout. `long_compute` (>30–60 min) is\n externalized.\n2. **Submit via the scheduler adapter** (default `external_job.backend: slurm`):\n ```bash\n node scripts/slurm-adapter.cjs submit \\\n --plan <plan_id> --phase <phase> -- sbatch --parsable \\\n --output=Artifacts/jobs/%j/out.log ./run.sh\n ```\n The helper writes `.planning/async-jobs/<job>.json` (the versioned stability\n contract — `docs/reference/planning-artifacts.md`) and refuses to create a\n second non-terminal manifest for a `plan_id` that already has one\n (duplicate-execution guard).\n3. **Commit the manifest + a handoff**, then return **`external_job_waiting`**\n and stop. Do **not** write `SUMMARY.md` — SUMMARY is deferred until the job\n reaches a terminal state and its `expected_artifacts` are verified.\n4. **Resume path.** `execute-phase` safe-resume, `resume-project`, and\n `pause-work` reconcile against the manifest and never re-dispatch the plan.\n When the job is `completed-unverified`, run `verification_command` (surface\n it; it is untrusted — confirm before executing), then write `SUMMARY.md` and\n close the plan.\n\nManifest commands cross a trust seam: a Capability (or anything that can write\n`.planning/`) produces them; the core loop consumes them. Never auto-run\n`submit_command` / `verification_command` / `resume_command` — surface the exact\ncommand and require explicit confirmation first.\n"
},
"produces": [
".planning/async-jobs/<job>.json"
],
"consumes": [
"PLAN.md"
],
"when": "external_job.enabled",
"onError": "skip"
},
{
"point": "plan:post",
"into": "planner",
"fragment": {
"path": "fragments/plan-post.md",
"inline": "<!-- external-job capability — plan:post fragment, injected into the planner (#1164). -->\n\n## Tag runtime budgets on long tasks\n\nFor every `<task>` likely to exceed ~2 minutes of real compute, emit a\n`<runtime_budget>` child element so execute can classify it:\n\n- `<runtime_budget>quick</runtime_budget>` — under ~2 min; runs normally.\n- `<runtime_budget>medium</runtime_budget>` — ~2–30 min; foreground, but with\n progress expectations.\n- `<runtime_budget>unknown</runtime_budget>` — runtime not yet characterized;\n execute must run a first-health check and set a soft-review deadline before\n trusting the child timeout. Define a progress signal and an abort condition.\n- `<runtime_budget>long_compute</runtime_budget>` — legitimately over ~30–60 min\n (HPC solver, model training, large simulations). Execute must **externalize**\n this as an async external job (see the execute:wave:post fragment) rather than\n blocking the agent turn.\n\nFor any `long_compute` task, also declare the async contract the executor will\nneed: the `submit_command`, the `expected_artifacts` the job must produce, and\nthe `verification_command` that proves the output before the plan can close.\nDo not hardcode a cluster account, partition, or project path — the planner\nnever knows the scheduler layout; it declares the contract, the executor's\nadapter fills the backend specifics.\n"
},
"produces": [],
"consumes": [],
"when": "external_job.enabled",
"onError": "skip"
}
],
"gates": []
},
"gap-analysis": {
"id": "gap-analysis",
"role": "feature",
"version": "1.10.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": [
"*"
],
"unsupported": []
},
"skills": [],
"agents": [],
"hooks": [],
"config": {
"workflow.post_planning_gaps": {
"type": "boolean",
"default": true,
"description": "Run the post-planning gap analysis report after plans are generated."
}
},
"steps": [],
"contributions": [],
"gates": [
{
"point": "plan:post",
"check": {
"query": "gap-analysis.plan-post"
},
"when": "workflow.post_planning_gaps",
"blocking": false,
"onError": "skip"
}
]
},
"gemini": {
"id": "gemini",
"role": "reviewer",
"version": "1.10.0",
"title": "Gemini CLI",
"description": "Google Gemini CLI — cross-AI /gsd:review reviewer lane only; not a GSD install target (no runtime body, no artifacts). Spawned as `gemini -p - -m <model>` with the plan piped on stdin.",
"tier": "full",
"requires": [],
"engines": {
"gsd": ">=1.8.0"
},
"reviewer": {
"slug": "gemini",
"flags": [
"--gemini"
],
"transport": "spawn",
"probe": {
"kind": "command-exists",
"binary": "gemini"
},
"invoke": {
"binary": "gemini",
"args": [
"{{model}}",
"-p",
"-"
],
"promptChannel": "stdin",
"outputChannel": "stdout",
"modelArg": "-m",
"effortChannel": "none"
},
"timeoutFloorMs": 900000,
"emptyOutput": "stub-with-stderr",
"reviewsSection": "Gemini",
"evidenceClass": "source-grounded",
"requiresBinaries": [],
"promptBudgetKey": null,
"modelConfigKey": "review.models.gemini",
"handler": null
},
"config": {
"review.models.gemini": {
"type": "string",
"default": "",
"description": "Model passed to the Gemini reviewer lane."
}
}
},
"graphify": {
"id": "graphify",
"role": "feature",
"version": "1.10.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": [
"*"
],
"unsupported": []
},
"skills": [
"graphify"
],
"agents": [],
"activationKey": "graphify.enabled",
"config": {
"graphify.enabled": {
"type": "boolean",
"default": false,
"description": "Enable the graphify knowledge-graph command + skill."
}
},
"commands": [
{
"family": "graphify",
"module": "graphify-command-router.cjs",
"router": "routeGraphifyCommand"
}
],
"hooks": [],
"steps": [],
"contributions": [],
"gates": []
},
"hermes": {
"id": "hermes",
"role": "runtime",
"version": "1.10.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"
]
},
"localConfigDir": ".hermes",
"configFormat": "settings-json",
"artifactLayout": {
"global": [
{
"kind": "skills",
"destSubpath": "skills/gsd",
"prefix": "gsd-",
"nesting": "nested",
"recursive": false,
"converter": "convertClaudeCommandToClaudeSkill"
}
],
"local": [
{
"kind": "skills",
"destSubpath": "skills/gsd",
"prefix": "gsd-",
"nesting": "nested",
"recursive": false,
"converter": "convertClaudeCommandToClaudeSkill"
}
]
},
"commandStyle": "slash-hyphen",
"hooksSurface": "settings-json",
"hookEvents": "claude",
"sandboxTier": "none",
"supportTier": 2,
"installSurface": "settings-json",
"writesSharedSettings": true,
"permissionWriter": null,
"extendedHookEvents": [],
"extensionEvents": "hermes",
"hostBehaviors": {
"skillFrontmatterVersion": true,
"skillsManifestPrefix": "skills/gsd/",
"trackCategoryDescription": true,
"writeCategoryDescription": true,
"reportSkillsCount": true,
"legacyCommandsGsdCleanup": true,
"brandingRewrites": {
"CLAUDE.md": "HERMES.md",
"Claude Code": "Hermes Agent",
".claude/": ".hermes/"
},
"reapplyCommand": "gsd-update --reapply (mention the skill name)",
"legacyCommandsGsdInstallMigration": true,
"legacyCommandsGsdUninstall": true,
"hyphenNameAgentBody": true
},
"hostIntegration": {
"embeddingMode": "imperative",
"commandSurface": "slash-programmatic",
"dispatch": {
"namedDispatch": false,
"nested": true,
"maxDepth": 1,
"background": true,
"subagentToolkit": "read-only",
"backgroundDispatch": false,
"isolation": "undocumented"
},
"modelMode": "active",
"hookBus": "host",
"stateIO": "filesystem",
"transport": "mcp",
"runtime": "python",
"effortSurface": "undocumented"
}
}
},
"intel": {
"id": "intel",
"role": "feature",
"version": "1.10.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": [
"*"
],
"unsupported": []
},
"skills": [],
"agents": [],
"activationKey": "intel.enabled",
"config": {
"intel.enabled": {
"type": "boolean",
"default": false,
"description": "Enable the intel code-intelligence command."
}
},
"commands": [
{
"family": "intel",
"module": "intel-command-router.cjs",
"router": "routeIntelCommand"
}
],
"hooks": [],
"steps": [
{
"point": "plan:pre",
"ref": {
"command": "intel api-surface"
},
"produces": [
".planning/intel/API-SURFACE.md"
],
"consumes": [],
"when": "intel.enabled",
"onError": "skip"
}
],
"contributions": [],
"gates": []
},
"kilo": {
"id": "kilo",
"role": "runtime",
"version": "1.10.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"
],
"skillsHome": {
"kind": "dot-home",
"name": ".kilo",
"env": []
}
},
"localConfigDir": ".kilo",
"configFormat": "settings-json",
"artifactLayout": {
"global": [
{
"kind": "commands",
"destSubpath": "command",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": null
},
{
"kind": "skills",
"destSubpath": "skills",
"prefix": "gsd-",
"nesting": "flat",
"recursive": true,
"converter": "convertClaudeCommandToKiloSkill"
}
],
"local": [
{
"kind": "commands",
"destSubpath": "command",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": null
},
{
"kind": "skills",
"destSubpath": "skills",
"prefix": "gsd-",
"nesting": "flat",
"recursive": true,
"converter": "convertClaudeCommandToKiloSkill"
}
]
},
"commandStyle": "slash-hyphen",
"hooksSurface": "none",
"extensionEvents": "kilo",
"sandboxTier": "none",
"supportTier": 2,
"installSurface": "settings-json",
"writesSharedSettings": false,
"permissionWriter": "kilo",
"extendedHookEvents": [],
"hostIntegration": {
"embeddingMode": "imperative",
"commandSurface": "slash-file",
"dispatch": {
"namedDispatch": true,
"nested": true,
"maxDepth": -1,
"background": true,
"subagentToolkit": "undocumented",
"backgroundDispatch": false,
"isolation": "undocumented"
},
"modelMode": "active",
"hookBus": "host",
"stateIO": "filesystem",
"transport": "mcp",
"runtime": "bun",
"effortSurface": "undocumented"
},
"hostBehaviors": {
"reapplyCommand": "/gsd-update --reapply",
"attributionConfigResolver": "kilo",
"flatCommandDir": "command",
"combinedFamilyInstall": true,
"frontmatterDialect": "kilo",
"nativePlugin": {
"dir": "plugins",
"file": "gsd-core.js",
"source": ".kilo/plugins/gsd-core.js"
},
"skipUpdateBannerCommand": true
}
}
},
"kimi": {
"id": "kimi",
"role": "runtime",
"version": "1.10.0",
"title": "Kimi CLI",
"description": "Kimi CLI (Moonshot AI) — generic agents root at ~/.config/agents; skills + kimi-agents artifact layout; native config.toml [[hooks]] bus at ~/.kimi/config.toml; background dispatch; 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"
],
"probeExists": "skills"
},
"localConfigDir": ".kimi-code",
"configFormat": "none",
"artifactLayout": {
"global": [
{
"kind": "skills",
"destSubpath": "skills",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": "convertClaudeCommandToKimiSkill"
},
{
"kind": "kimi-agents",
"destSubpath": "agents",
"prefix": "gsd",
"nesting": "flat",
"recursive": false,
"converter": null
}
],
"local": []
},
"commandStyle": "slash-hyphen",
"hooksSurface": "kimi-hooks-toml",
"hookEvents": "claude",
"sandboxTier": "none",
"supportTier": 2,
"installSurface": "profile-marker-only",
"writesSharedSettings": false,
"permissionWriter": null,
"extendedHookEvents": [
"SubagentStop",
"Stop",
"PreCompact",
"SubagentStart"
],
"hostIntegration": {
"embeddingMode": "imperative",
"commandSurface": "slash-file",
"dispatch": {
"namedDispatch": true,
"nested": false,
"maxDepth": 1,
"background": true,
"subagentToolkit": "undocumented",
"backgroundDispatch": true,
"isolation": "orchestrator-worktree"
},
"modelMode": "passive",
"hookBus": "host",
"stateIO": "filesystem",
"transport": "mcp",
"runtime": "python",
"effortSurface": "undocumented"
},
"orchestratorExec": {
"command": "kimi",
"args": [
"--print"
],
"cwdFlag": "--work-dir",
"promptFlag": "--prompt"
},
"hostBehaviors": {
"reapplyCommand": "/skill:gsd-update --reapply",
"localInstallDeferred": true,
"verificationStyle": "kimi",
"agentManifestStyle": "kimi-nested",
"doneBannerStyle": "kimi-agent-file",
"skipSharedHooksInstall": true
}
}
},
"kimi-code": {
"id": "kimi-code",
"role": "runtime",
"version": "1.10.0",
"title": "Kimi Code CLI",
"description": "Kimi Code CLI (Moonshot AI, Node) — Agent Skills auto-discovered at ~/.kimi-code/skills; global AGENTS.md at ~/.kimi-code/AGENTS.md; native config.toml + [[hooks]] bus; three built-in subagents (coder/explore/plan), NO custom named subagents; background dispatch; tier-2 support. Distinct from Python kimi-cli (the 'kimi' capability) per ADR-1239 EoS — Kimi Code cannot dispatch named subagents so the kimi-agents YAML layout does NOT apply; persona injection rides the existing ${AGENT_SKILLS_*} workflow fallback. Install-layout, agent-install-check, and install-time decision (kimi vs kimi-code) land in follow-up PRs; this descriptor is the EoS foundation.",
"tier": "core",
"requires": [],
"engines": {
"gsd": ">=1.7.0"
},
"runtime": {
"configHome": {
"kind": "dot-home",
"name": ".kimi-code",
"env": [
"KIMI_CODE_HOME"
],
"probe": [
"~/.kimi-code"
],
"probeExists": "config.toml"
},
"localConfigDir": ".kimi-code",
"configFormat": "none",
"artifactLayout": {
"global": [
{
"kind": "skills",
"destSubpath": "skills",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": "convertClaudeCommandToKimiCodeSkill"
}
],
"local": []
},
"commandStyle": "slash-hyphen",
"hooksSurface": "kimi-hooks-toml",
"hookEvents": "claude",
"sandboxTier": "none",
"supportTier": 2,
"installSurface": "profile-marker-only",
"writesSharedSettings": false,
"permissionWriter": null,
"extendedHookEvents": [
"SubagentStop",
"Stop",
"PreCompact",
"SubagentStart"
],
"hostIntegration": {
"embeddingMode": "declarative",
"commandSurface": "slash-file",
"dispatch": {
"namedDispatch": false,
"nested": true,
"maxDepth": "undocumented",
"background": true,
"subagentToolkit": "built-in-only",
"backgroundDispatch": true,
"builtInSubagents": [
"coder",
"explore",
"plan"
],
"isolation": "orchestrator-worktree"
},
"modelMode": "passive",
"hookBus": "host",
"stateIO": "filesystem",
"transport": "mcp",
"runtime": "node"
},
"orchestratorExec": {
"command": "kimi",
"args": [],
"cwdFlag": null,
"promptFlag": "--prompt"
},
"hostBehaviors": {
"reapplyCommand": "/skill:gsd-update --reapply",
"localInstallDeferred": true,
"verificationStyle": "kimi-code",
"agentManifestStyle": "none",
"doneBannerStyle": "kimi-code",
"skipSharedHooksInstall": true,
"namedSubagentsSupported": false
}
},
"reviewer": {
"slug": "kimi-code",
"flags": [
"--kimi-code"
],
"transport": "spawn",
"probe": {
"kind": "command-capability",
"binary": "kimi",
"needle": "--output-format",
"timeoutMs": 5000
},
"invoke": {
"binary": "kimi",
"args": [
"{{model}}",
"-p",
"{{prompt}}"
],
"promptChannel": "argv-file-ref",
"outputChannel": "stdout",
"modelArg": "-m",
"effortChannel": "none"
},
"timeoutFloorMs": 900000,
"emptyOutput": "stub-with-stderr",
"reviewsSection": "Kimi Code",
"evidenceClass": "source-grounded",
"requiresBinaries": [],
"promptBudgetKey": null,
"modelConfigKey": "review.models.kimi-code",
"handler": null
},
"config": {
"review.models.kimi-code": {
"type": "string",
"default": "",
"description": "Model passed to the Kimi Code reviewer lane."
}
}
},
"llama-cpp": {
"id": "llama-cpp",
"role": "reviewer",
"version": "1.10.0",
"title": "llama.cpp",
"description": "llama.cpp server — cross-AI /gsd:review reviewer lane only; not a GSD install target (no runtime body, no artifacts). OpenAI-compatible HTTP transport against a user-configured `review.llama_cpp_host` (POST /v1/chat/completions); model discovered via GET /v1/models piped through jq. Capability id/folder are kebab (`llama-cpp`, required by KEBAB_RE); `reviewer.slug` stays snake (`llama_cpp`) to match the shipped roster and the `review.llama_cpp_host` config key (ADR-2782's three-namespace trap).",
"tier": "full",
"requires": [],
"engines": {
"gsd": ">=1.8.0"
},
"reviewer": {
"slug": "llama_cpp",
"flags": [
"--llama-cpp"
],
"transport": "openai-http",
"probe": {
"kind": "http-reachable",
"hostConfigKey": "review.llama_cpp_host",
"path": "/v1/models",
"timeoutMs": 2000
},
"invoke": {
"hostConfigKey": "review.llama_cpp_host",
"defaultHost": "http://localhost:8080",
"path": "/v1/chat/completions",
"modelDiscovery": "first-from-models-endpoint",
"fallbackModel": "local-model",
"effortChannel": "none"
},
"timeoutFloorMs": 120000,
"emptyOutput": "stub-with-stderr",
"reviewsSection": "llama.cpp",
"evidenceClass": "source-grounded",
"requiresBinaries": [],
"promptBudgetKey": "review.max_prompt_tokens_per_reviewer.llama_cpp",
"modelConfigKey": "review.models.llama_cpp",
"handler": "openai-compatible"
},
"config": {
"review.models.llama_cpp": {
"type": "string",
"default": "",
"description": "Model requested from the llama.cpp reviewer lane; empty discovers the first model from /v1/models."
},
"review.llama_cpp_host": {
"type": "string",
"default": "",
"description": "Base URL of the llama.cpp OpenAI-compatible server."
},
"review.max_prompt_tokens_per_reviewer.llama_cpp": {
"type": "number",
"default": -1,
"description": "Prompt-token budget for the llama.cpp reviewer lane. Unset is -1, a sentinel: 0 is a legitimate value meaning \"do not trim this lane\", so it cannot double as \"not configured\"."
}
}
},
"lm-studio": {
"id": "lm-studio",
"role": "reviewer",
"version": "1.10.0",
"title": "LM Studio",
"description": "LM Studio local model server — cross-AI /gsd:review reviewer lane only; not a GSD install target (no runtime body, no artifacts). OpenAI-compatible HTTP transport against a user-configured `review.lm_studio_host` (POST /v1/chat/completions); model discovered via GET /v1/models piped through jq. Capability id/folder are kebab (`lm-studio`, required by KEBAB_RE); `reviewer.slug` stays snake (`lm_studio`) to match the shipped roster and the `review.lm_studio_host` config key (ADR-2782's three-namespace trap).",
"tier": "full",
"requires": [],
"engines": {
"gsd": ">=1.8.0"
},
"reviewer": {
"slug": "lm_studio",
"flags": [
"--lm-studio"
],
"transport": "openai-http",
"probe": {
"kind": "http-reachable",
"hostConfigKey": "review.lm_studio_host",
"path": "/v1/models",
"timeoutMs": 2000
},
"invoke": {
"hostConfigKey": "review.lm_studio_host",
"defaultHost": "http://localhost:1234",
"path": "/v1/chat/completions",
"modelDiscovery": "first-from-models-endpoint",
"fallbackModel": "local-model",
"effortChannel": "none"
},
"timeoutFloorMs": 120000,
"emptyOutput": "stub-with-stderr",
"reviewsSection": "LM Studio",
"evidenceClass": "source-grounded",
"requiresBinaries": [],
"promptBudgetKey": "review.max_prompt_tokens_per_reviewer.lm_studio",
"modelConfigKey": "review.models.lm_studio",
"handler": "openai-compatible"
},
"config": {
"review.models.lm_studio": {
"type": "string",
"default": "",
"description": "Model requested from the LM Studio reviewer lane; empty discovers the first model from /v1/models."
},
"review.lm_studio_host": {
"type": "string",
"default": "",
"description": "Base URL of the LM Studio OpenAI-compatible server."
},
"review.max_prompt_tokens_per_reviewer.lm_studio": {
"type": "number",
"default": -1,
"description": "Prompt-token budget for the LM Studio reviewer lane. Unset is -1, a sentinel: 0 is a legitimate value meaning \"do not trim this lane\", so it cannot double as \"not configured\"."
}
}
},
"mempalace": {
"id": "mempalace",
"role": "feature",
"version": "1.10.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": [
"*"
],
"unsupported": []
},
"skills": [
"mempalace-recall",
"mempalace-capture"
],
"agents": [
"gsd-mempalace-curator"
],
"hooks": [],
"config": {
"mempalace.enabled": {
"type": "boolean",
"default": false,
"description": "Master toggle for the MemPalace memory capability."
},
"mempalace.memory_mode": {
"type": "enum",
"values": [
"augment",
"kg_backend",
"replace"
],
"default": "augment",
"description": "How MemPalace relates to GSD native memory during recall/capture. 'augment' (default): the palace is an additive layer alongside .planning/graphs/ — native memory stays authoritative. 'kg_backend': knowledge-graph queries resolve against the palace's temporal KG as the primary source, with .planning/graphs/ as fallback. 'replace': recall resolves through the palace as the source of truth, native memory as fallback. Every mode stays onError:skip and default-resilient — an unreachable palace degrades to native memory, and GSD keeps writing .planning/graphs/ so no memory is lost. Cross-mode migration of existing .planning/graphs/ into the palace is a separate, not-yet-implemented concern."
},
"mempalace.wing": {
"type": "string",
"default": "",
"description": "Palace wing name; empty derives from project_code / project dir."
},
"mempalace.recall_on_discuss": {
"type": "boolean",
"default": true,
"description": "Inject wake-up + search recall at discuss:pre."
},
"mempalace.recall_on_plan": {
"type": "boolean",
"default": true,
"description": "Produce MEMORY-RECALL.md at plan:pre."
},
"mempalace.capture_artifacts": {
"type": "boolean",
"default": true,
"description": "File CONTEXT/PLAN/SUMMARY and learnings into the palace at phase boundaries."
},
"mempalace.mirror_kg": {
"type": "boolean",
"default": true,
"description": "Mirror decisions/learnings into MemPalace's temporal knowledge graph."
},
"mempalace.cross_project_tunnels": {
"type": "boolean",
"default": false,
"description": "Propose/create cross-wing tunnels at ship:post."
},
"mempalace.diary_journal": {
"type": "boolean",
"default": true,
"description": "Write a per-agent diary entry at ship:post."
},
"mempalace.auto_capture_hooks": {
"type": "boolean",
"default": false,
"description": "Reserved / not yet implemented: will install MemPalace's native stop/precompact Claude Code hooks for passive mid-session capture (the capability's hooks array is currently empty)."
}
},
"steps": [
{
"point": "discuss:post",
"ref": {
"skill": "mempalace-capture"
},
"produces": [],
"consumes": [
"CONTEXT.md"
],
"when": "mempalace.enabled",
"onError": "skip"
},
{
"point": "plan:pre",
"ref": {
"skill": "mempalace-recall"
},
"produces": [
"MEMORY-RECALL.md"
],
"consumes": [
"CONTEXT.md"
],
"when": "mempalace.enabled",
"onError": "skip"
},
{
"point": "plan:post",
"ref": {
"skill": "mempalace-capture"
},
"produces": [],
"consumes": [
"PLAN.md"
],
"when": "mempalace.enabled",
"onError": "skip"
},
{
"point": "verify:post",
"ref": {
"skill": "mempalace-capture"
},
"produces": [],
"consumes": [
"SUMMARY.md"
],
"when": "mempalace.enabled",
"onError": "skip"
},
{
"point": "ship:post",
"ref": {
"agent": "gsd-mempalace-curator"
},
"produces": [],
"consumes": [
"UAT.md"
],
"when": "mempalace.enabled",
"onError": "skip"
}
],
"contributions": [
{
"point": "discuss:pre",
"into": "orchestrator",
"fragment": {
"path": "fragments/recall-discuss.md",
"inline": "<!--\n MemPalace capability — contribution fragment.\n Rendered into the discuss:pre orchestrator prompt when `mempalace.recall_on_discuss` is true.\n Contributes DATA (recall instructions), not control flow. onError: skip — never blocks discussion.\n-->\n### Memory recall (MemPalace)\n\n**Gate first.** Read `.planning/config.json`. If `mempalace.enabled` is not `true`, or `mempalace.recall_on_discuss` is `false`, **skip this entire section** and continue the discussion unchanged. (This contribution is only injected when the capability is enabled; the `recall_on_discuss` check lets you turn discuss-time recall off without disabling the rest of the capability.)\n\nOtherwise — before gathering new context, surface what you already know. This is read-only and side-effect-free; if MemPalace is unreachable, note \"memory unavailable\" and continue — recall never blocks discussion.\n\n1. **Resolve the wing.** Use `mempalace.wing` if set; otherwise derive it from `project_code` (fall back to the project directory name).\n2. **Wake up (cheap, ~600–900 tokens).**\n - Interactive run → call `mempalace_search` after a wake-up read of the wing.\n - Headless/cron run (no MCP server) → run `mempalace wake-up --wing <wing>` via the CLI.\n3. **Targeted recall.** Search the palace for prior work on this phase's topic:\n - Interactive → `mempalace_search(query=<phase topic>, wing=<wing>)` and, when `mempalace.mirror_kg` is on, `mempalace_kg_query` / `mempalace_kg_timeline` for decision facts and their validity windows.\n - Headless → `mempalace search \"<phase topic>\" --wing <wing>`.\n4. **Mode awareness** (`mempalace.memory_mode`). `augment` (default): the palace is an *additional* layer — never skip `.planning/graphs/` or STATE. `kg_backend`: query the palace's temporal KG as the *primary* graph source, with `.planning/graphs/` as fallback. `replace`: resolve recall *through the palace as the source of truth*, native memory as fallback. In every mode an unreachable palace falls back to native memory — recall never blocks.\n5. **Surface, don't dump.** Fold the top relevant drawers, decisions, patterns, and *surprises* into the discussion as prior context — cite drawer/fact provenance. Do not paste raw search output.\n\nIf any MemPalace call errors or times out, skip the rest of recall and proceed with discussion as normal.\n"
},
"produces": [],
"consumes": [],
"when": "mempalace.enabled",
"onError": "skip"
},
{
"point": "execute:wave:post",
"into": "verifier",
"fragment": {
"path": "fragments/capture-problems.md",
"inline": "<!--\n MemPalace capability — contribution fragment.\n Rendered into the execute:wave:post verifier prompt when `mempalace.capture_artifacts` is true.\n Contributes DATA (capture instructions), not control flow. onError: skip — never fails a wave.\n-->\n### Capture problems → fixes (MemPalace)\n\n**Gate first.** Read `.planning/config.json`. If `mempalace.enabled` is not `true`, or `mempalace.capture_artifacts` is `false`, **skip this entire section** and let the wave complete unchanged. (This contribution is only injected when the capability is enabled; the `capture_artifacts` check lets you turn capture off without disabling the rest of the capability.)\n\nOtherwise — after verifying this wave, persist any *confirmed* problem→fix pairs into the palace so they are recalled in future phases. This is best-effort; if MemPalace is unreachable, skip silently — capture never fails a wave.\n\nFor each confirmed bug/issue resolved in this wave:\n\n1. **Resolve the wing** (`mempalace.wing`, else `project_code`, else project dir) and target `room: problems`.\n2. **Dedupe first.** Call `mempalace_check_duplicate` (interactive) before filing so re-runs don't create duplicate drawers.\n3. **File the drawer verbatim.** Store the problem statement and its fix as a drawer in `room: problems` — interactive: `mempalace_add_drawer`; headless: stage the artifact under the `problems/` folder and run `mempalace mine` (no `--room` flag — see [CLI reference](https://mempalaceofficial.com/reference/cli.html); room assignment is via `detect_room()` folder-path match per the [mining guide](https://mempalaceofficial.com/guide/mining.html); use the same staging pattern documented in `gsd-mempalace-capture` Step 3). Include provenance (`source_file`, phase id).\n4. **Mirror the KG fact** when `mempalace.mirror_kg` is on: add `(<bug>, fixed_by, <fix>)` with `valid_from` = the phase date via `mempalace_kg_add`.\n5. **Mode awareness** (`mempalace.memory_mode`). Under `augment` the fact is an *additive* mirror alongside `.planning/graphs/`. Under `kg_backend`/`replace` the palace is the *authoritative* store for the fact; GSD still writes `.planning/graphs/` through its normal graphify, so an unreachable palace never loses it.\n\nCaptures are idempotent: deterministic drawer IDs + `check_duplicate` mean re-running the wave re-files the same content without duplication. On any error, skip and let the wave complete normally.\n"
},
"produces": [],
"consumes": [],
"when": "mempalace.enabled",
"onError": "skip"
}
],
"gates": []
},
"nyquist": {
"id": "nyquist",
"role": "feature",
"version": "1.10.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": [
"*"
],
"unsupported": []
},
"skills": [
"validate-phase"
],
"agents": [
"gsd-nyquist-auditor"
],
"hooks": [],
"config": {
"workflow.nyquist_validation": {
"type": "boolean",
"default": true,
"description": "Enable Nyquist validation coverage auditing."
}
},
"steps": [
{
"point": "verify:post",
"ref": {
"skill": "validate-phase"
},
"produces": [
"VALIDATION.md"
],
"consumes": [
"SUMMARY.md"
],
"when": "workflow.nyquist_validation",
"onError": "halt"
}
],
"contributions": [],
"gates": []
},
"ollama": {
"id": "ollama",
"role": "reviewer",
"version": "1.10.0",
"title": "Ollama",
"description": "Ollama local model server — cross-AI /gsd:review reviewer lane only; not a GSD install target (no runtime body, no artifacts). OpenAI-compatible HTTP transport against a user-configured `review.ollama_host` (POST /v1/chat/completions); model discovered via GET /v1/models piped through jq.",
"tier": "full",
"requires": [],
"engines": {
"gsd": ">=1.8.0"
},
"reviewer": {
"slug": "ollama",
"flags": [
"--ollama"
],
"transport": "openai-http",
"probe": {
"kind": "http-reachable",
"hostConfigKey": "review.ollama_host",
"path": "/v1/models",
"timeoutMs": 2000
},
"invoke": {
"hostConfigKey": "review.ollama_host",
"defaultHost": "http://localhost:11434",
"path": "/v1/chat/completions",
"modelDiscovery": "first-from-models-endpoint",
"fallbackModel": "llama3",
"effortChannel": "none"
},
"timeoutFloorMs": 120000,
"emptyOutput": "stub-with-stderr",
"reviewsSection": "Ollama",
"evidenceClass": "source-grounded",
"requiresBinaries": [],
"promptBudgetKey": "review.max_prompt_tokens_per_reviewer.ollama",
"modelConfigKey": "review.models.ollama",
"handler": "openai-compatible"
},
"config": {
"review.models.ollama": {
"type": "string",
"default": "",
"description": "Model requested from the Ollama reviewer lane; empty discovers the first model from /v1/models."
},
"review.ollama_host": {
"type": "string",
"default": "",
"description": "Base URL of the Ollama OpenAI-compatible server."
},
"review.max_prompt_tokens_per_reviewer.ollama": {
"type": "number",
"default": -1,
"description": "Prompt-token budget for the Ollama reviewer lane. Unset is -1, a sentinel: 0 is a legitimate value meaning \"do not trim this lane\", so it cannot double as \"not configured\"."
}
}
},
"opencode": {
"id": "opencode",
"role": "runtime",
"version": "1.10.0",
"title": "OpenCode",
"description": "OpenCode — XDG-based config dir; flat commands/ + 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"
]
},
"localConfigDir": ".opencode",
"configFormat": "settings-json",
"artifactLayout": {
"global": [
{
"kind": "commands",
"destSubpath": "commands",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": null
},
{
"kind": "skills",
"destSubpath": "skills",
"prefix": "gsd-",
"nesting": "flat",
"recursive": true,
"converter": "convertClaudeCommandToOpencodeSkill"
}
],
"local": [
{
"kind": "commands",
"destSubpath": "commands",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": null
},
{
"kind": "skills",
"destSubpath": "skills",
"prefix": "gsd-",
"nesting": "flat",
"recursive": true,
"converter": "convertClaudeCommandToOpencodeSkill"
}
]
},
"commandStyle": "slash-hyphen",
"hooksSurface": "none",
"extensionEvents": "opencode",
"sandboxTier": "none",
"supportTier": 2,
"installSurface": "settings-json",
"writesSharedSettings": true,
"permissionWriter": "opencode",
"extendedHookEvents": [],
"hostIntegration": {
"embeddingMode": "imperative",
"commandSurface": "slash-file",
"dispatch": {
"namedDispatch": true,
"nested": "undocumented",
"maxDepth": "undocumented",
"background": false,
"subagentToolkit": "full",
"backgroundDispatch": false,
"isolation": "orchestrator-worktree"
},
"modelMode": "active",
"hookBus": "host",
"stateIO": "filesystem",
"transport": "mcp",
"runtime": "bun",
"effortSurface": "argv"
},
"orchestratorExec": {
"command": "opencode",
"args": [
"run"
],
"cwdFlag": "--dir",
"promptFlag": null
},
"hostBehaviors": {
"reapplyCommand": "/gsd-update --reapply",
"attributionConfigResolver": "opencode",
"flatCommandDir": "commands",
"combinedFamilyInstall": true,
"frontmatterDialect": "opencode",
"nativePlugin": {
"dir": "plugins",
"file": "gsd-core.js",
"source": ".opencode/plugins/gsd-core.js"
},
"skipHomePrefixSubstitution": true,
"skipSettingsUi": true,
"skipUpdateBannerCommand": true,
"skipCodexSkillsManifest": true
}
},
"reviewer": {
"slug": "opencode",
"flags": [
"--opencode"
],
"transport": "spawn",
"probe": {
"kind": "command-exists",
"binary": "opencode"
},
"invoke": {
"binary": "opencode",
"args": [
"run",
"{{model}}",
"{{effort}}",
"--format",
"json",
"-"
],
"promptChannel": "stdin",
"outputChannel": "stdout",
"modelArg": "--model",
"effortChannel": "argv"
},
"timeoutFloorMs": 660000,
"emptyOutput": "stub-with-stderr",
"reviewsSection": "OpenCode",
"evidenceClass": "source-grounded",
"requiresBinaries": [],
"promptBudgetKey": null,
"modelConfigKey": "review.models.opencode",
"handler": "opencode"
},
"config": {
"review.models.opencode": {
"type": "string",
"default": "",
"description": "Model passed to the OpenCode reviewer lane."
}
}
},
"pattern-mapper": {
"id": "pattern-mapper",
"role": "feature",
"version": "1.10.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": [
"*"
],
"unsupported": []
},
"skills": [],
"agents": [
"gsd-pattern-mapper"
],
"hooks": [],
"config": {
"workflow.pattern_mapper": {
"type": "boolean",
"default": true,
"description": "Run the pattern mapper before planning when context or research is available."
}
},
"steps": [
{
"point": "plan:pre",
"ref": {
"agent": "gsd-pattern-mapper"
},
"fragment": {
"path": "fragments/plan-pre.md",
"inline": "<pattern_mapping_context>\n**Phase:** {phase_number} - {phase_name}\n**Phase directory:** {phase_dir}\n**Padded phase:** {padded_phase}\n\n<files_to_read>\n- {context_path} (USER DECISIONS from /gsd:discuss-phase)\n- {research_path} (Technical Research)\n</files_to_read>\n\n**Output file:** {phase_dir}/{padded_phase}-PATTERNS.md\n\nExtract the list of files to be created/modified from CONTEXT.md and RESEARCH.md. For each file, classify by role and data flow, find the closest existing analog in the codebase, extract concrete code excerpts, and produce PATTERNS.md.\n</pattern_mapping_context>\n"
},
"produces": [
"PATTERNS.md"
],
"consumes": [
"RESEARCH.md"
],
"when": "workflow.pattern_mapper",
"onError": "skip"
}
],
"contributions": [],
"gates": []
},
"pi": {
"id": "pi",
"role": "runtime",
"version": "1.10.0",
"title": "pi",
"description": "pi (pi.dev) — bun-runtime programmatic-CLI; TS ExtensionAPI (registerCommand/registerTool/registerProvider/pi.on); single native-extension file at ~/.pi/agent/extensions/gsd.js (.js, not .cjs — pi's extension auto-discovery accepts only .ts/.js, #2470); no shared-settings hook surface; tier-2 support.",
"tier": "core",
"requires": [],
"engines": {
"gsd": ">=1.7.0"
},
"runtime": {
"configHome": {
"kind": "dot-home-nested",
"name": "agent",
"parent": ".pi",
"env": [
"PI_CODING_AGENT_DIR"
]
},
"localConfigDir": ".pi",
"configFormat": "none",
"artifactLayout": {
"global": [],
"local": []
},
"commandStyle": "slash-hyphen",
"hooksSurface": "none",
"extensionEvents": "pi",
"sandboxTier": "none",
"supportTier": 2,
"installSurface": "profile-marker-only",
"writesSharedSettings": false,
"permissionWriter": null,
"extendedHookEvents": [],
"hostIntegration": {
"embeddingMode": "imperative",
"commandSurface": "slash-programmatic",
"dispatch": {
"namedDispatch": false,
"nested": false,
"maxDepth": 0,
"background": false,
"backgroundDispatch": false,
"subagentToolkit": "undocumented",
"isolation": "none"
},
"modelMode": "active",
"hookBus": "host",
"stateIO": "session-log-append",
"transport": "native-extension",
"runtime": "bun",
"effortSurface": "undocumented"
},
"hostBehaviors": {
"nativePlugin": {
"dir": "extensions",
"file": "gsd.js",
"source": "pi/gsd.cjs"
},
"pluginOnlyInstall": true,
"sharedHooksDirName": "gsd-hooks"
}
}
},
"profile-pipeline": {
"id": "profile-pipeline",
"role": "feature",
"version": "1.10.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": [
"*"
],
"unsupported": []
},
"skills": [
"profile-user"
],
"agents": [
"gsd-user-profiler"
],
"config": {
"profile-pipeline.enabled": {
"type": "boolean",
"default": false,
"description": "Enable the developer profiling pipeline commands (scan-sessions, extract-messages, profile-sample, write-profile, etc.)."
}
},
"commands": [
{
"family": "scan-sessions",
"module": "profile-pipeline-command-router.cjs",
"router": "routeScanSessions"
},
{
"family": "extract-messages",
"module": "profile-pipeline-command-router.cjs",
"router": "routeExtractMessages"
},
{
"family": "profile-sample",
"module": "profile-pipeline-command-router.cjs",
"router": "routeProfileSample"
},
{
"family": "write-profile",
"module": "profile-pipeline-command-router.cjs",
"router": "routeWriteProfile"
},
{
"family": "profile-questionnaire",
"module": "profile-pipeline-command-router.cjs",
"router": "routeProfileQuestionnaire"
},
{
"family": "generate-dev-preferences",
"module": "profile-pipeline-command-router.cjs",
"router": "routeGenerateDevPreferences"
},
{
"family": "generate-claude-profile",
"module": "profile-pipeline-command-router.cjs",
"router": "routeGenerateClaudeProfile"
},
{
"family": "generate-claude-md",
"module": "profile-pipeline-command-router.cjs",
"router": "routeGenerateClaudeMd"
}
],
"hooks": [],
"steps": [],
"contributions": [],
"gates": []
},
"qwen": {
"id": "qwen",
"role": "runtime",
"version": "1.10.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"
]
},
"localConfigDir": ".qwen",
"configFormat": "settings-json",
"artifactLayout": {
"global": [
{
"kind": "skills",
"destSubpath": "skills",
"prefix": "gsd-",
"nesting": "nested",
"recursive": false,
"converter": "convertClaudeCommandToClaudeSkill"
},
{
"kind": "agents",
"destSubpath": "agents",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": "convertClaudeAgentToQwenAgent"
}
],
"local": [
{
"kind": "skills",
"destSubpath": "skills",
"prefix": "gsd-",
"nesting": "nested",
"recursive": false,
"converter": "convertClaudeCommandToClaudeSkill"
},
{
"kind": "agents",
"destSubpath": "agents",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": "convertClaudeAgentToQwenAgent"
}
]
},
"commandStyle": "slash-hyphen",
"hooksSurface": "settings-json",
"hookEvents": "claude",
"sandboxTier": "none",
"supportTier": 2,
"installSurface": "settings-json",
"writesSharedSettings": true,
"permissionWriter": null,
"extendedHookEvents": [
"SubagentStop",
"Stop",
"PreCompact",
"SubagentStart"
],
"hostIntegration": {
"embeddingMode": "imperative",
"commandSurface": "slash-file",
"dispatch": {
"namedDispatch": true,
"nested": false,
"maxDepth": 1,
"background": true,
"subagentToolkit": "full",
"backgroundDispatch": false,
"isolation": "undocumented"
},
"modelMode": "passive",
"hookBus": "host",
"stateIO": "filesystem",
"transport": "mcp",
"runtime": "node",
"effortSurface": "undocumented"
},
"hostBehaviors": {
"skillPriorityFrontmatter": true,
"brandingRewrites": {
"CLAUDE.md": "QWEN.md",
"Claude Code": "Qwen Code",
".claude/": ".qwen/"
},
"legacyCommandsGsdCleanup": true,
"legacyCommandsGsdInstallMigration": true,
"legacyCommandsGsdUninstall": true,
"hyphenNameAgentBody": true
}
},
"reviewer": {
"slug": "qwen",
"flags": [
"--qwen"
],
"transport": "spawn",
"probe": {
"kind": "command-exists",
"binary": "qwen"
},
"invoke": {
"binary": "qwen",
"args": [
"-"
],
"promptChannel": "stdin",
"outputChannel": "stdout",
"modelArg": null,
"effortChannel": "none"
},
"timeoutFloorMs": 900000,
"emptyOutput": "stub-with-stderr",
"reviewsSection": "Qwen",
"evidenceClass": "source-grounded",
"requiresBinaries": [],
"promptBudgetKey": null,
"modelConfigKey": null,
"handler": null
}
},
"refactor-trigger": {
"id": "refactor-trigger",
"role": "feature",
"version": "1.10.0",
"title": "Complexity-triggered refactor",
"description": "Measures the complexity of the code a phase touched and, when a function crosses a configured threshold or jumps past its recorded anchor, surfaces a scoped refactor proposal at .planning/phases/<N>/<NN>-REFACTOR.md. Advisory by default — it never edits code and never blocks. Opt-in strict mode blocks /gsd-ship while a proposal is untriaged; a declined proposal is recorded in the broken-windows ledger when that capability is present. Operationalizes 'refactor early, refactor often' as continuous pressure instead of a thing you have to remember (issue #1953).",
"tier": "full",
"requires": [],
"engines": {
"gsd": ">=1.10.0"
},
"runtimeCompat": {
"supported": [
"*"
],
"unsupported": []
},
"skills": [],
"agents": [],
"activationKey": "refactor.trigger_enabled",
"config": {
"refactor.trigger_enabled": {
"type": "boolean",
"default": false,
"description": "Enable the complexity-triggered refactor hook. When true, an execute:post step evaluates the complexity of the files the phase touched and writes a scoped refactor proposal if a function crosses refactor.complexity_threshold or jumps past refactor.complexity_jump_delta. Opt-in; when false the hook never runs. Issue #1953."
},
"refactor.complexity_threshold": {
"type": "number",
"default": 15,
"description": "Absolute per-function complexity above which a refactor proposal is surfaced. Semantics match ESLint's `complexity: {max: N}` — the trigger is STRICTLY GREATER, so a score of exactly N does not trigger. Default 15 follows SonarSource's default; ESLint's own default is 20 and radon's rank C begins at 11. Raise it if proposals feel like noise."
},
"refactor.complexity_jump_delta": {
"type": "number",
"default": 5,
"description": "Complexity growth above which a refactor proposal is surfaced even when the absolute threshold is not reached. Measured against the function's anchor — the score recorded the last time the function was consciously dispositioned — so it accumulates across phases and catches slow creep the absolute threshold would miss. Strictly greater, as with the threshold."
},
"refactor.trigger_strict": {
"type": "boolean",
"default": false,
"description": "Record an untriaged refactor proposal as an open `deviation` entry in the broken-windows ledger, so it becomes a tracked task that must be resolved before ship. Off by default and deliberately so: a blocking complexity number is a metric an executor can satisfy by splitting one coherent function into two incoherent ones, so the entry clears on the proposal being DISPOSITIONED (gsd-tools refactor accept|decline), never on the score improving. Ship blocking is the broken-windows capability's existing ship:pre gate — enable it with workflow.windows_enforce. With broken-windows absent, strict mode still records the proposal locally and says so; it cannot block on its own. Advisory mode (the default) surfaces the same proposal and tracks nothing."
}
},
"commands": [
{
"family": "refactor",
"module": "refactor-trigger-command-router.cjs",
"router": "routeRefactorTriggerCommand"
}
],
"hooks": [],
"steps": [
{
"point": "execute:post",
"ref": {
"command": "refactor evaluate"
},
"produces": [
"REFACTOR.md"
],
"consumes": [],
"when": "refactor.trigger_enabled",
"onError": "skip"
}
],
"contributions": [],
"gates": []
},
"research": {
"id": "research",
"role": "feature",
"version": "1.10.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": [
"*"
],
"unsupported": []
},
"skills": [],
"agents": [
"gsd-phase-researcher"
],
"hooks": [],
"config": {
"workflow.research": {
"type": "boolean",
"default": true,
"description": "Run phase research before planning when research artifacts are missing or explicitly refreshed."
}
},
"steps": [
{
"point": "plan:pre",
"ref": {
"agent": "gsd-phase-researcher"
},
"fragment": {
"path": "fragments/plan-pre.md",
"inline": "<objective>\nResearch how to implement Phase {phase_number}: {phase_name}\nAnswer: \"What do I need to know to PLAN this phase well?\"\n</objective>\n\n<files_to_read>\n- {context_path} (USER DECISIONS from /gsd:discuss-phase)\n- {requirements_path} (Project requirements)\n- {state_path} (Project decisions and history)\n</files_to_read>\n\n${AGENT_SKILLS_RESEARCHER}\n\n<additional_context>\n**Phase description:** {phase_description}\n**Phase requirement IDs (MUST address):** {phase_req_ids}\n\n**Project instructions:** Read ./CLAUDE.md or ./.claude/CLAUDE.md if either exists; follow project-specific guidelines.\n**Project skills:** Check .claude/skills/ or .agents/skills/ directory if either exists. Read SKILL.md files and account for project skill patterns.\n</additional_context>\n\n<output>\nWrite to: {phase_dir}/{phase_num}-RESEARCH.md\n</output>\n"
},
"produces": [
"RESEARCH.md"
],
"consumes": [
"CONTEXT.md"
],
"when": "workflow.research",
"onError": "skip"
}
],
"contributions": [],
"gates": []
},
"schema-gate": {
"id": "schema-gate",
"role": "feature",
"version": "1.10.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": [
"*"
],
"unsupported": []
},
"skills": [],
"agents": [],
"hooks": [],
"config": {
"workflow.schema_push_detection": {
"type": "boolean",
"default": true,
"description": "Enable ORM schema push detection during planning. When schema-relevant files are detected in the phase scope, a [BLOCKING] push task is injected into the plan."
}
},
"steps": [],
"contributions": [
{
"point": "plan:pre",
"into": "planner",
"fragment": {
"path": "fragments/plan-pre.md",
"inline": "# Schema Push Detection Gate\n\n> Detects schema-relevant files in the phase scope 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.\n\nCheck if any files in the phase scope match schema patterns:\n\n```bash\nPHASE_SECTION=$(gsd_run query roadmap.get-phase \"${PHASE}\" --pick section 2>/dev/null)\n```\n\nScan `PHASE_SECTION`, `CONTEXT.md` (if loaded), and `RESEARCH.md` (if exists) for file paths matching these ORM patterns:\n\n| ORM | File Patterns |\n|-----|--------------|\n| Payload CMS | `src/collections/**/*.ts`, `src/globals/**/*.ts` |\n| Prisma | `prisma/schema.prisma`, `prisma/schema/*.prisma` |\n| Drizzle | `drizzle/schema.ts`, `src/db/schema.ts`, `drizzle/*.ts` |\n| Supabase | `supabase/migrations/*.sql` |\n| TypeORM | `src/entities/**/*.ts`, `src/migrations/**/*.ts` |\n\nAlso check if any existing PLAN.md files for this phase already reference these file patterns in `files_modified`.\n\n**If schema-relevant files detected:**\n\nSet `SCHEMA_PUSH_REQUIRED=true` and `SCHEMA_ORM={detected_orm}`.\n\nDetermine the push command for the detected ORM:\n\n| ORM | Push Command | Non-TTY Workaround |\n|-----|-------------|-------------------|\n| Payload CMS | `npx payload migrate` | `CI=true PAYLOAD_MIGRATING=true npx payload migrate` |\n| Prisma | `npx prisma db push` | `npx prisma db push --accept-data-loss` (if destructive) |\n| Drizzle | `npx drizzle-kit push` | `npx drizzle-kit push` |\n| Supabase | `supabase db push` | Set `SUPABASE_ACCESS_TOKEN` env var |\n| TypeORM | `npx typeorm migration:run` | `npx typeorm migration:run -d src/data-source.ts` |\n\nInject the following into the planner prompt (step 8) as an additional constraint:\n\n```markdown\n<schema_push_requirement>\n**[BLOCKING] Schema Push Required**\n\nThis phase modifies schema-relevant files ({detected_files}). The planner MUST include\na `[BLOCKING]` task that runs the database schema push command AFTER all schema file\nmodifications are complete but BEFORE verification.\n\n- ORM detected: {SCHEMA_ORM}\n- Push command: {push_command}\n- Non-TTY workaround: {env_hint}\n- If push requires interactive prompts that cannot be suppressed, flag the task for\n manual intervention with `autonomous: false`\n\nThis task is mandatory — the phase CANNOT pass verification without it. Build and\ntype checks will pass without the push (types come from config, not the live database),\ncreating a false-positive verification state.\n</schema_push_requirement>\n```\n\nDisplay: `Schema files detected ({SCHEMA_ORM}) — [BLOCKING] push task will be injected into plans`\n\n**If no schema-relevant files detected:** Skip silently.\n"
},
"produces": [],
"consumes": [
"CONTEXT.md"
],
"when": "workflow.schema_push_detection",
"onError": "skip"
}
],
"gates": []
},
"security": {
"id": "security",
"role": "feature",
"version": "1.10.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": [
"*"
],
"unsupported": []
},
"skills": [
"secure-phase"
],
"agents": [
"gsd-security-auditor"
],
"hooks": [],
"config": {
"workflow.security_enforcement": {
"type": "boolean",
"default": true,
"description": "Enable security threat-mitigation verification before phase advancement."
},
"workflow.security_asvs_level": {
"type": "number",
"default": 1,
"description": "OWASP ASVS level used by security review guidance."
},
"workflow.security_block_on": {
"type": "enum",
"values": [
"critical",
"high",
"medium",
"low",
"none"
],
"default": "high",
"description": "Minimum open threat severity that blocks advancement."
}
},
"steps": [
{
"point": "verify:post",
"ref": {
"skill": "secure-phase"
},
"produces": [
"SECURITY.md"
],
"consumes": [
"SUMMARY.md"
],
"when": "workflow.security_enforcement",
"onError": "halt"
}
],
"contributions": [
{
"point": "plan:pre",
"into": "planner",
"fragment": {
"inline": "Each PLAN.md must include a <threat_model> block when security enforcement is active. Use the configured ASVS level and blocking threshold from workflow.security_asvs_level and workflow.security_block_on."
},
"configValues": {
"security_asvs_level": "workflow.security_asvs_level",
"security_block_on": "workflow.security_block_on"
},
"produces": [],
"consumes": [
"CONTEXT.md"
],
"when": "workflow.security_enforcement"
}
],
"gates": [
{
"point": "ship:pre",
"check": {
"predicate": {
"kind": "artifact-frontmatter-equals",
"artifact": "SECURITY.md",
"field": "threats_open",
"equals": 0
}
},
"when": "workflow.security_enforcement",
"blocking": true,
"onError": "halt"
}
]
},
"tdd": {
"id": "tdd",
"role": "feature",
"version": "1.10.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": [
"*"
],
"unsupported": []
},
"skills": [],
"agents": [],
"hooks": [],
"config": {
"workflow.tdd_mode": {
"type": "boolean",
"default": false,
"description": "Enable TDD mode: planner annotates eligible tasks type:tdd and executor enforces RED/GREEN/REFACTOR gate sequence."
}
},
"steps": [],
"contributions": [
{
"point": "plan:pre",
"into": "planner",
"fragment": {
"inline": "<tdd_mode_active>\n**TDD Mode is ENABLED.** Apply TDD heuristics to all eligible tasks:\n- Business logic with defined I/O → type: tdd\n- API endpoints with request/response contracts → type: tdd\n- Data transformations, validation, algorithms → type: tdd\n- UI, config, glue code, CRUD → standard plan (type: execute)\nEach TDD plan gets one feature with RED/GREEN/REFACTOR gate sequence.\n</tdd_mode_active>"
},
"produces": [],
"consumes": [],
"when": "workflow.tdd_mode",
"onError": "skip"
}
],
"gates": [
{
"point": "execute:post",
"check": {
"query": "tdd.review-checkpoint"
},
"when": "workflow.tdd_mode",
"blocking": false,
"onError": "skip"
}
]
},
"trae": {
"id": "trae",
"role": "runtime",
"version": "1.10.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"
]
},
"localConfigDir": ".trae",
"configFormat": "none",
"artifactLayout": {
"global": [
{
"kind": "skills",
"destSubpath": "skills",
"prefix": "gsd-",
"nesting": "nested",
"recursive": false,
"converter": "convertClaudeCommandToTraeSkill"
},
{
"kind": "agents",
"destSubpath": "agents",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": "convertClaudeAgentToTraeAgent"
}
],
"local": [
{
"kind": "skills",
"destSubpath": "skills",
"prefix": "gsd-",
"nesting": "nested",
"recursive": false,
"converter": "convertClaudeCommandToTraeSkill"
},
{
"kind": "agents",
"destSubpath": "agents",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": "convertClaudeAgentToTraeAgent"
}
]
},
"commandStyle": "slash-hyphen",
"hooksSurface": "none",
"sandboxTier": "none",
"supportTier": 2,
"installSurface": "profile-marker-only",
"writesSharedSettings": false,
"permissionWriter": null,
"extendedHookEvents": [],
"hostIntegration": {
"embeddingMode": "imperative",
"commandSurface": "slash-file",
"dispatch": {
"namedDispatch": true,
"nested": "undocumented",
"maxDepth": "undocumented",
"background": true,
"subagentToolkit": "undocumented",
"backgroundDispatch": "undocumented",
"isolation": "undocumented"
},
"modelMode": "passive",
"hookBus": "engine",
"stateIO": "filesystem",
"transport": "mcp",
"runtime": "node",
"effortSurface": "undocumented"
},
"hostBehaviors": {
"skipSharedHooksInstall": true,
"soloStageMetadata": "workflow",
"projectInstructionFile": ".trae/rules/rules.md"
}
}
},
"ui": {
"id": "ui",
"role": "feature",
"version": "1.10.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": [
"*"
],
"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."
}
},
"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"
}
],
"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"
}
]
},
"vscode": {
"id": "vscode",
"role": "runtime",
"version": "1.10.0",
"title": "VS Code",
"description": "VS Code — Marketplace/VSIX extension; no file-projected config directory; IDE-profile reference host (active vscode.lm model, engine-owned hook bus, sandboxed globalState/workspaceState stateIO).",
"tier": "core",
"requires": [],
"engines": {
"gsd": ">=1.7.0"
},
"runtime": {
"configHome": {
"kind": "none",
"name": "vscode",
"env": []
},
"localConfigDir": null,
"configFormat": "none",
"artifactLayout": {
"global": [],
"local": []
},
"commandStyle": "slash-hyphen",
"hooksSurface": "none",
"extensionEvents": "none",
"sandboxTier": "none",
"supportTier": 1,
"installSurface": "none",
"writesSharedSettings": false,
"permissionWriter": null,
"extendedHookEvents": [],
"hostIntegration": {
"embeddingMode": "imperative",
"commandSurface": "palette",
"dispatch": {
"namedDispatch": true,
"nested": true,
"maxDepth": 5,
"background": true,
"subagentToolkit": "undocumented",
"backgroundDispatch": "undocumented",
"isolation": "undocumented"
},
"modelMode": "active",
"hookBus": "engine",
"stateIO": "sandboxed-storage",
"transport": "mcp",
"runtime": "sandboxed-web",
"effortSurface": "undocumented"
}
}
},
"windsurf": {
"id": "windsurf",
"role": "runtime",
"version": "1.10.0",
"title": "Windsurf",
"description": "Windsurf (Codeium) — workspace workflow artifact layout for slash commands; Cascade native hooks.json blocking hook bus (pre_write_code, pre_run_command); 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"
]
},
"localConfigDir": ".windsurf",
"configFormat": "none",
"artifactLayout": {
"global": [
{
"kind": "agents",
"destSubpath": "agents",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": "convertClaudeAgentToWindsurfAgent"
}
],
"local": [
{
"kind": "commands",
"destSubpath": "workflows",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": "convertClaudeCommandToWindsurfWorkflow"
},
{
"kind": "agents",
"destSubpath": "agents",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": "convertClaudeAgentToWindsurfAgent"
}
]
},
"commandStyle": "slash-hyphen",
"hooksSurface": "windsurf-hooks-json",
"sandboxTier": "none",
"supportTier": 2,
"installSurface": "profile-marker-only",
"writesSharedSettings": false,
"permissionWriter": null,
"extendedHookEvents": [],
"hostIntegration": {
"embeddingMode": "declarative",
"commandSurface": "slash-file",
"dispatch": {
"namedDispatch": "undocumented",
"nested": "undocumented",
"maxDepth": "undocumented",
"background": "undocumented",
"subagentToolkit": "undocumented",
"backgroundDispatch": "undocumented",
"isolation": "none"
},
"modelMode": "passive",
"hookBus": "host",
"stateIO": "filesystem",
"transport": "mcp",
"runtime": "undocumented",
"effortSurface": "undocumented"
},
"hostBehaviors": {
"skipSharedHooksInstall": true,
"legacyDevinSkillsCleanup": true,
"installsCommandBodiesForWorkflowDelegation": true,
"verificationStyle": "windsurf-workflows"
}
}
},
"zcode": {
"id": "zcode",
"role": "runtime",
"version": "1.10.0",
"title": "ZCode",
"description": "ZCode (Z.ai) — desktop Agentic Development Environment for GLM-5.2; Claude-shaped nested skills at ~/.zcode/skills/<name>/SKILL.md, slash commands, named subagents, native MCP; declarative plugin surface; profile-marker install; tier-2 community support.",
"tier": "core",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtime": {
"configHome": {
"kind": "dot-home",
"name": ".zcode",
"env": [
"ZCODE_CONFIG_DIR"
]
},
"localConfigDir": ".zcode",
"configFormat": "none",
"artifactLayout": {
"global": [
{
"kind": "skills",
"destSubpath": "skills",
"prefix": "gsd-",
"nesting": "nested",
"recursive": false,
"converter": "convertClaudeCommandToClaudeSkill"
},
{
"kind": "commands",
"destSubpath": "commands",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": null
},
{
"kind": "agents",
"destSubpath": "agents",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": null
}
],
"local": [
{
"kind": "skills",
"destSubpath": "skills",
"prefix": "gsd-",
"nesting": "nested",
"recursive": false,
"converter": "convertClaudeCommandToClaudeSkill"
},
{
"kind": "commands",
"destSubpath": "commands",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": null
},
{
"kind": "agents",
"destSubpath": "agents",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": null
}
]
},
"commandStyle": "slash-hyphen",
"hooksSurface": "none",
"sandboxTier": "none",
"supportTier": 2,
"installSurface": "profile-marker-only",
"writesSharedSettings": false,
"permissionWriter": null,
"extendedHookEvents": [],
"hostIntegration": {
"embeddingMode": "declarative",
"commandSurface": "slash-file",
"dispatch": {
"namedDispatch": true,
"nested": "undocumented",
"maxDepth": "undocumented",
"background": false,
"subagentToolkit": "full",
"backgroundDispatch": false,
"isolation": "none"
},
"modelMode": "passive",
"hookBus": "host",
"stateIO": "filesystem",
"transport": "mcp",
"runtime": "electron",
"effortSurface": "undocumented"
},
"hostBehaviors": {
"skipSharedHooksInstall": true
}
}
}
};
const bySkill = {
"ai-integration-phase": "ai-integration",
"code-review": "code-review",
"graphify": "graphify",
"mempalace-recall": "mempalace",
"mempalace-capture": "mempalace",
"validate-phase": "nyquist",
"profile-user": "profile-pipeline",
"secure-phase": "security",
"ui-phase": "ui",
"ui-review": "ui"
};
const byAgent = {
"gsd-framework-selector": "ai-integration",
"gsd-ai-researcher": "ai-integration",
"gsd-domain-researcher": "ai-integration",
"gsd-eval-planner": "ai-integration",
"gsd-code-reviewer": "code-review",
"gsd-code-fixer": "code-review",
"gsd-mempalace-curator": "mempalace",
"gsd-nyquist-auditor": "nyquist",
"gsd-pattern-mapper": "pattern-mapper",
"gsd-user-profiler": "profile-pipeline",
"gsd-phase-researcher": "research",
"gsd-security-auditor": "security",
"gsd-ui-checker": "ui",
"gsd-ui-auditor": "ui"
};
const byLoopPoint = {
"discuss:pre": {
"steps": [],
"contributions": [
{
"capId": "mempalace",
"point": "discuss:pre",
"into": "orchestrator",
"fragment": {
"path": "fragments/recall-discuss.md",
"inline": "<!--\n MemPalace capability — contribution fragment.\n Rendered into the discuss:pre orchestrator prompt when `mempalace.recall_on_discuss` is true.\n Contributes DATA (recall instructions), not control flow. onError: skip — never blocks discussion.\n-->\n### Memory recall (MemPalace)\n\n**Gate first.** Read `.planning/config.json`. If `mempalace.enabled` is not `true`, or `mempalace.recall_on_discuss` is `false`, **skip this entire section** and continue the discussion unchanged. (This contribution is only injected when the capability is enabled; the `recall_on_discuss` check lets you turn discuss-time recall off without disabling the rest of the capability.)\n\nOtherwise — before gathering new context, surface what you already know. This is read-only and side-effect-free; if MemPalace is unreachable, note \"memory unavailable\" and continue — recall never blocks discussion.\n\n1. **Resolve the wing.** Use `mempalace.wing` if set; otherwise derive it from `project_code` (fall back to the project directory name).\n2. **Wake up (cheap, ~600–900 tokens).**\n - Interactive run → call `mempalace_search` after a wake-up read of the wing.\n - Headless/cron run (no MCP server) → run `mempalace wake-up --wing <wing>` via the CLI.\n3. **Targeted recall.** Search the palace for prior work on this phase's topic:\n - Interactive → `mempalace_search(query=<phase topic>, wing=<wing>)` and, when `mempalace.mirror_kg` is on, `mempalace_kg_query` / `mempalace_kg_timeline` for decision facts and their validity windows.\n - Headless → `mempalace search \"<phase topic>\" --wing <wing>`.\n4. **Mode awareness** (`mempalace.memory_mode`). `augment` (default): the palace is an *additional* layer — never skip `.planning/graphs/` or STATE. `kg_backend`: query the palace's temporal KG as the *primary* graph source, with `.planning/graphs/` as fallback. `replace`: resolve recall *through the palace as the source of truth*, native memory as fallback. In every mode an unreachable palace falls back to native memory — recall never blocks.\n5. **Surface, don't dump.** Fold the top relevant drawers, decisions, patterns, and *surprises* into the discussion as prior context — cite drawer/fact provenance. Do not paste raw search output.\n\nIf any MemPalace call errors or times out, skip the rest of recall and proceed with discussion as normal.\n"
},
"produces": [],
"consumes": [],
"when": "mempalace.enabled",
"onError": "skip"
}
],
"gates": []
},
"discuss:post": {
"steps": [
{
"capId": "mempalace",
"point": "discuss:post",
"ref": {
"skill": "mempalace-capture"
},
"produces": [],
"consumes": [
"CONTEXT.md"
],
"when": "mempalace.enabled",
"onError": "skip"
}
],
"contributions": [],
"gates": []
},
"plan:pre": {
"steps": [
{
"capId": "ai-integration",
"point": "plan:pre",
"ref": {
"skill": "ai-integration-phase"
},
"produces": [
"AI-SPEC.md"
],
"consumes": [
"CONTEXT.md"
],
"when": "workflow.ai_integration_phase",
"onError": "skip"
},
{
"capId": "intel",
"point": "plan:pre",
"ref": {
"command": "intel api-surface"
},
"produces": [
".planning/intel/API-SURFACE.md"
],
"consumes": [],
"when": "intel.enabled",
"onError": "skip"
},
{
"capId": "mempalace",
"point": "plan:pre",
"ref": {
"skill": "mempalace-recall"
},
"produces": [
"MEMORY-RECALL.md"
],
"consumes": [
"CONTEXT.md"
],
"when": "mempalace.enabled",
"onError": "skip"
},
{
"capId": "research",
"point": "plan:pre",
"ref": {
"agent": "gsd-phase-researcher"
},
"fragment": {
"path": "fragments/plan-pre.md",
"inline": "<objective>\nResearch how to implement Phase {phase_number}: {phase_name}\nAnswer: \"What do I need to know to PLAN this phase well?\"\n</objective>\n\n<files_to_read>\n- {context_path} (USER DECISIONS from /gsd:discuss-phase)\n- {requirements_path} (Project requirements)\n- {state_path} (Project decisions and history)\n</files_to_read>\n\n${AGENT_SKILLS_RESEARCHER}\n\n<additional_context>\n**Phase description:** {phase_description}\n**Phase requirement IDs (MUST address):** {phase_req_ids}\n\n**Project instructions:** Read ./CLAUDE.md or ./.claude/CLAUDE.md if either exists; follow project-specific guidelines.\n**Project skills:** Check .claude/skills/ or .agents/skills/ directory if either exists. Read SKILL.md files and account for project skill patterns.\n</additional_context>\n\n<output>\nWrite to: {phase_dir}/{phase_num}-RESEARCH.md\n</output>\n"
},
"produces": [
"RESEARCH.md"
],
"consumes": [
"CONTEXT.md"
],
"when": "workflow.research",
"onError": "skip"
},
{
"capId": "ui",
"point": "plan:pre",
"ref": {
"skill": "ui-phase"
},
"produces": [
"UI-SPEC.md"
],
"consumes": [
"CONTEXT.md"
],
"when": "workflow.ui_phase",
"onError": "skip"
},
{
"capId": "pattern-mapper",
"point": "plan:pre",
"ref": {
"agent": "gsd-pattern-mapper"
},
"fragment": {
"path": "fragments/plan-pre.md",
"inline": "<pattern_mapping_context>\n**Phase:** {phase_number} - {phase_name}\n**Phase directory:** {phase_dir}\n**Padded phase:** {padded_phase}\n\n<files_to_read>\n- {context_path} (USER DECISIONS from /gsd:discuss-phase)\n- {research_path} (Technical Research)\n</files_to_read>\n\n**Output file:** {phase_dir}/{padded_phase}-PATTERNS.md\n\nExtract the list of files to be created/modified from CONTEXT.md and RESEARCH.md. For each file, classify by role and data flow, find the closest existing analog in the codebase, extract concrete code excerpts, and produce PATTERNS.md.\n</pattern_mapping_context>\n"
},
"produces": [
"PATTERNS.md"
],
"consumes": [
"RESEARCH.md"
],
"when": "workflow.pattern_mapper",
"onError": "skip"
}
],
"contributions": [
{
"capId": "ai-integration",
"point": "plan:pre",
"into": "planner",
"fragment": {
"path": "fragments/api-coverage-plan-pre.md",
"inline": "# API Coverage Decision Checkpoint\n\n> Full API Coverage by Default — Opt Out, Never Opt In. Fires when a phase\n> integrates an external API / SDK / service. Most non-API phases will not fire\n> it — that is the point.\n\n## Why this exists\n\n\"We integrated the API\" too often silently means \"we integrated whatever the\nfirst use case exercised.\" Every un-built capability is then an invisible hole,\ndiscovered later by a user who reasonably expected it to work. The phase sealed\ngreen because its tasks completed; nobody decided the gaps were acceptable,\nbecause nobody enumerated them. This checkpoint makes the surface **visible and\ndecided** before the phase can seal.\n\n## Detect whether this phase integrates an external API\n\nThe detector is a deterministic scan over the phase scope. It strips fenced\ncode blocks first, so a trigger term inside a code snippet does not fire. It\nreturns a typed result: `{ detected, signals[], terms }`. Run it on the phase\nscope (the concatenation of this phase's ROADMAP section + the PLAN body):\n\n```bash\nSCOPE=\"$(cat \"${PHASE_DIR}\"/*-PLAN.md 2>/dev/null) $(gsd_run query roadmap.get-phase \"${PHASE}\" 2>/dev/null || true)\"\nAPI_COVERAGE_JSON=$(printf '%s' \"$SCOPE\" | node gsd-core/bin/lib/api-coverage.cjs --json 2>/dev/null || echo '{\"detected\":false,\"signals\":[]}')\n```\n\nRead `API_COVERAGE_JSON.detected`. Act on it only — do **not** pattern-match the\nprose yourself.\n\n**If `detected` is `false`:** this phase does not integrate an external API. Skip\nthe checkpoint entirely and continue planning. Do not raise it with the user.\n\n**If `detected` is `true`:** an external-API integration is in scope. You MUST\nproduce a **coverage matrix** before the plan is finalized.\n\n**If `detected` is `true` but the phase genuinely integrates no external API**\n(the detector is deterministic, not infallible — confirm by re-reading the phase\nscope, not by preference): do NOT fabricate a matrix row for a capability that\ndoes not exist. Write a reasoned declaration to `${PHASE_DIR}/COVERAGE.md`\ninstead:\n\n```markdown\nNo external API integration: <one-line reason — what the phase touches instead>.\n```\n\nThe reason is required, exactly like an `OPT-OUT` reason. The seal-time gate\naccepts this declaration in place of a matrix.\n\n## Produce the coverage matrix\n\nEnumerate the external API's full **capability surface** — the verb/endpoint/method\nlist (e.g. for a music service: `search`, `play`, `pause`, `skip`, `set_volume`,\n`get_playlist`, `create_playlist`, `add_to_playlist`, …). For each capability\nrecord a decision, starting from **full coverage** as the default:\n\n| capability | decision | reason |\n|---|---|---|\n| `<capability-id>` | `INTEGRATE` \\| `OPT-OUT` | `<one-line reason if OPT-OUT>` |\n\nRules:\n\n- **`INTEGRATE` is the default.** Every capability starts as INTEGRATE; the\n matrix is the *subtraction record*.\n- **Every `OPT-OUT` MUST carry a one-line reason** (`not needed`, `not needed\n yet`, `explicitly out of scope`, …). An opt-out without a reason is an\n un-decided hole — the exact failure mode this gate exists to close.\n- **A second integration against the same need** (e.g. a second platform for the\n same capability) starts from the **same full-coverage baseline** as the first.\n Do not carry over the first integration's opt-outs silently — re-decide each\n capability for the new surface, so a first-class/fallback asymmetry cannot\n accumulate.\n\nWrite the matrix to `${PHASE_DIR}/COVERAGE.md` (canonical markdown-table form):\n\n```markdown\n# API Coverage — <service>\n\n> Full coverage by default. Opt-outs are explicit, reasoned decisions.\n\n| capability | decision | reason |\n|---|---|---|\n| search | INTEGRATE | |\n| playlists | INTEGRATE | |\n| skip | OPT-OUT | not needed yet — tracked for follow-up phase |\n```\n\nA fenced ` ```coverage ` JSON block is also accepted for machine-generated\nmatrices; the markdown table is preferred (human-editable, diff-friendly).\n\n## The seal-time gate\n\nThis checkpoint is enforced. At `verify:pre` the `api-coverage.verify-pre` gate\nruns `check api-coverage.verify-pre <phase-dir>`:\n\n- If `COVERAGE.md` exists, it is validated — every row needs a valid decision and\n every `OPT-OUT` a reason. A malformed/partial matrix **blocks the seal**. A\n reasoned `No external API integration: …` declaration (and no rows) passes.\n- If `COVERAGE.md` is absent, the detector runs again over the phase scope. If a\n strong external-API-integration signal is found, the seal is **blocked** until a\n matrix is produced. If no signal is found, the phase is treated as a non-API\n phase and the seal proceeds.\n\nSo: an API-integrating phase cannot seal without a decided matrix. Produce it at\nplan time; do not leave it for seal time.\n\n## Tuning the vocabulary (optional)\n\nThe trigger vocabulary is a curated, additive-only set in\n`gsd-core/bin/lib/api-coverage.cjs` (`DEFAULT_API_COVERAGE_TERMS`). To widen it\nfor a project, override at the call site:\n\n```bash\nprintf '%s' \"$SCOPE\" | node gsd-core/bin/lib/api-coverage.cjs --json \\\n --verbs integrate,wrap,connect,embed --nouns api,sdk,rest,grpc,webhook,plugin\n```\n\nThe whole checkpoint is toggleable via `workflow.api_coverage_gate` in\n`.planning/config.json`.\n"
},
"produces": [
"COVERAGE.md"
],
"consumes": [
"CONTEXT.md"
],
"when": "workflow.api_coverage_gate",
"onError": "skip"
},
{
"capId": "assumption-delta",
"point": "plan:pre",
"into": "planner",
"fragment": {
"path": "fragments/plan-pre.md",
"inline": "# Assumption-Delta Architecture Checkpoint\n\n> Advisory, non-blocking. Fires **only** when the phase scope shows a singular→plural / required→optional / derived→chosen transition. When it fires, it surfaces ONE identity-model question before the plan is finalized. Most phases will not fire it — that is the point.\n\n## Why this exists\n\nMost quietly-imported architectural debt does not come from a missing upfront design phase. It comes at the *seam*: a later phase introduces a second case (a second platform, auth method, tenant, region, source of truth) and nobody re-asks whether the original abstraction still names the right thing. The phase that adds the second case is exactly the 20-minute conversation that prevents an afternoon of later cleanup.\n\n## Run the detector\n\nThe detector is a deterministic scan over the phase scope text. It strips fenced code blocks first, so a trigger word that appears only inside a code snippet does not fire. It returns a typed result: `{ detected, signals[], terms }`. Resolve it through the `assumption-delta scan` query (same phase-section resolver as `roadmap.get-phase`):\n\n```bash\nASSUMPTION_DELTA_JSON=$(gsd_run query assumption-delta scan \"${PHASE}\" --json 2>/dev/null || echo '{\"detected\":false,\"signals\":[],\"terms\":{}}')\n```\n\n> If the phase section cannot be resolved (no `ROADMAP.md` / unknown phase), the query emits `{ \"detected\": false, ... }` — the checkpoint does not fire. Do not block on it.\n>\n> Optional tuning — pass `--terms <comma-list>` to replace the curated pluralization cues for this project (the `optional`/`chosen` cues keep their defaults): `gsd_run query assumption-delta scan \"${PHASE}\" --json --terms second,alternative,fallback`.\n\n## Decision branch\n\nRead `ASSUMPTION_DELTA_JSON`. Act on `detected` only — do **not** pattern-match the human prose.\n\n**If `detected` is `false`:** this phase does not change a core assumption. Skip the checkpoint entirely and continue planning. Do not raise it with the user.\n\n**If `detected` is `true`:** a core assumption may have lost its monopoly. The `signals[]` array tells you which family fired:\n\n| `kind` | What changed | The question to answer |\n|---|---|---|\n| `pluralization` | A second X was introduced where there was one (second platform / auth method / tenant / region / source of truth) | Does the current primary key / identity model still name the right noun? |\n| `optional` | A required / `only` field became optional | Is the field still the right anchor, or has the anchor moved? |\n| `chosen` | A derived value became chosen, or a constant became a parameter | Has a configuration decision become a modeling decision? |\n\nBefore finalizing the plan, answer this for the user and record the decision explicitly:\n\n> **Promote vs. add-alongside.** The usual correct move when a generalization occurs is to **promote** the new general representation to the primary and **demote** the old specific one to a detail of one variant — *not* to add the new one alongside the still-required old one. Adding alongside silently contradicts the generalized intent (a later variant that does not fit the old primary can be stored but never confirmed as a default).\n\nRecord the outcome in the PLAN.md front matter / a `<assumption_delta_decision>` block:\n\n- The **noun** that is now primary (the generalized identity).\n- The **decision**: `promote` | `add-alongside` | `no-change`, with a one-line rationale.\n- If `add-alongside`: call it out as accepted debt and note what would force a later promote.\n\n## Optional companion: an invariant test\n\nWhen `detected` is `true`, suggest (do not require) a contract/invariant test that encodes the now-generalized intent — e.g. *\"every confirmed default round-trips through the primary use-path, for every supported variant.\"* That test goes red the instant a future phase reintroduces the singular assumption, so the regression cannot land silently. If the user accepts, add the test as a task in the plan.\n\n## Tuning the vocabulary (optional)\n\nThe trigger vocabulary is a curated, additive-only set in `gsd-core/bin/lib/assumption-delta.cjs` (`DEFAULT_ASSUMPTION_DELTA_TERMS`). Bare \"or\" is intentionally excluded — it is too common in prose and would make the gate fire constantly. To widen or narrow the cues for a project, override at the call site with `--terms <comma-list>` (replaces the pluralization cues; `optional`/`chosen` keep defaults). The whole checkpoint is toggleable via `workflow.assumption_delta` in `.planning/config.json`.\n\nThis checkpoint is advisory: it informs and records; it never blocks the phase.\n"
},
"produces": [],
"consumes": [
"CONTEXT.md"
],
"when": "workflow.assumption_delta",
"onError": "skip"
},
{
"capId": "schema-gate",
"point": "plan:pre",
"into": "planner",
"fragment": {
"path": "fragments/plan-pre.md",
"inline": "# Schema Push Detection Gate\n\n> Detects schema-relevant files in the phase scope 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.\n\nCheck if any files in the phase scope match schema patterns:\n\n```bash\nPHASE_SECTION=$(gsd_run query roadmap.get-phase \"${PHASE}\" --pick section 2>/dev/null)\n```\n\nScan `PHASE_SECTION`, `CONTEXT.md` (if loaded), and `RESEARCH.md` (if exists) for file paths matching these ORM patterns:\n\n| ORM | File Patterns |\n|-----|--------------|\n| Payload CMS | `src/collections/**/*.ts`, `src/globals/**/*.ts` |\n| Prisma | `prisma/schema.prisma`, `prisma/schema/*.prisma` |\n| Drizzle | `drizzle/schema.ts`, `src/db/schema.ts`, `drizzle/*.ts` |\n| Supabase | `supabase/migrations/*.sql` |\n| TypeORM | `src/entities/**/*.ts`, `src/migrations/**/*.ts` |\n\nAlso check if any existing PLAN.md files for this phase already reference these file patterns in `files_modified`.\n\n**If schema-relevant files detected:**\n\nSet `SCHEMA_PUSH_REQUIRED=true` and `SCHEMA_ORM={detected_orm}`.\n\nDetermine the push command for the detected ORM:\n\n| ORM | Push Command | Non-TTY Workaround |\n|-----|-------------|-------------------|\n| Payload CMS | `npx payload migrate` | `CI=true PAYLOAD_MIGRATING=true npx payload migrate` |\n| Prisma | `npx prisma db push` | `npx prisma db push --accept-data-loss` (if destructive) |\n| Drizzle | `npx drizzle-kit push` | `npx drizzle-kit push` |\n| Supabase | `supabase db push` | Set `SUPABASE_ACCESS_TOKEN` env var |\n| TypeORM | `npx typeorm migration:run` | `npx typeorm migration:run -d src/data-source.ts` |\n\nInject the following into the planner prompt (step 8) as an additional constraint:\n\n```markdown\n<schema_push_requirement>\n**[BLOCKING] Schema Push Required**\n\nThis phase modifies schema-relevant files ({detected_files}). The planner MUST include\na `[BLOCKING]` task that runs the database schema push command AFTER all schema file\nmodifications are complete but BEFORE verification.\n\n- ORM detected: {SCHEMA_ORM}\n- Push command: {push_command}\n- Non-TTY workaround: {env_hint}\n- If push requires interactive prompts that cannot be suppressed, flag the task for\n manual intervention with `autonomous: false`\n\nThis task is mandatory — the phase CANNOT pass verification without it. Build and\ntype checks will pass without the push (types come from config, not the live database),\ncreating a false-positive verification state.\n</schema_push_requirement>\n```\n\nDisplay: `Schema files detected ({SCHEMA_ORM}) — [BLOCKING] push task will be injected into plans`\n\n**If no schema-relevant files detected:** Skip silently.\n"
},
"produces": [],
"consumes": [
"CONTEXT.md"
],
"when": "workflow.schema_push_detection",
"onError": "skip"
},
{
"capId": "security",
"point": "plan:pre",
"into": "planner",
"fragment": {
"inline": "Each PLAN.md must include a <threat_model> block when security enforcement is active. Use the configured ASVS level and blocking threshold from workflow.security_asvs_level and workflow.security_block_on."
},
"configValues": {
"security_asvs_level": "workflow.security_asvs_level",
"security_block_on": "workflow.security_block_on"
},
"produces": [],
"consumes": [
"CONTEXT.md"
],
"when": "workflow.security_enforcement"
},
{
"capId": "tdd",
"point": "plan:pre",
"into": "planner",
"fragment": {
"inline": "<tdd_mode_active>\n**TDD Mode is ENABLED.** Apply TDD heuristics to all eligible tasks:\n- Business logic with defined I/O → type: tdd\n- API endpoints with request/response contracts → type: tdd\n- Data transformations, validation, algorithms → type: tdd\n- UI, config, glue code, CRUD → standard plan (type: execute)\nEach TDD plan gets one feature with RED/GREEN/REFACTOR gate sequence.\n</tdd_mode_active>"
},
"produces": [],
"consumes": [],
"when": "workflow.tdd_mode",
"onError": "skip"
}
],
"gates": [
{
"capId": "drift",
"point": "plan:pre",
"check": {
"query": "verify.codebase-drift"
},
"when": "workflow.plan_drift_precheck",
"blocking": false,
"onError": "skip"
},
{
"capId": "ui",
"point": "plan:pre",
"check": {
"query": "ui.plan-gate"
},
"when": "workflow.ui_safety_gate",
"blocking": true,
"onError": "halt"
}
]
},
"plan:post": {
"steps": [
{
"capId": "mempalace",
"point": "plan:post",
"ref": {
"skill": "mempalace-capture"
},
"produces": [],
"consumes": [
"PLAN.md"
],
"when": "mempalace.enabled",
"onError": "skip"
}
],
"contributions": [
{
"capId": "claude-orchestration",
"point": "plan:post",
"into": "planner",
"fragment": {
"path": "fragments/plan-post.md",
"inline": "# Claude orchestration — ultraplan plan-offload ownership (BETA)\n\n> Injected at `plan:post` `into: planner` only when\n> `claude_orchestration.enabled` is true. Default-off; `onError: skip`.\n\n## Ownership declaration\n\nThe `gsd-ultraplan-phase` plan-offload surface (offloading GSD's plan phase to\nClaude Code's ultraplan cloud) is **owned by this capability**, not by a\nstandalone BETA skill. Both surfaces share one runtime gate\n(`claude_orchestration.enabled`), one BETA boundary, and one Claude-Code-only\ndetection seam.\n\n## When the planner should consider ultraplan offload\n\nWhen this contribution is active (capability enabled, Claude Code runtime), the\nplanner MAY offer the `/gsd-ultraplan-phase` path as an alternative to local\n`/gsd-plan-phase` for phases where cloud-assisted planning adds value. This is\nadvisory, not mandatory — the stable local planner remains the default.\n\n## Fallback contract\n\nIf the capability is disabled, or the runtime is not Claude Code, ultraplan\noffload is **not surfaced** and the planner proceeds with the standard local\n`/gsd-plan-phase`. The `gsd-ultraplan-phase` command itself remains installed\n(its own runtime gate already no-ops on non-Claude runtimes); this contribution\nonly governs whether the capability manifest advertises it as part of the\norchestration surface.\n"
},
"produces": [],
"consumes": [
"CONTEXT.md"
],
"when": "claude_orchestration.enabled",
"onError": "skip"
},
{
"capId": "external-job",
"point": "plan:post",
"into": "planner",
"fragment": {
"path": "fragments/plan-post.md",
"inline": "<!-- external-job capability — plan:post fragment, injected into the planner (#1164). -->\n\n## Tag runtime budgets on long tasks\n\nFor every `<task>` likely to exceed ~2 minutes of real compute, emit a\n`<runtime_budget>` child element so execute can classify it:\n\n- `<runtime_budget>quick</runtime_budget>` — under ~2 min; runs normally.\n- `<runtime_budget>medium</runtime_budget>` — ~2–30 min; foreground, but with\n progress expectations.\n- `<runtime_budget>unknown</runtime_budget>` — runtime not yet characterized;\n execute must run a first-health check and set a soft-review deadline before\n trusting the child timeout. Define a progress signal and an abort condition.\n- `<runtime_budget>long_compute</runtime_budget>` — legitimately over ~30–60 min\n (HPC solver, model training, large simulations). Execute must **externalize**\n this as an async external job (see the execute:wave:post fragment) rather than\n blocking the agent turn.\n\nFor any `long_compute` task, also declare the async contract the executor will\nneed: the `submit_command`, the `expected_artifacts` the job must produce, and\nthe `verification_command` that proves the output before the plan can close.\nDo not hardcode a cluster account, partition, or project path — the planner\nnever knows the scheduler layout; it declares the contract, the executor's\nadapter fills the backend specifics.\n"
},
"produces": [],
"consumes": [],
"when": "external_job.enabled",
"onError": "skip"
}
],
"gates": [
{
"capId": "gap-analysis",
"point": "plan:post",
"check": {
"query": "gap-analysis.plan-post"
},
"when": "workflow.post_planning_gaps",
"blocking": false,
"onError": "skip"
}
]
},
"execute:pre": {
"steps": [],
"contributions": [],
"gates": []
},
"execute:wave:pre": {
"steps": [],
"contributions": [
{
"capId": "claude-orchestration",
"point": "execute:wave:pre",
"into": "executor",
"fragment": {
"path": "fragments/execute-wave-pre.md",
"inline": "# Claude orchestration — Workflow execution backend (BETA)\n\n> Injected at `execute:wave:pre` `into: executor` only when\n> `claude_orchestration.enabled` is true. Default-off; `onError: skip`.\n\n## When this contribution is active\n\nThe Claude orchestration capability is **default-off and BETA**. It activates only\nwhen ALL of the following hold:\n\n1. `claude_orchestration.enabled` is `true` in `.planning/config.json`, AND\n2. the active runtime is **Claude Code** (the Workflow tool is Claude / Agent\n SDK-specific), AND\n3. `claude_orchestration.execution_backend` resolves to `workflow` — either\n explicitly, or via `auto` — **and** the Agent SDK version is\n `>= claude_orchestration.min_agent_sdk_version` (default `0.3.149`). The SDK\n floor applies in both `auto` and `workflow` modes (fail-closed: a pre-release\n or older SDK never activates the preview backend).\n\nDetection is fail-closed: any miss degrades to **inline, manual, one-agent-per-\nmessage dispatch** — exactly today's behaviour. On a non-Claude runtime this\ncontribution is a no-op.\n\n## Why `execute:wave:pre` (not `execute:wave:post`)\n\nThis is a **dispatch-backend selector** — it decides HOW a wave's executor agents\nare spawned. That decision has to be made BEFORE the wave's `Agent()` calls in\n`execute-phase.md` step 3, not after the wave has already finished (#2285). The\ncapability previously registered at `execute:wave:post`, which fires only after\nworktree merge/post-merge tests/tracking updates — by then the wave was already\ndispatched inline, so the contribution was structurally unable to change how\ndispatch happened. This fragment is injected at the point that actually precedes\ndispatch.\n\n## What the orchestrator does when the Workflow backend is active\n\nBefore spawning executor agents for the current wave (execute-phase.md step 3),\nresolve the dispatch backend through the single composed CLI seam:\n\n```bash\ngsd-tools claude-orchestration resolve-wave-dispatch \\\n --waves \"$WAVE_MANIFEST_PATH\" --run-id \"$PHASE_RUN_ID\" \\\n --runtime \"$RUNTIME\" \\\n --phase-dir \"$PHASE_DIR\" --raw\n```\n\n`--agent-sdk-version` is no longer passed here (#2590). The router resolves the\ninstalled Agent SDK version itself; see **Agent SDK version** below. The former\n`${AGENT_SDK_VERSION:+--agent-sdk-version \"$AGENT_SDK_VERSION\"}` line was also\n**shell-dependent**: zsh does not word-split unquoted parameter expansions, so it\ncollapsed to a SINGLE argv element there, `argValue()` never matched, and the run\nfailed into `agent_sdk_version_unknown` — indistinguishable from genuinely\nunknown. Pass `--agent-sdk-version <ver>` explicitly only to pin a version.\n\nThis composes `detectWorkflowBackend` (the gate ladder above) with\n`emitWorkflowScript` (the wave→plan mapping below) in ONE call — the pure\nfunction backing it is `resolveWaveDispatch` in\n`gsd-core/bin/lib/claude-orchestration.cjs`. Response shape:\n`{ backend: 'inline'|'workflow', reason, script?, summary? }`.\n\n### Manifest construction (`$WAVE_MANIFEST_PATH`, `$PHASE_RUN_ID`, `$PHASE_DIR`)\n\nThese are NOT pre-existing execute-phase.md variables — the orchestrator builds\nthem at this step, from data it already has in-context from `discover_and_group_plans`\n(the `PLAN_INDEX` JSON) and step 2.5 (the per-plan `USE_WORKTREES_FOR_PLAN` decision):\n\n1. **`$PHASE_DIR`** — reuse `{phase_dir}` from the `INIT` bundle (already loaded\n in the `initialize` step). No new value needed.\n\n2. **`$PHASE_RUN_ID`** — a stable identifier for THIS phase-execution attempt, so\n `resumeFromRunId` can resume an interrupted run without re-dispatching plans\n the Workflow tool already completed. Construct it deterministically —\n `execute-{phase_number}-{phase_slug}` — from `INIT`'s `phase_number`/`phase_slug`\n (both are already validated identifiers used elsewhere in this workflow, so\n they satisfy `emitWorkflowScript`'s `isScriptableIdentifier` check). Do NOT\n mint a new random id per wave — the SAME `$PHASE_RUN_ID` is reused for every\n wave in the phase so the Workflow tool can correctly track cross-wave resume\n state.\n\n3. **`$WAVE_MANIFEST_PATH`** — a fresh temp file for THIS wave's manifest (one\n wave = one `waves` array with a single entry, matching the wave-by-wave\n dispatch loop; do not batch multiple waves into one manifest — waves are\n dispatched in wave order, not all at once):\n\n ```bash\n WAVE_MANIFEST_PATH=$(mktemp \"${TMPDIR:-/tmp}/gsd-wave-dispatch-XXXXXX\") && mv \"$WAVE_MANIFEST_PATH\" \"$WAVE_MANIFEST_PATH.json\" && WAVE_MANIFEST_PATH=\"$WAVE_MANIFEST_PATH.json\"\n ```\n\n Then **use the Write tool** (not a bash/jq pipeline — the orchestrator already\n has every field parsed in-context) to write the manifest JSON to\n `$WAVE_MANIFEST_PATH`:\n\n ```json\n {\n \"waves\": [\n {\n \"id\": \"wave-{N}\",\n \"plans\": [\n {\n \"id\": \"{plan_id}\",\n \"brief\": \"{the SAME <objective>...<success_criteria> prompt block step 3 builds for this plan's inline Agent() call}\",\n \"files_modified\": [\"{from PLAN_INDEX.plans[].files_modified for this plan}\"],\n \"use_worktree\": {true unless step 2.5 set USE_WORKTREES_FOR_PLAN=false for this plan}\n }\n ]\n }\n ]\n }\n ```\n\n - **`id`** — the plan id from `PLAN_INDEX`, e.g. `\"01-01\"`.\n - **`brief`** — MUST carry the same task content as step 3's inline `Agent()`\n prompt (the `<objective>`/`<execution_context>`/`<files_to_read>`/\n `<success_criteria>` block, with `{plan_number}`/`{phase_number}`/\n `{phase_name}` substituted) — a short summary here would NOT reproduce\n step 3's behavior and would violate the \"identical artifacts\" contract.\n - **`files_modified`** — copy verbatim from the plan's `PLAN_INDEX` entry.\n - **`use_worktree`** — `true` for every plan UNLESS step 2.5's per-plan\n worktree gate (`execute-phase/steps/per-plan-worktree-gate.md`) set\n `USE_WORKTREES_FOR_PLAN=false` for that plan (submodule-touching plan, or\n project-level `USE_WORKTREES=false`) — in which case pass `false` here so\n `emitWorkflowScript` omits `isolation: \"worktree\"` for that plan (#2772 /\n #2285 finding 1). **Never** hardcode `true` — that would force worktree\n isolation on a plan the inline path explicitly keeps out of worktrees.\n\n4. **`$AGENT_SDK_VERSION`** — no longer built here; the router resolves it.\n\n**Agent SDK version:** the orchestrator has no *bash-computable* way to\nintrospect the live Agent SDK version — but the router runs in Node, so it\nresolves the version itself (#2590), in this order:\n\n1. an explicit `--agent-sdk-version <ver>` (pin a version),\n2. `GSD_AGENT_SDK_VERSION`,\n3. the **installed** `@anthropic-ai/claude-agent-sdk` package version, read from\n its `package.json` on disk by walking `node_modules` up the tree. (Read\n directly rather than via `require.resolve`: the SDK's `exports` map does not\n expose `./package.json`, so `require.resolve` throws\n `ERR_PACKAGE_PATH_NOT_EXPORTED`.)\n\nPreviously nothing computed this at all, so gate 5 returned\n`agent_sdk_version_unknown` on **every** automated run and the Workflow backend\ncould never activate — while `gsd-tools capability state` still reported the\ncapability `active: true`. Fail-closed is preserved: when no version can be\nresolved, gate 5 still declines to `inline`. What changed is that a resolvable\nversion is now actually found, so a genuinely-too-old SDK reports\n`agent_sdk_version_below_floor` — the truthful reason — instead of `unknown`.\n\n**If `backend == \"workflow\"`:** run the emitted `script` via the Workflow tool\nfor THIS wave instead of the per-message `Agent()` loop in step 3. The script\ncomposes the SAME `gsd-executor` agent type the inline path uses, with\nworktree isolation applied PER PLAN from the manifest's `use_worktree` field\n(see `emitWorkflowScript`):\n\n- **waves → one or more sequential `parallel()` barriers** — each wave is a\n barrier group; when plans within a wave share `files_modified`, they are split\n into separate sequential stages within that wave's barrier.\n- **plans → `agent(brief, { agentType: 'gsd-executor', isolation: 'worktree' })`**\n when `use_worktree` is not `false`, or `agent(brief, { agentType: 'gsd-executor' })`\n (no isolation) when it is — so the produced `SUMMARY.md` and commits are\n identical to inline dispatch, INCLUDING the inline path's submodule safety\n gate (#2772 / #2285 finding 1).\n- **`files_modified` overlap → separate sequential stages** — the same overlap\n rule execute-phase already applies inline (step 1 of the wave loop).\n- **`resumeFromRunId`** — **pass `summary.resumeRunId` as the Workflow tool's\n `resumeFromRunId` INPUT when you invoke the tool.** It is a tool parameter,\n not a script function; the script deliberately does not call it (#2590 — doing\n so threw \"resumeFromRunId is not defined\" and rejected the entire script).\n Omitting it from the tool invocation silently regresses phase-resume to a\n no-op: an interrupted phase re-runs completed plans.\n\nThe orchestrator still runs steps 4–5.8 (wait for completion, worktree cleanup,\npost-merge gate, tracking update) exactly as it does for inline dispatch — the\nWorkflow backend only replaces HOW agents are spawned for this wave, not what\nhappens after they return.\n\n**If `backend == \"inline\"`** (any gate miss, or `resolve-wave-dispatch` itself\nunavailable/erroring): proceed to step 3's standard per-message `Agent()`\ndispatch — the default, byte-identical-to-today path. `onError: skip` on this\ncontribution means a `resolve-wave-dispatch` command failure is treated exactly\nlike an `inline` result, never as a fatal wave error.\n\n## Fallback contract\n\nDetection is fail-closed end-to-end: capability disabled, non-Claude runtime,\n`execution_backend:\"inline\"`, missing/incapable host descriptor, unknown or\nbelow-floor Agent SDK version, or an `emitWorkflowScript` failure on a malformed\nwave manifest — ANY of these degrades to `backend:\"inline\"` and execute-phase's\nstandard inline dispatch (step 3) runs unmodified. The Workflow backend never\npartially activates; the executor MUST NOT assume parallelism, a shared budget,\nor resume-from-run-id semantics when `backend == \"inline\"`.\n"
},
"produces": [],
"consumes": [
"PLAN.md"
],
"when": "claude_orchestration.enabled",
"onError": "skip"
}
],
"gates": []
},
"execute:wave:post": {
"steps": [],
"contributions": [
{
"capId": "external-job",
"point": "execute:wave:post",
"into": "executor",
"fragment": {
"path": "fragments/execute-wave-post.md",
"inline": "<!-- external-job capability — execute:wave:post fragment, injected into the executor (#1164).\n\n Why wave:post, not wave:pre (#1164 refinement A): execute-phase.md only\n dispatches execute:wave:post today — wave:pre is declared in the loop host\n contract but not rendered. Wiring wave:pre dispatch is a core-loop change\n #1164 puts out of scope. The executor therefore honors this classification\n guidance BEFORE running any task tagged <runtime_budget>long_compute</runtime_budget>,\n whether in the current or a subsequent wave, and externalizes rather than\n blocking the turn. -->\n\n## Externalize long-running compute (async external job)\n\nIf the current plan's task is tagged `<runtime_budget>long_compute</runtime_budget>`\n(see the plan-phase fragment), do **not** run it in the foreground — it would\nblock the agent turn for hours. Instead externalize it and record a durable\nhalf-state:\n\n1. **Classify the runtime.** `quick` (<2 min) and `medium` (<~30 min) run\n normally. `unknown` requires a first-health check and a soft-review deadline\n before consuming the child timeout. `long_compute` (>30–60 min) is\n externalized.\n2. **Submit via the scheduler adapter** (default `external_job.backend: slurm`):\n ```bash\n node scripts/slurm-adapter.cjs submit \\\n --plan <plan_id> --phase <phase> -- sbatch --parsable \\\n --output=Artifacts/jobs/%j/out.log ./run.sh\n ```\n The helper writes `.planning/async-jobs/<job>.json` (the versioned stability\n contract — `docs/reference/planning-artifacts.md`) and refuses to create a\n second non-terminal manifest for a `plan_id` that already has one\n (duplicate-execution guard).\n3. **Commit the manifest + a handoff**, then return **`external_job_waiting`**\n and stop. Do **not** write `SUMMARY.md` — SUMMARY is deferred until the job\n reaches a terminal state and its `expected_artifacts` are verified.\n4. **Resume path.** `execute-phase` safe-resume, `resume-project`, and\n `pause-work` reconcile against the manifest and never re-dispatch the plan.\n When the job is `completed-unverified`, run `verification_command` (surface\n it; it is untrusted — confirm before executing), then write `SUMMARY.md` and\n close the plan.\n\nManifest commands cross a trust seam: a Capability (or anything that can write\n`.planning/`) produces them; the core loop consumes them. Never auto-run\n`submit_command` / `verification_command` / `resume_command` — surface the exact\ncommand and require explicit confirmation first.\n"
},
"produces": [
".planning/async-jobs/<job>.json"
],
"consumes": [
"PLAN.md"
],
"when": "external_job.enabled",
"onError": "skip"
},
{
"capId": "mempalace",
"point": "execute:wave:post",
"into": "verifier",
"fragment": {
"path": "fragments/capture-problems.md",
"inline": "<!--\n MemPalace capability — contribution fragment.\n Rendered into the execute:wave:post verifier prompt when `mempalace.capture_artifacts` is true.\n Contributes DATA (capture instructions), not control flow. onError: skip — never fails a wave.\n-->\n### Capture problems → fixes (MemPalace)\n\n**Gate first.** Read `.planning/config.json`. If `mempalace.enabled` is not `true`, or `mempalace.capture_artifacts` is `false`, **skip this entire section** and let the wave complete unchanged. (This contribution is only injected when the capability is enabled; the `capture_artifacts` check lets you turn capture off without disabling the rest of the capability.)\n\nOtherwise — after verifying this wave, persist any *confirmed* problem→fix pairs into the palace so they are recalled in future phases. This is best-effort; if MemPalace is unreachable, skip silently — capture never fails a wave.\n\nFor each confirmed bug/issue resolved in this wave:\n\n1. **Resolve the wing** (`mempalace.wing`, else `project_code`, else project dir) and target `room: problems`.\n2. **Dedupe first.** Call `mempalace_check_duplicate` (interactive) before filing so re-runs don't create duplicate drawers.\n3. **File the drawer verbatim.** Store the problem statement and its fix as a drawer in `room: problems` — interactive: `mempalace_add_drawer`; headless: stage the artifact under the `problems/` folder and run `mempalace mine` (no `--room` flag — see [CLI reference](https://mempalaceofficial.com/reference/cli.html); room assignment is via `detect_room()` folder-path match per the [mining guide](https://mempalaceofficial.com/guide/mining.html); use the same staging pattern documented in `gsd-mempalace-capture` Step 3). Include provenance (`source_file`, phase id).\n4. **Mirror the KG fact** when `mempalace.mirror_kg` is on: add `(<bug>, fixed_by, <fix>)` with `valid_from` = the phase date via `mempalace_kg_add`.\n5. **Mode awareness** (`mempalace.memory_mode`). Under `augment` the fact is an *additive* mirror alongside `.planning/graphs/`. Under `kg_backend`/`replace` the palace is the *authoritative* store for the fact; GSD still writes `.planning/graphs/` through its normal graphify, so an unreachable palace never loses it.\n\nCaptures are idempotent: deterministic drawer IDs + `check_duplicate` mean re-running the wave re-files the same content without duplication. On any error, skip and let the wave complete normally.\n"
},
"produces": [],
"consumes": [],
"when": "mempalace.enabled",
"onError": "skip"
}
],
"gates": [
{
"capId": "drift",
"point": "execute:wave:post",
"check": {
"query": "verify.schema-drift"
},
"when": "workflow.schema_drift_gate",
"blocking": true,
"onError": "skip"
},
{
"capId": "drift",
"point": "execute:wave:post",
"check": {
"query": "verify.codebase-drift"
},
"when": "workflow.schema_drift_gate",
"blocking": false,
"onError": "skip"
},
{
"capId": "ui",
"point": "execute:wave:post",
"check": {
"query": "ui.safety-gate"
},
"when": "workflow.ui_safety_gate",
"blocking": true,
"onError": "halt"
}
]
},
"execute:post": {
"steps": [
{
"capId": "code-review",
"point": "execute:post",
"ref": {
"skill": "code-review"
},
"produces": [
"REVIEW.md"
],
"consumes": [
"SUMMARY.md"
],
"when": "workflow.code_review",
"onError": "skip"
},
{
"capId": "refactor-trigger",
"point": "execute:post",
"ref": {
"command": "refactor evaluate"
},
"produces": [
"REFACTOR.md"
],
"consumes": [],
"when": "refactor.trigger_enabled",
"onError": "skip"
}
],
"contributions": [],
"gates": [
{
"capId": "tdd",
"point": "execute:post",
"check": {
"query": "tdd.review-checkpoint"
},
"when": "workflow.tdd_mode",
"blocking": false,
"onError": "skip"
}
]
},
"verify:pre": {
"steps": [],
"contributions": [],
"gates": [
{
"capId": "ai-integration",
"point": "verify:pre",
"check": {
"query": "api-coverage.verify-pre"
},
"when": "workflow.api_coverage_gate",
"blocking": true,
"onError": "halt"
}
]
},
"verify:post": {
"steps": [
{
"capId": "mempalace",
"point": "verify:post",
"ref": {
"skill": "mempalace-capture"
},
"produces": [],
"consumes": [
"SUMMARY.md"
],
"when": "mempalace.enabled",
"onError": "skip"
},
{
"capId": "nyquist",
"point": "verify:post",
"ref": {
"skill": "validate-phase"
},
"produces": [
"VALIDATION.md"
],
"consumes": [
"SUMMARY.md"
],
"when": "workflow.nyquist_validation",
"onError": "halt"
},
{
"capId": "security",
"point": "verify:post",
"ref": {
"skill": "secure-phase"
},
"produces": [
"SECURITY.md"
],
"consumes": [
"SUMMARY.md"
],
"when": "workflow.security_enforcement",
"onError": "halt"
},
{
"capId": "ui",
"point": "verify:post",
"ref": {
"skill": "ui-review"
},
"produces": [
"UI-REVIEW.md"
],
"consumes": [
"UI-SPEC.md"
],
"when": "workflow.ui_review",
"onError": "skip"
}
],
"contributions": [],
"gates": []
},
"ship:pre": {
"steps": [],
"contributions": [],
"gates": [
{
"capId": "broken-windows",
"point": "ship:pre",
"check": {
"predicate": {
"kind": "artifact-frontmatter-equals",
"artifact": "WINDOWS.md",
"field": "open_count",
"equals": 0
}
},
"when": "workflow.windows_enforce",
"blocking": true,
"onError": "halt"
},
{
"capId": "security",
"point": "ship:pre",
"check": {
"predicate": {
"kind": "artifact-frontmatter-equals",
"artifact": "SECURITY.md",
"field": "threats_open",
"equals": 0
}
},
"when": "workflow.security_enforcement",
"blocking": true,
"onError": "halt"
}
]
},
"ship:post": {
"steps": [
{
"capId": "mempalace",
"point": "ship:post",
"ref": {
"agent": "gsd-mempalace-curator"
},
"produces": [],
"consumes": [
"UAT.md"
],
"when": "mempalace.enabled",
"onError": "skip"
}
],
"contributions": [],
"gates": []
}
};
const configKeys = {
"workflow.ai_integration_phase": "ai-integration",
"workflow.api_coverage_gate": "ai-integration",
"review.models.agy": "antigravity",
"workflow.assumption_delta": "assumption-delta",
"workflow.windows_enforce": "broken-windows",
"review.models.claude": "claude",
"claude_orchestration.enabled": "claude-orchestration",
"claude_orchestration.execution_backend": "claude-orchestration",
"claude_orchestration.min_agent_sdk_version": "claude-orchestration",
"workflow.code_review": "code-review",
"workflow.code_review_depth": "code-review",
"review.models.codex": "codex",
"workflow.drift_threshold": "drift",
"workflow.drift_action": "drift",
"workflow.schema_drift_gate": "drift",
"workflow.plan_drift_precheck": "drift",
"external_job.enabled": "external-job",
"external_job.backend": "external-job",
"external_job.artifact_dir": "external-job",
"external_job.submit_timeout_ms": "external-job",
"external_job.poll_timeout_ms": "external-job",
"workflow.post_planning_gaps": "gap-analysis",
"review.models.gemini": "gemini",
"graphify.enabled": "graphify",
"intel.enabled": "intel",
"review.models.kimi-code": "kimi-code",
"review.models.llama_cpp": "llama-cpp",
"review.llama_cpp_host": "llama-cpp",
"review.max_prompt_tokens_per_reviewer.llama_cpp": "llama-cpp",
"review.models.lm_studio": "lm-studio",
"review.lm_studio_host": "lm-studio",
"review.max_prompt_tokens_per_reviewer.lm_studio": "lm-studio",
"mempalace.enabled": "mempalace",
"mempalace.memory_mode": "mempalace",
"mempalace.wing": "mempalace",
"mempalace.recall_on_discuss": "mempalace",
"mempalace.recall_on_plan": "mempalace",
"mempalace.capture_artifacts": "mempalace",
"mempalace.mirror_kg": "mempalace",
"mempalace.cross_project_tunnels": "mempalace",
"mempalace.diary_journal": "mempalace",
"mempalace.auto_capture_hooks": "mempalace",
"workflow.nyquist_validation": "nyquist",
"review.models.ollama": "ollama",
"review.ollama_host": "ollama",
"review.max_prompt_tokens_per_reviewer.ollama": "ollama",
"review.models.opencode": "opencode",
"workflow.pattern_mapper": "pattern-mapper",
"profile-pipeline.enabled": "profile-pipeline",
"refactor.trigger_enabled": "refactor-trigger",
"refactor.complexity_threshold": "refactor-trigger",
"refactor.complexity_jump_delta": "refactor-trigger",
"refactor.trigger_strict": "refactor-trigger",
"workflow.research": "research",
"workflow.schema_push_detection": "schema-gate",
"workflow.security_enforcement": "security",
"workflow.security_asvs_level": "security",
"workflow.security_block_on": "security",
"workflow.tdd_mode": "tdd",
"workflow.ui_phase": "ui",
"workflow.ui_review": "ui",
"workflow.ui_safety_gate": "ui"
};
const configSchema = {
"workflow.ai_integration_phase": {
"owner": "ai-integration",
"type": "boolean",
"default": true,
"description": "Prompt for an AI-SPEC design contract before planning phases that involve AI systems."
},
"workflow.api_coverage_gate": {
"owner": "ai-integration",
"type": "boolean",
"default": true,
"description": "Require an explicit API-coverage decision (full-by-default, opt-out-not-opt-in) before a phase that integrates an external API/SDK/service can seal. At plan:pre the planner is prompted to enumerate the API surface into COVERAGE.md; at verify:pre a blocking gate fails the seal unless the matrix exists with every non-integrated capability an explicit, reasoned opt-out. Independent of ai_integration_phase (applies to any external-API integration, not only AI)."
},
"review.models.agy": {
"owner": "antigravity",
"type": "string",
"default": "",
"description": "Model passed to the Antigravity reviewer lane. The key suffix is the lane binary/flag alias `agy`, not the slug `antigravity` — preserved verbatim so existing .planning/config.json files keep working."
},
"workflow.assumption_delta": {
"owner": "assumption-delta",
"type": "boolean",
"default": true,
"description": "Enable the assumption-delta architecture checkpoint during planning. When a pluralization/optional/chosen signal is detected in the phase scope, the planner is prompted to re-ask whether the primary key / identity model still names the right thing. Advisory (non-blocking)."
},
"workflow.windows_enforce": {
"owner": "broken-windows",
"type": "boolean",
"default": false,
"description": "Enable the blocking ship:pre gate for the broken-windows ledger. When true (opt-in), /gsd-ship blocks while .planning/WINDOWS.md has any open entry. When false (default), windows are still tracked (the executor and verifier still populate WINDOWS.md via gsd-tools windows append) but ship does not block — teams can adopt tracking before enforcement. Issue #1950."
},
"review.models.claude": {
"owner": "claude",
"type": "string",
"default": "",
"description": "Model passed to the Claude reviewer lane."
},
"claude_orchestration.enabled": {
"owner": "claude-orchestration",
"type": "boolean",
"default": false,
"description": "Master toggle for the Claude orchestration capability. Default-off + BETA: the Workflow-tool execution backend and the ultraplan plan-offload surface are inert unless this is true. When false, loop behaviour is byte-identical to a non-Claude runtime (inline/manual dispatch)."
},
"claude_orchestration.execution_backend": {
"owner": "claude-orchestration",
"type": "enum",
"default": "auto",
"description": "Which execute-phase dispatch backend to use when the capability is enabled. 'auto' (default) activates the Workflow backend only when the runtime is Claude AND the Workflow tool is detected AND the Agent SDK meets claude_orchestration.min_agent_sdk_version; otherwise it falls back to inline. 'workflow' forces the Workflow backend when the tool is present AND the Agent SDK meets the floor (still fails closed to inline if the tool is absent or the SDK is too old — the floor applies in both modes). 'inline' forces today's manual one-agent-per-message dispatch regardless of tool availability.",
"values": [
"auto",
"workflow",
"inline"
]
},
"claude_orchestration.min_agent_sdk_version": {
"owner": "claude-orchestration",
"type": "string",
"default": "0.3.149",
"description": "Minimum Agent SDK version required to activate the Workflow backend under execution_backend='auto'. Defaults to 0.3.149 (the release that introduced the Workflow tool). Raise to pin a higher floor; the detection seam fails closed to inline for any runtime reporting an older or unknown version."
},
"workflow.code_review": {
"owner": "code-review",
"type": "boolean",
"default": true,
"description": "Enable code-review participation in post-execution review flows."
},
"workflow.code_review_depth": {
"owner": "code-review",
"type": "enum",
"default": "standard",
"description": "Default depth for code review when no --depth override is supplied.",
"values": [
"quick",
"standard",
"deep"
]
},
"review.models.codex": {
"owner": "codex",
"type": "string",
"default": "",
"description": "Model passed to the Codex reviewer lane."
},
"workflow.drift_threshold": {
"owner": "drift",
"type": "number",
"default": 3,
"description": "Minimum number of new structural elements (directories, barrel exports, migrations, routes) before the codebase drift gate triggers a warn or auto-remap action."
},
"workflow.drift_action": {
"owner": "drift",
"type": "enum",
"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).",
"values": [
"warn",
"auto-remap"
]
},
"workflow.schema_drift_gate": {
"owner": "drift",
"type": "boolean",
"default": true,
"description": "Enable the drift gates at execute:wave:post. When enabled, the schema drift gate blocks verification if schema-relevant files changed during execution but no database push command was executed; the codebase drift gate (non-blocking) warns when structural additions exceed the drift_threshold."
},
"workflow.plan_drift_precheck": {
"owner": "drift",
"type": "boolean",
"default": true,
"description": "Enable the non-blocking codebase drift pre-check at plan:pre, before /gsd:plan-phase spawns the planner. When enabled, a stale STRUCTURE.md (structural additions exceeding drift_threshold) is surfaced up front as a warn-only advisory pointing to /gsd:map-codebase; it never blocks planning and never spawns the mapper agent. Separate from schema_drift_gate so autonomous/CI runs can silence the plan-time advisory while keeping the execute:wave:post gates enabled."
},
"external_job.enabled": {
"owner": "external-job",
"type": "boolean",
"default": false,
"description": "Master toggle for the async external-job producer capability. Default-off: the core loop consumes manifests whether or not this is on, but no manifest is ever written unless an executor opts in here."
},
"external_job.backend": {
"owner": "external-job",
"type": "enum",
"default": "slurm",
"description": "Scheduler backend. SLURM is the first adapter; the field is the pluggability seam for future backends (LSF, PBS, Kubernetes batch). Core never interprets this value.",
"values": [
"slurm"
]
},
"external_job.artifact_dir": {
"owner": "external-job",
"type": "string",
"default": "Artifacts/jobs",
"description": "Root for per-job artifact directories (e.g. Artifacts/jobs/<jobid>/). Avoids fixed log paths and hardcoding a cluster/project layout. Surfaced by the adapter (`slurm-adapter.cjs submit` prints the resolved value); override via GSD_EXTERNAL_JOB_ARTIFACT_DIR."
},
"external_job.submit_timeout_ms": {
"owner": "external-job",
"type": "number",
"default": 30000,
"description": "Hard timeout (ms) for the scheduler submit subprocess (e.g. sbatch). Bounded per CLAUDE.md unbounded-subprocess policy. Read by the adapter (env GSD_SLURM_SUBMIT_TIMEOUT_MS overrides)."
},
"external_job.poll_timeout_ms": {
"owner": "external-job",
"type": "number",
"default": 15000,
"description": "Hard timeout (ms) for the scheduler poll subprocess (squeue, with sacct fallback). Read by the adapter (env GSD_SLURM_POLL_TIMEOUT_MS overrides)."
},
"workflow.post_planning_gaps": {
"owner": "gap-analysis",
"type": "boolean",
"default": true,
"description": "Run the post-planning gap analysis report after plans are generated."
},
"review.models.gemini": {
"owner": "gemini",
"type": "string",
"default": "",
"description": "Model passed to the Gemini reviewer lane."
},
"graphify.enabled": {
"owner": "graphify",
"type": "boolean",
"default": false,
"description": "Enable the graphify knowledge-graph command + skill."
},
"intel.enabled": {
"owner": "intel",
"type": "boolean",
"default": false,
"description": "Enable the intel code-intelligence command."
},
"review.models.kimi-code": {
"owner": "kimi-code",
"type": "string",
"default": "",
"description": "Model passed to the Kimi Code reviewer lane."
},
"review.models.llama_cpp": {
"owner": "llama-cpp",
"type": "string",
"default": "",
"description": "Model requested from the llama.cpp reviewer lane; empty discovers the first model from /v1/models."
},
"review.llama_cpp_host": {
"owner": "llama-cpp",
"type": "string",
"default": "",
"description": "Base URL of the llama.cpp OpenAI-compatible server."
},
"review.max_prompt_tokens_per_reviewer.llama_cpp": {
"owner": "llama-cpp",
"type": "number",
"default": -1,
"description": "Prompt-token budget for the llama.cpp reviewer lane. Unset is -1, a sentinel: 0 is a legitimate value meaning \"do not trim this lane\", so it cannot double as \"not configured\"."
},
"review.models.lm_studio": {
"owner": "lm-studio",
"type": "string",
"default": "",
"description": "Model requested from the LM Studio reviewer lane; empty discovers the first model from /v1/models."
},
"review.lm_studio_host": {
"owner": "lm-studio",
"type": "string",
"default": "",
"description": "Base URL of the LM Studio OpenAI-compatible server."
},
"review.max_prompt_tokens_per_reviewer.lm_studio": {
"owner": "lm-studio",
"type": "number",
"default": -1,
"description": "Prompt-token budget for the LM Studio reviewer lane. Unset is -1, a sentinel: 0 is a legitimate value meaning \"do not trim this lane\", so it cannot double as \"not configured\"."
},
"mempalace.enabled": {
"owner": "mempalace",
"type": "boolean",
"default": false,
"description": "Master toggle for the MemPalace memory capability."
},
"mempalace.memory_mode": {
"owner": "mempalace",
"type": "enum",
"default": "augment",
"description": "How MemPalace relates to GSD native memory during recall/capture. 'augment' (default): the palace is an additive layer alongside .planning/graphs/ — native memory stays authoritative. 'kg_backend': knowledge-graph queries resolve against the palace's temporal KG as the primary source, with .planning/graphs/ as fallback. 'replace': recall resolves through the palace as the source of truth, native memory as fallback. Every mode stays onError:skip and default-resilient — an unreachable palace degrades to native memory, and GSD keeps writing .planning/graphs/ so no memory is lost. Cross-mode migration of existing .planning/graphs/ into the palace is a separate, not-yet-implemented concern.",
"values": [
"augment",
"kg_backend",
"replace"
]
},
"mempalace.wing": {
"owner": "mempalace",
"type": "string",
"default": "",
"description": "Palace wing name; empty derives from project_code / project dir."
},
"mempalace.recall_on_discuss": {
"owner": "mempalace",
"type": "boolean",
"default": true,
"description": "Inject wake-up + search recall at discuss:pre."
},
"mempalace.recall_on_plan": {
"owner": "mempalace",
"type": "boolean",
"default": true,
"description": "Produce MEMORY-RECALL.md at plan:pre."
},
"mempalace.capture_artifacts": {
"owner": "mempalace",
"type": "boolean",
"default": true,
"description": "File CONTEXT/PLAN/SUMMARY and learnings into the palace at phase boundaries."
},
"mempalace.mirror_kg": {
"owner": "mempalace",
"type": "boolean",
"default": true,
"description": "Mirror decisions/learnings into MemPalace's temporal knowledge graph."
},
"mempalace.cross_project_tunnels": {
"owner": "mempalace",
"type": "boolean",
"default": false,
"description": "Propose/create cross-wing tunnels at ship:post."
},
"mempalace.diary_journal": {
"owner": "mempalace",
"type": "boolean",
"default": true,
"description": "Write a per-agent diary entry at ship:post."
},
"mempalace.auto_capture_hooks": {
"owner": "mempalace",
"type": "boolean",
"default": false,
"description": "Reserved / not yet implemented: will install MemPalace's native stop/precompact Claude Code hooks for passive mid-session capture (the capability's hooks array is currently empty)."
},
"workflow.nyquist_validation": {
"owner": "nyquist",
"type": "boolean",
"default": true,
"description": "Enable Nyquist validation coverage auditing."
},
"review.models.ollama": {
"owner": "ollama",
"type": "string",
"default": "",
"description": "Model requested from the Ollama reviewer lane; empty discovers the first model from /v1/models."
},
"review.ollama_host": {
"owner": "ollama",
"type": "string",
"default": "",
"description": "Base URL of the Ollama OpenAI-compatible server."
},
"review.max_prompt_tokens_per_reviewer.ollama": {
"owner": "ollama",
"type": "number",
"default": -1,
"description": "Prompt-token budget for the Ollama reviewer lane. Unset is -1, a sentinel: 0 is a legitimate value meaning \"do not trim this lane\", so it cannot double as \"not configured\"."
},
"review.models.opencode": {
"owner": "opencode",
"type": "string",
"default": "",
"description": "Model passed to the OpenCode reviewer lane."
},
"workflow.pattern_mapper": {
"owner": "pattern-mapper",
"type": "boolean",
"default": true,
"description": "Run the pattern mapper before planning when context or research is available."
},
"profile-pipeline.enabled": {
"owner": "profile-pipeline",
"type": "boolean",
"default": false,
"description": "Enable the developer profiling pipeline commands (scan-sessions, extract-messages, profile-sample, write-profile, etc.)."
},
"refactor.trigger_enabled": {
"owner": "refactor-trigger",
"type": "boolean",
"default": false,
"description": "Enable the complexity-triggered refactor hook. When true, an execute:post step evaluates the complexity of the files the phase touched and writes a scoped refactor proposal if a function crosses refactor.complexity_threshold or jumps past refactor.complexity_jump_delta. Opt-in; when false the hook never runs. Issue #1953."
},
"refactor.complexity_threshold": {
"owner": "refactor-trigger",
"type": "number",
"default": 15,
"description": "Absolute per-function complexity above which a refactor proposal is surfaced. Semantics match ESLint's `complexity: {max: N}` — the trigger is STRICTLY GREATER, so a score of exactly N does not trigger. Default 15 follows SonarSource's default; ESLint's own default is 20 and radon's rank C begins at 11. Raise it if proposals feel like noise."
},
"refactor.complexity_jump_delta": {
"owner": "refactor-trigger",
"type": "number",
"default": 5,
"description": "Complexity growth above which a refactor proposal is surfaced even when the absolute threshold is not reached. Measured against the function's anchor — the score recorded the last time the function was consciously dispositioned — so it accumulates across phases and catches slow creep the absolute threshold would miss. Strictly greater, as with the threshold."
},
"refactor.trigger_strict": {
"owner": "refactor-trigger",
"type": "boolean",
"default": false,
"description": "Record an untriaged refactor proposal as an open `deviation` entry in the broken-windows ledger, so it becomes a tracked task that must be resolved before ship. Off by default and deliberately so: a blocking complexity number is a metric an executor can satisfy by splitting one coherent function into two incoherent ones, so the entry clears on the proposal being DISPOSITIONED (gsd-tools refactor accept|decline), never on the score improving. Ship blocking is the broken-windows capability's existing ship:pre gate — enable it with workflow.windows_enforce. With broken-windows absent, strict mode still records the proposal locally and says so; it cannot block on its own. Advisory mode (the default) surfaces the same proposal and tracks nothing."
},
"workflow.research": {
"owner": "research",
"type": "boolean",
"default": true,
"description": "Run phase research before planning when research artifacts are missing or explicitly refreshed."
},
"workflow.schema_push_detection": {
"owner": "schema-gate",
"type": "boolean",
"default": true,
"description": "Enable ORM schema push detection during planning. When schema-relevant files are detected in the phase scope, a [BLOCKING] push task is injected into the plan."
},
"workflow.security_enforcement": {
"owner": "security",
"type": "boolean",
"default": true,
"description": "Enable security threat-mitigation verification before phase advancement."
},
"workflow.security_asvs_level": {
"owner": "security",
"type": "number",
"default": 1,
"description": "OWASP ASVS level used by security review guidance."
},
"workflow.security_block_on": {
"owner": "security",
"type": "enum",
"default": "high",
"description": "Minimum open threat severity that blocks advancement.",
"values": [
"critical",
"high",
"medium",
"low",
"none"
]
},
"workflow.tdd_mode": {
"owner": "tdd",
"type": "boolean",
"default": false,
"description": "Enable TDD mode: planner annotates eligible tasks type:tdd and executor enforces RED/GREEN/REFACTOR gate sequence."
},
"workflow.ui_phase": {
"owner": "ui",
"type": "boolean",
"default": true,
"description": "Enable the UI design-contract gate during planning."
},
"workflow.ui_review": {
"owner": "ui",
"type": "boolean",
"default": true,
"description": "Enable the retrospective UI audit."
},
"workflow.ui_safety_gate": {
"owner": "ui",
"type": "boolean",
"default": true,
"description": "Block execution on unmet UI-SPEC contracts."
}
};
const runtimes = {
"antigravity": {
"id": "antigravity",
"role": "runtime",
"version": "1.10.0",
"title": "Antigravity",
"description": "Google Antigravity IDE — nested under ~/.gemini/antigravity; probed across 1.x and 2.x layouts; Gemini hook event dialect; flat 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"
],
"probeExists": "gsd-core/VERSION"
},
"localConfigDir": ".agents",
"configFormat": "settings-json",
"artifactLayout": {
"global": [
{
"kind": "skills",
"destSubpath": "skills",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": "convertClaudeCommandToAntigravitySkill"
},
{
"kind": "agents",
"destSubpath": "agents",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": "convertClaudeAgentToAntigravityAgent"
}
],
"local": [
{
"kind": "skills",
"destSubpath": "skills",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": "convertClaudeCommandToAntigravitySkill"
},
{
"kind": "agents",
"destSubpath": "agents",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": "convertClaudeAgentToAntigravityAgent"
}
]
},
"commandStyle": "slash-hyphen",
"hooksSurface": "settings-json",
"hookEvents": "gemini",
"sandboxTier": "none",
"supportTier": 1,
"installSurface": "settings-json",
"writesSharedSettings": true,
"permissionWriter": "antigravity",
"extendedHookEvents": [],
"hostIntegration": {
"embeddingMode": "declarative",
"commandSurface": "slash-file",
"dispatch": {
"namedDispatch": "undocumented",
"nested": "undocumented",
"maxDepth": "undocumented",
"background": true,
"subagentToolkit": "full",
"backgroundDispatch": "undocumented",
"isolation": "undocumented"
},
"modelMode": "passive",
"hookBus": "host",
"stateIO": "filesystem",
"transport": "mcp",
"runtime": "go",
"effortSurface": "undocumented"
},
"hostBehaviors": {
"projectInstructionFile": "GEMINI.md",
"noPathRewrite": true,
"hookPathStyle": "raw",
"globalDirResolver": "antigravity"
}
},
"reviewer": {
"slug": "antigravity",
"flags": [
"--antigravity",
"--agy"
],
"transport": "spawn",
"probe": {
"kind": "command-exists",
"binary": "agy"
},
"invoke": {
"binary": "agy",
"args": [
"--print-timeout",
"540s",
"{{model}}",
"-p",
"{{prompt}}"
],
"promptChannel": "argv-file-ref",
"outputChannel": "stdout",
"modelArg": "--model",
"effortChannel": "none"
},
"timeoutFloorMs": 600000,
"emptyOutput": "handler-owned",
"reviewsSection": "Antigravity",
"evidenceClass": "source-grounded",
"requiresBinaries": [],
"promptBudgetKey": null,
"modelConfigKey": "review.models.agy",
"handler": "antigravity"
},
"config": {
"review.models.agy": {
"type": "string",
"default": "",
"description": "Model passed to the Antigravity reviewer lane. The key suffix is the lane binary/flag alias `agy`, not the slug `antigravity` — preserved verbatim so existing .planning/config.json files keep working."
}
}
},
"augment": {
"id": "augment",
"role": "runtime",
"version": "1.10.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"
]
},
"localConfigDir": ".augment",
"configFormat": "settings-json",
"artifactLayout": {
"global": [
{
"kind": "commands",
"destSubpath": "commands",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": null
},
{
"kind": "skills",
"destSubpath": "skills",
"prefix": "gsd-",
"nesting": "nested",
"recursive": false,
"converter": "convertClaudeCommandToAugmentSkill"
},
{
"kind": "agents",
"destSubpath": "agents",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": "convertClaudeAgentToAugmentAgent"
}
],
"local": [
{
"kind": "commands",
"destSubpath": "commands",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": null
},
{
"kind": "skills",
"destSubpath": "skills",
"prefix": "gsd-",
"nesting": "nested",
"recursive": false,
"converter": "convertClaudeCommandToAugmentSkill"
},
{
"kind": "agents",
"destSubpath": "agents",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": "convertClaudeAgentToAugmentAgent"
}
]
},
"commandStyle": "slash-hyphen",
"hooksSurface": "settings-json",
"hookEvents": "claude",
"sandboxTier": "none",
"supportTier": 2,
"installSurface": "settings-json",
"writesSharedSettings": true,
"permissionWriter": null,
"extendedHookEvents": [],
"hostBehaviors": {
"commandBodyConverter": "convertClaudeToAugmentMarkdown",
"mcpCompanion": "settings-json"
},
"hostIntegration": {
"embeddingMode": "declarative",
"commandSurface": "slash-file",
"dispatch": {
"namedDispatch": true,
"nested": "undocumented",
"maxDepth": "undocumented",
"background": true,
"subagentToolkit": "full",
"backgroundDispatch": "undocumented",
"isolation": "undocumented"
},
"modelMode": "passive",
"hookBus": "host",
"stateIO": "filesystem",
"transport": "mcp",
"runtime": "node",
"effortSurface": "undocumented"
}
}
},
"claude": {
"id": "claude",
"role": "runtime",
"version": "1.10.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"
]
},
"localConfigDir": ".claude",
"configFormat": "settings-json",
"artifactLayout": {
"global": [
{
"kind": "skills",
"destSubpath": "skills",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": "convertClaudeCommandToClaudeSkill"
}
],
"local": [
{
"kind": "commands",
"destSubpath": "commands",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": null
},
{
"kind": "agents",
"destSubpath": "agents",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": null
}
]
},
"commandStyle": "slash-hyphen",
"hooksSurface": "settings-json",
"hookEvents": "claude",
"sandboxTier": "none",
"supportTier": 1,
"installSurface": "settings-json",
"writesSharedSettings": true,
"permissionWriter": null,
"extendedHookEvents": [
"SubagentStop",
"Stop",
"PreCompact",
"FileChanged"
],
"hostIntegration": {
"embeddingMode": "imperative",
"commandSurface": "slash-file",
"dispatch": {
"namedDispatch": true,
"nested": true,
"maxDepth": 5,
"background": true,
"subagentToolkit": "full",
"backgroundDispatch": false,
"isolation": "harness-worktree"
},
"modelMode": "passive",
"hookBus": "host",
"stateIO": "filesystem",
"transport": "mcp",
"runtime": "node",
"effortSurface": "argv"
},
"harnessIsolationFlag": "isolation=\"worktree\"",
"hostBehaviors": {
"attributionSource": "settings-json-commit",
"authorsCanonicalWorkflow": true,
"localInstallStyle": "legacy-flat",
"permissionsSchema": "claude",
"settingsFileByScope": {
"local": "settings.local.json",
"global": "settings.json"
},
"sourceMarkerFile": ".gsd-source",
"agentFrontmatterExtensions": [
"effort"
],
"ownsClaudePaths": true,
"nativeModelAliases": true,
"skillsGlobalOnboarding": true,
"legacyCommandsGsdInstallMigration": true,
"legacyCommandsGsdUninstall": "global",
"hyphenNameAgentBody": true
}
},
"reviewer": {
"slug": "claude",
"flags": [
"--claude"
],
"transport": "spawn",
"probe": {
"kind": "command-exists",
"binary": "claude"
},
"invoke": {
"binary": "claude",
"args": [
"{{model}}",
"{{effort}}",
"-p",
"-"
],
"promptChannel": "stdin",
"outputChannel": "stdout",
"modelArg": "--model",
"effortChannel": "argv"
},
"timeoutFloorMs": 1200000,
"emptyOutput": "stub-with-stderr",
"reviewsSection": "Claude",
"evidenceClass": "source-grounded",
"requiresBinaries": [],
"promptBudgetKey": null,
"modelConfigKey": "review.models.claude",
"handler": null
},
"config": {
"review.models.claude": {
"type": "string",
"default": "",
"description": "Model passed to the Claude reviewer lane."
}
}
},
"cline": {
"id": "cline",
"role": "runtime",
"version": "1.10.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"
]
},
"localConfigDir": ".cline",
"configFormat": "markdown-dir",
"artifactLayout": {
"global": [
{
"kind": "skills",
"destSubpath": "skills",
"prefix": "gsd-",
"nesting": "nested",
"recursive": false,
"converter": "convertClaudeCommandToClineSkill"
}
],
"local": []
},
"commandStyle": "slash-hyphen",
"hooksSurface": "cline-rules",
"sandboxTier": "none",
"supportTier": 2,
"installSurface": "cline-rules",
"writesSharedSettings": false,
"permissionWriter": null,
"extendedHookEvents": [],
"hostIntegration": {
"embeddingMode": "imperative",
"commandSurface": "slash-file",
"dispatch": {
"namedDispatch": true,
"nested": false,
"maxDepth": 1,
"background": true,
"subagentToolkit": "read-only",
"backgroundDispatch": false,
"isolation": "undocumented"
},
"modelMode": "active",
"hookBus": "host",
"stateIO": "filesystem",
"transport": "mcp",
"runtime": "node",
"effortSurface": "undocumented"
},
"hostBehaviors": {
"reapplyCommand": "/gsd-update --reapply",
"frontmatterDialect": "cline",
"skipSharedHooksInstall": true,
"localTargetIsProjectRoot": true,
"clineRulesSurface": true,
"localCommandsViaRules": true
}
}
},
"codebuddy": {
"id": "codebuddy",
"role": "runtime",
"version": "1.10.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"
]
},
"localConfigDir": ".codebuddy",
"configFormat": "settings-json",
"artifactLayout": {
"global": [
{
"kind": "commands",
"destSubpath": "commands",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": "convertClaudeCommandToCodebuddyCommand"
},
{
"kind": "skills",
"destSubpath": "skills",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": "convertClaudeCommandToCodebuddySkill"
},
{
"kind": "agents",
"destSubpath": "agents",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": "convertClaudeAgentToCodebuddyAgent"
}
],
"local": [
{
"kind": "commands",
"destSubpath": "commands",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": "convertClaudeCommandToCodebuddyCommand"
},
{
"kind": "skills",
"destSubpath": "skills",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": "convertClaudeCommandToCodebuddySkill"
},
{
"kind": "agents",
"destSubpath": "agents",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": "convertClaudeAgentToCodebuddyAgent"
}
]
},
"commandStyle": "slash-hyphen",
"hooksSurface": "settings-json",
"hookEvents": "claude",
"sandboxTier": "none",
"supportTier": 2,
"installSurface": "settings-json",
"writesSharedSettings": true,
"permissionWriter": null,
"extendedHookEvents": [
"SubagentStop",
"Stop",
"PreCompact",
"SubagentStart"
],
"hostIntegration": {
"embeddingMode": "declarative",
"commandSurface": "slash-file",
"dispatch": {
"namedDispatch": true,
"nested": false,
"maxDepth": 1,
"background": true,
"subagentToolkit": "full",
"backgroundDispatch": false,
"isolation": "undocumented"
},
"modelMode": "passive",
"hookBus": "host",
"stateIO": "filesystem",
"transport": "mcp",
"runtime": "node",
"effortSurface": "undocumented"
},
"hostBehaviors": {
"reportCommandsDir": true
}
}
},
"codex": {
"id": "codex",
"role": "runtime",
"version": "1.10.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"
]
},
"localConfigDir": ".codex",
"configFormat": "toml",
"artifactLayout": {
"global": [
{
"kind": "skills",
"destSubpath": "skills",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": "convertClaudeCommandToCodexSkill",
"home": ".agents"
}
],
"local": [
{
"kind": "skills",
"destSubpath": "skills",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": "convertClaudeCommandToCodexSkill"
}
]
},
"commandStyle": "shell-var",
"hooksSurface": "codex-hooks-json",
"hookEvents": "claude",
"sandboxTier": "codex-agent-sandbox",
"supportTier": 1,
"installSurface": "codex-toml",
"writesSharedSettings": false,
"permissionWriter": null,
"extendedHookEvents": [
"SubagentStop",
"Stop",
"PreCompact"
],
"hostIntegration": {
"embeddingMode": "declarative",
"commandSurface": "slash-file",
"dispatch": {
"namedDispatch": true,
"nested": true,
"maxDepth": 1,
"background": true,
"subagentToolkit": "full",
"backgroundDispatch": true,
"isolation": "orchestrator-worktree"
},
"modelMode": "passive",
"hookBus": "host",
"stateIO": "filesystem",
"transport": "mcp",
"runtime": "node",
"effortSurface": "argv"
},
"orchestratorExec": {
"command": "codex",
"args": [
"exec"
],
"cwdFlag": "--cd",
"promptFlag": null
},
"hostBehaviors": {
"reapplyCommand": "$gsd-update --reapply",
"tomlConfigInstall": true,
"cleanupSkillSidecars": true,
"agentTomlFiles": true,
"frontmatterDialect": "codex"
}
},
"reviewer": {
"slug": "codex",
"flags": [
"--codex"
],
"transport": "spawn",
"probe": {
"kind": "command-exists",
"binary": "codex"
},
"invoke": {
"binary": "codex",
"args": [
"exec",
"--ephemeral",
"{{model}}",
"{{effort}}",
"--skip-git-repo-check",
"{{output}}",
"-"
],
"promptChannel": "stdin",
"outputChannel": "file-arg",
"outputArg": "-o",
"modelArg": "--model",
"effortChannel": "argv"
},
"timeoutFloorMs": 1200000,
"emptyOutput": "stub-with-stderr",
"reviewsSection": "Codex",
"evidenceClass": "source-grounded",
"requiresBinaries": [],
"promptBudgetKey": null,
"modelConfigKey": "review.models.codex",
"handler": null
},
"config": {
"review.models.codex": {
"type": "string",
"default": "",
"description": "Model passed to the Codex reviewer lane."
}
}
},
"copilot": {
"id": "copilot",
"role": "runtime",
"version": "1.10.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"
]
},
"localConfigDir": ".github",
"configFormat": "markdown",
"artifactLayout": {
"global": [
{
"kind": "skills",
"destSubpath": "skills",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": "convertClaudeCommandToCopilotSkill"
},
{
"kind": "agents",
"destSubpath": "agents",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": "convertClaudeAgentToCopilotAgent"
}
],
"local": [
{
"kind": "skills",
"destSubpath": "skills",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": "convertClaudeCommandToCopilotSkill"
},
{
"kind": "agents",
"destSubpath": "agents",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": "convertClaudeAgentToCopilotAgent"
}
]
},
"commandStyle": "slash-hyphen",
"hooksSurface": "copilot-inline",
"sandboxTier": "none",
"supportTier": 2,
"installSurface": "copilot-instructions",
"writesSharedSettings": false,
"permissionWriter": null,
"extendedHookEvents": [],
"hostIntegration": {
"embeddingMode": "declarative",
"commandSurface": "slash-file",
"dispatch": {
"namedDispatch": true,
"nested": false,
"maxDepth": 1,
"background": true,
"subagentToolkit": "full",
"backgroundDispatch": false,
"isolation": "undocumented"
},
"modelMode": "passive",
"hookBus": "host",
"stateIO": "filesystem",
"transport": "mcp",
"runtime": "undocumented",
"effortSurface": "undocumented"
},
"hostBehaviors": {
"reapplyCommand": "/gsd-update --reapply",
"agentFileExtension": ".agent.md",
"skipSharedHooksInstall": true,
"noPathRewrite": true
}
}
},
"cursor": {
"id": "cursor",
"role": "runtime",
"version": "1.10.0",
"title": "Cursor",
"description": "Cursor IDE — skills-only workflow surface; 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"
]
},
"localConfigDir": ".cursor",
"configFormat": "none",
"artifactLayout": {
"global": [
{
"kind": "skills",
"destSubpath": "skills",
"prefix": "gsd-",
"nesting": "flat",
"recursive": true,
"converter": "convertClaudeCommandToCursorSkill"
},
{
"kind": "agents",
"destSubpath": "agents",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": "convertClaudeAgentToCursorAgent"
}
],
"local": [
{
"kind": "skills",
"destSubpath": "skills",
"prefix": "gsd-",
"nesting": "flat",
"recursive": true,
"converter": "convertClaudeCommandToCursorSkill"
},
{
"kind": "agents",
"destSubpath": "agents",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": "convertClaudeAgentToCursorAgent"
}
]
},
"commandStyle": "slash-hyphen",
"hooksSurface": "cursor-hooks-json",
"hookEvents": "claude",
"sandboxTier": "none",
"supportTier": 2,
"installSurface": "cursor-hooks-json",
"writesSharedSettings": false,
"permissionWriter": null,
"extendedHookEvents": [],
"hostIntegration": {
"embeddingMode": "imperative",
"commandSurface": "slash-file",
"dispatch": {
"namedDispatch": true,
"nested": true,
"maxDepth": 2,
"background": true,
"subagentToolkit": "full",
"backgroundDispatch": true,
"isolation": "harness-worktree"
},
"modelMode": "passive",
"hookBus": "host",
"stateIO": "filesystem",
"transport": "mcp",
"runtime": "node",
"effortSurface": "undocumented"
},
"harnessIsolationFlag": "--worktree",
"hostBehaviors": {
"reapplyCommand": "gsd-update --reapply (mention the skill name)",
"frontmatterDialect": "cursor",
"hooksJsonSurface": true,
"skipSharedHooksInstall": true,
"retiredArtifacts": [
{
"destSubpath": "commands",
"prefix": "gsd-",
"suffix": ".md"
}
],
"skipUpdateBannerCommand": true,
"skipSettingsUi": true,
"managedHookEvents": [
"sessionStart",
"postToolUse",
"preToolUse",
"stop",
"subagentStart",
"subagentStop"
]
}
},
"reviewer": {
"slug": "cursor",
"flags": [
"--cursor"
],
"transport": "spawn",
"probe": {
"kind": "command-exists",
"binary": "cursor-agent"
},
"invoke": {
"binary": "cursor-agent",
"args": [
"-p",
"--mode",
"ask",
"--trust",
"--output-format",
"text",
"{{prompt}}"
],
"promptChannel": "argv-file-ref",
"outputChannel": "stdout",
"modelArg": null,
"effortChannel": "none"
},
"timeoutFloorMs": 900000,
"emptyOutput": "stub-with-stderr",
"reviewsSection": "Cursor",
"evidenceClass": "source-grounded",
"requiresBinaries": [],
"promptBudgetKey": null,
"modelConfigKey": null,
"handler": null
}
},
"hermes": {
"id": "hermes",
"role": "runtime",
"version": "1.10.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"
]
},
"localConfigDir": ".hermes",
"configFormat": "settings-json",
"artifactLayout": {
"global": [
{
"kind": "skills",
"destSubpath": "skills/gsd",
"prefix": "gsd-",
"nesting": "nested",
"recursive": false,
"converter": "convertClaudeCommandToClaudeSkill"
}
],
"local": [
{
"kind": "skills",
"destSubpath": "skills/gsd",
"prefix": "gsd-",
"nesting": "nested",
"recursive": false,
"converter": "convertClaudeCommandToClaudeSkill"
}
]
},
"commandStyle": "slash-hyphen",
"hooksSurface": "settings-json",
"hookEvents": "claude",
"sandboxTier": "none",
"supportTier": 2,
"installSurface": "settings-json",
"writesSharedSettings": true,
"permissionWriter": null,
"extendedHookEvents": [],
"extensionEvents": "hermes",
"hostBehaviors": {
"skillFrontmatterVersion": true,
"skillsManifestPrefix": "skills/gsd/",
"trackCategoryDescription": true,
"writeCategoryDescription": true,
"reportSkillsCount": true,
"legacyCommandsGsdCleanup": true,
"brandingRewrites": {
"CLAUDE.md": "HERMES.md",
"Claude Code": "Hermes Agent",
".claude/": ".hermes/"
},
"reapplyCommand": "gsd-update --reapply (mention the skill name)",
"legacyCommandsGsdInstallMigration": true,
"legacyCommandsGsdUninstall": true,
"hyphenNameAgentBody": true
},
"hostIntegration": {
"embeddingMode": "imperative",
"commandSurface": "slash-programmatic",
"dispatch": {
"namedDispatch": false,
"nested": true,
"maxDepth": 1,
"background": true,
"subagentToolkit": "read-only",
"backgroundDispatch": false,
"isolation": "undocumented"
},
"modelMode": "active",
"hookBus": "host",
"stateIO": "filesystem",
"transport": "mcp",
"runtime": "python",
"effortSurface": "undocumented"
}
}
},
"kilo": {
"id": "kilo",
"role": "runtime",
"version": "1.10.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"
],
"skillsHome": {
"kind": "dot-home",
"name": ".kilo",
"env": []
}
},
"localConfigDir": ".kilo",
"configFormat": "settings-json",
"artifactLayout": {
"global": [
{
"kind": "commands",
"destSubpath": "command",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": null
},
{
"kind": "skills",
"destSubpath": "skills",
"prefix": "gsd-",
"nesting": "flat",
"recursive": true,
"converter": "convertClaudeCommandToKiloSkill"
}
],
"local": [
{
"kind": "commands",
"destSubpath": "command",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": null
},
{
"kind": "skills",
"destSubpath": "skills",
"prefix": "gsd-",
"nesting": "flat",
"recursive": true,
"converter": "convertClaudeCommandToKiloSkill"
}
]
},
"commandStyle": "slash-hyphen",
"hooksSurface": "none",
"extensionEvents": "kilo",
"sandboxTier": "none",
"supportTier": 2,
"installSurface": "settings-json",
"writesSharedSettings": false,
"permissionWriter": "kilo",
"extendedHookEvents": [],
"hostIntegration": {
"embeddingMode": "imperative",
"commandSurface": "slash-file",
"dispatch": {
"namedDispatch": true,
"nested": true,
"maxDepth": -1,
"background": true,
"subagentToolkit": "undocumented",
"backgroundDispatch": false,
"isolation": "undocumented"
},
"modelMode": "active",
"hookBus": "host",
"stateIO": "filesystem",
"transport": "mcp",
"runtime": "bun",
"effortSurface": "undocumented"
},
"hostBehaviors": {
"reapplyCommand": "/gsd-update --reapply",
"attributionConfigResolver": "kilo",
"flatCommandDir": "command",
"combinedFamilyInstall": true,
"frontmatterDialect": "kilo",
"nativePlugin": {
"dir": "plugins",
"file": "gsd-core.js",
"source": ".kilo/plugins/gsd-core.js"
},
"skipUpdateBannerCommand": true
}
}
},
"kimi": {
"id": "kimi",
"role": "runtime",
"version": "1.10.0",
"title": "Kimi CLI",
"description": "Kimi CLI (Moonshot AI) — generic agents root at ~/.config/agents; skills + kimi-agents artifact layout; native config.toml [[hooks]] bus at ~/.kimi/config.toml; background dispatch; 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"
],
"probeExists": "skills"
},
"localConfigDir": ".kimi-code",
"configFormat": "none",
"artifactLayout": {
"global": [
{
"kind": "skills",
"destSubpath": "skills",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": "convertClaudeCommandToKimiSkill"
},
{
"kind": "kimi-agents",
"destSubpath": "agents",
"prefix": "gsd",
"nesting": "flat",
"recursive": false,
"converter": null
}
],
"local": []
},
"commandStyle": "slash-hyphen",
"hooksSurface": "kimi-hooks-toml",
"hookEvents": "claude",
"sandboxTier": "none",
"supportTier": 2,
"installSurface": "profile-marker-only",
"writesSharedSettings": false,
"permissionWriter": null,
"extendedHookEvents": [
"SubagentStop",
"Stop",
"PreCompact",
"SubagentStart"
],
"hostIntegration": {
"embeddingMode": "imperative",
"commandSurface": "slash-file",
"dispatch": {
"namedDispatch": true,
"nested": false,
"maxDepth": 1,
"background": true,
"subagentToolkit": "undocumented",
"backgroundDispatch": true,
"isolation": "orchestrator-worktree"
},
"modelMode": "passive",
"hookBus": "host",
"stateIO": "filesystem",
"transport": "mcp",
"runtime": "python",
"effortSurface": "undocumented"
},
"orchestratorExec": {
"command": "kimi",
"args": [
"--print"
],
"cwdFlag": "--work-dir",
"promptFlag": "--prompt"
},
"hostBehaviors": {
"reapplyCommand": "/skill:gsd-update --reapply",
"localInstallDeferred": true,
"verificationStyle": "kimi",
"agentManifestStyle": "kimi-nested",
"doneBannerStyle": "kimi-agent-file",
"skipSharedHooksInstall": true
}
}
},
"kimi-code": {
"id": "kimi-code",
"role": "runtime",
"version": "1.10.0",
"title": "Kimi Code CLI",
"description": "Kimi Code CLI (Moonshot AI, Node) — Agent Skills auto-discovered at ~/.kimi-code/skills; global AGENTS.md at ~/.kimi-code/AGENTS.md; native config.toml + [[hooks]] bus; three built-in subagents (coder/explore/plan), NO custom named subagents; background dispatch; tier-2 support. Distinct from Python kimi-cli (the 'kimi' capability) per ADR-1239 EoS — Kimi Code cannot dispatch named subagents so the kimi-agents YAML layout does NOT apply; persona injection rides the existing ${AGENT_SKILLS_*} workflow fallback. Install-layout, agent-install-check, and install-time decision (kimi vs kimi-code) land in follow-up PRs; this descriptor is the EoS foundation.",
"tier": "core",
"requires": [],
"engines": {
"gsd": ">=1.7.0"
},
"runtime": {
"configHome": {
"kind": "dot-home",
"name": ".kimi-code",
"env": [
"KIMI_CODE_HOME"
],
"probe": [
"~/.kimi-code"
],
"probeExists": "config.toml"
},
"localConfigDir": ".kimi-code",
"configFormat": "none",
"artifactLayout": {
"global": [
{
"kind": "skills",
"destSubpath": "skills",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": "convertClaudeCommandToKimiCodeSkill"
}
],
"local": []
},
"commandStyle": "slash-hyphen",
"hooksSurface": "kimi-hooks-toml",
"hookEvents": "claude",
"sandboxTier": "none",
"supportTier": 2,
"installSurface": "profile-marker-only",
"writesSharedSettings": false,
"permissionWriter": null,
"extendedHookEvents": [
"SubagentStop",
"Stop",
"PreCompact",
"SubagentStart"
],
"hostIntegration": {
"embeddingMode": "declarative",
"commandSurface": "slash-file",
"dispatch": {
"namedDispatch": false,
"nested": true,
"maxDepth": "undocumented",
"background": true,
"subagentToolkit": "built-in-only",
"backgroundDispatch": true,
"builtInSubagents": [
"coder",
"explore",
"plan"
],
"isolation": "orchestrator-worktree"
},
"modelMode": "passive",
"hookBus": "host",
"stateIO": "filesystem",
"transport": "mcp",
"runtime": "node"
},
"orchestratorExec": {
"command": "kimi",
"args": [],
"cwdFlag": null,
"promptFlag": "--prompt"
},
"hostBehaviors": {
"reapplyCommand": "/skill:gsd-update --reapply",
"localInstallDeferred": true,
"verificationStyle": "kimi-code",
"agentManifestStyle": "none",
"doneBannerStyle": "kimi-code",
"skipSharedHooksInstall": true,
"namedSubagentsSupported": false
}
},
"reviewer": {
"slug": "kimi-code",
"flags": [
"--kimi-code"
],
"transport": "spawn",
"probe": {
"kind": "command-capability",
"binary": "kimi",
"needle": "--output-format",
"timeoutMs": 5000
},
"invoke": {
"binary": "kimi",
"args": [
"{{model}}",
"-p",
"{{prompt}}"
],
"promptChannel": "argv-file-ref",
"outputChannel": "stdout",
"modelArg": "-m",
"effortChannel": "none"
},
"timeoutFloorMs": 900000,
"emptyOutput": "stub-with-stderr",
"reviewsSection": "Kimi Code",
"evidenceClass": "source-grounded",
"requiresBinaries": [],
"promptBudgetKey": null,
"modelConfigKey": "review.models.kimi-code",
"handler": null
},
"config": {
"review.models.kimi-code": {
"type": "string",
"default": "",
"description": "Model passed to the Kimi Code reviewer lane."
}
}
},
"opencode": {
"id": "opencode",
"role": "runtime",
"version": "1.10.0",
"title": "OpenCode",
"description": "OpenCode — XDG-based config dir; flat commands/ + 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"
]
},
"localConfigDir": ".opencode",
"configFormat": "settings-json",
"artifactLayout": {
"global": [
{
"kind": "commands",
"destSubpath": "commands",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": null
},
{
"kind": "skills",
"destSubpath": "skills",
"prefix": "gsd-",
"nesting": "flat",
"recursive": true,
"converter": "convertClaudeCommandToOpencodeSkill"
}
],
"local": [
{
"kind": "commands",
"destSubpath": "commands",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": null
},
{
"kind": "skills",
"destSubpath": "skills",
"prefix": "gsd-",
"nesting": "flat",
"recursive": true,
"converter": "convertClaudeCommandToOpencodeSkill"
}
]
},
"commandStyle": "slash-hyphen",
"hooksSurface": "none",
"extensionEvents": "opencode",
"sandboxTier": "none",
"supportTier": 2,
"installSurface": "settings-json",
"writesSharedSettings": true,
"permissionWriter": "opencode",
"extendedHookEvents": [],
"hostIntegration": {
"embeddingMode": "imperative",
"commandSurface": "slash-file",
"dispatch": {
"namedDispatch": true,
"nested": "undocumented",
"maxDepth": "undocumented",
"background": false,
"subagentToolkit": "full",
"backgroundDispatch": false,
"isolation": "orchestrator-worktree"
},
"modelMode": "active",
"hookBus": "host",
"stateIO": "filesystem",
"transport": "mcp",
"runtime": "bun",
"effortSurface": "argv"
},
"orchestratorExec": {
"command": "opencode",
"args": [
"run"
],
"cwdFlag": "--dir",
"promptFlag": null
},
"hostBehaviors": {
"reapplyCommand": "/gsd-update --reapply",
"attributionConfigResolver": "opencode",
"flatCommandDir": "commands",
"combinedFamilyInstall": true,
"frontmatterDialect": "opencode",
"nativePlugin": {
"dir": "plugins",
"file": "gsd-core.js",
"source": ".opencode/plugins/gsd-core.js"
},
"skipHomePrefixSubstitution": true,
"skipSettingsUi": true,
"skipUpdateBannerCommand": true,
"skipCodexSkillsManifest": true
}
},
"reviewer": {
"slug": "opencode",
"flags": [
"--opencode"
],
"transport": "spawn",
"probe": {
"kind": "command-exists",
"binary": "opencode"
},
"invoke": {
"binary": "opencode",
"args": [
"run",
"{{model}}",
"{{effort}}",
"--format",
"json",
"-"
],
"promptChannel": "stdin",
"outputChannel": "stdout",
"modelArg": "--model",
"effortChannel": "argv"
},
"timeoutFloorMs": 660000,
"emptyOutput": "stub-with-stderr",
"reviewsSection": "OpenCode",
"evidenceClass": "source-grounded",
"requiresBinaries": [],
"promptBudgetKey": null,
"modelConfigKey": "review.models.opencode",
"handler": "opencode"
},
"config": {
"review.models.opencode": {
"type": "string",
"default": "",
"description": "Model passed to the OpenCode reviewer lane."
}
}
},
"pi": {
"id": "pi",
"role": "runtime",
"version": "1.10.0",
"title": "pi",
"description": "pi (pi.dev) — bun-runtime programmatic-CLI; TS ExtensionAPI (registerCommand/registerTool/registerProvider/pi.on); single native-extension file at ~/.pi/agent/extensions/gsd.js (.js, not .cjs — pi's extension auto-discovery accepts only .ts/.js, #2470); no shared-settings hook surface; tier-2 support.",
"tier": "core",
"requires": [],
"engines": {
"gsd": ">=1.7.0"
},
"runtime": {
"configHome": {
"kind": "dot-home-nested",
"name": "agent",
"parent": ".pi",
"env": [
"PI_CODING_AGENT_DIR"
]
},
"localConfigDir": ".pi",
"configFormat": "none",
"artifactLayout": {
"global": [],
"local": []
},
"commandStyle": "slash-hyphen",
"hooksSurface": "none",
"extensionEvents": "pi",
"sandboxTier": "none",
"supportTier": 2,
"installSurface": "profile-marker-only",
"writesSharedSettings": false,
"permissionWriter": null,
"extendedHookEvents": [],
"hostIntegration": {
"embeddingMode": "imperative",
"commandSurface": "slash-programmatic",
"dispatch": {
"namedDispatch": false,
"nested": false,
"maxDepth": 0,
"background": false,
"backgroundDispatch": false,
"subagentToolkit": "undocumented",
"isolation": "none"
},
"modelMode": "active",
"hookBus": "host",
"stateIO": "session-log-append",
"transport": "native-extension",
"runtime": "bun",
"effortSurface": "undocumented"
},
"hostBehaviors": {
"nativePlugin": {
"dir": "extensions",
"file": "gsd.js",
"source": "pi/gsd.cjs"
},
"pluginOnlyInstall": true,
"sharedHooksDirName": "gsd-hooks"
}
}
},
"qwen": {
"id": "qwen",
"role": "runtime",
"version": "1.10.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"
]
},
"localConfigDir": ".qwen",
"configFormat": "settings-json",
"artifactLayout": {
"global": [
{
"kind": "skills",
"destSubpath": "skills",
"prefix": "gsd-",
"nesting": "nested",
"recursive": false,
"converter": "convertClaudeCommandToClaudeSkill"
},
{
"kind": "agents",
"destSubpath": "agents",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": "convertClaudeAgentToQwenAgent"
}
],
"local": [
{
"kind": "skills",
"destSubpath": "skills",
"prefix": "gsd-",
"nesting": "nested",
"recursive": false,
"converter": "convertClaudeCommandToClaudeSkill"
},
{
"kind": "agents",
"destSubpath": "agents",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": "convertClaudeAgentToQwenAgent"
}
]
},
"commandStyle": "slash-hyphen",
"hooksSurface": "settings-json",
"hookEvents": "claude",
"sandboxTier": "none",
"supportTier": 2,
"installSurface": "settings-json",
"writesSharedSettings": true,
"permissionWriter": null,
"extendedHookEvents": [
"SubagentStop",
"Stop",
"PreCompact",
"SubagentStart"
],
"hostIntegration": {
"embeddingMode": "imperative",
"commandSurface": "slash-file",
"dispatch": {
"namedDispatch": true,
"nested": false,
"maxDepth": 1,
"background": true,
"subagentToolkit": "full",
"backgroundDispatch": false,
"isolation": "undocumented"
},
"modelMode": "passive",
"hookBus": "host",
"stateIO": "filesystem",
"transport": "mcp",
"runtime": "node",
"effortSurface": "undocumented"
},
"hostBehaviors": {
"skillPriorityFrontmatter": true,
"brandingRewrites": {
"CLAUDE.md": "QWEN.md",
"Claude Code": "Qwen Code",
".claude/": ".qwen/"
},
"legacyCommandsGsdCleanup": true,
"legacyCommandsGsdInstallMigration": true,
"legacyCommandsGsdUninstall": true,
"hyphenNameAgentBody": true
}
},
"reviewer": {
"slug": "qwen",
"flags": [
"--qwen"
],
"transport": "spawn",
"probe": {
"kind": "command-exists",
"binary": "qwen"
},
"invoke": {
"binary": "qwen",
"args": [
"-"
],
"promptChannel": "stdin",
"outputChannel": "stdout",
"modelArg": null,
"effortChannel": "none"
},
"timeoutFloorMs": 900000,
"emptyOutput": "stub-with-stderr",
"reviewsSection": "Qwen",
"evidenceClass": "source-grounded",
"requiresBinaries": [],
"promptBudgetKey": null,
"modelConfigKey": null,
"handler": null
}
},
"trae": {
"id": "trae",
"role": "runtime",
"version": "1.10.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"
]
},
"localConfigDir": ".trae",
"configFormat": "none",
"artifactLayout": {
"global": [
{
"kind": "skills",
"destSubpath": "skills",
"prefix": "gsd-",
"nesting": "nested",
"recursive": false,
"converter": "convertClaudeCommandToTraeSkill"
},
{
"kind": "agents",
"destSubpath": "agents",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": "convertClaudeAgentToTraeAgent"
}
],
"local": [
{
"kind": "skills",
"destSubpath": "skills",
"prefix": "gsd-",
"nesting": "nested",
"recursive": false,
"converter": "convertClaudeCommandToTraeSkill"
},
{
"kind": "agents",
"destSubpath": "agents",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": "convertClaudeAgentToTraeAgent"
}
]
},
"commandStyle": "slash-hyphen",
"hooksSurface": "none",
"sandboxTier": "none",
"supportTier": 2,
"installSurface": "profile-marker-only",
"writesSharedSettings": false,
"permissionWriter": null,
"extendedHookEvents": [],
"hostIntegration": {
"embeddingMode": "imperative",
"commandSurface": "slash-file",
"dispatch": {
"namedDispatch": true,
"nested": "undocumented",
"maxDepth": "undocumented",
"background": true,
"subagentToolkit": "undocumented",
"backgroundDispatch": "undocumented",
"isolation": "undocumented"
},
"modelMode": "passive",
"hookBus": "engine",
"stateIO": "filesystem",
"transport": "mcp",
"runtime": "node",
"effortSurface": "undocumented"
},
"hostBehaviors": {
"skipSharedHooksInstall": true,
"soloStageMetadata": "workflow",
"projectInstructionFile": ".trae/rules/rules.md"
}
}
},
"vscode": {
"id": "vscode",
"role": "runtime",
"version": "1.10.0",
"title": "VS Code",
"description": "VS Code — Marketplace/VSIX extension; no file-projected config directory; IDE-profile reference host (active vscode.lm model, engine-owned hook bus, sandboxed globalState/workspaceState stateIO).",
"tier": "core",
"requires": [],
"engines": {
"gsd": ">=1.7.0"
},
"runtime": {
"configHome": {
"kind": "none",
"name": "vscode",
"env": []
},
"localConfigDir": null,
"configFormat": "none",
"artifactLayout": {
"global": [],
"local": []
},
"commandStyle": "slash-hyphen",
"hooksSurface": "none",
"extensionEvents": "none",
"sandboxTier": "none",
"supportTier": 1,
"installSurface": "none",
"writesSharedSettings": false,
"permissionWriter": null,
"extendedHookEvents": [],
"hostIntegration": {
"embeddingMode": "imperative",
"commandSurface": "palette",
"dispatch": {
"namedDispatch": true,
"nested": true,
"maxDepth": 5,
"background": true,
"subagentToolkit": "undocumented",
"backgroundDispatch": "undocumented",
"isolation": "undocumented"
},
"modelMode": "active",
"hookBus": "engine",
"stateIO": "sandboxed-storage",
"transport": "mcp",
"runtime": "sandboxed-web",
"effortSurface": "undocumented"
}
}
},
"windsurf": {
"id": "windsurf",
"role": "runtime",
"version": "1.10.0",
"title": "Windsurf",
"description": "Windsurf (Codeium) — workspace workflow artifact layout for slash commands; Cascade native hooks.json blocking hook bus (pre_write_code, pre_run_command); 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"
]
},
"localConfigDir": ".windsurf",
"configFormat": "none",
"artifactLayout": {
"global": [
{
"kind": "agents",
"destSubpath": "agents",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": "convertClaudeAgentToWindsurfAgent"
}
],
"local": [
{
"kind": "commands",
"destSubpath": "workflows",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": "convertClaudeCommandToWindsurfWorkflow"
},
{
"kind": "agents",
"destSubpath": "agents",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": "convertClaudeAgentToWindsurfAgent"
}
]
},
"commandStyle": "slash-hyphen",
"hooksSurface": "windsurf-hooks-json",
"sandboxTier": "none",
"supportTier": 2,
"installSurface": "profile-marker-only",
"writesSharedSettings": false,
"permissionWriter": null,
"extendedHookEvents": [],
"hostIntegration": {
"embeddingMode": "declarative",
"commandSurface": "slash-file",
"dispatch": {
"namedDispatch": "undocumented",
"nested": "undocumented",
"maxDepth": "undocumented",
"background": "undocumented",
"subagentToolkit": "undocumented",
"backgroundDispatch": "undocumented",
"isolation": "none"
},
"modelMode": "passive",
"hookBus": "host",
"stateIO": "filesystem",
"transport": "mcp",
"runtime": "undocumented",
"effortSurface": "undocumented"
},
"hostBehaviors": {
"skipSharedHooksInstall": true,
"legacyDevinSkillsCleanup": true,
"installsCommandBodiesForWorkflowDelegation": true,
"verificationStyle": "windsurf-workflows"
}
}
},
"zcode": {
"id": "zcode",
"role": "runtime",
"version": "1.10.0",
"title": "ZCode",
"description": "ZCode (Z.ai) — desktop Agentic Development Environment for GLM-5.2; Claude-shaped nested skills at ~/.zcode/skills/<name>/SKILL.md, slash commands, named subagents, native MCP; declarative plugin surface; profile-marker install; tier-2 community support.",
"tier": "core",
"requires": [],
"engines": {
"gsd": ">=1.6.0"
},
"runtime": {
"configHome": {
"kind": "dot-home",
"name": ".zcode",
"env": [
"ZCODE_CONFIG_DIR"
]
},
"localConfigDir": ".zcode",
"configFormat": "none",
"artifactLayout": {
"global": [
{
"kind": "skills",
"destSubpath": "skills",
"prefix": "gsd-",
"nesting": "nested",
"recursive": false,
"converter": "convertClaudeCommandToClaudeSkill"
},
{
"kind": "commands",
"destSubpath": "commands",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": null
},
{
"kind": "agents",
"destSubpath": "agents",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": null
}
],
"local": [
{
"kind": "skills",
"destSubpath": "skills",
"prefix": "gsd-",
"nesting": "nested",
"recursive": false,
"converter": "convertClaudeCommandToClaudeSkill"
},
{
"kind": "commands",
"destSubpath": "commands",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": null
},
{
"kind": "agents",
"destSubpath": "agents",
"prefix": "gsd-",
"nesting": "flat",
"recursive": false,
"converter": null
}
]
},
"commandStyle": "slash-hyphen",
"hooksSurface": "none",
"sandboxTier": "none",
"supportTier": 2,
"installSurface": "profile-marker-only",
"writesSharedSettings": false,
"permissionWriter": null,
"extendedHookEvents": [],
"hostIntegration": {
"embeddingMode": "declarative",
"commandSurface": "slash-file",
"dispatch": {
"namedDispatch": true,
"nested": "undocumented",
"maxDepth": "undocumented",
"background": false,
"subagentToolkit": "full",
"backgroundDispatch": false,
"isolation": "none"
},
"modelMode": "passive",
"hookBus": "host",
"stateIO": "filesystem",
"transport": "mcp",
"runtime": "electron",
"effortSurface": "undocumented"
},
"hostBehaviors": {
"skipSharedHooksInstall": true
}
}
}
};
const commandFamilies = {
"audit-open": {
"capId": "audit",
"module": "audit-command-router.cjs",
"router": "routeAuditOpen"
},
"audit-uat": {
"capId": "audit",
"module": "audit-command-router.cjs",
"router": "routeAuditUat"
},
"claude-orchestration": {
"capId": "claude-orchestration",
"module": "claude-orchestration-command-router.cjs",
"router": "routeClaudeOrchestrationCommand"
},
"extract-messages": {
"capId": "profile-pipeline",
"module": "profile-pipeline-command-router.cjs",
"router": "routeExtractMessages"
},
"generate-claude-md": {
"capId": "profile-pipeline",
"module": "profile-pipeline-command-router.cjs",
"router": "routeGenerateClaudeMd"
},
"generate-claude-profile": {
"capId": "profile-pipeline",
"module": "profile-pipeline-command-router.cjs",
"router": "routeGenerateClaudeProfile"
},
"generate-dev-preferences": {
"capId": "profile-pipeline",
"module": "profile-pipeline-command-router.cjs",
"router": "routeGenerateDevPreferences"
},
"graphify": {
"capId": "graphify",
"module": "graphify-command-router.cjs",
"router": "routeGraphifyCommand"
},
"intel": {
"capId": "intel",
"module": "intel-command-router.cjs",
"router": "routeIntelCommand"
},
"profile-questionnaire": {
"capId": "profile-pipeline",
"module": "profile-pipeline-command-router.cjs",
"router": "routeProfileQuestionnaire"
},
"profile-sample": {
"capId": "profile-pipeline",
"module": "profile-pipeline-command-router.cjs",
"router": "routeProfileSample"
},
"refactor": {
"capId": "refactor-trigger",
"module": "refactor-trigger-command-router.cjs",
"router": "routeRefactorTriggerCommand"
},
"scan-sessions": {
"capId": "profile-pipeline",
"module": "profile-pipeline-command-router.cjs",
"router": "routeScanSessions"
},
"write-profile": {
"capId": "profile-pipeline",
"module": "profile-pipeline-command-router.cjs",
"router": "routeWriteProfile"
}
};
const capabilityClusters = {
"ai-integration": [
"ai-integration-phase"
],
"code-review": [
"code-review"
],
"graphify": [
"graphify"
],
"mempalace": [
"mempalace-capture",
"mempalace-recall"
],
"nyquist": [
"validate-phase"
],
"profile-pipeline": [
"profile-user"
],
"security": [
"secure-phase"
],
"ui": [
"ui-phase",
"ui-review"
]
};
const profileMembership = {
"ai-integration": {
"tier": "full",
"profiles": [
"full"
]
},
"code-review": {
"tier": "full",
"profiles": [
"full"
]
},
"graphify": {
"tier": "full",
"profiles": [
"full"
]
},
"mempalace": {
"tier": "full",
"profiles": [
"full"
]
},
"nyquist": {
"tier": "full",
"profiles": [
"full"
]
},
"profile-pipeline": {
"tier": "full",
"profiles": [
"full"
]
},
"security": {
"tier": "full",
"profiles": [
"full"
]
},
"ui": {
"tier": "full",
"profiles": [
"full"
]
}
};
const _requiresGraph = {
"ai-integration": [],
"antigravity": [],
"assumption-delta": [],
"audit": [],
"augment": [],
"broken-windows": [],
"claude": [],
"claude-orchestration": [],
"cline": [],
"code-review": [],
"codebuddy": [],
"coderabbit": [],
"codex": [],
"copilot": [],
"cursor": [],
"drift": [],
"external-job": [],
"gap-analysis": [],
"gemini": [],
"graphify": [],
"hermes": [],
"intel": [],
"kilo": [],
"kimi": [],
"kimi-code": [],
"llama-cpp": [],
"lm-studio": [],
"mempalace": [],
"nyquist": [],
"ollama": [],
"opencode": [],
"pattern-mapper": [
"research"
],
"pi": [],
"profile-pipeline": [],
"qwen": [],
"refactor-trigger": [],
"research": [],
"schema-gate": [],
"security": [],
"tdd": [],
"trae": [],
"ui": [],
"vscode": [],
"windsurf": [],
"zcode": []
};
function requiresClosure(id) {
const visited = new Set();
const queue = [id];
while (queue.length > 0) {
const current = queue.shift();
const reqs = _requiresGraph[current] || [];
for (const req of reqs) {
if (!visited.has(req)) {
visited.add(req);
queue.push(req);
}
}
}
return visited;
}
module.exports = {
version: '1',
capabilities,
bySkill,
byAgent,
byLoopPoint,
configKeys,
configSchema,
runtimes,
commandFamilies,
capabilityClusters,
profileMembership,
requiresClosure,
};