Skip to content

fix: disambiguate same-numbered stories across epics (build-cmd + orchestrator-helper)#47

Open
cannt wants to merge 1 commit into
bmad-code-org:mainfrom
cannt:fix/epic-qualified-story-resolution
Open

fix: disambiguate same-numbered stories across epics (build-cmd + orchestrator-helper)#47
cannt wants to merge 1 commit into
bmad-code-org:mainfrom
cannt:fix/epic-qualified-story-resolution

Conversation

@cannt

@cannt cannt commented Jun 25, 2026

Copy link
Copy Markdown

Problem

When two epics each contain a story with the same bare number (e.g. epic foo story 6.1 and epic bar story 6.1), build-cmd and orchestrator-helper's sprint-status get / normalize-key resolve a story by its bare number / prefix. In a multi-epic sprint that can match the wrong epic's story:

  • a spawned worker may operate on a different epic's story file, and
  • sprint-status lookups can read/write the wrong key.

Fix

The codebase already disambiguates stories by epic via normalize_story_key_for_epic() (core/story_keys.py) — it is used throughout the epic internals (orchestrator_epic_agents.py, core/epic_parser.py, core/sprint.py) and is unit-tested. But two command surfaces do not thread the epic through and fall back to bare-number / prefix resolution: build-cmd (worker spawn) and orchestrator-helper's normalize-key / sprint-status get.

This PR makes those two surfaces consistent with the rest of the codebase: it threads an optional --epic into them and routes through the same normalize_story_key_for_epic(). A new {{story_key}} prompt variable lets the worker target the exact story file instead of a prefix-* glob. No new resolution logic is introduced.

Backward compatibility

Without --epic, behavior is unchanged — {{story_key}} falls back to {{story_prefix}}, and resolution falls back to the existing normalize_story_key.

Changes

  • core/prompt_rendering.py — optional story_key param + {{story_key}} replacement
  • commands/tmux.py — parse --epic in build-cmd, resolve the epic-qualified key, pass it on
  • commands/orchestrator.py — accept --epic on sprint-status get and normalize-key; two small helpers
  • data/prompts/auto.md — target {{story_key}}.md; warn against cross-epic number collisions
  • steps-c/step-03a-execute-review.md — pass --epic {epic} into build-cmd
  • scripts/smoke-test.sh — update the two build-cmd assertions to expect --epic {epic}

Tests

Added:

  • _resolve_with_epic disambiguates a bare number across two epics, and falls back to the plain resolver without an epic.
  • build-cmd auto 1 --epic <e> renders the matching epic's exact story file and not the other epic's.

npm run pack:dry-run, npm run test:cli, and npm run test:smoke all pass. python3 -m unittest discover -s tests → 413/414. The one failing test (test_build_cmd_uses_legacy_ai_command_consistently_for_claude, asserting claude --print vs --dangerously-skip-permissions) also fails on a clean checkout of main and is unrelated to this change.

No new dependencies.

Summary by CodeRabbit

  • New Features

    • Added epic-aware command handling, allowing story selection and normalization to target a specific epic when needed.
    • Updated generated prompts and build commands to use the matching story key for the selected epic.
  • Bug Fixes

    • Improved disambiguation when multiple epics contain the same story number, reducing the chance of picking the wrong story.
  • Tests

    • Added regression coverage for epic-specific story resolution and command output.

@coderabbitai

coderabbitai Bot commented Jun 25, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@cannt, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 38 minutes and 36 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: de34e1bc-0e7b-4ec0-aa9f-575fa60f2e6e

📥 Commits

Reviewing files that changed from the base of the PR and between 7fe0e7e and 7e32301.

📒 Files selected for processing (9)
  • scripts/smoke-test.sh
  • skills/bmad-story-automator/data/prompts/auto.md
  • skills/bmad-story-automator/src/story_automator/commands/orchestrator.py
  • skills/bmad-story-automator/src/story_automator/commands/tmux.py
  • skills/bmad-story-automator/src/story_automator/core/prompt_rendering.py
  • skills/bmad-story-automator/src/story_automator/core/story_keys.py
  • skills/bmad-story-automator/steps-c/step-03a-execute-review.md
  • tests/test_normalize_story_key.py
  • tests/test_state_policy_metadata.py

Walkthrough

The CLI now accepts an optional --epic selector in sprint-status get, normalize-key, and build-cmd. Epic-aware story-key resolution now feeds prompt rendering, and the story prompt, step file, smoke test, and unit tests were updated to use epic-specific story files.

Changes

Epic-scoped story key flow

