Skip to content

fix(sprintstatus): recognize split-story keys with a letter suffix#147

Merged
pbean merged 1 commit into
mainfrom
fix/story-key-letter-suffix
Jul 15, 2026
Merged

fix(sprintstatus): recognize split-story keys with a letter suffix#147
pbean merged 1 commit into
mainfrom
fix/story-key-letter-suffix

Conversation

@pbean

@pbean pbean commented Jul 15, 2026

Copy link
Copy Markdown
Collaborator

Fixes #144.

Problem

BMAD's split-story convention produces keys like 2-6a-example-slug, but STORY_RE/SHORT_REF_RE required a digits-only story number. Split stories fell into unknown_keys and were silently dropped: invisible to the backlog count and the TUI sprint tree, unselectable via --story, skipped by run (which then walked on to later — possibly dependent — stories out of order), and skipped by advance()'s epic-lift. The only trace was a journal event.

Why not the issue's two-line fix

The reporter's minimal fix (an uncaptured [a-z]?) resolves the invisibility but introduces a mis-selection: with the suffix discarded, --story 2-6a parses to num=6 and matches both halves — the engine's _pick_next selector filter could dispatch 2-6b when asked for 2-6a, and the TUI would render both halves identically ({num}-{slug}6-example-slug twice). The reporter flagged exactly this in their "design decision" caveat, so this PR takes the fuller path they sketched.

Change

  • Suffix is a first-class field (Story.suffix, StorySelector.suffix); group numbering shifts once inside sprintstatus.py, the only module that parses story keys. num stays an int (it has no arithmetic consumers — selection equality and display only).
  • Selection semantics: 2-6a / 2.6a / --epic 2 --story 6a / full key select exactly that half. A plain 2-6 (suffix=None) matches the whole 2-6a/2-6b family in file order — mirroring how short refs already ignore the slug. Conservative shape: single lowercase letter (2-6ab-x, 2-6A-x stay unknown).
  • Epic-lift in advance() now works for split keys for free (it silently skipped them before).
  • TUI renders halves distinctly: 6a-build-structure / 6b-extend-structure.
  • Silent-skip surfaced (the issue's "beyond the fix" suggestion): run and --dry-run print warning: ignoring unparseable sprint-status keys: … to stderr, matching the note validate already emits; the sprint-status-unknown-keys journal event stays the durable record.

Verification

  • 2259 passed, 1 skipped (full suite), trunk check clean.
  • New tests: split-pair load + file order + unknown-shape rejection, selector forms for every suffixed ref, suffix-exact vs family selection (incl. not-actionable and no-match errors), split-key epic-lift, dry-run selection + stderr warning, TUI label rendering.
  • End-to-end repro of the issue's scenario in a sandbox (2-5 done / 2-6a backlog / 2-6b backlog / 2-7 backlog / weird-key):
    • run --dry-run --epic 22-6a, 2-6b, 2-7 (previously jumped to 2-7) + the warning line
    • --story 2-6a → exactly 2-6a; --story 2-6 → the pair; --story 2.6b → exactly 2-6b
    • --story 2-6cno story matches '2-6c'

Summary by CodeRabbit

  • Bug Fixes
    • Split stories such as 2-6a and 2-6b are now parsed and selected correctly.
    • Selecting a specific suffix targets only that split story, while unsuffixed references select the full family in file order.
    • Story labels now display split suffixes accurately.
    • run and --dry-run warn when sprint-status entries cannot be parsed.

BMAD splits an oversized story into 2-6a/2-6b, but STORY_RE required a
digits-only story number, so split stories fell into unknown_keys and
were silently dropped: invisible to run/--story/the TUI tree and skipped
by advance()'s epic-lift — with the loop walking on to later, possibly
dependent, stories.

The suffix is a first-class Story/StorySelector field rather than an
uncaptured widening (which would have let --story 2-6a dispatch 2-6b and
rendered both halves identically in the TUI): 2-6a / 2.6a / --epic 2
--story 6a select exactly that half, while a plain 2-6 matches the whole
split family in file order. run/--dry-run now also print a stderr
warning when sprint-status keys remain unparseable instead of only
journaling them.

Fixes #144
@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c6c7db70-f0d3-4f25-a4c7-8abc42b032bf

📥 Commits

Reviewing files that changed from the base of the PR and between 73ce62f and 95853b3.

📒 Files selected for processing (8)
  • CHANGELOG.md
  • src/bmad_loop/cli.py
  • src/bmad_loop/sprintstatus.py
  • src/bmad_loop/tui/widgets.py
  • tests/test_cli.py
  • tests/test_sprintstatus.py
  • tests/test_sprintstatus_advance.py
  • tests/test_tui_app.py

Walkthrough

Split-story suffixes are now parsed into sprint-status stories and selectors, enabling exact-half or whole-family selection. Run modes warn about unknown keys, TUI labels show suffixes, and tests cover parsing, selection, advancement, warnings, and rendering.

Changes

Split-story sprint-status support

Layer / File(s) Summary
Parse and select split stories
src/bmad_loop/sprintstatus.py, tests/test_sprintstatus.py, tests/test_sprintstatus_advance.py
Story keys and selectors preserve lowercase suffixes, exact suffixed references select one half, unsuffixed references select the family, and advancement behavior is tested.
CLI warnings and story references
src/bmad_loop/cli.py, tests/test_cli.py, CHANGELOG.md
Run and dry-run warn about unparseable keys, help text documents suffixed references, and the fix is recorded in the changelog.
Suffix-aware story labels
src/bmad_loop/tui/widgets.py, tests/test_tui_app.py
TUI labels include split suffixes and regression tests verify distinct labels for split halves.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CLI
  participant sprintstatus
  participant SprintStatusYAML
  CLI->>sprintstatus: Parse story selector
  sprintstatus->>SprintStatusYAML: Load sprint-status
  SprintStatusYAML-->>sprintstatus: Return parsed stories and unknown keys
  sprintstatus-->>CLI: Select actionable story and report unknown keys
Loading

Suggested reviewers: dracic

Poem

I’m a rabbit who split tales with care,
Now 6a and 6b appear.
The loop picks the proper small part,
Warnings hop forth when keys depart,
And labels show each suffix clear!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title succinctly states the main fix: sprintstatus now recognizes split-story keys with suffixes.
Linked Issues check ✅ Passed The changes address #144 by parsing suffixed keys, selecting them correctly, warning on unknown keys, and preserving journal behavior.
Out of Scope Changes check ✅ Passed All code changes support split-story parsing, selection, warnings, TUI rendering, and tests; no unrelated functionality is introduced.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/story-key-letter-suffix

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@pbean pbean merged commit 541a294 into main Jul 15, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Sprint-status story keys with a letter suffix (e.g. 2-6a) are silently skipped by STORY_RE

1 participant