Skip to content

fix(herdr): corroborate busy cursor panes before waiting-on-human escalation - #11

Merged
Amplify-Logic merged 4 commits into
mainfrom
fm/firstmate-herdr-cursor-liveness-l3
Jul 20, 2026
Merged

fix(herdr): corroborate busy cursor panes before waiting-on-human escalation#11
Amplify-Logic merged 4 commits into
mainfrom
fm/firstmate-herdr-cursor-liveness-l3

Conversation

@Amplify-Logic

Copy link
Copy Markdown
Owner

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_busy now checks blocked/idle herdr readings against the phase-stable busy footer signature (e.g. ctrl+c to stop, esc to interrupt) before escalating, and fm-watch.sh's handle_push_transition adds 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.
  • Added regression coverage in tests/fm-backend-herdr.test.sh and tests/fm-supervision-events.test.sh for the observed wP:p4 false-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.
  • Updated docs/architecture.md, docs/herdr-backend.md, and docs/cursor-harness.md to 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:p4

=== Manual replay of the 2026-07-19 default:wP:p4 incident ===

--- Case 1: herdr pushes agent_status=blocked while the cursor pane is MID-TURN ---
pane tail rendered by cursor:
  → Add a follow-up                                          ctrl+c to stop
wake queue: (empty - no stale wake fired)
supervisor wakes: (none)
triage log: [2026-07-21T01:10:28+0200] absorbed push blocked (busy footer, not waiting on human): default:wP:p4
dedupe marker committed: no

--- Case 2: same blocked push, but the cursor pane is genuinely IDLE ---
pane tail rendered by cursor:
  → Add a follow-up

  Cursor Grok 4.5 Low · 7%
