feat(watcher): absorb benign wakes, only when the crew is provably working - #15
Merged
Merged
Conversation
…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.
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.
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 viafm-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 (nodone: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 pastFM_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-duplicateCAPTAIN_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 readmeta loop (not upstream'swindow=/tmux capture-pane) andw="fm-<id>"labels;window_to_taskmapsfm-<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: fakeherdrpane-read +FM_CREW_STATE_BINstub) pins the classifier predicates and the behavioral absorb/surface contract.tests/fm-wake-queue.test.shgains 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/*.shclean; 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.shis no longer one-shot on every detection—it classifies wakes in bash, advances suppression markers for benign ones, logs tostate/.watch-triage.log, and only enqueues and exits on actionable events so firstmate re-arms once per real need instead of on everyworking:/ turn-end during long validation.Safety (kunchenguid#126): no-verb signals and non-terminal stale panes are absorbed only when
crew_is_provably_working(viabin/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 afterFM_STALE_ESCALATE_SECS; no-change heartbeats absorb unless a fleet-scan backstop finds an unsurfaced captain-relevant line.New
bin/fm-classify-lib.shcentralizes captain-verb regex, signal/stale/scan helpers, and the provably-working predicate;bin/fm-supervise-daemon.shdrops duplicate classifiers and uses the shared scan. Whilestate/.afkis 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 infm-wake-queue.test.shlock the contract.Reviewed by Cursor Bugbot for commit cd90eb2. Bugbot is set up for automated code reviews on this repo. Configure here.