Layer / File(s) Summary
CLI resolution and normalization
skills/bmad-story-automator/src/story_automator/commands/orchestrator.py, tests/test_normalize_story_key.py
sprint-status get and normalize-key accept --epic, resolve story keys with epic-scoped helpers, and add regression coverage for epic-qualified and fallback lookup.
build-cmd epic prompt flow
skills/bmad-story-automator/src/story_automator/commands/tmux.py, skills/bmad-story-automator/src/story_automator/core/prompt_rendering.py, skills/bmad-story-automator/data/prompts/auto.md, skills/bmad-story-automator/steps-c/step-03a-execute-review.md, tests/test_state_policy_metadata.py, scripts/smoke-test.sh
build-cmd parses --epic, forwards the resolved story_key into prompt rendering, and the prompt text, step file, state-policy test, and smoke test now target epic-specific story files.

Sequence Diagram(s)

sequenceDiagram
  participant build_cmd as "build-cmd in tmux.py"
  participant normalize_story_key_for_epic as "normalize_story_key_for_epic"
  participant render_step_prompt as "render_step_prompt"
  participant auto_prompt as "skills/bmad-story-automator/data/prompts/auto.md"

  build_cmd->>normalize_story_key_for_epic: derive epic-scoped story_key from --epic
  build_cmd->>render_step_prompt: pass story_key into step prompt rendering
  render_step_prompt->>auto_prompt: replace {{story_key}} with the resolved story_key
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • bmad-code-org/bmad-automator#23: Introduces the epic-aware story key resolution helpers and related sprint-status matching logic that this PR extends into CLI, prompts, and tests.

Suggested reviewers

  • bma-d

Poem

I hopped through the code with an epic new key,
and every story file found its own little tree.
--epic went thump, the prompt purred along,
now each bunny tale lands where it truly belongs. 🐇

🚥 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 clearly matches the main change: epic-aware disambiguation for same-numbered stories in build-cmd and orchestrator-helper.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@cannt cannt marked this pull request as ready for review June 25, 2026 10:43
@cannt cannt requested a review from bma-d as a code owner June 25, 2026 10:43

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
skills/bmad-story-automator/src/story_automator/commands/tmux.py (1)

202-208: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Duplicate epic-resolution logic.

This block (num = re.split(r"[.\-]", story_id)[-1]normalize_story_key_for_epic(...)) duplicates _resolve_with_epic in orchestrator.py (Lines 399-405). Consider extracting a shared helper in story_keys.py so the two surfaces can't drift.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@skills/bmad-story-automator/src/story_automator/commands/tmux.py` around
lines 202 - 208, The epic-resolution logic in the tmux command duplicates the
same story-key handling already implemented in Orchestrator._resolve_with_epic,
so refactor it into a shared helper in story_keys.py and have both call sites
use that helper. Keep the behavior for deriving the numeric suffix from story_id
and passing it through normalize_story_key_for_epic, but centralize the logic so
StoryAutomator and Orchestrator stay aligned and cannot drift.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@skills/bmad-story-automator/src/story_automator/commands/tmux.py`:
- Around line 202-208: The epic-resolution logic in the tmux command duplicates
the same story-key handling already implemented in
Orchestrator._resolve_with_epic, so refactor it into a shared helper in
story_keys.py and have both call sites use that helper. Keep the behavior for
deriving the numeric suffix from story_id and passing it through
normalize_story_key_for_epic, but centralize the logic so StoryAutomator and
Orchestrator stay aligned and cannot drift.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 78bffe4c-a521-487c-bb6c-3f4c0e4f9d08

📥 Commits

Reviewing files that changed from the base of the PR and between f332173 and 7fe0e7e.

📒 Files selected for processing (8)
  • scripts/smoke-test.sh
  • skills/bmad-story-automator/data/prompts/auto.md
  • skills/bmad-story-automator/src/story_automator/commands/orchestrator.py
  • skills/bmad-story-automator/src/story_automator/commands/tmux.py
  • skills/bmad-story-automator/src/story_automator/core/prompt_rendering.py
  • skills/bmad-story-automator/steps-c/step-03a-execute-review.md
  • tests/test_normalize_story_key.py
  • tests/test_state_policy_metadata.py

@cannt

cannt commented Jun 25, 2026

Copy link
Copy Markdown
Author

Addressed the duplicate epic-resolution nitpick: extracted resolve_bare_story_for_epic into core/story_keys.py, and both call sites (build-cmd in tmux.py and _resolve_with_epic in orchestrator.py) now use it — so the two surfaces can't drift. Also let me drop the now-unused import re from tmux.py. Suite still green (pack/cli/smoke + unittest, the one pre-existing failure aside).

@cannt cannt force-pushed the fix/epic-qualified-story-resolution branch from 7fe0e7e to 7e32301 Compare June 25, 2026 11:03
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.

1 participant