Skip to main content

BMAD Workflow — Agile Story Management

BMAD (Breakthrough Method of Agile AI-driven Development) is the story-authoring and sprint-management workflow for all ktayl-solution and platform development projects. It bridges Claude-assisted story writing, version-controlled story files in minicloud-gitops, and GitHub Issues — with full automation at every step.


Why BMAD?

Without BMADWith BMAD
Stories written ad-hoc in GitHub Issues — no standard formatStories versioned in git, reviewed like code
No traceability between architecture doc and implementation tasksEach story references the CdCF section it implements
Story duplication when re-running issue creationIdempotent sync — [S001-slug] prefix prevents duplicates
New project sprint setup takes 30+ minBackstage template generates stubs + opens PR in 2 min
Definition of Done inconsistent per engineerTech-stack-specific DoD enforced by template

The Complete Flow

┌─────────────────────────────────────────────────────────────────────┐
│ ENTRY POINTS │
│ │
│ A. Backstage template B. Claude in session C. Manual edit │
│ "New BMAD Sprint" (BMAD agent mode) S00N-slug.md │
└─────────────────┬──────────────────┬───────────────────┬───────────┘
│ │ │
└──────────────────┴───────────────────┘


┌────────────────────────────────┐
│ minicloud-gitops │
│ bmad/stories/<proj>/<ms>/ │
│ S001-slug.md │
│ S002-slug.md ... │
└────────────┬───────────────────┘
│ PR merged to main

┌────────────────────────────────┐
│ .github/workflows/ │
│ bmad-sync.yml fires │
│ → bmad-to-github.sh │
└────────────┬───────────────────┘


┌──────────────────────────────────────────────┐
│ platform-backlog GitHub Issues │
│ [S001-slug] title ← milestone attached │
│ [S002-slug] title ← project board added │
│ ... │
└──────────────────────────────────────────────┘

Repository Layout

Stories live in minicloud-gitops alongside the GitOps manifests:

minicloud-gitops/
├── bmad/
│ ├── templates/
│ │ └── story-template.md # copy this when writing manually
│ └── stories/
│ └── <project-code>/ # e.g. cert-1, claims-service, portal
│ └── <milestone-slug>/ # e.g. m1-m2, m3-m5, sprint-1
│ ├── SPRINT-OVERVIEW.md # generated by Backstage template
│ ├── S001-setup.md
│ ├── S002-domain.md
│ └── ...
└── scripts/
└── bmad-to-github.sh # bridge: YAML frontmatter → GitHub Issues

Story ID convention

S<NNN>-<short-slug> within each sprint directory

S001–S009 core functional stories (domain, API, events, auth)
S010–S019 infrastructure (CI, k8s manifests, ArgoCD, monitoring)
S020+ cross-cutting (security hardening, performance, docs)

IDs reset per sprint directory — cert-1/m3-m5/S001 is different from cert-1/m1-m2/S001.


The fastest path. No CLI, no manual file creation.

  1. Go to Backstage → Create → select New BMAD Sprint

  2. Fill in the form:

    FieldExampleNotes
    Project codecert-1Used as directory name — kebab-case
    Milestone slugm3-m5Directory name within project
    GitHub Milestone titleCERT-1 M3-M5 — ktayl-claims-service (Java)Must match GitHub exactly
    Tech stackjava-springDrives DoD checklist in stories
    Story count6Number of stubs to scaffold
  3. Backstage generates story stubs and opens a PR on minicloud-gitops

  4. Review the PR — open each S00N-*.md and fill in domain-specific Acceptance Criteria

  5. Merge the PRbmad-sync.yml fires and creates all issues on platform-backlog automatically

:::tip Before merging the Backstage-generated PR The generated stories have placeholder ACs. Take 30 minutes to fill in the real acceptance criteria from the CdCF before merging — the GitHub Issues should be actionable, not boilerplate. :::


Path B — Claude Session (BMAD agent mode)

For domain-heavy sprints where Claude co-authors the stories from the CdCF.

In a Claude Code session, describe the sprint and reference the CdCF sections:

Write 10 BMAD stories for ktayl-claims-service Sprint M3-M5.
The service is a Java Spring Boot claims management API.
Reference: CdCF §6.2 (BF-SIN-01 to BF-SIN-06), §8 (NATS integration), §9 (data model).
Use the story template at bmad/templates/story-template.md.
Output to bmad/stories/cert-1/m3-m5/

Claude produces fully formed stories with CdCF-referenced ACs, technical notes, and DoD. Review, adjust, commit.


