Skip to content

feat(resolve): intent-gap patch-restore re-drive + inline-review-layer preflight (re-cut)#86

Merged
pbean merged 19 commits into
mainfrom
feat/intent-gap-patch-restore-v2
Jul 8, 2026
Merged

feat(resolve): intent-gap patch-restore re-drive + inline-review-layer preflight (re-cut)#86
pbean merged 19 commits into
mainfrom
feat/intent-gap-patch-restore-v2

Conversation

@pbean

@pbean pbean commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

Re-cut of 29ba684 (the tip of feat/intent-gap-patch-restore) onto post-stories main (#65 / #77 / #82). Supersedes #76, which was closed unmerged pending the post-hoc audit — this PR carries the original work plus the audit's close-out items for it. Sweep-workflow extension remains tracked in #75.

What the original commit does (unchanged in intent)

  • Phase A — preflight: bmad-loop validate/run-start now require bmad-review-verification-gap (BMAD-METHOD#2550) and a customize.toml marker in bmad-dev-auto; docs aligned.
  • Phase B — intent-gap patch-restore (BMAD-METHOD#2564): when a review-stage intent gap halt saved the attempted change as a patch and the human confirms the reading was correct, bmad-loop resolve latches the patch (--restore-patch / resolution.json), re-arms the spec to in-review, and the engine re-applies it onto the baseline before each fresh dispatch, so the session resumes review instead of re-implementing.

Re-cut conflict resolution (runs.py, model.py — as the audit mapped)

Union of both features, folding the restore branch into the stories-hardened re-arm: the in-review flip joins the UnicodeDecodeError-guarded status-flip path, story-escalation-resolved carries baseline and restore, and the stories sentinel-clear branch is preserved.

Audit close-out items (new in this PR, one commit each)

  • T1 guard (runs.rearm_escalation + tests): a patch-restore re-arm on a sentinel-wedged stories task is rejected before any mutation — a sentinel is a pre-planning halt, so there is no attempt to restore and the re-dispatch is a planning leg. A real stories spec stays a legitimate restore target.
  • B4d (audit MEDIUM-1): cmd_resolve rejects --restore-patch for worktree-isolation runs (restore is in-place-only; the latch would silently never restore) + the missing outside-project-but-existing-file rejection test (LOW-5).
  • T2 reconcile: with fix(runs): advance the re-arm baseline so a resolved re-drive cannot orphan resolution commits #78 advancing the re-arm baseline to the post-resolve HEAD, "applies cleanly by construction" is now conditional — verify.apply_patch docstring rewritten, resolve SKILL.md warns that resolution commits overlapping the patch's files make the restore fail loudly (omit restore_patch when the resolution already carries the work), and a combined engine test drives the full interaction (resolution commit → failed apply → re-escalation, no session dispatched, resolution commit untouched).
  • T4 gate fix: the latched patch file is untracked halt residue that survives every reset; post-fix(verify): stop treating ledger/spec-only stories as "no changes since baseline" #79's file-granular excludes its mere presence would vacuously pass the proof-of-work gate. It is now excluded at all three call sites (sprint / bundle / stories) + tests.
  • LOW-4 docs: the two stale lines the source plan targeted (README "two-layer review", FEATURES "2 parallel layers") → three layers.
  • E2E integration: the stories/sprint sandbox scaffolds (written on the stories branch) now provision the new preflight prereqs — found by running the merged suites together for the first time.

Final-review round (adversarial multi-agent review of this PR, one commit each)

The review re-verified every audit close-out claim above against the diff (all real) and every CI/CodeRabbit signal (green), then found and fixed:

  • F1 — sprint-mode restore could never reach step-04. The re-drive dispatched the bare story key, so step-01's spec-pointer early exit (in-review → step-04) never fired and its dirty-tree check HALTed blocked on the very diff the orchestrator had just applied — every sprint restore burned one session and insta-re-escalated. The dispatch now points at task.spec_file when a restore is latched (stories folder+id dispatch already early-exits and is untouched).
  • F2 — the spec's own baseline went stale on the restore path. Re-arm advances task.baseline_commit, but only step-03 stamps baseline_revision and the in-review route skips it — the re-driven step-04 would diff (and on an intent-gap/bad-spec re-triage, revert) since the ORIGINAL sha, clawing back the non-overlapping resolve commits the baseline advance blesses. Re-arm now re-stamps baseline_revision on the restore path (new verify.set_frontmatter_field).
  • F3 — silent restore drops. A corrupt resolution.json (quieter than an absent one) and --restore-patch "" both silently downgraded to a from-scratch re-arm that consumes the escalation — the confirmed restore decision was unrecoverable. Both now abort loudly (resolve.ResolutionError).
  • F4 — spec-less escalations (ambiguous two-file wedge, unknown --story selector, session died pre-spec) latched a restore with no in-review routing target; rejected at the T1 seam.
  • F5 — outside-project artifact dirs (a supported layout) failed every restore on the bare is_relative_to(project) check; containment now uses the same trusted roots as verify.spec_within_roots.
  • F6 — worktree rejection ordering/UX. The isolation check ran AFTER the interactive session it invalidates and the agent had no isolation signal; now: flag validated pre-session, build_context exports restore_supported, SKILL.md tells the agent never to negotiate an unsupported restore, the error names the --no-interactive escape, and the guard also keys on the recorded task.worktree_path.
  • F7 — two more stale "two review hunters" lines (setup-guide + _require_base_skills docstring) → three.
  • F8 — TUI Re-arm silently dropped a recorded restore decision (the button is enabled by the very marker whose restore_patch it ignored); the modal now flags it and the re-arm notifies that only bmad-loop resolve honors a restore.
  • E2E scenario 7: sprint-mode intent-gap → resolve --restore-patch → resume, through the real CLI + tmux; the fake skill blocks loudly unless the prompt carries the spec pointer AND the tree was restored.

Known residuals filed as issues (pre-existing or design-scale, deliberately not in this PR): #88 (from-scratch re-drives can vacuously pass proof-of-work on untracked halt residue — needs snapshot subtraction in the gate), #89 (the baseline-match verify gate reads baseline_commit, which the generic skill never writes — dead in production, masked by fixtures), #90 (an abandoned restore attempt's untracked files get baselined at the next re-arm and swept into the corrected story's commit), #91 (cleanup batch: precondition centralization, path-normalizer dedup, test-fixture dedup).

Verification

  • Full suite: 1689 passed, 1 skipped, 0 failed (was 1674 pre-review; 15 new tests, every fix verified to fail without its change).
  • trunk check (no filter): clean.
  • New sandbox E2E leg drives the full sprint restore dance through the real run/resolve --restore-patch/resume binaries.

Summary by CodeRabbit

  • New Features
    • Added intent-gap patch-restore recovery that saves an attempted change and can automatically re-apply it when re-arming.
    • Added bmad-loop resolve --restore-patch for patch-based re-arm/resume.
    • Expanded adversarial review to 3 parallel layers (“Verification Gap” included) and updated preflight skill/config requirements.
    • In the escalation TUI, added a warning when a recorded resolution includes a restore patch that TUI “Re-arm” won’t honor.
  • Bug Fixes
    • Strengthened restore validation for path, isolation/worktree, sentinel/escalated safety, and spec/prompt routing; improved marker/error handling and baseline/restoration semantics.
  • Documentation / Tests
    • Updated setup/feature/skill docs and broadened CLI/engine/verify/TUI/E2E test coverage for restore and frontmatter behaviors.

pbean added 7 commits July 7, 2026 23:06
…r preflight

Integrate the July 4-6 upstream bmad-dev-auto changes into bmad-loop.

Phase A - provisioning + doc alignment:
- install: require bmad-review-verification-gap (BMAD-METHOD #2550) and a
  bmad-dev-auto customize.toml marker in the base-skill preflight; the three
  inline step-04 review hunters are now all bmm prerequisites
- devcontract: refresh the in-review reconcile note (restore path re-arms TO
  in-review pre-session; reconcile semantics unchanged)
- verify: document finalize_commit's intentional residual-artifact squash (#2563)
- docs: README skill table, FEATURES review-layer + resolve notes, CHANGELOG

Phase B - intent-gap patch-restore re-drive (BMAD-METHOD #2564):
A review-stage intent-gap halt now saves the reverted attempt as a patch file.
When the attempted reading was correct, bmad-loop resolve re-arms the spec to
in-review and re-applies that patch onto the baseline before the re-drive, so
the session resumes review (step-04) on the restored diff instead of
re-implementing from scratch.
- model.StoryTask.restore_patch latch; verify.apply_patch; runs.rearm_escalation
  restore branch (in-review vs ready-for-dev + restore journal field)
- engine: Engine._restore_patch applied in the dev loop gated on feedback-is-None
  (escalates rather than dispatch on apply failure; cleared on commit)
- cli: --restore-patch flag + strict validation + resolution.json passthrough
- resolve.read_resolution; bmad-loop-resolve SKILL.md guidance

Sweep-workflow extension tracked in #75.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Re-cut of 29ba684 onto post-stories main (supersedes closed PR #76).
Conflicts (model.py, runs.py, test_model.py, CHANGELOG) resolved as a
union of both features, folding the restore branch into the
stories-hardened re-arm: the in-review flip joins the
UnicodeDecodeError-guarded status-flip path, story-escalation-resolved
carries baseline+restore, and a patch-restore re-arm on a
sentinel-wedged story is rejected outright before any mutation (T1: a
sentinel is a pre-planning halt — never lay implementation onto the
tree ahead of a planning leg).
… (T1)

The guard itself landed inside the re-cut's runs.py merge resolution.
Covers both sides: rejection leaves the sentinel on disk, the task
ESCALATED, no latch persisted and nothing journaled; a real stories
spec (sentinel_kind unset) stays a legitimate restore target.
Restore is an in-place-only recovery: a worktree re-drive discards and
re-mounts the unit's worktree (engine._finish_inflight), so a latched
patch would silently never restore. Reject before any re-arm, mirroring
the missing/outside-project rejections; also add the audit's missing
outside-project-but-existing-file rejection test.
…nced re-arm baseline (T2)

Since #78 re-arm advances the baseline to the post-resolve HEAD while
the saved patch diffs from the ORIGINAL baseline, 'applies cleanly by
construction' held only when the resolve session left the patched files
untouched. Docstring + rearm docstring already state the conditional;
the resolve SKILL.md now warns that resolution commits overlapping the
patch's files make the restore fail loudly (omit restore_patch when the
resolution already carries the work). Combined engine test drives the
full interaction: resolution commit rewrites the patched line, restore
apply fails, the story re-escalates with no session dispatched and the
resolution commit untouched.
The intent-gap patch file is untracked halt residue under the protected
artifact dirs — it survives every reset, so with #79's file-granular
excludes its mere presence would let a restore re-drive whose session
produced nothing pass the 'no changes since baseline' gate. Thread the
task's restore_patch into verify_dev_exclude_relpaths at all three
proof-of-work call sites (sprint, bundle, stories) so the gate keys on
the applied work, not the patch that carried it.
The two lines the source plan targeted but 29ba684 missed: the README
review-disabled paragraph and the FEATURES capability-matrix row still
said two-layer / 2 parallel layers. Also note the new restore guards in
the CHANGELOG bullet.
…folds

The stories/sprint sandbox scaffolds were written on the stories branch
before the intent-gap preflight upgrade landed, so they hand-roll the
OLD base-skill set — the run now fails preflight wanting
bmad-review-verification-gap and bmad-dev-auto's customize.toml marker.
Install both stubs in both scaffolds.
@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 384284cf-5329-4cdc-9c7d-edd76515415d

📥 Commits

Reviewing files that changed from the base of the PR and between f0b43a1 and f5c1b36.

📒 Files selected for processing (2)
  • src/bmad_loop/resolve.py
  • tests/test_resolve.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/test_resolve.py
  • src/bmad_loop/resolve.py

Walkthrough

Adds patch-restore recovery for intent-gap review halts, including CLI re-arm support, engine patch reapplication, restore-aware verification gates, and updated review-layer requirements and documentation.

Changes

Intent-gap patch-restore feature

Layer / File(s) Summary
State and patch primitives
src/bmad_loop/model.py, src/bmad_loop/resolve.py, src/bmad_loop/verify.py, tests/test_model.py, tests/test_resolve.py, tests/test_verify.py
StoryTask persists restore_patch, resolution markers are parsed with strict error handling, patch application is added, and verification/frontmatter helpers and tests account for restore-aware behavior.
Rearm and resolve flow
src/bmad_loop/runs.py, src/bmad_loop/cli.py, src/bmad_loop/tui/app.py, src/bmad_loop/tui/screens/modals.py, tests/test_runs.py, tests/test_cli.py, tests/test_tui_app.py
rearm_escalation accepts restore_patch, resolve validates and forwards restore-patch input, and the TUI surfaces recorded-restore warnings while tests cover restore and rejection paths.
Engine restore dispatch
src/bmad_loop/engine.py, tests/test_engine.py
The engine reapplies a latched patch before dev dispatch, escalates on patch-apply failure, retries restoration after rollback, and clears the latch after commit.
Preflight requirements and docs
src/bmad_loop/install.py, tests/test_install.py, docs/setup-guide.md, README.md, docs/FEATURES.md, CHANGELOG.md, src/bmad_loop/data/skills/bmad-loop-resolve/SKILL.md, src/bmad_loop/devcontract.py, tests/test_stories_e2e.py
Install preflight now requires customize.toml and bmad-review-verification-gap, and the docs, scaffolding, and end-to-end tests reflect the three-layer review setup and restore flow.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related issues

Possibly related PRs

Poem

I hopped through patches, soft and neat,
Reapplied the diff, then found my beat.
Three review layers guard the run,
And restore_patch shines like morning sun. 🐇

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the two main changes: intent-gap patch restore re-drive and inline review-layer preflight.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/intent-gap-patch-restore-v2

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

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

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

Inline comments:
In `@src/bmad_loop/runs.py`:
- Line 569: The comment in the story escalation guard uses a non-ASCII
multiplication symbol, which Ruff flags. Update the wording in the relevant T1
guard comment inside the runs-related logic to use the ASCII character x instead
of ×, keeping the rest of the note unchanged.

In `@tests/test_engine.py`:
- Line 2229: The docstring in the test around the baseline advance scenario uses
a non-ASCII multiplication sign, which Ruff flags. Update the docstring in the
affected test to replace the `×` character with plain ASCII `x` while keeping
the rest of the wording unchanged.

In `@tests/test_resolve.py`:
- Line 341: The docstring in the story test uses a non-ASCII multiplication
symbol, which Ruff flags as invalid style. Update the affected docstring in the
test around the story/patch-restore description to use the ASCII character x
instead of ×, keeping the wording otherwise unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 730ac306-29f7-4aa9-8593-5c2c7b5efc30

📥 Commits

Reviewing files that changed from the base of the PR and between c1649b9 and 18d2d67.

📒 Files selected for processing (20)
  • CHANGELOG.md
  • README.md
  • docs/FEATURES.md
  • src/bmad_loop/cli.py
  • src/bmad_loop/data/skills/bmad-loop-resolve/SKILL.md
  • src/bmad_loop/devcontract.py
  • src/bmad_loop/engine.py
  • src/bmad_loop/install.py
  • src/bmad_loop/model.py
  • src/bmad_loop/resolve.py
  • src/bmad_loop/runs.py
  • src/bmad_loop/verify.py
  • tests/test_cli.py
  • tests/test_engine.py
  • tests/test_install.py
  • tests/test_model.py
  • tests/test_resolve.py
  • tests/test_runs.py
  • tests/test_stories_e2e.py
  • tests/test_verify.py

Comment thread src/bmad_loop/runs.py Outdated
Comment thread tests/test_engine.py Outdated
Comment thread tests/test_resolve.py Outdated
@pbean

pbean commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator Author

augment review

@augmentcode

augmentcode Bot commented Jul 8, 2026

Copy link
Copy Markdown

Looks like you're out of credits for this review. Head to your account settings to top up and keep reviews running. https://app.augmentcode.com/account/subscription

…abbit)

CodeRabbit (Ruff RUF002/RUF003) flagged the U+00D7 MULTIPLICATION SIGN in
three comment/docstring sites; swap them to ASCII 'x'. Also fixes two sibling
occurrences the review missed in the same PR (tests/test_verify.py T4
docstrings). Intentional × count glyphs (CLI/TUI) and – en-dash parsing
regexes are left untouched.
@pbean

pbean commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator Author

Addressed in e6ef573.

All three RUF002/RUF003 ambiguous-× findings are valid and fixed (swapped to ASCII x):

  • src/bmad_loop/runs.py:569 (comment, RUF003)
  • tests/test_engine.py:2229 (docstring, RUF002)
  • tests/test_resolve.py:341 (docstring, RUF002)

While fixing, I also caught two sibling occurrences the review didn't flag — same class, introduced by this PR's T4 verify tests — and fixed them in the same commit:

  • tests/test_verify.py:1113 and :1125 (docstrings, RUF002)

Deliberately left untouched: the intentional × count glyphs in user-facing output (cli.py / tui/screens/dashboard.py, e.g. dev×2) and the en-dashes baked into deferredwork.py's legacy-ledger parsing regexes and fixtures — both are load-bearing (RUF001, a rule this repo doesn't enable). ruff check --select RUF002,RUF003 and trunk check are both clean; 289 affected-module tests pass.

pbean added 10 commits July 8, 2026 01:02
…ter (review F1)

A bare '/bmad-dev-auto <key>' never takes step-01's spec-pointer intent
check, so the in-review status the re-arm set could not route to step-04
— and the freeform/epic path's version-control sanity check HALTs
blocked on the very diff _restore_patch just applied, re-escalating
every sprint-mode restore after one burned session. Point the dispatch
at task.spec_file (preserved by re-arm for exactly this) so step-01
EARLY EXITs to step-04 before that check. Stories mode already
early-exits via folder+id dispatch and is untouched.
…e re-arm (review F2)

The in-review route skips step-03 — the only step that stamps
baseline_revision — so the re-driven step-04 diffed (and on an
intent-gap/bad-spec re-triage, reverted) 'since' the ORIGINAL
pre-attempt sha, clawing back the non-overlapping resolve-session
commits the baseline advance explicitly blesses as the re-drive's
starting point. rearm_escalation now re-stamps the field to the
advanced task baseline on the restore path (loud RearmError on
failure); from-scratch re-arms leave it alone (step-03 owns it there).
New verify.set_frontmatter_field does the same line surgery as
set_frontmatter_status but inserts a missing key.
…ly re-driving from scratch (review F3)

read_resolution collapsed a present-but-unparseable resolution.json
into None — indistinguishable from 'nothing recorded' and quieter than
absence (the produced-marker warning keys on existence). Likewise
--restore-patch '' (unset shell var) fell through 'if not raw' as a
plain re-arm, even masking a recorded restore. Both silently consumed
the escalation (a re-armed task can't be re-resolved), losing the
human's confirmed restore decision. read_resolution now returns None
only for an ABSENT marker and raises ResolutionError otherwise;
_resolve_restore_patch errors on an empty flag and on an empty or
non-string restore_patch field.
The latch was assigned unconditionally while the in-review routing flip
lives under 'if task.spec_file:' — so an escalation with no recorded
spec (ambiguous two-file wedge, unknown --story selector, session died
before naming one) latched the patch with no routing target, and the
engine would lay it onto the tree before a planning leg. Rejected at
the T1 seam, before any mutation; from-scratch re-arms unaffected.
…oots (review F5)

bmad-dev-auto contractually saves the intent-gap patch under
implementation_artifacts, and artifact dirs configured outside the
project tree are a supported layout (bmadconfig keeps them absolute;
verify special-cases them throughout) — but the containment check was a
bare is_relative_to(project), so that layout rejected every legitimate
restore with an error blaming the path. Validate against the same
trusted roots as verify.spec_within_roots instead. Knock-ons are safe:
the T4 proof-of-work exclude skips out-of-project paths (which git
can't count as changes anyway) and git apply takes absolute patch
paths. Restore tests now provision the minimal _bmad config every real
project has.
…t would invalidate (review F6)

The worktree-isolation rejection ran only AFTER resolve.run_session —
the agent (with no isolation signal in its context and none in
SKILL.md) would legitimately negotiate a restore, record it, and only
then hit an rc=1 whose naive retry unlinks resolution.json and repeats
the whole session; the only deterministic escape (--no-interactive) was
unnamed. Now: the explicit --restore-patch flag is validated before the
session (policy loaded once, killing the double TOML parse);
build_context exports restore_supported and SKILL.md tells the agent
never to negotiate a restore when it is false (both reseeds updated);
the rejection message names the --no-interactive escape; and the guard
keys on the recorded task.worktree_path as well as live policy, so a
policy edit between escalation and resolve can't skew it.
The LOW-4 prose sweep (84d4da1) claimed completion but missed
docs/setup-guide.md and the _require_base_skills docstring.
…opped silently (review F8)

The escalation modal's Re-arm button is enabled by resolution.json's
existence yet re-armed with no restore latch — the always-assign
semantics actively select from-scratch — so a restore_patch the human
confirmed through the interactive resolve flow was discarded without a
word (reachable via resolve -> 'n' at the confirm -> TUI Re-arm).
Auto-honoring from the TUI would be worse (a consumed marker is
indistinguishable from a fresh one on disk), so Re-arm stays a plain
from-scratch re-drive; but the modal hint now flags the recorded
restore and the re-arm notifies that only 'bmad-loop resolve' applies
it. An unreadable marker counts as recorded (it may carry one).
…(scenario 7)

The fake dev CLI grows the #2564 choreography: a committed
.intent-gap-<story> marker makes the first dispatch save the attempted
change as a patch, revert, and block; an in-review re-drive asserts the
two orchestrator contracts the way real step-01 would — the prompt must
carry an explicit spec pointer (F1) and the attempt must already be
restored onto the tree — blocking loudly otherwise, then resumes review
to done without re-implementing. The test also pins the F2 re-stamp
(spec baseline_revision == post-re-arm HEAD) and that the restored line
lands exactly once in the story commit.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

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

Inline comments:
In `@src/bmad_loop/resolve.py`:
- Around line 58-61: `read_resolution` currently misses `UnicodeDecodeError`
from `path.read_text(encoding="utf-8")`, so corrupt UTF-8 in `resolution.json`
escapes instead of becoming `ResolutionError`. Update the `except` handling in
`resolve.read_resolution` to catch `UnicodeDecodeError` alongside the existing
unreadable/JSON cases, and keep wrapping it in `ResolutionError` with the same
path context so `cli._resolve_restore_patch` and `tui.app._restore_recorded`
continue to handle it cleanly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6fdfed7c-abcb-46b2-b059-a7d010135f35

📥 Commits

Reviewing files that changed from the base of the PR and between e6ef573 and f0b43a1.

📒 Files selected for processing (14)
  • docs/setup-guide.md
  • src/bmad_loop/cli.py
  • src/bmad_loop/data/skills/bmad-loop-resolve/SKILL.md
  • src/bmad_loop/engine.py
  • src/bmad_loop/resolve.py
  • src/bmad_loop/runs.py
  • src/bmad_loop/tui/app.py
  • src/bmad_loop/tui/screens/modals.py
  • src/bmad_loop/verify.py
  • tests/test_cli.py
  • tests/test_engine.py
  • tests/test_resolve.py
  • tests/test_stories_e2e.py
  • tests/test_tui_app.py
✅ Files skipped from review due to trivial changes (2)
  • docs/setup-guide.md
  • src/bmad_loop/data/skills/bmad-loop-resolve/SKILL.md
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/bmad_loop/engine.py
  • src/bmad_loop/cli.py
  • tests/test_engine.py

Comment thread src/bmad_loop/resolve.py
…ot UnicodeDecodeError (CodeRabbit)

UnicodeDecodeError is a ValueError, not an OSError, so it escaped
read_resolution's except tuple as a crash instead of the clean
fail-loud abort F3 added — same bug class as the read_frontmatter /
stories read-path hardening.
@pbean

pbean commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator Author

augment review
@CodeRabbit review

@augmentcode

augmentcode Bot commented Jul 8, 2026

Copy link
Copy Markdown

Looks like you're out of credits for this review. Head to your account settings to top up and keep reviews running. https://app.augmentcode.com/account/subscription

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown

@pbean Got it — kicking off a fresh review of the PR.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@pbean pbean merged commit 9ea999c into main Jul 8, 2026
9 checks passed
pbean added a commit that referenced this pull request Jul 9, 2026
…k bundles (#75) (#93)

An escalated sweep bundle can now be resolved with
`bmad-loop resolve <run> --restore-patch <path>`, re-arming the bundle spec to
`in-review` and re-applying the saved patch on the re-drive — the same recovery
sprint/story runs got in #86. Bundles lack sprint-status entries and SweepEngine
overrides `_loop` wholesale, so it never reached the base `_finish_inflight`
re-drive wiring; two small deltas close the gap (the apply seam, latch-clear on
commit, proof-of-work exclusion, rearm, and CLI resolve->resume were already
inherited/pre-wired):

- `_generic_bundle_prompt`: point a restore re-drive at the in-review bundle spec
  (task.spec_file) instead of the fresh intent.md, so step-01's spec-pointer
  intent check early-exits before its version-control sanity check HALTs blocked
  on the restored dirty tree (mirrors the sprint F1 fix, engine.py:2133-2141).
- `_run_bundle` recovery: latch `resolved_redrive` and pass `cause="resolved"`
  for a re-armed task, mirroring `_finish_inflight` (engine.py:1151-1157). This
  is a correctness fix — without the latch a non-critical exhaustion mid-re-drive
  silently DEFERs the human-resolved escalation (escalation.py:110). It also
  repairs the pre-existing from-scratch sweep re-drive, which had the same bug.

Tests: 4 engine cases in test_sweep.py (restore prompt, restore re-drive ->
done + latch cleared + prompt-points-at-spec, pause-not-defer exhaustion for the
restore and from-scratch paths) + an escalating-dev conftest helper; E2E
scenario 8 drives the real sweep/resolve/resume binaries through tmux (the only
coverage of the sweep-specific CLI resume path). All new tests fail without the
fix. Full suite 1695 passed / 1 skipped; trunk clean.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant