Skip to content

feat(watcher): absorb benign wakes, only when the crew is provably working - #15

Merged
leo1oel merged 1 commit into
herdr-backendfrom
port-watch-absorption
Jun 29, 2026
Merged

leo1oel merged 1 commit into
herdr-backendfrom
port-watch-absorption

Conversation

@leo1oel

@leo1oel leo1oel commented Jun 29, 2026 •

Copy link
Copy Markdown
Owner

Ports upstream kunchenguid#107 + kunchenguid#126 to the herdr fork — always-on in-bash wake triage.

What

The watcher classifies every wake in bash and absorbs the benign majority (advance the suppression marker, log to state/.watch-triage.log, keep blocking) instead of waking firstmate's LLM for each. It queues + exits only on an actionable wake, so firstmate re-arms once per actionable event — eliminating quiet-stretch churn during a long validation.

kunchenguid#126's safety inversion: a no-verb wake (bare turn-end, working: note, non-terminal stale) is absorbed only while the crew is provably working — its no-mistakes run is in an actively-running step, or its pane shows the busy signature (read via fm-crew-state.sh). A crew that stopped with no running pipeline + no busy pane is surfaced immediately, so a finish reported only through interactive pane menus (no done: status) is never swallowed.

Pieces

  • bin/fm-classify-lib.sh (new) — shared classifier (captain-verb set, signal/stale/heartbeat predicates, scan_captain_relevant_statuses, crew_is_provably_working). Sourced by both the watcher and the away-mode daemon so the overlapping policy can't drift.
  • bin/fm-watch.sh — absorb gates on signal/stale/heartbeat; the costly provably-working read runs only on the no-verb, non-afk path. Not-provably-working non-terminal stale surfaces at once; provably-working one absorbs + starts a wedge timer that still escalates past FM_STALE_ESCALATE_SECS. Heartbeats absorb unless the fleet-scan backstop catches an unsurfaced captain-relevant status. afk → one-shot, skips the provably-working read.
  • bin/fm-supervise-daemon.sh — sources the lib, drops its now-duplicate CAPTAIN_RE_DEFAULT/last_status_line/status_is_captain_relevant/window_to_task; housekeeping uses the shared scan.

herdr adaptation