Official BMAD Tools

The BMAD Method provides a set of Claude Code skills that augment our custom workflow. Install once per repo:

npx bmad-method install --directory . --modules bmm --tools claude-code --yes
# Creates _bmad/ directory. Add to .gitignore: /_bmad/

Tool-to-workflow mapping

WhenUseWhat it does
Before writing a story/bmad-forge-ideaAdversarial pressure-testing — 3 verdicts: hardened / killed / clearer
Sprint kick-off/bmad-sprint-planningReadiness gate (PASS/CONCERNS/FAIL), produces sprint-status.yaml
Writing specs/ACs/bmad-advanced-elicitationPre-mortem, first-principles, red-team analysis
Architecture decisions/bmad-party-modeMulti-agent design discussion; Anti-Consensus Club template
Deep unknowns/bmad-deep-reconStructured research: 6 types × 3 modes (discovery/targeted/validation)
Story implementation/bmad-buildSpec-driven autonomous loop: clarify → implement → review → report
Autonomous multi-story/bmad-build-autoUnattended build loop; exits with blocked or done
Post-sprint[bmad-review]Evidence-based retrospective; 3 verdicts: accepted / accepted-with-open-items / rejected

Named agents for story co-authoring

In a Claude session, invoke agents by name to use their specialist persona:

AgentInvokeWhen
Mary (BA)/bmad-agent-maryEliciting ACs from CdCF functional requirements
John (PM)/bmad-agent-johnWriting the PRD, prioritisation, MoSCoW
Sally (UX)/bmad-agent-sallyUser journeys, edge-case flows
Winston (Architect)/bmad-agent-winstonTechnical notes, ADRs, system boundaries
Amelia (Dev)/bmad-agent-ameliaTask breakdown, implementation approach

Web planning (Gemini Gems / ChatGPT GPTs)

For analysis and planning conversations, BMAD bundles are available as web tools. The design principle: Plan in the web, build in the IDE.

BundlePlatformWhen
Product Brief CoachGem / GPTInitial scoping
PRD CoachGem / GPTWriting the requirements doc
UX CoachGem / GPTUser flow design
Brainstorming CoachGem / GPTIdea exploration before bmad-forge-idea

Polished artifacts from web sessions go into bmad/stories/<project>/<ms>/ for git-versioning and the bridge script.


Path C — Manual Story Creation

For adding individual stories to an existing sprint mid-sprint.

# Copy the template
cp bmad/templates/story-template.md \
bmad/stories/cert-1/m1-m2/S011-performance-tests.md

# Edit the story
# Fill in id, title, estimate, labels, milestone, and all sections

Commit and push to main. The bmad-sync.yml action detects the changed file and creates the new issue. Existing issues in the same directory are skipped (idempotent).


Story Format Reference

Every story file has a YAML frontmatter block followed by structured Markdown sections.

Frontmatter

---
id: S001-short-slug # REQUIRED — used as [S001-short-slug] issue title prefix
title: "Full story title" # REQUIRED — GitHub Issue title (prefix added automatically)
status: Ready # Draft | Ready | In Progress | Done
type: Story # Story | Bug | Spike | Task
epic: project-slug # free-form epic grouping
milestone: "CERT-1 M1-M2 — ktayl-policy-service (Go)" # MUST match GitHub Milestone exactly
estimate: 3 # story points — Fibonacci: 1, 2, 3, 5, 8, 13
labels: [go, api, cert-1] # auto-created on platform-backlog if missing
priority: Must # Must | Should | Could | Won't (MoSCoW)
assignee: AndreLiar
---

Mandatory sections

SectionPurpose
## StoryUser story sentence: "As a {role}, I want {action} so that {benefit}."
## BackgroundContext — CdCF section reference, why this story exists
## Acceptance CriteriaNumbered - [ ] AC-N: checkboxes — testable, unambiguous
## Definition of DoneAlways include: L0 lint ✅ · L1 tests ✅ · PR merged ✅ · ArgoCD Synced ✅
## TasksImplementation steps as - [ ] TASK-N: checkboxes
## Dependencies"Depends on: S002" · "Blocks: S007"

Optional sections

SectionWhen to add
## Technical NotesNon-obvious design decisions, gotchas, library choices
## Spike NotesFor type: Spike — time-boxed investigation questions

Running the Bridge Script Manually

The script is idempotent — safe to re-run. Issues with [S001-slug] already in their title are skipped.

cd ~/Developer/cloudplateform/minicloud-gitops

