fix(herdr): corroborate busy cursor panes before waiting-on-human escalation - #11
Merged
Merged
Conversation
Herdr can report agent_status=blocked while a cursor worker is mid-turn (ctrl+c to stop footer). Corroborate blocked/idle against that busy signature before immediate stale escalation or poll-path idle. Co-authored-by: Cursor <cursoragent@cursor.com>
…mary Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Intent
Fix herdr misclassifying a BUSY cursor pane as blocked/waiting-on-human, which fired immediate stale wakes for healthy cursor workers (observed 2026-07-19 on default:wP:p4 mid-turn with the ctrl+c to stop busy footer). Corroborate herdr blocked/idle readings against that phase-stable busy signature before escalation; do not match the spinner verb (Working/Running). Idle cursor panes must still escalate correctly. Include regression coverage for the wP:p4 false-blocked scenario.
What Changed
fm_backend_herdr_capture_shows_busynow checks blocked/idle herdr readings against the phase-stable busy footer signature (e.g.ctrl+c to stop,esc to interrupt) before escalating, andfm-watch.sh'shandle_push_transitionadds a matching defense-in-depth capture, so a mid-turn busy cursor pane is absorbed instead of firing a stale waiting-on-human wake; genuinely idle panes still escalate.tests/fm-backend-herdr.test.shandtests/fm-supervision-events.test.shfor the observedwP:p4false-blocked scenario (busy footer absorbed, spinner verbs like Working/Running ignored, idle pane escalates), plus fixes for jq-1.6 and preflight fixture gaps in previously failing e2e tests and an SC2329 lint suppression on an indirectly-invoked test stub.docs/architecture.md,docs/herdr-backend.md, anddocs/cursor-harness.mdto document the idle/blocked corroboration behavior.Risk Assessment
✅ Low: Well-bounded fix that fails toward escalation on any capture error, fully satisfies the stated intent (busy-footer corroboration on both push and poll paths, spinner verb excluded, idle panes still escalate), and ships targeted regression tests; remaining findings are minor maintainability notes.
Testing
Baseline full e2e suite passed; targeted regression tests for the wP:p4 busy-footer fix and the three previously-failing tests all pass, and a manual replay of the incident through the real watcher shows the busy pane absorbed (triage log, empty wake queue, no marker) while an idle blocked pane still fires the immediate stale wake. No UI surface is involved (CLI/daemon behavior), so evidence is CLI transcripts and logs.
Evidence: Manual wP:p4 incident replay transcript (busy pane absorbed, idle pane escalates)
--- Case 1: herdr pushes blocked while cursor pane is MID-TURN --- pane tail: → Add a follow-up ctrl+c to stop wake queue: (empty - no stale wake fired) supervisor wakes: (none) triage log: absorbed push blocked (busy footer, not waiting on human): default:wP:p4 dedupe marker committed: no --- Case 2: same blocked push, cursor pane genuinely IDLE --- wake queue: stale default:wP:p4 (herdr: agent blocked - waiting on human, escalated immediately) supervisor wakes: SUPERVISOR WOKEN: stale: default:wP:p4Evidence: herdr backend regression test log (busy-footer coverage)
Evidence: watcher supervision-events test log (wP:p4 absorb + idle escalation + window_is_busy)
Evidence: previously failing tests now passing (cmux, secondmate liveness, herdr spawn)
Pipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ No issues found.
✅ **Rebase** - passed
✅ No issues found.
bin/backends/herdr.sh:1384- The default busy regex literal 'esc (to )?interrupt|Working...|Ctrl+c:cancel|ctrl+c to stop' now exists in three copies: bin/fm-tmux-lib.sh:58 (FM_TMUX_BUSY_REGEX_DEFAULT), bin/fm-watch.sh:121 (BUSY_REGEX), and the new inline default in fm_backend_herdr_capture_shows_busy. herdr.sh does not source fm-tmux-lib.sh, so the third copy can silently drift when a new harness footer is added to the shared default. Consider extracting the default into a small shared lib (or having herdr.sh reuse FM_TMUX_BUSY_REGEX_DEFAULT) so the corroboration gate and the poll-path regex can never disagree.bin/backends/herdr.sh:1384- The intent forbids matching the spinner verb (Working/Running), and the corroboration regex conforms for the observed cursor forms (bare 'Working'/'Running' don't match; the regression test covers this). However the shared FM_BUSY_REGEX default does contain 'Working...' (another harness's footer), so if cursor ever renders its spinner verb with a literal trailing ellipsis, the gate would match the spinner after all. Noting the residual coupling; no action required now.tests/fm-supervision-events.test.sh:112- The new window_is_busy test block overrides fm_backend_busy_state() to always print 'idle' and never restores it, so the override leaks into every later test in tests/fm-supervision-events.test.sh. The currently-following tests (event_wait_or_sleep pane-list exclusion) don't consult busy state, and earlier tests in the file already leak fm_backend_capture the same way, but any future test appended below this point that relies on real busy-state resolution will silently see 'idle'. Consider unset -f (or a saved/restored definition) after the assertion block.bin/fm-watch.sh:661- A genuine blocked escalation on the push path now performs two pane captures: fm_backend_herdr_apply_transition captures via fm_backend_herdr_capture_shows_busy, then handle_push_transition captures again for its defense-in-depth check. This is a deliberate layering tradeoff and the captures are bounded (40 lines), so it's acceptable; noting it in case the double read ever shows up in herdr CLI latency.🔧 **Test** - 1 issue found → auto-fixed ✅
command -v tmux >/dev/null || { echo "tmux is required for e2e tests" >&2; exit 1; }; tmux -V; rc=0; for t in tests/*.test.sh; do echo "== $t =="; bash "$t" || rc=1; done; exit "$rc"🔧 Fix: fix jq-1.6 and preflight fixture gaps in failing tests
✅ Re-checked - no issues remain.
command -v tmux >/dev/null || { echo "tmux is required for e2e tests" >&2; exit 1; }; tmux -V; rc=0; for t in tests/*.test.sh; do echo "== $t =="; bash "$t" || rc=1; done; exit "$rc"Baseline configured suitefor t in tests/*.test.sh; do bash "$t"; done(ran successfully before this round)bash tests/fm-backend-herdr.test.sh— includes the new fm_backend_herdr_capture_shows_busy and apply_transition busy-footer regression tests (busy footer matches, idle pane does not, spinner verb ignored, busy-blocked absorbed, idle-blocked actionable)bash tests/fm-supervision-events.test.sh— handle_push_transition wP:p4 busy-footer absorb, idle-pane escalation, and window_is_busy idle corroborationbash tests/fm-secondmate-liveness.test.shandbash tests/fm-spawn-herdr-presentation.test.sh— the two previously failing e2e tests, now passingbash tests/fm-backend-cmux.test.sh— send_text_submit 'send-failed' on absent target (jq-1.6 surface_exists fix)Manual replay of the 2026-07-19 default:wP:p4 incident through the real bin/fm-watch.sh handle_push_transition, capturing wake queue, triage log, dedupe marker, and supervisor wake state for both a mid-turn busy pane and a genuinely idle pane✅ **Document** - passed
✅ No issues found.
🔧 **Lint** - 1 issue found → auto-fixed ✅
🔧 Fix: silence SC2329 on indirectly-invoked test stub
✅ Re-checked - no issues remain.
✅ **Push** - passed
✅ No issues found.