Skip to content

Release 0.7.7 — cross-platform fixes + dev-session stall hardening#24

Merged
pbean merged 8 commits into
mainfrom
release/0.7.7
Jun 29, 2026
Merged

Release 0.7.7 — cross-platform fixes + dev-session stall hardening#24
pbean merged 8 commits into
mainfrom
release/0.7.7

Conversation

@pbean

@pbean pbean commented Jun 29, 2026

Copy link
Copy Markdown
Collaborator

Release 0.7.7

A fixes-only patch release. Most are cross-cutting correctness/hardening fixes
surfaced while reviewing the psmux Windows backend (#19) but independent of it
— landed on their own so the latent-bug fixes ship now, while the psmux work
rebases onto these seams separately. It also folds in a dev-session stall-detection
hardening
fix surfaced by a real run, where long PlayMode/multi-subagent stories
were being falsely deferred.

Fixed

  • Case-insensitive spec-status gates (verify.status_of) — a hand-edited spec
    with a stray-cased Done/In-Review silently failed the dev/review gate and the
    story never advanced. Also fixes an invalid git reset --hard the run's baseline commit rollback hint (now a <baseline_commit> placeholder).
  • Path guards reject .. traversal and OS-foreign absolute paths — profile /
    plugin-manifest "must be project-relative" checks now reject ../ escapes and
    Windows/POSIX cross-absolute paths on every platform (is_absolute_path /
    has_parent_ref).
  • as_posix() state serialization — worktree spec_file and resolve
    resolution_path persist with forward slashes for a single cross-OS contract
    (no-op on POSIX).
  • Inode in the TUI stat-gated cachest_ino folded into _stat_sig so an
    atomic same-size state.json rewrite within one coarse mtime tick (WSL2 drvfs)
    can't be served stale.
  • Activity-aware dev-session stall detection + wake-nudge resilience — dev
    sessions were mis-stalled two ways: a long productive turn (building a diff,
    streaming a subagent) was killed because the idle-grace window measured
    time-since-last-Stop, and a turn ended to await a background process (a Unity
    PlayMode run) stalled because bmad-auto never re-invoked it. The grace window now
    measures genuine inactivity — pane-log growth re-arms it — and on real silence the
    orchestrator wakes the session with up to limits.dev_stall_nudges (new, default
    2) nudges before stalling; a fresh Stop restores the budget, session_timeout_min
    stays the ceiling.

Verification

  • Full suite green (1185 passed); version-sync / module-skills-sync / lock guards
    green; trunk check (no filter) clean.
  • Each fix carries its own regression tests.

Release commit stamps 0.7.7 across all version files and curates the CHANGELOG; on
merge to main, the Release workflow auto-creates the v0.7.7 tag and GitHub
release from the [0.7.7] CHANGELOG section.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Made spec status comparisons case-insensitive and whitespace-safe across verification and status advancement.
    • Strengthened path validation to reject .. traversal and OS-foreign/absolute paths in profile and manifest inputs.
    • Normalized persisted spec/context/resolution paths to forward slashes for consistent cross-OS behavior.
    • Improved TUI cache invalidation by including filesystem inode, and fixed dev-session stalling behavior using inactivity-based grace re-arming plus wake nudges.
  • New Features
    • Added limits.dev_stall_nudges (default 2) to control wake nudges before marking a dev session stalled.
  • Tests
    • Added regression coverage for path safety, status normalization, inode-based cache refresh, and dev-session grace/nudge behavior.
  • Chores
    • Bumped version to 0.7.7 in project metadata and manifests.
  • Documentation
    • Updated CHANGELOG and policy/TUI/game-engine tuning docs for the new dev-session tuning keys.

pbean and others added 5 commits June 28, 2026 17:44
Spec-frontmatter status gates compared the raw `status:` value, so a
hand-edited spec carrying a stray-cased `Done`/`In-Review` silently failed
to advance a story. Add `verify.status_of()` (strip + lower) and route the
six spec-frontmatter status reads through it (verify dev/review + bundle
gates, engine post-dev sync, sweep bundle). The template and sprint-status
tokens are lowercase, so behavior is unchanged for well-formed specs;
`devcontract` keeps its own lowercasing (it parses skill prose).

Also fix the manual-rollback notice: an empty baseline rendered an invalid
`git reset --hard the run's baseline commit` — use a `<baseline_commit>`
placeholder instead.

Cross-cutting fix surfaced by PR #19; landed independently of the psmux work.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The profile- and plugin-manifest "must be project-relative" guards used
`Path(value).is_absolute()`, which is platform-dependent (on Windows a
POSIX-absolute `/etc/passwd` reads as *not* absolute) and never caught
relative `..` escapes — so `../../etc` and cross-OS-absolute paths slipped
through. Add `platform_util.is_absolute_path` (absolute in either POSIX or
Windows terms) and `has_parent_ref` (a `..` segment in either flavor) and
pair them at every guard site in adapters/profile.py and plugins/manifest.py.
Hardening surfaced by PR #19; landed independently of the psmux work.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A worktree task's `spec_file` (state.json) and the resolve context's
`resolution_path` were serialized via `str(Path)`, which emits backslashes
on Windows — so a state/context file written on one OS read back with
OS-specific separators. Persist both via `as_posix()` so the cross-OS state
contract is a single forward-slash string everywhere (a no-op on POSIX).
Cross-OS portability surfaced by PR #19; landed independently of the psmux work.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The data layer caches a parse while a file's (mtime_ns, size) is unchanged.
On a coarse-mtime filesystem (e.g. WSL2 drvfs) a same-size rewrite within one
mtime tick is invisible to that signature, so the dashboard serves a stale
parse. The engine rewrites state.json atomically (temp + os.replace), so every
write lands on a fresh inode — fold st_ino into _stat_sig to catch the change
regardless of mtime resolution. Surfaced by PR #19; landed independently.

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

coderabbitai Bot commented Jun 29, 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: 34 minutes

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable usage-based reviews in Billing to keep reviews running — 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 see our Fair Usage Limits Policy for further information, and refer to the rate limits docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3c2806ad-13ff-4594-8e25-07439ba99c04

📥 Commits

Reviewing files that changed from the base of the PR and between 5d06206 and f0e639b.

📒 Files selected for processing (3)
  • src/automator/adapters/generic.py
  • src/automator/data/settings/core.toml
  • src/automator/policy.py

Walkthrough

Release 0.7.7 updates status normalization, project-relative path checks, POSIX path serialization, TUI cache invalidation, dev-session stall nudges, and version metadata.

Changes

0.7.7 behavioral updates

Layer / File(s) Summary
Spec status normalization
src/automator/verify.py, src/automator/engine.py, src/automator/sweep.py, tests/test_verify.py
Introduces normalized frontmatter status reads and applies them to verification and sync checks.
Project-relative path validation
src/automator/platform_util.py, src/automator/adapters/profile.py, src/automator/plugins/manifest.py, tests/test_platform_util.py
Adds cross-platform absolute-path and parent-reference detection and applies both checks to profile and manifest parsing.
POSIX path serialization
src/automator/model.py, src/automator/resolve.py, tests/test_engine_worktree.py, tests/test_resolve.py
Serializes worktree-relative spec paths and resolve context paths with forward slashes, with regression coverage for the serialized values.
TUI cache invalidation
src/automator/tui/data.py, tests/test_tui_data.py
Extends the TUI stat signature to include inode so cached run state refreshes after atomic same-size rewrites.
Dev stall nudges
src/automator/adapters/generic.py, src/automator/policy.py, src/automator/data/settings/core.toml, README.md, docs/FEATURES.md, docs/game-engine-plugin-guide.md, docs/tui-guide.md, tests/test_generic_tmux.py
Adds dev-session inactivity tracking, wake nudges, policy configuration, and documentation for the new stall behavior.
Version metadata and changelog
pyproject.toml, src/automator/__init__.py, module.yaml, src/automator/data/skills/bmad-auto-setup/assets/module.yaml, .claude-plugin/marketplace.json, CHANGELOG.md
Updates package, module, and plugin version fields and adds the 0.7.7 changelog entry and release link.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐇 I hop through statuses, trimmed and bright,
Paths stay straight with slashes right.
If logs keep growing, caches wake,
And stalled sessions get a nudge to make.
Seven-seven twinkles in the burrow light ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 45.10% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the release and its two main themes: cross-platform fixes and dev-session stall hardening.
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.7

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 Jun 29, 2026

Copy link
Copy Markdown
🤖 Augment PR Summary

Summary: Release 0.7.7 is a fixes-only patch focused on cross-platform correctness and more reliable dev-session stall handling.

Changes:

  • Bumped version stamps to 0.7.7 across packaging/module metadata and updated the changelog/release link.
  • Normalized all spec-frontmatter status gates via verify.status_of() (case/whitespace-insensitive) and fixed a manual rollback placeholder.
  • Hardened “must be project-relative” path validation to reject .. traversal and OS-foreign absolute/drive-qualified paths across platforms.
  • Serialized persisted relative paths using as_posix() so state/context files have a single cross-OS path contract.
  • Improved TUI cache invalidation by adding inode (st_ino) to the stat signature to avoid stale reads on coarse-mtime filesystems.
  • Reworked dev-session stall detection to measure inactivity (pane-log growth re-arms grace) and added configurable wake nudges (limits.dev_stall_nudges) before marking a session stalled.
  • Added/updated regression tests covering status normalization, path guards, POSIX path serialization, inode-based refresh, and dev-session grace/nudge behavior.

Technical Notes: The new inactivity-based grace window and wake-nudge budget are bounded by limits.session_timeout_min / spec.timeout_s, preserving an overall ceiling while avoiding false stalls during long-running work or background waits.

🤖 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. 1 suggestion posted.

Fix All in Augment

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

inside the project?" validation."""
text = str(value)
win = PureWindowsPath(text)
return PurePosixPath(text).is_absolute() or bool(win.drive or win.root)

@augmentcode augmentcode Bot Jun 29, 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/platform_util.py:57: is_absolute_path() currently treats Windows drive-relative paths like C:foo as “absolute” because win.drive is truthy even when win.root is empty; that doesn’t match the docstring’s “absolute” definition and could be surprising. If the intent is “reject any drive-qualified path for project-relative guards”, it may be worth clarifying that in the docstring to avoid misuse elsewhere.

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.

Already addressed in 72067a1 (pre-dates this review snapshot at 66cff31): the docstring now explicitly documents that C:foo drive-relative paths are rejected on purpose — "Strictly broader than 'absolute'; the extra rejection of C:foo is intentional for these guards." No change needed.

@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/resolve.py (1)

76-78: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a regression test for build_context() path normalization.

This contract change is cross-OS sensitive, but there is no paired test in the provided suite like the StoryTask.spec_file serialization check. A small assertion on resolution_path in context.json would lock this fix in.

🤖 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/resolve.py` around lines 76 - 78, Add a regression test
covering build_context() path normalization so the context contract stays stable
across OSes. Update the existing test suite around build_context()/context.json
serialization to assert that resolution_path is emitted in POSIX form via
resolution_path(...).as_posix(), similar to the StoryTask.spec_file
serialization check. Keep the assertion focused on the serialized context value
so this cross-platform behavior is locked in.
🤖 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/resolve.py`:
- Around line 76-78: Add a regression test covering build_context() path
normalization so the context contract stays stable across OSes. Update the
existing test suite around build_context()/context.json serialization to assert
that resolution_path is emitted in POSIX form via
resolution_path(...).as_posix(), similar to the StoryTask.spec_file
serialization check. Keep the assertion focused on the serialized context value
so this cross-platform behavior is locked in.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: bf304d10-d455-4e0f-8bc8-706a7f125915

📥 Commits

Reviewing files that changed from the base of the PR and between 15c7b80 and 66cff31.

⛔ Files ignored due to path filters (11)
  • docs/images/dashboard.png is excluded by !**/*.png
  • docs/images/dashboard.svg is excluded by !**/*.svg
  • docs/images/demo.gif is excluded by !**/*.gif
  • docs/images/settings-scm.png is excluded by !**/*.png
  • docs/images/settings-scm.svg is excluded by !**/*.svg
  • 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 (19)
  • .claude-plugin/marketplace.json
  • CHANGELOG.md
  • module.yaml
  • pyproject.toml
  • src/automator/__init__.py
  • src/automator/adapters/profile.py
  • src/automator/data/skills/bmad-auto-setup/assets/module.yaml
  • src/automator/engine.py
  • src/automator/model.py
  • src/automator/platform_util.py
  • src/automator/plugins/manifest.py
  • src/automator/resolve.py
  • src/automator/sweep.py
  • src/automator/tui/data.py
  • src/automator/verify.py
  • tests/test_engine_worktree.py
  • tests/test_platform_util.py
  • tests/test_tui_data.py
  • tests/test_verify.py

pbean and others added 2 commits June 28, 2026 21:06
- is_absolute_path: reframe the docstring as "rejects any rooted/drive-qualified
  path" — the helper intentionally rejects a Windows drive-relative `C:foo` (not
  strictly absolute) because such a path is never a valid in-project relative
  path. Behavior unchanged; pin the intent with a `C:foo` test row.
  (augmentcode[bot], PR #24)
- resolve.build_context: lock the resolution_path as_posix() serialization with a
  forward-slash assertion, mirroring the spec_file posix test. (coderabbitai[bot])

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

Dev sessions were mis-stalled two ways: a long productive turn (building a diff,
streaming a subagent) was killed because the idle-grace window measured
time-since-last-Stop, and a turn ended to await a background process (a Unity
PlayMode run) stalled because bmad-auto never re-invokes it. The grace window now
measures genuine inactivity — pane-log growth re-arms it — and on real silence the
orchestrator wakes the session with up to limits.dev_stall_nudges (new, default 2)
nudges before stalling; a fresh Stop restores the budget, session_timeout_min
stays the ceiling. New knob wired through policy + settings schema; docs and the
[0.7.7] CHANGELOG entry updated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@pbean pbean changed the title Release 0.7.7 — cross-platform fixes Release 0.7.7 — cross-platform fixes + dev-session stall hardening Jun 29, 2026

@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/policy.py`:
- Around line 54-60: The `dev_stall_nudges` help text and mirrored
settings/schema copy in `policy.py` currently promise that any response restores
the nudge budget, but `wait_for_completion()` only resets `stall_nudges_left` on
a fresh `Stop` while pane-log growth merely re-arms the grace window. Update the
docstring/help text near `dev_stall_nudges` and the matching text referenced by
`wait_for_completion()`/the settings schema so they describe the actual reset
behavior consistently.
🪄 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: 7dc15b3c-2c72-4b89-92b8-c51d50831cd1

📥 Commits

Reviewing files that changed from the base of the PR and between 72067a1 and 5d06206.

📒 Files selected for processing (9)
  • CHANGELOG.md
  • README.md
  • docs/FEATURES.md
  • docs/game-engine-plugin-guide.md
  • docs/tui-guide.md
  • src/automator/adapters/generic.py
  • src/automator/data/settings/core.toml
  • src/automator/policy.py
  • tests/test_generic_tmux.py
✅ Files skipped from review due to trivial changes (3)
  • docs/game-engine-plugin-guide.md
  • README.md
  • docs/FEATURES.md

Comment thread src/automator/policy.py Outdated
@pbean

pbean commented Jun 29, 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. 1 suggestion posted.

Fix All in Augment

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

if event.event == "SessionEnd":
return self._final(handle, spec, "crashed", session_id, transcript_path)

def _log_activity_key(self, task_id: str) -> tuple[int, int] | None:

@augmentcode augmentcode Bot Jun 29, 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/adapters/generic.py:304: The module docstring says the pane log is “NEVER parsed for control flow”, but _log_activity_key() now uses it to re-arm the stall grace window, which does affect control flow; consider updating that docstring to avoid misleading future readers.

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.

Valid — fixed in f0e639b. The module docstring now scopes "never parsed for control flow" to the log contents and notes that its growth (mtime/size, not the bytes) is read as a liveness signal to re-arm the dev-stall grace window.

Address two valid automated-reviewer findings on PR #24 (both doc-only, from
the activity-aware stall detection in 5d06206):

- dev_stall_nudges help/schema said "any response restores the budget", but
  wait_for_completion() restores the nudge budget only on a fresh Stop; pane
  output merely re-arms the grace window. Fixed the three mirrored copies
  (policy.py field comment, POLICY_TEMPLATE, core.toml settings schema).
- generic.py module docstring claimed the pane log is "NEVER parsed for
  control flow"; _log_activity_key() now reads its growth (mtime/size, not the
  bytes) as a liveness signal for stall detection. Scoped the claim to the log
  contents and noted the metadata liveness signal.

The other two findings were already fixed in 72067a1 (platform_util C:foo
docstring; resolve.py POSIX resolution_path regression test).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@pbean pbean merged commit ec715d4 into main Jun 29, 2026
7 checks passed
@pbean pbean deleted the release/0.7.7 branch June 29, 2026 05:58
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