Skip to content

chore(release): 0.7.12#35

Merged
pbean merged 10 commits into
mainfrom
release/0.7.12
Jul 2, 2026
Merged

chore(release): 0.7.12#35
pbean merged 10 commits into
mainfrom
release/0.7.12

Conversation

@pbean

@pbean pbean commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator

Release 0.7.12. On merge to main, .github/workflows/release.yml auto-creates the v0.7.12 tag and GitHub release from the CHANGELOG.

Added

  • Weighted token totals in the TUI. The dashboard tokens column and the run-header summary now show the cost-proportional weighted total (cache-read tokens discounted by the run's cache_read_weight — the same weighting the per-story budget enforces), so the headline number tracks spend rather than context re-reads. The previous unweighted total moves to a new raw column. The weight is read from the run's persisted policy snapshot with a 0.1 fallback, so no engine/policy changes were needed and the TUI stays a pure observer.

Fixed

  • A failed attempt's work is preserved before an auto-rollback hard reset instead of being silently discarded. Commits above baseline are parked under attempt-preserve/<run_id>-<head8> and the uncommitted working-tree diff (tracked edits and run-created untracked files) under refs/attempt-preserve-dirty/ — both recoverable by name and surviving gc. A rollback that cannot create the ref pauses for manual recovery rather than destroying work.
  • Process liveness is now identity-aware, so a reused PID no longer reads as a live run (which used to block resume of a dead run, strand worktree reclaim, leak sessions, and show dead runs as RUNNING). On win32 the engine also ignores console SIGINT/SIGBREAK during a run so a ConPTY Ctrl+C broadcast can't kill it.
  • A story from a resolved escalation that still can't finish now re-escalates instead of being silently deferred.
  • A resumed --epic N run stays scoped to its epic and no longer declares the epic "done" while stories remain — the selector/cap are persisted and restored on resume.

Full notes in CHANGELOG.md.

Verification

  • release.py check: version fields all agree on 0.7.12; [0.7.12] CHANGELOG section present.
  • Skill forks reseeded; tests/test_module_skills_sync.py green.
  • Token feature + model tests green; TUI screenshots/demo regenerated to reflect the new raw column.
  • Full trunk check: no issues.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Dashboard tables and run header now display weighted token totals alongside the raw token count, including a new raw column.
  • Bug Fixes
    • Resumed runs now restore the original epic/story scope and story cap.
    • Rollbacks now preserve uncommitted in-progress changes for recovery (including worktree/untracked handling and distinct recovery refs across repeated failures).
    • Story selection exhausts the current epic first; epic-scoped selection respects document order.
    • Resolved critical escalations re-escalate when they can’t complete.
  • Chores
    • Bumped versions to 0.7.12 and updated the changelog.

pbean and others added 5 commits July 1, 2026 11:43
…alations

Two bugs surfaced driving Epic 9 in a real project (document-ordered, not
numeric, sprint board; epic 9 physically last in the file):

1. A story from a human-resolved CRITICAL `blocked` escalation whose re-drive
   still couldn't converge was silently downgraded to a plateau-defer (and its
   implemented code rolled back). `decide_dev`/`decide_review_session` now
   re-escalate (PAUSE) instead of DEFER when `task.resolved_redrive` is latched,
   so the blocker resurfaces for the human and `_escalate` preserves the tree.

2. `resume` rebuilt the Engine without the run's `--epic`/`--story`/
   `--max-stories`, so a resumed `--epic N` run widened to every epic; combined
   with strict file-order selection, deferring/finishing an out-of-file-order
   epic's story bounced the pick to an earlier-in-file epic and fired a spurious
   "epic N complete" boundary. Fixes:
   - persist epic_filter/story_filter/max_stories in RunState and restore them
     on resume (also closes the max_stories-dropped-on-resume gap);
   - `_pick_next` now exhausts the current epic before advancing (next_actionable
     gains an `epic` filter), so an epic-boundary only fires when the current
     epic is genuinely out of actionable stories. Document-order epic execution
     is preserved (fallback is still file order).

Adds coverage for story index 0, document-order boundaries, resume-scope
restoration, and defer-vs-escalate on resolved_redrive (new test_escalation.py).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CHANGELOG entry + version stamp (scripts/sync_version.py) for the resume-scope
and resolved-escalation defer fixes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ack reset

A story that escalates before it commits leaves its implementation in the
working tree. On resume of a resolved re-drive (or any rollback_on_failure
reset), `_rollback_or_pause` runs `git reset --hard baseline` plus its
untracked cleanup, discarding that diff — only committed work above baseline
was parked (attempt-preserve/). A dev session that stalled mid-implementation
therefore restarted from zero every run.

Park the uncommitted diff too: `verify.snapshot_worktree` captures tracked
edits AND run-created untracked files (which `git stash create` misses) via a
throwaway temp index honoring .gitignore, and `_preserve_attempt_worktree`
refs it under refs/attempt-preserve-dirty/<slug>-<baseline8> before every
auto-rollback reset. No-op on a clean tree; best-effort so it never blocks
the reset. Complements _preserve_attempt_commits.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The dashboard tokens column and run-header summary now show the cost-proportional weighted total (cache reads discounted per the run's cache_read_weight, matching the engine budget check), with the previous unweighted total preserved in a new 'raw' 7th column. The weight is read from the persisted policy snapshot with a 0.1 fallback, so no engine/policy/persistence changes are needed and the TUI stays a pure observer.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rtional weighted…

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@pbean, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 44 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

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

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

How do review limits work?

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

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

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: be5208eb-cd3b-47af-a27c-ee5995038504

📥 Commits

Reviewing files that changed from the base of the PR and between 87836ca and c7a9f11.

📒 Files selected for processing (1)
  • src/automator/tui/screens/dashboard.py

Walkthrough

This release bumps version metadata to 0.7.12 and updates run scope persistence, epic-scoped selection, rollback preservation, escalation handling, and weighted/raw token display.

Changes

Release 0.7.12

Layer / File(s) Summary
Version and changelog bump
.claude-plugin/marketplace.json, module.yaml, src/automator/data/skills/bmad-auto-setup/assets/module.yaml, pyproject.toml, src/automator/__init__.py, CHANGELOG.md
Version strings updated to 0.7.12 across metadata files and a new CHANGELOG entry documents the release.
RunState scope fields
src/automator/model.py, tests/test_model.py
RunState persists epic_filter, story_filter, max_stories with serialization support, and computes cache-read weight from policy_snapshot.
CLI persist and restore scope on resume
src/automator/cli.py, tests/test_cli.py, tests/test_engine.py
cmd_run saves scope fields into state; _resume_paused_run restores them into the resumed Engine, verified by capturing-Engine tests.
Epic-exhausting story selection
src/automator/sprintstatus.py, src/automator/engine.py, tests/test_sprintstatus.py, tests/test_engine.py
next_actionable gains an epic filter; _pick_next exhausts the current epic before falling back to other epics.
snapshot_worktree git helper
src/automator/verify.py, tests/test_verify.py
New _git_env and snapshot_worktree commit uncommitted tracked/untracked changes to a recovery ref, excluding gitignored files, returning None when clean.
Engine preserves attempt worktree during rollback
src/automator/engine.py, tests/test_engine.py
_rollback_or_pause calls new _preserve_attempt_worktree to snapshot dirty changes to refs/attempt-preserve-dirty/ before reset, journaling success/failure.
Exhausted-budget decision helpers and integration
src/automator/escalation.py, tests/test_escalation.py, tests/test_engine.py
New _exhausted_action/_exhaust_reason route budget-exhausted decisions to PAUSE with re-escalation reasoning when resolved_redrive is set, instead of always deferring.
cache_read_weight and weighted/raw token rendering
src/automator/model.py, src/automator/tui/screens/dashboard.py, src/automator/tui/widgets.py, tests/test_tui_app.py
RunState.cache_read_weight() derives a weighting factor from policy_snapshot; dashboard and header now display weighted totals alongside a new raw token count.

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

Possibly related PRs

Poem

A hop, a bump, to 0.7.12,
I stash your dirty work, safe and true. 🥕
Epics finish before I roam,
Weighted tokens light my home.
Re-escalate, don't defer the fight —
This bunny’s PR lands just right! 🐇

🚥 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 identifies this as the 0.7.12 release and matches the version bump and release-oriented changes.
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 unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch release/0.7.12

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.

@augmentcode

augmentcode Bot commented Jul 2, 2026

Copy link
Copy Markdown
🤖 Augment PR Summary

Summary: Releases 0.7.12, focusing on safer rollback/recovery, more reliable resume behavior, and token totals in the TUI that better track actual spend.

Changes:

  • Bumped versions across project/module/plugin metadata and added 0.7.12 release notes + tag link in CHANGELOG.md.
  • Persisted run scope/cap (--epic/--story/--max-stories) into RunState and restored it on resume so resumed runs don’t silently widen scope.
  • Hardened story selection to exhaust the current epic before advancing, while preserving document-order epic execution.
  • Preserved failed-attempt work before rollback: committed work via recovery refs and uncommitted diffs via refs/attempt-preserve-dirty/… snapshots.
  • Added verify.snapshot_worktree using a temporary git index and a synthetic author/committer identity to work without local/global git identity configuration.
  • Adjusted budget-exhaustion decisions so resolved_redrive re-escalates (pauses) instead of silently deferring.
  • Updated the TUI to show weighted token totals (cache reads discounted) plus a new raw column, and prevented late poll callbacks from crashing after screen teardown.
  • Expanded regression/unit tests covering resume scoping, epic selection, rollback preservation, snapshot semantics, and TUI rendering.

Technical Notes: The TUI reads cache_read_weight from the persisted policy snapshot with a 0.1 fallback, and snapshotting respects the “unknown baseline” (baseline_untracked=None) invariant to avoid staging user pre-existing untracked files.

🤖 Was this summary useful? React with 👍 or 👎

@augmentcode augmentcode 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.

Review completed. 2 suggestions posted.

Fix All in Augment

Comment augment review to trigger a new review at any time.

Comment thread src/automator/engine.py Outdated
# Same git-safe, length-bounded slug as _preserve_attempt_commits so an
# exotic/overlong --run-id can't blow the ref-name limit and drop the ref.
slug = "".join(c if (c.isalnum() or c in "_-") else "-" for c in self.state.run_id)[:64]
ref = f"refs/attempt-preserve-dirty/{slug}-{baseline[:8]}"

@augmentcode augmentcode Bot Jul 2, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

_preserve_attempt_worktree names the recovery ref with {baseline[:8]}; if multiple rollbacks happen for the same run/story baseline, this update-ref will overwrite the previous snapshot and make earlier preserved work unrecoverable (journal entries keep only the ref name).

Severity: medium

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in 632b086. The recovery ref now carries a per-retry discriminator — refs/attempt-preserve-dirty/{slug}-{baseline[:8]}-{task.attempt}. task.attempt only ever increments across a task (never resets), so repeated dirty rollbacks against the same fixed baseline_commit no longer reuse the ref name and orphan an earlier attempt's snapshot. Locked by a new regression test, test_rollback_preserves_distinct_refs_across_repeated_dirty_rollbacks, which drives two dirty→rollback cycles on the same baseline and asserts two distinct, git show-able refs.

Comment thread src/automator/verify.py Outdated

Returns ``ref_name`` on success, or ``None`` when the tree is clean relative
to HEAD (nothing to preserve — the intended non-destructive uncommitted-revert
case). Raises :class:`GitError` on any git failure so a caller can never

@augmentcode augmentcode Bot Jul 2, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

snapshot_worktree’s docstring says raising GitError means callers “can never … reset past unpreserved work”, but Engine._preserve_attempt_worktree catches GitError and proceeds with a destructive reset; this contract mismatch could mislead future callers about the safety semantics.

Severity: low

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in 632b086. The docstring is reworded: the GitError raise surfaces the capture failure so the caller can decide, rather than implying it universally stops a reset past unpreserved work. The commit-preservation caller refuses to reset; the best-effort worktree caller (_preserve_attempt_worktree) journals the failure and proceeds — the recovery ref is a safety net, not a gate.

@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: 5

🧹 Nitpick comments (2)
tests/test_escalation.py (1)

34-36: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Consider asserting the reason on the budget-left RETRY path.

This test only checks action == Action.RETRY, so it does not catch that _exhaust_reason is currently rewriting the reason on the retry branch (see the escalation.py comment). Adding assert "re-escalating" not in decision.reason would lock in the intended behavior.

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

In `@tests/test_escalation.py` around lines 34 - 36, The budget-left retry test
for decide_dev only asserts Action.RETRY, so it can miss the incorrect reason
rewrite in the retry path. Update the test to also check the Decision.reason
from decide_dev, specifically that it does not contain the re-escalating text on
the resolved_redrive/budget-remains path, so the intended behavior in
_exhaust_reason is preserved.
tests/test_tui_app.py (1)

190-207: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a RunHeader assertion for parity with the sibling test.

This fallback test only checks the tasks-table cells; the sibling test (test_task_table_shows_weighted_and_raw_tokens) also asserts the RunHeader text. Without a header check here, a regression in widgets.py's default-weight accumulation path could go undetected.

✅ Proposed addition
         assert tasks_table.get_cell("1-1-login", "tokens") == "260"
         assert tasks_table.get_cell("1-1-login", "raw") == "1,160"
+        header = str(screen.query_one("`#runheader`", RunHeader).content)
+        assert "260 tokens (1,160 raw)" in header
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_tui_app.py` around lines 190 - 207, Add a RunHeader assertion to
match the sibling token-weight test and cover the same UI path. In
test_token_weight_falls_back_to_default, after selecting the run in
DashboardScreen, also query the RunHeader and assert its text reflects the
weighted total and raw total for the fallback-weight case. This keeps parity
with test_task_table_shows_weighted_and_raw_tokens and verifies the
default-weight accumulation behavior exercised through widgets.py.
🤖 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/automator/engine.py`:
- Around line 793-797: The recovery ref created by _preserve_attempt_worktree in
engine.py can collide across retries because it is keyed only on
task.baseline_commit, so later attempts overwrite earlier preserved snapshots.
Update the ref naming in _preserve_attempt_worktree (and any call path that
invokes verify.snapshot_worktree) to include a per-retry discriminator such as
task.attempt, matching the retry loop in _dev_phase so each preservation is
unique and previous worktree snapshots are retained.

In `@src/automator/escalation.py`:
- Around line 71-82: The retry path in decide_dev is building an exhausted-style
reason even when Action.RETRY is returned, which makes the repair feedback and
journal message misleading; update the logic so _exhaust_reason(task, ...) is
only used when the exhausted action is actually selected, and otherwise pass
through the original reason. Apply the same fix in decide_review_session as
well, using the same pattern around the budget_left check and the existing
_exhaust_reason helper so both functions keep retry reasons distinct from
exhausted reasons.

In `@src/automator/verify.py`:
- Around line 277-278: The snapshotting logic in verify.py is too broad because
the current _git_env("add", "-A") call in the restore/snapshot flow records
every untracked file, including pre-existing user files. Update the caller to
pass the baseline untracked snapshot into this path, then change the Git staging
logic to use git add -u for tracked modifications and add only newly created
untracked files relative to that baseline. Keep the fix localized to the restore
helper around _git_env and the code that builds the attempt-preserve-dirty
snapshot.
- Around line 290-292: The snapshot commit in _git(repo, "commit-tree", ...) can
fail when no local/global Git identity is configured, so set synthetic author
and committer identity via _git_env before creating the worktree snapshot.
Update the snapshot path in verify.py around the commit-tree call to pass
GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL, GIT_COMMITTER_NAME, and GIT_COMMITTER_EMAIL
so the recovery flow completes reliably.

In `@tests/test_engine.py`:
- Around line 1118-1147: Add a regression test for repeated rollback on the same
baseline in test_rollback_preserves_uncommitted_attempt_worktree by driving two
consecutive dirty-then-rollback cycles through engine._rollback_or_pause on the
same StoryTask.task.baseline_commit. Verify _preserve_attempt_worktree (and its
attempt-preserve-dirty ref creation) does not overwrite the first preserved
snapshot, and assert both recovered snapshots remain accessible in the journal
after the second rollback.

---

Nitpick comments:
In `@tests/test_escalation.py`:
- Around line 34-36: The budget-left retry test for decide_dev only asserts
Action.RETRY, so it can miss the incorrect reason rewrite in the retry path.
Update the test to also check the Decision.reason from decide_dev, specifically
that it does not contain the re-escalating text on the
resolved_redrive/budget-remains path, so the intended behavior in
_exhaust_reason is preserved.

In `@tests/test_tui_app.py`:
- Around line 190-207: Add a RunHeader assertion to match the sibling
token-weight test and cover the same UI path. In
test_token_weight_falls_back_to_default, after selecting the run in
DashboardScreen, also query the RunHeader and assert its text reflects the
weighted total and raw total for the fallback-weight case. This keeps parity
with test_task_table_shows_weighted_and_raw_tokens and verifies the
default-weight accumulation behavior exercised through widgets.py.
🪄 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: 8035db79-d7ee-4b9c-9b5c-d92205b9d2c1

📥 Commits

Reviewing files that changed from the base of the PR and between b9a7705 and 5dcbf26.

⛔ Files ignored due to path filters (13)
  • docs/images/dashboard.png is excluded by !**/*.png
  • docs/images/dashboard.svg is excluded by !**/*.svg
  • docs/images/decision-answer.png is excluded by !**/*.png
  • docs/images/decision-answer.svg is excluded by !**/*.svg
  • docs/images/deferred-modal.png is excluded by !**/*.png
  • docs/images/deferred-modal.svg is excluded by !**/*.svg
  • docs/images/demo.gif is excluded by !**/*.gif
  • docs/images/settings.svg is excluded by !**/*.svg
  • docs/images/start-run-modal.png is excluded by !**/*.png
  • docs/images/start-run-modal.svg is excluded by !**/*.svg
  • docs/images/sweep-decision.png is excluded by !**/*.png
  • docs/images/sweep-decision.svg is excluded by !**/*.svg
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (21)
  • .claude-plugin/marketplace.json
  • CHANGELOG.md
  • module.yaml
  • pyproject.toml
  • src/automator/__init__.py
  • src/automator/cli.py
  • src/automator/data/skills/bmad-auto-setup/assets/module.yaml
  • src/automator/engine.py
  • src/automator/escalation.py
  • src/automator/model.py
  • src/automator/sprintstatus.py
  • src/automator/tui/screens/dashboard.py
  • src/automator/tui/widgets.py
  • src/automator/verify.py
  • tests/test_cli.py
  • tests/test_engine.py
  • tests/test_escalation.py
  • tests/test_model.py
  • tests/test_sprintstatus.py
  • tests/test_tui_app.py
  • tests/test_verify.py

Comment thread src/automator/engine.py
Comment thread src/automator/escalation.py Outdated
Comment thread src/automator/verify.py Outdated
Comment thread src/automator/verify.py Outdated
Comment thread tests/test_engine.py
Address CodeRabbit/Augment findings on the auto-rollback safety net added in
aedbcf9 — the snapshot could silently lose the very work it exists to protect.

- verify.snapshot_worktree: scope the capture to what safe_rollback would destroy
  — `add -u` for tracked edits/deletions plus only run-created untracked files
  (untracked_files minus baseline_untracked), never a pre-existing user file; and
  commit-tree under a synthetic bmad-auto identity so the snapshot succeeds with no
  git user.name/email configured (else the best-effort caller caught the GitError
  and reset past the work). Signature gains keyword-only baseline_untracked and the
  docstring is reworded to match.
- engine._preserve_attempt_worktree: key the recovery ref on task.attempt so
  repeated dirty rollbacks against the same fixed baseline no longer orphan an
  earlier attempt's snapshot.
- escalation: only wrap the reason with the exhausted "re-escalating" wording on
  the budget-exhausted branch; a plain RETRY keeps its plain reason.
- tests: no-identity + untracked-scope snapshot cases, repeated-rollback distinct
  refs regression, and a budget-left reason assertion. CHANGELOG hardened note.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@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/automator/verify.py`:
- Around line 292-296: Treat missing baseline_untracked as unknown in
verify.py’s snapshot staging logic: in the code path that computes new from
untracked_files(repo), do not coerce baseline_untracked to an empty list when it
is None. Update the handling around the untracked staging block so that when
baseline_untracked is unavailable, the code skips staging untracked files
entirely instead of calling _git_env("add", ...) for them; keep the existing
behavior only when a real baseline snapshot is present. Use the existing
untracked_files, safe_rollback, and _git_env flow to locate and adjust this
decision point.
🪄 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: 23946aa2-7103-497d-b5be-9b5aaf1289d1

📥 Commits

Reviewing files that changed from the base of the PR and between 5dcbf26 and 632b086.

📒 Files selected for processing (7)
  • CHANGELOG.md
  • src/automator/engine.py
  • src/automator/escalation.py
  • src/automator/verify.py
  • tests/test_engine.py
  • tests/test_escalation.py
  • tests/test_verify.py
✅ Files skipped from review due to trivial changes (1)
  • CHANGELOG.md
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/automator/escalation.py
  • tests/test_escalation.py
  • src/automator/engine.py
  • tests/test_engine.py

Comment thread src/automator/verify.py Outdated
Address CodeRabbit round-2 finding on the run-scoped snapshot from 632b086.
`snapshot_worktree` coerced `baseline_untracked or []`, so a None baseline (a
pre-upgrade/resumed run with no snapshot) treated every current untracked file
as run-created and parked it — a scope mismatch with safe_rollback (which
deletes no untracked files when baseline_untracked is None) and a privacy leak.

Guard on None explicitly, mirroring attempt_dirty/safe_rollback: park no
untracked file when the baseline is unknown (tracked edits are still parked via
add -u). New test test_snapshot_worktree_unknown_baseline_skips_untracked locks
it: a None-baseline snapshot captures the tracked edit but not the untracked file.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@pbean

pbean commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator Author

augment review

@augmentcode augmentcode 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.

Review completed. 2 suggestions posted.

Fix All in Augment

Comment augment review to trigger a new review at any time.

def _apply_tasks(self, state: RunState) -> None:
table = self.query_one("#tasks", DataTable)
weight = state.cache_read_weight()
for key, task in state.tasks.items():

@augmentcode augmentcode Bot Jul 2, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

src/automator/tui/screens/dashboard.py:546: The tokens cell shows "-" when weighted is 0; if cache_read_weight is 0 and a task only has cache-read tokens, the weighted total can be 0 while raw is nonzero, so this looks like missing data rather than “0 weighted tokens.”

Severity: low

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Good catch — fixed in 7b06edf. The tokens cell now gates on whether the task has any tokens at all, not on the weighted value, mirroring the adjacent raw cell:

tokens = f"{weighted:,}" if task.tokens.total else "-"

So with cache_read_weight = 0 a cache-read-only task (weighted 0, raw nonzero) renders tokens = "0" instead of "-"; "-" is now reserved for a task with genuinely no tokens. Locked by test_zero_weighted_tokens_shows_zero_not_dash (cache_read_weight: 0.0, a task with only cache_read_tokens=1000, asserts tokens == "0" and raw == "1,000").

Comment thread src/automator/engine.py
)
except verify.GitError:
self.journal.append("attempt-worktree-preserve-failed", story_key=task.story_key)
return

@augmentcode augmentcode Bot Jul 2, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

src/automator/engine.py:892: Catching verify.GitError here journals only a generic attempt-worktree-preserve-failed entry, dropping the underlying git error output; if the subsequent reset destroys work, this makes diagnosis of the preservation failure much harder.

Severity: low

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in 7b06edf. _preserve_attempt_worktree now binds the exception and journals its message:

except verify.GitError as exc:
    self.journal.append(
        "attempt-worktree-preserve-failed", story_key=task.story_key, error=str(exc)
    )
    return

The GitError messages already carry the underlying git stderr (e.g. git commit-tree (snapshot) failed in <repo>: <output>), so this keeps the diagnosis breadcrumb when a preservation failure is followed by a destructive reset. Covered by test_rollback_worktree_preserve_failure_journals_git_error, which forces a GitError and asserts the journal entry's error field carries the detail while the reset still proceeds.

pbean and others added 2 commits July 2, 2026 09:51
…tokens

Address two low-severity Augment findings on PR #35.

- engine._preserve_attempt_worktree: bind the caught verify.GitError and journal
  its message (`error=str(exc)`) instead of a bare entry, so a preservation
  failure before a destructive reset leaves a diagnosable breadcrumb (the
  commit-tree/update-ref stderr) rather than just "it failed".
- tui dashboard _apply_tasks: gate the `tokens` cell on `task.tokens.total`, not on
  the weighted value. With cache_read_weight=0 a cache-read-only task has
  weighted==0 but nonzero raw; it now shows "0" instead of "-" (which reads as
  missing data). "-" stays reserved for a task with no tokens at all.

Tests: preserve-failure journals the git error; a zero-weighted cache-read-only
task renders tokens "0" not "-".

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A poll worker delivers its snapshot on the UI thread via call_from_thread; that
callback can land after the DashboardScreen has been unmounted (app shutdown) or
switched away, when the widgets it queries are gone — raising
`NoMatches: No nodes match '#runs' on DashboardScreen()`. Guard `_apply` on
`self.is_running`, which flips False on teardown but stays True while the screen
is merely backgrounded under a pushed screen, so a stale apply is dropped while a
live background one still refreshes.

Fixes an intermittent crash (and the corresponding flaky test_tui_settings
teardown). Regression test: applying a snapshot to a no-longer-running screen is
a no-op instead of a NoMatches crash.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@pbean

pbean commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator Author

@CodeRabbit review
augment review

@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown

@pbean I'll review the changes now.

✅ 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.

@augmentcode augmentcode 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.

Review completed. No suggestions at this time.

Comment augment review to trigger a new review at any time.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/automator/tui/screens/dashboard.py (1)

553-568: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Minor duplication in the total-gate check.

The if task.tokens.total else "-" gate is repeated for both tokens and raw. Could extract into a single boolean, but this is purely cosmetic.

♻️ Optional simplification
         for key, task in state.tasks.items():
             weighted = task.tokens.weighted_total(weight)
+            has_tokens = bool(task.tokens.total)
             # Gate on total (any tokens?), not on `weighted`: with cache_read_weight=0
             # a cache-read-only task has weighted==0 but nonzero raw — show "0", not "-"
             # (which reads as missing data). "-" means the task has no tokens at all.
-            tokens = f"{weighted:,}" if task.tokens.total else "-"
-            raw = f"{task.tokens.total:,}" if task.tokens.total else "-"
+            tokens = f"{weighted:,}" if has_tokens else "-"
+            raw = f"{task.tokens.total:,}" if has_tokens else "-"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/automator/tui/screens/dashboard.py` around lines 553 - 568, The
total-token check is duplicated when building the dashboard row in the task
loop, so simplify the `tokens`/`raw` formatting in `dashboard.py` by computing a
single boolean or helper from `task.tokens.total` and reusing it for both
fields. Keep the existing behavior in the `state.tasks.items()` iteration and
preserve the current `weighted` vs raw display logic in the same block.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/automator/tui/screens/dashboard.py`:
- Around line 553-568: The total-token check is duplicated when building the
dashboard row in the task loop, so simplify the `tokens`/`raw` formatting in
`dashboard.py` by computing a single boolean or helper from `task.tokens.total`
and reusing it for both fields. Keep the existing behavior in the
`state.tasks.items()` iteration and preserve the current `weighted` vs raw
display logic in the same block.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 369ea9e1-7f05-4089-9b3e-be02c567ee05

📥 Commits

Reviewing files that changed from the base of the PR and between 7015fce and 87836ca.

📒 Files selected for processing (5)
  • CHANGELOG.md
  • src/automator/engine.py
  • src/automator/tui/screens/dashboard.py
  • tests/test_engine.py
  • tests/test_tui_app.py
✅ Files skipped from review due to trivial changes (1)
  • CHANGELOG.md
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/automator/engine.py
  • tests/test_engine.py

Address a trivial CodeRabbit nitpick: the `if task.tokens.total` gate was
repeated for the `tokens` and `raw` cells in _apply_tasks. Extract it into a
single `has_tokens` boolean and reuse. No behavior change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@pbean

pbean commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator Author

Applied the token-cell dedupe nitpick in c7a9f11 — extracted has_tokens = bool(task.tokens.total) and reused it for the tokens/raw cells (no behavior change). Augment's re-review had no suggestions; this was the only open item.

@pbean pbean merged commit 06ec378 into main Jul 2, 2026
7 checks passed
@pbean pbean deleted the release/0.7.12 branch July 2, 2026 18:12
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