diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json
index b4e1212fd..d37df153f 100644
--- a/.claude-plugin/marketplace.json
+++ b/.claude-plugin/marketplace.json
@@ -12,7 +12,7 @@
"name": "bmad-loop",
"source": "./src/bmad_loop/data/skills",
"description": "Automation-mode skills driven by the bmad-loop orchestrator: project bootstrap (bmad-loop-setup), interactive escalation resolution (bmad-loop-resolve) and deferred-work sweep triage (bmad-loop-sweep) — the inner dev primitive (which self-reviews and commits) is the upstream bmad-dev-auto skill",
- "version": "0.11.1",
+ "version": "0.12.0",
"author": {
"name": "pinkyd"
},
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7192b6095..0785b006b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,8 @@ breaking changes may land in a minor release.
## [Unreleased]
+## [0.12.0] — 2026-09-20
+
### Added
- Restore and normalize attempt-owned specs on Windows through NT handle-relative
@@ -478,6 +480,13 @@ breaking changes may land in a minor release.
### Fixed
+- Bound the GitHub release body `scripts/release.py publish` sends at GitHub's
+ 125,000-character limit, cutting at an entry boundary and linking the full
+ `CHANGELOG.md` section. An oversize section was rejected with HTTP 422 only after
+ `gh release create` had pushed the tag, stranding a tag with no release that the
+ next publish then treated as already published. `prepare` warns when a section
+ will be truncated.
+
- Bind legacy migration dispatch and publication to the ledger bytes actually accepted
(DW-311, DW-316). Retire stale recovery authority when the ledger changes before
the true post-hook adapter-launch boundary, then publish a validated one-path,
@@ -6114,7 +6123,8 @@ enforced in CI.
implementation phase, driven by a Python control loop with hook-based session transport and
resumable on-disk run state.
-[Unreleased]: https://github.com/bmad-code-org/bmad-loop/compare/v0.11.1...HEAD
+[Unreleased]: https://github.com/bmad-code-org/bmad-loop/compare/v0.12.0...HEAD
+[0.12.0]: https://github.com/bmad-code-org/bmad-loop/releases/tag/v0.12.0
[0.11.1]: https://github.com/bmad-code-org/bmad-loop/releases/tag/v0.11.1
[0.11.0]: https://github.com/bmad-code-org/bmad-loop/releases/tag/v0.11.0
[0.10.0]: https://github.com/bmad-code-org/bmad-loop/releases/tag/v0.10.0
diff --git a/docs/images/dashboard.png b/docs/images/dashboard.png
index 0b7175634..f6f0853a6 100644
Binary files a/docs/images/dashboard.png and b/docs/images/dashboard.png differ
diff --git a/docs/images/dashboard.svg b/docs/images/dashboard.svg
index c6e7ed57c..21b108f7d 100644
--- a/docs/images/dashboard.svg
+++ b/docs/images/dashboard.svg
@@ -19,229 +19,229 @@
font-weight: 700;
}
- .terminal-3840421111-matrix {
+ .terminal-3504221515-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-3840421111-title {
+ .terminal-3504221515-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-3840421111-r1 { fill: #c5c8c6 }
-.terminal-3840421111-r2 { fill: #e0e0e0 }
-.terminal-3840421111-r3 { fill: #a0a3a6 }
-.terminal-3840421111-r4 { fill: #0053aa }
-.terminal-3840421111-r5 { fill: #e2e2e2;font-weight: bold }
-.terminal-3840421111-r6 { fill: #e0e0e0;font-weight: bold }
-.terminal-3840421111-r7 { fill: #98e024 }
-.terminal-3840421111-r8 { fill: #9d9d9d }
-.terminal-3840421111-r9 { fill: #fd971f }
-.terminal-3840421111-r10 { fill: #a1a1a1 }
-.terminal-3840421111-r11 { fill: #ddedf9;font-weight: bold }
-.terminal-3840421111-r12 { fill: #272727 }
-.terminal-3840421111-r13 { fill: #003054 }
-.terminal-3840421111-r14 { fill: #1e1e1e }
-.terminal-3840421111-r15 { fill: #0053aa;font-weight: bold }
-.terminal-3840421111-r16 { fill: #797979 }
-.terminal-3840421111-r17 { fill: #3e3e3e }
-.terminal-3840421111-r18 { fill: #262626 }
-.terminal-3840421111-r19 { fill: #0178d4 }
-.terminal-3840421111-r20 { fill: #121212 }
-.terminal-3840421111-r21 { fill: #191919 }
-.terminal-3840421111-r22 { fill: #9e9e9e }
-.terminal-3840421111-r23 { fill: #58d1eb }
-.terminal-3840421111-r24 { fill: #f4005f }
-.terminal-3840421111-r25 { fill: #f4005f;font-weight: bold }
-.terminal-3840421111-r26 { fill: #ffa62b;font-weight: bold }
-.terminal-3840421111-r27 { fill: #495259 }
+ .terminal-3504221515-r1 { fill: #c5c8c6 }
+.terminal-3504221515-r2 { fill: #e0e0e0 }
+.terminal-3504221515-r3 { fill: #a0a3a6 }
+.terminal-3504221515-r4 { fill: #0053aa }
+.terminal-3504221515-r5 { fill: #e2e2e2;font-weight: bold }
+.terminal-3504221515-r6 { fill: #e0e0e0;font-weight: bold }
+.terminal-3504221515-r7 { fill: #98e024 }
+.terminal-3504221515-r8 { fill: #9d9d9d }
+.terminal-3504221515-r9 { fill: #fd971f }
+.terminal-3504221515-r10 { fill: #a1a1a1 }
+.terminal-3504221515-r11 { fill: #ddedf9;font-weight: bold }
+.terminal-3504221515-r12 { fill: #272727 }
+.terminal-3504221515-r13 { fill: #003054 }
+.terminal-3504221515-r14 { fill: #1e1e1e }
+.terminal-3504221515-r15 { fill: #0053aa;font-weight: bold }
+.terminal-3504221515-r16 { fill: #797979 }
+.terminal-3504221515-r17 { fill: #3e3e3e }
+.terminal-3504221515-r18 { fill: #262626 }
+.terminal-3504221515-r19 { fill: #0178d4 }
+.terminal-3504221515-r20 { fill: #121212 }
+.terminal-3504221515-r21 { fill: #191919 }
+.terminal-3504221515-r22 { fill: #9e9e9e }
+.terminal-3504221515-r23 { fill: #58d1eb }
+.terminal-3504221515-r24 { fill: #f4005f }
+.terminal-3504221515-r25 { fill: #f4005f;font-weight: bold }
+.terminal-3504221515-r26 { fill: #ffa62b;font-weight: bold }
+.terminal-3504221515-r27 { fill: #495259 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- bmad-loop
+ bmad-loop
-
+
-
- ⭘bmad-loop — ~/code/acme-search
-─Runs─────────────────────────────│20260612-141000-e5f6▶ running started 2026-06-12T14:10:00 epic 2
- st run type │tasks 5 done 2 deferred 1 escalated 0 1,059,100 tokens (2,473,900 raw)
-✔ 20260610-090000-a1b2 story │───────────────────────────────────────────────────────────────────────────────────────────────────────────────────
-▶ 20260611-101500-c3d4 sweep │ story phase agent dev review tokens info
- ▶ 20260612-141000-e5f6 story │ 2-1-search-index done - ×1 ×1 338,400 a1b2c3d4e5f6
-│ 2-2-typeahead done - ×1 ×2 283,200 b2c3d4e5f607
-│ 2-3-search-ranking review-running - ×1 ×1 224,300
-│ 2-4-saved-searches dev-running - ×1 ×0 81,900
-│ 2-5-search-analytics deferred - ×2 ×1 131,300 dev budget exhausted
-▎│▉
-─ Sprint ─────────────────────────│───────────────────────────────────────────────────────────────────────────────────────────────────────────────────
-▼ Epic 1 · 3/3 ✓│JournalLogAttention
-├ ✓ 1-auth│╸━━━━━━━╺━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
-├ ✓ 2-session-tokens│▊▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▎
-├ ✓ 3-password-reset│▊21:08:11session-start task_id=2-1-search-index role=dev story_key=2-1-search-index▎
-└ ✓ retrospective│▊21:08:11story-done story_key=2-1-search-index commit=a1b2c3d4e5f6▎
-▼ Epic 2 · 2/5│▊21:08:11session-start task_id=2-2-typeahead role=dev story_key=2-2-typeahead▎
-├ ✓ 1-search-index│▊21:08:11review-cycle story_key=2-2-typeahead cycle=2 findings=3▎
-├ ✓ 2-typeahead│▊21:08:11story-done story_key=2-2-typeahead commit=b2c3d4e5f607▎
-├ ▶ 3-search-ranking│▊21:08:11story-start story_key=2-3-search-ranking epic=2▎
-├ ◆ 4-saved-searches│▊21:08:11spec-approved story_key=2-3-search-ranking tokens=1834▎
-└ ○ 5-search-analytics│▊21:08:11dev-done story_key=2-3-search-ranking tasks_done=3 tasks_total=3▎
-▼ Epic 3 · 0/2│▊21:08:11verify-ok story_key=2-3-search-ranking commands=pytest -q, ruff check .▎
-├ · 1-billing-portal│▊21:08:11review-start story_key=2-3-search-ranking role=review▎
-└ · 2-invoices│▊21:08:11escalation-preference story_key=2-3-search-ranking detail=reviewer used codex for the scoring math▎
-│▊21:08:11story-deferred story_key=2-5-search-analytics reason=dev budget exhausted▎
-│▊▎
-│▊▎
-─ Deferred Work ──────────────────│▊▎
-DW-1 Harden the OAuth token refre…│▊▎
-DW-2 Add pagination to the search…│▊▎
-DW-3 Replace the ad-hoc ranking w…│▊▎
-DW-4 Flaky retry in the indexer i…│▊▎
-DW-5 ✓ Polish the empty-state cop…│▊▎
-│▊▎
-│▊▎
-│▊▎
-│▊▎
-│▊▎
-│▊▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎
- y copy ^w resize q quit r run s sweep e resume p review R resolve d decisions a attach x stop S soft-stop D delete A archive▏^p palette
+
+ ⭘bmad-loop — ~/code/acme-search
+─Runs─────────────────────────────│20260612-141000-e5f6▶ running started 2026-06-12T14:10:00 epic 2
+ st run type │tasks 5 done 2 deferred 1 escalated 0 1,059,100 tokens (2,473,900 raw)
+✔ 20260610-090000-a1b2 story │───────────────────────────────────────────────────────────────────────────────────────────────────────────────────
+▶ 20260611-101500-c3d4 sweep │ story phase agent dev review tokens info
+ ▶ 20260612-141000-e5f6 story │ 2-1-search-index done - ×1 ×1 338,400 a1b2c3d4e5f6
+│ 2-2-typeahead done - ×1 ×2 283,200 b2c3d4e5f607
+│ 2-3-search-ranking review-running - ×1 ×1 224,300
+│ 2-4-saved-searches dev-running - ×1 ×0 81,900
+│ 2-5-search-analytics deferred - ×2 ×1 131,300 dev budget exhausted
+▎│▉
+─ Sprint ─────────────────────────│───────────────────────────────────────────────────────────────────────────────────────────────────────────────────
+▼ Epic 1 · 3/3 ✓│JournalLogAttention
+├ ✓ 1-auth│╸━━━━━━━╺━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+├ ✓ 2-session-tokens│▊▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▎
+├ ✓ 3-password-reset│▊11:19:17session-start task_id=2-1-search-index role=dev story_key=2-1-search-index▎
+└ ✓ retrospective│▊11:19:17story-done story_key=2-1-search-index commit=a1b2c3d4e5f6▎
+▼ Epic 2 · 2/5│▊11:19:17session-start task_id=2-2-typeahead role=dev story_key=2-2-typeahead▎
+├ ✓ 1-search-index│▊11:19:17review-cycle story_key=2-2-typeahead cycle=2 findings=3▎
+├ ✓ 2-typeahead│▊11:19:17story-done story_key=2-2-typeahead commit=b2c3d4e5f607▎
+├ ▶ 3-search-ranking│▊11:19:17story-start story_key=2-3-search-ranking epic=2▎
+├ ◆ 4-saved-searches│▊11:19:17spec-approved story_key=2-3-search-ranking tokens=1834▎
+└ ○ 5-search-analytics│▊11:19:17dev-done story_key=2-3-search-ranking tasks_done=3 tasks_total=3▎
+▼ Epic 3 · 0/2│▊11:19:17verify-ok story_key=2-3-search-ranking commands=pytest -q, ruff check .▎
+├ · 1-billing-portal│▊11:19:17review-start story_key=2-3-search-ranking role=review▎
+└ · 2-invoices│▊11:19:17escalation-preference story_key=2-3-search-ranking detail=reviewer used codex for the scoring math▎
+│▊11:19:17story-deferred story_key=2-5-search-analytics reason=dev budget exhausted▎
+│▊▎
+│▊▎
+─ Deferred Work ──────────────────│▊▎
+DW-1 Harden the OAuth token refre…│▊▎
+DW-2 Add pagination to the search…│▊▎
+DW-3 Replace the ad-hoc ranking w…│▊▎
+DW-4 Flaky retry in the indexer i…│▊▎
+DW-5 ✓ Polish the empty-state cop…│▊▎
+│▊▎
+│▊▎
+│▊▎
+│▊▎
+│▊▎
+│▊▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎
+ y copy ^w resize q quit r run s sweep e resume p review R resolve d decisions a attach x stop S soft-stop D delete A archive▏^p palette
diff --git a/docs/images/demo.gif b/docs/images/demo.gif
index df15f27a0..f63d09a72 100644
Binary files a/docs/images/demo.gif and b/docs/images/demo.gif differ
diff --git a/docs/images/settings.svg b/docs/images/settings.svg
index b46551ce9..ee1ce6612 100644
--- a/docs/images/settings.svg
+++ b/docs/images/settings.svg
@@ -19,210 +19,210 @@
font-weight: 700;
}
- .terminal-1170740573-matrix {
+ .terminal-1963529466-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-1170740573-title {
+ .terminal-1963529466-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-1170740573-r1 { fill: #c5c8c6 }
-.terminal-1170740573-r2 { fill: #e0e0e0 }
-.terminal-1170740573-r3 { fill: #a0a3a6 }
-.terminal-1170740573-r4 { fill: #121212 }
-.terminal-1170740573-r5 { fill: #6f6f6f }
-.terminal-1170740573-r6 { fill: #000000 }
-.terminal-1170740573-r7 { fill: #ffa62b;font-weight: bold }
-.terminal-1170740573-r8 { fill: #495259 }
+ .terminal-1963529466-r1 { fill: #c5c8c6 }
+.terminal-1963529466-r2 { fill: #e0e0e0 }
+.terminal-1963529466-r3 { fill: #a0a3a6 }
+.terminal-1963529466-r4 { fill: #121212 }
+.terminal-1963529466-r5 { fill: #6f6f6f }
+.terminal-1963529466-r6 { fill: #000000 }
+.terminal-1963529466-r7 { fill: #ffa62b;font-weight: bold }
+.terminal-1963529466-r8 { fill: #495259 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- bmad-loop
+ bmad-loop
-
+
-
- ⭘bmad-loop — ~/code/acme-search
-
-~/code/acme-search/.bmad-loop/policy.toml
-running engines snapshot policy at start — changes apply to new runs and resumes
-▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
-▶ gates — approval gates, escalation & retrospective behavior
-
-▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
-▶ review — separate adversarial review session toggle
-
-▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
-▶ stories — story-queue source: classic sprint board vs. folder+id dispatch
-
-▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
-▶ limits — cycle/attempt caps, timeout & token budget
-
-▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
-▶ verify — post-implementation verification commands
-
-▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
-▶ notify — desktop & file notifications
-
-▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
-▶ adapter — CLI client, model & bypass flags (base for all stages)
-
-▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
-▶ adapter.dev — dev-stage adapter overrides
-
-▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▇▇
-▶ adapter.review — review-stage adapter overrides
-
-▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
-▶ adapter.triage — triage-stage adapter overrides
-
-▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
-▶ sweep — deferred-work sweep automation
-
-▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
-▶ scm — git isolation, branching & merge-back
-
-▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
- esc back ^s save ^e expand/collapse all q quit r run s sweep e resume p review R resolve d decisions a attach x stop S soft-s▏^ppalette
+
+ ⭘bmad-loop — ~/code/acme-search
+
+~/code/acme-search/.bmad-loop/policy.toml
+running engines snapshot policy at start — changes apply to new runs and resumes
+▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+▶ gates — approval gates, escalation & retrospective behavior
+
+▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+▶ review — separate adversarial review session toggle
+
+▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+▶ stories — story-queue source: classic sprint board vs. folder+id dispatch
+
+▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+▶ limits — cycle/attempt caps, timeout & token budget
+
+▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+▶ verify — post-implementation verification commands
+
+▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+▶ notify — desktop & file notifications
+
+▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+▶ adapter — CLI client, model & bypass flags (base for all stages)
+
+▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+▶ adapter.dev — dev-stage adapter overrides
+
+▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▇▇
+▶ adapter.review — review-stage adapter overrides
+
+▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+▶ adapter.triage — triage-stage adapter overrides
+
+▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+▶ sweep — deferred-work sweep automation
+
+▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+▶ scm — git isolation, branching & merge-back
+
+▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+ esc back ^s save ^e expand/collapse all q quit r run s sweep e resume p review R resolve d decisions a attach x stop S soft-s▏^ppalette
diff --git a/docs/images/start-run-modal.png b/docs/images/start-run-modal.png
index e3e41a4c6..42abf0986 100644
Binary files a/docs/images/start-run-modal.png and b/docs/images/start-run-modal.png differ
diff --git a/docs/images/start-run-modal.svg b/docs/images/start-run-modal.svg
index f36d91883..2a2d6eff0 100644
--- a/docs/images/start-run-modal.svg
+++ b/docs/images/start-run-modal.svg
@@ -19,242 +19,242 @@
font-weight: 700;
}
- .terminal-3367129347-matrix {
+ .terminal-1675448583-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-3367129347-title {
+ .terminal-1675448583-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-3367129347-r1 { fill: #e0e0e0 }
-.terminal-3367129347-r2 { fill: #646464 }
-.terminal-3367129347-r3 { fill: #4a4c4d }
-.terminal-3367129347-r4 { fill: #c5c8c6 }
-.terminal-3367129347-r5 { fill: #0a2c4e }
-.terminal-3367129347-r6 { fill: #646464;font-weight: bold }
-.terminal-3367129347-r7 { fill: #476419 }
-.terminal-3367129347-r8 { fill: #494949 }
-.terminal-3367129347-r9 { fill: #0053aa }
-.terminal-3367129347-r10 { fill: #4a4a4a }
-.terminal-3367129347-r11 { fill: #704717;font-weight: bold }
-.terminal-3367129347-r12 { fill: #704717 }
-.terminal-3367129347-r13 { fill: #e0e0e0;font-weight: bold }
-.terminal-3367129347-r14 { fill: #1e1e1e }
-.terminal-3367129347-r15 { fill: #191919 }
-.terminal-3367129347-r16 { fill: #7f7f7f }
-.terminal-3367129347-r17 { fill: #161616 }
-.terminal-3367129347-r18 { fill: #0a1e2c }
-.terminal-3367129347-r19 { fill: #737373 }
-.terminal-3367129347-r20 { fill: #0a2c4e;font-weight: bold }
-.terminal-3367129347-r21 { fill: #1a1a1a }
-.terminal-3367129347-r22 { fill: #0b3a5f }
-.terminal-3367129347-r23 { fill: #121212 }
-.terminal-3367129347-r24 { fill: #141414 }
-.terminal-3367129347-r25 { fill: #232323 }
-.terminal-3367129347-r26 { fill: #2e5e68 }
-.terminal-3367129347-r27 { fill: #6c0a30 }
-.terminal-3367129347-r28 { fill: #242f38 }
-.terminal-3367129347-r29 { fill: #000f18 }
-.terminal-3367129347-r30 { fill: #9e9e9e }
-.terminal-3367129347-r31 { fill: #455969;font-weight: bold }
-.terminal-3367129347-r32 { fill: #6c0a30;font-weight: bold }
-.terminal-3367129347-r33 { fill: #4b4b4b }
-.terminal-3367129347-r34 { fill: #6db2ff }
-.terminal-3367129347-r35 { fill: #2d2d2d }
-.terminal-3367129347-r36 { fill: #ddedf9;font-weight: bold }
-.terminal-3367129347-r37 { fill: #004295 }
-.terminal-3367129347-r38 { fill: #0d0d0d }
-.terminal-3367129347-r39 { fill: #704d1c;font-weight: bold }
-.terminal-3367129347-r40 { fill: #282b2e }
+ .terminal-1675448583-r1 { fill: #e0e0e0 }
+.terminal-1675448583-r2 { fill: #646464 }
+.terminal-1675448583-r3 { fill: #4a4c4d }
+.terminal-1675448583-r4 { fill: #c5c8c6 }
+.terminal-1675448583-r5 { fill: #0a2c4e }
+.terminal-1675448583-r6 { fill: #646464;font-weight: bold }
+.terminal-1675448583-r7 { fill: #476419 }
+.terminal-1675448583-r8 { fill: #494949 }
+.terminal-1675448583-r9 { fill: #0053aa }
+.terminal-1675448583-r10 { fill: #4a4a4a }
+.terminal-1675448583-r11 { fill: #704717;font-weight: bold }
+.terminal-1675448583-r12 { fill: #704717 }
+.terminal-1675448583-r13 { fill: #e0e0e0;font-weight: bold }
+.terminal-1675448583-r14 { fill: #1e1e1e }
+.terminal-1675448583-r15 { fill: #191919 }
+.terminal-1675448583-r16 { fill: #7f7f7f }
+.terminal-1675448583-r17 { fill: #161616 }
+.terminal-1675448583-r18 { fill: #0a1e2c }
+.terminal-1675448583-r19 { fill: #737373 }
+.terminal-1675448583-r20 { fill: #0a2c4e;font-weight: bold }
+.terminal-1675448583-r21 { fill: #1a1a1a }
+.terminal-1675448583-r22 { fill: #0b3a5f }
+.terminal-1675448583-r23 { fill: #121212 }
+.terminal-1675448583-r24 { fill: #141414 }
+.terminal-1675448583-r25 { fill: #232323 }
+.terminal-1675448583-r26 { fill: #2e5e68 }
+.terminal-1675448583-r27 { fill: #6c0a30 }
+.terminal-1675448583-r28 { fill: #242f38 }
+.terminal-1675448583-r29 { fill: #000f18 }
+.terminal-1675448583-r30 { fill: #9e9e9e }
+.terminal-1675448583-r31 { fill: #455969;font-weight: bold }
+.terminal-1675448583-r32 { fill: #6c0a30;font-weight: bold }
+.terminal-1675448583-r33 { fill: #4b4b4b }
+.terminal-1675448583-r34 { fill: #6db2ff }
+.terminal-1675448583-r35 { fill: #2d2d2d }
+.terminal-1675448583-r36 { fill: #ddedf9;font-weight: bold }
+.terminal-1675448583-r37 { fill: #004295 }
+.terminal-1675448583-r38 { fill: #0d0d0d }
+.terminal-1675448583-r39 { fill: #704d1c;font-weight: bold }
+.terminal-1675448583-r40 { fill: #282b2e }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- bmad-loop
+ bmad-loop
-
+
-
- ⭘bmad-loop — ~/code/acme-search
-─Runs─────────────────────────────│20260611-101500-c3d4 [sweep] ▶ running started 2026-06-11T10:15:00 epic 2
- st run type │tasks 1█▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀█
-✔ 20260610-090000-a1b2 story │⚑ decis██ for the auth hardening epic?
- ▶ 20260611-101500-c3d4 sweep │ press█start run█
-▶ 20260612-141000-e5f6 story │────────██───────────────────────────────────────────
-│ story █▊▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▎█ review tokens info
-│ dw-sear█▊sprint mode — sprint-status.yaml▼▎█ ×1 153,300 2b7f0a91c440
-│█▊▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎█▉
-│────────█▊▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▎█───────────────────────────────────────────
-▎│Journal█▊stories mode: spec folder holding stories.yaml + ▎█
-─ Sprint ─────────────────────────│╸━━━━━━━█▊▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎█━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
-▼ Epic 1 · 3/3 ✓│▊▔▔▔▔▔▔▔█▊▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▎█▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▎
-├ ✓ 1-auth│▊21:08:█▊epic — blank for all (sprint mode)▎█▎
-├ ✓ 2-session-tokens│▊21:08:█▊▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎█ions=2▎
-├ ✓ 3-password-reset│▊21:08:█▊▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▎█2▎
-└ ✓ retrospective│▊21:08:█▊story — 3-1 / slug / full key (sprint), or story id ▎█h refresh race now, or hold it for the ▎
-▼ Epic 2 · 2/5│▊█▊▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎█▎
-├ ✓ 1-search-index│▊█▊▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▎█▎
-├ ✓ 2-typeahead│▊█▊max stories — blank for no limit▎█▎
-├ ▶ 3-search-ranking│▊█▊▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎█▎
-├ ◆ 4-saved-searches│▊█▊▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▎█▎
-└ ○ 5-search-analytics│▊█▊▐X▌ dry run (print the plan, spawn nothing) ▎█▎
-▼ Epic 3 · 0/2│▊█▊▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎█▎
-├ · 1-billing-portal│▊██▎
-└ · 2-invoices│▊█┌────────────────────────────────────────────────────────┐█▎
-│▊█│sprint mode — walks sprint-status.yaml│█▎
-│▊█└────────────────────────────────────────────────────────┘█▎
-│▊██▎
-─ Deferred Work ──────────────────│▊██▎
-DW-1 Harden the OAuth token refre…│▊██▎
-DW-2 Add pagination to the search…│▊██▎
-DW-3 Replace the ad-hoc ranking w…│▊██▎
-DW-4 Flaky retry in the indexer i…│▊██▎
-DW-5 ✓ Polish the empty-state cop…│▊█▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔█▎
-│▊█ start cancel █▎
-│▊█▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█▎
-│▊██▎
-│▊█▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄█▎
-│▊▎
-│▊▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎
- y copy ^w resize q quit r run s sweep e resume p review R resolve d decisions a attach x stop S soft-stop D delete A archive▏^p palette
+
+ ⭘bmad-loop — ~/code/acme-search
+─Runs─────────────────────────────│20260611-101500-c3d4 [sweep] ▶ running started 2026-06-11T10:15:00 epic 2
+ st run type │tasks 1█▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀█
+✔ 20260610-090000-a1b2 story │⚑ decis██ for the auth hardening epic?
+ ▶ 20260611-101500-c3d4 sweep │ press█start run█
+▶ 20260612-141000-e5f6 story │────────██───────────────────────────────────────────
+│ story █▊▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▎█ review tokens info
+│ dw-sear█▊sprint mode — sprint-status.yaml▼▎█ ×1 153,300 2b7f0a91c440
+│█▊▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎█▉
+│────────█▊▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▎█───────────────────────────────────────────
+▎│Journal█▊stories mode: spec folder holding stories.yaml + ▎█
+─ Sprint ─────────────────────────│╸━━━━━━━█▊▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎█━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+▼ Epic 1 · 3/3 ✓│▊▔▔▔▔▔▔▔█▊▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▎█▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▎
+├ ✓ 1-auth│▊11:19:█▊epic — blank for all (sprint mode)▎█▎
+├ ✓ 2-session-tokens│▊11:19:█▊▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎█ions=2▎
+├ ✓ 3-password-reset│▊11:19:█▊▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▎█2▎
+└ ✓ retrospective│▊11:19:█▊story — 3-1 / slug / full key (sprint), or story id ▎█h refresh race now, or hold it for the ▎
+▼ Epic 2 · 2/5│▊█▊▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎█▎
+├ ✓ 1-search-index│▊█▊▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▎█▎
+├ ✓ 2-typeahead│▊█▊max stories — blank for no limit▎█▎
+├ ▶ 3-search-ranking│▊█▊▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎█▎
+├ ◆ 4-saved-searches│▊█▊▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▎█▎
+└ ○ 5-search-analytics│▊█▊▐X▌ dry run (print the plan, spawn nothing) ▎█▎
+▼ Epic 3 · 0/2│▊█▊▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎█▎
+├ · 1-billing-portal│▊██▎
+└ · 2-invoices│▊█┌────────────────────────────────────────────────────────┐█▎
+│▊█│sprint mode — walks sprint-status.yaml│█▎
+│▊█└────────────────────────────────────────────────────────┘█▎
+│▊██▎
+─ Deferred Work ──────────────────│▊██▎
+DW-1 Harden the OAuth token refre…│▊██▎
+DW-2 Add pagination to the search…│▊██▎
+DW-3 Replace the ad-hoc ranking w…│▊██▎
+DW-4 Flaky retry in the indexer i…│▊██▎
+DW-5 ✓ Polish the empty-state cop…│▊█▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔█▎
+│▊█ start cancel █▎
+│▊█▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█▎
+│▊██▎
+│▊█▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄█▎
+│▊▎
+│▊▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎
+ y copy ^w resize q quit r run s sweep e resume p review R resolve d decisions a attach x stop S soft-stop D delete A archive▏^p palette
diff --git a/docs/images/sweep-decision.png b/docs/images/sweep-decision.png
index 3815da969..ceec37c09 100644
Binary files a/docs/images/sweep-decision.png and b/docs/images/sweep-decision.png differ
diff --git a/docs/images/sweep-decision.svg b/docs/images/sweep-decision.svg
index dddf55b40..f7b984522 100644
--- a/docs/images/sweep-decision.svg
+++ b/docs/images/sweep-decision.svg
@@ -19,230 +19,230 @@
font-weight: 700;
}
- .terminal-1950470266-matrix {
+ .terminal-1599328406-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-1950470266-title {
+ .terminal-1599328406-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-1950470266-r1 { fill: #c5c8c6 }
-.terminal-1950470266-r2 { fill: #e0e0e0 }
-.terminal-1950470266-r3 { fill: #a0a3a6 }
-.terminal-1950470266-r4 { fill: #0053aa }
-.terminal-1950470266-r5 { fill: #e2e2e2;font-weight: bold }
-.terminal-1950470266-r6 { fill: #e0e0e0;font-weight: bold }
-.terminal-1950470266-r7 { fill: #98e024 }
-.terminal-1950470266-r8 { fill: #9d9d9d }
-.terminal-1950470266-r9 { fill: #fd971f }
-.terminal-1950470266-r10 { fill: #a1a1a1 }
-.terminal-1950470266-r11 { fill: #fd971f;font-weight: bold }
-.terminal-1950470266-r12 { fill: #ddedf9;font-weight: bold }
-.terminal-1950470266-r13 { fill: #1e1e1e }
-.terminal-1950470266-r14 { fill: #003054 }
-.terminal-1950470266-r15 { fill: #272727 }
-.terminal-1950470266-r16 { fill: #797979 }
-.terminal-1950470266-r17 { fill: #0053aa;font-weight: bold }
-.terminal-1950470266-r18 { fill: #262626 }
-.terminal-1950470266-r19 { fill: #0178d4 }
-.terminal-1950470266-r20 { fill: #121212 }
-.terminal-1950470266-r21 { fill: #191919 }
-.terminal-1950470266-r22 { fill: #3e3e3e }
-.terminal-1950470266-r23 { fill: #9e9e9e }
-.terminal-1950470266-r24 { fill: #58d1eb }
-.terminal-1950470266-r25 { fill: #f4005f }
-.terminal-1950470266-r26 { fill: #f4005f;font-weight: bold }
-.terminal-1950470266-r27 { fill: #ffa62b;font-weight: bold }
-.terminal-1950470266-r28 { fill: #495259 }
+ .terminal-1599328406-r1 { fill: #c5c8c6 }
+.terminal-1599328406-r2 { fill: #e0e0e0 }
+.terminal-1599328406-r3 { fill: #a0a3a6 }
+.terminal-1599328406-r4 { fill: #0053aa }
+.terminal-1599328406-r5 { fill: #e2e2e2;font-weight: bold }
+.terminal-1599328406-r6 { fill: #e0e0e0;font-weight: bold }
+.terminal-1599328406-r7 { fill: #98e024 }
+.terminal-1599328406-r8 { fill: #9d9d9d }
+.terminal-1599328406-r9 { fill: #fd971f }
+.terminal-1599328406-r10 { fill: #a1a1a1 }
+.terminal-1599328406-r11 { fill: #fd971f;font-weight: bold }
+.terminal-1599328406-r12 { fill: #ddedf9;font-weight: bold }
+.terminal-1599328406-r13 { fill: #1e1e1e }
+.terminal-1599328406-r14 { fill: #003054 }
+.terminal-1599328406-r15 { fill: #272727 }
+.terminal-1599328406-r16 { fill: #797979 }
+.terminal-1599328406-r17 { fill: #0053aa;font-weight: bold }
+.terminal-1599328406-r18 { fill: #262626 }
+.terminal-1599328406-r19 { fill: #0178d4 }
+.terminal-1599328406-r20 { fill: #121212 }
+.terminal-1599328406-r21 { fill: #191919 }
+.terminal-1599328406-r22 { fill: #3e3e3e }
+.terminal-1599328406-r23 { fill: #9e9e9e }
+.terminal-1599328406-r24 { fill: #58d1eb }
+.terminal-1599328406-r25 { fill: #f4005f }
+.terminal-1599328406-r26 { fill: #f4005f;font-weight: bold }
+.terminal-1599328406-r27 { fill: #ffa62b;font-weight: bold }
+.terminal-1599328406-r28 { fill: #495259 }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- bmad-loop
+ bmad-loop
-
+
-
- ⭘bmad-loop — ~/code/acme-search
-─Runs─────────────────────────────│20260611-101500-c3d4 [sweep] ▶ running started 2026-06-11T10:15:00 epic 2
- st run type │tasks 1 done 1 deferred 0 escalated 0 153,300 tokens (369,300 raw)
-✔ 20260610-090000-a1b2 story │⚑ decision needed: DW-1 — Reopen the OAuth refresh race now, or hold it for the auth hardening epic?
- ▶ 20260611-101500-c3d4 sweep │ press a to attach and answer
-▶ 20260612-141000-e5f6 story │───────────────────────────────────────────────────────────────────────────────────────────────────────────────────
-│ story phase agent dev review tokens info
-│ dw-search-pagination done - ×1 ×1 153,300 2b7f0a91c440
-│▉
-│───────────────────────────────────────────────────────────────────────────────────────────────────────────────────
-▎│JournalLogAttention
-─ Sprint ─────────────────────────│╸━━━━━━━╺━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
-▼ Epic 1 · 3/3 ✓│▊▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▎
-├ ✓ 1-auth│▊21:08:11sweep-start cycle=1▎
-├ ✓ 2-session-tokens│▊21:08:11triage-done bundles=1 already_resolved=1 decisions=2▎
-├ ✓ 3-password-reset│▊21:08:11bundle-done bundle=search-pagination dw_ids=DW-2▎
-└ ✓ retrospective│▊21:08:11decision-pending dw_id=DW-1 question=Reopen the OAuth refresh race now, or hold it for the ▎
-▼ Epic 2 · 2/5│▊auth … ▎
-├ ✓ 1-search-index│▊▎
-├ ✓ 2-typeahead│▊▎
-├ ▶ 3-search-ranking│▊▎
-├ ◆ 4-saved-searches│▊▎
-└ ○ 5-search-analytics│▊▎
-▼ Epic 3 · 0/2│▊▎
-├ · 1-billing-portal│▊▎
-└ · 2-invoices│▊▎
-│▊▎
-│▊▎
-│▊▎
-─ Deferred Work ──────────────────│▊▎
-DW-1 Harden the OAuth token refre…│▊▎
-DW-2 Add pagination to the search…│▊▎
-DW-3 Replace the ad-hoc ranking w…│▊▎
-DW-4 Flaky retry in the indexer i…│▊▎
-DW-5 ✓ Polish the empty-state cop…│▊▎
-│▊▎
-│▊▎
-│▊▎
-│▊▎
-│▊▎
-│▊▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎
- y copy ^w resize q quit r run s sweep e resume p review R resolve d decisions a attach x stop S soft-stop D delete A archive▏^p palette
+
+ ⭘bmad-loop — ~/code/acme-search
+─Runs─────────────────────────────│20260611-101500-c3d4 [sweep] ▶ running started 2026-06-11T10:15:00 epic 2
+ st run type │tasks 1 done 1 deferred 0 escalated 0 153,300 tokens (369,300 raw)
+✔ 20260610-090000-a1b2 story │⚑ decision needed: DW-1 — Reopen the OAuth refresh race now, or hold it for the auth hardening epic?
+ ▶ 20260611-101500-c3d4 sweep │ press a to attach and answer
+▶ 20260612-141000-e5f6 story │───────────────────────────────────────────────────────────────────────────────────────────────────────────────────
+│ story phase agent dev review tokens info
+│ dw-search-pagination done - ×1 ×1 153,300 2b7f0a91c440
+│▉
+│───────────────────────────────────────────────────────────────────────────────────────────────────────────────────
+▎│JournalLogAttention
+─ Sprint ─────────────────────────│╸━━━━━━━╺━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+▼ Epic 1 · 3/3 ✓│▊▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▎
+├ ✓ 1-auth│▊11:19:17sweep-start cycle=1▎
+├ ✓ 2-session-tokens│▊11:19:17triage-done bundles=1 already_resolved=1 decisions=2▎
+├ ✓ 3-password-reset│▊11:19:17bundle-done bundle=search-pagination dw_ids=DW-2▎
+└ ✓ retrospective│▊11:19:17decision-pending dw_id=DW-1 question=Reopen the OAuth refresh race now, or hold it for the ▎
+▼ Epic 2 · 2/5│▊auth … ▎
+├ ✓ 1-search-index│▊▎
+├ ✓ 2-typeahead│▊▎
+├ ▶ 3-search-ranking│▊▎
+├ ◆ 4-saved-searches│▊▎
+└ ○ 5-search-analytics│▊▎
+▼ Epic 3 · 0/2│▊▎
+├ · 1-billing-portal│▊▎
+└ · 2-invoices│▊▎
+│▊▎
+│▊▎
+│▊▎
+─ Deferred Work ──────────────────│▊▎
+DW-1 Harden the OAuth token refre…│▊▎
+DW-2 Add pagination to the search…│▊▎
+DW-3 Replace the ad-hoc ranking w…│▊▎
+DW-4 Flaky retry in the indexer i…│▊▎
+DW-5 ✓ Polish the empty-state cop…│▊▎
+│▊▎
+│▊▎
+│▊▎
+│▊▎
+│▊▎
+│▊▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎
+ y copy ^w resize q quit r run s sweep e resume p review R resolve d decisions a attach x stop S soft-stop D delete A archive▏^p palette
diff --git a/module.yaml b/module.yaml
index 2a2d12643..7040a022b 100644
--- a/module.yaml
+++ b/module.yaml
@@ -1,7 +1,7 @@
code: bmad-loop
name: BMAD Loop Skills
description: "Automation-mode skills driven by the bmad-loop orchestrator: interactive escalation resolution (bmad-loop-resolve) and deferred-work sweep triage (bmad-loop-sweep) — the inner dev primitive (which self-reviews and commits) is the upstream bmad-build-auto skill (bmad-dev-auto on pre-rename releases)"
-module_version: 0.11.1
+module_version: 0.12.0
default_selected: false
module_greeting: >
BMAD Loop installed — both the automation skills and the
diff --git a/pyproject.toml b/pyproject.toml
index 90b005ea4..88a7252c6 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
[project]
name = "bmad-loop"
-version = "0.11.1"
+version = "0.12.0"
description = "Deterministic ralph-loop orchestrator for the BMAD implementation phase"
readme = "README.md"
license = "MIT"
diff --git a/scripts/release.py b/scripts/release.py
index d5f23efdd..fc5fc528e 100644
--- a/scripts/release.py
+++ b/scripts/release.py
@@ -141,6 +141,40 @@ def has_curated_section(text: str, version: str) -> bool:
return bool(body)
+# GitHub rejects a release whose body exceeds this many characters with
+# `HTTP 422: body is too long (maximum is 125000 characters)`. `gh release create`
+# creates the tag before the API rejects the body, so an unbounded section leaves a
+# tag with no release behind — and a re-run then sees the tag and refuses to publish.
+GITHUB_NOTES_LIMIT = 125_000
+
+
+def bound_release_notes(notes: str, version: str, url: str, limit: int = GITHUB_NOTES_LIMIT) -> str:
+ """Return ``notes`` if they fit GitHub's release-body limit, else a prefix that
+ does, cut at an entry boundary and closed with a pointer at the full section.
+
+ The CHANGELOG stays the record; the release body is a view of it. Truncating
+ the view is preferable to curating the record down to the limit."""
+ if len(notes) <= limit:
+ return notes
+ footer = (
+ "\n\n---\n\n_Release notes truncated at GitHub's "
+ f"{limit:,}-character limit. The full section is in "
+ f"[CHANGELOG.md]({url}/blob/v{version}/CHANGELOG.md)._"
+ )
+ budget = limit - len(footer)
+ # Cut where the next entry begins so no entry is sliced mid-sentence; fall back
+ # to a hard cut only when the very first entry alone overflows the budget.
+ cut = notes.rfind("\n- ", 0, budget)
+ head = notes[:cut] if cut > 0 else notes[:budget]
+ head = head.rstrip()
+ # A `### Type` heading left with no entries under it points at nothing — drop it.
+ lines = head.splitlines()
+ while lines and lines[-1].startswith("#"):
+ lines.pop()
+ head = "\n".join(lines).rstrip()
+ return head + footer
+
+
# The shape a promoted heading must take: `## [X.Y.Z] — YYYY-MM-DD`. `section_re`
# accepts any suffix after `]`, so nothing else notices a dateless or garbled one.
# Every release heading in CHANGELOG.md matches this — the sole exception is the
@@ -374,7 +408,7 @@ def cmd_prepare(args: argparse.Namespace) -> int:
changelog = CHANGELOG.read_text()
problems: list[str] = []
if branch == "main":
- problems.append("on `main`; run prepare from a release/feature branch")
+ problems.append("on `main`; run prepare from a feature branch")
if tag_exists(tag):
problems.append(f"tag {tag} already exists")
if not version_gt(version, canonical):
@@ -463,6 +497,21 @@ def cmd_prepare(args: argparse.Namespace) -> int:
print(f"prepare {tag} on branch '{branch}' (was {canonical}, last tag {last_tag or 'none'})")
print(f"assets: {reason}")
+ if branch.startswith("release/"):
+ # release.yml publishes on push to `release/*` (maintenance branches), so a PR
+ # branch by that name tags its own pre-merge commit the moment it is pushed.
+ print(
+ f"warning: '{branch}' matches release.yml's `release/*` trigger — pushing it "
+ f"publishes {tag} from this branch immediately, before any PR merges. That is "
+ "the maintenance-branch flow; a PR aimed at `main` belongs on `chore/release-X.Y.Z`"
+ )
+ section_len = len(extract_section(changelog, version) or "")
+ if section_len > GITHUB_NOTES_LIMIT:
+ print(
+ f"warning: the `## [{version}]` section is {section_len:,} chars; GitHub caps release "
+ f"notes at {GITHUB_NOTES_LIMIT:,}, so `publish` will truncate the release body and "
+ "link the full CHANGELOG section"
+ )
if args.dry_run:
print("\n[dry-run] planned actions:")
@@ -523,6 +572,14 @@ def cmd_publish(args: argparse.Namespace) -> int:
if not notes:
_die(f"no CHANGELOG `## [{version}]` section — cannot publish release notes")
+ bounded = bound_release_notes(notes, version, repo_url())
+ if bounded is not notes:
+ print(
+ f"release notes: CHANGELOG section is {len(notes):,} chars, over GitHub's "
+ f"{GITHUB_NOTES_LIMIT:,} limit — publishing a truncated body that links the full section"
+ )
+ notes = bounded
+
sha = _git_out("rev-parse", "HEAD")
if args.dry_run:
print(f"[dry-run] would create release {tag} at {sha[:12]} with notes:\n")
diff --git a/src/bmad_loop/__init__.py b/src/bmad_loop/__init__.py
index 6ea1a1b57..8670fad44 100644
--- a/src/bmad_loop/__init__.py
+++ b/src/bmad_loop/__init__.py
@@ -8,4 +8,4 @@
directory under .bmad-loop/runs/.
"""
-__version__ = "0.11.1"
+__version__ = "0.12.0"
diff --git a/src/bmad_loop/data/skills/bmad-loop-setup/assets/module.yaml b/src/bmad_loop/data/skills/bmad-loop-setup/assets/module.yaml
index 2a2d12643..7040a022b 100644
--- a/src/bmad_loop/data/skills/bmad-loop-setup/assets/module.yaml
+++ b/src/bmad_loop/data/skills/bmad-loop-setup/assets/module.yaml
@@ -1,7 +1,7 @@
code: bmad-loop
name: BMAD Loop Skills
description: "Automation-mode skills driven by the bmad-loop orchestrator: interactive escalation resolution (bmad-loop-resolve) and deferred-work sweep triage (bmad-loop-sweep) — the inner dev primitive (which self-reviews and commits) is the upstream bmad-build-auto skill (bmad-dev-auto on pre-rename releases)"
-module_version: 0.11.1
+module_version: 0.12.0
default_selected: false
module_greeting: >
BMAD Loop installed — both the automation skills and the
diff --git a/tests/test_release.py b/tests/test_release.py
index 85f1d9317..9b4a10205 100644
--- a/tests/test_release.py
+++ b/tests/test_release.py
@@ -361,17 +361,103 @@ def test_publish_passes_check_false_so_the_swallow_inspects_the_rc(monkeypatch,
assert seen["check"] is False
+# --- publish bounds the release body -------------------------------------- #
+# GitHub rejects a body over 125,000 chars with HTTP 422 *after* `gh` has created the
+# tag, which strands a tag with no release (v0.12.0's first publish). The body is a
+# view of the CHANGELOG, so it is the body that yields, at an entry boundary.
+def _long_section(entries: int, *, width: int = 200) -> str:
+ return "### Fixed\n\n" + "\n".join(f"- Entry {i:05d}. " + "x" * width for i in range(entries))
+
+
+def test_bound_release_notes_returns_short_notes_untouched():
+ notes = "### Fixed\n\n- **A thing.** It no longer breaks."
+ assert release.bound_release_notes(notes, "0.5.0", REPO_URL) is notes
+
+
+def test_bound_release_notes_cuts_at_an_entry_boundary_and_links_the_changelog():
+ notes = _long_section(40)
+ out = release.bound_release_notes(notes, "0.5.0", REPO_URL, limit=2_000)
+ assert len(out) <= 2_000
+ body, _, footer = out.partition("\n\n---\n\n")
+ # Every surviving line is a whole entry — none sliced mid-sentence.
+ assert all(line.startswith("- Entry ") and line.endswith("x") for line in body.splitlines()[2:])
+ assert "- Entry 00000." in body
+ assert f"{REPO_URL}/blob/v0.5.0/CHANGELOG.md" in footer
+ assert "truncated at GitHub's 2,000-character limit" in footer
+
+
+def test_bound_release_notes_drops_a_heading_left_with_no_entries():
+ notes = "### Added\n\n- " + "a" * 400 + "\n\n### Fixed\n\n- " + "b" * 400
+ out = release.bound_release_notes(notes, "0.5.0", REPO_URL, limit=700)
+ body = out.partition("\n\n---\n\n")[0]
+ assert "### Added" in body
+ assert "### Fixed" not in body # its only entry did not fit, so the heading goes too
+
+
+def test_bound_release_notes_hard_cuts_when_even_the_first_entry_overflows():
+ notes = "- " + "z" * 5_000
+ out = release.bound_release_notes(notes, "0.5.0", REPO_URL, limit=600)
+ assert len(out) <= 600
+ assert out.startswith("- zzz")
+
+
+def test_publish_sends_a_bounded_body_and_says_so(monkeypatch, capsys, tmp_path):
+ cl = tmp_path / "CHANGELOG.md"
+ cl.write_text(SAMPLE.replace("- **A thing.** It no longer breaks.", _long_section(1_000)))
+ monkeypatch.setattr(release, "CHANGELOG", cl)
+ monkeypatch.setattr(release.sync_version, "read_canonical", lambda: "0.5.0")
+ monkeypatch.setattr(release, "tag_exists", lambda tag: False)
+ monkeypatch.setattr(release, "repo_url", lambda: REPO_URL)
+ monkeypatch.setattr(release, "_git_out", lambda *a: "deadbeef" * 5)
+ monkeypatch.setattr(release.shutil, "which", lambda name: f"/usr/bin/{name}")
+ seen: dict[str, object] = {}
+
+ def fake_run(*a, **kw):
+ seen.update(kw)
+ return SimpleNamespace(returncode=0, stdout="", stderr="")
+
+ monkeypatch.setattr(release.subprocess, "run", fake_run)
+ rc = release.cmd_publish(SimpleNamespace(dry_run=False))
+ assert rc == 0
+ sent = seen["input"]
+ assert isinstance(sent, str) and len(sent) <= release.GITHUB_NOTES_LIMIT
+ assert f"{REPO_URL}/blob/v0.5.0/CHANGELOG.md" in sent
+ assert "over GitHub's 125,000 limit" in capsys.readouterr().out
+
+
+def test_prepare_warns_on_a_release_star_branch(monkeypatch, capsys, tmp_path):
+ # `_prepare_dry_run` pins the branch to `release/0.5.0`, the shape release.yml publishes
+ # from on push — the warning is what tells a PR author they are about to self-publish.
+ assert _prepare_dry_run(monkeypatch, tmp_path, PROMOTED) == 0
+ out = capsys.readouterr().out
+ assert "matches release.yml's `release/*` trigger" in out
+
+
+def test_prepare_stays_quiet_on_a_chore_branch(monkeypatch, capsys, tmp_path):
+ assert _prepare_dry_run(monkeypatch, tmp_path, PROMOTED, branch="chore/release-0.5.0") == 0
+ assert "release/*" not in capsys.readouterr().out
+
+
+def test_prepare_warns_when_the_section_will_be_truncated(monkeypatch, capsys, tmp_path):
+ long_promoted = PROMOTED.replace("- **A thing.** It no longer breaks.", _long_section(1_000))
+ assert _prepare_dry_run(monkeypatch, tmp_path, long_promoted) == 0
+ out = capsys.readouterr().out
+ assert "warning:" in out and "will truncate the release body" in out
+
+
# --- prepare refuses an unpromoted changelog -------------------------------- #
# `--dry-run` still runs every precondition before returning, so it drives the guard
# without mutating anything; `no_assets` + an absent `trunk` keep the whole path
# subprocess-free.
-def _prepare_dry_run(monkeypatch, tmp_path, changelog_text, *, version="0.5.0"):
+def _prepare_dry_run(
+ monkeypatch, tmp_path, changelog_text, *, version="0.5.0", branch="release/0.5.0"
+):
cl = tmp_path / "CHANGELOG.md"
cl.write_text(changelog_text)
monkeypatch.setattr(release, "CHANGELOG", cl)
monkeypatch.setattr(release.sync_version, "read_canonical", lambda: "0.4.3")
monkeypatch.setattr(release, "repo_url", lambda: REPO_URL)
- monkeypatch.setattr(release, "current_branch", lambda: "release/0.5.0")
+ monkeypatch.setattr(release, "current_branch", lambda: branch)
monkeypatch.setattr(release, "last_release_tag", lambda: "v0.4.3")
monkeypatch.setattr(release, "tag_exists", lambda tag: False)
monkeypatch.setattr(release, "dirty_paths", lambda: ["CHANGELOG.md"])
diff --git a/uv.lock b/uv.lock
index 5c2343e4a..5e03166b4 100644
--- a/uv.lock
+++ b/uv.lock
@@ -17,7 +17,7 @@ wheels = [
[[package]]
name = "bmad-loop"
-version = "0.11.1"
+version = "0.12.0"
source = { editable = "." }
dependencies = [
{ name = "psutil", marker = "sys_platform == 'win32'" },