bmad-method 6.10.1-next.10 → 6.10.1-next.12

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "bmad-method",
4
- "version": "6.10.1-next.10",
4
+ "version": "6.10.1-next.12",
5
5
  "description": "Breakthrough Method of Agile AI-driven Development",
6
6
  "keywords": [
7
7
  "agile",
@@ -40,12 +40,13 @@
40
40
  "lint:fix": "eslint . --ext .js,.cjs,.mjs,.yaml --fix",
41
41
  "lint:md": "markdownlint-cli2 \"**/*.md\"",
42
42
  "prepare": "command -v husky >/dev/null 2>&1 && husky || exit 0",
43
- "quality": "npm run format:check && npm run lint && npm run lint:md && npm run docs:build && npm run test:install && npm run test:urls && npm run validate:refs && npm run validate:skills && npm run docs:validate-sidebar",
43
+ "quality": "npm run format:check && npm run lint && npm run lint:md && npm run docs:build && npm run test:install && npm run test:urls && npm run test:renderer && npm run validate:refs && npm run validate:skills && npm run docs:validate-sidebar",
44
44
  "rebundle": "node tools/installer/bundlers/bundle-web.js rebundle",
45
- "test": "npm run test:refs && npm run test:install && npm run test:urls && npm run test:channels && npm run test:skills && npm run lint && npm run lint:md && npm run format:check",
45
+ "test": "npm run test:refs && npm run test:install && npm run test:urls && npm run test:channels && npm run test:renderer && npm run test:skills && npm run lint && npm run lint:md && npm run format:check",
46
46
  "test:channels": "node test/test-installer-channels.js",
47
47
  "test:install": "node test/test-installation-components.js",
48
48
  "test:refs": "node test/test-file-refs-csv.js",
49
+ "test:renderer": "node test/test-quick-dev-renderer.js",
49
50
  "test:skills": "node test/test-validate-skills.js",
50
51
  "test:urls": "node test/test-parse-source-urls.js",
51
52
  "validate:refs": "node tools/validate-file-refs.js --strict",
@@ -3,113 +3,11 @@ name: bmad-quick-dev
3
3
  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.'
4
4
  ---
5
5
 
6
- # Quick Dev New Preview Workflow
6
+ Run this, substituting `{skill-root}` with the absolute path to this skill's base directory, without changing the cwd:
7
7
 
8
- **Goal:** Turn user intent into a hardened, reviewable artifact.
8
+ ```bash
9
+ uv run {skill-root}/render.py
10
+ ```
9
11
 
10
- **CRITICAL:** If a step says "read fully and follow step-XX", you read and follow step-XX. No exceptions.
11
-
12
- Subagents, when the capability is available, are an important part of this workflow. Use them as directed by the workflow steps.
13
- If you need an explicit user instruction to run them, ask once now for the whole workflow run.
14
-
15
- ## READY FOR DEVELOPMENT STANDARD
16
-
17
- A specification is "Ready for Development" when:
18
-
19
- - **Actionable**: Every task has a file path and specific action.
20
- - **Logical**: Tasks ordered by dependency.
21
- - **Testable**: All ACs use Given/When/Then.
22
- - **Complete**: No placeholders or TBDs.
23
- - **Sufficient**: No known requirement, acceptance, dependency, or implementation gaps remain unresolved.
24
- - **Coherent**: No unresolved ambiguities or internal contradictions.
25
-
26
- ## SCOPE STANDARD
27
-
28
- A specification should target a **single user-facing goal** within **900–1600 tokens**:
29
-
30
- - **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.
31
- - Split: "add dark mode toggle AND refactor auth to JWT AND build admin dashboard"
32
- - Don't split: "add validation and display errors" / "support drag-and-drop AND paste AND retry"
33
- - **900–1600 tokens**: Optimal range for LLM consumption. Below 900 risks ambiguity; above 1600 risks context-rot in implementation agents.
34
- - **Neither limit is a gate.** Both are proposals with user override.
35
-
36
- ## Conventions
37
-
38
- - Bare paths (e.g. `step-01-clarify-and-route.md`) resolve from the skill root.
39
- - `{skill-root}` resolves to this skill's installed directory (where `customize.toml` lives).
40
- - `{project-root}`-prefixed paths resolve from the project working directory.
41
- - `{skill-name}` resolves to the skill directory's basename.
42
-
43
- ## On Activation
44
-
45
- ### Step 1: Resolve the Workflow Block
46
-
47
- Run: `python3 {project-root}/_bmad/scripts/resolve_customization.py --skill {skill-root} --key workflow`
48
-
49
- **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:
50
-
51
- 1. `{skill-root}/customize.toml` — defaults
52
- 2. `{project-root}/_bmad/custom/{skill-name}.toml` — team overrides
53
- 3. `{project-root}/_bmad/custom/{skill-name}.user.toml` — personal overrides
54
-
55
- 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.
56
-
57
- ### Step 2: Execute Prepend Steps
58
-
59
- Execute each entry in `{workflow.activation_steps_prepend}` in order before proceeding.
60
-
61
- ### Step 3: Load Persistent Facts
62
-
63
- 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.
64
-
65
- ### Step 4: Load Config
66
-
67
- Load config from `{project-root}/_bmad/bmm/config.yaml` and resolve:
68
-
69
- - `project_name`, `planning_artifacts`, `implementation_artifacts`, `user_name`
70
- - `communication_language`, `document_output_language`, `user_skill_level`
71
- - `date` as system-generated current datetime
72
- - `sprint_status` = `{implementation_artifacts}/sprint-status.yaml`
73
- - `project_context` = `**/project-context.md` (load if exists)
74
- - YOU MUST ALWAYS SPEAK OUTPUT in your Agent communication style with the config `{communication_language}`
75
- - Language MUST be tailored to `{user_skill_level}`
76
- - Generate all documents in `{document_output_language}`
77
-
78
- ### Step 5: Greet the User
79
-
80
- Greet `{user_name}`, speaking in `{communication_language}`.
81
-
82
- ### Step 6: Execute Append Steps
83
-
84
- Execute each entry in `{workflow.activation_steps_append}` in order.
85
-
86
- Activation is complete. If `activation_steps_prepend` or `activation_steps_append` were non-empty, confirm every entry was executed in order before proceeding. Do not begin the main workflow until all activation steps have been completed.
87
-
88
- ## WORKFLOW ARCHITECTURE
89
-
90
- This uses **step-file architecture** for disciplined execution:
91
-
92
- - **Micro-file Design**: Each step is self-contained and followed exactly
93
- - **Just-In-Time Loading**: Only load the current step file
94
- - **Sequential Enforcement**: Complete steps in order, no skipping
95
- - **State Tracking**: Persist progress via spec frontmatter and in-memory variables
96
- - **Append-Only Building**: Build artifacts incrementally
97
-
98
- ### Step Processing Rules
99
-
100
- 1. **READ COMPLETELY**: Read the entire step file before acting
101
- 2. **FOLLOW SEQUENCE**: Execute sections in order
102
- 3. **WAIT FOR INPUT**: Halt at checkpoints and wait for human
103
- 4. **LOAD NEXT**: When directed, read fully and follow the next step file
104
-
105
- ### Critical Rules (NO EXCEPTIONS)
106
-
107
- - **NEVER** load multiple step files simultaneously
108
- - **ALWAYS** read entire step file before execution
109
- - **NEVER** skip steps or optimize the sequence
110
- - **ALWAYS** follow the exact instructions in the step file
111
- - **ALWAYS** halt at checkpoints and wait for human input
112
-
113
- ## FIRST STEP
114
-
115
- Read fully and follow: `./step-01-clarify-and-route.md` to begin the workflow.
12
+ - **On success:** follow the instruction it prints to stdout; ignore stderr.
13
+ - **On any failure** (including `uv` not being installed): report what it printed and HALT.
@@ -0,0 +1,347 @@
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.
16
+
17
+ Customization: three-layer merge of {skill}/customize.toml +
18
+ _bmad/custom/bmad-quick-dev.toml + .user.toml (same structural rules as
19
+ resolve_customization.py). The resolved [workflow] values fill {workflow.*}
20
+ placeholders, so this skill needs no runtime resolve_customization.py call.
21
+ Other single-curly placeholders ({project-root}, {spec_file}, {skill-root},
22
+ ...) pass through untouched for the LLM to resolve during workflow execution.
23
+
24
+ Every invocation rebuilds from scratch — no hash, no cache.
25
+ Python 3.11+ stdlib only. UTF-8 I/O.
26
+ """
27
+
28
+ import os
29
+ import posixpath
30
+ import re
31
+ import sys
32
+ import tomllib
33
+
34
+
35
+ def find_project_root():
36
+ """Walk up from cwd until a _bmad/ directory is found. On failure, print a
37
+ HALT instruction to stdout and exit non-zero."""
38
+ current = os.path.abspath(os.getcwd())
39
+ while True:
40
+ candidate = os.path.join(current, "_bmad")
41
+ if os.path.isdir(candidate):
42
+ return current
43
+ parent = os.path.dirname(current)
44
+ if parent == current:
45
+ print(
46
+ f"HALT and report to the user: no _bmad/ directory found walking up from {os.getcwd()}"
47
+ )
48
+ sys.exit(1)
49
+ current = parent
50
+
51
+
52
+ def load_toml(path, required=False):
53
+ """Load a TOML file. For required files, HALT (stdout) on missing/parse
54
+ error so the LLM-driven workflow stops — stdout is how this script signals
55
+ workflow halts to its LLM caller. For optional files, write a stderr
56
+ warning and return {}."""
57
+ if not os.path.isfile(path):
58
+ if required:
59
+ print(
60
+ f"HALT and report to the user: required config file not found: {path} — "
61
+ "ensure this is a post-#2285 BMAD install"
62
+ )
63
+ sys.exit(1)
64
+ return {}
65
+ try:
66
+ with open(path, "rb") as fh:
67
+ parsed = tomllib.load(fh)
68
+ except tomllib.TOMLDecodeError as error:
69
+ if required:
70
+ print(f"HALT and report to the user: failed to parse {path}: {error}")
71
+ sys.exit(1)
72
+ print(f"render.py: warning: failed to parse {path}: {error}", file=sys.stderr)
73
+ return {}
74
+ except OSError as error:
75
+ if required:
76
+ print(f"HALT and report to the user: failed to read {path}: {error}")
77
+ sys.exit(1)
78
+ print(f"render.py: warning: failed to read {path}: {error}", file=sys.stderr)
79
+ return {}
80
+ if not isinstance(parsed, dict):
81
+ return {}
82
+ return parsed
83
+
84
+
85
+ def _deep_merge(base, override):
86
+ """Dict-aware deep merge. Lists and scalars: override wins (we don't need
87
+ the full keyed-merge semantics of resolve_config.py — quick-dev only reads
88
+ flat scalars out of [core] and [modules.bmm])."""
89
+ if isinstance(base, dict) and isinstance(override, dict):
90
+ result = dict(base)
91
+ for key, value in override.items():
92
+ result[key] = _deep_merge(result[key], value) if key in result else value
93
+ return result
94
+ return override
95
+
96
+
97
+ def _detect_keyed_merge_field(items):
98
+ """Return 'code' or 'id' if every table item carries that same field.
99
+ Mixed or partial arrays return None and fall through to append."""
100
+ if not items or not all(isinstance(item, dict) for item in items):
101
+ return None
102
+ for candidate in ("code", "id"):
103
+ if all(item.get(candidate) is not None for item in items):
104
+ return candidate
105
+ return None
106
+
107
+
108
+ def _merge_by_key(base, override, key_name):
109
+ result = []
110
+ index_by_key = {}
111
+ for item in base:
112
+ if not isinstance(item, dict):
113
+ continue
114
+ if item.get(key_name) is not None:
115
+ index_by_key[item[key_name]] = len(result)
116
+ result.append(dict(item))
117
+ for item in override:
118
+ if not isinstance(item, dict):
119
+ result.append(item)
120
+ continue
121
+ key = item.get(key_name)
122
+ if key is not None and key in index_by_key:
123
+ result[index_by_key[key]] = dict(item)
124
+ else:
125
+ if key is not None:
126
+ index_by_key[key] = len(result)
127
+ result.append(dict(item))
128
+ return result
129
+
130
+
131
+ def _merge_arrays(base, override):
132
+ """Shape-aware array merge: keyed merge if every item has code/id, else append."""
133
+ base_arr = base if isinstance(base, list) else []
134
+ override_arr = override if isinstance(override, list) else []
135
+ keyed_field = _detect_keyed_merge_field(base_arr + override_arr)
136
+ if keyed_field:
137
+ return _merge_by_key(base_arr, override_arr, keyed_field)
138
+ return base_arr + override_arr
139
+
140
+
141
+ def _structural_merge(base, override):
142
+ """Faithful port of resolve_customization.py's deep_merge: tables deep-merge,
143
+ arrays-of-tables keyed by code/id replace-then-append (other arrays append),
144
+ scalars override. Used only for the [workflow] customization layers — the
145
+ central-config path keeps its own simpler _deep_merge. Duplicated rather than
146
+ imported to keep this skill self-contained."""
147
+ if isinstance(base, dict) and isinstance(override, dict):
148
+ result = dict(base)
149
+ for key, over_val in override.items():
150
+ result[key] = (
151
+ _structural_merge(result[key], over_val) if key in result else over_val
152
+ )
153
+ return result
154
+ if isinstance(base, list) and isinstance(override, list):
155
+ return _merge_arrays(base, override)
156
+ return override
157
+
158
+
159
+ def resolve_workflow(root, skill_dir, skill_name):
160
+ """Resolve the [workflow] customization block via the three-layer merge
161
+ (skill defaults -> team -> user), highest priority last. Same structural
162
+ rules as resolve_customization.py. All three layers are optional: a missing
163
+ or unparseable file warns (via load_toml) and is skipped."""
164
+ defaults = load_toml(posixpath.join(skill_dir, "customize.toml"))
165
+ custom_dir = posixpath.join(root, "_bmad", "custom")
166
+ team = load_toml(posixpath.join(custom_dir, f"{skill_name}.toml"))
167
+ user = load_toml(posixpath.join(custom_dir, f"{skill_name}.user.toml"))
168
+ merged = _structural_merge(defaults, team)
169
+ merged = _structural_merge(merged, user)
170
+ workflow = merged.get("workflow")
171
+ return workflow if isinstance(workflow, dict) else {}
172
+
173
+
174
+ def load_central_config(root):
175
+ """Four-layer merge of _bmad/config.toml and its peers (highest priority
176
+ last). HALTs if the base _bmad/config.toml is missing or unparseable."""
177
+ bmad_dir = posixpath.join(root, "_bmad")
178
+ base_team = load_toml(posixpath.join(bmad_dir, "config.toml"), required=True)
179
+ base_user = load_toml(posixpath.join(bmad_dir, "config.user.toml"))
180
+ custom_team = load_toml(posixpath.join(bmad_dir, "custom", "config.toml"))
181
+ custom_user = load_toml(posixpath.join(bmad_dir, "custom", "config.user.toml"))
182
+
183
+ merged = _deep_merge(base_team, base_user)
184
+ merged = _deep_merge(merged, custom_team)
185
+ merged = _deep_merge(merged, custom_user)
186
+ return merged
187
+
188
+
189
+ def flatten_central_config(merged):
190
+ """Lift scalar keys from [core] and [modules.bmm] into a single namespace.
191
+ Module keys take precedence on collision (installer strips core keys from
192
+ module buckets, so collisions shouldn't happen in practice)."""
193
+ flat = {}
194
+ modules = merged.get("modules")
195
+ modules = modules if isinstance(modules, dict) else {}
196
+ for section in (merged.get("core"), modules.get("bmm")):
197
+ if not isinstance(section, dict):
198
+ continue
199
+ for key, value in section.items():
200
+ if isinstance(value, bool):
201
+ flat[key] = "true" if value else "false"
202
+ elif isinstance(value, (str, int, float)):
203
+ flat[key] = str(value)
204
+ return flat
205
+
206
+
207
+ def render_template(content, vars_):
208
+ """Resolve {{.var}} substitutions. Unresolved references emit an empty string
209
+ (Go's missingkey=zero semantics)."""
210
+ return re.sub(r"\{\{\.(\w+)\}\}", lambda m: vars_.get(m.group(1), ""), content)
211
+
212
+
213
+ def _scalar_str(value):
214
+ """Stringify a scalar for inline rendering: booleans lowercase (matching
215
+ BMad config conventions), None as empty, everything else via str()."""
216
+ if value is None:
217
+ return ""
218
+ if isinstance(value, bool):
219
+ return "true" if value else "false"
220
+ return str(value)
221
+
222
+
223
+ # [workflow] keys holding review layers ([[workflow.review_layers]] tables with
224
+ # id/name/instruction/when fields). This renderer knows this skill's
225
+ # customization schema outright — layer semantics are materialized here, not
226
+ # interpreted by the LLM at run time.
227
+ _REVIEW_LAYER_KEYS = ("review_layers", "oneshot_review_layers")
228
+
229
+
230
+ def _render_review_layers(layers):
231
+ """Materialize review layers into direct invocation blocks. A layer with an
232
+ empty or missing instruction is disabled (that is how an override turns off
233
+ a default layer) and drops out entirely. A `when` condition is the one part
234
+ that stays with the LLM: it renders as a run-time guard line. No active
235
+ layers renders as the HALT instruction the workflow would otherwise have to
236
+ derive from an empty list."""
237
+ active = [
238
+ layer
239
+ for layer in layers
240
+ if isinstance(layer, dict) and _scalar_str(layer.get("instruction")).strip()
241
+ ]
242
+ if not active:
243
+ return (
244
+ "No review layers are active. HALT with status `blocked` and "
245
+ "blocking condition `no active review layers`."
246
+ )
247
+ blocks = []
248
+ for layer in active:
249
+ title = (
250
+ _scalar_str(layer.get("name")).strip()
251
+ or _scalar_str(layer.get("id")).strip()
252
+ or "Review layer"
253
+ )
254
+ lines = [f"#### {title}", ""]
255
+ when = _scalar_str(layer.get("when")).strip()
256
+ if when:
257
+ lines.append(
258
+ "Run this layer only if the following holds in the "
259
+ f"current context: `{when}`"
260
+ )
261
+ lines.append("")
262
+ lines.append(_scalar_str(layer.get("instruction")).strip("\n"))
263
+ blocks.append("\n".join(lines))
264
+ return "\n\n".join(blocks)
265
+
266
+
267
+ def _render_workflow_value(key, value):
268
+ """Format a resolved [workflow] value for inline substitution. Review-layer
269
+ keys materialize as invocation blocks; other lists render as markdown
270
+ bullets (empty -> '_None._'); scalars render verbatim. Each list item uses
271
+ the same scalar formatting so booleans stay consistent. Entries are emitted
272
+ as-is so runtime placeholders like {project-root} or {diff_output} survive
273
+ for the LLM to resolve."""
274
+ if key in _REVIEW_LAYER_KEYS and isinstance(value, list):
275
+ return _render_review_layers(value)
276
+ if isinstance(value, list):
277
+ if not value:
278
+ return "_None._"
279
+ return "\n".join(f"- {_scalar_str(item)}" for item in value)
280
+ return _scalar_str(value)
281
+
282
+
283
+ def render_workflow(content, workflow):
284
+ """Resolve {workflow.<key>} placeholders from the resolved [workflow] block.
285
+ Unknown keys emit an empty string (missingkey=zero, matching render_template).
286
+ Distinct regex from render_template so single-curly runtime placeholders
287
+ elsewhere are untouched."""
288
+ return re.sub(
289
+ r"\{workflow\.(\w+)\}",
290
+ lambda m: _render_workflow_value(m.group(1), workflow.get(m.group(1))),
291
+ content,
292
+ )
293
+
294
+
295
+ def main():
296
+ script_dir = os.path.dirname(os.path.abspath(__file__))
297
+ skill_name = os.path.basename(script_dir)
298
+ root = find_project_root()
299
+ root = root.replace(os.sep, "/")
300
+
301
+ vars_ = flatten_central_config(load_central_config(root))
302
+
303
+ for key in list(vars_.keys()):
304
+ vars_[key] = vars_[key].replace("{project-root}", root)
305
+
306
+ vars_["project_root"] = root
307
+
308
+ implementation_artifacts = vars_.get("implementation_artifacts", "").strip()
309
+ if not implementation_artifacts:
310
+ print(
311
+ "HALT and report to the user: config is missing `implementation_artifacts` "
312
+ "(expected under [core] or [modules.bmm] in _bmad/config.toml)"
313
+ )
314
+ sys.exit(1)
315
+
316
+ vars_["sprint_status"] = posixpath.join(
317
+ implementation_artifacts, "sprint-status.yaml"
318
+ )
319
+ vars_["deferred_work_file"] = posixpath.join(
320
+ implementation_artifacts, "deferred-work.md"
321
+ )
322
+
323
+ workflow = resolve_workflow(root, script_dir.replace(os.sep, "/"), skill_name)
324
+
325
+ out_dir = posixpath.join(root, "_bmad", "render", skill_name)
326
+ os.makedirs(out_dir, exist_ok=True)
327
+
328
+ for fname in os.listdir(out_dir):
329
+ if fname.endswith(".md"):
330
+ os.remove(posixpath.join(out_dir, fname))
331
+
332
+ for fname in sorted(os.listdir(script_dir)):
333
+ if not fname.endswith(".md") or fname == "SKILL.md":
334
+ continue
335
+ src = posixpath.join(script_dir, fname)
336
+ dst = posixpath.join(out_dir, fname)
337
+ with open(src, "r", encoding="utf-8", newline="") as fh:
338
+ content = fh.read()
339
+ with open(dst, "w", encoding="utf-8", newline="") as fh:
340
+ fh.write(render_workflow(render_template(content, vars_), workflow))
341
+
342
+ workflow_md = posixpath.join(out_dir, "workflow.md")
343
+ print(f"read and follow {workflow_md}")
344
+
345
+
346
+ if __name__ == "__main__":
347
+ main()
@@ -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
- - YOU MUST ALWAYS SPEAK OUTPUT in your Agent communication style with the config `{communication_language}`
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 `{epic_num}` and `{story_num}`. If the spec is not an epic story, skip silently and leave `{story_key}` unset.
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 `{story_key}` to that full key. Zero or multiple matches → leave `{story_key}` unset (warn on multiple).
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 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
 
@@ -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
- - YOU MUST ALWAYS SPEAK OUTPUT in your Agent communication style with the config `{communication_language}`
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
11
  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 `{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}`.
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. 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>
@@ -5,7 +5,7 @@
5
5
 
6
6
  ## RULES
7
7
 
8
- - YOU MUST ALWAYS SPEAK OUTPUT in your Agent communication style with the config `{communication_language}`
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,7 +24,7 @@ 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 `{target_status}` = `in-progress`.
27
+ Follow `./sync-sprint-status.md` with `target_status` = `in-progress`.
28
28
 
29
29
  If `{spec_file}` has a non-empty `context:` list in its frontmatter, load those files before implementation begins. When handing to a subagent, include them in the subagent prompt so it has access to the referenced context.
30
30
 
@@ -1,12 +1,8 @@
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
- - YOU MUST ALWAYS SPEAK OUTPUT in your Agent communication style with the config `{communication_language}`
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.
11
7
  - Run subagents synchronously: launch them together, then wait for all results before continuing.
12
8
 
@@ -22,13 +18,11 @@ Do NOT `git add` anything — this is read-only inspection.
22
18
 
23
19
  ### Review
24
20
 
25
- The review layers are `{workflow.review_layers}`, resolved during activation.
26
-
27
- Skip every layer whose `instruction` is empty or missing — that is how an override disables a default layer — and every layer whose `when` condition (if present) does not hold in the current context. If no layers remain, HALT with status `blocked` and blocking condition `no active review layers`.
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.
28
22
 
29
- Execute all remaining 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.
23
+ {workflow.review_layers}
30
24
 
31
- 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.
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.
32
26
 
33
27
  ### Classify
34
28
 
@@ -48,7 +42,7 @@ If a layer's instruction requires subagents and none are available, generate one
48
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.
49
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.
50
44
  - **patch** — Auto-fix. These are the only findings that survive loopbacks.
51
- - **defer** — Append one new entry to `{deferred_work_file}` using this format. Do not modify existing entries or look for duplicates.
45
+ - **defer** — Append one new entry to `{{.deferred_work_file}}` using this format. Do not modify existing entries or look for duplicates.
52
46
  ```markdown
53
47
  - source_spec: `{spec_file}`
54
48
  summary: <one sentence>
@@ -5,7 +5,7 @@
5
5
 
6
6
  ## RULES
7
7
 
8
- - YOU MUST ALWAYS SPEAK OUTPUT in your Agent communication style with the config `{communication_language}`
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 `{target_status}` = `review`.
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
- Run: `python3 {project-root}/_bmad/scripts/resolve_customization.py --skill {skill-root} --key workflow.on_complete`
76
+ If anything appears below, follow it as the final terminal instruction before exiting; otherwise exit normally.
77
77
 
78
- If the resolved `workflow.on_complete` is non-empty, follow it as the final terminal instruction before exiting.
78
+ {workflow.on_complete}
@@ -1,12 +1,8 @@
1
- ---
2
- deferred_work_file: '{implementation_artifacts}/deferred-work.md'
3
- ---
4
-
5
1
  # Step One-Shot: Implement, Review, Present
6
2
 
7
3
  ## RULES
8
4
 
9
- - YOU MUST ALWAYS SPEAK OUTPUT in your Agent communication style with the config `{communication_language}`
5
+ - **Language** Speak in `{{.communication_language}}`. Write any file output in `{{.document_output_language}}`.
10
6
  - NEVER auto-push.
11
7
  - All review subagents must run at the same model capability as the current session.
12
8
  - Run subagents synchronously: launch them together, then wait for all results before continuing.
@@ -15,24 +11,24 @@ deferred_work_file: '{implementation_artifacts}/deferred-work.md'
15
11
 
16
12
  ### Implement
17
13
 
18
- Follow `./sync-sprint-status.md` with `{target_status}` = `in-progress`.
14
+ Follow `./sync-sprint-status.md` with `target_status` = `in-progress`.
19
15
 
20
16
  Implement the clarified intent directly.
21
17
 
22
18
  ### Review
23
19
 
24
- The review layers for this route are `{workflow.oneshot_review_layers}`, resolved during activation.
20
+ Execute these review layers in parallel wherever their execution methods allow, following each layer's instruction verbatim after substituting any runtime placeholders:
25
21
 
26
- Skip every layer whose `instruction` is empty or missing, and every layer whose `when` condition (if present) does not hold. If no layers remain, HALT with status `blocked` and blocking condition `no active review layers`. Execute all remaining layers in parallel wherever their execution methods allow, following each layer's `instruction` verbatim after substituting any runtime placeholders.
22
+ {workflow.oneshot_review_layers}
27
23
 
28
- 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 and paste back the findings.
24
+ 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 and paste back the findings.
29
25
 
30
26
  ### Classify
31
27
 
32
28
  Deduplicate all review findings. Three categories only:
33
29
 
34
30
  - **patch** — trivially fixable. Auto-fix immediately.
35
- - **defer** — pre-existing issue not caused by this change. Append one new entry to `{deferred_work_file}` using this format. Do not modify existing entries or look for duplicates.
31
+ - **defer** — pre-existing issue not caused by this change. Append one new entry to `{{.deferred_work_file}}` using this format. Do not modify existing entries or look for duplicates.
36
32
  ```markdown
37
33
  - source_spec: `{spec_file}`
38
34
  summary: <one sentence>
@@ -44,7 +40,7 @@ If a finding is caused by this change but too significant for a trivial patch, H
44
40
 
45
41
  ### Generate Spec Trace
46
42
 
47
- Set `{title}` = a concise title derived from the clarified intent.
43
+ Set `title` = a concise title derived from the clarified intent.
48
44
 
49
45
  Write `{spec_file}` using `./spec-template.md`. Fill only these sections — delete all others:
50
46
 
@@ -52,7 +48,7 @@ Write `{spec_file}` using `./spec-template.md`. Fill only these sections — del
52
48
  2. **Title and Intent** — `# {title}` heading and `## Intent` with **Problem** and **Approach** lines. Reuse the summary you already generated for the terminal.
53
49
  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).
54
50
 
55
- Follow `./sync-sprint-status.md` with `{target_status}` = `review`.
51
+ Follow `./sync-sprint-status.md` with `target_status` = `review`.
56
52
 
57
53
  ### Commit
58
54
 
@@ -77,6 +73,6 @@ Workflow complete.
77
73
 
78
74
  ## On Complete
79
75
 
80
- Run: `python3 {project-root}/_bmad/scripts/resolve_customization.py --skill {skill-root} --key workflow.on_complete`
76
+ If anything appears below, follow it as the final terminal instruction before exiting; otherwise exit normally.
81
77
 
82
- If the resolved `workflow.on_complete` is non-empty, follow it as the final terminal instruction before exiting.
78
+ {workflow.on_complete}
@@ -1,19 +1,19 @@
1
1
  # Sync Sprint Status
2
2
 
3
- 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.
3
+ 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.
4
4
 
5
5
  ## Preconditions
6
6
 
7
7
  Skip this entire file (return to caller) if ANY of:
8
- - `{story_key}` is unset
9
- - `{sprint_status}` does not exist on disk
8
+ - `story_key` is unset
9
+ - `{{.sprint_status}}` does not exist on disk
10
10
 
11
11
  ## Instructions
12
12
 
13
- 1. Load the FULL `{sprint_status}` file.
13
+ 1. Load the FULL `{{.sprint_status}}` file.
14
14
  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.
15
- 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.
15
+ 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.
16
16
  4. Set `development_status[{story_key}]` to `{target_status}`.
17
- 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`.
17
+ 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`.
18
18
  6. Refresh `last_updated` to the current date.
19
19
  7. Save the file, preserving ALL comments and structure including STATUS DEFINITIONS and WORKFLOW NOTES.
@@ -0,0 +1,85 @@
1
+ # Quick Dev New Preview Workflow
2
+
3
+ **Goal:** Turn user intent into a hardened, reviewable artifact.
4
+
5
+ **CRITICAL:** If a step says "read fully and follow step-XX", you read and follow step-XX. No exceptions.
6
+
7
+ Subagents, when the capability is available, are an important part of this workflow. Use them as directed by the workflow steps.
8
+ If you need an explicit user instruction to run them, ask once now for the whole workflow run.
9
+
10
+ ## READY FOR DEVELOPMENT STANDARD
11
+
12
+ A specification is "Ready for Development" when:
13
+
14
+ - **Actionable**: Every task has a file path and specific action.
15
+ - **Logical**: Tasks ordered by dependency.
16
+ - **Testable**: All ACs use Given/When/Then.
17
+ - **Complete**: No placeholders or TBDs.
18
+ - **Sufficient**: No known requirement, acceptance, dependency, or implementation gaps remain unresolved.
19
+ - **Coherent**: No unresolved ambiguities or internal contradictions.
20
+
21
+ ## SCOPE STANDARD
22
+
23
+ A specification should target a **single user-facing goal** within **900–1600 tokens**:
24
+
25
+ - **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.
26
+ - Split: "add dark mode toggle AND refactor auth to JWT AND build admin dashboard"
27
+ - Don't split: "add validation and display errors" / "support drag-and-drop AND paste AND retry"
28
+ - **900–1600 tokens**: Optimal range for LLM consumption. Below 900 risks ambiguity; above 1600 risks context-rot in implementation agents.
29
+ - **Neither limit is a gate.** Both are proposals with user override.
30
+
31
+ ## Conventions
32
+
33
+ - Bare paths (e.g. `step-01-clarify-and-route.md`) resolve from the skill root.
34
+ - `{skill-root}` resolves to this skill's installed directory (where `customize.toml` lives).
35
+ - `{project-root}`-prefixed paths resolve from the project working directory.
36
+ - `{skill-name}` resolves to the skill directory's basename.
37
+
38
+ ## On Activation
39
+
40
+ ### Step 1: Execute Prepend Steps
41
+
42
+ Execute each of these steps in order before proceeding (`_None._` means skip):
43
+
44
+ {workflow.activation_steps_prepend}
45
+
46
+ ### Step 2: Load Persistent Facts
47
+
48
+ Treat every entry below 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 (`_None._` means none):
49
+
50
+ {workflow.persistent_facts}
51
+
52
+ ### Step 3: Execute Append Steps
53
+
54
+ Execute each of these steps in order (`_None._` means skip):
55
+
56
+ {workflow.activation_steps_append}
57
+
58
+ ## WORKFLOW ARCHITECTURE
59
+
60
+ This uses **step-file architecture** for disciplined execution:
61
+
62
+ - **Micro-file Design**: Each step is self-contained and followed exactly
63
+ - **Just-In-Time Loading**: Only load the current step file
64
+ - **Sequential Enforcement**: Complete steps in order, no skipping
65
+ - **State Tracking**: Persist progress via spec frontmatter and in-memory variables
66
+ - **Append-Only Building**: Build artifacts incrementally
67
+
68
+ ### Step Processing Rules
69
+
70
+ 1. **READ COMPLETELY**: Read the entire step file before acting
71
+ 2. **FOLLOW SEQUENCE**: Execute sections in order
72
+ 3. **WAIT FOR INPUT**: Halt at checkpoints and wait for human
73
+ 4. **LOAD NEXT**: When directed, read fully and follow the next step file
74
+
75
+ ### Critical Rules (NO EXCEPTIONS)
76
+
77
+ - **NEVER** load multiple step files simultaneously
78
+ - **ALWAYS** read entire step file before execution
79
+ - **NEVER** skip steps or optimize the sequence
80
+ - **ALWAYS** follow the exact instructions in the step file
81
+ - **ALWAYS** halt at checkpoints and wait for human input
82
+
83
+ ## FIRST STEP
84
+
85
+ Read fully and follow: `./step-01-clarify-and-route.md` to begin the workflow.
@@ -35,6 +35,13 @@ platforms:
35
35
  target_dir: .agent/skills
36
36
  global_target_dir: ~/.gemini/antigravity/skills
37
37
 
38
+ antigravity-cli:
39
+ name: "Antigravity CLI (AGY)"
40
+ preferred: false
41
+ installer:
42
+ target_dir: .agents/skills
43
+ global_target_dir: ~/.gemini/antigravity-cli/skills
44
+
38
45
  auggie:
39
46
  name: "Auggie"
40
47
  preferred: false
@@ -10,7 +10,7 @@ Before running inference-based validation, run the deterministic validator:
10
10
  node tools/validate-skills.js --json path/to/skill-dir
11
11
  ```
12
12
 
13
- This checks 12 rules deterministically: SKILL-01, SKILL-02, SKILL-03, SKILL-04, SKILL-05, SKILL-06, SKILL-07, PATH-02, STEP-01, STEP-06, STEP-07, SEQ-02.
13
+ This checks 13 rules deterministically: SKILL-01, SKILL-02, SKILL-03, SKILL-04, SKILL-05, SKILL-06, SKILL-07, PATH-02, STEP-01, STEP-06, STEP-07, SEQ-02, TPL-01.
14
14
 
15
15
  Review its JSON output. For any rule that produced **zero findings** in the first pass, **skip it** during inference-based validation below — it has already been verified. If a rule produced any findings, the inference validator should still review that rule (some rules like SKILL-04 and SKILL-06 have sub-checks that benefit from judgment). Focus your inference effort on the remaining rules that require judgment (PATH-01, PATH-03, PATH-04, PATH-05, WF-03, STEP-02, STEP-03, STEP-04, STEP-05, SEQ-01, REF-01, REF-02, REF-03).
16
16
 
@@ -253,6 +253,16 @@ If no findings are generated (from either pass), the skill passes validation.
253
253
 
254
254
  ---
255
255
 
256
+ ### TPL-01 — Template Files Must Not Contain Compile-Time Substitutions
257
+
258
+ - **Severity:** HIGH
259
+ - **Applies to:** `.md` files whose name contains `template` (case-insensitive)
260
+ - **Rule:** Template files seed durable, version-controlled artifacts (e.g. spec files) that execute on other machines. A `{{.var}}` compile-time substitution would be baked at render time and freeze a machine-local value into every artifact produced from the template.
261
+ - **Detection:** Regex `\{\{\.\w+\}\}` match anywhere in a file whose basename matches `/template/i`.
262
+ - **Fix:** Remove the `{{.var}}` reference. Use single-curly `{var}` if the value should be resolved at LLM runtime by the consumer of the generated artifact.
263
+
264
+ ---
265
+
256
266
  ### REF-01 — Variable References Must Be Defined
257
267
 
258
268
  - **Severity:** HIGH
@@ -17,6 +17,7 @@
17
17
  * - STEP-06: step frontmatter has no name/description
18
18
  * - STEP-07: step count 2-10
19
19
  * - SEQ-02: no time estimates
20
+ * - TPL-01: template files must not contain compile-time {{.var}} substitutions
20
21
  *
21
22
  * Usage:
22
23
  * node tools/validate-skills.js # All skills, human-readable
@@ -43,6 +44,8 @@ const positionalArgs = args.filter((a) => !a.startsWith('--'));
43
44
  const NAME_REGEX = /^bmad-[a-z0-9]+(-[a-z0-9]+)*$/;
44
45
  const STEP_FILENAME_REGEX = /^step-\d{2}[a-z]?-[a-z0-9-]+\.md$/;
45
46
  const TIME_ESTIMATE_PATTERNS = [/takes?\s+\d+\s*min/i, /~\s*\d+\s*min/i, /estimated\s+time/i, /\bETA\b/];
47
+ const TEMPLATE_FILENAME_REGEX = /template/i;
48
+ const COMPILE_TIME_SUB_REGEX = /\{\{\.\w+\}\}/;
46
49
 
47
50
  const SEVERITY_ORDER = { CRITICAL: 0, HIGH: 1, MEDIUM: 2, LOW: 3 };
48
51
 
@@ -535,6 +538,36 @@ function validateSkill(skillDir) {
535
538
  }
536
539
  }
537
540
 
541
+ // --- TPL-01: template files must not contain compile-time {{.var}} substitutions ---
542
+ // Template files seed durable, version-controlled artifacts (spec files) that
543
+ // execute on other machines. Baking a {{.var}} at render time would freeze a
544
+ // machine-local value into every downstream artifact.
545
+ for (const filePath of allFiles) {
546
+ if (path.extname(filePath) !== '.md') continue;
547
+ const base = path.basename(filePath);
548
+ if (!TEMPLATE_FILENAME_REGEX.test(base)) continue;
549
+
550
+ const relFile = path.relative(skillDir, filePath);
551
+ const content = safeReadFile(filePath, findings, relFile);
552
+ if (content === null) continue;
553
+
554
+ const lines = content.split('\n');
555
+ for (const [i, line] of lines.entries()) {
556
+ const match = line.match(COMPILE_TIME_SUB_REGEX);
557
+ if (match) {
558
+ findings.push({
559
+ rule: 'TPL-01',
560
+ title: 'Template files must not contain compile-time substitutions',
561
+ severity: 'HIGH',
562
+ file: relFile,
563
+ line: i + 1,
564
+ detail: `Template file contains compile-time substitution \`${match[0]}\` — this would be baked at render time and leak a machine-local value into every spec produced from the template.`,
565
+ fix: 'Remove the `{{.var}}` reference. Use single-curly `{var}` if the value should be resolved at LLM runtime by the consumer of the generated spec.',
566
+ });
567
+ }
568
+ }
569
+ }
570
+
538
571
  return findings;
539
572
  }
540
573