The stale path keeps our handle=/fm-backend read meta loop (not upstream's window=/tmux capture-pane) and w="fm-<id>" labels; window_to_task maps fm-<id> cleanly. Busy detection stays the fork's Claude-only regex. X-mode references in upstream's docs are dropped (we skipped X-mode).

Tests

tests/fm-watch-triage.test.sh (new, 11 cases, self-contained: fake herdr pane-read + FM_CREW_STATE_BIN stub) pins the classifier predicates and the behavioral absorb/surface contract. tests/fm-wake-queue.test.sh gains a queue-safety test (provably-working stale never enters the durable queue). AGENTS.md §2/§5/§8, the afk skill, and docs synced.

shellcheck bin/*.sh tests/*.sh clean; all 14 suites green.


Note

Medium Risk
Supervision behavior changes materially—benign wakes no longer always reach firstmate—so mis-triage could delay or miss escalations; mitigated by provably-working checks, immediate surface when not working, heartbeat backstop, and extensive tests.

Overview
Ports upstream kunchenguid#107 + kunchenguid#126: bin/fm-watch.sh is no longer one-shot on every detection—it classifies wakes in bash, advances suppression markers for benign ones, logs to state/.watch-triage.log, and only enqueues and exits on actionable events so firstmate re-arms once per real need instead of on every working: / turn-end during long validation.

Safety (kunchenguid#126): no-verb signals and non-terminal stale panes are absorbed only when crew_is_provably_working (via bin/fm-crew-state.sh: active no-mistakes run-step or busy pane). Crews that stopped without a captain-relevant status surface immediately, including finishes reported only through pane UI. Provably-working stale panes can still wedge-escalate after FM_STALE_ESCALATE_SECS; no-change heartbeats absorb unless a fleet-scan backstop finds an unsurfaced captain-relevant line.

New bin/fm-classify-lib.sh centralizes captain-verb regex, signal/stale/scan helpers, and the provably-working predicate; bin/fm-supervise-daemon.sh drops duplicate classifiers and uses the shared scan. While state/.afk is set, the watcher reverts to one-shot (every wake to the daemon; no absorb path).

Docs/skills and tests/fm-watch-triage.test.sh (11 cases) plus a queue-safety case in fm-wake-queue.test.sh lock the contract.

Reviewed by Cursor Bugbot for commit cd90eb2. Bugbot is set up for automated code reviews on this repo. Configure here.

…rking

Port upstream kunchenguid#107 + kunchenguid#126 to the herdr fork: always-on in-bash wake triage.

The watcher now classifies every wake in bash and absorbs the benign majority
(advance the suppression marker, log to state/.watch-triage.log, keep blocking)
instead of waking firstmate's LLM for each. It queues and exits only on an
actionable wake, so firstmate re-arms once per actionable event instead of once
per wake - eliminating the quiet-stretch churn during a long crew validation.

kunchenguid#126's inversion is the safety property: a no-verb wake (a bare turn-end, a
working: note, a non-terminal stale) is absorbed ONLY while the crew shows
positive evidence it is still working - its no-mistakes run for its branch is in
an actively-running step, or its pane shows the harness busy signature, read via
bin/fm-crew-state.sh. A crew that stopped with no running pipeline and no busy
pane is surfaced immediately, so a finish reported only through interactive pane
menus (no done: status) is never swallowed.

- bin/fm-classify-lib.sh (new): the shared classifier - captain-relevant verb
  set, signal/stale/heartbeat predicates, scan_captain_relevant_statuses, and the
  provably-working predicate (crew_is_provably_working, reusing fm-crew-state.sh;
  FM_CREW_STATE_BIN override for tests). Sourced by BOTH the watcher and the
  away-mode daemon so the overlapping policy cannot drift.
- bin/fm-watch.sh: absorb gates on the signal / stale / heartbeat paths; the
  costly provably-working read runs only on the no-verb, non-afk path. A
  not-provably-working non-terminal stale surfaces at once; a provably-working one
  absorbs and starts a wedge timer that still escalates past FM_STALE_ESCALATE_SECS.
  Heartbeats absorb unless the fleet-scan backstop finds an unsurfaced
  captain-relevant status. While state/.afk exists the watcher reverts to one-shot
  (every wake surfaced for the daemon) and skips the provably-working read.
- bin/fm-supervise-daemon.sh: sources fm-classify-lib.sh and drops its now-duplicate
  CAPTAIN_RE_DEFAULT / last_status_line / status_is_captain_relevant / window_to_task;
  its housekeeping heartbeat scan uses the shared scan_captain_relevant_statuses.
- bin/fm-watch-arm.sh: comment updated for the actionable-wake behavior.

herdr adaptation: the stale path keeps our handle=/fm-backend read meta loop
(not upstream's window=/tmux capture-pane) and w="fm-<id>" labels; window_to_task
maps "fm-<id>" cleanly. Busy detection stays the fork's Claude-only regex.

Tests: tests/fm-watch-triage.test.sh (new, 11 cases, self-contained per the
fork's harness convention with a fake herdr pane-read + FM_CREW_STATE_BIN stub)
covers the classifier predicates and the behavioral absorb/surface contract
(captain-verb surface, no-verb absorb-when-working, no-verb surface-when-stopped,
terminal stale, non-terminal stale absorb-then-wedge, immediate stopped-crew
surface, heartbeat absorb + backstop). tests/fm-wake-queue.test.sh gains a
queue-safety test that a provably-working stale never enters the durable queue.
AGENTS.md sections 2/5/8, the afk skill, and docs synced. All 14 suites green;
shellcheck clean.
@leo1oel
leo1oel merged commit a22dc58 into herdr-backend Jun 29, 2026
1 check passed
@leo1oel
leo1oel deleted the port-watch-absorption branch June 29, 2026 09:19
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