fix(adapters): bound dev/review stall nudges + diagnose result-less Stops#151
Conversation
…tops The idle wake nudge is delivered as a submitted turn, so a session that merely answers it ends in another result-less Stop — which refilled the per-silence nudge budget, re-armed the grace window, and repeated until session_timeout_min, burning a turn per cycle. Dev/review sessions now get the same monotonic cap injected workflow sessions already had: after [limits] dev_stall_nudges_cap (default 6) total nudges the session is declared stalled instead; post-kill reconcile still rescues a finished one whose terminal artifact is on disk. STALL_NUDGE_TEXT now also states that a prose reply cannot end the session, and every result-less Stop leaves a diagnostic breadcrumb (tasks/<task_id>/resultless-stops.jsonl: pending / not-terminal / stale-mtime / ambiguous / no-artifact / no-result-json) so the trigger — why a completed-looking session read as result-less — is answerable from the run dir. Closes #149
WalkthroughDev/review sessions now use a configurable monotonic stall-nudge cap. Result-less Stop paths write JSONL diagnostic breadcrumbs, with policy, settings, engine wiring, tests, and documentation updated accordingly. ChangesStall nudge control and diagnostics
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant DevSession
participant GenericAdapter
participant ResultArtifact
participant BreadcrumbFile
DevSession->>GenericAdapter: receive idle wake-nudge
GenericAdapter->>ResultArtifact: poll completion result
ResultArtifact-->>GenericAdapter: result-less Stop
GenericAdapter->>BreadcrumbFile: append diagnostic breadcrumb
GenericAdapter-->>DevSession: continue until cap or completion
Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
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/data/settings/core.toml`:
- Line 113: Remove the duplicate [[section.field]] declaration near
workflow_stall_nudges_cap in the settings definition, keeping only the
declaration that begins the intended field record. Ensure no empty field entry
is created before workflow_stall_nudges_cap.
🪄 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: e20fd183-aad5-439f-96a7-0e9bf69f904b
📒 Files selected for processing (12)
CHANGELOG.mddocs/FEATURES.mddocs/game-engine-plugin-guide.mddocs/tui-guide.mdsrc/bmad_loop/adapters/base.pysrc/bmad_loop/adapters/generic.pysrc/bmad_loop/data/settings/core.tomlsrc/bmad_loop/engine.pysrc/bmad_loop/policy.pytests/test_generic_tmux.pytests/test_plugin_workflows.pytests/test_policy.py
Closes #149.
The bug
The idle wake nudge (
STALL_NUDGE_TEXT) is delivered as a submitted turn (send-keys+ Enter), so a session that merely answers it ends in another result-less Stop — which refilled the per-silence nudge budget (generic.py, fresh-Stop refill), re-armed the grace window, and repeated untilsession_timeout_min(90 min default), burning a turn of tokens per cycle. The monotonic cap that fixes exactly this livelock already existed (workflow_stall_nudges_cap, 0.8.0) but was wired only for injected workflow sessions; dev/review sessions were deliberately left uncapped and thus exposed.Note for reproducers: the issue's "~5 seconds" is the event-poll tick, not the idle threshold — the nudge fires after
dev_stall_grace_s(default 600 s) of pane-log silence following a result-less Stop. Slow Stop hooks alone never trigger it at defaults; the loop needs a Stop whose artifact read-back comes up empty.The fix
[limits] dev_stall_nudges_cap(default 6): dev/review sessions now get the same monotonic never-restored cap; after that many total nudges the session is declaredstalledinstead of riding the refill. Post-kill reconcile still rescues a finished session whose terminal artifact is on disk; otherwise the normal stalled retry/defer ladder applies. Under default timing the cap leaves ~an hour of idle grace for legit slow background waits (Unity PlayMode runs, long tests).STALL_NUDGE_TEXTnow states that a prose reply cannot end the session — only the workflow's completion artifact does — so a wake turn is steered toward finalizing rather than explaining.{ts, verdict, detail}totasks/<task_id>/resultless-stops.jsonl(pending/not-terminal/stale-mtime/ambiguous/no-artifact/no-result-json), so the trigger half of Claude adapter: Stop hook + idle detection re-entrant loop prevents session from ending #149 — why a completed-looking session read as result-less — is answerable straight from the run dir next time.Verification
dev_stall_grace_s=2, cap=2): a session that answers every nudge with a fresh result-less Stop received exactly 2 nudges, thensession-end stalled→story-deferred→run-complete, with oneno-artifactbreadcrumb per Stop. Pre-fix this scenario nudges until the 90-minute timeout.Summary by CodeRabbit
Bug Fixes
New Settings
dev_stall_nudges_cap, defaulting to 6, to control the maximum wake nudges per session.Documentation