feat(#1135): migrate planning hooks to capabilities (#1141)

* feat(#1135): migrate planning hooks to capabilities

* chore(#1135): add phase 6 planning capabilities changeset

* fix(#1135): satisfy lint for agent hook rendering
This commit is contained in:
Tom Boucher
2026-06-12 19:51:54 -04:00
committed by GitHub
parent fd01e7a12e
commit 4ab5c7b3f2
20 changed files with 1025 additions and 176 deletions

View File

@@ -7,6 +7,49 @@
*/
const capabilities = {
"ai-integration": {
"id": "ai-integration",
"role": "feature",
"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": [],
"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."
}
},
"steps": [
{
"point": "plan:pre",
"ref": {
"skill": "ai-integration-phase"
},
"produces": [
"AI-SPEC.md"
],
"consumes": [
"CONTEXT.md"
],
"when": "workflow.ai_integration_phase",
"onError": "skip"
}
],
"contributions": [],
"gates": []
},
"antigravity": {
"id": "antigravity",
"role": "runtime",
@@ -832,6 +875,50 @@ const capabilities = {
"extendedHookEvents": []
}
},
"pattern-mapper": {
"id": "pattern-mapper",
"role": "feature",
"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"
],
"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": []
},
"qwen": {
"id": "qwen",
"role": "runtime",
@@ -885,6 +972,48 @@ const capabilities = {
]
}
},
"research": {
"id": "research",
"role": "feature",
"title": "Phase research",
"description": "Optional phase research before planning; owns the phase researcher agent and workflow.research activation key.",
"tier": "standard",
"requires": [],
"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": []
},
"trae": {
"id": "trae",
"role": "runtime",
@@ -1070,12 +1199,19 @@ const capabilities = {
};
const bySkill = {
"ai-integration-phase": "ai-integration",
"graphify": "graphify",
"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-pattern-mapper": "pattern-mapper",
"gsd-phase-researcher": "research",
"gsd-ui-checker": "ui",
"gsd-ui-auditor": "ui"
};
@@ -1093,6 +1229,40 @@ const byLoopPoint = {
},
"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": "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",
@@ -1107,6 +1277,25 @@ const byLoopPoint = {
],
"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": [],
@@ -1198,14 +1387,23 @@ const byLoopPoint = {
};
const configKeys = {
"workflow.ai_integration_phase": "ai-integration",
"graphify.enabled": "graphify",
"intel.enabled": "intel",
"workflow.pattern_mapper": "pattern-mapper",
"workflow.research": "research",
"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."
},
"graphify.enabled": {
"owner": "graphify",
"type": "boolean",
@@ -1218,6 +1416,18 @@ const configSchema = {
"default": false,
"description": "Enable the intel code-intelligence command."
},
"workflow.pattern_mapper": {
"owner": "pattern-mapper",
"type": "boolean",
"default": true,
"description": "Run the pattern mapper before planning when context or research is available."
},
"workflow.research": {
"owner": "research",
"type": "boolean",
"default": true,
"description": "Run phase research before planning when research artifacts are missing or explicitly refreshed."
},
"workflow.ui_phase": {
"owner": "ui",
"type": "boolean",
@@ -2155,6 +2365,9 @@ const commandFamilies = {
};
const capabilityClusters = {
"ai-integration": [
"ai-integration-phase"
],
"graphify": [
"graphify"
],
@@ -2165,6 +2378,12 @@ const capabilityClusters = {
};
const profileMembership = {
"ai-integration": {
"tier": "full",
"profiles": [
"full"
]
},
"graphify": {
"tier": "full",
"profiles": [
@@ -2180,6 +2399,7 @@ const profileMembership = {
};
const _requiresGraph = {
"ai-integration": [],
"antigravity": [],
"audit": [],
"augment": [],
@@ -2196,7 +2416,11 @@ const _requiresGraph = {
"kilo": [],
"kimi": [],
"opencode": [],
"pattern-mapper": [
"research"
],
"qwen": [],
"research": [],
"trae": [],
"ui": [],
"windsurf": []

View File

@@ -411,45 +411,20 @@ If "Run discuss-phase first":
```
**Exit the plan-phase workflow. Do not continue.**
## 4.5. Check AI-SPEC
## 4.5. Resolve AI-SPEC Artifact
**Skip if:** `ai_integration_phase_enabled` from config is false, or `--skip-ai-spec` flag provided.
AI integration activation is owned by the `ai-integration` capability's `plan:pre` step hook. The plan-phase host only discovers existing artifacts here so the planner can consume them; it must not read the capability's config key directly.
```bash
AI_SPEC_FILE=$(ls "${PHASE_DIR}"/*-AI-SPEC.md 2>/dev/null | head -1)
AI_PHASE_CFG=$(gsd_run query config-get workflow.ai_integration_phase 2>/dev/null || echo "true")
AI_SPEC_PATH="${AI_SPEC_FILE}"
FRAMEWORK_LINE=""
if [ -n "$AI_SPEC_FILE" ]; then
FRAMEWORK_LINE=$(grep "Selected Framework:" "${AI_SPEC_FILE}" | head -1)
fi
```
**Skip if `AI_PHASE_CFG` is `false`.**
**If `AI_SPEC_FILE` is empty:** Check phase goal for AI keywords:
```bash
echo "${phase_goal}" | grep -qi "agent\|llm\|rag\|chatbot\|embedding\|langchain\|llamaindex\|crewai\|langgraph\|openai\|anthropic\|vector\|eval\|ai system"
```
**If AI keywords detected AND no AI-SPEC.md:**
```
◆ Note: This phase appears to involve AI system development.
Consider running /gsd:ai-integration-phase {N} before planning to:
- Select the right framework for your use case
- Research its docs and best practices
- Design an evaluation strategy
Continue planning without AI-SPEC? (non-blocking — /gsd:ai-integration-phase can be run after)
```
Use AskUserQuestion with options:
- "Continue — plan without AI-SPEC"
- "Stop — I'll run /gsd:ai-integration-phase {N} first"
If "Stop": Exit with `/gsd:ai-integration-phase {N}` reminder.
If "Continue": Proceed. (Non-blocking — planner will note AI-SPEC is absent.)
**If `AI_SPEC_FILE` is non-empty:** Extract framework for planner context:
```bash
FRAMEWORK_LINE=$(grep "Selected Framework:" "${AI_SPEC_FILE}" | head -1)
```
Pass `ai_spec_path` and `framework_line` to planner in step 7 so it can reference the AI design contract.
If `AI_SPEC_FILE` is non-empty, pass `AI_SPEC_PATH` and `FRAMEWORK_LINE` to the planner in step 8 so it can reference the AI design contract. If it is empty, the active `ai-integration` capability hook in step 5.6 handles any AI-system nudge or `/gsd:ai-integration-phase` dispatch.
## 5. Handle Research
@@ -525,41 +500,21 @@ Display banner:
```bash
PHASE_DESC=$(gsd_run query roadmap.get-phase "${PHASE}" --pick section)
if [ -z "${PLAN_PRE_HOOKS_JSON:-}" ]; then
PLAN_PRE_HOOKS_JSON=$(gsd_run loop render-hooks plan:pre --raw)
fi
```
Research prompt:
Find the active `research` step hook in `PLAN_PRE_HOOKS_JSON`. Use the hook's `fragment.inline` as the prompt template and substitute the phase fields below before spawning its declared `ref.agent`.
```markdown
<objective>
Research how to implement Phase {phase_number}: {phase_name}
Answer: "What do I need to know to PLAN this phase well?"
</objective>
<files_to_read>
- {context_path} (USER DECISIONS from /gsd:discuss-phase)
- {requirements_path} (Project requirements)
- {state_path} (Project decisions and history)
</files_to_read>
${AGENT_SKILLS_RESEARCHER}
<additional_context>
**Phase description:** {phase_description}
**Phase requirement IDs (MUST address):** {phase_req_ids}
**Project instructions:** Read ./CLAUDE.md or ./.claude/CLAUDE.md if either exists — follow project-specific guidelines
**Project skills:** Check .claude/skills/ or .agents/skills/ directory (if either exists) — read SKILL.md files, research should account for project skill patterns
</additional_context>
<output>
Write to: {phase_dir}/{phase_num}-RESEARCH.md
</output>
{research_hook.fragment.inline}
```
```
Agent(
prompt=research_prompt,
subagent_type="gsd-phase-researcher",
prompt=filled_research_hook_fragment,
subagent_type=research_hook.ref.agent,
model="{researcher_model}",
description="Research Phase {phase}"
)
@@ -646,21 +601,31 @@ Opt out: set security_enforcement: false in .planning/config.json
Continue to step 5.6. Security config is passed to the planner in step 8.
## 5.6. UI Design Contract Gate
## 5.6. Plan:Pre Capability Dispatch and UI Design Contract Gate
> Capability-driven dispatch. Resolves active `plan:pre` hooks via the capability registry; each hook's `when` condition (`workflow.ui_phase` for step hooks, `workflow.ui_safety_gate` for gate hooks) is evaluated by the registry — no inline config-get needed.
> Capability-driven dispatch. Resolves active `plan:pre` hooks via the capability registry; each hook's `when` condition is evaluated by the registry — no inline config-get needed. This section handles skill-based planning preflights such as `ai-integration`, agent-backed hooks through `ref.agent`, and the UI gate whose deterministic check comes from `check.query`.
>
> **Config semantics (cutover fix):** `workflow.ui_phase` gates UI-SPEC *generation* (step); `workflow.ui_safety_gate` gates the *planning block* (gate). Both-on = identical to OLD §5.6. Intended change: `{ui_phase:true, ui_safety_gate:false}` now auto-generates in pipelines but does NOT block manual planning (each key controls exactly what its description says).
```bash
HOOKS_JSON=$(gsd_run loop render-hooks plan:pre --raw)
PLAN_PRE_HOOKS_JSON=${PLAN_PRE_HOOKS_JSON:-$(gsd_run loop render-hooks plan:pre --raw)}
HOOKS_JSON="$PLAN_PRE_HOOKS_JSON"
```
Read the `activeHooks` array directly from `HOOKS_JSON` (in-context — do NOT invoke a shell pipeline).
Read the `activeHooks` array directly from `PLAN_PRE_HOOKS_JSON` / `HOOKS_JSON` (in-context — do NOT invoke a shell pipeline).
**Branch 1 — both toggles off (`activeHooks` is empty or absent):** Skip to step 6.
**Branch 1 — all plan:pre hooks inactive (`activeHooks` is empty or absent):** Skip to step 6.
Run whenever **any** `plan:pre` UI hook is active — including the step-only case (`workflow.ui_safety_gate` off). (`check.query` = `"ui.plan-gate"`; router normalizes dots→hyphens.)
**Generic step hook dispatch contract:** For each active entry where `kind == "step"`:
- If `ref.skill` is set, dispatch with `Skill(skill="gsd-${ref.skill}", args="${PHASE} --auto ${GSD_WS}")` when pipeline mode allows auto-chaining. Prepend `gsd-` to `ref.skill` — `ui-phase` → `gsd-ui-phase`.
- If `ref.agent` is set, dispatch with `Agent(prompt=filled_hook_fragment, subagent_type=ref.agent, model="{researcher_model}")`. Use the hook's `fragment.inline` as the prompt body and fill phase fields before spawning.
- The `research` hook is handled by §5.1's research decision. The `pattern-mapper` hook is handled by §7.8 after `RESEARCH_PATH` is known. Future plan:pre agent hooks use the same `ref.agent` fragment contract.
**AI integration capability:** If the active `ai-integration` step hook is present, `AI_SPEC_PATH` is empty, and the phase goal contains AI keywords (`agent`, `llm`, `rag`, `chatbot`, `embedding`, `langchain`, `llamaindex`, `crewai`, `langgraph`, `openai`, `anthropic`, `vector`, `eval`, `ai system`), then:
- In pipeline / `--auto` mode, invoke the hook's `ref.skill` via `Skill(skill="gsd-${ref.skill}", args="${PHASE} --auto ${GSD_WS}")`.
- In manual mode, display the existing non-blocking `/gsd:ai-integration-phase {N}` recommendation and let the user continue planning without AI-SPEC or stop to run the capability workflow first.
Run the UI deterministic gate whenever **any** `plan:pre` UI hook is active — including the step-only case (`workflow.ui_safety_gate` off). (`check.query` = `"ui.plan-gate"`; router normalizes dots→hyphens.)
```bash
GATE=$(gsd_run check ui-plan-gate "${PHASE}" --raw)
@@ -689,13 +654,13 @@ Read the ephemeral auto-chain flag:
AUTO_CHAIN=$(gsd_run query check auto-mode --pick auto_chain_active 2>/dev/null || echo "false")
```
**Branch 5 — `AUTO_CHAIN` is `true` (pipeline / `--auto`):** Fire each active **step** hook — runs independently of whether a gate is active (covers `{ui_phase:true, ui_safety_gate:false}`). For each entry in `activeHooks` (in array order) where `kind == "step"` and `ref.skill` is set:
**Branch 5 — `AUTO_CHAIN` is `true` (pipeline / `--auto`):** Fire each active UI **step** hook — runs independently of whether a gate is active (covers `{ui_phase:true,ui_safety_gate:false}`). For each entry in `activeHooks` (in array order) where `kind == "step"` and `ref.skill` is set:
```
Skill(skill="gsd-${ref.skill}", args="${PHASE} --auto ${GSD_WS}")
```
(prepend `gsd-` to `ref.skill` — `ui-phase` → `gsd-ui-phase`.) After all step hooks return, re-read:
After all UI step hooks return, re-read:
```bash
UI_SPEC_FILE=$(ls "${PHASE_DIR}"/*-UI-SPEC.md 2>/dev/null | head -1)
@@ -845,14 +810,7 @@ Proceed to Step 7.8 (or Step 8 if pattern mapper is disabled) only if user selec
## 7.8. Spawn gsd-pattern-mapper Agent (Optional)
**Skip if** `workflow.pattern_mapper` is explicitly set to `false` in config.json (absent key = enabled). Also skip if no CONTEXT.md and no RESEARCH.md exist for this phase (nothing to extract file lists from).
Check config:
```bash
PATTERN_MAPPER_CFG=$(gsd_run query config-get workflow.pattern_mapper 2>/dev/null || echo "true")
```
**If `PATTERN_MAPPER_CFG` is `false`:** Skip to step 8.
Pattern mapper activation is owned by the `pattern-mapper` capability's `plan:pre` step hook. Read `PLAN_PRE_HOOKS_JSON` and skip if no active step hook has `capId == "pattern-mapper"` and `ref.agent == "gsd-pattern-mapper"`. Also skip if no CONTEXT.md and no RESEARCH.md exist for this phase (nothing to extract file lists from).
**If PATTERNS.md already exists** (`PATTERNS_PATH` is non-empty from step 7): Skip to step 8 (use existing).
@@ -865,30 +823,17 @@ Display banner:
◆ Spawning pattern mapper... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze)
```
Pattern mapper prompt:
Use the active `pattern-mapper` hook's `fragment.inline` as the prompt template and substitute the phase fields below before spawning its declared `ref.agent`.
```markdown
<pattern_mapping_context>
**Phase:** {phase_number} - {phase_name}
**Phase directory:** {phase_dir}
**Padded phase:** {padded_phase}
<files_to_read>
- {context_path} (USER DECISIONS from /gsd:discuss-phase)
- {research_path} (Technical Research)
</files_to_read>
**Output file:** {phase_dir}/{padded_phase}-PATTERNS.md
Extract 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.
</pattern_mapping_context>
{pattern_mapper_hook.fragment.inline}
```
Spawn with:
```
Agent(
prompt="{above}",
subagent_type="gsd-pattern-mapper",
prompt=filled_pattern_mapper_hook_fragment,
subagent_type=pattern_mapper_hook.ref.agent,
model="{researcher_model}",
)
```
@@ -944,6 +889,7 @@ Planner prompt:
- {verification_path} (Verification Gaps - if --gaps)
- {uat_path} (UAT Gaps - if --gaps)
- {reviews_path} (Cross-AI Review Feedback - if --reviews; actionable findings must be incorporated or explicitly deferred/rejected in PLAN.md)
- {AI_SPEC_PATH} (AI Design Contract — framework and evaluation strategy, if exists)
- {UI_SPEC_PATH} (UI Design Contract — visual/interaction specs, if exists)
- {SPEC_PATH} (Phase SPEC — carries the ## Edge Coverage section to lift covered/backstop edges from, if exists)
- {SPIKE_FINDINGS_PATH} (Spike Findings — validated patterns, constraints, landmines from experiments, if exists)