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-reset21:08:11session-start           task_id=2-1-search-index  role=dev  story_key=2-1-search-index -└ ✓ retrospective21:08:11story-done              story_key=2-1-search-index  commit=a1b2c3d4e5f6 -▼ Epic 2 · 2/521:08:11session-start           task_id=2-2-typeahead  role=dev  story_key=2-2-typeahead -├ ✓ 1-search-index21:08:11review-cycle            story_key=2-2-typeahead  cycle=2  findings=3 -├ ✓ 2-typeahead21:08:11story-done              story_key=2-2-typeahead  commit=b2c3d4e5f607 -├ ▶ 3-search-ranking21:08:11story-start             story_key=2-3-search-ranking  epic=2 -├ ◆ 4-saved-searches21:08:11spec-approved           story_key=2-3-search-ranking  tokens=1834 -└ ○ 5-search-analytics21:08:11dev-done                story_key=2-3-search-ranking  tasks_done=3  tasks_total=3 -▼ Epic 3 · 0/221:08:11verify-ok               story_key=2-3-search-ranking  commands=pytest -q, ruff check . -├ · 1-billing-portal21:08:11review-start            story_key=2-3-search-ranking  role=review -└ · 2-invoices21: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-reset11:19:17session-start           task_id=2-1-search-index  role=dev  story_key=2-1-search-index +└ ✓ retrospective11:19:17story-done              story_key=2-1-search-index  commit=a1b2c3d4e5f6 +▼ Epic 2 · 2/511:19:17session-start           task_id=2-2-typeahead  role=dev  story_key=2-2-typeahead +├ ✓ 1-search-index11:19:17review-cycle            story_key=2-2-typeahead  cycle=2  findings=3 +├ ✓ 2-typeahead11:19:17story-done              story_key=2-2-typeahead  commit=b2c3d4e5f607 +├ ▶ 3-search-ranking11:19:17story-start             story_key=2-3-search-ranking  epic=2 +├ ◆ 4-saved-searches11:19:17spec-approved           story_key=2-3-search-ranking  tokens=1834 +└ ○ 5-search-analytics11:19:17dev-done                story_key=2-3-search-ranking  tasks_done=3  tasks_total=3 +▼ Epic 3 · 0/211:19:17verify-ok               story_key=2-3-search-ranking  commands=pytest -q, ruff check . +├ · 1-billing-portal11:19:17review-start            story_key=2-3-search-ranking  role=review +└ · 2-invoices11: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   pressstart run - 20260612-141000-e5f6  story ─────────────────────────────────────────────────── - story  ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ review  tokens        info                 - dw-searsprint mode — sprint-status.yaml ×1      153,300       2b7f0a91c440         -▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ -────────▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔─────────────────────────────────────────── -Journalstories mode: spec folder holding stories.yaml +  -─ Sprint ─────────────────────────━━━━━━━▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -▼ Epic 1 · 3/3 ✓▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ -├ ✓ 1-auth21:08:epic — blank for all (sprint mode) -├ ✓ 2-session-tokens21:08:▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ions=2 -├ ✓ 3-password-reset21:08:▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔2 -└ ✓ retrospective21: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-typeaheadmax stories — blank for no limit -├ ▶ 3-search-ranking▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ -├ ◆ 4-saved-searches▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ -└ ○ 5-search-analyticsX 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   pressstart run + 20260612-141000-e5f6  story ─────────────────────────────────────────────────── + story  ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ review  tokens        info                 + dw-searsprint mode — sprint-status.yaml ×1      153,300       2b7f0a91c440         +▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ +────────▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔─────────────────────────────────────────── +Journalstories mode: spec folder holding stories.yaml +  +─ Sprint ─────────────────────────━━━━━━━▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +▼ Epic 1 · 3/3 ✓▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ +├ ✓ 1-auth11:19:epic — blank for all (sprint mode) +├ ✓ 2-session-tokens11:19:▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ions=2 +├ ✓ 3-password-reset11:19:▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔2 +└ ✓ retrospective11: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-typeaheadmax stories — blank for no limit +├ ▶ 3-search-ranking▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ +├ ◆ 4-saved-searches▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ +└ ○ 5-search-analyticsX 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-auth21:08:11sweep-start             cycle=1 -├ ✓ 2-session-tokens21:08:11triage-done             bundles=1  already_resolved=1  decisions=2 -├ ✓ 3-password-reset21:08:11bundle-done             bundle=search-pagination  dw_ids=DW-2 -└ ✓ retrospective21:08:11decision-pending        dw_id=DW-1  question=Reopen the OAuth refresh race now, or hold it for the    -▼ Epic 2 · 2/5auth …                                                                        -├ ✓ 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-auth11:19:17sweep-start             cycle=1 +├ ✓ 2-session-tokens11:19:17triage-done             bundles=1  already_resolved=1  decisions=2 +├ ✓ 3-password-reset11:19:17bundle-done             bundle=search-pagination  dw_ids=DW-2 +└ ✓ retrospective11:19:17decision-pending        dw_id=DW-1  question=Reopen the OAuth refresh race now, or hold it for the    +▼ Epic 2 · 2/5auth …                                                                        +├ ✓ 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'" },