[6.11.0] sprint_plan.py: hardcoded positional identifier scheme — `generate` writes an empty development_status instead of failing
Component: bmad-sprint-planning — scripts/sprint_plan.py
Version observed: BMad Method v6.11.0
Severity: data loss (generate, fix); unusable (status, validate)
Summary
The skill assumes positional identifiers — Epic 1, Story 1.2, epic-1, 1-2-slug — in four
hardcoded regexes. A project that uses non-positional identifiers cannot use any subcommand, and
two of them rewrite the tracking file rather than failing.
Non-positional keys are a reasonable choice: they mean adding an epic never renumbers another. The skill has no way to be told about them.
The four patterns
All in sprint_plan.py. None is read from config, environment, or the customization file:
EPIC_KEY_RE = re.compile(r"^epic-(\d+)$") # :55
RETRO_KEY_RE = re.compile(r"^epic-(\d+)-retrospective$") # :56
STORY_KEY_RE = re.compile(r"^(\d+)-(\d+)([a-z]?)-.+") # :57
EPIC_RE = re.compile(r"^#{1,3}\s*Epic\s+(\d+)\s*:?\s*(.*?)\s*#*\s*$", re.IGNORECASE) # :44
Impact, in severity order
generateis destructive on a non-matching epics file. It resolves zero epics and zero stories and writes an emptydevelopment_status, replacing a populated file with nothing. It does not detect that it parsed nothing from a non-empty input.fixrebuilds into the positional scheme, discarding the project's identifiers — which are also the keys shared with the epics file, the test-design handoff's story bindings, and every story filename on disk.statusreads the same keys, so its counts are silently meaningless rather than absent.validateis safe but always negative. It reports every key as unrecognized, so it cannot distinguish "this project uses a different scheme" from "this file is corrupt".
Reproduction
A project whose epics file uses headings like ## CPM-EP-EVIDENCE: An evidence log that cannot lie
and ### CPM-EVIDENCE-S01: Five outcome states with one precedence order, and whose tracking file
uses matching lowercased keys.
$ python sprint_plan.py validate --status-file _bmad-output/implementation-artifacts/sprint-status.yaml
{"ok": true, "action": "validate", "valid": false, "problems": [
"unrecognized key 'epic-platform' (expected epic-N, N-M-slug, or epic-N-retrospective)",
"unrecognized key 'cpm-evidence-s01-five-outcome-states-precedence-order' (...)",
... 62 keys, all of them ...
]}
Running the script's own patterns against that epics file:
epic headings matched: 0
story headings matched: 0
total headings in file: 81
So generate would resolve nothing and emit an empty development_status.
Why it cannot be worked around
customize.toml exposes exactly four keys — activation_steps_prepend, activation_steps_append,
persistent_facts, on_complete. None reaches parsing or validation.
Editing the script works until the next update overwrites it: the customize.toml beside it carries
DO NOT EDIT -- overwritten on every update.
Suggested fixes, cheapest first
- Make
generaterefuse when it parses zero epics from a non-empty epics file. One guard, and it closes the data-loss case regardless of everything else. Parsing nothing out of 81 headings is a parse failure, not an empty backlog. - Have
validatedistinguish "no key matches any known pattern" — likely a different identifier scheme — from "some keys are malformed" — likely corruption. The current message reads as the latter for what is usually the former. - Expose the patterns in
customize.toml, e.g. a[workflow.identifiers]block carrying the four regexes, so a project declares its scheme once and every subcommand honours it.
Secondary: the inline dependency assumes uv
sprint_plan.py declares its dependency as PEP 723 inline metadata:
# /// script
# requires-python = ">=3.10"
# dependencies = ["ruamel.yaml>=0.18"]
# ///
Only uv run provisions that automatically. A project standardized on another package manager gets
ModuleNotFoundError: No module named 'ruamel' from the documented invocation, with nothing
indicating the dependency is the problem rather than the script. A line in the skill's instructions
about non-uv environments — install ruamel.yaml into the interpreter you invoke it with — would
save the rediscovery.
Source: bmad-code-org/BMAD-METHOD