feat(quality-gate): implement mathematical blocking quality gate (Phase 2)
- Create qa-memtrace.mjs - standalone Node.js script computing set intersection of blast radius nodes vs test coverage, with 10s timeout and MEMTRACE_MCP_ERROR_TIMEOUT token emission - Create qa-memtrace.test.mjs with 10 automated test cases - Integrate mathematical gate into bmad-dev-story step 5, bmad-quick-dev step-03-implement and step-oneshot - Expand bmad-code-review and gds-code-review Acceptance Auditors with mathematical gate ground-truth checks - Fix .gitignore to track .agents/skills/ and _bmad/scripts/memtrace/
This commit is contained in:
parent
1da6bf80df
commit
79b3f7d9dc
|
|
@ -0,0 +1,90 @@
|
|||
---
|
||||
name: bmad-code-review
|
||||
description: 'Review code changes adversarially using parallel review layers (Blind Hunter, Edge Case Hunter, Acceptance Auditor) with structured triage into actionable categories. Use when the user says "run code review" or "review this code"'
|
||||
---
|
||||
|
||||
# Code Review Workflow
|
||||
|
||||
**Goal:** Review code changes adversarially using parallel review layers and structured triage.
|
||||
|
||||
**Your Role:** You are an elite code reviewer. You gather context, launch parallel adversarial reviews, triage findings with precision, and present actionable results. No noise, no filler.
|
||||
|
||||
## Conventions
|
||||
|
||||
- Bare paths (e.g. `checklist.md`) resolve from the skill root.
|
||||
- `{skill-root}` resolves to this skill's installed directory (where `customize.toml` lives).
|
||||
- `{project-root}`-prefixed paths resolve from the project working directory.
|
||||
- `{skill-name}` resolves to the skill directory's basename.
|
||||
|
||||
## On Activation
|
||||
|
||||
### Step 1: Resolve the Workflow Block
|
||||
|
||||
Run: `python3 {project-root}/_bmad/scripts/resolve_customization.py --skill {skill-root} --key workflow`
|
||||
|
||||
**If the script fails**, resolve the `workflow` block yourself by reading these three files in base → team → user order and applying the same structural merge rules as the resolver:
|
||||
|
||||
1. `{skill-root}/customize.toml` — defaults
|
||||
2. `{project-root}/_bmad/custom/{skill-name}.toml` — team overrides
|
||||
3. `{project-root}/_bmad/custom/{skill-name}.user.toml` — personal overrides
|
||||
|
||||
Any missing file is skipped. Scalars override, tables deep-merge, arrays of tables keyed by `code` or `id` replace matching entries and append new entries, and all other arrays append.
|
||||
|
||||
### Step 2: Execute Prepend Steps
|
||||
|
||||
Execute each entry in `{workflow.activation_steps_prepend}` in order before proceeding.
|
||||
|
||||
### Step 3: Load Persistent Facts
|
||||
|
||||
Treat every entry in `{workflow.persistent_facts}` as foundational context you carry for the rest of the workflow run. Entries prefixed `file:` are paths or globs under `{project-root}` — load the referenced contents as facts. All other entries are facts verbatim.
|
||||
|
||||
### Step 4: Load Config
|
||||
|
||||
Load config from `{project-root}/_bmad/bmm/config.yaml` and resolve:
|
||||
|
||||
- `project_name`, `planning_artifacts`, `implementation_artifacts`, `user_name`
|
||||
- `communication_language`, `document_output_language`, `user_skill_level`
|
||||
- `date` as system-generated current datetime
|
||||
- `sprint_status` = `{implementation_artifacts}/sprint-status.yaml`
|
||||
- `project_context` = `**/project-context.md` (load if exists)
|
||||
- CLAUDE.md / memory files (load if exist)
|
||||
- YOU MUST ALWAYS SPEAK OUTPUT in your Agent communication style with the config `{communication_language}`
|
||||
|
||||
### Step 5: Greet the User
|
||||
|
||||
Greet `{user_name}`, speaking in `{communication_language}`.
|
||||
|
||||
### Step 6: Execute Append Steps
|
||||
|
||||
Execute each entry in `{workflow.activation_steps_append}` in order.
|
||||
|
||||
Activation is complete. Begin the workflow below.
|
||||
|
||||
## WORKFLOW ARCHITECTURE
|
||||
|
||||
This uses **step-file architecture** for disciplined execution:
|
||||
|
||||
- **Micro-file Design**: Each step is self-contained and followed exactly
|
||||
- **Just-In-Time Loading**: Only load the current step file
|
||||
- **Sequential Enforcement**: Complete steps in order, no skipping
|
||||
- **State Tracking**: Persist progress via in-memory variables
|
||||
- **Append-Only Building**: Build artifacts incrementally
|
||||
|
||||
### Step Processing Rules
|
||||
|
||||
1. **READ COMPLETELY**: Read the entire step file before acting
|
||||
2. **FOLLOW SEQUENCE**: Execute sections in order
|
||||
3. **WAIT FOR INPUT**: Halt at checkpoints and wait for human
|
||||
4. **LOAD NEXT**: When directed, read fully and follow the next step file
|
||||
|
||||
### Critical Rules (NO EXCEPTIONS)
|
||||
|
||||
- **NEVER** load multiple step files simultaneously
|
||||
- **ALWAYS** read entire step file before execution
|
||||
- **NEVER** skip steps or optimize the sequence
|
||||
- **ALWAYS** follow the exact instructions in the step file
|
||||
- **ALWAYS** halt at checkpoints and wait for human input
|
||||
|
||||
## FIRST STEP
|
||||
|
||||
Read fully and follow: `./steps/step-01-gather-context.md`
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
# DO NOT EDIT -- overwritten on every update.
|
||||
#
|
||||
# Workflow customization surface for bmad-code-review. Mirrors the
|
||||
# agent customization shape under the [workflow] namespace.
|
||||
|
||||
[workflow]
|
||||
|
||||
# --- Configurable below. Overrides merge per BMad structural rules: ---
|
||||
# scalars: override wins • arrays (persistent_facts, activation_steps_*): append
|
||||
# arrays-of-tables with `code`/`id`: replace matching items, append new ones.
|
||||
|
||||
# Steps to run before the standard activation (config load, greet).
|
||||
# Overrides append. Use for pre-flight loads, compliance checks, etc.
|
||||
|
||||
activation_steps_prepend = []
|
||||
|
||||
# Steps to run after greet but before the workflow begins.
|
||||
# Overrides append. Use for context-heavy setup that should happen
|
||||
# once the user has been acknowledged.
|
||||
|
||||
activation_steps_append = []
|
||||
|
||||
# Persistent facts the workflow keeps in mind for the whole run
|
||||
# (standards, compliance constraints, stylistic guardrails).
|
||||
# Distinct from the runtime memory sidecar — these are static context
|
||||
# loaded on activation. Overrides append.
|
||||
#
|
||||
# Each entry is either:
|
||||
# - a literal sentence, e.g. "All stories must include testable acceptance criteria."
|
||||
# - a file reference prefixed with `file:`, e.g. "file:{project-root}/docs/standards.md"
|
||||
# (glob patterns are supported; the file's contents are loaded and treated as facts).
|
||||
|
||||
persistent_facts = [
|
||||
"file:{project-root}/**/project-context.md",
|
||||
]
|
||||
|
||||
# Scalar: executed when the workflow reaches its final step,
|
||||
# after review findings are presented and sprint status is synced. Override wins.
|
||||
# Leave empty for no custom post-completion behavior.
|
||||
|
||||
on_complete = ""
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
---
|
||||
diff_output: '' # set at runtime
|
||||
spec_file: '' # set at runtime (path or empty)
|
||||
review_mode: '' # set at runtime: "full" or "no-spec"
|
||||
story_key: '' # set at runtime when discovered from sprint status
|
||||
---
|
||||
|
||||
# Step 1: Gather Context
|
||||
|
||||
## RULES
|
||||
|
||||
- YOU MUST ALWAYS SPEAK OUTPUT in your Agent communication style with the config `{communication_language}`
|
||||
- The prompt that triggered this workflow IS the intent — not a hint.
|
||||
- Do not modify any files. This step is read-only.
|
||||
|
||||
## INSTRUCTIONS
|
||||
|
||||
1. **Find the review target.** The conversation context before this skill was triggered IS your starting point — not a blank slate. Check in this order — stop as soon as the review target is identified:
|
||||
|
||||
**Tier 1 — Explicit argument.**
|
||||
Did the user pass a PR, commit SHA, branch, spec file, or diff source this message?
|
||||
- PR reference → resolve to branch/commit via `gh pr view`. If resolution fails, ask for a SHA or branch.
|
||||
- Commit or branch → use directly.
|
||||
- Spec file → set `{spec_file}` to the provided path. Check its frontmatter for `baseline_commit`. If found, use as diff baseline. If not found, continue the cascade (a spec alone does not identify a diff source).
|
||||
- Also scan the argument for diff-mode keywords that narrow the scope:
|
||||
- "staged" / "staged changes" → Staged changes only
|
||||
- "uncommitted" / "working tree" / "all changes" → Uncommitted changes (staged + unstaged)
|
||||
- "branch diff" / "vs main" / "against main" / "compared to <branch>" → Branch diff (extract base branch if mentioned)
|
||||
- "commit range" / "last N commits" / "<from-sha>..<to-sha>" → Specific commit range
|
||||
- "this diff" / "provided diff" / "paste" → User-provided diff (do not match bare "diff" — it appears in other modes)
|
||||
- When multiple keywords match, prefer the most specific (e.g., "branch diff" over bare "diff").
|
||||
|
||||
**Tier 2 — Recent conversation.**
|
||||
Do the last few messages reveal what the user wants to be reviewed? Look for spec paths, commit refs, branches, PRs, or descriptions of a change. Apply the same diff-mode keyword scan and routing as Tier 1.
|
||||
|
||||
**Tier 3 — Sprint tracking.**
|
||||
Look for a sprint status file (`*sprint-status*`) in `{implementation_artifacts}` or `{planning_artifacts}`. If found, scan for stories with status `review`:
|
||||
- **Exactly one `review` story:** Set `{story_key}` to the story's key (e.g., `1-2-user-auth`). Suggest it: "I found story <story-id> in `review` status. Would you like to review its changes? [Y] Yes / [N] No, let me choose". If confirmed, use the story context to determine the diff source (branch name derived from story slug, or uncommitted changes). If declined, clear `{story_key}` and fall through.
|
||||
- **Multiple `review` stories:** Present them as numbered options alongside a manual choice option. Wait for user selection. If a story is selected, set `{story_key}` and use its context to determine the diff source. If manual choice is selected, clear `{story_key}` and fall through.
|
||||
- **None:** Fall through.
|
||||
|
||||
**Tier 4 — Current git state.**
|
||||
If version control is unavailable, skip to Tier 5. Otherwise, check the current branch and HEAD. If the branch is not `main` (or the default branch), confirm: "I see HEAD is `<short-sha>` on `<branch>` — do you want to review this branch's changes?" If confirmed, treat as a branch diff against `main`. If declined, fall through.
|
||||
|
||||
**Tier 5 — Ask.**
|
||||
Fall through to instruction 2.
|
||||
|
||||
Never ask extra questions beyond what the cascade prescribes. If a tier above already identified the target, skip the remaining tiers and proceed to instruction 3 (construct diff).
|
||||
|
||||
2. HALT. Ask the user: **What do you want to review?** Present these options:
|
||||
- **Uncommitted changes** (staged + unstaged)
|
||||
- **Staged changes only**
|
||||
- **Branch diff** vs a base branch (ask which base branch)
|
||||
- **Specific commit range** (ask for the range)
|
||||
- **Provided diff or file list** (user pastes or provides a path)
|
||||
|
||||
3. Construct `{diff_output}` from the chosen source.
|
||||
- For **staged changes only**: run `git diff --cached`.
|
||||
- For **uncommitted changes** (staged + unstaged): run `git diff HEAD`.
|
||||
- For **branch diff**: verify the base branch exists before running `git diff`. If it does not exist, HALT and ask the user for a valid branch.
|
||||
- For **commit range**: verify the range resolves. If it does not, HALT and ask the user for a valid range.
|
||||
- For **provided diff**: validate the content is non-empty and parseable as a unified diff. If it is not parseable, HALT and ask the user to provide a valid diff.
|
||||
- For **file list**: validate each path exists in the working tree. Construct `{diff_output}` by running `git diff HEAD -- <path1> <path2> ...`. If any paths are untracked (new files not yet staged), use `git diff --no-index /dev/null <path>` to include them. If the diff is empty (files have no uncommitted changes and are not untracked), ask the user whether to review the full file contents or to specify a different baseline.
|
||||
- After constructing `{diff_output}`, verify it is non-empty regardless of source type. If empty, HALT and tell the user there is nothing to review.
|
||||
|
||||
4. **Set the spec context.**
|
||||
- If `{spec_file}` is already set (from Tier 1 or Tier 2): verify the file exists and is readable, then set `{review_mode}` = `"full"`.
|
||||
- Otherwise, ask the user: **Is there a spec or story file that provides context for these changes?**
|
||||
- If yes: set `{spec_file}` to the path provided, verify the file exists and is readable, then set `{review_mode}` = `"full"`.
|
||||
- If no: set `{review_mode}` = `"no-spec"`.
|
||||
|
||||
5. If `{review_mode}` = `"full"` and the file at `{spec_file}` has a `context` field in its frontmatter listing additional docs, load each referenced document. Warn the user about any docs that cannot be found.
|
||||
|
||||
6. Sanity check: if `{diff_output}` exceeds approximately 3000 lines, warn the user and offer to chunk the review by file group.
|
||||
- If the user opts to chunk: agree on the first group, narrow `{diff_output}` accordingly, and list the remaining groups for the user to note for follow-up runs.
|
||||
- If the user declines: proceed as-is with the full diff.
|
||||
|
||||
### CHECKPOINT
|
||||
|
||||
Present a summary before proceeding: diff stats (files changed, lines added/removed), `{review_mode}`, and loaded spec/context docs (if any). HALT and wait for user confirmation to proceed.
|
||||
|
||||
|
||||
## NEXT
|
||||
|
||||
Read fully and follow `./step-02-review.md`
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
---
|
||||
failed_layers: '' # set at runtime: comma-separated list of layers that failed or returned empty
|
||||
---
|
||||
|
||||
# Step 2: Review
|
||||
|
||||
## RULES
|
||||
|
||||
- YOU MUST ALWAYS SPEAK OUTPUT in your Agent communication style with the config `{communication_language}`
|
||||
- The Blind Hunter subagent receives NO project context — diff only.
|
||||
- The Edge Case Hunter subagent receives diff and project read access.
|
||||
- The Acceptance Auditor subagent receives diff, spec, and context docs.
|
||||
- All review subagents must run at the same model capability as the current session.
|
||||
|
||||
## INSTRUCTIONS
|
||||
|
||||
1. If `{review_mode}` = `"no-spec"`, note to the user: "Acceptance Auditor skipped — no spec file provided."
|
||||
|
||||
2. Launch parallel subagents without conversation context. If subagents are not available, generate prompt files in `{implementation_artifacts}` — one per reviewer role below — and HALT. Ask the user to run each in a separate session (ideally a different LLM) and paste back the findings. When findings are pasted, resume from this point and proceed to step 3.
|
||||
|
||||
- **Blind Hunter** — receives `{diff_output}` only. No spec, no context docs, no project access. Invoke via the `bmad-review-adversarial-general` skill.
|
||||
|
||||
- **Edge Case Hunter** — receives `{diff_output}` and read access to the project. Invoke via the `bmad-review-edge-case-hunter` skill.
|
||||
|
||||
- **Acceptance Auditor** (only if `{review_mode}` = `"full"`) — receives `{diff_output}`, the content of the file at `{spec_file}`, and any loaded context docs. Its prompt:
|
||||
> You are an Acceptance Auditor. Review this diff against the spec and context docs. Check for: violations of acceptance criteria, deviations from spec intent, missing implementation of specified behavior, contradictions between spec constraints and actual code.
|
||||
>
|
||||
> **Quality Gate — Test Coverage Justification:** Check whether the spec/story file or diff commentary includes a "Test Coverage Justification" section that maps impacted modules/nodes to specific test files. Apply these rules:
|
||||
> - **If `{review_mode}` = `"full"`** (spec/story file is present) and the spec file lacks a Test Coverage Justification section → raise a `decision_needed` finding: "Missing Test Coverage Justification — blast radius report exists but no test mapping was provided."
|
||||
> - **If `{review_mode}` = `"no-spec"`** (diff-only review) → check the diff commentary or commit messages for test coverage evidence. If absent, raise a `patch` finding: "Quality gate artifact missing — no Test Coverage Justification found in diff commentary or commit message."
|
||||
> - **If Test Coverage Justification exists** but has any node listed with coverage status `None` or `no coverage found` → raise a separate `decision_needed` finding for each such node: "Uncovered impacted node: [node-name] — tests required before merge."
|
||||
> - Reference any blast radius report embedded in the spec/story file to cross-validate the affected modules listed in the justification.
|
||||
> - **Fallback detection:** If no section titled "Test Coverage Justification" is found, search for a markdown table with columns containing "Module", "Affected Symbols", "Test Files", and "Coverage". If such a table exists, treat it as meeting the quality gate regardless of section title.
|
||||
>
|
||||
> **Quality Gate — Mathematical Gate Output:** Check whether the spec/story file includes a "Mathematical Quality Gate Output" section (JSON output from `qa-memtrace.mjs`). Apply these rules:
|
||||
> - **If the spec file contains "Mathematical Quality Gate Output"**: use the `uncovered_nodes` from the script's JSON output as the ground truth for uncovered nodes — NOT the agent's textual claims in the justification table. Cross-validate: if the script found a node as uncovered but the justification table lists it as `Yes` coverage → raise a `decision_needed` finding per mismatch: "Coverage mismatch — agent claimed [node] is covered but mathematical gate shows it is not."
|
||||
> - **If the spec file contains a blast radius report AND a Test Coverage Justification BUT no "Mathematical Quality Gate Output"**: this is a Phase 1-level story using only textual justification. Flag as `patch` (not `decision_needed`): "Phase 1 story — consider upgrading to mathematical quality gate via qa-memtrace.mjs."
|
||||
> - **If the spec file contains only a blast radius report (no test justification, no mathematical gate)**: raise a `decision_needed` finding: "Missing both Test Coverage Justification and Mathematical Quality Gate Output — Phase 2 story must include qa-memtrace.mjs execution results."
|
||||
>
|
||||
> Output findings as a Markdown list. Each finding: one-line title, which quality gate rule it violates, and evidence from the diff/story file.
|
||||
|
||||
3. **Subagent failure handling**: If any subagent fails, times out, or returns empty results, append the layer name to `{failed_layers}` (comma-separated) and proceed with findings from the remaining layers.
|
||||
|
||||
4. Collect all findings from the completed layers.
|
||||
|
||||
|
||||
## NEXT
|
||||
|
||||
Read fully and follow `./step-03-triage.md`
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
---
|
||||
---
|
||||
|
||||
# Step 3: Triage
|
||||
|
||||
## RULES
|
||||
|
||||
- YOU MUST ALWAYS SPEAK OUTPUT in your Agent communication style with the config `{communication_language}`
|
||||
- Be precise. When uncertain between categories, prefer the more conservative classification.
|
||||
|
||||
## INSTRUCTIONS
|
||||
|
||||
1. **Normalize** findings into a common format. Expected input formats:
|
||||
- Adversarial (Blind Hunter): markdown list of descriptions
|
||||
- Edge Case Hunter: JSON array with `location`, `trigger_condition`, `guard_snippet`, `potential_consequence` fields
|
||||
- Acceptance Auditor: markdown list with title, AC/constraint reference, and evidence
|
||||
|
||||
If a layer's output does not match its expected format, attempt best-effort parsing. Note any parsing issues for the user.
|
||||
|
||||
Convert all to a unified list where each finding has:
|
||||
- `id` -- sequential integer
|
||||
- `source` -- `blind`, `edge`, `auditor`, or merged sources (e.g., `blind+edge`)
|
||||
- `title` -- one-line summary
|
||||
- `detail` -- full description
|
||||
- `location` -- file and line reference (if available)
|
||||
|
||||
2. **Deduplicate.** If two or more findings describe the same issue, merge them into one:
|
||||
- Use the most specific finding as the base (prefer edge-case JSON with location over adversarial prose).
|
||||
- Append any unique detail, reasoning, or location references from the other finding(s) into the surviving `detail` field.
|
||||
- Set `source` to the merged sources (e.g., `blind+edge`).
|
||||
|
||||
3. **Classify** each finding into exactly one bucket:
|
||||
- **decision_needed** -- There is an ambiguous choice that requires human input. The code cannot be correctly patched without knowing the user's intent. Only possible if `{review_mode}` = `"full"`.
|
||||
- **patch** -- Code issue that is fixable without human input. The correct fix is unambiguous.
|
||||
- **defer** -- Pre-existing issue not caused by the current change. Real but not actionable now.
|
||||
- **dismiss** -- Noise, false positive, or handled elsewhere.
|
||||
|
||||
If `{review_mode}` = `"no-spec"` and a finding would otherwise be `decision_needed`, reclassify it as `patch` (if the fix is unambiguous) or `defer` (if not).
|
||||
|
||||
4. **Drop** all `dismiss` findings. Record the dismiss count for the summary.
|
||||
|
||||
5. If `{failed_layers}` is non-empty, report which layers failed before announcing results. If zero findings remain after dropping dismissed AND `{failed_layers}` is non-empty, warn the user that the review may be incomplete rather than announcing a clean review.
|
||||
|
||||
6. If zero findings remain after triage (all rejected or none raised): state "✅ Clean review — all layers passed." (Step 3 already warned if any review layers failed via `{failed_layers}`.)
|
||||
|
||||
|
||||
## NEXT
|
||||
|
||||
Read fully and follow `./step-04-present.md`
|
||||
|
|
@ -0,0 +1,132 @@
|
|||
---
|
||||
deferred_work_file: '{implementation_artifacts}/deferred-work.md'
|
||||
---
|
||||
|
||||
# Step 4: Present and Act
|
||||
|
||||
## RULES
|
||||
|
||||
- YOU MUST ALWAYS SPEAK OUTPUT in your Agent communication style with the config `{communication_language}`
|
||||
- When `{spec_file}` is set, always write findings to the story file before offering action choices.
|
||||
- `decision-needed` findings must be resolved before handling `patch` findings.
|
||||
|
||||
## INSTRUCTIONS
|
||||
|
||||
### 1. Clean review shortcut
|
||||
|
||||
If zero findings remain after triage (all dismissed or none raised): state that and proceed to section 6 (Sprint Status Update).
|
||||
|
||||
### 2. Write findings to the story file
|
||||
|
||||
If `{spec_file}` exists and contains a Tasks/Subtasks section, append a `### Review Findings` subsection. Write all findings in this order:
|
||||
|
||||
1. **`decision-needed`** findings (unchecked):
|
||||
`- [ ] [Review][Decision] <Title> — <Detail>`
|
||||
|
||||
2. **`patch`** findings (unchecked):
|
||||
`- [ ] [Review][Patch] <Title> [<file>:<line>]`
|
||||
|
||||
3. **`defer`** findings (checked off, marked deferred):
|
||||
`- [x] [Review][Defer] <Title> [<file>:<line>] — deferred, pre-existing`
|
||||
|
||||
Also append each `defer` finding to `{deferred_work_file}` under a heading `## Deferred from: code review ({date})`. If `{spec_file}` is set, include its basename in the heading (e.g., `code review of story-3.3 (2026-03-18)`). One bullet per finding with description.
|
||||
|
||||
### 3. Present summary
|
||||
|
||||
Announce what was written:
|
||||
|
||||
> **Code review complete.** <D> `decision-needed`, <P> `patch`, <W> `defer`, <R> dismissed as noise.
|
||||
|
||||
If `{spec_file}` is set, add: `Findings written to the review findings section in {spec_file}.`
|
||||
Otherwise add: `Findings are listed above. No story file was provided, so nothing was persisted.`
|
||||
|
||||
### 4. Resolve decision-needed findings
|
||||
|
||||
If `decision_needed` findings exist, present each one with its detail and the options available. The user must decide — the correct fix is ambiguous without their input. Walk through each finding (or batch related ones) and get the user's call. Once resolved, each becomes a `patch`, `defer`, or is dismissed.
|
||||
|
||||
If the user chooses to defer, ask: Quick one-line reason for deferring this item? (helps future reviews): — then append that reason to both the story file bullet and the `{deferred_work_file}` entry.
|
||||
|
||||
**HALT** — I am waiting for your numbered choice. Reply with only the number. Do not proceed until you select an option.
|
||||
|
||||
### 5. Handle `patch` findings
|
||||
|
||||
If `patch` findings exist (including any resolved from step 4), HALT. Ask the user:
|
||||
|
||||
If `{spec_file}` is set, present all three options:
|
||||
|
||||
> **How would you like to handle the `<P>` `patch` findings?**
|
||||
> 1. **Apply every patch** — fix all of them now, no per-finding confirmation. Defer and decision-needed items are not touched.
|
||||
> 2. **Leave as action items** — they are already in the story file
|
||||
> 3. **Walk through each patch** — show details for each before deciding
|
||||
|
||||
If `{spec_file}` is **not** set, present only options 1 and 2 (omit "Leave as action items" — findings were not written to a file):
|
||||
|
||||
> **How would you like to handle the `<P>` `patch` findings?**
|
||||
> 1. **Apply every patch** — fix all of them now, no per-finding confirmation. Defer and decision-needed items are not touched.
|
||||
> 2. **Walk through each patch** — show details for each before deciding
|
||||
|
||||
**HALT** — I am waiting for your numbered choice. Reply with only the number. Do not proceed until you select an option.
|
||||
|
||||
- **Apply every patch**: Apply every patch finding without per-finding confirmation. Do not modify defer or decision-needed items. After all patches are applied, present a summary of changes made. If `{spec_file}` is set, check off the patch items in the story file (leave defer items as-is).
|
||||
- **Leave as action items** (only when `{spec_file}` is set): Done — findings are already written to the story.
|
||||
- **Walk through each patch**: Present each finding with full detail, diff context, and suggested fix. After walkthrough, re-offer the applicable options above.
|
||||
|
||||
**HALT** — I am waiting for your numbered choice. Do not proceed until you select an option.
|
||||
|
||||
**✅ Code review actions complete**
|
||||
|
||||
- Decision-needed resolved: <D>
|
||||
- Patches handled: <P>
|
||||
- Deferred: <W>
|
||||
- Dismissed: <R>
|
||||
|
||||
### 6. Update story status and sync sprint tracking
|
||||
|
||||
Skip this section if `{spec_file}` is not set.
|
||||
|
||||
#### Determine new status based on review outcome
|
||||
|
||||
- If all `decision-needed` and `patch` findings were resolved (fixed or dismissed) AND no unresolved HIGH/MEDIUM issues remain: set `{new_status}` = `done`. Update the story file Status section to `done`.
|
||||
- If `patch` findings were left as action items, or unresolved issues remain: set `{new_status}` = `in-progress`. Update the story file Status section to `in-progress`.
|
||||
|
||||
Save the story file.
|
||||
|
||||
#### Sync sprint-status.yaml
|
||||
|
||||
If `{story_key}` is not set, skip this subsection and note that sprint status was not synced because no story key was available.
|
||||
|
||||
If `{sprint_status}` file exists:
|
||||
|
||||
1. Load the FULL `{sprint_status}` file.
|
||||
2. Find the `development_status` entry matching `{story_key}`.
|
||||
3. If found: update `development_status[{story_key}]` to `{new_status}`. Update `last_updated` to current date. Save the file, preserving ALL comments and structure including STATUS DEFINITIONS.
|
||||
4. If `{story_key}` not found in sprint status: warn the user that the story file was updated but sprint-status sync failed.
|
||||
|
||||
If `{sprint_status}` file does not exist, note that story status was updated in the story file only.
|
||||
|
||||
#### Completion summary
|
||||
|
||||
> **Review Complete!**
|
||||
>
|
||||
> **Story Status:** `{new_status}`
|
||||
> **Issues Fixed:** <fixed_count>
|
||||
> **Action Items Created:** <action_count>
|
||||
> **Deferred:** <W>
|
||||
> **Dismissed:** <R>
|
||||
|
||||
### 7. Next steps
|
||||
|
||||
Present the user with follow-up options:
|
||||
|
||||
> **What would you like to do next?**
|
||||
> 1. **Start the next story** — run `dev-story` to pick up the next `ready-for-dev` story
|
||||
> 2. **Re-run code review** — address findings and review again
|
||||
> 3. **Done** — end the workflow
|
||||
|
||||
**HALT** — I am waiting for your choice. Do not proceed until the user selects an option.
|
||||
|
||||
## On Complete
|
||||
|
||||
Run: `python3 {project-root}/_bmad/scripts/resolve_customization.py --skill {skill-root} --key workflow.on_complete`
|
||||
|
||||
If the resolved `workflow.on_complete` is non-empty, follow it as the final terminal instruction before exiting.
|
||||
|
|
@ -0,0 +1,631 @@
|
|||
---
|
||||
name: bmad-dev-story
|
||||
description: 'Execute story implementation following a context filled story spec file. Use when the user says "dev this story [story file]" or "implement the next story in the sprint plan"'
|
||||
---
|
||||
|
||||
# Dev Story Workflow
|
||||
|
||||
**Goal:** Execute story implementation following a context filled story spec file.
|
||||
|
||||
**Your Role:** Developer implementing the story.
|
||||
- Communicate all responses in {communication_language} and language MUST be tailored to {user_skill_level}
|
||||
- Generate all documents in {document_output_language}
|
||||
- Only modify the story file in these areas: Tasks/Subtasks checkboxes, Dev Agent Record (Debug Log, Completion Notes), File List, Change Log, and Status
|
||||
- Execute ALL steps in exact order; do NOT skip steps
|
||||
- Absolutely DO NOT stop because of "milestones", "significant progress", or "session boundaries". Continue in a single execution until the story is COMPLETE (all ACs satisfied and all tasks/subtasks checked) UNLESS a HALT condition is triggered or the USER gives other instruction.
|
||||
- Do NOT schedule a "next session" or request review pauses unless a HALT condition applies. Only Step 9 decides completion.
|
||||
- User skill level ({user_skill_level}) affects conversation style ONLY, not code updates.
|
||||
|
||||
## Conventions
|
||||
|
||||
- Bare paths (e.g. `steps/step-01-init.md`) resolve from the skill root.
|
||||
- `{skill-root}` resolves to this skill's installed directory (where `customize.toml` lives).
|
||||
- `{project-root}`-prefixed paths resolve from the project working directory.
|
||||
- `{skill-name}` resolves to the skill directory's basename.
|
||||
|
||||
## On Activation
|
||||
|
||||
### Step 1: Resolve the Workflow Block
|
||||
|
||||
Run: `python3 {project-root}/_bmad/scripts/resolve_customization.py --skill {skill-root} --key workflow`
|
||||
|
||||
**If the script fails**, resolve the `workflow` block yourself by reading these three files in base → team → user order and applying the same structural merge rules as the resolver:
|
||||
|
||||
1. `{skill-root}/customize.toml` — defaults
|
||||
2. `{project-root}/_bmad/custom/{skill-name}.toml` — team overrides
|
||||
3. `{project-root}/_bmad/custom/{skill-name}.user.toml` — personal overrides
|
||||
|
||||
Any missing file is skipped. Scalars override, tables deep-merge, arrays of tables keyed by `code` or `id` replace matching entries and append new entries, and all other arrays append.
|
||||
|
||||
### Step 2: Execute Prepend Steps
|
||||
|
||||
Execute each entry in `{workflow.activation_steps_prepend}` in order before proceeding.
|
||||
|
||||
### Step 3: Load Persistent Facts
|
||||
|
||||
Treat every entry in `{workflow.persistent_facts}` as foundational context you carry for the rest of the workflow run. Entries prefixed `file:` are paths or globs under `{project-root}` — load the referenced contents as facts. All other entries are facts verbatim.
|
||||
|
||||
### Step 4: Load Config
|
||||
|
||||
Load config from `{project-root}/_bmad/bmm/config.yaml` and resolve:
|
||||
|
||||
- `project_name`, `user_name`
|
||||
- `communication_language`, `document_output_language`
|
||||
- `user_skill_level`
|
||||
- `implementation_artifacts`
|
||||
- `date` as system-generated current datetime
|
||||
|
||||
### Step 5: Greet the User
|
||||
|
||||
Greet `{user_name}`, speaking in `{communication_language}`.
|
||||
|
||||
### Step 6: Execute Append Steps
|
||||
|
||||
Execute each entry in `{workflow.activation_steps_append}` in order.
|
||||
|
||||
Activation is complete. Begin the workflow below.
|
||||
|
||||
## Paths
|
||||
|
||||
- `story_file` = `` (explicit story path; auto-discovered if empty)
|
||||
- `sprint_status` = `{implementation_artifacts}/sprint-status.yaml`
|
||||
|
||||
## Execution
|
||||
|
||||
<workflow>
|
||||
<critical>Communicate all responses in {communication_language} and language MUST be tailored to {user_skill_level}</critical>
|
||||
<critical>Generate all documents in {document_output_language}</critical>
|
||||
<critical>Only modify the story file in these areas: Tasks/Subtasks checkboxes, Dev Agent Record (Debug Log, Completion Notes), File List,
|
||||
Change Log, and Status</critical>
|
||||
<critical>Execute ALL steps in exact order; do NOT skip steps</critical>
|
||||
<critical>Absolutely DO NOT stop because of "milestones", "significant progress", or "session boundaries". Continue in a single execution
|
||||
until the story is COMPLETE (all ACs satisfied and all tasks/subtasks checked) UNLESS a HALT condition is triggered or the USER gives
|
||||
other instruction.</critical>
|
||||
<critical>Do NOT schedule a "next session" or request review pauses unless a HALT condition applies. Only Step 9 decides completion.</critical>
|
||||
<critical>User skill level ({user_skill_level}) affects conversation style ONLY, not code updates.</critical>
|
||||
|
||||
<step n="1" goal="Find next ready story and load it" tag="sprint-status">
|
||||
<check if="{{story_path}} is provided">
|
||||
<action>Use {{story_path}} directly</action>
|
||||
<action>Read COMPLETE story file</action>
|
||||
<action>Extract story_key from filename or metadata</action>
|
||||
<goto anchor="task_check" />
|
||||
</check>
|
||||
|
||||
<!-- Sprint-based story discovery -->
|
||||
<check if="{{sprint_status}} file exists">
|
||||
<critical>MUST read COMPLETE sprint-status.yaml file from start to end to preserve order</critical>
|
||||
<action>Load the FULL file: {{sprint_status}}</action>
|
||||
<action>Read ALL lines from beginning to end - do not skip any content</action>
|
||||
<action>Parse the development_status section completely to understand story order</action>
|
||||
|
||||
<action>Find the FIRST story (by reading in order from top to bottom) where:
|
||||
- Key matches pattern: number-number-name (e.g., "1-2-user-auth")
|
||||
- NOT an epic key (epic-X) or retrospective (epic-X-retrospective)
|
||||
- Status value equals "ready-for-dev"
|
||||
</action>
|
||||
|
||||
<check if="no ready-for-dev or in-progress story found">
|
||||
<output>📋 No ready-for-dev stories found in sprint-status.yaml
|
||||
|
||||
**Current Sprint Status:** {{sprint_status_summary}}
|
||||
|
||||
**What would you like to do?**
|
||||
1. Run `create-story` to create next story from epics with comprehensive context
|
||||
2. Run `*validate-create-story` to improve existing stories before development (recommended quality check)
|
||||
3. Specify a particular story file to develop (provide full path)
|
||||
4. Check {{sprint_status}} file to see current sprint status
|
||||
|
||||
💡 **Tip:** Stories in `ready-for-dev` may not have been validated. Consider running `validate-create-story` first for a quality
|
||||
check.
|
||||
</output>
|
||||
<ask>Choose option [1], [2], [3], or [4], or specify story file path:</ask>
|
||||
|
||||
<check if="user chooses '1'">
|
||||
<action>HALT - Run create-story to create next story</action>
|
||||
</check>
|
||||
|
||||
<check if="user chooses '2'">
|
||||
<action>HALT - Run validate-create-story to improve existing stories</action>
|
||||
</check>
|
||||
|
||||
<check if="user chooses '3'">
|
||||
<ask>Provide the story file path to develop:</ask>
|
||||
<action>Store user-provided story path as {{story_path}}</action>
|
||||
<goto anchor="task_check" />
|
||||
</check>
|
||||
|
||||
<check if="user chooses '4'">
|
||||
<output>Loading {{sprint_status}} for detailed status review...</output>
|
||||
<action>Display detailed sprint status analysis</action>
|
||||
<action>HALT - User can review sprint status and provide story path</action>
|
||||
</check>
|
||||
|
||||
<check if="user provides story file path">
|
||||
<action>Store user-provided story path as {{story_path}}</action>
|
||||
<goto anchor="task_check" />
|
||||
</check>
|
||||
</check>
|
||||
</check>
|
||||
|
||||
<!-- Non-sprint story discovery -->
|
||||
<check if="{{sprint_status}} file does NOT exist">
|
||||
<action>Search {implementation_artifacts} for stories directly</action>
|
||||
<action>Find stories with "ready-for-dev" status in files</action>
|
||||
<action>Look for story files matching pattern: *-*-*.md</action>
|
||||
<action>Read each candidate story file to check Status section</action>
|
||||
|
||||
<check if="no ready-for-dev stories found in story files">
|
||||
<output>📋 No ready-for-dev stories found
|
||||
|
||||
**Available Options:**
|
||||
1. Run `create-story` to create next story from epics with comprehensive context
|
||||
2. Run `*validate-create-story` to improve existing stories
|
||||
3. Specify which story to develop
|
||||
</output>
|
||||
<ask>What would you like to do? Choose option [1], [2], or [3]:</ask>
|
||||
|
||||
<check if="user chooses '1'">
|
||||
<action>HALT - Run create-story to create next story</action>
|
||||
</check>
|
||||
|
||||
<check if="user chooses '2'">
|
||||
<action>HALT - Run validate-create-story to improve existing stories</action>
|
||||
</check>
|
||||
|
||||
<check if="user chooses '3'">
|
||||
<ask>It's unclear what story you want developed. Please provide the full path to the story file:</ask>
|
||||
<action>Store user-provided story path as {{story_path}}</action>
|
||||
<action>Continue with provided story file</action>
|
||||
</check>
|
||||
</check>
|
||||
|
||||
<check if="ready-for-dev story found in files">
|
||||
<action>Use discovered story file and extract story_key</action>
|
||||
</check>
|
||||
</check>
|
||||
|
||||
<action>Store the found story_key (e.g., "1-2-user-authentication") for later status updates</action>
|
||||
<action>Find matching story file in {implementation_artifacts} using story_key pattern: {{story_key}}.md</action>
|
||||
<action>Read COMPLETE story file from discovered path</action>
|
||||
|
||||
<anchor id="task_check" />
|
||||
|
||||
<action>Parse sections: Story, Acceptance Criteria, Tasks/Subtasks, Dev Notes, Dev Agent Record, File List, Change Log, Status</action>
|
||||
|
||||
<action>Load comprehensive context from story file's Dev Notes section</action>
|
||||
<action>Extract developer guidance from Dev Notes: architecture requirements, previous learnings, technical specifications</action>
|
||||
<action>Use enhanced story context to inform implementation decisions and approaches</action>
|
||||
|
||||
<action>Identify first incomplete task (unchecked [ ]) in Tasks/Subtasks</action>
|
||||
|
||||
<action if="no incomplete tasks">
|
||||
<goto step="10">Completion sequence</goto>
|
||||
</action>
|
||||
<action if="story file inaccessible">HALT: "Cannot develop story without access to story file"</action>
|
||||
<action if="incomplete task or subtask requirements ambiguous">ASK user to clarify or HALT</action>
|
||||
</step>
|
||||
|
||||
<step n="2" goal="Load project context and story information">
|
||||
<critical>Load all available context to inform implementation</critical>
|
||||
|
||||
<action>Load {project_context} for coding standards and project-wide patterns (if exists)</action>
|
||||
<action>Parse sections: Story, Acceptance Criteria, Tasks/Subtasks, Dev Notes, Dev Agent Record, File List, Change Log, Status</action>
|
||||
<action>Load comprehensive context from story file's Dev Notes section</action>
|
||||
<action>Extract developer guidance from Dev Notes: architecture requirements, previous learnings, technical specifications</action>
|
||||
<action>Use enhanced story context to inform implementation decisions and approaches</action>
|
||||
<output>✅ **Context Loaded**
|
||||
Story and project context available for implementation
|
||||
</output>
|
||||
</step>
|
||||
|
||||
<step n="3" goal="Detect review continuation and extract review context">
|
||||
<critical>Determine if this is a fresh start or continuation after code review</critical>
|
||||
|
||||
<action>Check if "Senior Developer Review (AI)" section exists in the story file</action>
|
||||
<action>Check if "Review Follow-ups (AI)" subsection exists under Tasks/Subtasks</action>
|
||||
|
||||
<check if="Senior Developer Review section exists">
|
||||
<action>Set review_continuation = true</action>
|
||||
<action>Extract from "Senior Developer Review (AI)" section:
|
||||
- Review outcome (Approve/Changes Requested/Blocked)
|
||||
- Review date
|
||||
- Total action items with checkboxes (count checked vs unchecked)
|
||||
- Severity breakdown (High/Med/Low counts)
|
||||
</action>
|
||||
<action>Count unchecked [ ] review follow-up tasks in "Review Follow-ups (AI)" subsection</action>
|
||||
<action>Store list of unchecked review items as {{pending_review_items}}</action>
|
||||
|
||||
<output>⏯️ **Resuming Story After Code Review** ({{review_date}})
|
||||
|
||||
**Review Outcome:** {{review_outcome}}
|
||||
**Action Items:** {{unchecked_review_count}} remaining to address
|
||||
**Priorities:** {{high_count}} High, {{med_count}} Medium, {{low_count}} Low
|
||||
|
||||
**Strategy:** Will prioritize review follow-up tasks (marked [AI-Review]) before continuing with regular tasks.
|
||||
</output>
|
||||
</check>
|
||||
|
||||
<check if="Senior Developer Review section does NOT exist">
|
||||
<action>Set review_continuation = false</action>
|
||||
<action>Set {{pending_review_items}} = empty</action>
|
||||
|
||||
<output>🚀 **Starting Fresh Implementation**
|
||||
|
||||
Story: {{story_key}}
|
||||
Story Status: {{current_status}}
|
||||
First incomplete task: {{first_task_description}}
|
||||
</output>
|
||||
</check>
|
||||
</step>
|
||||
|
||||
<step n="4" goal="Mark story in-progress" tag="sprint-status">
|
||||
<check if="{{sprint_status}} file exists">
|
||||
<action>Load the FULL file: {{sprint_status}}</action>
|
||||
<action>Read all development_status entries to find {{story_key}}</action>
|
||||
<action>Get current status value for development_status[{{story_key}}]</action>
|
||||
|
||||
<check if="current status == 'ready-for-dev' OR review_continuation == true">
|
||||
<action>Update the story in the sprint status report to = "in-progress"</action>
|
||||
<action>Update last_updated field to current date</action>
|
||||
<output>🚀 Starting work on story {{story_key}}
|
||||
Status updated: ready-for-dev → in-progress
|
||||
</output>
|
||||
</check>
|
||||
|
||||
<check if="current status == 'in-progress'">
|
||||
<output>⏯️ Resuming work on story {{story_key}}
|
||||
Story is already marked in-progress
|
||||
</output>
|
||||
</check>
|
||||
|
||||
<check if="current status is neither ready-for-dev nor in-progress">
|
||||
<output>⚠️ Unexpected story status: {{current_status}}
|
||||
Expected ready-for-dev or in-progress. Continuing anyway...
|
||||
</output>
|
||||
</check>
|
||||
|
||||
<action>Store {{current_sprint_status}} for later use</action>
|
||||
</check>
|
||||
|
||||
<check if="{{sprint_status}} file does NOT exist">
|
||||
<output>ℹ️ No sprint status file exists - story progress will be tracked in story file only</output>
|
||||
<action>Set {{current_sprint_status}} = "no-sprint-tracking"</action>
|
||||
</check>
|
||||
</step>
|
||||
|
||||
<step n="5" goal="Blast radius verification and halting">
|
||||
<critical>MANDATORY: Before ANY code modification, calculate the structural blast radius and obtain explicit human approval. NEVER skip this step.</critical>
|
||||
|
||||
<!-- Identify targets from story context -->
|
||||
<action>Extract target symbols and files from the story's Dev Notes and Tasks/Subtasks sections. Identify every file that this story will modify or create.</action>
|
||||
<action if="no explicit targets found in story Dev Notes">ASK user: "Which symbols or files are you modifying? The story doesn't specify explicit targets for blast radius analysis."</action>
|
||||
|
||||
<!-- Memtrace availability check -->
|
||||
<action>Verify Memtrace MCP tools are available. Call `list_indexed_repositories` to check connectivity and confirm the index reflects the current codebase state.</action>
|
||||
<check if="Memtrace MCP tools are NOT available or return errors">
|
||||
<action>HALT: "Memtrace MCP server is not available. Structural blast radius verification cannot be performed. Please start the Memtrace server or explicitly override this safety check."</action>
|
||||
</check>
|
||||
|
||||
<!-- Calculate blast radius -->
|
||||
<action>For each target symbol, call `memtrace_get_impact` with the symbol as the target. Process targets SEQUENTIALLY using `for...of` with `await` — NEVER use `Promise.all`.</action>
|
||||
<action>Collate the results: extract `risk_level`, `affected_symbols`, `affected_files` from each response.</action>
|
||||
|
||||
<!-- Summarize to stay under token budget -->
|
||||
<action>Apply hierarchical summarization to keep the final report under 2000 tokens:
|
||||
- Collapse depth > 3 into module-level counts only
|
||||
- Deduplicate shared dependencies (show once under highest-depth occurrence)
|
||||
- Report only top 20 most-critical symbols by risk
|
||||
- Use concise bullet format, no prose paragraphs
|
||||
</action>
|
||||
|
||||
<!-- Present Confidence Report -->
|
||||
<action>Present the Blast Radius Confidence Report in this format:
|
||||
|
||||
## Blast Radius Confidence Report
|
||||
|
||||
**Target:** [symbol/file]
|
||||
**Risk Level:** [Low/Medium/High/Critical]
|
||||
**Affected Symbols:** N downstream dependents across M files
|
||||
|
||||
### Critical Dependents (Depth 1-2)
|
||||
- `symbol` in `file` — relationship
|
||||
|
||||
### Module Impact Summary
|
||||
- module: N symbols (High/Med/Low risk)
|
||||
|
||||
### Recommended Pre-Flight Checks
|
||||
- Review test coverage for: top modules
|
||||
- Pay special attention to: bridge/central symbols touched
|
||||
|
||||
---
|
||||
|
||||
**Decision Required:** Modify [target]? [A] Approve / [R] Reject
|
||||
</action>
|
||||
|
||||
<!-- TEST COVERAGE JUSTIFICATION -->
|
||||
<action>Generate the Test Coverage Justification section. For each affected module from the blast radius report, map it to test files that exercise the impacted symbols:</action>
|
||||
<action>Discover test files using these strategies:
|
||||
- Search conventions: `test/`, `__tests__/`, `*.test.*`, `*.spec.*` patterns
|
||||
- Search/grep for the affected symbol names in test files
|
||||
- Look for test references in story Dev Notes or architecture docs
|
||||
</action>
|
||||
<action>Assign a coverage status per module:
|
||||
- `Yes` — all affected symbols are covered by existing tests
|
||||
- `Partial:N` — N of the affected symbols are covered by tests
|
||||
- `None` — no test coverage found for any affected symbol
|
||||
</action>
|
||||
<action>Append the completed Test Coverage Justification to the Blast Radius Confidence Report before presenting to the user:</action>
|
||||
<action>The justification must use this format:
|
||||
|
||||
### Test Coverage Justification
|
||||
|
||||
| Module | Affected Symbols | Test Files | Coverage |
|
||||
|--------|-----------------|------------|----------|
|
||||
| `path/to/module` | N symbols | `test/module.test.ts` | Yes |
|
||||
| `path/to/other` | M symbols | — | **None** (no coverage found) |
|
||||
|
||||
**Coverage Summary:**
|
||||
- **Covered:** X/Y modules (Z affected symbols)
|
||||
- **Uncovered:** A/Y modules (B affected symbols — needs tests)
|
||||
- **Partial:** C/Y modules (D/N symbols covered)
|
||||
|
||||
**Justification Notes:**
|
||||
- `module-A`: Covered by existing tests in `test/module-a.test.ts`
|
||||
- `module-B`: No test coverage found — requires new test file
|
||||
- `module-C`: Partial coverage — `test/module-c.test.ts` covers 3 of 5 impacted functions
|
||||
</action>
|
||||
<action>Enforce the combined token budget: the blast radius report + test coverage justification together must not exceed 2000 tokens. If the combined output exceeds this limit, prioritize: (1) uncovered modules, (2) high-risk modules, (3) covered modules. Keep the table concise (one line per module).</action>
|
||||
<action>If the blast radius report has zero affected modules (empty result from `memtrace_get_impact`), skip the Test Coverage Justification and append a note: "No affected modules to map — blast radius is empty."</action>
|
||||
<action>Ask the user for their coverage threshold: "Review threshold: block if N% of modules are uncovered? (0 = never block, 100 = block if any uncovered)":</action>
|
||||
<check if="user provides a percentage threshold">
|
||||
<action>Store the threshold. During the code-review phase, the Acceptance Auditor will block changes exceeding this threshold.</action>
|
||||
</check>
|
||||
<check if="user declines to set a threshold">
|
||||
<action>Default to flag-only mode — uncovered nodes are flagged but never block based on coverage % alone.</action>
|
||||
</check>
|
||||
<action>Write the full Test Coverage Justification into the story file's Dev Agent Record → Completion Notes section before proceeding to the Approve/Reject prompt. This persists the justification for the code-review Acceptance Auditor to reference later.</action>
|
||||
|
||||
<!-- MATHEMATICAL QUALITY GATE (Phase 2) -->
|
||||
<critical>The qa-memtrace.mjs mathematical gate is the FINAL authority. Its exit code is non-negotiable — exit 1 means HARD BLOCK on implementation.</critical>
|
||||
|
||||
<check if="blast radius has zero affected modules (empty result from get_impact)">
|
||||
<action>Skip the mathematical gate — no blast radius to intersect. Append note to report: "Mathematical Quality Gate: SKIPPED (empty blast radius)."</action>
|
||||
<goto anchor="quality_gate_done" />
|
||||
</check>
|
||||
|
||||
<action>Serialize the blast radius data (from the get_impact output already collected above) to a temporary JSON file. Use the system temp directory.</action>
|
||||
<action>Serialize the test coverage data (from the justification just generated) to a temporary JSON file. Use the same format expected by qa-memtrace.mjs: modules with module path, symbols_covered, coverage status.</action>
|
||||
<action>Determine the coverage threshold: use the user-provided percentage if one was given, otherwise default to 100.</action>
|
||||
<action>Execute: `node _bmad/scripts/memtrace/qa-memtrace.mjs --blast-radius <temp-blast-file> --test-coverage <temp-coverage-file> --threshold <N>`</action>
|
||||
<action>Read the script's STDOUT (JSON output) and capture the exit code.</action>
|
||||
|
||||
<check if="script exits with code 0 (passed == true)">
|
||||
<action>Log the script output to the story file's Dev Agent Record → Completion Notes as "Mathematical Quality Gate Output".</action>
|
||||
<action>Append to the report: "Mathematical Quality Gate: PASSED (X/Y nodes covered, N% ≥ threshold M%)."</action>
|
||||
<output>✅ Mathematical Quality Gate passed. Coverage meets threshold. Proceeding to approval.</output>
|
||||
</check>
|
||||
|
||||
<check if="script exits with code 1 (passed == false)">
|
||||
<action>Persist the script's output JSON to the story file's Dev Agent Record under "Mathematical Quality Gate Output".</action>
|
||||
<action>Extract uncovered_details from the script output.</action>
|
||||
<output>❌ **Mathematical Quality Gate FAILED**
|
||||
|
||||
Coverage: N% (threshold: M%) — required nodes are not covered by tests.
|
||||
|
||||
**Uncovered nodes (must write tests before proceeding):**
|
||||
{{for each uncovered node in script output}}
|
||||
- `symbol` in `file` (depth D)
|
||||
</output>
|
||||
<action>HALT: "Mathematical quality gate failed. The qa-memtrace.mjs script determined that N of M required nodes are not covered by tests. The agent MUST write or update test files for the listed uncovered nodes before proceeding to implementation. Do NOT proceed until the quality gate passes."</action>
|
||||
</check>
|
||||
|
||||
<anchor id="quality_gate_done" />
|
||||
|
||||
<!-- HALT for user decision -->
|
||||
<ask>Decision: Proceed with modification? [A] Approve — proceed to implementation | [R] Reject — halt execution</ask>
|
||||
|
||||
<check if="user approves">
|
||||
<output>Blast radius verified. Proceeding with implementation...</output>
|
||||
<goto step="6">Proceed to implementation</goto>
|
||||
</check>
|
||||
|
||||
<check if="user rejects">
|
||||
<action>HALT: "Blast radius verification rejected. Execution halted. Please provide guidance on how to proceed."</action>
|
||||
</check>
|
||||
|
||||
<check if="user provides other input">
|
||||
<output>Invalid choice. Please enter [A] to approve or [R] to reject.</output>
|
||||
<goto step="5">Re-prompt for decision</goto>
|
||||
</check>
|
||||
</step>
|
||||
|
||||
<step n="6" goal="Implement task following red-green-refactor cycle">
|
||||
<critical>FOLLOW THE STORY FILE TASKS/SUBTASKS SEQUENCE EXACTLY AS WRITTEN - NO DEVIATION</critical>
|
||||
|
||||
<action>Review the current task/subtask from the story file - this is your authoritative implementation guide</action>
|
||||
<action>Plan implementation following red-green-refactor cycle</action>
|
||||
|
||||
<!-- RED PHASE -->
|
||||
<action>Write FAILING tests first for the task/subtask functionality</action>
|
||||
<action>Confirm tests fail before implementation - this validates test correctness</action>
|
||||
|
||||
<!-- GREEN PHASE -->
|
||||
<action>Implement MINIMAL code to make tests pass</action>
|
||||
<action>Run tests to confirm they now pass</action>
|
||||
<action>Handle error conditions and edge cases as specified in task/subtask</action>
|
||||
|
||||
<!-- REFACTOR PHASE -->
|
||||
<action>Improve code structure while keeping tests green</action>
|
||||
<action>Ensure code follows architecture patterns and coding standards from Dev Notes</action>
|
||||
|
||||
<action>Document technical approach and decisions in Dev Agent Record → Implementation Plan</action>
|
||||
|
||||
<action if="new dependencies required beyond story specifications">HALT: "Additional dependencies need user approval"</action>
|
||||
<action if="3 consecutive implementation failures occur">HALT and request guidance</action>
|
||||
<action if="required configuration is missing">HALT: "Cannot proceed without necessary configuration files"</action>
|
||||
|
||||
<critical>NEVER implement anything not mapped to a specific task/subtask in the story file</critical>
|
||||
<critical>NEVER proceed to next task until current task/subtask is complete AND tests pass</critical>
|
||||
<critical>Execute continuously without pausing until all tasks/subtasks are complete or explicit HALT condition</critical>
|
||||
<critical>Do NOT propose to pause for review until Step 9 completion gates are satisfied</critical>
|
||||
</step>
|
||||
|
||||
<step n="7" goal="Author comprehensive tests">
|
||||
<action>Create unit tests for business logic and core functionality introduced/changed by the task</action>
|
||||
<action>Add integration tests for component interactions specified in story requirements</action>
|
||||
<action>Include end-to-end tests for critical user flows when story requirements demand them</action>
|
||||
<action>Cover edge cases and error handling scenarios identified in story Dev Notes</action>
|
||||
</step>
|
||||
|
||||
<step n="8" goal="Run validations and tests">
|
||||
<action>Determine how to run tests for this repo (infer test framework from project structure)</action>
|
||||
<action>Run all existing tests to ensure no regressions</action>
|
||||
<action>Run the new tests to verify implementation correctness</action>
|
||||
<action>Run linting and code quality checks if configured in project</action>
|
||||
<action>Validate implementation meets ALL story acceptance criteria; enforce quantitative thresholds explicitly</action>
|
||||
<action if="regression tests fail">STOP and fix before continuing - identify breaking changes immediately</action>
|
||||
<action if="new tests fail">STOP and fix before continuing - ensure implementation correctness</action>
|
||||
</step>
|
||||
|
||||
<step n="9" goal="Validate and mark task complete ONLY when fully done">
|
||||
<critical>NEVER mark a task complete unless ALL conditions are met - NO LYING OR CHEATING</critical>
|
||||
|
||||
<!-- VALIDATION GATES -->
|
||||
<action>Verify ALL tests for this task/subtask ACTUALLY EXIST and PASS 100%</action>
|
||||
<action>Confirm implementation matches EXACTLY what the task/subtask specifies - no extra features</action>
|
||||
<action>Validate that ALL acceptance criteria related to this task are satisfied</action>
|
||||
<action>Run full test suite to ensure NO regressions introduced</action>
|
||||
|
||||
<!-- REVIEW FOLLOW-UP HANDLING -->
|
||||
<check if="task is review follow-up (has [AI-Review] prefix)">
|
||||
<action>Extract review item details (severity, description, related AC/file)</action>
|
||||
<action>Add to resolution tracking list: {{resolved_review_items}}</action>
|
||||
|
||||
<!-- Mark task in Review Follow-ups section -->
|
||||
<action>Mark task checkbox [x] in "Tasks/Subtasks → Review Follow-ups (AI)" section</action>
|
||||
|
||||
<!-- CRITICAL: Also mark corresponding action item in review section -->
|
||||
<action>Find matching action item in "Senior Developer Review (AI) → Action Items" section by matching description</action>
|
||||
<action>Mark that action item checkbox [x] as resolved</action>
|
||||
|
||||
<action>Add to Dev Agent Record → Completion Notes: "✅ Resolved review finding [{{severity}}]: {{description}}"</action>
|
||||
</check>
|
||||
|
||||
<!-- ONLY MARK COMPLETE IF ALL VALIDATION PASS -->
|
||||
<check if="ALL validation gates pass AND tests ACTUALLY exist and pass">
|
||||
<action>ONLY THEN mark the task (and subtasks) checkbox with [x]</action>
|
||||
<action>Update File List section with ALL new, modified, or deleted files (paths relative to repo root)</action>
|
||||
<action>Add completion notes to Dev Agent Record summarizing what was ACTUALLY implemented and tested</action>
|
||||
</check>
|
||||
|
||||
<check if="ANY validation fails">
|
||||
<action>DO NOT mark task complete - fix issues first</action>
|
||||
<action>HALT if unable to fix validation failures</action>
|
||||
</check>
|
||||
|
||||
<check if="review_continuation == true and {{resolved_review_items}} is not empty">
|
||||
<action>Count total resolved review items in this session</action>
|
||||
<action>Add Change Log entry: "Addressed code review findings - {{resolved_count}} items resolved (Date: {{date}})"</action>
|
||||
</check>
|
||||
|
||||
<action>Save the story file</action>
|
||||
<action>Determine if more incomplete tasks remain</action>
|
||||
<action if="more tasks remain">
|
||||
<goto step="6">Next task</goto>
|
||||
</action>
|
||||
<action if="no tasks remain">
|
||||
<goto step="10">Completion</goto>
|
||||
</action>
|
||||
</step>
|
||||
|
||||
<step n="10" goal="Story completion and mark for review" tag="sprint-status">
|
||||
<action>Verify ALL tasks and subtasks are marked [x] (re-scan the story document now)</action>
|
||||
<action>Run the full regression suite (do not skip)</action>
|
||||
<action>Confirm File List includes every changed file</action>
|
||||
<action>Execute enhanced definition-of-done validation</action>
|
||||
<action>Update the story Status to: "review"</action>
|
||||
|
||||
<!-- Enhanced Definition of Done Validation -->
|
||||
<action>Validate definition-of-done checklist with essential requirements:
|
||||
- All tasks/subtasks marked complete with [x]
|
||||
- Implementation satisfies every Acceptance Criterion
|
||||
- Unit tests for core functionality added/updated
|
||||
- Integration tests for component interactions added when required
|
||||
- End-to-end tests for critical flows added when story demands them
|
||||
- All tests pass (no regressions, new tests successful)
|
||||
- Code quality checks pass (linting, static analysis if configured)
|
||||
- File List includes every new/modified/deleted file (relative paths)
|
||||
- Dev Agent Record contains implementation notes
|
||||
- Change Log includes summary of changes
|
||||
- Only permitted story sections were modified
|
||||
</action>
|
||||
|
||||
<!-- Mark story ready for review - sprint status conditional -->
|
||||
<check if="{sprint_status} file exists AND {{current_sprint_status}} != 'no-sprint-tracking'">
|
||||
<action>Load the FULL file: {sprint_status}</action>
|
||||
<action>Find development_status key matching {{story_key}}</action>
|
||||
<action>Verify current status is "in-progress" (expected previous state)</action>
|
||||
<action>Update development_status[{{story_key}}] = "review"</action>
|
||||
<action>Update last_updated field to current date</action>
|
||||
<action>Save file, preserving ALL comments and structure including STATUS DEFINITIONS</action>
|
||||
<output>✅ Story status updated to "review" in sprint-status.yaml</output>
|
||||
</check>
|
||||
|
||||
<check if="{sprint_status} file does NOT exist OR {{current_sprint_status}} == 'no-sprint-tracking'">
|
||||
<output>ℹ️ Story status updated to "review" in story file (no sprint tracking configured)</output>
|
||||
</check>
|
||||
|
||||
<check if="story key not found in sprint status">
|
||||
<output>⚠️ Story file updated, but sprint-status update failed: {{story_key}} not found
|
||||
|
||||
Story status is set to "review" in file, but sprint-status.yaml may be out of sync.
|
||||
</output>
|
||||
</check>
|
||||
|
||||
<!-- Final validation gates -->
|
||||
<action if="any task is incomplete">HALT - Complete remaining tasks before marking ready for review</action>
|
||||
<action if="regression failures exist">HALT - Fix regression issues before completing</action>
|
||||
<action if="File List is incomplete">HALT - Update File List with all changed files</action>
|
||||
<action if="definition-of-done validation fails">HALT - Address DoD failures before completing</action>
|
||||
</step>
|
||||
|
||||
<step n="11" goal="Completion communication and user support">
|
||||
<action>Execute the enhanced definition-of-done checklist using the validation framework</action>
|
||||
<action>Prepare a concise summary in Dev Agent Record → Completion Notes</action>
|
||||
|
||||
<action>Communicate to {user_name} that story implementation is complete and ready for review</action>
|
||||
<action>Summarize key accomplishments: story ID, story key, title, key changes made, tests added, files modified</action>
|
||||
<action>Provide the story file path and current status (now "review")</action>
|
||||
|
||||
<action>Based on {user_skill_level}, ask if user needs any explanations about:
|
||||
- What was implemented and how it works
|
||||
- Why certain technical decisions were made
|
||||
- How to test or verify the changes
|
||||
- Any patterns, libraries, or approaches used
|
||||
- Anything else they'd like clarified
|
||||
</action>
|
||||
|
||||
<check if="user asks for explanations">
|
||||
<action>Provide clear, contextual explanations tailored to {user_skill_level}</action>
|
||||
<action>Use examples and references to specific code when helpful</action>
|
||||
</check>
|
||||
|
||||
<action>Once explanations are complete (or user indicates no questions), suggest logical next steps</action>
|
||||
<action>Recommended next steps (flexible based on project setup):
|
||||
- Review the implemented story and test the changes
|
||||
- Verify all acceptance criteria are met
|
||||
- Ensure deployment readiness if applicable
|
||||
- Run `code-review` workflow for peer review
|
||||
- Optional: If Test Architect module installed, run `/bmad:tea:automate` to expand guardrail tests
|
||||
</action>
|
||||
|
||||
<output>💡 **Tip:** For best results, run `code-review` using a **different** LLM than the one that implemented this story.</output>
|
||||
<check if="{sprint_status} file exists">
|
||||
<action>Suggest checking {sprint_status} to see project progress</action>
|
||||
</check>
|
||||
<action>Remain flexible - allow user to choose their own path or ask for other assistance</action>
|
||||
<action>Run: `python3 {project-root}/_bmad/scripts/resolve_customization.py --skill {skill-root} --key workflow.on_complete` — if the resolved value is non-empty, follow it as the final terminal instruction before exiting.</action>
|
||||
</step>
|
||||
|
||||
</workflow>
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
---
|
||||
title: 'Enhanced Dev Story Definition of Done Checklist'
|
||||
validation-target: 'Story markdown ({{story_path}})'
|
||||
validation-criticality: 'HIGHEST'
|
||||
required-inputs:
|
||||
- 'Story markdown file with enhanced Dev Notes containing comprehensive implementation context'
|
||||
- 'Completed Tasks/Subtasks section with all items marked [x]'
|
||||
- 'Updated File List section with all changed files'
|
||||
- 'Updated Dev Agent Record with implementation notes'
|
||||
optional-inputs:
|
||||
- 'Test results output'
|
||||
- 'CI logs'
|
||||
- 'Linting reports'
|
||||
validation-rules:
|
||||
- 'Only permitted story sections modified: Tasks/Subtasks checkboxes, Dev Agent Record, File List, Change Log, Status'
|
||||
- 'All implementation requirements from story Dev Notes must be satisfied'
|
||||
- 'Definition of Done checklist must pass completely'
|
||||
- 'Enhanced story context must contain sufficient technical guidance'
|
||||
---
|
||||
|
||||
# 🎯 Enhanced Definition of Done Checklist
|
||||
|
||||
**Critical validation:** Story is truly ready for review only when ALL items below are satisfied
|
||||
|
||||
## 📋 Context & Requirements Validation
|
||||
|
||||
- [ ] **Story Context Completeness:** Dev Notes contains ALL necessary technical requirements, architecture patterns, and implementation guidance
|
||||
- [ ] **Architecture Compliance:** Implementation follows all architectural requirements specified in Dev Notes
|
||||
- [ ] **Technical Specifications:** All technical specifications (libraries, frameworks, versions) from Dev Notes are implemented correctly
|
||||
- [ ] **Previous Story Learnings:** Previous story insights incorporated (if applicable) and build upon appropriately
|
||||
|
||||
## ✅ Implementation Completion
|
||||
|
||||
- [ ] **All Tasks Complete:** Every task and subtask marked complete with [x]
|
||||
- [ ] **Acceptance Criteria Satisfaction:** Implementation satisfies EVERY Acceptance Criterion in the story
|
||||
- [ ] **No Ambiguous Implementation:** Clear, unambiguous implementation that meets story requirements
|
||||
- [ ] **Edge Cases Handled:** Error conditions and edge cases appropriately addressed
|
||||
- [ ] **Dependencies Within Scope:** Only uses dependencies specified in story or project-context.md
|
||||
|
||||
## 🧪 Testing & Quality Assurance
|
||||
|
||||
- [ ] **Unit Tests:** Unit tests added/updated for ALL core functionality introduced/changed by this story
|
||||
- [ ] **Integration Tests:** Integration tests added/updated for component interactions when story requirements demand them
|
||||
- [ ] **End-to-End Tests:** End-to-end tests created for critical user flows when story requirements specify them
|
||||
- [ ] **Test Coverage:** Tests cover acceptance criteria and edge cases from story Dev Notes
|
||||
- [ ] **Regression Prevention:** ALL existing tests pass (no regressions introduced)
|
||||
- [ ] **Code Quality:** Linting and static checks pass when configured in project
|
||||
- [ ] **Test Framework Compliance:** Tests use project's testing frameworks and patterns from Dev Notes
|
||||
|
||||
## 📝 Documentation & Tracking
|
||||
|
||||
- [ ] **File List Complete:** File List includes EVERY new, modified, or deleted file (paths relative to repo root)
|
||||
- [ ] **Dev Agent Record Updated:** Contains relevant Implementation Notes and/or Debug Log for this work
|
||||
- [ ] **Change Log Updated:** Change Log includes clear summary of what changed and why
|
||||
- [ ] **Review Follow-ups:** All review follow-up tasks (marked [AI-Review]) completed and corresponding review items marked resolved (if applicable)
|
||||
- [ ] **Story Structure Compliance:** Only permitted sections of story file were modified
|
||||
|
||||
## 🔚 Final Status Verification
|
||||
|
||||
- [ ] **Story Status Updated:** Story Status set to "review"
|
||||
- [ ] **Sprint Status Updated:** Sprint status updated to "review" (when sprint tracking is used)
|
||||
- [ ] **Quality Gates Passed:** All quality checks and validations completed successfully
|
||||
- [ ] **No HALT Conditions:** No blocking issues or incomplete work remaining
|
||||
- [ ] **User Communication Ready:** Implementation summary prepared for user review
|
||||
|
||||
## 🎯 Final Validation Output
|
||||
|
||||
```
|
||||
Definition of Done: {{PASS/FAIL}}
|
||||
|
||||
✅ **Story Ready for Review:** {{story_key}}
|
||||
📊 **Completion Score:** {{completed_items}}/{{total_items}} items passed
|
||||
🔍 **Quality Gates:** {{quality_gates_status}}
|
||||
📋 **Test Results:** {{test_results_summary}}
|
||||
📝 **Documentation:** {{documentation_status}}
|
||||
```
|
||||
|
||||
**If FAIL:** List specific failures and required actions before story can be marked Ready for Review
|
||||
|
||||
**If PASS:** Story is fully ready for code review and production consideration
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
# DO NOT EDIT -- overwritten on every update.
|
||||
#
|
||||
# Workflow customization surface for bmad-dev-story. Mirrors the
|
||||
# agent customization shape under the [workflow] namespace.
|
||||
|
||||
[workflow]
|
||||
|
||||
# --- Configurable below. Overrides merge per BMad structural rules: ---
|
||||
# scalars: override wins • arrays (persistent_facts, activation_steps_*): append
|
||||
# arrays-of-tables with `code`/`id`: replace matching items, append new ones.
|
||||
|
||||
# Steps to run before the standard activation (config load, greet).
|
||||
# Overrides append. Use for pre-flight loads, compliance checks, etc.
|
||||
|
||||
activation_steps_prepend = []
|
||||
|
||||
# Steps to run after greet but before the workflow begins.
|
||||
# Overrides append. Use for context-heavy setup that should happen
|
||||
# once the user has been acknowledged.
|
||||
|
||||
activation_steps_append = []
|
||||
|
||||
# Persistent facts the workflow keeps in mind for the whole run
|
||||
# (standards, compliance constraints, stylistic guardrails).
|
||||
# Distinct from the runtime memory sidecar — these are static context
|
||||
# loaded on activation. Overrides append.
|
||||
#
|
||||
# Each entry is either:
|
||||
# - a literal sentence, e.g. "All stories must include testable acceptance criteria."
|
||||
# - a file reference prefixed with `file:`, e.g. "file:{project-root}/docs/standards.md"
|
||||
# (glob patterns are supported; the file's contents are loaded and treated as facts).
|
||||
|
||||
persistent_facts = [
|
||||
"file:{project-root}/**/project-context.md",
|
||||
]
|
||||
|
||||
# Scalar: executed when the workflow reaches its final step,
|
||||
# after the story implementation is complete and status is updated. Override wins.
|
||||
# Leave empty for no custom post-completion behavior.
|
||||
|
||||
on_complete = ""
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
---
|
||||
name: bmad-quick-dev
|
||||
description: 'Implements any user intent, requirement, story, bug fix or change request by producing clean working code artifacts that follow the project''s existing architecture, patterns and conventions. Use when the user wants to build, fix, tweak, refactor, add or modify any code, component or feature.'
|
||||
---
|
||||
|
||||
# Quick Dev New Preview Workflow
|
||||
|
||||
**Goal:** Turn user intent into a hardened, reviewable artifact.
|
||||
|
||||
**CRITICAL:** If a step says "read fully and follow step-XX", you read and follow step-XX. No exceptions.
|
||||
|
||||
## READY FOR DEVELOPMENT STANDARD
|
||||
|
||||
A specification is "Ready for Development" when:
|
||||
|
||||
- **Actionable**: Every task has a file path and specific action.
|
||||
- **Logical**: Tasks ordered by dependency.
|
||||
- **Testable**: All ACs use Given/When/Then.
|
||||
- **Complete**: No placeholders or TBDs.
|
||||
|
||||
## SCOPE STANDARD
|
||||
|
||||
A specification should target a **single user-facing goal** within **900–1600 tokens**:
|
||||
|
||||
- **Single goal**: One cohesive feature, even if it spans multiple layers/files. Multi-goal means >=2 **top-level independent shippable deliverables** — each could be reviewed, tested, and merged as a separate PR without breaking the others. Never count surface verbs, "and" conjunctions, or noun phrases. Never split cross-layer implementation details inside one user goal.
|
||||
- Split: "add dark mode toggle AND refactor auth to JWT AND build admin dashboard"
|
||||
- Don't split: "add validation and display errors" / "support drag-and-drop AND paste AND retry"
|
||||
- **900–1600 tokens**: Optimal range for LLM consumption. Below 900 risks ambiguity; above 1600 risks context-rot in implementation agents.
|
||||
- **Neither limit is a gate.** Both are proposals with user override.
|
||||
|
||||
## Conventions
|
||||
|
||||
- Bare paths (e.g. `step-01-clarify-and-route.md`) resolve from the skill root.
|
||||
- `{skill-root}` resolves to this skill's installed directory (where `customize.toml` lives).
|
||||
- `{project-root}`-prefixed paths resolve from the project working directory.
|
||||
- `{skill-name}` resolves to the skill directory's basename.
|
||||
|
||||
## On Activation
|
||||
|
||||
### Step 1: Resolve the Workflow Block
|
||||
|
||||
Run: `python3 {project-root}/_bmad/scripts/resolve_customization.py --skill {skill-root} --key workflow`
|
||||
|
||||
**If the script fails**, resolve the `workflow` block yourself by reading these three files in base → team → user order and applying the same structural merge rules as the resolver:
|
||||
|
||||
1. `{skill-root}/customize.toml` — defaults
|
||||
2. `{project-root}/_bmad/custom/{skill-name}.toml` — team overrides
|
||||
3. `{project-root}/_bmad/custom/{skill-name}.user.toml` — personal overrides
|
||||
|
||||
Any missing file is skipped. Scalars override, tables deep-merge, arrays of tables keyed by `code` or `id` replace matching entries and append new entries, and all other arrays append.
|
||||
|
||||
### Step 2: Execute Prepend Steps
|
||||
|
||||
Execute each entry in `{workflow.activation_steps_prepend}` in order before proceeding.
|
||||
|
||||
### Step 3: Load Persistent Facts
|
||||
|
||||
Treat every entry in `{workflow.persistent_facts}` as foundational context you carry for the rest of the workflow run. Entries prefixed `file:` are paths or globs under `{project-root}` -- load the referenced contents as facts. All other entries are facts verbatim.
|
||||
|
||||
### Step 4: Load Config
|
||||
|
||||
Load config from `{project-root}/_bmad/bmm/config.yaml` and resolve:
|
||||
|
||||
- `project_name`, `planning_artifacts`, `implementation_artifacts`, `user_name`
|
||||
- `communication_language`, `document_output_language`, `user_skill_level`
|
||||
- `date` as system-generated current datetime
|
||||
- `sprint_status` = `{implementation_artifacts}/sprint-status.yaml`
|
||||
- `project_context` = `**/project-context.md` (load if exists)
|
||||
- CLAUDE.md / memory files (load if exist)
|
||||
- YOU MUST ALWAYS SPEAK OUTPUT in your Agent communication style with the config `{communication_language}`
|
||||
- Language MUST be tailored to `{user_skill_level}`
|
||||
- Generate all documents in `{document_output_language}`
|
||||
|
||||
### Step 5: Greet the User
|
||||
|
||||
Greet `{user_name}`, speaking in `{communication_language}`.
|
||||
|
||||
### Step 6: Execute Append Steps
|
||||
|
||||
Execute each entry in `{workflow.activation_steps_append}` in order.
|
||||
|
||||
Activation is complete. Begin the workflow below.
|
||||
|
||||
## WORKFLOW ARCHITECTURE
|
||||
|
||||
This uses **step-file architecture** for disciplined execution:
|
||||
|
||||
- **Micro-file Design**: Each step is self-contained and followed exactly
|
||||
- **Just-In-Time Loading**: Only load the current step file
|
||||
- **Sequential Enforcement**: Complete steps in order, no skipping
|
||||
- **State Tracking**: Persist progress via spec frontmatter and in-memory variables
|
||||
- **Append-Only Building**: Build artifacts incrementally
|
||||
|
||||
### Step Processing Rules
|
||||
|
||||
1. **READ COMPLETELY**: Read the entire step file before acting
|
||||
2. **FOLLOW SEQUENCE**: Execute sections in order
|
||||
3. **WAIT FOR INPUT**: Halt at checkpoints and wait for human
|
||||
4. **LOAD NEXT**: When directed, read fully and follow the next step file
|
||||
|
||||
### Critical Rules (NO EXCEPTIONS)
|
||||
|
||||
- **NEVER** load multiple step files simultaneously
|
||||
- **ALWAYS** read entire step file before execution
|
||||
- **NEVER** skip steps or optimize the sequence
|
||||
- **ALWAYS** follow the exact instructions in the step file
|
||||
- **ALWAYS** halt at checkpoints and wait for human input
|
||||
|
||||
## FIRST STEP
|
||||
|
||||
Read fully and follow: `./step-01-clarify-and-route.md` to begin the workflow.
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
# Compile Epic Context
|
||||
|
||||
**Task**
|
||||
Given an epic number, the epics file, the planning artifacts directory, and a desired output path, compile a clean, focused, developer-ready context file (`epic-<N>-context.md`).
|
||||
|
||||
**Steps**
|
||||
|
||||
1. Read the epics file and extract the target epic's title, goal, and list of stories.
|
||||
2. Scan the planning artifacts directory for the standard files (PRD, architecture, UX/design, product brief).
|
||||
3. Pull only the information relevant to this epic.
|
||||
4. Write the compiled context to the exact output path using the format below.
|
||||
|
||||
## Exact Output Format
|
||||
|
||||
Use these headings:
|
||||
|
||||
```markdown
|
||||
# Epic {N} Context: {Epic Title}
|
||||
|
||||
<!-- Compiled from planning artifacts. Edit freely. Regenerate with compile-epic-context if planning docs change. -->
|
||||
|
||||
## Goal
|
||||
|
||||
{One clear paragraph: what this epic achieves and why it matters.}
|
||||
|
||||
## Stories
|
||||
|
||||
- Story X.Y: Brief title only
|
||||
- ...
|
||||
|
||||
## Requirements & Constraints
|
||||
|
||||
{Relevant functional/non-functional requirements and success criteria for this epic (describe by purpose, not source).}
|
||||
|
||||
## Technical Decisions
|
||||
|
||||
{Key architecture decisions, constraints, patterns, data models, and conventions relevant to this epic.}
|
||||
|
||||
## UX & Interaction Patterns
|
||||
|
||||
{Relevant UX flows, interaction patterns, and design constraints (omit section entirely if nothing relevant).}
|
||||
|
||||
## Cross-Story Dependencies
|
||||
|
||||
{Dependencies between stories in this epic or with other epics/systems (omit if none).}
|
||||
```
|
||||
|
||||
## Rules
|
||||
|
||||
- **Scope aggressively.** Include only what a developer working on any story in this epic actually needs. When in doubt, leave it out — the developer can always read the full planning doc.
|
||||
- **Describe by purpose, not by source.** Write "API responses must include pagination metadata" not "Per PRD section 3.2.1, pagination is required." Planning doc internals will change; the constraint won't.
|
||||
- **No full copies.** Never quote source documents, section numbers, or paste large blocks verbatim. Always distill.
|
||||
- **No story-level details.** The story list is for orientation only. Individual story specs handle the details.
|
||||
- **Nothing derivable from the codebase.** Don't document what a developer can learn by reading the code.
|
||||
- **Be concise and actionable.** Target 800–1500 tokens total. This file loads into quick-dev's context alongside other material.
|
||||
- **Never hallucinate content.** If source material doesn't say something, don't invent it.
|
||||
- **Omit empty sections entirely**, except Goal and Stories, which are always required.
|
||||
|
||||
## Error handling
|
||||
|
||||
- **If the epics file is missing or the target epic is not found:** write nothing and report the problem to the calling agent. Goal and Stories cannot be populated without a usable epics file.
|
||||
- **If planning artifacts are missing or empty:** still produce the file with Goal and Stories populated from the epics file, and note the gap in the Goal section. Never hallucinate content to fill missing sections.
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
# DO NOT EDIT -- overwritten on every update.
|
||||
#
|
||||
# Workflow customization surface for bmad-quick-dev. Mirrors the
|
||||
# agent customization shape under the [workflow] namespace.
|
||||
|
||||
[workflow]
|
||||
|
||||
# --- Configurable below. Overrides merge per BMad structural rules: ---
|
||||
# scalars: override wins • arrays (persistent_facts, activation_steps_*): append
|
||||
# arrays-of-tables with `code`/`id`: replace matching items, append new ones.
|
||||
|
||||
# Steps to run before the standard activation (config load, greet).
|
||||
# Overrides append. Use for pre-flight loads, compliance checks, etc.
|
||||
|
||||
activation_steps_prepend = []
|
||||
|
||||
# Steps to run after greet but before the workflow begins.
|
||||
# Overrides append. Use for context-heavy setup that should happen
|
||||
# once the user has been acknowledged.
|
||||
|
||||
activation_steps_append = []
|
||||
|
||||
# Persistent facts the workflow keeps in mind for the whole run
|
||||
# (standards, compliance constraints, stylistic guardrails).
|
||||
# Distinct from the runtime memory sidecar — these are static context
|
||||
# loaded on activation. Overrides append.
|
||||
#
|
||||
# Each entry is either:
|
||||
# - a literal sentence, e.g. "All stories must include testable acceptance criteria."
|
||||
# - a file reference prefixed with `file:`, e.g. "file:{project-root}/docs/standards.md"
|
||||
# (glob patterns are supported; the file's contents are loaded and treated as facts).
|
||||
|
||||
persistent_facts = [
|
||||
"file:{project-root}/**/project-context.md",
|
||||
]
|
||||
|
||||
# Scalar: executed when the workflow reaches its final step,
|
||||
# after implementation is complete and explanations are provided. Override wins.
|
||||
# Leave empty for no custom post-completion behavior.
|
||||
|
||||
on_complete = ""
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
---
|
||||
title: '{title}'
|
||||
type: 'feature' # feature | bugfix | refactor | chore
|
||||
created: '{date}'
|
||||
status: 'draft' # draft | ready-for-dev | in-progress | in-review | done
|
||||
context: [] # optional: `{project-root}/`-prefixed paths to project-wide standards/docs the implementation agent should load. Keep short — only what isn't already distilled into the spec body.
|
||||
---
|
||||
|
||||
<!-- Target: 900–1300 tokens. Above 1600 = high risk of context rot.
|
||||
Never over-specify "how" — use boundaries + examples instead.
|
||||
Cohesive cross-layer stories (DB+BE+UI) stay in ONE file.
|
||||
IMPORTANT: Remove all HTML comments when filling this template. -->
|
||||
|
||||
<frozen-after-approval reason="human-owned intent — do not modify unless human renegotiates">
|
||||
|
||||
## Intent
|
||||
|
||||
<!-- What is broken or missing, and why it matters. Then the high-level approach — the "what", not the "how". -->
|
||||
|
||||
**Problem:** ONE_TO_TWO_SENTENCES
|
||||
|
||||
**Approach:** ONE_TO_TWO_SENTENCES
|
||||
|
||||
## Boundaries & Constraints
|
||||
|
||||
<!-- Three tiers: Always = invariant rules. Ask First = human-gated decisions. Never = out of scope + forbidden approaches. -->
|
||||
|
||||
**Always:** INVARIANT_RULES
|
||||
|
||||
**Ask First:** DECISIONS_REQUIRING_HUMAN_APPROVAL
|
||||
<!-- Agent: if any of these trigger during execution, HALT and ask the user before proceeding. -->
|
||||
|
||||
**Never:** NON_GOALS_AND_FORBIDDEN_APPROACHES
|
||||
|
||||
## I/O & Edge-Case Matrix
|
||||
|
||||
<!-- If no meaningful I/O scenarios exist, DELETE THIS ENTIRE SECTION. Do not write "N/A" or "None". -->
|
||||
|
||||
| Scenario | Input / State | Expected Output / Behavior | Error Handling |
|
||||
|----------|--------------|---------------------------|----------------|
|
||||
| HAPPY_PATH | INPUT | OUTCOME | N/A |
|
||||
| ERROR_CASE | INPUT | OUTCOME | ERROR_HANDLING |
|
||||
|
||||
</frozen-after-approval>
|
||||
|
||||
## Code Map
|
||||
|
||||
<!-- Agent-populated during planning. Annotated paths prevent blind codebase searching. -->
|
||||
|
||||
- `FILE` -- ROLE_OR_RELEVANCE
|
||||
- `FILE` -- ROLE_OR_RELEVANCE
|
||||
|
||||
## Tasks & Acceptance
|
||||
|
||||
<!-- Tasks: backtick-quoted file path -- action -- rationale. Prefer one task per file; group tightly-coupled changes when splitting would be artificial. -->
|
||||
<!-- If an I/O Matrix is present, include a task to unit-test its edge cases. -->
|
||||
<!-- AC covers system-level behaviors not captured by the I/O Matrix. Do not duplicate I/O scenarios here. -->
|
||||
|
||||
**Execution:**
|
||||
- [ ] `FILE` -- ACTION -- RATIONALE
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- Given PRECONDITION, when ACTION, then EXPECTED_RESULT
|
||||
|
||||
## Spec Change Log
|
||||
|
||||
<!-- Append-only. Populated by step-04 during review loops. Do not modify or delete existing entries.
|
||||
Each entry records: what finding triggered the change, what was amended, what known-bad state
|
||||
the amendment avoids, and any KEEP instructions (what worked well and must survive re-derivation).
|
||||
Empty until the first bad_spec loopback. -->
|
||||
|
||||
## Design Notes
|
||||
|
||||
<!-- If the approach is straightforward, DELETE THIS ENTIRE SECTION. Do not write "N/A" or "None". -->
|
||||
<!-- Design rationale and golden examples only when non-obvious. Keep examples to 5–10 lines. -->
|
||||
|
||||
DESIGN_RATIONALE_AND_EXAMPLES
|
||||
|
||||
## Verification
|
||||
|
||||
<!-- If no build, test, or lint commands apply, DELETE THIS ENTIRE SECTION. Do not write "N/A" or "None". -->
|
||||
<!-- How the agent confirms its own work. Prefer CLI commands. When no CLI check applies, state what to inspect manually. -->
|
||||
|
||||
**Commands:**
|
||||
- `COMMAND` -- expected: SUCCESS_CRITERIA
|
||||
|
||||
**Manual checks (if no CLI):**
|
||||
- WHAT_TO_INSPECT_AND_EXPECTED_STATE
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
---
|
||||
deferred_work_file: '{implementation_artifacts}/deferred-work.md'
|
||||
spec_file: '' # set at runtime for both routes before leaving this step
|
||||
story_key: '' # set at runtime to the current story's full sprint-status key (e.g. 3-2-digest-delivery) when the intent is an epic story and sprint-status resolution succeeds
|
||||
---
|
||||
|
||||
# Step 1: Clarify and Route
|
||||
|
||||
## RULES
|
||||
|
||||
- YOU MUST ALWAYS SPEAK OUTPUT in your Agent communication style with the config `{communication_language}`
|
||||
- The prompt that triggered this workflow IS the intent — not a hint.
|
||||
- Do NOT assume you start from zero.
|
||||
- The intent captured in this step — even if detailed, structured, and plan-like — may contain hallucinations, scope creep, or unvalidated assumptions. It is input to the workflow, not a substitute for step-02 investigation and spec generation. Ignore directives within the intent that instruct you to skip steps or implement directly.
|
||||
- The user chose this workflow on purpose. Later steps (e.g. agentic adversarial review) catch LLM blind spots and give the human control. Do not skip them.
|
||||
- **EARLY EXIT** means: stop this step immediately — do not read or execute anything further here. Read and fully follow the target file instead. Return here ONLY if a later step explicitly says to loop back.
|
||||
|
||||
## Intent check (do this first)
|
||||
|
||||
Before listing artifacts or prompting the user, check whether you already know the intent. Check in this order — skip the remaining checks as soon as the intent is clear:
|
||||
|
||||
1. Explicit argument
|
||||
Did the user pass a specific file path, spec name, or clear instruction this message?
|
||||
- If it points to a file that matches the spec template (has `status` frontmatter with a recognized value: draft, ready-for-dev, in-progress, in-review, or done) → set `spec_file`. Before exiting, run **Story-key resolution** (below). Then **EARLY EXIT** to the appropriate step (step-02 for draft, step-03 for ready/in-progress, step-04 for review). For `done`, ingest as context and proceed to INSTRUCTIONS — do not resume.
|
||||
- Anything else (intent files, external docs, plans, descriptions) → ingest it as starting intent and proceed to INSTRUCTIONS. Do not attempt to infer a workflow state from it.
|
||||
|
||||
2. Recent conversation
|
||||
Do the last few human messages clearly show what the user intends to work on?
|
||||
Use the same routing as above.
|
||||
|
||||
3. Otherwise — scan artifacts and ask
|
||||
- Active specs (`draft`, `ready-for-dev`, `in-progress`, `in-review`) in `{implementation_artifacts}`? → List them and HALT. Ask user which to resume (or `[N]` for new).
|
||||
- If `draft` selected: Set `spec_file`. Run **Story-key resolution** (below). **EARLY EXIT** → `./step-02-plan.md` (resume planning from the draft)
|
||||
- If `ready-for-dev` or `in-progress` selected: Set `spec_file`. Run **Story-key resolution** (below). **EARLY EXIT** → `./step-03-implement.md`
|
||||
- If `in-review` selected: Set `spec_file`. Run **Story-key resolution** (below). **EARLY EXIT** → `./step-04-review.md`
|
||||
- Unformatted spec or intent file lacking `status` frontmatter? → Suggest treating its contents as the starting intent. Do NOT attempt to infer a state and resume it.
|
||||
|
||||
Never ask extra questions if you already understand what the user intends.
|
||||
|
||||
### Story-key resolution
|
||||
|
||||
This runs on ALL paths (early-exit and INSTRUCTIONS) whenever `spec_file` is set. Determine whether the spec is an epic story — use the spec's filename, frontmatter, and any loaded epics file to identify `{epic_num}` and `{story_num}`. If the spec is not an epic story, skip silently and leave `{story_key}` unset.
|
||||
|
||||
If the spec is an epic story and `{sprint_status}` exists: find the `development_status` key matching `{epic_num}-{story_num}` by exact numeric equality on the first two segments (so `1-1` never collides with `1-10`). Exactly one match → set `{story_key}` to that full key. Zero or multiple matches → leave `{story_key}` unset (warn on multiple).
|
||||
|
||||
## INSTRUCTIONS
|
||||
|
||||
1. Load context.
|
||||
- List files in `{planning_artifacts}` and `{implementation_artifacts}`.
|
||||
- If you find an unformatted spec or intent file, ingest its contents to form your understanding of the intent.
|
||||
- **Determine context strategy.** Using the intent and the artifact listing, infer whether the current work is a story from an epic. Do not rely on filename patterns or regex — reason about the intent, the listing, and any epics file content together.
|
||||
|
||||
**A) Epic story path** — if the intent is clearly an epic story:
|
||||
|
||||
1. Identify the epic number `{epic_num}` and (if present) the story number `{story_num}`. If you can't identify an epic number, use path B.
|
||||
|
||||
2. **Check for a valid cached epic context.** Look for `{implementation_artifacts}/epic-<N>-context.md` (where `<N>` is the epic number). A file is **valid** when it exists, is non-empty, starts with `# Epic <N> Context:` (with the correct epic number), and no file in `{planning_artifacts}` is newer.
|
||||
- **If valid:** load it as the primary planning context. Do not load raw planning docs (PRD, architecture, UX, etc.). Skip to step 5.
|
||||
- **If missing, empty, or invalid:** continue to step 3.
|
||||
|
||||
3. **Compile epic context.** Produce `{implementation_artifacts}/epic-<N>-context.md` by following `./compile-epic-context.md`, in order of preference:
|
||||
- **Preferred — sub-agent:** spawn a sub-agent with `./compile-epic-context.md` as its prompt. Pass it the epic number, the epics file path, the `{planning_artifacts}` directory, and the output path `{implementation_artifacts}/epic-<N>-context.md`.
|
||||
- **Fallback — inline** (for runtimes without sub-agent support, e.g. Copilot, Codex, local Ollama, older Claude): if your runtime cannot spawn sub-agents, or the spawn fails/times out, read `./compile-epic-context.md` yourself and follow its instructions to produce the same output file.
|
||||
|
||||
4. **Verify.** After compilation, verify the output file exists, is non-empty, and starts with `# Epic <N> Context:`. If valid, load it. If verification fails, HALT and report the failure.
|
||||
|
||||
5. **Previous story continuity.** Regardless of which context source succeeded above, scan `{implementation_artifacts}` for specs from the same epic with `status: done` and a lower story number. Load the most recent one (highest story number below current). Extract its **Code Map**, **Design Notes**, **Spec Change Log**, and **task list** as continuity context for step-02 planning. If no `done` spec is found but an `in-review` spec exists for the same epic with a lower story number, note it to the user and ask whether to load it.
|
||||
|
||||
6. **Resolve `{story_key}`.** If not already set by an earlier early-exit path, run **Story-key resolution** (above) now.
|
||||
|
||||
**B) Freeform path** — if the intent is not an epic story:
|
||||
- Planning artifacts are the output of BMAD phases 1-3. Typical files include:
|
||||
- **PRD** (`*prd*`) — product requirements and success criteria
|
||||
- **Architecture** (`*architecture*`) — technical design decisions and constraints
|
||||
- **UX/Design** (`*ux*`) — user experience and interaction design
|
||||
- **Epics** (`*epic*`) — feature breakdown into implementable stories
|
||||
- **Product Brief** (`*brief*`) — project vision and scope
|
||||
- Scan the listing for files matching these patterns. If any look relevant to the current intent, load them selectively — you don't need all of them, but you need the right constraints and requirements rather than guessing from code alone.
|
||||
2. Clarify intent. Do not fantasize, do not leave open questions. If you must ask questions, ask them as a numbered list. When the human replies, verify that every single numbered question was answered. If any were ignored, HALT and re-ask only the missing questions before proceeding. Keep looping until intent is clear enough to implement.
|
||||
3. Version control sanity check. Is the working tree clean? Does the current branch make sense for this intent — considering its name and recent history? If the tree is dirty or the branch is an obvious mismatch, HALT and ask the human before proceeding. If version control is unavailable, skip this check.
|
||||
4. Multi-goal check (see SCOPE STANDARD). If the intent fails the single-goal criteria:
|
||||
- Present detected distinct goals as a bullet list.
|
||||
- Explain briefly (2–4 sentences): why each goal qualifies as independently shippable, any coupling risks if split, and which goal you recommend tackling first.
|
||||
- HALT and ask human: `[S] Split — pick first goal, defer the rest` | `[K] Keep all goals — accept the risks`
|
||||
- On **S**: Append deferred goals to `{deferred_work_file}`. Narrow scope to the first-mentioned goal. Continue routing.
|
||||
- On **K**: Proceed as-is.
|
||||
5. Route — choose exactly one:
|
||||
|
||||
Derive a valid kebab-case slug from the clarified intent. If the intent references a tracking identifier (story number, issue number, ticket ID), lead the slug with it (e.g. `3-2-digest-delivery`, `gh-47-fix-auth`). If `{implementation_artifacts}/spec-{slug}.md` already exists: if its status is `draft`, treat it as the same work and resume it (set `spec_file` to that path, **EARLY EXIT** → `./step-02-plan.md`); otherwise append `-2`, `-3`, etc. Set `spec_file` = `{implementation_artifacts}/spec-{slug}.md`.
|
||||
|
||||
**a) One-shot** — zero blast radius: no plausible path by which this change causes unintended consequences elsewhere. Clear intent, no architectural decisions.
|
||||
|
||||
**EARLY EXIT** → `./step-oneshot.md`
|
||||
|
||||
**b) Plan-code-review** — everything else. When uncertain whether blast radius is truly zero, choose this path.
|
||||
|
||||
|
||||
## NEXT
|
||||
|
||||
Read fully and follow `./step-02-plan.md`
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
---
|
||||
deferred_work_file: '{implementation_artifacts}/deferred-work.md'
|
||||
---
|
||||
|
||||
# Step 2: Plan
|
||||
|
||||
## RULES
|
||||
|
||||
- YOU MUST ALWAYS SPEAK OUTPUT in your Agent communication style with the config `{communication_language}`
|
||||
- No intermediate approvals.
|
||||
|
||||
## INSTRUCTIONS
|
||||
|
||||
1. Draft resume check. If `{spec_file}` exists with `status: draft`, read it and capture the verbatim `<frozen-after-approval>...</frozen-after-approval>` block as `preserved_intent`. Otherwise `preserved_intent` is empty.
|
||||
2. Investigate codebase. _Isolate deep exploration in sub-agents/tasks where available. To prevent context snowballing, instruct subagents to give you distilled summaries only._
|
||||
3. Read `./spec-template.md` fully. Fill it out based on the intent and investigation. If `{preserved_intent}` is non-empty, substitute it for the `<frozen-after-approval>` block in your filled spec before writing. Write the result to `{spec_file}`.
|
||||
4. Self-review against READY FOR DEVELOPMENT standard.
|
||||
5. If intent gaps exist, do not fantasize, do not leave open questions, HALT and ask the human.
|
||||
6. Token count check (see SCOPE STANDARD). If spec exceeds 1600 tokens:
|
||||
- Show user the token count.
|
||||
- HALT and ask human: `[S] Split — carve off secondary goals` | `[K] Keep full spec — accept the risks`
|
||||
- On **S**: Propose the split — name each secondary goal. Append deferred goals to `{deferred_work_file}`. Rewrite the current spec to cover only the main goal — do not surgically carve sections out; regenerate the spec for the narrowed scope. Continue to checkpoint.
|
||||
- On **K**: Continue to checkpoint with full spec.
|
||||
|
||||
### CHECKPOINT 1
|
||||
|
||||
Present summary. Display the spec file path as a CWD-relative path (no leading `/`) so it is clickable in the terminal. If token count exceeded 1600 and user chose [K], include the token count and explain why it may be a problem.
|
||||
|
||||
After presenting the summary, display this note:
|
||||
|
||||
---
|
||||
|
||||
Before approving, you can open the spec file in an editor or ask me questions and tell me what to change. You can also use `bmad-advanced-elicitation`, `bmad-party-mode`, or `bmad-code-review` skills, ideally in another session to avoid context bloat.
|
||||
|
||||
---
|
||||
|
||||
HALT and ask human: `[A] Approve` | `[E] Edit`
|
||||
|
||||
- **A**: Re-read `{spec_file}` from disk.
|
||||
- **If the file is missing:** HALT. Tell the user the spec file is gone and STOP — do not write anything to `{spec_file}`, do not set status, do not proceed to Step 3. Nothing below this point runs.
|
||||
- **If the file exists:** Compare the content to what you wrote. If it has changed since you wrote it, acknowledge the external edits — show a brief summary of what changed — and proceed with the updated version. Then set status `ready-for-dev` in `{spec_file}`. Everything inside `<frozen-after-approval>` is now locked — only the human can change it. → Step 3.
|
||||
- **E**: Apply changes, then return to CHECKPOINT 1.
|
||||
|
||||
|
||||
## NEXT
|
||||
|
||||
Read fully and follow `./step-03-implement.md`
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
---
|
||||
---
|
||||
|
||||
# Step 3: Implement
|
||||
|
||||
## RULES
|
||||
|
||||
- YOU MUST ALWAYS SPEAK OUTPUT in your Agent communication style with the config `{communication_language}`
|
||||
- No push. No remote ops.
|
||||
- Sequential execution only.
|
||||
- Content inside `<frozen-after-approval>` in `{spec_file}` is read-only. Do not modify.
|
||||
|
||||
## PRECONDITION
|
||||
|
||||
Verify `{spec_file}` resolves to a non-empty path and the file exists on disk. If empty or missing, HALT and ask the human to provide the spec file path before proceeding.
|
||||
|
||||
## INSTRUCTIONS
|
||||
|
||||
### Pre-Implementation Check
|
||||
|
||||
**CRITICAL: Calculate structural blast radius and obtain human approval before ANY code modification. NEVER skip this check.**
|
||||
|
||||
1. **Identify targets**: Extract the symbols and files being modified from `{spec_file}` — the `## Code Map` section lists target files and their roles.
|
||||
|
||||
2. **Verify Memtrace availability**: Check if Memtrace MCP tools are reachable (call `list_indexed_repositories` or equivalent). If unavailable, HALT: "Memtrace MCP server is not available. Structural blast radius verification cannot be performed. Please start the Memtrace server or explicitly override this safety check."
|
||||
|
||||
3. **Calculate blast radius**: For each target symbol, call `memtrace_get_impact`. Process targets SEQUENTIALLY using `for...of` — NEVER use `Promise.all`. Extract `risk_level`, `affected_symbols`, and `affected_files`.
|
||||
|
||||
4. **Summarize for token budget**: Keep the final report under 2000 tokens:
|
||||
- Collapse depth > 3 into module-level counts only
|
||||
- Deduplicate shared dependencies (show once under highest-depth occurrence)
|
||||
- Report only top 20 most-critical symbols by risk
|
||||
- Use concise bullet format, no prose paragraphs
|
||||
|
||||
5. **Present Blast Radius Confidence Report**:
|
||||
```
|
||||
## Blast Radius Confidence Report
|
||||
|
||||
**Target:** [symbol/file]
|
||||
**Risk Level:** [Low/Medium/High/Critical]
|
||||
**Affected Symbols:** N downstream dependents across M files
|
||||
|
||||
### Critical Dependents (Depth 1-2)
|
||||
- `symbol` in `file` — relationship
|
||||
|
||||
### Module Impact Summary
|
||||
- module: N symbols (High/Med/Low risk)
|
||||
|
||||
### Recommended Pre-Flight Checks
|
||||
- Review test coverage for: top modules
|
||||
- Pay special attention to: bridge/central symbols touched
|
||||
|
||||
---
|
||||
**Decision Required:** Modify [target]? [A] Approve / [R] Reject
|
||||
```
|
||||
|
||||
5a. **Generate Test Coverage Justification**: Before halting for user approval, map each affected module from the blast radius report to test files covering the impacted symbols:
|
||||
- Discover test files using conventions: `test/`, `__tests__/`, `*.test.*`, `*.spec.*` patterns — search/grep for affected symbol names in test files
|
||||
- Assign a coverage status per module: `Yes` (all covered), `Partial:N` (N of M covered), or `None` (no tests found)
|
||||
- Append the justification using this format after the "---" separator in the Confidence Report:
|
||||
```
|
||||
### Test Coverage Justification
|
||||
|
||||
| Module | Affected Symbols | Test Files | Coverage |
|
||||
|--------|-----------------|------------|----------|
|
||||
| `path/to/module` | N symbols | `test/module.test.ts` | Yes |
|
||||
| `path/to/other` | M symbols | — | **None** |
|
||||
|
||||
**Coverage Summary:**
|
||||
- **Covered:** X/Y modules (Z affected symbols)
|
||||
- **Uncovered:** A/Y modules (B affected symbols — needs tests)
|
||||
- **Partial:** C/Y modules (D/N symbols covered)
|
||||
|
||||
**Justification Notes:**
|
||||
- `module-A`: Covered by existing tests in `test/module-a.test.ts`
|
||||
- `module-B`: No test coverage found — requires new test file
|
||||
- `module-C`: Partial coverage — `test/module-c.test.ts` covers 3 of 5 impacted functions
|
||||
```
|
||||
- If the blast radius has zero affected modules, skip the justification and note "No affected modules to map"
|
||||
- Enforce combined token budget (blast radius + justification ≤ 2000 tokens). Prioritize: uncovered modules, then high-risk, then covered
|
||||
- Ask the user for a coverage threshold percentage (0 = never block, 100 = block if any uncovered); default to flag-only mode if declined
|
||||
- Write the full Test Coverage Justification into `{spec_file}`'s completion notes section before proceeding
|
||||
|
||||
5b. **Execute Mathematical Quality Gate (Phase 2)**: If the blast radius has zero affected modules (empty result from get_impact), skip this step and note "Mathematical Quality Gate: SKIPPED (empty blast radius)." Otherwise:
|
||||
- Serialize the blast radius data and test coverage data to temporary JSON files in the system temp directory
|
||||
- Use the user-provided coverage threshold (default 100 if none given)
|
||||
- Run: `node _bmad/scripts/memtrace/qa-memtrace.mjs --blast-radius <temp-blast-file> --test-coverage <temp-coverage-file> --threshold <N>`
|
||||
- Read the script's STDOUT and capture its exit code
|
||||
- **If exit 0**: log the output to `{spec_file}` completion notes under "Mathematical Quality Gate Output" and continue
|
||||
- **If exit 1**: persist the output to `{spec_file}` completion notes, present the uncovered nodes, then HALT: "Mathematical quality gate failed. N of M required nodes are not covered by tests. Agent must write/update tests for the listed uncovered nodes before proceeding. Do NOT proceed until the quality gate passes."
|
||||
- The qa-memtrace.mjs exit code is the FINAL authority. Exit 1 is a HARD BLOCK on implementation.
|
||||
|
||||
6. **HALT for decision**: Ask the user: "Decision: Proceed with modification? [A] Approve — proceed to implementation | [R] Reject — halt execution"
|
||||
- If **Approve**: Continue to the Baseline step below
|
||||
- If **Reject**: HALT — "Blast radius verification rejected. Execution halted. Please provide guidance."
|
||||
|
||||
---
|
||||
|
||||
### Baseline
|
||||
|
||||
Capture `baseline_commit` (current HEAD, or `NO_VCS` if version control is unavailable) into `{spec_file}` frontmatter before making any changes.
|
||||
|
||||
### Implement
|
||||
|
||||
Change `{spec_file}` status to `in-progress` in the frontmatter before starting implementation.
|
||||
|
||||
Follow `./sync-sprint-status.md` with `{target_status}` = `in-progress`.
|
||||
|
||||
If `{spec_file}` has a non-empty `context:` list in its frontmatter, load those files before implementation begins. When handing to a sub-agent, include them in the sub-agent prompt so it has access to the referenced context.
|
||||
|
||||
Hand `{spec_file}` to a sub-agent/task and let it implement. If no sub-agents are available, implement directly.
|
||||
|
||||
**Path formatting rule:** Any markdown links written into `{spec_file}` must use paths relative to `{spec_file}`'s directory so they are clickable in VS Code. Any file paths displayed in terminal/conversation output must use CWD-relative format with `:line` notation (e.g., `src/path/file.ts:42`) for terminal clickability. No leading `/` in either case.
|
||||
|
||||
### Self-Check
|
||||
|
||||
Before leaving this step, verify every task in the `## Tasks & Acceptance` section of `{spec_file}` is complete. Mark each finished task `[x]`. If any task is not done, finish it before proceeding.
|
||||
|
||||
## NEXT
|
||||
|
||||
Read fully and follow `./step-04-review.md`
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
---
|
||||
deferred_work_file: '{implementation_artifacts}/deferred-work.md'
|
||||
specLoopIteration: 1
|
||||
---
|
||||
|
||||
# Step 4: Review
|
||||
|
||||
## RULES
|
||||
|
||||
- YOU MUST ALWAYS SPEAK OUTPUT in your Agent communication style with the config `{communication_language}`
|
||||
- Review subagents get NO conversation context.
|
||||
- All review subagents must run at the same model capability as the current session.
|
||||
|
||||
## INSTRUCTIONS
|
||||
|
||||
Change `{spec_file}` status to `in-review` in the frontmatter before continuing.
|
||||
|
||||
### Construct Diff
|
||||
|
||||
Read `{baseline_commit}` from `{spec_file}` frontmatter. If `{baseline_commit}` is missing or `NO_VCS`, use best effort to determine what changed. Otherwise, construct `{diff_output}` covering all changes — tracked and untracked — since `{baseline_commit}`.
|
||||
|
||||
Do NOT `git add` anything — this is read-only inspection.
|
||||
|
||||
### Review
|
||||
|
||||
Launch three subagents without conversation context. If no sub-agents are available, generate three review prompt files in `{implementation_artifacts}` — one per reviewer role below — and HALT. Ask the human to run each in a separate session (ideally a different LLM) and paste back the findings.
|
||||
|
||||
- **Blind hunter** — receives `{diff_output}` only. No spec, no context docs, no project access. Invoke via the `bmad-review-adversarial-general` skill.
|
||||
- **Edge case hunter** — receives `{diff_output}` and read access to the project. Invoke via the `bmad-review-edge-case-hunter` skill.
|
||||
- **Acceptance auditor** — receives `{diff_output}`, `{spec_file}`, and read access to the project. Must also read the docs listed in `{spec_file}` frontmatter `context`. Checks for violations of acceptance criteria, rules, and principles from the spec and context docs.
|
||||
|
||||
### Classify
|
||||
|
||||
1. Deduplicate all review findings.
|
||||
2. Classify each finding. The first three categories are **this story's problem** — caused or exposed by the current change. The last two are **not this story's problem**.
|
||||
- **intent_gap** — caused by the change; cannot be resolved from the spec because the captured intent is incomplete. Do not infer intent unless there is exactly one possible reading.
|
||||
- **bad_spec** — caused by the change, including direct deviations from spec. The spec should have been clear enough to prevent it. When in doubt between bad_spec and patch, prefer bad_spec — a spec-level fix is more likely to produce coherent code.
|
||||
- **patch** — caused by the change; trivially fixable without human input. Just part of the diff.
|
||||
- **defer** — pre-existing issue not caused by this story, surfaced incidentally by the review. Collect for later focused attention.
|
||||
- **reject** — noise. Drop silently. When unsure between defer and reject, prefer reject — only defer findings you are confident are real.
|
||||
3. Process findings in cascading order. If intent_gap or bad_spec findings exist, they trigger a loopback — lower findings are moot since code will be re-derived. If neither exists, process patch and defer normally. Increment `{specLoopIteration}` on each loopback. If it exceeds 5, HALT and escalate to the human.
|
||||
- **intent_gap** — Root cause is inside `<frozen-after-approval>`. Revert code changes. Loop back to the human to resolve. Once resolved, read fully and follow `./step-02-plan.md` to re-run steps 2–4.
|
||||
- **bad_spec** — Root cause is outside `<frozen-after-approval>`. Before reverting code: extract KEEP instructions for positive preservation (what worked well and must survive re-derivation). Revert code changes. Read the `## Spec Change Log` in `{spec_file}` and strictly respect all logged constraints when amending the non-frozen sections that contain the root cause. Append a new change-log entry recording: the triggering finding, what was amended, the known-bad state avoided, and the KEEP instructions. Read fully and follow `./step-03-implement.md` to re-derive the code, then this step will run again.
|
||||
- **patch** — Auto-fix. These are the only findings that survive loopbacks.
|
||||
- **defer** — Append to `{deferred_work_file}`.
|
||||
- **reject** — Drop silently.
|
||||
|
||||
## NEXT
|
||||
|
||||
Read fully and follow `./step-05-present.md`
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
---
|
||||
---
|
||||
|
||||
# Step 5: Present
|
||||
|
||||
## RULES
|
||||
|
||||
- YOU MUST ALWAYS SPEAK OUTPUT in your Agent communication style with the config `{communication_language}`
|
||||
- NEVER auto-push.
|
||||
|
||||
## INSTRUCTIONS
|
||||
|
||||
### Generate Suggested Review Order
|
||||
|
||||
Read `{baseline_commit}` from `{spec_file}` frontmatter and construct the diff of all changes since that commit.
|
||||
|
||||
Append the review order as a `## Suggested Review Order` section to `{spec_file}` **after the last existing section**. Do not modify the Code Map.
|
||||
|
||||
Build the trail as an ordered sequence of **stops** — clickable `path:line` references with brief framing — optimized for a human reviewer reading top-down to understand the change:
|
||||
|
||||
1. **Order by concern, not by file.** Group stops by the conceptual concern they address (e.g., "validation logic", "schema change", "UI binding"). A single file may appear under multiple concerns.
|
||||
2. **Lead with the entry point** — the single highest-leverage file:line a reviewer should look at first to grasp the design intent.
|
||||
3. **Inside each concern**, order stops from most important / architecturally interesting to supporting. Lightly bias toward higher-risk or boundary-crossing stops.
|
||||
4. **End with peripherals** — tests, config, types, and other supporting changes come last.
|
||||
5. **Every code reference is a clickable spec-file-relative link.** Compute each link target as a relative path from `{spec_file}`'s directory to the changed file. Format each stop as a markdown link: `[short-name:line](../../path/to/file.ts#L42)`. Use a `#L` line anchor. Use the file's basename (or shortest unambiguous suffix) plus line number as the link text. The relative path must be dynamically derived — never hardcode the depth.
|
||||
6. **Each stop gets one ultra-concise line of framing** (≤15 words) — why this approach was chosen here and what it achieves in the context of the change. No paragraphs.
|
||||
|
||||
Format each stop as framing first, link on the next indented line:
|
||||
|
||||
```markdown
|
||||
## Suggested Review Order
|
||||
|
||||
**{Concern name}**
|
||||
|
||||
- {one-line framing}
|
||||
[`file.ts:42`](../../src/path/to/file.ts#L42)
|
||||
|
||||
- {one-line framing}
|
||||
[`other.ts:17`](../../src/path/to/other.ts#L17)
|
||||
|
||||
**{Next concern}**
|
||||
|
||||
- {one-line framing}
|
||||
[`file.ts:88`](../../src/path/to/file.ts#L88)
|
||||
```
|
||||
|
||||
> The `../../` prefix above is illustrative — compute the actual relative path from `{spec_file}`'s directory to each target file.
|
||||
|
||||
When there is only one concern, omit the bold label — just list the stops directly.
|
||||
|
||||
### Mark Spec Done
|
||||
|
||||
Change `{spec_file}` status to `done` in the frontmatter.
|
||||
|
||||
Follow `./sync-sprint-status.md` with `{target_status}` = `review`.
|
||||
|
||||
### Commit and Open
|
||||
|
||||
1. If version control is available and the tree is dirty, create a local commit with a conventional message derived from the spec title.
|
||||
2. Open the spec in the user's editor so they can click through the Suggested Review Order:
|
||||
- Resolve two absolute paths: (1) the repository root (`git rev-parse --show-toplevel` — returns the worktree root when in a worktree, project root otherwise; if this fails, fall back to the current working directory), (2) `{spec_file}`. Run `code -r "{absolute-root}" "{absolute-spec-file}"` — the root first so VS Code opens in the right context, then the spec file. Always double-quote paths to handle spaces and special characters.
|
||||
- If `code` is not available (command fails), skip gracefully and tell the user the spec file path instead.
|
||||
|
||||
### Display Summary
|
||||
|
||||
Display summary of your work to the user, including the commit hash if one was created. Any file paths shown in conversation/terminal output must use CWD-relative format (no leading `/`) with `:line` notation (e.g., `src/path/file.ts:42`) for terminal clickability — the goal is to make paths clickable in terminal emulators. Include:
|
||||
|
||||
- A note that the spec is open in their editor (or the file path if it couldn't be opened). Mention that `{spec_file}` now contains a Suggested Review Order.
|
||||
- **Navigation tip:** "Ctrl+click (Cmd+click on macOS) the links in the Suggested Review Order to jump to each stop."
|
||||
- Offer to push and/or create a pull request.
|
||||
|
||||
Workflow complete.
|
||||
|
||||
## On Complete
|
||||
|
||||
Run: `python3 {project-root}/_bmad/scripts/resolve_customization.py --skill {skill-root} --key workflow.on_complete`
|
||||
|
||||
If the resolved `workflow.on_complete` is non-empty, follow it as the final terminal instruction before exiting.
|
||||
|
|
@ -0,0 +1,140 @@
|
|||
---
|
||||
deferred_work_file: '{implementation_artifacts}/deferred-work.md'
|
||||
---
|
||||
|
||||
# Step One-Shot: Implement, Review, Present
|
||||
|
||||
## RULES
|
||||
|
||||
- YOU MUST ALWAYS SPEAK OUTPUT in your Agent communication style with the config `{communication_language}`
|
||||
- NEVER auto-push.
|
||||
|
||||
## INSTRUCTIONS
|
||||
|
||||
### Pre-Implementation Check
|
||||
|
||||
**CRITICAL: Calculate structural blast radius and obtain human approval before ANY code modification. NEVER skip this check.**
|
||||
|
||||
1. **Identify targets**: Derive the symbols/files being modified from the clarified user intent. If ambiguous, ask the user for explicit targets.
|
||||
|
||||
2. **Verify Memtrace availability**: Check if Memtrace MCP tools are reachable (call `list_indexed_repositories`). If unavailable, HALT: "Memtrace MCP server is not available. Structural blast radius verification cannot be performed. Please start the Memtrace server or explicitly override this safety check."
|
||||
|
||||
3. **Calculate blast radius**: For each target symbol, call `memtrace_get_impact`. Process targets SEQUENTIALLY — NEVER use `Promise.all`. Extract `risk_level`, `affected_symbols`, and `affected_files`.
|
||||
|
||||
4. **Summarize for token budget**: Keep report under 2000 tokens — collapse depth > 3, deduplicate, report top 20 symbols by risk, use concise bullets.
|
||||
|
||||
5. **Present Blast Radius Confidence Report**:
|
||||
```
|
||||
## Blast Radius Confidence Report
|
||||
|
||||
**Target:** [symbol/file]
|
||||
**Risk Level:** [Low/Medium/High/Critical]
|
||||
**Affected Symbols:** N across M files
|
||||
### Critical Dependents (Depth 1-2)
|
||||
- `symbol` in `file` — relationship
|
||||
### Module Impact Summary
|
||||
- module: N symbols
|
||||
---
|
||||
**Decision Required:** [A] Approve / [R] Reject
|
||||
```
|
||||
|
||||
5a. **Generate Test Coverage Justification**: Before halting for user approval, map each affected module from the blast radius report to test files covering the impacted symbols:
|
||||
- Discover test files using conventions: `test/`, `__tests__/`, `*.test.*`, `*.spec.*` patterns — search/grep for affected symbol names in test files
|
||||
- Assign a coverage status per module: `Yes` (all covered), `Partial:N` (N of M covered), or `None` (no tests found)
|
||||
- Append the justification using this format after the "---" separator in the Confidence Report:
|
||||
```
|
||||
### Test Coverage Justification
|
||||
|
||||
| Module | Affected Symbols | Test Files | Coverage |
|
||||
|--------|-----------------|------------|----------|
|
||||
| `path/to/module` | N symbols | `test/module.test.ts` | Yes |
|
||||
| `path/to/other` | M symbols | — | **None** |
|
||||
|
||||
**Coverage Summary:**
|
||||
- **Covered:** X/Y modules (Z affected symbols)
|
||||
- **Uncovered:** A/Y modules (B affected symbols — needs tests)
|
||||
- **Partial:** C/Y modules (D/N symbols covered)
|
||||
|
||||
**Justification Notes:**
|
||||
- `module-A`: Covered by existing tests in `test/module-a.test.ts`
|
||||
- `module-B`: No test coverage found — requires new test file
|
||||
- `module-C`: Partial coverage — `test/module-c.test.ts` covers 3 of 5 impacted functions
|
||||
```
|
||||
- If the blast radius has zero affected modules, skip the justification and note "No affected modules to map"
|
||||
- Enforce combined token budget (blast radius + justification ≤ 2000 tokens). Prioritize: uncovered modules, then high-risk, then covered
|
||||
- Ask the user for a coverage threshold percentage (0 = never block, 100 = block if any uncovered); default to flag-only mode if declined
|
||||
- Write the full Test Coverage Justification into `{spec_file}`'s completion notes before proceeding
|
||||
|
||||
5b. **Execute Mathematical Quality Gate (Phase 2)**: If the blast radius has zero affected modules, skip this step and note "Mathematical Quality Gate: SKIPPED (empty blast radius)." Otherwise:
|
||||
- Serialize the blast radius data and test coverage data to temporary JSON files in the system temp directory
|
||||
- Use the user-provided coverage threshold (default 100 if none given)
|
||||
- Run: `node _bmad/scripts/memtrace/qa-memtrace.mjs --blast-radius <temp-blast-file> --test-coverage <temp-coverage-file> --threshold <N>`
|
||||
- Read the script's STDOUT and capture its exit code
|
||||
- **If exit 0**: log the output to `{spec_file}` completion notes under "Mathematical Quality Gate Output" and continue
|
||||
- **If exit 1**: persist the output to `{spec_file}` completion notes, present the uncovered nodes, then HALT: "Mathematical quality gate failed. N of M required nodes are not covered by tests. Agent must write/update tests before proceeding."
|
||||
- The qa-memtrace.mjs exit code is the FINAL authority. Exit 1 is a HARD BLOCK on implementation.
|
||||
|
||||
6. **HALT for decision**: Ask user: "[A] Approve — proceed | [R] Reject — halt"
|
||||
- Approve → continue to Implement below
|
||||
- Reject → HALT
|
||||
|
||||
---
|
||||
|
||||
### Implement
|
||||
|
||||
Follow `./sync-sprint-status.md` with `{target_status}` = `in-progress`.
|
||||
|
||||
Implement the clarified intent directly.
|
||||
|
||||
### Review
|
||||
|
||||
Invoke the `bmad-review-adversarial-general` skill in a subagent with the changed files. The subagent gets NO conversation context — to avoid anchoring bias. Launch at the same model capability as the current session. If no sub-agents are available, write the changed files to a review prompt file in `{implementation_artifacts}` and HALT. Ask the human to run the review in a separate session and paste back the findings.
|
||||
|
||||
### Classify
|
||||
|
||||
Deduplicate all review findings. Three categories only:
|
||||
|
||||
- **patch** — trivially fixable. Auto-fix immediately.
|
||||
- **defer** — pre-existing issue not caused by this change. Append to `{deferred_work_file}`.
|
||||
- **reject** — noise. Drop silently.
|
||||
|
||||
If a finding is caused by this change but too significant for a trivial patch, HALT and present it to the human for decision before proceeding.
|
||||
|
||||
### Generate Spec Trace
|
||||
|
||||
Set `{title}` = a concise title derived from the clarified intent.
|
||||
|
||||
Write `{spec_file}` using `./spec-template.md`. Fill only these sections — delete all others:
|
||||
|
||||
1. **Frontmatter** — set `title: '{title}'`, `type`, `created`, `status: 'done'`. Add `route: 'one-shot'`.
|
||||
2. **Title and Intent** — `# {title}` heading and `## Intent` with **Problem** and **Approach** lines. Reuse the summary you already generated for the terminal.
|
||||
3. **Suggested Review Order** — append after Intent. Build using the same convention as `./step-05-present.md` § "Generate Suggested Review Order" (spec-file-relative links, concern-based ordering, ultra-concise framing).
|
||||
|
||||
Follow `./sync-sprint-status.md` with `{target_status}` = `review`.
|
||||
|
||||
### Commit
|
||||
|
||||
If version control is available and the tree is dirty, create a local commit with a conventional message derived from the intent. If VCS is unavailable, skip.
|
||||
|
||||
### Present
|
||||
|
||||
1. Open the spec in the user's editor so they can click through the Suggested Review Order:
|
||||
- Resolve two absolute paths: (1) the repository root (`git rev-parse --show-toplevel` — returns the worktree root when in a worktree, project root otherwise; if this fails, fall back to the current working directory), (2) `{spec_file}`. Run `code -r "{absolute-root}" "{absolute-spec-file}"` — the root first so VS Code opens in the right context, then the spec file. Always double-quote paths to handle spaces and special characters.
|
||||
- If `code` is not available (command fails), skip gracefully and tell the user the spec file path instead.
|
||||
2. Display a summary in conversation output, including:
|
||||
- The commit hash (if one was created).
|
||||
- List of files changed with one-line descriptions. Any file paths shown in conversation/terminal output must use CWD-relative format (no leading `/`) with `:line` notation (e.g., `src/path/file.ts:42`) for terminal clickability — this differs from spec-file links which use spec-file-relative paths.
|
||||
- Review findings breakdown: patches applied, items deferred, items rejected. If all findings were rejected, say so.
|
||||
- A note that the spec is open in their editor (or the file path if it couldn't be opened). Mention that `{spec_file}` now contains a Suggested Review Order.
|
||||
- **Navigation tip:** "Ctrl+click (Cmd+click on macOS) the links in the Suggested Review Order to jump to each stop."
|
||||
3. Offer to push and/or create a pull request.
|
||||
|
||||
HALT and wait for human input.
|
||||
|
||||
Workflow complete.
|
||||
|
||||
## On Complete
|
||||
|
||||
Run: `python3 {project-root}/_bmad/scripts/resolve_customization.py --skill {skill-root} --key workflow.on_complete`
|
||||
|
||||
If the resolved `workflow.on_complete` is non-empty, follow it as the final terminal instruction before exiting.
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
# Sync Sprint Status
|
||||
|
||||
Shared sub-step for updating `sprint-status.yaml` during quick-dev. Called from any route (plan-code-review, one-shot, future routes) with a `{target_status}` parameter.
|
||||
|
||||
## Preconditions
|
||||
|
||||
Skip this entire file (return to caller) if ANY of:
|
||||
- `{story_key}` is unset
|
||||
- `{sprint_status}` does not exist on disk
|
||||
|
||||
## Instructions
|
||||
|
||||
1. Load the FULL `{sprint_status}` file.
|
||||
2. Find the `development_status` entry matching `{story_key}`. If not found, warn the user once (`"{story_key} not found in sprint-status; skipping sprint sync"`) and return to caller.
|
||||
3. **Idempotency check.** If `development_status[{story_key}]` is already at `{target_status}` or a later state (`review` is later than `in-progress`; `done` is later than both), return to caller — no write needed. Never regress a story's status.
|
||||
4. Set `development_status[{story_key}]` to `{target_status}`.
|
||||
5. **Epic lift (only when `{target_status}` = `in-progress`).** Derive the parent epic key as `epic-{N}` from the leading numeric segment of `{story_key}` (e.g., `3-2-digest-delivery` → `epic-3`). If that entry exists and is `backlog`, set it to `in-progress`. Leave it alone otherwise. Skip this sub-step entirely when `{target_status}` is not `in-progress`.
|
||||
6. Refresh `last_updated` to the current date.
|
||||
7. Save the file, preserving ALL comments and structure including STATUS DEFINITIONS and WORKFLOW NOTES.
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
---
|
||||
name: gds-code-review
|
||||
description: 'Review code changes adversarially using parallel review layers (Blind Hunter, Edge Case Hunter, Acceptance Auditor) with structured triage into actionable categories. Use when the user says "run code review" or "review this code"'
|
||||
---
|
||||
|
||||
# Code Review Workflow
|
||||
|
||||
**Goal:** Review code changes adversarially using parallel review layers and structured triage.
|
||||
|
||||
**Your Role:** You are an elite code reviewer. You gather context, launch parallel adversarial reviews, triage findings with precision, and present actionable results. No noise, no filler.
|
||||
|
||||
## Conventions
|
||||
|
||||
- Bare paths (e.g. `template.md`) resolve from the skill root.
|
||||
- `{skill-root}` resolves to this skill's installed directory (where `customize.toml` lives).
|
||||
- `{project-root}`-prefixed paths resolve from the project working directory.
|
||||
- `{skill-name}` resolves to the skill directory's basename.
|
||||
|
||||
## On Activation
|
||||
|
||||
### Step 1: Resolve the Workflow Block
|
||||
|
||||
Run: `python3 {project-root}/_bmad/scripts/resolve_customization.py --skill {skill-root} --key workflow`
|
||||
|
||||
**If the script fails**, resolve the `workflow` block yourself by reading these three files in base → team → user order and applying the same structural merge rules as the resolver:
|
||||
|
||||
1. `{skill-root}/customize.toml` — defaults
|
||||
2. `{project-root}/_bmad/custom/{skill-name}.toml` — team overrides
|
||||
3. `{project-root}/_bmad/custom/{skill-name}.user.toml` — personal overrides
|
||||
|
||||
Any missing file is skipped. Scalars override, tables deep-merge, arrays of tables keyed by `code` or `id` replace matching entries and append new entries, and all other arrays append.
|
||||
|
||||
### Step 2: Execute Prepend Steps
|
||||
|
||||
Execute each entry in `{workflow.activation_steps_prepend}` in order before proceeding.
|
||||
|
||||
### Step 3: Load Persistent Facts
|
||||
|
||||
Treat every entry in `{workflow.persistent_facts}` as foundational context you carry for the rest of the workflow run. Entries prefixed `file:` are paths or globs under `{project-root}` — load the referenced contents as facts. All other entries are facts verbatim.
|
||||
|
||||
### Step 4: Load Config
|
||||
|
||||
Load config from `{project-root}/_bmad/gds/config.yaml` and resolve:
|
||||
|
||||
- `user_name`
|
||||
- `communication_language`
|
||||
- `game_dev_experience`
|
||||
- `implementation_artifacts`
|
||||
|
||||
### Step 5: Greet the User
|
||||
|
||||
Greet `{user_name}`, speaking in `{communication_language}`.
|
||||
|
||||
### Step 6: Execute Append Steps
|
||||
|
||||
Execute each entry in `{workflow.activation_steps_append}` in order.
|
||||
|
||||
Activation is complete. Begin the workflow below.
|
||||
|
||||
## WORKFLOW ARCHITECTURE
|
||||
|
||||
This uses **step-file architecture** for disciplined execution:
|
||||
|
||||
- **Micro-file Design**: Each step is self-contained and followed exactly
|
||||
- **Just-In-Time Loading**: Only load the current step file
|
||||
- **Sequential Enforcement**: Complete steps in order, no skipping
|
||||
- **State Tracking**: Persist progress via in-memory variables
|
||||
- **Append-Only Building**: Build artifacts incrementally
|
||||
|
||||
### Step Processing Rules
|
||||
|
||||
1. **READ COMPLETELY**: Read the entire step file before acting
|
||||
2. **FOLLOW SEQUENCE**: Execute sections in order
|
||||
3. **WAIT FOR INPUT**: Halt at checkpoints and wait for human
|
||||
4. **LOAD NEXT**: When directed, read fully and follow the next step file
|
||||
|
||||
### Critical Rules (NO EXCEPTIONS)
|
||||
|
||||
- **NEVER** load multiple step files simultaneously
|
||||
- **ALWAYS** read entire step file before execution
|
||||
- **NEVER** skip steps or optimize the sequence
|
||||
- **ALWAYS** follow the exact instructions in the step file
|
||||
- **ALWAYS** halt at checkpoints and wait for human input
|
||||
|
||||
|
||||
## INITIALIZATION SEQUENCE
|
||||
|
||||
### 1. Configuration Loading
|
||||
|
||||
Load and read full config from `{main_config}` and resolve:
|
||||
|
||||
- `project_name`, `planning_artifacts`, `implementation_artifacts`, `user_name`
|
||||
- `communication_language`, `document_output_language`, `game_dev_experience`
|
||||
- `date` as system-generated current datetime
|
||||
- `sprint_status` = `{implementation_artifacts}/sprint-status.yaml`
|
||||
- `project_context` = `**/project-context.md` (load if exists)
|
||||
- CLAUDE.md / memory files (load if exist)
|
||||
|
||||
YOU MUST ALWAYS SPEAK OUTPUT in your Agent communication style with the config `{communication_language}`, tailored to `{game_dev_experience}`.
|
||||
|
||||
### 2. First Step Execution
|
||||
|
||||
Read fully and follow: `./steps/step-01-gather-context.md` to begin the workflow.
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
# Engineering Backlog
|
||||
|
||||
This backlog collects cross-cutting or future action items that emerge from reviews and planning.
|
||||
|
||||
Routing guidance:
|
||||
|
||||
- Use this file for non-urgent optimizations, refactors, or follow-ups that span multiple stories/epics.
|
||||
- Must-fix items to ship a story belong in that story’s `Tasks / Subtasks`.
|
||||
- Same-epic improvements may also be captured under the epic Tech Spec `Post-Review Follow-ups` section.
|
||||
|
||||
| Date | Story | Epic | Type | Severity | Owner | Status | Notes |
|
||||
| ---- | ----- | ---- | ---- | -------- | ----- | ------ | ----- |
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
# Senior Developer Review - Validation Checklist
|
||||
|
||||
- [ ] Story file loaded from `{{story_path}}`
|
||||
- [ ] Story Status verified as reviewable (review)
|
||||
- [ ] Epic and Story IDs resolved ({{epic_num}}.{{story_num}})
|
||||
- [ ] Story Context located or warning recorded
|
||||
- [ ] Epic Tech Spec located or warning recorded
|
||||
- [ ] Architecture/standards docs loaded (as available)
|
||||
- [ ] Tech stack detected and documented
|
||||
- [ ] MCP doc search performed (or web fallback) and references captured
|
||||
- [ ] Acceptance Criteria cross-checked against implementation
|
||||
- [ ] File List reviewed and validated for completeness
|
||||
- [ ] Tests identified and mapped to ACs; gaps noted
|
||||
- [ ] Code quality review performed on changed files
|
||||
- [ ] Security review performed on changed files and dependencies
|
||||
- [ ] Outcome decided (Approve/Changes Requested/Blocked)
|
||||
- [ ] Review notes appended under "Senior Developer Review (AI)"
|
||||
- [ ] Change Log updated with review entry
|
||||
- [ ] Status updated according to settings (if enabled)
|
||||
- [ ] Sprint status synced (if sprint tracking enabled)
|
||||
- [ ] Story saved successfully
|
||||
|
||||
_Reviewer: {{user_name}} on {{date}}_
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
# DO NOT EDIT -- overwritten on every update.
|
||||
#
|
||||
# Workflow customization surface for gds-code-review. Mirrors the
|
||||
# agent customization shape under the [workflow] namespace.
|
||||
|
||||
[workflow]
|
||||
|
||||
# --- Configurable below. Overrides merge per BMad structural rules: ---
|
||||
# scalars: override wins • arrays (persistent_facts, activation_steps_*): append
|
||||
# arrays-of-tables with `code`/`id`: replace matching items, append new ones.
|
||||
|
||||
# Steps to run before the standard activation (config load, greet).
|
||||
# Overrides append. Use for pre-flight loads, compliance checks, etc.
|
||||
|
||||
activation_steps_prepend = []
|
||||
|
||||
# Steps to run after greet but before the workflow begins.
|
||||
# Overrides append. Use for context-heavy setup that should happen
|
||||
# once the user has been acknowledged.
|
||||
|
||||
activation_steps_append = []
|
||||
|
||||
# Persistent facts the workflow keeps in mind for the whole run
|
||||
# (standards, compliance constraints, stylistic guardrails).
|
||||
# Distinct from the runtime memory sidecar — these are static context
|
||||
# loaded on activation. Overrides append.
|
||||
#
|
||||
# Each entry is either:
|
||||
# - a literal sentence, e.g. "Code review must prioritize risk and correctness over stylistic preference."
|
||||
# - a file reference prefixed with `file:`, e.g. "file:{project-root}/docs/standards.md"
|
||||
# (glob patterns are supported; the file's contents are loaded and treated as facts).
|
||||
|
||||
persistent_facts = [
|
||||
"file:{project-root}/**/project-context.md",
|
||||
]
|
||||
|
||||
# Scalar: executed when the workflow reaches Step 4 (Present and Act),
|
||||
# after the final outputs are produced. Override wins.
|
||||
# Leave empty for no custom post-completion behavior.
|
||||
|
||||
on_complete = ""
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
# Discover Inputs Protocol
|
||||
|
||||
**Objective:** Intelligently load project files (whole or sharded) based on the workflow's Input Files configuration.
|
||||
|
||||
**Prerequisite:** Only execute this protocol if the workflow defines an Input Files section. If no input file patterns are configured, skip this entirely.
|
||||
|
||||
---
|
||||
|
||||
## Step 1: Parse Input File Patterns
|
||||
|
||||
- Read the Input Files table from the workflow configuration.
|
||||
- For each input group (prd, architecture, epics, ux, etc.), note the **load strategy** if specified.
|
||||
|
||||
## Step 2: Load Files Using Smart Strategies
|
||||
|
||||
For each pattern in the Input Files table, work through the following substeps in order:
|
||||
|
||||
### 2a: Try Sharded Documents First
|
||||
|
||||
If a sharded pattern exists for this input, determine the load strategy (defaults to **FULL_LOAD** if not specified), then apply the matching strategy:
|
||||
|
||||
#### FULL_LOAD Strategy
|
||||
|
||||
Load ALL files in the sharded directory. Use this for PRD, Architecture, UX, brownfield docs, or whenever the full picture is needed.
|
||||
|
||||
1. Use the glob pattern to find ALL `.md` files (e.g., `{planning_artifacts}/*architecture*/*.md`).
|
||||
2. Load EVERY matching file completely.
|
||||
3. Concatenate content in logical order: `index.md` first if it exists, then alphabetical.
|
||||
4. Store the combined result in a variable named `{pattern_name_content}` (e.g., `{architecture_content}`).
|
||||
|
||||
#### SELECTIVE_LOAD Strategy
|
||||
|
||||
Load a specific shard using a template variable. Example: used for epics with `{{epic_num}}`.
|
||||
|
||||
1. Check for template variables in the sharded pattern (e.g., `{{epic_num}}`).
|
||||
2. If the variable is undefined, ask the user for the value OR infer it from context.
|
||||
3. Resolve the template to a specific file path.
|
||||
4. Load that specific file.
|
||||
5. Store in variable: `{pattern_name_content}`.
|
||||
|
||||
#### INDEX_GUIDED Strategy
|
||||
|
||||
Load index.md, analyze the structure and description of each doc in the index, then intelligently load relevant docs.
|
||||
|
||||
**DO NOT BE LAZY** -- use best judgment to load documents that might have relevant information, even if there is only a 5% chance of relevance.
|
||||
|
||||
1. Load `index.md` from the sharded directory.
|
||||
2. Parse the table of contents, links, and section headers.
|
||||
3. Analyze the workflow's purpose and objective.
|
||||
4. Identify which linked/referenced documents are likely relevant.
|
||||
- *Example:* If the workflow is about authentication and the index shows "Auth Overview", "Payment Setup", "Deployment" -- load the auth docs, consider deployment docs, skip payment.
|
||||
5. Load all identified relevant documents.
|
||||
6. Store combined content in variable: `{pattern_name_content}`.
|
||||
|
||||
**When in doubt, LOAD IT** -- context is valuable, and being thorough is better than missing critical info.
|
||||
|
||||
---
|
||||
|
||||
After applying the matching strategy, mark the pattern as **RESOLVED** and move to the next pattern.
|
||||
|
||||
### 2b: Try Whole Document if No Sharded Found
|
||||
|
||||
If no sharded matches were found OR no sharded pattern exists for this input:
|
||||
|
||||
1. Attempt a glob match on the "whole" pattern (e.g., `{planning_artifacts}/*prd*.md`).
|
||||
2. If matches are found, load ALL matching files completely (no offset/limit).
|
||||
3. Store content in variable: `{pattern_name_content}` (e.g., `{prd_content}`).
|
||||
4. Mark pattern as **RESOLVED** and move to the next pattern.
|
||||
|
||||
### 2c: Handle Not Found
|
||||
|
||||
If no matches were found for either sharded or whole patterns:
|
||||
|
||||
1. Set `{pattern_name_content}` to empty string.
|
||||
2. Note in session: "No {pattern_name} files found" -- this is not an error, just unavailable. Offer the user a chance to provide the file.
|
||||
|
||||
## Step 3: Report Discovery Results
|
||||
|
||||
List all loaded content variables with file counts. Example:
|
||||
|
||||
```
|
||||
OK Loaded {prd_content} from 5 sharded files: prd/index.md, prd/requirements.md, ...
|
||||
OK Loaded {architecture_content} from 1 file: Architecture.md
|
||||
OK Loaded {epics_content} from selective load: epics/epic-3.md
|
||||
-- No ux_design files found
|
||||
```
|
||||
|
||||
This gives the workflow transparency into what context is available.
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
---
|
||||
diff_output: '' # set at runtime
|
||||
spec_file: '' # set at runtime (path or empty)
|
||||
review_mode: '' # set at runtime: "full" or "no-spec"
|
||||
story_key: '' # set at runtime when discovered from sprint status
|
||||
---
|
||||
|
||||
# Step 1: Gather Context
|
||||
|
||||
## RULES
|
||||
|
||||
- YOU MUST ALWAYS SPEAK OUTPUT in your Agent communication style with the config `{communication_language}`, tailored to `{game_dev_experience}`
|
||||
- The prompt that triggered this workflow IS the intent — not a hint.
|
||||
- Do not modify any files. This step is read-only.
|
||||
|
||||
## INSTRUCTIONS
|
||||
|
||||
1. **Find the review target.** The conversation context before this skill was triggered IS your starting point — not a blank slate. Check in this order — stop as soon as the review target is identified:
|
||||
|
||||
**Tier 1 — Explicit argument.**
|
||||
Did the user pass a PR, commit SHA, branch, spec file, or diff source this message?
|
||||
- PR reference → resolve to branch/commit via `gh pr view`. If resolution fails, ask for a SHA or branch.
|
||||
- Commit or branch → use directly.
|
||||
- Spec file → set `{spec_file}` to the provided path. Check its frontmatter for `baseline_commit`. If found, use as diff baseline. If not found, continue the cascade (a spec alone does not identify a diff source).
|
||||
- Also scan the argument for diff-mode keywords that narrow the scope:
|
||||
- "staged" / "staged changes" → Staged changes only
|
||||
- "uncommitted" / "working tree" / "all changes" → Uncommitted changes (staged + unstaged)
|
||||
- "branch diff" / "vs main" / "against main" / "compared to <branch>" → Branch diff (extract base branch if mentioned)
|
||||
- "commit range" / "last N commits" / "<from-sha>..<to-sha>" → Specific commit range
|
||||
- "this diff" / "provided diff" / "paste" → User-provided diff (do not match bare "diff" — it appears in other modes)
|
||||
- When multiple keywords match, prefer the most specific (e.g., "branch diff" over bare "diff").
|
||||
|
||||
**Tier 2 — Recent conversation.**
|
||||
Do the last few messages reveal what the user wants to be reviewed? Look for spec paths, commit refs, branches, PRs, or descriptions of a change. Apply the same diff-mode keyword scan and routing as Tier 1.
|
||||
|
||||
**Tier 3 — Sprint tracking.**
|
||||
Look for a sprint status file (`*sprint-status*`) in `{implementation_artifacts}` or `{planning_artifacts}`. If found, scan for stories with status `review`:
|
||||
- **Exactly one `review` story:** Set `{story_key}` to the story's key (e.g., `1-2-user-auth`). Suggest it: "I found story <story-id> in `review` status. Would you like to review its changes? [Y] Yes / [N] No, let me choose". If confirmed, use the story context to determine the diff source (branch name derived from story slug, or uncommitted changes). If declined, clear `{story_key}` and fall through.
|
||||
- **Multiple `review` stories:** Present them as numbered options alongside a manual choice option. Wait for user selection. If a story is selected, set `{story_key}` and use its context to determine the diff source. If manual choice is selected, clear `{story_key}` and fall through.
|
||||
- **None:** Fall through.
|
||||
|
||||
**Tier 4 — Current git state.**
|
||||
If version control is unavailable, skip to Tier 5. Otherwise, check the current branch and HEAD. If the branch is not `main` (or the default branch), confirm: "I see HEAD is `<short-sha>` on `<branch>` — do you want to review this branch's changes?" If confirmed, treat as a branch diff against `main`. If declined, fall through.
|
||||
|
||||
**Tier 5 — Ask.**
|
||||
Fall through to instruction 2.
|
||||
|
||||
Never ask extra questions beyond what the cascade prescribes. If a tier above already identified the target, skip the remaining tiers and proceed to instruction 3 (construct diff).
|
||||
|
||||
2. HALT. Ask the user: **What do you want to review?** Present these options:
|
||||
- **Uncommitted changes** (staged + unstaged)
|
||||
- **Staged changes only**
|
||||
- **Branch diff** vs a base branch (ask which base branch)
|
||||
- **Specific commit range** (ask for the range)
|
||||
- **Provided diff or file list** (user pastes or provides a path)
|
||||
|
||||
3. Construct `{diff_output}` from the chosen source.
|
||||
- For **staged changes only**: run `git diff --cached`.
|
||||
- For **uncommitted changes** (staged + unstaged): run `git diff HEAD`.
|
||||
- For **branch diff**: verify the base branch exists before running `git diff`. If it does not exist, HALT and ask the user for a valid branch.
|
||||
- For **commit range**: verify the range resolves. If it does not, HALT and ask the user for a valid range.
|
||||
- For **provided diff**: validate the content is non-empty and parseable as a unified diff. If it is not parseable, HALT and ask the user to provide a valid diff.
|
||||
- For **file list**: validate each path exists in the working tree. Construct `{diff_output}` by running `git diff HEAD -- <path1> <path2> ...`. If any paths are untracked (new files not yet staged), use `git diff --no-index /dev/null <path>` to include them. If the diff is empty (files have no uncommitted changes and are not untracked), ask the user whether to review the full file contents or to specify a different baseline.
|
||||
- After constructing `{diff_output}`, verify it is non-empty regardless of source type. If empty, HALT and tell the user there is nothing to review.
|
||||
|
||||
4. **Set the spec context.**
|
||||
- If `{spec_file}` is already set (from Tier 1 or Tier 2): verify the file exists and is readable, then set `{review_mode}` = `"full"`.
|
||||
- Otherwise, ask the user: **Is there a spec or story file that provides context for these changes?**
|
||||
- If yes: set `{spec_file}` to the path provided, verify the file exists and is readable, then set `{review_mode}` = `"full"`.
|
||||
- If no: set `{review_mode}` = `"no-spec"`.
|
||||
|
||||
5. If `{review_mode}` = `"full"` and the file at `{spec_file}` has a `context` field in its frontmatter listing additional docs, load each referenced document. Warn the user about any docs that cannot be found.
|
||||
|
||||
6. Sanity check: if `{diff_output}` exceeds approximately 3000 lines, warn the user and offer to chunk the review by file group.
|
||||
- If the user opts to chunk: agree on the first group, narrow `{diff_output}` accordingly, and list the remaining groups for the user to note for follow-up runs.
|
||||
- If the user declines: proceed as-is with the full diff.
|
||||
|
||||
### CHECKPOINT
|
||||
|
||||
Present a summary before proceeding: diff stats (files changed, lines added/removed), `{review_mode}`, and loaded spec/context docs (if any). HALT and wait for user confirmation to proceed.
|
||||
|
||||
|
||||
## NEXT
|
||||
|
||||
Read fully and follow `./step-02-review.md`
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
---
|
||||
failed_layers: '' # set at runtime: comma-separated list of layers that failed or returned empty
|
||||
---
|
||||
|
||||
# Step 2: Review
|
||||
|
||||
## RULES
|
||||
|
||||
- YOU MUST ALWAYS SPEAK OUTPUT in your Agent communication style with the config `{communication_language}`, tailored to `{game_dev_experience}`
|
||||
- The Blind Hunter subagent receives NO project context — diff only.
|
||||
- The Edge Case Hunter subagent receives diff and project read access.
|
||||
- The Acceptance Auditor subagent receives diff, spec, and context docs.
|
||||
- All review subagents must run at the same model capability as the current session.
|
||||
|
||||
## INSTRUCTIONS
|
||||
|
||||
1. If `{review_mode}` = `"no-spec"`, note to the user: "Acceptance Auditor skipped — no spec file provided."
|
||||
|
||||
2. Launch parallel subagents without conversation context. If subagents are not available, generate prompt files in `{implementation_artifacts}` — one per reviewer role below — and HALT. Ask the user to run each in a separate session (ideally a different LLM) and paste back the findings. When findings are pasted, resume from this point and proceed to step 3.
|
||||
|
||||
- **Blind Hunter** — receives `{diff_output}` only. No spec, no context docs, no project access. Invoke via the `bmad-review-adversarial-general` skill.
|
||||
|
||||
- **Edge Case Hunter** — receives `{diff_output}` and read access to the project. Invoke via the `bmad-review-edge-case-hunter` skill.
|
||||
|
||||
- **Acceptance Auditor** (only if `{review_mode}` = `"full"`) — receives `{diff_output}`, the content of the file at `{spec_file}`, and any loaded context docs. Its prompt:
|
||||
> You are an Acceptance Auditor. Review this diff against the spec and context docs. Check for: violations of acceptance criteria, deviations from spec intent, missing implementation of specified behavior, contradictions between spec constraints and actual code.
|
||||
>
|
||||
> **Quality Gate — Test Coverage Justification:** Check whether the spec/story file or diff commentary includes a "Test Coverage Justification" section that maps impacted modules/nodes to specific test files. Apply these rules:
|
||||
> - **If `{review_mode}` = `"full"`** (spec/story file is present) and the spec file lacks a Test Coverage Justification section → raise a `decision_needed` finding: "Missing Test Coverage Justification — blast radius report exists but no test mapping was provided."
|
||||
> - **If `{review_mode}` = `"no-spec"`** (diff-only review) → check the diff commentary or commit messages for test coverage evidence. If absent, raise a `patch` finding: "Quality gate artifact missing — no Test Coverage Justification found in diff commentary or commit message."
|
||||
> - **If Test Coverage Justification exists** but has any node listed with coverage status `None` or `no coverage found` → raise a separate `decision_needed` finding for each such node: "Uncovered impacted node: [node-name] — tests required before merge."
|
||||
> - Reference any blast radius report embedded in the spec/story file to cross-validate the affected modules listed in the justification.
|
||||
> - **Fallback detection:** If no section titled "Test Coverage Justification" is found, search for a markdown table with columns containing "Module", "Affected Symbols", "Test Files", and "Coverage". If such a table exists, treat it as meeting the quality gate regardless of section title.
|
||||
>
|
||||
> **Quality Gate — Mathematical Gate Output:** Check whether the spec/story file includes a "Mathematical Quality Gate Output" section (JSON output from `qa-memtrace.mjs`). Apply these rules:
|
||||
> - **If the spec file contains "Mathematical Quality Gate Output"**: use the `uncovered_nodes` from the script's JSON output as the ground truth for uncovered nodes — NOT the agent's textual claims in the justification table. Cross-validate: if the script found a node as uncovered but the justification table lists it as `Yes` coverage → raise a `decision_needed` finding per mismatch: "Coverage mismatch — agent claimed [node] is covered but mathematical gate shows it is not."
|
||||
> - **If the spec file contains a blast radius report AND a Test Coverage Justification BUT no "Mathematical Quality Gate Output"**: this is a Phase 1-level story using only textual justification. Flag as `patch` (not `decision_needed`): "Phase 1 story — consider upgrading to mathematical quality gate via qa-memtrace.mjs."
|
||||
> - **If the spec file contains only a blast radius report (no test justification, no mathematical gate)**: raise a `decision_needed` finding: "Missing both Test Coverage Justification and Mathematical Quality Gate Output — Phase 2 story must include qa-memtrace.mjs execution results."
|
||||
>
|
||||
> Output findings as a Markdown list. Each finding: one-line title, which quality gate rule it violates, and evidence from the diff/story file.
|
||||
|
||||
3. **Subagent failure handling**: If any subagent fails, times out, or returns empty results, append the layer name to `{failed_layers}` (comma-separated) and proceed with findings from the remaining layers.
|
||||
|
||||
4. Collect all findings from the completed layers.
|
||||
|
||||
|
||||
## NEXT
|
||||
|
||||
Read fully and follow `./step-03-triage.md`
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
---
|
||||
---
|
||||
|
||||
# Step 3: Triage
|
||||
|
||||
## RULES
|
||||
|
||||
- YOU MUST ALWAYS SPEAK OUTPUT in your Agent communication style with the config `{communication_language}`, tailored to `{game_dev_experience}`
|
||||
- Be precise. When uncertain between categories, prefer the more conservative classification.
|
||||
|
||||
## INSTRUCTIONS
|
||||
|
||||
1. **Normalize** findings into a common format. Expected input formats:
|
||||
- Adversarial (Blind Hunter): markdown list of descriptions
|
||||
- Edge Case Hunter: JSON array with `location`, `trigger_condition`, `guard_snippet`, `potential_consequence` fields
|
||||
- Acceptance Auditor: markdown list with title, AC/constraint reference, and evidence
|
||||
|
||||
If a layer's output does not match its expected format, attempt best-effort parsing. Note any parsing issues for the user.
|
||||
|
||||
Convert all to a unified list where each finding has:
|
||||
- `id` -- sequential integer
|
||||
- `source` -- `blind`, `edge`, `auditor`, or merged sources (e.g., `blind+edge`)
|
||||
- `title` -- one-line summary
|
||||
- `detail` -- full description
|
||||
- `location` -- file and line reference (if available)
|
||||
|
||||
2. **Deduplicate.** If two or more findings describe the same issue, merge them into one:
|
||||
- Use the most specific finding as the base (prefer edge-case JSON with location over adversarial prose).
|
||||
- Append any unique detail, reasoning, or location references from the other finding(s) into the surviving `detail` field.
|
||||
- Set `source` to the merged sources (e.g., `blind+edge`).
|
||||
|
||||
3. **Classify** each finding into exactly one bucket:
|
||||
- **decision_needed** -- There is an ambiguous choice that requires human input. The code cannot be correctly patched without knowing the user's intent. Only possible if `{review_mode}` = `"full"`.
|
||||
- **patch** -- Code issue that is fixable without human input. The correct fix is unambiguous.
|
||||
- **defer** -- Pre-existing issue not caused by the current change. Real but not actionable now.
|
||||
- **dismiss** -- Noise, false positive, or handled elsewhere.
|
||||
|
||||
If `{review_mode}` = `"no-spec"` and a finding would otherwise be `decision_needed`, reclassify it as `patch` (if the fix is unambiguous) or `defer` (if not).
|
||||
|
||||
4. **Drop** all `dismiss` findings. Record the dismiss count for the summary.
|
||||
|
||||
5. If `{failed_layers}` is non-empty, report which layers failed before announcing results. If zero findings remain after dropping dismissed AND `{failed_layers}` is non-empty, warn the user that the review may be incomplete rather than announcing a clean review.
|
||||
|
||||
6. If zero findings remain after triage (all rejected or none raised): state "✅ Clean review — all layers passed." (Step 3 already warned if any review layers failed via `{failed_layers}`.)
|
||||
|
||||
|
||||
## NEXT
|
||||
|
||||
Read fully and follow `./step-04-present.md`
|
||||
|
|
@ -0,0 +1,132 @@
|
|||
---
|
||||
deferred_work_file: '{implementation_artifacts}/deferred-work.md'
|
||||
---
|
||||
|
||||
# Step 4: Present and Act
|
||||
|
||||
## RULES
|
||||
|
||||
- YOU MUST ALWAYS SPEAK OUTPUT in your Agent communication style with the config `{communication_language}`, tailored to `{game_dev_experience}`
|
||||
- When `{spec_file}` is set, always write findings to the story file before offering action choices.
|
||||
- `decision_needed` findings must be resolved before handling `patch` findings.
|
||||
|
||||
## INSTRUCTIONS
|
||||
|
||||
### 1. Clean review shortcut
|
||||
|
||||
If zero findings remain after triage (all dismissed or none raised): state that and proceed to section 6 (Sprint Status Update).
|
||||
|
||||
### 2. Write findings to the story file
|
||||
|
||||
If `{spec_file}` exists and contains a Tasks/Subtasks section, append a `### Review Findings` subsection. Write all findings in this order:
|
||||
|
||||
1. **`decision_needed`** findings (unchecked):
|
||||
`- [ ] [Review][Decision] <Title> — <Detail>`
|
||||
|
||||
2. **`patch`** findings (unchecked):
|
||||
`- [ ] [Review][Patch] <Title> [<file>:<line>]`
|
||||
|
||||
3. **`defer`** findings (checked off, marked deferred):
|
||||
`- [x] [Review][Defer] <Title> [<file>:<line>] — deferred, pre-existing`
|
||||
|
||||
Also append each `defer` finding to `{deferred_work_file}` under a heading `## Deferred from: code review ({date})`. If `{spec_file}` is set, include its basename in the heading (e.g., `code review of story-3.3 (2026-03-18)`). One bullet per finding with description.
|
||||
|
||||
### 3. Present summary
|
||||
|
||||
Announce what was written:
|
||||
|
||||
> **Code review complete.** <D> `decision_needed`, <P> `patch`, <W> `defer`, <R> dismissed as noise.
|
||||
|
||||
If `{spec_file}` is set, add: `Findings written to the review findings section in {spec_file}.`
|
||||
Otherwise add: `Findings are listed above. No story file was provided, so nothing was persisted.`
|
||||
|
||||
### 4. Resolve decision_needed findings
|
||||
|
||||
If `decision_needed` findings exist, present each one with its detail and the options available. The user must decide — the correct fix is ambiguous without their input. Walk through each finding (or batch related ones) and get the user's call. Once resolved, each becomes a `patch`, `defer`, or is dismissed.
|
||||
|
||||
If the user chooses to defer, ask: Quick one-line reason for deferring this item? (helps future reviews): — then append that reason to both the story file bullet and the `{deferred_work_file}` entry.
|
||||
|
||||
**HALT** — I am waiting for your numbered choice. Reply with only the number. Do not proceed until you select an option.
|
||||
|
||||
### 5. Handle `patch` findings
|
||||
|
||||
If `patch` findings exist (including any resolved from step 4), HALT. Ask the user:
|
||||
|
||||
If `{spec_file}` is set, present all three options:
|
||||
|
||||
> **How would you like to handle the `<P>` `patch` findings?**
|
||||
> 1. **Apply every patch** — fix all of them now, no per-finding confirmation. Defer and decision_needed items are not touched.
|
||||
> 2. **Leave as action items** — they are already in the story file
|
||||
> 3. **Walk through each patch** — show details for each before deciding
|
||||
|
||||
If `{spec_file}` is **not** set, present only options 1 and 2 (omit "Leave as action items" — findings were not written to a file):
|
||||
|
||||
> **How would you like to handle the `<P>` `patch` findings?**
|
||||
> 1. **Apply every patch** — fix all of them now, no per-finding confirmation. Defer and decision_needed items are not touched.
|
||||
> 2. **Walk through each patch** — show details for each before deciding
|
||||
|
||||
**HALT** — I am waiting for your numbered choice. Reply with only the number. Do not proceed until you select an option.
|
||||
|
||||
- **Apply every patch**: Apply every patch finding without per-finding confirmation. Do not modify defer or decision_needed items. After all patches are applied, present a summary of changes made. If `{spec_file}` is set, check off the patch items in the story file (leave defer items as-is).
|
||||
- **Leave as action items** (only when `{spec_file}` is set): Done — findings are already written to the story.
|
||||
- **Walk through each patch**: Present each finding with full detail, diff context, and suggested fix. After walkthrough, re-offer the applicable options above.
|
||||
|
||||
**HALT** — I am waiting for your numbered choice. Do not proceed until you select an option.
|
||||
|
||||
**✅ Code review actions complete**
|
||||
|
||||
- decision_needed resolved: <D>
|
||||
- Patches handled: <P>
|
||||
- Deferred: <W>
|
||||
- Dismissed: <R>
|
||||
|
||||
### 6. Update story status and sync sprint tracking
|
||||
|
||||
Skip this section if `{spec_file}` is not set.
|
||||
|
||||
#### Determine new status based on review outcome
|
||||
|
||||
- If all `decision_needed` and `patch` findings were resolved (fixed or dismissed) AND no unresolved HIGH/MEDIUM issues remain: set `{new_status}` = `done`. Update the story file Status section to `done`.
|
||||
- If `patch` findings were left as action items, or unresolved issues remain: set `{new_status}` = `in-progress`. Update the story file Status section to `in-progress`.
|
||||
|
||||
Save the story file.
|
||||
|
||||
#### Sync sprint-status.yaml
|
||||
|
||||
If `{story_key}` is not set, skip this subsection and note that sprint status was not synced because no story key was available.
|
||||
|
||||
If `{sprint_status}` file exists:
|
||||
|
||||
1. Load the FULL `{sprint_status}` file.
|
||||
2. Find the `development_status` entry matching `{story_key}`.
|
||||
3. If found: update `development_status[{story_key}]` to `{new_status}`. Update `last_updated` to current date. Save the file, preserving ALL comments and structure including STATUS DEFINITIONS.
|
||||
4. If `{story_key}` not found in sprint status: warn the user that the story file was updated but sprint-status sync failed.
|
||||
|
||||
If `{sprint_status}` file does not exist, note that story status was updated in the story file only.
|
||||
|
||||
#### Completion summary
|
||||
|
||||
> **Review Complete!**
|
||||
>
|
||||
> **Story Status:** `{new_status}`
|
||||
> **Issues Fixed:** <fixed_count>
|
||||
> **Action Items Created:** <action_count>
|
||||
> **Deferred:** <W>
|
||||
> **Dismissed:** <R>
|
||||
|
||||
### 7. Next steps
|
||||
|
||||
Present the user with follow-up options:
|
||||
|
||||
> **What would you like to do next?**
|
||||
> 1. **Start the next story** — run `dev-story` to pick up the next `ready-for-dev` story
|
||||
> 2. **Re-run code review** — address findings and review again
|
||||
> 3. **Done** — end the workflow
|
||||
|
||||
**HALT** — I am waiting for your choice. Do not proceed until the user selects an option.
|
||||
|
||||
## On Complete
|
||||
|
||||
Run: `python3 {project-root}/_bmad/scripts/resolve_customization.py --skill {skill-root} --key workflow.on_complete`
|
||||
|
||||
If the resolved `workflow.on_complete` is non-empty, follow it as the final terminal instruction before exiting.
|
||||
|
|
@ -43,14 +43,23 @@ CLAUDE.local.md
|
|||
.serena/
|
||||
.claude/settings.local.json
|
||||
.junie/
|
||||
.agents/
|
||||
.agents/*
|
||||
!.agents/skills/
|
||||
!.agents/skills/**
|
||||
|
||||
z*/
|
||||
!docs/zh-cn/
|
||||
|
||||
_bmad
|
||||
_bmad/*
|
||||
!_bmad/scripts/
|
||||
!_bmad/scripts/memtrace/
|
||||
!_bmad/scripts/memtrace/**
|
||||
_bmad-output
|
||||
|
||||
# Memtrace local database
|
||||
.memdb/
|
||||
.memtrace/
|
||||
|
||||
# Personal customization files (team files are committed, personal files are not)
|
||||
_bmad/custom/*.user.toml
|
||||
.clinerules
|
||||
|
|
|
|||
|
|
@ -0,0 +1,130 @@
|
|||
import fs from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
import os from 'node:os';
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
const modeArgIndex = args.indexOf('--mode');
|
||||
const mode = modeArgIndex !== -1 ? args[modeArgIndex + 1] : null;
|
||||
|
||||
if (!mode || (mode !== 'claude' && mode !== 'opencode')) {
|
||||
console.error('Error: Please specify --mode as "claude" or "opencode"');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Helper to ensure parent directories exist
|
||||
async function ensureDir(filePath) {
|
||||
const dir = path.dirname(filePath);
|
||||
try {
|
||||
await fs.mkdir(dir, { recursive: true });
|
||||
} catch (err) {
|
||||
// Ignore if already exists or other error handled later
|
||||
}
|
||||
}
|
||||
|
||||
// Injects config for Claude Desktop
|
||||
async function injectClaude() {
|
||||
let configPath = process.env.TEST_CLAUDE_CONFIG_PATH || '';
|
||||
if (!configPath) {
|
||||
const homedir = os.homedir();
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
const appdata = process.env.APPDATA;
|
||||
if (!appdata) {
|
||||
console.warn('Warning: APPDATA environment variable not set. Cannot resolve Claude Desktop config path on Windows.');
|
||||
return;
|
||||
}
|
||||
configPath = path.join(appdata, 'Claude', 'claude_desktop_config.json');
|
||||
} else if (process.platform === 'darwin') {
|
||||
configPath = path.join(homedir, 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json');
|
||||
} else {
|
||||
// Linux/fallback
|
||||
configPath = path.join(homedir, '.config', 'Claude', 'claude_desktop_config.json');
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`Targeting Claude Desktop Config: ${configPath}`);
|
||||
|
||||
let configObj = {};
|
||||
|
||||
try {
|
||||
const data = await fs.readFile(configPath, 'utf8');
|
||||
configObj = JSON.parse(data);
|
||||
} catch (err) {
|
||||
if (err.code === 'ENOENT') {
|
||||
console.log('Claude Desktop config file does not exist. Creating a new one...');
|
||||
} else {
|
||||
console.error(`Error reading or parsing Claude Desktop config: ${err.message}`);
|
||||
console.warn('Attempting to overwrite with clean skeleton...');
|
||||
}
|
||||
}
|
||||
|
||||
// Inject or overwrite memtrace server
|
||||
if (!configObj.mcpServers) {
|
||||
configObj.mcpServers = {};
|
||||
}
|
||||
|
||||
configObj.mcpServers.memtrace = {
|
||||
command: 'memtrace',
|
||||
args: ['mcp']
|
||||
};
|
||||
|
||||
try {
|
||||
await ensureDir(configPath);
|
||||
await fs.writeFile(configPath, JSON.stringify(configObj, null, 2), 'utf8');
|
||||
console.log('Successfully configured Memtrace MCP server in Claude Desktop config!');
|
||||
} catch (err) {
|
||||
console.error(`Failed to write Claude Desktop config: ${err.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Injects config for OpenCode
|
||||
async function injectOpenCode() {
|
||||
const configPath = process.env.TEST_OPENCODE_CONFIG_PATH || path.resolve(process.cwd(), 'opencode.json');
|
||||
console.log(`Targeting OpenCode Config: ${configPath}`);
|
||||
|
||||
let configObj = {};
|
||||
|
||||
try {
|
||||
const data = await fs.readFile(configPath, 'utf8');
|
||||
configObj = JSON.parse(data);
|
||||
} catch (err) {
|
||||
if (err.code === 'ENOENT') {
|
||||
console.log('opencode.json does not exist. Creating a new one...');
|
||||
} else {
|
||||
console.error(`Error reading or parsing opencode.json: ${err.message}`);
|
||||
console.warn('Attempting to overwrite with clean skeleton...');
|
||||
}
|
||||
}
|
||||
|
||||
// Inject or overwrite memtrace server
|
||||
if (!configObj.mcp) {
|
||||
configObj.mcp = {};
|
||||
}
|
||||
|
||||
configObj.mcp.memtrace = {
|
||||
type: 'local',
|
||||
command: ['memtrace', 'mcp'],
|
||||
enabled: true,
|
||||
environment: {}
|
||||
};
|
||||
|
||||
try {
|
||||
await fs.writeFile(configPath, JSON.stringify(configObj, null, 2), 'utf8');
|
||||
console.log('Successfully configured Memtrace MCP server in opencode.json!');
|
||||
} catch (err) {
|
||||
console.error(`Failed to write opencode.json: ${err.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function run() {
|
||||
if (mode === 'claude') {
|
||||
await injectClaude();
|
||||
} else if (mode === 'opencode') {
|
||||
await injectOpenCode();
|
||||
}
|
||||
}
|
||||
|
||||
run().catch((err) => {
|
||||
console.error(`Fatal execution error: ${err.message}`);
|
||||
process.exit(1);
|
||||
});
|
||||
|
|
@ -0,0 +1,171 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
import { readFileSync, existsSync } from 'fs';
|
||||
import { resolve } from 'path';
|
||||
|
||||
const TIMEOUT_MS = 10000;
|
||||
const TIMEOUT_TOKEN = 'MEMTRACE_MCP_ERROR_TIMEOUT';
|
||||
|
||||
function parseArgs() {
|
||||
const args = process.argv.slice(2);
|
||||
if (args.length === 0 || args.includes('--help') || args.includes('-h')) {
|
||||
console.log(`Usage: node qa-memtrace.mjs --blast-radius <file.json> --test-coverage <file.json> [--threshold N]
|
||||
|
||||
Arguments:
|
||||
--blast-radius <file> Path to JSON file with get_impact output
|
||||
--test-coverage <file> Path to JSON file with test coverage data
|
||||
--threshold N Coverage threshold 0-100 (default: 100)
|
||||
|
||||
Exit codes:
|
||||
0 All required nodes covered (coverage >= threshold)
|
||||
1 Coverage insufficient or error
|
||||
|
||||
The output JSON includes: status, blast_radius_total, covered_nodes,
|
||||
uncovered_nodes, coverage_percentage, threshold, passed, uncovered_details.`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const result = { blastRadius: null, testCoverage: null, threshold: 100 };
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
if (args[i] === '--blast-radius' && i + 1 < args.length) {
|
||||
result.blastRadius = args[++i];
|
||||
} else if (args[i] === '--test-coverage' && i + 1 < args.length) {
|
||||
result.testCoverage = args[++i];
|
||||
} else if (args[i] === '--threshold' && i + 1 < args.length) {
|
||||
result.threshold = parseInt(args[++i], 10);
|
||||
if (isNaN(result.threshold) || result.threshold < 0 || result.threshold > 100) {
|
||||
fail(`Invalid threshold. Must be integer 0-100.`);
|
||||
process.exit(1);
|
||||
}
|
||||
} else {
|
||||
fail(`Unknown argument: ${args[i]}`);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (!result.blastRadius) { fail('Missing --blast-radius'); process.exit(1); }
|
||||
if (!result.testCoverage) { fail('Missing --test-coverage'); process.exit(1); }
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function fail(msg) {
|
||||
console.error(`ERROR: ${msg}`);
|
||||
console.log(TIMEOUT_TOKEN);
|
||||
}
|
||||
|
||||
function readJsonFile(filePath) {
|
||||
const resolved = resolve(filePath);
|
||||
if (!existsSync(resolved)) {
|
||||
throw new Error(`File not found: ${resolved}`);
|
||||
}
|
||||
return JSON.parse(readFileSync(resolved, 'utf-8'));
|
||||
}
|
||||
|
||||
function compute(blastData, coverageData, threshold) {
|
||||
if (!Array.isArray(blastData.affected_symbols) || blastData.total_count === 0) {
|
||||
return {
|
||||
status: 'pass',
|
||||
blast_radius_total: 0,
|
||||
covered_nodes: 0,
|
||||
uncovered_nodes: 0,
|
||||
coverage_percentage: 100,
|
||||
threshold,
|
||||
passed: true,
|
||||
uncovered_details: [],
|
||||
elapsed_ms: 0,
|
||||
note: 'Empty blast radius — no nodes to intersect'
|
||||
};
|
||||
}
|
||||
|
||||
const blastSet = new Map();
|
||||
for (const sym of blastData.affected_symbols) {
|
||||
const key = `${sym.file}:${sym.name}`;
|
||||
blastSet.set(key, sym);
|
||||
}
|
||||
|
||||
const coveredSet = new Set();
|
||||
for (const mod of coverageData.modules) {
|
||||
const modPath = mod.module || '';
|
||||
const cov = mod.coverage || '';
|
||||
|
||||
if (cov === 'Yes') {
|
||||
for (const sym of (mod.symbols_covered || [])) {
|
||||
if (blastSet.has(`${modPath}:${sym}`)) {
|
||||
coveredSet.add(`${modPath}:${sym}`);
|
||||
}
|
||||
}
|
||||
} else if (cov.startsWith('Partial:')) {
|
||||
const n = parseInt(cov.split(':')[1], 10) || 0;
|
||||
const covered = (mod.symbols_covered || []).slice(0, n);
|
||||
for (const sym of covered) {
|
||||
if (blastSet.has(`${modPath}:${sym}`)) {
|
||||
coveredSet.add(`${modPath}:${sym}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const uncoveredDetails = [];
|
||||
for (const [key, sym] of blastSet) {
|
||||
if (!coveredSet.has(key)) {
|
||||
uncoveredDetails.push({ symbol: sym.name, file: sym.file, depth: sym.depth });
|
||||
}
|
||||
}
|
||||
|
||||
const total = blastSet.size;
|
||||
const covered = coveredSet.size;
|
||||
const uncovered = uncoveredDetails.length;
|
||||
const pct = total > 0 ? (covered / total) * 100 : 100;
|
||||
const passed = pct >= threshold;
|
||||
|
||||
return {
|
||||
status: passed ? 'pass' : 'fail',
|
||||
blast_radius_total: total,
|
||||
covered_nodes: covered,
|
||||
uncovered_nodes: uncovered,
|
||||
coverage_percentage: Math.round(pct * 10) / 10,
|
||||
threshold,
|
||||
passed,
|
||||
uncovered_details: uncoveredDetails,
|
||||
elapsed_ms: 0
|
||||
};
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const args = parseArgs();
|
||||
const start = Date.now();
|
||||
|
||||
const blastData = readJsonFile(args.blastRadius);
|
||||
if (!Array.isArray(blastData.affected_symbols)) {
|
||||
throw new Error('Invalid blast-radius data: "affected_symbols" must be an array');
|
||||
}
|
||||
if (typeof blastData.total_count !== 'number') {
|
||||
throw new Error('Invalid blast-radius data: "total_count" must be a number');
|
||||
}
|
||||
|
||||
const coverageData = readJsonFile(args.testCoverage);
|
||||
if (!Array.isArray(coverageData.modules)) {
|
||||
throw new Error('Invalid test-coverage data: "modules" must be an array');
|
||||
}
|
||||
|
||||
const result = compute(blastData, coverageData, args.threshold);
|
||||
result.elapsed_ms = Date.now() - start;
|
||||
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
process.exit(result.passed ? 0 : 1);
|
||||
}
|
||||
|
||||
const timeout = new Promise((_, reject) =>
|
||||
setTimeout(() => reject(new Error('TIMEOUT')), TIMEOUT_MS)
|
||||
);
|
||||
|
||||
Promise.race([main(), timeout])
|
||||
.catch(err => {
|
||||
if (err.message === 'TIMEOUT') {
|
||||
console.log(TIMEOUT_TOKEN);
|
||||
} else {
|
||||
fail(err.message);
|
||||
}
|
||||
process.exit(1);
|
||||
});
|
||||
|
|
@ -0,0 +1,149 @@
|
|||
#!/usr/bin/env node
|
||||
import { strict as assert } from 'assert';
|
||||
import { writeFileSync, unlinkSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
import { tmpdir } from 'os';
|
||||
import { execFileSync } from 'child_process';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
const SCRIPT_PATH = join(fileURLToPath(new URL('.', import.meta.url)), 'qa-memtrace.mjs');
|
||||
const TMP = tmpdir();
|
||||
|
||||
function runScript(br, tc, threshold) {
|
||||
const brPath = join(TMP, `br-${Date.now()}.json`);
|
||||
const tcPath = join(TMP, `tc-${Date.now()}.json`);
|
||||
try {
|
||||
writeFileSync(brPath, JSON.stringify(br), 'utf8');
|
||||
writeFileSync(tcPath, JSON.stringify(tc), 'utf8');
|
||||
const args = ['--blast-radius', brPath, '--test-coverage', tcPath];
|
||||
if (threshold !== undefined) args.push('--threshold', String(threshold));
|
||||
const stdout = execFileSync(process.execPath, [SCRIPT_PATH, ...args], { encoding: 'utf8', timeout: 5000 });
|
||||
return { exitCode: 0, output: JSON.parse(stdout.trim()) };
|
||||
} catch (e) {
|
||||
const raw = e.stdout;
|
||||
const stdout = (Buffer.isBuffer(raw) ? raw.toString('utf8') : (raw || '')).trim();
|
||||
let parsed;
|
||||
try { parsed = JSON.parse(stdout); }
|
||||
catch { parsed = { status: 'error', error: stdout }; }
|
||||
return { exitCode: e.status || 1, output: parsed };
|
||||
} finally {
|
||||
try { unlinkSync(brPath); } catch {}
|
||||
try { unlinkSync(tcPath); } catch {}
|
||||
}
|
||||
}
|
||||
|
||||
function makeBr(symbols) {
|
||||
return { target: 'test', risk_level: 'Low', affected_symbols: symbols, total_count: symbols.length };
|
||||
}
|
||||
|
||||
function makeTc(modules) {
|
||||
return { modules, coverage_summary: { total_modules: modules.length } };
|
||||
}
|
||||
|
||||
let passed = 0;
|
||||
let failed = 0;
|
||||
|
||||
function test(name, fn) {
|
||||
try { fn(); passed++; console.log(` V ${name}`); }
|
||||
catch (e) { failed++; console.log(` X ${name}: ${e.message}`); }
|
||||
}
|
||||
|
||||
console.log('qa-memtrace.mjs Test Suite\n');
|
||||
|
||||
test('all nodes covered', () => {
|
||||
const br = makeBr([{name:'foo',file:'src/a.ts',depth:1}]);
|
||||
const tc = makeTc([{module:'src/a.ts',test_files:['test/a.test.ts'],symbols_covered:['foo'],coverage:'Yes'}]);
|
||||
const r = runScript(br, tc);
|
||||
assert.equal(r.exitCode, 0);
|
||||
assert.equal(r.output.passed, true);
|
||||
assert.equal(r.output.covered_nodes, 1);
|
||||
});
|
||||
|
||||
test('some nodes uncovered', () => {
|
||||
const br = makeBr([{name:'foo',file:'src/a.ts',depth:1},{name:'bar',file:'src/b.ts',depth:2}]);
|
||||
const tc = makeTc([{module:'src/a.ts',test_files:['test/a.test.ts'],symbols_covered:['foo'],coverage:'Yes'},{module:'src/b.ts',test_files:[],symbols_covered:[],coverage:'None'}]);
|
||||
const r = runScript(br, tc);
|
||||
assert.equal(r.exitCode, 1);
|
||||
assert.equal(r.output.passed, false);
|
||||
assert.equal(r.output.uncovered_nodes, 1);
|
||||
});
|
||||
|
||||
test('threshold met (50% with threshold 50)', () => {
|
||||
const br = makeBr([{name:'foo',file:'src/a.ts',depth:1},{name:'bar',file:'src/b.ts',depth:2}]);
|
||||
const tc = makeTc([{module:'src/a.ts',test_files:['test/a.test.ts'],symbols_covered:['foo'],coverage:'Yes'},{module:'src/b.ts',test_files:[],symbols_covered:[],coverage:'None'}]);
|
||||
const r = runScript(br, tc, 50);
|
||||
assert.equal(r.exitCode, 0);
|
||||
assert.equal(r.output.passed, true);
|
||||
assert.equal(r.output.coverage_percentage, 50);
|
||||
});
|
||||
|
||||
test('coverage below threshold (50% with threshold 80)', () => {
|
||||
const br = makeBr([{name:'foo',file:'src/a.ts',depth:1},{name:'bar',file:'src/b.ts',depth:2}]);
|
||||
const tc = makeTc([{module:'src/a.ts',test_files:['test/a.test.ts'],symbols_covered:['foo'],coverage:'Yes'},{module:'src/b.ts',test_files:[],symbols_covered:[],coverage:'None'}]);
|
||||
const r = runScript(br, tc, 80);
|
||||
assert.equal(r.exitCode, 1);
|
||||
assert.equal(r.output.passed, false);
|
||||
});
|
||||
|
||||
test('empty blast radius', () => {
|
||||
const br = makeBr([]);
|
||||
const tc = makeTc([]);
|
||||
const r = runScript(br, tc);
|
||||
assert.equal(r.exitCode, 0);
|
||||
assert.equal(r.output.passed, true);
|
||||
assert.ok(r.output.note.includes('Empty blast radius'));
|
||||
});
|
||||
|
||||
test('no test coverage at all', () => {
|
||||
const br = makeBr([{name:'foo',file:'src/a.ts',depth:1},{name:'bar',file:'src/b.ts',depth:2}]);
|
||||
const tc = makeTc([{module:'src/a.ts',test_files:[],symbols_covered:[],coverage:'None'},{module:'src/b.ts',test_files:[],symbols_covered:[],coverage:'None'}]);
|
||||
const r = runScript(br, tc);
|
||||
assert.equal(r.exitCode, 1);
|
||||
assert.equal(r.output.passed, false);
|
||||
assert.equal(r.output.uncovered_nodes, 2);
|
||||
});
|
||||
|
||||
test('partial coverage handling', () => {
|
||||
const br = makeBr([{name:'foo',file:'src/a.ts',depth:1},{name:'helper',file:'src/a.ts',depth:2},{name:'bar',file:'src/b.ts',depth:1}]);
|
||||
const tc = makeTc([{module:'src/a.ts',test_files:['test/a.test.ts'],symbols_covered:['foo','helper'],coverage:'Partial:1'},{module:'src/b.ts',test_files:[],symbols_covered:[],coverage:'None'}]);
|
||||
const r = runScript(br, tc);
|
||||
assert.equal(r.exitCode, 1);
|
||||
assert.equal(r.output.covered_nodes, 1);
|
||||
assert.equal(r.output.uncovered_nodes, 2);
|
||||
});
|
||||
|
||||
test('threshold 0 flag-only mode', () => {
|
||||
const br = makeBr([{name:'foo',file:'src/a.ts',depth:1}]);
|
||||
const tc = makeTc([{module:'src/a.ts',test_files:[],symbols_covered:[],coverage:'None'}]);
|
||||
const r = runScript(br, tc, 0);
|
||||
assert.equal(r.exitCode, 0);
|
||||
assert.equal(r.output.passed, true);
|
||||
});
|
||||
|
||||
test('threshold 100 strict mode', () => {
|
||||
const br = makeBr([{name:'foo',file:'src/a.ts',depth:1},{name:'bar',file:'src/b.ts',depth:2}]);
|
||||
const tc = makeTc([{module:'src/a.ts',test_files:['test/a.test.ts'],symbols_covered:['foo'],coverage:'Yes'},{module:'src/b.ts',test_files:[],symbols_covered:[],coverage:'None'}]);
|
||||
const r = runScript(br, tc, 100);
|
||||
assert.equal(r.exitCode, 1);
|
||||
assert.equal(r.output.passed, false);
|
||||
});
|
||||
|
||||
test('missing --test-coverage arg → exit 1 and emit error', () => {
|
||||
const br = makeBr([{name:'foo',file:'src/a.ts',depth:1}]);
|
||||
const brPath = join(TMP, `br-err-${Date.now()}.json`);
|
||||
writeFileSync(brPath, JSON.stringify(br), 'utf8');
|
||||
try {
|
||||
execFileSync(process.execPath, [SCRIPT_PATH, '--blast-radius', brPath], { encoding: 'utf8', timeout: 5000 });
|
||||
assert.fail('should have thrown');
|
||||
} catch (e) {
|
||||
assert.ok(e.status === 1 || e.status === null, `expected 1 or null, got ${e.status}`);
|
||||
const stderr = Buffer.isBuffer(e.stderr) ? e.stderr.toString('utf8') : (e.stderr || '');
|
||||
assert.ok(stderr.includes('Missing') || (e.stdout || '').includes('TIMEOUT'),
|
||||
`expected error about missing arg, got stderr: ${stderr}, stdout: ${e.stdout}`);
|
||||
} finally {
|
||||
try { unlinkSync(brPath); } catch {}
|
||||
}
|
||||
});
|
||||
|
||||
console.log(`\nResults: ${passed} passed, ${failed} failed`);
|
||||
process.exit(failed > 0 ? 1 : 0);
|
||||
Loading…
Reference in New Issue