# 1. Always dry-run first
bash scripts/bmad-to-github.sh bmad/stories/cert-1/m1-m2 \
--milestone "CERT-1 M1-M2 — ktayl-policy-service (Go)" \
--dry-run

# 2. For real
bash scripts/bmad-to-github.sh bmad/stories/cert-1/m1-m2 \
--milestone "CERT-1 M1-M2 — ktayl-policy-service (Go)"

# Options
# --repo org/repo default: andrelair-platform/platform-backlog
# --project N default: 1 (minicloud platform roadmap)
# --dry-run print actions, create nothing

The script:

  1. Parses YAML frontmatter with Python (no yq dependency)
  2. Prefixes the title with [S001-slug] for deduplication
  3. Auto-creates missing labels on platform-backlog (grey, update description later)
  4. Attaches each issue to the milestone and project board
  5. Prints Created: #250 or Skipped (already exists): #250

GitHub Milestone Management

Each sprint has one GitHub Milestone on platform-backlog. The milestone title must match the milestone: field in every story's frontmatter character-for-character (including em-dashes and parentheses).

# Create a milestone before running the bridge script
gh milestone create \
--repo andrelair-platform/platform-backlog \
--title "CERT-1 M3-M5 — ktayl-claims-service (Java)" \
--description "Sprint M3-M5: claims service core + Spring Batch COREP" \
--due-date "2027-01-31"

# Verify it was created
gh api repos/andrelair-platform/platform-backlog/milestones \
--jq '.[] | [.number, .title] | @tsv'

Current milestones

#TitleSprint
10CERT-1 M0 — Architecture & Cadrage✅ Closed
11CERT-1 M1-M2 — ktayl-policy-service (Go)🟡 Active
12CERT-1 M3-M5 — ktayl-claims-service (Java)⬜ Planned
13CERT-1 M6 — ktayl-ai-claims-assistant (Python)⬜ Planned
14CERT-1 M7 — ktayl-portal (TypeScript)⬜ Planned
15CERT-1 M8 — Integration & UAT⬜ Planned

Label Conventions

LabelStack / Layer
go java python typescriptPrimary language
backend frontend devopsArchitecture layer
api database domain-logicCode area
ci gitops kubernetesPlatform area
security testing observabilityCross-cutting concern
cert-1 claims-service portalProject scope

Labels are auto-created (grey) by the bridge script if they don't exist. To update descriptions/colours:

gh label edit "cert-1" \
--color "7c3aed" \
--description "CERT-1 / RNCP39583 deliverable" \
--repo andrelair-platform/platform-backlog

Project Codes Registry

CodeProjectTech stackMilestones
cert-1ktayl Claims & Policy Platform (RNCP39583)Go → Java → Python → TypeScriptm0 → m1-m2 → m3-m5 → m6 → m7 → m8

To register a new project: add a row to this table and create the GitHub Milestones before the first sprint.


The GitHub Action (bmad-sync.yml)

The action at .github/workflows/bmad-sync.yml in minicloud-gitops:

  • Trigger: push to main with changes to bmad/stories/**/*.md
  • What it does: detects changed sprint directories, reads the milestone: field from the first story's frontmatter, runs bmad-to-github.sh for each directory
  • Auth: uses GITOPS_TOKEN org-level secret (already set, no configuration needed)
  • Idempotent: existing issues detected by [S001-slug] in:title search and skipped

:::note Adding stories to an existing sprint Just create the new SNNN-slug.md file, commit to main. The action fires, skips the 10 existing issues, creates only the new one. :::


Troubleshooting

"milestone not found" warning

The milestone: frontmatter value doesn't match any GitHub Milestone title exactly. Check for:

  • Trailing spaces
  • Wrong dash type (use em-dash, not or -)
  • Wrong milestone title (compare with gh api repos/.../milestones --jq '.[].title')

Duplicate issues created

The [S001-slug] prefix is missing from the issue title. This happens if issues were created by an old version of the script (before the prefix convention). Fix:

gh issue edit <number> \
--repo andrelair-platform/platform-backlog \
--title "[S001-short-slug] Original title here"

Label not found error

Older script versions required labels to exist before running. The current script creates missing labels automatically. Update the script: git pull origin main in minicloud-gitops.

Backstage template shows "not found"

The backstage-values.yaml in minicloud-gitops registers the template URL. After merging PR #696, ArgoCD syncs the ConfigMap, then kubectl rollout restart deployment/backstage -n backstage applies it. Template appears in Backstage within ~1 minute.