bmad-method 6.10.1-next.2 → 6.10.1-next.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +52 -27
- package/package.json +4 -3
- package/removals.txt +5 -0
- package/src/bmm-skills/1-analysis/bmad-agent-analyst/customize.toml +19 -4
- package/src/bmm-skills/1-analysis/bmad-product-brief/SKILL.md +1 -1
- package/src/bmm-skills/1-analysis/bmad-product-brief/customize.toml +5 -3
- package/src/bmm-skills/2-plan-workflows/bmad-prd/SKILL.md +2 -0
- package/src/bmm-skills/2-plan-workflows/bmad-prd/customize.toml +5 -3
- package/src/{core-skills → bmm-skills/2-plan-workflows}/bmad-spec/SKILL.md +18 -3
- package/src/{core-skills → bmm-skills/2-plan-workflows}/bmad-spec/assets/spec-template.md +1 -1
- package/src/bmm-skills/2-plan-workflows/bmad-spec/assets/stories-schema.md +44 -0
- package/src/{core-skills → bmm-skills/2-plan-workflows}/bmad-spec/customize.toml +3 -4
- package/src/bmm-skills/2-plan-workflows/bmad-ux/customize.toml +4 -2
- package/src/bmm-skills/3-solutioning/bmad-architecture/SKILL.md +2 -2
- package/src/bmm-skills/3-solutioning/bmad-architecture/customize.toml +4 -2
- package/src/bmm-skills/4-implementation/bmad-agent-dev/customize.toml +3 -0
- package/src/bmm-skills/4-implementation/bmad-code-review/SKILL.md +2 -4
- package/src/bmm-skills/4-implementation/bmad-code-review/customize.toml +52 -0
- package/src/bmm-skills/4-implementation/bmad-code-review/steps/step-01-gather-context.md +0 -1
- package/src/bmm-skills/4-implementation/bmad-code-review/steps/step-02-review.md +10 -24
- package/src/bmm-skills/4-implementation/bmad-code-review/steps/step-03-triage.md +3 -4
- package/src/bmm-skills/4-implementation/bmad-dev-auto/SKILL.md +33 -12
- package/src/bmm-skills/4-implementation/bmad-dev-auto/customize.toml +75 -0
- package/src/bmm-skills/4-implementation/bmad-dev-auto/spec-template.md +4 -4
- package/src/bmm-skills/4-implementation/bmad-dev-auto/step-01-clarify-and-route.md +22 -4
- package/src/bmm-skills/4-implementation/bmad-dev-auto/step-02-plan.md +5 -6
- package/src/bmm-skills/4-implementation/bmad-dev-auto/step-03-implement.md +8 -4
- package/src/bmm-skills/4-implementation/bmad-dev-auto/step-04-review.md +13 -19
- package/src/bmm-skills/4-implementation/bmad-dev-story/customize.toml +3 -0
- package/src/bmm-skills/4-implementation/bmad-quick-dev/SKILL.md +6 -108
- package/src/bmm-skills/4-implementation/bmad-quick-dev/customize.toml +73 -0
- package/src/bmm-skills/4-implementation/bmad-quick-dev/render.py +381 -0
- package/src/bmm-skills/4-implementation/bmad-quick-dev/spec-template.md +2 -2
- package/src/bmm-skills/4-implementation/bmad-quick-dev/step-01-clarify-and-route.md +11 -13
- package/src/bmm-skills/4-implementation/bmad-quick-dev/step-02-plan.md +4 -9
- package/src/bmm-skills/4-implementation/bmad-quick-dev/step-03-implement.md +12 -4
- package/src/bmm-skills/4-implementation/bmad-quick-dev/step-04-review.md +8 -20
- package/src/bmm-skills/4-implementation/bmad-quick-dev/step-05-present.md +4 -4
- package/src/bmm-skills/4-implementation/bmad-quick-dev/step-oneshot.md +12 -13
- package/src/bmm-skills/4-implementation/bmad-quick-dev/sync-sprint-status.md +6 -6
- package/src/bmm-skills/4-implementation/bmad-quick-dev/workflow.md +85 -0
- package/src/bmm-skills/module-help.csv +2 -4
- package/src/bmm-skills/module.yaml +2 -5
- package/src/bmm-skills/v6-shims/README.md +24 -0
- package/src/bmm-skills/{3-solutioning → v6-shims}/bmad-create-architecture/SKILL.md +1 -1
- package/src/bmm-skills/{2-plan-workflows → v6-shims}/bmad-create-prd/SKILL.md +3 -3
- package/src/bmm-skills/v6-shims/bmad-domain-research/SKILL.md +14 -0
- package/src/bmm-skills/{2-plan-workflows → v6-shims}/bmad-edit-prd/SKILL.md +3 -3
- package/src/bmm-skills/v6-shims/bmad-market-research/SKILL.md +14 -0
- package/src/bmm-skills/v6-shims/bmad-technical-research/SKILL.md +14 -0
- package/src/bmm-skills/{2-plan-workflows → v6-shims}/bmad-validate-prd/SKILL.md +3 -3
- package/src/core-skills/bmad-advanced-elicitation/SKILL.md +26 -103
- package/src/core-skills/bmad-advanced-elicitation/customize.toml +54 -0
- package/src/core-skills/bmad-advanced-elicitation/scripts/pick_methods.py +233 -0
- package/src/core-skills/bmad-advanced-elicitation/scripts/tests/test_pick_methods.py +228 -0
- package/src/core-skills/bmad-brainstorming/SKILL.md +3 -3
- package/src/core-skills/bmad-brainstorming/assets/brain-selector.html +2 -0
- package/src/core-skills/bmad-brainstorming/references/mode-autonomous.md +1 -1
- package/src/core-skills/bmad-brainstorming/scripts/brain.py +36 -6
- package/src/core-skills/bmad-brainstorming/scripts/tests/test_brain.py +22 -0
- package/src/core-skills/bmad-customize/SKILL.md +2 -2
- package/src/core-skills/bmad-deep-recon/SKILL.md +82 -0
- package/src/core-skills/bmad-deep-recon/assets/research.template.md +18 -0
- package/src/core-skills/bmad-deep-recon/customize.toml +212 -0
- package/src/core-skills/bmad-deep-recon/references/draft.md +8 -0
- package/src/core-skills/bmad-deep-recon/references/finalize.md +11 -0
- package/src/core-skills/bmad-deep-recon/references/html-briefing.md +16 -0
- package/src/core-skills/bmad-deep-recon/references/lifecycle.md +11 -0
- package/src/core-skills/bmad-deep-recon/references/process.md +10 -0
- package/src/core-skills/bmad-deep-recon/references/run.md +73 -0
- package/src/core-skills/bmad-deep-recon/references/selection.md +13 -0
- package/src/core-skills/bmad-deep-recon/references/synthesis.md +16 -0
- package/src/core-skills/bmad-deep-recon/references/verification.md +29 -0
- package/src/core-skills/bmad-deep-recon/scripts/recon_kit.py +322 -0
- package/src/core-skills/bmad-deep-recon/scripts/tests/test_recon_kit.py +144 -0
- package/src/core-skills/bmad-deep-recon/types/academic-lit.md +19 -0
- package/src/core-skills/bmad-deep-recon/types/competitive.md +19 -0
- package/src/core-skills/bmad-deep-recon/types/domain.md +19 -0
- package/src/core-skills/bmad-deep-recon/types/market.md +19 -0
- package/src/core-skills/bmad-deep-recon/types/technical.md +19 -0
- package/src/core-skills/bmad-deep-recon/types/user-voice.md +19 -0
- package/src/core-skills/bmad-forge-idea/SKILL.md +2 -2
- package/src/core-skills/bmad-forge-idea/scripts/resolve_personas.py +4 -1
- package/src/core-skills/bmad-help/SKILL.md +1 -1
- package/src/core-skills/bmad-party-mode/SKILL.md +2 -2
- package/src/core-skills/bmad-party-mode/scripts/resolve_party.py +11 -3
- package/src/core-skills/bmad-review/SKILL.md +49 -0
- package/src/core-skills/bmad-review/customize.toml +141 -0
- package/src/core-skills/bmad-review/references/editorial-common.md +56 -0
- package/src/core-skills/bmad-review/references/lens-adversarial.md +18 -0
- package/src/core-skills/bmad-review/references/lens-edge-case-hunter.md +54 -0
- package/src/core-skills/bmad-review/references/lens-prose.md +7 -0
- package/src/core-skills/bmad-review/references/lens-structure.md +9 -0
- package/src/core-skills/{bmad-review-verification-gap/SKILL.md → bmad-review/references/lens-verification-gap.md} +15 -39
- package/src/core-skills/bmad-review/references/structure-models.md +44 -0
- package/src/core-skills/bmad-review/scripts/tests/test_word_metrics.py +62 -0
- package/src/core-skills/bmad-review/scripts/word_metrics.py +102 -0
- package/src/core-skills/module-help.csv +3 -7
- package/src/core-skills/module.yaml +5 -0
- package/src/core-skills/v6-shims/README.md +25 -0
- package/src/core-skills/v6-shims/bmad-editorial-review/SKILL.md +6 -0
- package/src/core-skills/v6-shims/bmad-editorial-review/customize.toml +31 -0
- package/src/core-skills/v6-shims/bmad-editorial-review-prose/SKILL.md +6 -0
- package/src/core-skills/v6-shims/bmad-editorial-review-structure/SKILL.md +6 -0
- package/src/core-skills/v6-shims/bmad-review-adversarial-general/SKILL.md +6 -0
- package/src/core-skills/v6-shims/bmad-review-edge-case-hunter/SKILL.md +6 -0
- package/src/core-skills/v6-shims/bmad-review-verification-gap/SKILL.md +6 -0
- package/tools/installer/ide/platform-codes.yaml +7 -0
- package/tools/installer/modules/custom-module-manager.js +12 -6
- package/tools/installer/modules/external-manager.js +12 -8
- package/tools/installer/modules/git-env.js +47 -0
- package/tools/skill-validator.md +11 -1
- package/tools/validate-skills.js +33 -0
- package/src/bmm-skills/1-analysis/research/bmad-domain-research/SKILL.md +0 -96
- package/src/bmm-skills/1-analysis/research/bmad-domain-research/customize.toml +0 -41
- package/src/bmm-skills/1-analysis/research/bmad-domain-research/domain-steps/step-01-init.md +0 -137
- package/src/bmm-skills/1-analysis/research/bmad-domain-research/domain-steps/step-02-domain-analysis.md +0 -229
- package/src/bmm-skills/1-analysis/research/bmad-domain-research/domain-steps/step-03-competitive-landscape.md +0 -238
- package/src/bmm-skills/1-analysis/research/bmad-domain-research/domain-steps/step-04-regulatory-focus.md +0 -206
- package/src/bmm-skills/1-analysis/research/bmad-domain-research/domain-steps/step-05-technical-trends.md +0 -234
- package/src/bmm-skills/1-analysis/research/bmad-domain-research/domain-steps/step-06-research-synthesis.md +0 -450
- package/src/bmm-skills/1-analysis/research/bmad-domain-research/research.template.md +0 -29
- package/src/bmm-skills/1-analysis/research/bmad-market-research/SKILL.md +0 -96
- package/src/bmm-skills/1-analysis/research/bmad-market-research/customize.toml +0 -41
- package/src/bmm-skills/1-analysis/research/bmad-market-research/research.template.md +0 -29
- package/src/bmm-skills/1-analysis/research/bmad-market-research/steps/step-01-init.md +0 -184
- package/src/bmm-skills/1-analysis/research/bmad-market-research/steps/step-02-customer-behavior.md +0 -239
- package/src/bmm-skills/1-analysis/research/bmad-market-research/steps/step-03-customer-pain-points.md +0 -251
- package/src/bmm-skills/1-analysis/research/bmad-market-research/steps/step-04-customer-decisions.md +0 -261
- package/src/bmm-skills/1-analysis/research/bmad-market-research/steps/step-05-competitive-analysis.md +0 -173
- package/src/bmm-skills/1-analysis/research/bmad-market-research/steps/step-06-research-completion.md +0 -484
- package/src/bmm-skills/1-analysis/research/bmad-technical-research/SKILL.md +0 -96
- package/src/bmm-skills/1-analysis/research/bmad-technical-research/customize.toml +0 -41
- package/src/bmm-skills/1-analysis/research/bmad-technical-research/research.template.md +0 -29
- package/src/bmm-skills/1-analysis/research/bmad-technical-research/technical-steps/step-01-init.md +0 -137
- package/src/bmm-skills/1-analysis/research/bmad-technical-research/technical-steps/step-02-technical-overview.md +0 -239
- package/src/bmm-skills/1-analysis/research/bmad-technical-research/technical-steps/step-03-integration-patterns.md +0 -248
- package/src/bmm-skills/1-analysis/research/bmad-technical-research/technical-steps/step-04-architectural-patterns.md +0 -202
- package/src/bmm-skills/1-analysis/research/bmad-technical-research/technical-steps/step-05-implementation-research.md +0 -233
- package/src/bmm-skills/1-analysis/research/bmad-technical-research/technical-steps/step-06-research-synthesis.md +0 -493
- package/src/core-skills/bmad-brainstorming/analysis/catalog-analysis.md +0 -239
- package/src/core-skills/bmad-brainstorming/analysis/method-matrix.csv +0 -109
- package/src/core-skills/bmad-editorial-review-prose/SKILL.md +0 -86
- package/src/core-skills/bmad-editorial-review-structure/SKILL.md +0 -179
- package/src/core-skills/bmad-index-docs/SKILL.md +0 -66
- package/src/core-skills/bmad-review-adversarial-general/SKILL.md +0 -37
- package/src/core-skills/bmad-review-edge-case-hunter/SKILL.md +0 -73
- package/src/core-skills/bmad-review-edge-case-hunter/references/deletion-check.md +0 -14
- package/src/core-skills/bmad-shard-doc/SKILL.md +0 -105
- /package/src/{core-skills → bmm-skills/2-plan-workflows}/bmad-spec/assets/headless-schemas.md +0 -0
- /package/src/bmm-skills/{3-solutioning → v6-shims}/bmad-create-architecture/customize.toml +0 -0
- /package/src/bmm-skills/{2-plan-workflows → v6-shims}/bmad-create-prd/customize.toml +0 -0
- /package/src/bmm-skills/{2-plan-workflows → v6-shims}/bmad-edit-prd/customize.toml +0 -0
- /package/src/bmm-skills/{2-plan-workflows → v6-shims}/bmad-validate-prd/customize.toml +0 -0
- /package/src/core-skills/bmad-advanced-elicitation/{methods.csv → assets/methods.csv} +0 -0
- /package/src/core-skills/bmad-party-mode/scripts/tests/{test-resolve_party.py → test_resolve_party.py} +0 -0
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# /// script
|
|
3
|
+
# requires-python = ">=3.11"
|
|
4
|
+
# ///
|
|
5
|
+
"""render.py — bmad-quick-dev template renderer.
|
|
6
|
+
|
|
7
|
+
Resolves compile-time {{.variable}} placeholders from BMad's central config,
|
|
8
|
+
bakes absolute paths for {project-root} into derived values, resolves and
|
|
9
|
+
inlines the skill's [workflow] customization block, and writes rendered .md
|
|
10
|
+
files to {project-root}/_bmad/render/bmad-quick-dev/.
|
|
11
|
+
|
|
12
|
+
Config: four-layer merge of _bmad/config.toml + config.user.toml +
|
|
13
|
+
custom/config.toml + custom/config.user.toml (post-#2285 installs).
|
|
14
|
+
Keys surface from [core] and [modules.bmm]. Missing or unparseable
|
|
15
|
+
config.toml → HALT. A {{.var}} referenced by this skill's .md sources but
|
|
16
|
+
absent from the merged config → HALT (never a silent empty substitution).
|
|
17
|
+
Optional layers may be missing, but one that exists and cannot be parsed
|
|
18
|
+
or read → HALT.
|
|
19
|
+
|
|
20
|
+
Customization: three-layer merge of {skill}/customize.toml +
|
|
21
|
+
_bmad/custom/bmad-quick-dev.toml + .user.toml (same structural rules as
|
|
22
|
+
resolve_customization.py). The resolved [workflow] values fill {workflow.*}
|
|
23
|
+
placeholders, so this skill needs no runtime resolve_customization.py call.
|
|
24
|
+
Other single-curly placeholders ({project-root}, {spec_file}, {skill-root},
|
|
25
|
+
...) pass through untouched for the LLM to resolve during workflow execution.
|
|
26
|
+
|
|
27
|
+
Every invocation rebuilds from scratch — no hash, no cache.
|
|
28
|
+
Python 3.11+ stdlib only. UTF-8 I/O.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
import os
|
|
32
|
+
import posixpath
|
|
33
|
+
import re
|
|
34
|
+
import sys
|
|
35
|
+
import tomllib
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def find_project_root():
|
|
39
|
+
"""Walk up from cwd until a _bmad/ directory is found. On failure, print a
|
|
40
|
+
HALT instruction to stdout and exit non-zero."""
|
|
41
|
+
current = os.path.abspath(os.getcwd())
|
|
42
|
+
while True:
|
|
43
|
+
candidate = os.path.join(current, "_bmad")
|
|
44
|
+
if os.path.isdir(candidate):
|
|
45
|
+
return current
|
|
46
|
+
parent = os.path.dirname(current)
|
|
47
|
+
if parent == current:
|
|
48
|
+
print(
|
|
49
|
+
f"HALT and report to the user: no _bmad/ directory found walking up from {os.getcwd()}"
|
|
50
|
+
)
|
|
51
|
+
sys.exit(1)
|
|
52
|
+
current = parent
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def load_toml(path, required=False):
|
|
56
|
+
"""Load a TOML file. Only absence is negotiable: a missing optional file
|
|
57
|
+
returns {} (customization layers are optional), a missing required file
|
|
58
|
+
HALTs. A file that exists but cannot be parsed or read always HALTs —
|
|
59
|
+
stdout is how this script signals workflow halts to its LLM caller — the
|
|
60
|
+
user wrote it to be honored, and silently continuing with {} would discard
|
|
61
|
+
their customizations with no failure signal."""
|
|
62
|
+
if not os.path.isfile(path):
|
|
63
|
+
if required:
|
|
64
|
+
print(
|
|
65
|
+
f"HALT and report to the user: required config file not found: {path} — "
|
|
66
|
+
"ensure this is a post-#2285 BMAD install"
|
|
67
|
+
)
|
|
68
|
+
sys.exit(1)
|
|
69
|
+
return {}
|
|
70
|
+
try:
|
|
71
|
+
with open(path, "rb") as fh:
|
|
72
|
+
parsed = tomllib.load(fh)
|
|
73
|
+
except tomllib.TOMLDecodeError as error:
|
|
74
|
+
print(f"HALT and report to the user: failed to parse {path}: {error}")
|
|
75
|
+
sys.exit(1)
|
|
76
|
+
except OSError as error:
|
|
77
|
+
print(f"HALT and report to the user: failed to read {path}: {error}")
|
|
78
|
+
sys.exit(1)
|
|
79
|
+
if not isinstance(parsed, dict):
|
|
80
|
+
return {}
|
|
81
|
+
return parsed
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _deep_merge(base, override):
|
|
85
|
+
"""Dict-aware deep merge. Lists and scalars: override wins (we don't need
|
|
86
|
+
the full keyed-merge semantics of resolve_config.py — quick-dev only reads
|
|
87
|
+
flat scalars out of [core] and [modules.bmm])."""
|
|
88
|
+
if isinstance(base, dict) and isinstance(override, dict):
|
|
89
|
+
result = dict(base)
|
|
90
|
+
for key, value in override.items():
|
|
91
|
+
result[key] = _deep_merge(result[key], value) if key in result else value
|
|
92
|
+
return result
|
|
93
|
+
return override
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _detect_keyed_merge_field(items):
|
|
97
|
+
"""Return 'code' or 'id' if every table item carries that same field.
|
|
98
|
+
Mixed or partial arrays return None and fall through to append."""
|
|
99
|
+
if not items or not all(isinstance(item, dict) for item in items):
|
|
100
|
+
return None
|
|
101
|
+
for candidate in ("code", "id"):
|
|
102
|
+
if all(item.get(candidate) is not None for item in items):
|
|
103
|
+
return candidate
|
|
104
|
+
return None
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _merge_by_key(base, override, key_name):
|
|
108
|
+
result = []
|
|
109
|
+
index_by_key = {}
|
|
110
|
+
for item in base:
|
|
111
|
+
if not isinstance(item, dict):
|
|
112
|
+
continue
|
|
113
|
+
if item.get(key_name) is not None:
|
|
114
|
+
index_by_key[item[key_name]] = len(result)
|
|
115
|
+
result.append(dict(item))
|
|
116
|
+
for item in override:
|
|
117
|
+
if not isinstance(item, dict):
|
|
118
|
+
result.append(item)
|
|
119
|
+
continue
|
|
120
|
+
key = item.get(key_name)
|
|
121
|
+
if key is not None and key in index_by_key:
|
|
122
|
+
result[index_by_key[key]] = dict(item)
|
|
123
|
+
else:
|
|
124
|
+
if key is not None:
|
|
125
|
+
index_by_key[key] = len(result)
|
|
126
|
+
result.append(dict(item))
|
|
127
|
+
return result
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def _merge_arrays(base, override):
|
|
131
|
+
"""Shape-aware array merge: keyed merge if every item has code/id, else append."""
|
|
132
|
+
base_arr = base if isinstance(base, list) else []
|
|
133
|
+
override_arr = override if isinstance(override, list) else []
|
|
134
|
+
keyed_field = _detect_keyed_merge_field(base_arr + override_arr)
|
|
135
|
+
if keyed_field:
|
|
136
|
+
return _merge_by_key(base_arr, override_arr, keyed_field)
|
|
137
|
+
return base_arr + override_arr
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def _structural_merge(base, override):
|
|
141
|
+
"""Faithful port of resolve_customization.py's deep_merge: tables deep-merge,
|
|
142
|
+
arrays-of-tables keyed by code/id replace-then-append (other arrays append),
|
|
143
|
+
scalars override. Used only for the [workflow] customization layers — the
|
|
144
|
+
central-config path keeps its own simpler _deep_merge. Duplicated rather than
|
|
145
|
+
imported to keep this skill self-contained."""
|
|
146
|
+
if isinstance(base, dict) and isinstance(override, dict):
|
|
147
|
+
result = dict(base)
|
|
148
|
+
for key, over_val in override.items():
|
|
149
|
+
result[key] = (
|
|
150
|
+
_structural_merge(result[key], over_val) if key in result else over_val
|
|
151
|
+
)
|
|
152
|
+
return result
|
|
153
|
+
if isinstance(base, list) and isinstance(override, list):
|
|
154
|
+
return _merge_arrays(base, override)
|
|
155
|
+
return override
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def resolve_workflow(root, skill_dir, skill_name):
|
|
159
|
+
"""Resolve the [workflow] customization block via the three-layer merge
|
|
160
|
+
(skill defaults -> team -> user), highest priority last. Same structural
|
|
161
|
+
rules as resolve_customization.py. All three layers are optional: a missing
|
|
162
|
+
file is skipped, but an unparseable one HALTs (via load_toml)."""
|
|
163
|
+
defaults = load_toml(posixpath.join(skill_dir, "customize.toml"))
|
|
164
|
+
custom_dir = posixpath.join(root, "_bmad", "custom")
|
|
165
|
+
team = load_toml(posixpath.join(custom_dir, f"{skill_name}.toml"))
|
|
166
|
+
user = load_toml(posixpath.join(custom_dir, f"{skill_name}.user.toml"))
|
|
167
|
+
merged = _structural_merge(defaults, team)
|
|
168
|
+
merged = _structural_merge(merged, user)
|
|
169
|
+
workflow = merged.get("workflow")
|
|
170
|
+
return workflow if isinstance(workflow, dict) else {}
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def load_central_config(root):
|
|
174
|
+
"""Four-layer merge of _bmad/config.toml and its peers (highest priority
|
|
175
|
+
last). HALTs if the base _bmad/config.toml is missing or unparseable."""
|
|
176
|
+
bmad_dir = posixpath.join(root, "_bmad")
|
|
177
|
+
base_team = load_toml(posixpath.join(bmad_dir, "config.toml"), required=True)
|
|
178
|
+
base_user = load_toml(posixpath.join(bmad_dir, "config.user.toml"))
|
|
179
|
+
custom_team = load_toml(posixpath.join(bmad_dir, "custom", "config.toml"))
|
|
180
|
+
custom_user = load_toml(posixpath.join(bmad_dir, "custom", "config.user.toml"))
|
|
181
|
+
|
|
182
|
+
merged = _deep_merge(base_team, base_user)
|
|
183
|
+
merged = _deep_merge(merged, custom_team)
|
|
184
|
+
merged = _deep_merge(merged, custom_user)
|
|
185
|
+
return merged
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def flatten_central_config(merged):
|
|
189
|
+
"""Lift scalar keys from [core] and [modules.bmm] into a single namespace.
|
|
190
|
+
Module keys take precedence on collision (installer strips core keys from
|
|
191
|
+
module buckets, so collisions shouldn't happen in practice)."""
|
|
192
|
+
flat = {}
|
|
193
|
+
modules = merged.get("modules")
|
|
194
|
+
modules = modules if isinstance(modules, dict) else {}
|
|
195
|
+
for section in (merged.get("core"), modules.get("bmm")):
|
|
196
|
+
if not isinstance(section, dict):
|
|
197
|
+
continue
|
|
198
|
+
for key, value in section.items():
|
|
199
|
+
if isinstance(value, bool):
|
|
200
|
+
flat[key] = "true" if value else "false"
|
|
201
|
+
elif isinstance(value, (str, int, float)):
|
|
202
|
+
flat[key] = str(value)
|
|
203
|
+
return flat
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def render_template(content, vars_):
|
|
207
|
+
"""Resolve {{.var}} substitutions. Unresolved references emit an empty string,
|
|
208
|
+
but main() HALTs on any missing reference before rendering starts, so this
|
|
209
|
+
fallback never fires in practice."""
|
|
210
|
+
return re.sub(r"\{\{\.(\w+)\}\}", lambda m: vars_.get(m.group(1), ""), content)
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def collect_missing_vars(sources, vars_):
|
|
214
|
+
"""Map each {{.var}} name referenced by the source .md files but absent from
|
|
215
|
+
the merged config to the files that reference it. A missing key must HALT:
|
|
216
|
+
missingkey=zero rendering would bake a corrupted workflow (empty paths,
|
|
217
|
+
blank language lines) with no failure signal."""
|
|
218
|
+
missing = {}
|
|
219
|
+
for fname, content in sources:
|
|
220
|
+
for name in re.findall(r"\{\{\.(\w+)\}\}", content):
|
|
221
|
+
if name not in vars_:
|
|
222
|
+
files = missing.setdefault(name, [])
|
|
223
|
+
if fname not in files:
|
|
224
|
+
files.append(fname)
|
|
225
|
+
return missing
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def _scalar_str(value):
|
|
229
|
+
"""Stringify a scalar for inline rendering: booleans lowercase (matching
|
|
230
|
+
BMad config conventions), None as empty, everything else via str()."""
|
|
231
|
+
if value is None:
|
|
232
|
+
return ""
|
|
233
|
+
if isinstance(value, bool):
|
|
234
|
+
return "true" if value else "false"
|
|
235
|
+
return str(value)
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
# [workflow] keys holding review layers ([[workflow.review_layers]] tables with
|
|
239
|
+
# id/name/instruction/when fields). This renderer knows this skill's
|
|
240
|
+
# customization schema outright — layer semantics are materialized here, not
|
|
241
|
+
# interpreted by the LLM at run time.
|
|
242
|
+
_REVIEW_LAYER_KEYS = ("review_layers", "oneshot_review_layers")
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def _render_review_layers(layers):
|
|
246
|
+
"""Materialize review layers into direct invocation blocks. A layer with an
|
|
247
|
+
empty or missing instruction is disabled (that is how an override turns off
|
|
248
|
+
a default layer) and drops out entirely. A `when` condition is the one part
|
|
249
|
+
that stays with the LLM: it renders as a run-time guard line. No active
|
|
250
|
+
layers renders as the HALT instruction the workflow would otherwise have to
|
|
251
|
+
derive from an empty list."""
|
|
252
|
+
active = [
|
|
253
|
+
layer
|
|
254
|
+
for layer in layers
|
|
255
|
+
if isinstance(layer, dict) and _scalar_str(layer.get("instruction")).strip()
|
|
256
|
+
]
|
|
257
|
+
if not active:
|
|
258
|
+
return (
|
|
259
|
+
"No review layers are active. HALT with status `blocked` and "
|
|
260
|
+
"blocking condition `no active review layers`."
|
|
261
|
+
)
|
|
262
|
+
blocks = []
|
|
263
|
+
for layer in active:
|
|
264
|
+
title = (
|
|
265
|
+
_scalar_str(layer.get("name")).strip()
|
|
266
|
+
or _scalar_str(layer.get("id")).strip()
|
|
267
|
+
or "Review layer"
|
|
268
|
+
)
|
|
269
|
+
lines = [f"#### {title}", ""]
|
|
270
|
+
when = _scalar_str(layer.get("when")).strip()
|
|
271
|
+
if when:
|
|
272
|
+
lines.append(
|
|
273
|
+
"Run this layer only if the following holds in the "
|
|
274
|
+
f"current context: `{when}`"
|
|
275
|
+
)
|
|
276
|
+
lines.append("")
|
|
277
|
+
lines.append(_scalar_str(layer.get("instruction")).strip("\n"))
|
|
278
|
+
blocks.append("\n".join(lines))
|
|
279
|
+
return "\n\n".join(blocks)
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def _render_workflow_value(key, value):
|
|
283
|
+
"""Format a resolved [workflow] value for inline substitution. Review-layer
|
|
284
|
+
keys materialize as invocation blocks; other lists render as markdown
|
|
285
|
+
bullets (empty -> '_None._'); scalars render verbatim. Each list item uses
|
|
286
|
+
the same scalar formatting so booleans stay consistent. Entries are emitted
|
|
287
|
+
as-is so runtime placeholders like {project-root} or {diff_output} survive
|
|
288
|
+
for the LLM to resolve."""
|
|
289
|
+
if key in _REVIEW_LAYER_KEYS and isinstance(value, list):
|
|
290
|
+
return _render_review_layers(value)
|
|
291
|
+
if isinstance(value, list):
|
|
292
|
+
if not value:
|
|
293
|
+
return "_None._"
|
|
294
|
+
return "\n".join(f"- {_scalar_str(item)}" for item in value)
|
|
295
|
+
return _scalar_str(value)
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
def render_workflow(content, workflow):
|
|
299
|
+
"""Resolve {workflow.<key>} placeholders from the resolved [workflow] block.
|
|
300
|
+
Unknown keys emit an empty string (missingkey=zero, matching render_template).
|
|
301
|
+
Distinct regex from render_template so single-curly runtime placeholders
|
|
302
|
+
elsewhere are untouched."""
|
|
303
|
+
return re.sub(
|
|
304
|
+
r"\{workflow\.(\w+)\}",
|
|
305
|
+
lambda m: _render_workflow_value(m.group(1), workflow.get(m.group(1))),
|
|
306
|
+
content,
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
def main():
|
|
311
|
+
script_dir = os.path.dirname(os.path.abspath(__file__))
|
|
312
|
+
skill_name = os.path.basename(script_dir)
|
|
313
|
+
root = find_project_root()
|
|
314
|
+
root = root.replace(os.sep, "/")
|
|
315
|
+
|
|
316
|
+
vars_ = flatten_central_config(load_central_config(root))
|
|
317
|
+
|
|
318
|
+
for key in list(vars_.keys()):
|
|
319
|
+
vars_[key] = vars_[key].replace("{project-root}", root)
|
|
320
|
+
|
|
321
|
+
vars_["project_root"] = root
|
|
322
|
+
|
|
323
|
+
# Guarded ahead of the general missing-vars scan: sprint_status and
|
|
324
|
+
# deferred_work_file derive from it below, and unlike the scan (absent
|
|
325
|
+
# keys only) this also HALTs on a present-but-empty value.
|
|
326
|
+
implementation_artifacts = vars_.get("implementation_artifacts", "").strip()
|
|
327
|
+
if not implementation_artifacts:
|
|
328
|
+
print(
|
|
329
|
+
"HALT and report to the user: config is missing `implementation_artifacts` "
|
|
330
|
+
"(expected under [core] or [modules.bmm] in _bmad/config.toml)"
|
|
331
|
+
)
|
|
332
|
+
sys.exit(1)
|
|
333
|
+
|
|
334
|
+
vars_["sprint_status"] = posixpath.join(
|
|
335
|
+
implementation_artifacts, "sprint-status.yaml"
|
|
336
|
+
)
|
|
337
|
+
vars_["deferred_work_file"] = posixpath.join(
|
|
338
|
+
implementation_artifacts, "deferred-work.md"
|
|
339
|
+
)
|
|
340
|
+
|
|
341
|
+
sources = []
|
|
342
|
+
for fname in sorted(os.listdir(script_dir)):
|
|
343
|
+
if not fname.endswith(".md") or fname == "SKILL.md":
|
|
344
|
+
continue
|
|
345
|
+
with open(
|
|
346
|
+
posixpath.join(script_dir, fname), "r", encoding="utf-8", newline=""
|
|
347
|
+
) as fh:
|
|
348
|
+
sources.append((fname, fh.read()))
|
|
349
|
+
|
|
350
|
+
missing = collect_missing_vars(sources, vars_)
|
|
351
|
+
if missing:
|
|
352
|
+
details = "; ".join(
|
|
353
|
+
f"`{name}` (referenced by {', '.join(files)})"
|
|
354
|
+
for name, files in sorted(missing.items())
|
|
355
|
+
)
|
|
356
|
+
print(
|
|
357
|
+
f"HALT and report to the user: config is missing {details} "
|
|
358
|
+
"(expected under [core] or [modules.bmm] in _bmad/config.toml)"
|
|
359
|
+
)
|
|
360
|
+
sys.exit(1)
|
|
361
|
+
|
|
362
|
+
workflow = resolve_workflow(root, script_dir.replace(os.sep, "/"), skill_name)
|
|
363
|
+
|
|
364
|
+
out_dir = posixpath.join(root, "_bmad", "render", skill_name)
|
|
365
|
+
os.makedirs(out_dir, exist_ok=True)
|
|
366
|
+
|
|
367
|
+
for fname in os.listdir(out_dir):
|
|
368
|
+
if fname.endswith(".md"):
|
|
369
|
+
os.remove(posixpath.join(out_dir, fname))
|
|
370
|
+
|
|
371
|
+
for fname, content in sources:
|
|
372
|
+
dst = posixpath.join(out_dir, fname)
|
|
373
|
+
with open(dst, "w", encoding="utf-8", newline="") as fh:
|
|
374
|
+
fh.write(render_workflow(render_template(content, vars_), workflow))
|
|
375
|
+
|
|
376
|
+
workflow_md = posixpath.join(out_dir, "workflow.md")
|
|
377
|
+
print(f"read and follow {workflow_md}")
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
if __name__ == "__main__":
|
|
381
|
+
main()
|
|
@@ -46,10 +46,10 @@ context: [] # optional: `{project-root}/`-prefixed paths to project-wide standar
|
|
|
46
46
|
|
|
47
47
|
## Code Map
|
|
48
48
|
|
|
49
|
-
<!-- Agent-populated during planning. Annotated paths prevent blind codebase searching. -->
|
|
49
|
+
<!-- Agent-populated during planning: the distilled investigation map, so the spec carries what exploration found and the implementation handoff need only point here. Annotated paths prevent blind codebase searching. Entries may drill to symbol/line and carry reuse pointers ("mirror X at FILE:LINE") or read-only evidence, where they save the implementer a search. -->
|
|
50
50
|
|
|
51
51
|
- `FILE` -- ROLE_OR_RELEVANCE
|
|
52
|
-
- `FILE` -- ROLE_OR_RELEVANCE
|
|
52
|
+
- `FILE:LINE` -- ROLE_OR_RELEVANCE; reuse pointer or READ-ONLY evidence when relevant
|
|
53
53
|
|
|
54
54
|
## Tasks & Acceptance
|
|
55
55
|
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
---
|
|
2
|
-
deferred_work_file: '{implementation_artifacts}/deferred-work.md'
|
|
3
2
|
spec_file: '' # set at runtime for both routes before leaving this step
|
|
4
3
|
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
|
|
5
4
|
---
|
|
@@ -8,7 +7,7 @@ story_key: '' # set at runtime to the current story's full sprint-status key (e.
|
|
|
8
7
|
|
|
9
8
|
## RULES
|
|
10
9
|
|
|
11
|
-
-
|
|
10
|
+
- **Language** — Speak in `{{.communication_language}}`. Write any file output in `{{.document_output_language}}`.
|
|
12
11
|
- The prompt that triggered this workflow IS the intent — not a hint.
|
|
13
12
|
- Do NOT assume you start from zero.
|
|
14
13
|
- 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.
|
|
@@ -29,7 +28,7 @@ Before listing artifacts or prompting the user, check whether you already know t
|
|
|
29
28
|
Use the same routing as above.
|
|
30
29
|
|
|
31
30
|
3. Otherwise — scan artifacts and ask
|
|
32
|
-
- 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).
|
|
31
|
+
- 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).
|
|
33
32
|
- If `draft` selected: Set `spec_file`. Run **Story-key resolution** (below). **EARLY EXIT** → `./step-02-plan.md` (resume planning from the draft)
|
|
34
33
|
- If `ready-for-dev` or `in-progress` selected: Set `spec_file`. Run **Story-key resolution** (below). **EARLY EXIT** → `./step-03-implement.md`
|
|
35
34
|
- If `in-review` selected: Set `spec_file`. Run **Story-key resolution** (below). **EARLY EXIT** → `./step-04-review.md`
|
|
@@ -39,14 +38,14 @@ Never ask extra questions if you already understand what the user intends.
|
|
|
39
38
|
|
|
40
39
|
### Story-key resolution
|
|
41
40
|
|
|
42
|
-
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 `
|
|
41
|
+
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.
|
|
43
42
|
|
|
44
|
-
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 `
|
|
43
|
+
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).
|
|
45
44
|
|
|
46
45
|
## INSTRUCTIONS
|
|
47
46
|
|
|
48
47
|
1. Load context.
|
|
49
|
-
- List files in `{planning_artifacts}` and `{implementation_artifacts}`.
|
|
48
|
+
- List files in `{{.planning_artifacts}}` and `{{.implementation_artifacts}}`.
|
|
50
49
|
- If you find an unformatted spec or intent file, ingest its contents to form your understanding of the intent.
|
|
51
50
|
- **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.
|
|
52
51
|
|
|
@@ -54,17 +53,17 @@ If the spec is an epic story and `{sprint_status}` exists: find the `development
|
|
|
54
53
|
|
|
55
54
|
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.
|
|
56
55
|
|
|
57
|
-
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.
|
|
56
|
+
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.
|
|
58
57
|
- **If valid:** load it as the primary planning context. Do not load raw planning docs (PRD, architecture, UX, etc.). Skip to step 5.
|
|
59
58
|
- **If missing, empty, or invalid:** continue to step 3.
|
|
60
59
|
|
|
61
|
-
3. **Compile epic context.** Produce `{implementation_artifacts}/epic-<N>-context.md` by following `./compile-epic-context.md`, in order of preference:
|
|
62
|
-
- **Preferred — subagent:** spawn a subagent 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`.
|
|
60
|
+
3. **Compile epic context.** Produce `{{.implementation_artifacts}}/epic-<N>-context.md` by following `./compile-epic-context.md`, in order of preference:
|
|
61
|
+
- **Preferred — subagent:** spawn a subagent synchronously (wait for it to return in this turn) 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`.
|
|
63
62
|
- **Fallback — inline** (for runtimes without subagent support, e.g. Copilot, Codex, local Ollama, older Claude): if your runtime cannot spawn subagents, or the spawn fails/times out, read `./compile-epic-context.md` yourself and follow its instructions to produce the same output file.
|
|
64
63
|
|
|
65
64
|
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.
|
|
66
65
|
|
|
67
|
-
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.
|
|
66
|
+
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.
|
|
68
67
|
|
|
69
68
|
6. **Resolve `{story_key}`.** If not already set by an earlier early-exit path, run **Story-key resolution** (above) now.
|
|
70
69
|
|
|
@@ -82,7 +81,7 @@ If the spec is an epic story and `{sprint_status}` exists: find the `development
|
|
|
82
81
|
- Present detected distinct goals as a bullet list.
|
|
83
82
|
- Explain briefly (2–4 sentences): why each goal qualifies as independently shippable, any coupling risks if split, and which goal you recommend tackling first.
|
|
84
83
|
- HALT and ask human: `[S] Split — pick first goal, defer the rest` | `[K] Keep all goals — accept the risks`
|
|
85
|
-
- On **S**: For each deferred goal, append one new entry to `{deferred_work_file}` using this format. Do not modify existing entries or look for duplicates. Narrow scope to the first-mentioned goal. Continue routing.
|
|
84
|
+
- On **S**: For each deferred goal, append one new entry to `{{.deferred_work_file}}` using this format. Do not modify existing entries or look for duplicates. Narrow scope to the first-mentioned goal. Continue routing.
|
|
86
85
|
```markdown
|
|
87
86
|
- source_spec: none
|
|
88
87
|
summary: <one sentence naming the deferred goal>
|
|
@@ -91,7 +90,7 @@ If the spec is an epic story and `{sprint_status}` exists: find the `development
|
|
|
91
90
|
- On **K**: Proceed as-is.
|
|
92
91
|
5. Route — choose exactly one:
|
|
93
92
|
|
|
94
|
-
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`.
|
|
93
|
+
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`.
|
|
95
94
|
|
|
96
95
|
**a) One-shot** — zero blast radius: no plausible path by which this change causes unintended consequences elsewhere. Clear intent, no architectural decisions.
|
|
97
96
|
|
|
@@ -99,7 +98,6 @@ If the spec is an epic story and `{sprint_status}` exists: find the `development
|
|
|
99
98
|
|
|
100
99
|
**b) Plan-code-review** — everything else. When uncertain whether blast radius is truly zero, choose this path.
|
|
101
100
|
|
|
102
|
-
|
|
103
101
|
## NEXT
|
|
104
102
|
|
|
105
103
|
Read fully and follow `./step-02-plan.md`
|
|
@@ -1,25 +1,21 @@
|
|
|
1
|
-
---
|
|
2
|
-
deferred_work_file: '{implementation_artifacts}/deferred-work.md'
|
|
3
|
-
---
|
|
4
|
-
|
|
5
1
|
# Step 2: Plan
|
|
6
2
|
|
|
7
3
|
## RULES
|
|
8
4
|
|
|
9
|
-
-
|
|
5
|
+
- **Language** — Speak in `{{.communication_language}}`. Write any file output in `{{.document_output_language}}`.
|
|
10
6
|
- No intermediate approvals.
|
|
11
7
|
|
|
12
8
|
## INSTRUCTIONS
|
|
13
9
|
|
|
14
10
|
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.
|
|
15
|
-
2. Investigate codebase. _Isolate deep exploration in subagents/tasks where available. To prevent context snowballing, instruct subagents to give you distilled summaries only._
|
|
16
|
-
3. Read `./spec-template.md` fully. Fill it out based on the intent and investigation. If `
|
|
11
|
+
2. Investigate codebase. _Isolate deep exploration in synchronous subagents/tasks where available. To prevent context snowballing, instruct subagents to give you distilled summaries only._ Decide which findings actually matter for execution — the specific files, symbols/lines, reuse points, and read-only constraints — and carry those forward for the Code Map. This is where the investigation lands: the spec preserves it so it is never re-narrated to the implementer at dispatch time.
|
|
12
|
+
3. Read `./spec-template.md` fully. Fill it out based on the intent and investigation, resolving the template's `date` field to the current system date. Drain the investigation into the `## Code Map` section — annotated paths, symbol/line anchors, reuse pointers, and read-only evidence — so the spec is the implementer's investigation map and the step-03 handoff need only point at it. If `preserved_intent` is non-empty, replace the `<frozen-after-approval>` block in the spec you just filled out with `preserved_intent`, before writing. Write the result to `{spec_file}`.
|
|
17
13
|
4. Self-review against READY FOR DEVELOPMENT standard.
|
|
18
14
|
5. If intent gaps exist, do not fantasize, do not leave open questions, HALT and ask the human.
|
|
19
15
|
6. Token count check (see SCOPE STANDARD). If spec exceeds 1600 tokens:
|
|
20
16
|
- Show user the token count.
|
|
21
17
|
- HALT and ask human: `[S] Split — carve off secondary goals` | `[K] Keep full spec — accept the risks`
|
|
22
|
-
- On **S**: Propose the split — name each secondary goal. For each deferred goal, append one new entry to `{deferred_work_file}` using this format. Do not modify existing entries or look for duplicates. 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.
|
|
18
|
+
- On **S**: Propose the split — name each secondary goal. For each deferred goal, append one new entry to `{{.deferred_work_file}}` using this format. Do not modify existing entries or look for duplicates. 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.
|
|
23
19
|
```markdown
|
|
24
20
|
- source_spec: `{spec_file}`
|
|
25
21
|
summary: <one sentence naming the deferred goal>
|
|
@@ -46,7 +42,6 @@ HALT and ask human: `[A] Approve` | `[E] Edit`
|
|
|
46
42
|
- **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.
|
|
47
43
|
- **E**: Apply changes, then return to CHECKPOINT 1.
|
|
48
44
|
|
|
49
|
-
|
|
50
45
|
## NEXT
|
|
51
46
|
|
|
52
47
|
Read fully and follow `./step-03-implement.md`
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
## RULES
|
|
7
7
|
|
|
8
|
-
-
|
|
8
|
+
- **Language** — Speak in `{{.communication_language}}`. Write any file output in `{{.document_output_language}}`.
|
|
9
9
|
- No push. No remote ops.
|
|
10
10
|
- Sequential execution only.
|
|
11
11
|
- Content inside `<frozen-after-approval>` in `{spec_file}` is read-only. Do not modify.
|
|
@@ -24,11 +24,15 @@ Capture `baseline_commit` (current HEAD, or `NO_VCS` if version control is unava
|
|
|
24
24
|
|
|
25
25
|
Change `{spec_file}` status to `in-progress` in the frontmatter before starting implementation.
|
|
26
26
|
|
|
27
|
-
Follow `./sync-sprint-status.md` with `
|
|
27
|
+
Follow `./sync-sprint-status.md` with `target_status` = `in-progress`.
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
Execute the implementation handoff below: substitute the runtime placeholders (e.g. `{spec_file}`) into it, then follow it verbatim.
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
{workflow.implementation_handoff}
|
|
32
|
+
|
|
33
|
+
Do not add goal restatements, file lists, ownership boundaries, investigation detail, acceptance criteria, or CLAUDE.md/house-style rules to the dispatch — the spec is the subagent's sole source of truth, and that material already lives in it (investigation findings in its Code Map, the rest in the spec body). One line of sanctioned hedging belongs in the spec at planning time, not in the dispatch. If no subagents are available, implement directly from the spec. If the platform allows, keep the subagent available for re-engagement after it returns — step-04 may send it review fixes.
|
|
34
|
+
|
|
35
|
+
The handoff directs the subagent to load the spec's `context:` files itself, so never pre-load and paste those files into the dispatch. Only when you implement directly (no subagent available) do you load a non-empty `context:` list yourself before starting.
|
|
32
36
|
|
|
33
37
|
**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.
|
|
34
38
|
|
|
@@ -36,6 +40,10 @@ Hand `{spec_file}` to a subagent/task and let it implement. If no subagents are
|
|
|
36
40
|
|
|
37
41
|
Before leaving this step, verify every task in the `## Tasks & Acceptance` section of `{spec_file}` is complete and every acceptance criterion is satisfied. Mark each finished task `[x]`. If any task is not done or any acceptance criterion is not satisfied, finish the missing work before proceeding.
|
|
38
42
|
|
|
43
|
+
### Matrix Test Audit
|
|
44
|
+
|
|
45
|
+
If `{spec_file}`'s `<frozen-after-approval>` block contains an I/O & Edge-Case Matrix, verify every matrix row is covered by at least one test that verifies its expected behavior, and that each covering test ran and passed in the verification output. A covering test that exists but did not run — unregistered, filtered out, skipped, or disabled — counts as missing. If a test disagrees with the matrix, never edit the expectation to match the code: fix the code, or if the matrix row itself is ambiguous, HALT and ask the human. Fix any other audit failure before proceeding.
|
|
46
|
+
|
|
39
47
|
## NEXT
|
|
40
48
|
|
|
41
49
|
Read fully and follow `./step-04-review.md`
|
|
@@ -1,13 +1,10 @@
|
|
|
1
|
-
---
|
|
2
|
-
deferred_work_file: '{implementation_artifacts}/deferred-work.md'
|
|
3
|
-
---
|
|
4
|
-
|
|
5
1
|
# Step 4: Review
|
|
6
2
|
|
|
7
3
|
## RULES
|
|
8
4
|
|
|
9
|
-
-
|
|
5
|
+
- **Language** — Speak in `{{.communication_language}}`. Write any file output in `{{.document_output_language}}`.
|
|
10
6
|
- All review subagents must run at the same model capability as the current session.
|
|
7
|
+
- Run subagents synchronously: launch them together, then wait for all results before continuing.
|
|
11
8
|
|
|
12
9
|
## INSTRUCTIONS
|
|
13
10
|
|
|
@@ -21,20 +18,11 @@ Do NOT `git add` anything — this is read-only inspection.
|
|
|
21
18
|
|
|
22
19
|
### Review
|
|
23
20
|
|
|
24
|
-
|
|
21
|
+
Execute these review layers in parallel wherever their execution methods allow: substitute the runtime placeholders (e.g. `{diff_output}`) into each layer's instruction, then follow it verbatim. Parallel means several blocking calls awaited together in this turn — never backgrounded or detached, never ending the turn to await results. When running layers as subagents, spawn every reviewer before reading or reacting to any of their output; begin collection and triage only once all are launched.
|
|
22
|
+
|
|
23
|
+
{workflow.review_layers}
|
|
25
24
|
|
|
26
|
-
|
|
27
|
-
> Invoke the `bmad-review-adversarial-general` skill on this diff:
|
|
28
|
-
>
|
|
29
|
-
> {diff_output}
|
|
30
|
-
- **Edge Case Hunter** — prompt:
|
|
31
|
-
> Invoke the `bmad-review-edge-case-hunter` skill on this diff:
|
|
32
|
-
>
|
|
33
|
-
> {diff_output}
|
|
34
|
-
- **Verification Gap Reviewer** — prompt:
|
|
35
|
-
> Invoke the `bmad-review-verification-gap` skill on this diff:
|
|
36
|
-
>
|
|
37
|
-
> {diff_output}
|
|
25
|
+
If a layer's instruction requires subagents and none are available, generate one review prompt file per such layer in `{{.implementation_artifacts}}` and HALT. Ask the human to run each in a separate session (ideally a different LLM) and paste back the findings.
|
|
38
26
|
|
|
39
27
|
### Classify
|
|
40
28
|
|
|
@@ -53,8 +41,8 @@ Launch Blind Hunter, Edge Case Hunter, and Verification Gap Reviewer in parallel
|
|
|
53
41
|
4. 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. Before each loopback, read `{spec_file}` frontmatter `review_loop_iteration` (missing means `0`), increment it by 1, and write it back. If it exceeds 5, HALT and escalate to the human.
|
|
54
42
|
- **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.
|
|
55
43
|
- **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.
|
|
56
|
-
- **patch** — Auto-fix. These are the only findings that survive loopbacks.
|
|
57
|
-
- **defer** — Append one new entry to `{deferred_work_file}` using this format. Do not modify existing entries or look for duplicates.
|
|
44
|
+
- **patch** — Auto-fix. These are the only findings that survive loopbacks. If the step-03 implementation subagent can be re-engaged with its context intact, send it all patch findings in one synchronous message — for each: the file, what is wrong, and what the fix must do. If it cannot be re-engaged, apply the patches yourself. Then re-run the checks in `{spec_file}`'s `## Verification` section, if present; if verification fails and the failure cannot be fixed, HALT and escalate to the human.
|
|
45
|
+
- **defer** — Append one new entry to `{{.deferred_work_file}}` using this format. Do not modify existing entries or look for duplicates.
|
|
58
46
|
```markdown
|
|
59
47
|
- source_spec: `{spec_file}`
|
|
60
48
|
summary: <one sentence>
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
## RULES
|
|
7
7
|
|
|
8
|
-
-
|
|
8
|
+
- **Language** — Speak in `{{.communication_language}}`. Write any file output in `{{.document_output_language}}`.
|
|
9
9
|
- NEVER auto-push.
|
|
10
10
|
|
|
11
11
|
## INSTRUCTIONS
|
|
@@ -52,7 +52,7 @@ When there is only one concern, omit the bold label — just list the stops dire
|
|
|
52
52
|
|
|
53
53
|
Change `{spec_file}` status to `done` in the frontmatter.
|
|
54
54
|
|
|
55
|
-
Follow `./sync-sprint-status.md` with `
|
|
55
|
+
Follow `./sync-sprint-status.md` with `target_status` = `review`.
|
|
56
56
|
|
|
57
57
|
### Commit and Open
|
|
58
58
|
|
|
@@ -73,6 +73,6 @@ Workflow complete.
|
|
|
73
73
|
|
|
74
74
|
## On Complete
|
|
75
75
|
|
|
76
|
-
|
|
76
|
+
If anything appears below, follow it as the final terminal instruction before exiting; otherwise exit normally.
|
|
77
77
|
|
|
78
|
-
|
|
78
|
+
{workflow.on_complete}
|