wake queue: 1784589028	1	stale	default:wP:p4	stale: default:wP:p4 (herdr: agent blocked - waiting on human, escalated immediately, not via wedge timer)
supervisor wakes: SUPERVISOR WOKEN: stale: default:wP:p4 (herdr: agent blocked - waiting on human, escalated immediately, not via wedge timer)
Evidence: herdr backend regression test log (busy-footer coverage)
== tests/fm-backend-herdr.test.sh ==
ok - fm_backend_herdr_version_check: accepts the current protocol (14)
ok - fm_backend_herdr_version_check: refuses an old protocol loudly
ok - fm_backend_herdr_version_check: refuses loudly when herdr is not installed
ok - fm_backend_herdr_workspace_label: a primary home (no marker) resolves to 'firstmate'
ok - fm_backend_herdr_workspace_label: a secondmate home (.fm-secondmate-home) resolves to '2ndmate-<id>'
ok - fm_backend_herdr_workspace_label: trims whitespace around the marker's secondmate id
ok - fm_backend_herdr_workspace_label: an empty marker file falls back to the primary label 'firstmate'
ok - fm_backend_herdr_workspace_label: two different secondmate homes get two different, non-colliding labels
ok - fm_backend_herdr_cli: sets HERDR_SESSION AND appends a trailing --session flag on every call
ok - fm_backend_herdr_container_ensure: version-gates, starts the server, ensures the firstmate workspace, echoes session:workspace_id + the seeded default tab id
ok - fm_backend_herdr_container_ensure: reuses an existing firstmate workspace without recreating it, and reports no seeded default tab (adopted, not created)
ok - fm_backend_herdr_container_ensure: workspace create passes --no-focus
ok - fm_backend_herdr_container_ensure: creates the workspace under the SECONDMATE home's own label, not 'firstmate'
ok - fm_backend_herdr_create_task: prunes exactly the seeded default tab container_ensure identified, once the first real task tab exists
ok - herdr repeated spawn/teardown: one persistent firstmate workspace reused, zero orphans, default tab pruned, create ran once
ok - fm_backend_herdr_create_task: an ADOPTED workspace's pre-existing tab is never pruned (the created-vs-adopted gate)
ok - fm_backend_herdr_create_task: the label-collision startup-workspace scenario (2026-07-02 incident) leaves the captain's live tab untouched
ok - fm_backend_herdr_workspace_prune_seeded_default_tab: refuses to close the seeded default tab when its pane reports a working agent (defense in depth)
ok - no bin/ jq filter names a --arg/--argjson variable after a jq reserved keyword
ok - fm_backend_herdr_create_task: refuses a duplicate tab label (herdr's own tab create has no uniqueness check)
ok - fm_backend_herdr_create_task: a same-labeled tab with a live (even idle) registered agent still refuses exactly as before
ok - fm_backend_herdr_create_task: scans every same-labeled tab and refuses if any duplicate is live
ok - fm_backend_herdr_create_task: closes and replaces a same-labeled tab whose pane is dead (pane_not_found)
ok - fm_backend_herdr_create_task: closes and replaces a same-labeled tab whose pane is alive but hosts no registered agent (a restored plain shell)
ok - fm_backend_herdr_create_task: closes every confirmed same-labeled husk only after creating the replacement
ok - fm_backend_herdr_create_task: refuses success when a preexisting husk tab remains after replacement
ok - fm_backend_herdr_create_task: refuses (fail-safe) rather than guessing when the duplicate's agent state cannot be classified confidently
ok - fm_backend_herdr_create_task: creates the replacement tab BEFORE closing the husk tab, never the reverse
ok - fm_backend_herdr_create_task: creates a tab and parses tab_id/pane_id from the JSON response, prunes nothing when no seeded tab id is given
ok - fm_backend_herdr_create_task: tab create passes --no-focus
ok - fm_backend_herdr_workspace_find: matches only THIS home's own label among several coexisting workspaces
ok - fm_backend_herdr_list_live: scoped to this home's own workspace, never a sibling home's
ok - managed Herdr workspace lookup uses hidden physical home/project tokens, never its human label
ok - managed Herdr tasks use hidden ids, allow duplicate human outcomes, and recover both rows
ok - list_live supports hidden and legacy recovery while refusing another home workspace
ok - fm_backend_herdr_parse_target: splits '<session>:<pane_id>' on the FIRST colon (pane_id itself contains one)
ok - fm_backend_herdr_normalize_key: Enter/Escape/C-c map to herdr's verified enter/escape/ctrl+c
ok - fm_backend_herdr_capture: calls 'pane read <pane> --source recent --lines N' with the session set
ok - fm_backend_herdr_capture: works around the verified small-N '--lines' bug by over-fetching and trimming locally
ok - fm_backend_herdr_capture: ensures the session and preserves pane read failure
ok - fm_backend_herdr_send_key: normalizes the key and targets the right pane
ok - fm_backend_herdr_kill: calls pane close and stays best-effort on failure
ok - fm_backend_herdr_current_path: reads pane foreground_cwd (the live running process), not the frozen creation-time cwd
ok - fm_backend_herdr_busy_state: working -> busy
ok - fm_backend_herdr_busy_state: done -> idle, blocked -> idle (surfaced like a stale pane, not suppressed as busy)
ok - fm_backend_herdr_busy_state: unparseable/absent agent state reports unknown, the regex-fallback cue
ok - fm_backend_herdr_composer_state: a bare '❯' composer row reads empty
ok - fm_backend_herdr_composer_state: the ghost placeholder text reads empty, not pending
ok - fm_backend_herdr_composer_state: real composer text reads pending
ok - fm_backend_herdr_composer_state: a slash-command popup's argument-hint placeholder still reads pending (the incident fix)
ok - fm_backend_herdr_composer_state: reports unknown when the pane cannot be captured
ok - fm_backend_herdr_composer_state: reports unknown for bare shell prompts with no composer row
ok - fm_backend_herdr_composer_state: a native idle Pi separator composer reads empty
ok - fm_backend_herdr_composer_state: real Pi composer text remains pending
ok - fm_backend_herdr_composer_state: an incomplete lower Pi separator cannot inherit a stale empty row
ok - fm_backend_herdr_composer_state: Pi separators never authorize working, non-Pi, unreadable, or over-tall targets
ok - fm_backend_herdr_composer_state: a real-claude unbordered '❯' prompt row (no border box in view) reads empty
ok - fm_backend_herdr_composer_state: a real-claude unbordered '❯ <text>' prompt row reads pending
ok - fm_backend_herdr_composer_state: a live unbordered prompt row below a stale bordered decorative box still wins (not misread as the box's own row)
ok - fm_backend_herdr_composer_state: claude's dim prompt-suggestion ghost (the overnight wedge shape) reads empty
ok - fm_backend_herdr_composer_state: real typed text on the same claude prompt row still reads pending
ok - fm_backend_herdr_composer_state: grok's dark-truecolor placeholder (the TRUECOLOR gap) reads empty
ok - fm_backend_herdr_composer_state: grok's real bright typed input still reads pending
ok - fm_backend_herdr_composer_state: a real-codex unbordered '›' prompt row reads empty
ok - fm_backend_herdr_composer_state: a faint real-codex ghost suggestion reads empty
ok - fm_backend_herdr_composer_state: non-faint codex prompt text still reads pending
ok - fm_backend_herdr_wait_for_working: reports 'busy' immediately on the first poll, without spending the rest of the budget
ok - fm_backend_herdr_wait_for_working: a slow transition landing on a later sample within one window is still caught (robust against the 'slow transition' failure direction)
ok - fm_backend_herdr_wait_for_working: spreads six samples across the full budget endpoint without a final trailing sleep
ok - fm_backend_herdr_send_text_submit: applies the herdr minimum confirmation budget before polling agent-state
ok - fm_backend_herdr_wait_for_working: reports 'idle' (readable, genuinely not yet working) when 'busy' never appears
ok - fm_backend_herdr_wait_for_working: reports 'unknown' (a hard read failure, not a timing race) only when EVERY poll in the window fails
ok - fm_backend_herdr_wait_for_working: treats blocked as submit-active for confirmation without changing watcher busy-state semantics
ok - fm_backend_herdr_send_text_submit: reports 'empty' once agent_status reports working after one Enter, without ever reading the composer
ok - fm_backend_herdr_send_text_submit: reports 'pending' when agent_status never reports working after retried Enters (swallowed)
ok - fm_backend_herdr_send_text_submit: a slash-command popup's placeholder fill on Enter #1 never flips agent_status to working, so it does not short-circuit as submitted; Enter #2 is retried and lands it
ok - fm_backend_herdr_send_text_submit: a post-Enter blocked state confirms delivery without retrying into the prompt
ok - fm_backend_herdr_send_text_submit: preexisting working is not accepted as submit proof when the composer still holds the message
ok - fm_backend_herdr_send_text_submit: confirms submission via native agent-state alone, immune to a codex-style dynamic idle-tip composer that would have misread as 'pending' under the old composer-based confirmation
ok - fm_backend_herdr_composer_state: a faint real-codex dynamic idle-tip composer row reads empty
ok - fm_backend_composer_state (herdr): the pre-injection empty-box guard still refuses a genuinely non-empty composer, unaffected by the submit-confirmation change
ok - fm_backend_herdr_send_text_submit: a slow transition landing on a later sample within one Enter's budget is confirmed WITHOUT sending a needless extra Enter
ok - fm_backend_herdr_send_text_submit: reports 'send-failed' when the literal send-text call itself errors
ok - fm_backend_herdr_send_text_submit: reports 'unknown' when the post-Enter agent-get read fails (never retries past an unreadable target)
ok - fm_backend_validate: herdr is a known backend (P2)
ok - fm_backend_busy_state: tmux (no native primitive) always reports unknown, preserving the P1 regex-only path
error: unknown backend 'bogus' (known: tmux herdr zellij orca cmux)
ok - fm_backend_composer_state dispatches tmux/herdr/orca to their named classifiers, unknown for zellij/unrecognized backends
ok - fm-peek/fm-send: explicit stale targets matching metadata use the recorded backend
ok - fm_backend_herdr_normalize_event routes through the shared record with an empty from_status
ok - fm_backend_herdr_escalation_marker keys the dedupe marker exactly like the watcher's .stale-<key>
ok - fm_backend_herdr_apply_transition: blocked dedupe starts only after explicit commit
ok - fm_backend_herdr_capture_shows_busy: cursor mid-turn footer (ctrl+c to stop) reads busy
ok - fm_backend_herdr_capture_shows_busy: idle cursor pane (no ctrl+c to stop) is not busy
ok - fm_backend_herdr_capture_shows_busy: spinner verb alone is not the busy signal
ok - fm_backend_herdr_apply_transition: blocked with cursor busy footer (wP:p4) is absorbed, not actionable
ok - fm_backend_herdr_apply_transition: blocked with idle cursor pane remains actionable (waiting on human)
ok - fm_backend_herdr_apply_transition: a working edge clears the marker so the next ->blocked re-escalates
ok - fm_backend_herdr_clear_transition removes task-owned dedupe state
ok - fm_backend_herdr_apply_transition: idle/done (defer) and unknown/empty (fallback) take no fast action
ok - fm_backend_herdr_wait_transition: a home with no herdr panes falls back to polling (rc 2)
ok - fm_backend_herdr_wait_transition: below-capability protocol/schema falls back to polling (rc 2)
ok - fm_backend_herdr_wait_transition: reconnect level-reconcile returns an uncommitted blocked pane
ok - fm_backend_herdr_wait_transition: subscribes before reconnect level-reconcile
ok - fm_backend_herdr_wait_transition: a still-blocked, already-escalated pane is not re-delivered on reconnect
ok - fm_backend_herdr_wait_transition: a streamed ->blocked edge returns the record sub-poll
ok - fm_backend_herdr_wait_transition: streamed working clears the marker, idle/done are deferred (clean timeout)
ok - fm_backend_herdr_wait_transition: a reader/subscribe failure falls back to polling (rc 2)
ok - fm_backend_herdr_wait_transition: Bash 3.2-safe bad-ack path closes fd 9 and removes its FIFO
ok - fm_backend_herdr_wait_transition: stock macOS Bash clean timeout closes fd 9 and returns 1
rc=0
Evidence: watcher supervision-events test log (wP:p4 absorb + idle escalation + window_is_busy)
== tests/fm-supervision-events.test.sh ==
ok - handle_push_transition: a blocked crew enqueues a stale wake naming its window and wakes the supervisor
ok - handle_push_transition: enqueue failure cannot commit the Herdr dedupe marker
ok - handle_push_transition: a declared-pause crew is absorbed (no fast wake), left to the poll loop's long cadence
ok - handle_push_transition: busy cursor footer (ctrl+c to stop on wP:p4) absorbs false blocked
ok - handle_push_transition: idle cursor pane with blocked still escalates as waiting on human
ok - window_is_busy: herdr idle is corroborated by the pane busy footer (cursor ctrl+c to stop)
ok - event_wait_or_sleep: herdr windows go on the event pane list, but kind=secondmate endpoints are excluded
ok - event_wait_or_sleep: one cached capability probe owns validation across bounded waits
ok - event_wait_or_sleep: a home with no push-capable window is inert (sleeps POLL, never touches the event path)
ok - event_wait_or_sleep: consecutive event-path failures disable the fast-path and revert to pure polling (fail-closed)
# fm-supervision-events.test.sh: all assertions passed
rc=0
Evidence: previously failing tests now passing (cmux, secondmate liveness, herdr spawn)
== tests/fm-backend-cmux.test.sh ==
ok - fm_backend_cmux_version_check: accepts the verified minimum (0.64.17)
ok - fm_backend_cmux_version_check: accepts a newer version (0.70.0)
ok - fm_backend_cmux_version_check: refuses an old version loudly
ok - fm_backend_cmux_version_check: refuses loudly when cmux is not found on PATH or at the bundle path
ok - fm_backend_cmux_password: reads the first non-empty line of config/cmux-socket-password
ok - fm_backend_cmux_password: preserves spaces and tabs in config/cmux-socket-password
ok - fm_backend_cmux_password: respects FM_CONFIG_OVERRIDE
ok - fm_backend_cmux_password: empty when config/cmux-socket-password is absent
ok - fm_backend_cmux_cli: exports CMUX_SOCKET_PASSWORD when config/cmux-socket-password is set
ok - fm_backend_cmux_parse_target: splits '<workspace_uuid>:<surface_uuid>' on the first colon
ok - fm_backend_cmux_normalize_key: Enter/Escape/C-c map to cmux's verified enter/escape/ctrl-c
ok - fm_backend_cmux_scoped_title: scopes a primary task title with firstmate plus root hash
ok - fm_backend_cmux_scoped_title: scopes a secondmate task title with the home marker plus root hash
ok - fm_backend_cmux_scoped_title: includes the resolved FM_ROOT hash in the home label
ok - fm_backend_validate: cmux is a known backend
ok - fm_backend_busy_state: cmux (no native primitive) always reports unknown, same as tmux/zellij/orca
ok - fm_backend_composer_state: routes cmux to the cmux composer classifier
ok - fm_backend_cmux_ping_state: reports 'ok' on PONG
ok - fm_backend_cmux_ping_state: reports 'denied' when socketControlMode=cmuxOnly rejects the connection
ok - fm_backend_cmux_ping_state: reports 'unauth' when password mode rejects a missing/wrong password
ok - fm_backend_cmux_ping_state: reports 'unauth' when password mode rejects a wrong password (Invalid password)
ok - fm_backend_cmux_ping_state: reports 'down' when the app is not running yet
ok - fm_backend_cmux_ensure_running: returns immediately when cmux is already reachable
ok - fm_backend_cmux_ensure_running: fails fast on a denied socket without attempting to launch, naming every viable mode
ok - fm_backend_cmux_ensure_running: fails fast on an unauthenticated socket, naming the password config and the Automation mode alternative
ok - fm_backend_cmux_create_task: refuses a duplicate workspace title (cmux's own new-workspace has no uniqueness check)
ok - fm_backend_cmux_create_task: creates a workspace and parses workspace_id/surface_id from list responses
ok - fm_backend_cmux_target_ready: fails when the workspace/surface is not found (list-panes structural check)
ok - fm_backend_cmux_target_ready: verifies the workspace title against the expected label first
ok - fm_backend_cmux_target_ready: rejects a workspace id reused under a different title
ok - fm_backend_cmux_capture: fetches generously and trims to N lines locally
ok - fm_backend_cmux_capture: propagates a read-screen failure even when stdout is empty
ok - fm_backend_cmux_capture: fails when the target surface is absent
ok - fm_backend_cmux_send_key: normalizes the key (Escape -> escape) and targets the explicit workspace/surface
ok - fm_backend_cmux_send_key: recovers stale workspace/surface ids by expected label
ok - fm_backend_cmux_send_literal: calls send with an explicit workspace/surface and a -- separator
ok - fm_backend_cmux_current_path: actively probes with marked begin/end lines (zellij-shape frozen cwd)
ok - fm_backend_cmux_composer_state: a bare '❯' composer row reads empty
ok - fm_backend_cmux_composer_state: the ghost placeholder text reads empty, not pending
ok - fm_backend_cmux_composer_state: real composer text reads pending
ok - fm_backend_cmux_composer_state: a slash-command popup's argument-hint placeholder still reads pending (the incident fix)
ok - fm_backend_cmux_composer_state: reports unknown when the surface cannot be captured
ok - fm_backend_cmux_composer_state: reports unknown when no border-delimited composer row is found
ok - fm_backend_cmux_send_text_submit: reports 'empty' once the composer row reads empty after one Enter
ok - fm_backend_cmux_send_text_submit: reports 'pending' when the composer never clears after retried Enters (swallowed)
ok - fm_backend_cmux_send_text_submit: retries past a popup-placeholder-fill Enter and lands the real second Enter (the incident fix)
ok - fm_backend_cmux_send_text_submit: reports 'send-failed' when the target workspace/surface is absent
ok - fm_backend_cmux_window_of_workspace: walks windows and counts the membership-confirming workspace list
ok - fm_backend_cmux_window_of_workspace: echoes nothing when no window holds the workspace
ok - fm_backend_cmux_kill: closes the task workspace directly when it is not the last in its window
ok - fm_backend_cmux_kill: adds a throwaway sibling then closes the target when it is the last workspace in its window
ok - fm_backend_cmux_kill: never fails even when close-workspace fails
ok - fm_backend_cmux_kill: recovers stale workspace/surface ids by expected label
ok - fm_backend_cmux_list_live: lists only this home's scoped task workspaces using plain fm-<id> labels
ok - fm-spawn.sh: refuses backend=cmux for --secondmate spawns (mirrors Orca's refusal; no secondmate launch design exists yet)
rc=0
- Outcome: 🔧 1 issue found → auto-fixed ✅ across 2 runs (1h19m13s)

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

⚠️ **Review** - 4 infos
  • ℹ️ 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 ✅
  • 🚨 tests failed with exit code 1
  • 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 suite for t in tests/*.test.sh; do bash &#34;$t&#34;; 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 corroboration
  • bash tests/fm-secondmate-liveness.test.sh and bash tests/fm-spawn-herdr-presentation.test.sh — the two previously failing e2e tests, now passing
  • bash 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 ✅
  • ⚠️ linter found issues (exit code 1)

🔧 Fix: silence SC2329 on indirectly-invoked test stub
✅ Re-checked - no issues remain.

✅ **Push** - passed

✅ No issues found.

Amplify-Logic and others added 4 commits July 20, 2026 23:46
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>
@Amplify-Logic
Amplify-Logic merged commit ff1015f into main Jul 20, 2026
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