[6.11.0] sprint_plan.py: hardcoded positional identifier scheme — `generate` writes an empty development_status instead of failing

Author: millsksCreated Sep 4, 2026Updated Sep 4, 2026

Component: bmad-sprint-planningscripts/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

  1. generate is destructive on a non-matching epics file. It resolves zero epics and zero stories and writes an empty development_status, replacing a populated file with nothing. It does not detect that it parsed nothing from a non-empty input.
  2. fix rebuilds 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.
  3. status reads the same keys, so its counts are silently meaningless rather than absent.
  4. validate is 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

  1. Make generate refuse 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.
  2. Have validate distinguish "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.
  3. 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