From c53403870bdba244e9acbb9fbb788c6462e49b4b Mon Sep 17 00:00:00 2001 From: AG Werschky Date: Mon, 3 Aug 2026 18:50:45 -0600 Subject: [PATCH 1/3] fix(afk): stop catch-all re-escalating already-surfaced completions The away-mode catch-all scan only consulted .subsuper-seen-status-*, so a done: line firstmate already handled via always-on (.hb-surfaced-*) was re-escalated when afk started. Honour both markers in the scan without promoting hb into subsuper-seen, so the signal path still delivers. --- bin/fm-supervise-daemon.sh | 31 +++++++++++++++++++++++++++---- tests/fm-daemon.test.sh | 28 ++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 4 deletions(-) diff --git a/bin/fm-supervise-daemon.sh b/bin/fm-supervise-daemon.sh index 400a8bf535..ca521b55df 100755 --- a/bin/fm-supervise-daemon.sh +++ b/bin/fm-supervise-daemon.sh @@ -528,6 +528,29 @@ mark_status_seen() { # printf '%s' "$line" > "$state/.subsuper-seen-status-$(_stale_key "$task")" } +# 0 if this captain-relevant last line was already escalated or already +# surfaced to firstmate, so the catch-all scan must not re-fire it. +# Checks, in order: +# 1. .subsuper-seen-status- - written by the daemon's per-wake escalate +# path (mark_escalated_seen) and by the catch-all itself after it escalates. +# 2. .hb-surfaced- - written by the always-on watcher when it surfaces a +# captain-relevant status to firstmate (and also when the afk watcher hands +# the same line to this daemon). Without (2), a fresh away-mode session +# re-escalates historical done: lines firstmate already handled while afk +# was off - the defect observed 2026-08-02. +# Does NOT promote hb into subsuper-seen: the always-on/afk watcher marks hb +# before the daemon's signal path runs, and promoting would let classify_signal +# self-handle as "already escalated" without ever delivering the event. +status_line_already_seen() { # + local state=$1 task=$2 line=$3 key seen + key=$(_stale_key "$task") + seen="$state/.subsuper-seen-status-$key" + [ "$(cat "$seen" 2>/dev/null || true)" = "$line" ] && return 0 + seen="$state/.hb-surfaced-$key" + [ "$(cat "$seen" 2>/dev/null || true)" = "$line" ] && return 0 + return 1 +} + # Mark every captain-relevant status line a per-wake classification escalated as # seen, so the catch-all scan does not re-escalate the same line within # HEARTBEAT_SCAN_SECS. Mirrors classify_signal/classify_stale's relevance test. @@ -1060,14 +1083,14 @@ housekeeping() { # # (3) heartbeat scan (catch-all for a captain-relevant status the per-wake # classifier may have missed). Cheap: status files only, no tmux. The # captain-relevant filtering is the shared classifier's - # scan_captain_relevant_statuses; the daemon layers its digest dedup on top. + # scan_captain_relevant_statuses; the daemon layers its digest dedup on top + # via status_line_already_seen (subsuper-seen from escalate paths, and + # hb-surfaced from always-on / watcher surfacing - see that helper). if [ "$(_file_age "$state/.subsuper-last-scan")" -ge "${FM_HEARTBEAT_SCAN_SECS:-$HEARTBEAT_SCAN_SECS_DEFAULT}" ]; then _now > "$state/.subsuper-last-scan" - local seen while IFS="$(printf '\t')" read -r f task last; do [ -n "$f" ] || continue - seen="$state/.subsuper-seen-status-$(_stale_key "$task")" - [ "$(cat "$seen" 2>/dev/null || true)" = "$last" ] && continue + status_line_already_seen "$state" "$task" "$last" && continue escalate_add "$state" "$(basename "$f"): $last (catch-all scan)" mark_status_seen "$state" "$task" "$last" done < <(scan_captain_relevant_statuses "$state") diff --git a/tests/fm-daemon.test.sh b/tests/fm-daemon.test.sh index 0cadb5af1f..67cbbb8620 100755 --- a/tests/fm-daemon.test.sh +++ b/tests/fm-daemon.test.sh @@ -715,6 +715,33 @@ test_signal_escalate_marks_seen_no_catchall_refire() { pass "captain signal escalate marks seen so the catch-all scan does not re-fire" } +test_catchall_honours_hb_surfaced_no_refire() { + # Always-on firstmate already surfaced a done: line (watcher wrote + # .hb-surfaced-*), then away mode starts. The catch-all must NOT re-escalate + # that historical completion - the 2026-08-02 production defect. + local dir state key line + dir=$(make_supercase scan-hb-surfaced) + state="$dir/state" + line='done: Leads stage seed manifest landed - https://example.test/pr/131' + printf '%s\n' "$line" > "$state/hist-t9.status" + key=$(printf '%s' "hist-t9" | tr ':/.' '___') + # Simulate always-on mark_surfaced only (no daemon escalate / no subsuper-seen). + printf '%s' "$line" > "$state/.hb-surfaced-$key" + [ ! -e "$state/.subsuper-seen-status-$key" ] || fail "precondition: subsuper-seen must be absent" + rm -f "$state/.subsuper-last-scan" + : > "$state/.subsuper-escalations" + FM_STATE_OVERRIDE="$state" housekeeping "$state" + [ ! -s "$state/.subsuper-escalations" ] \ + || fail "catch-all re-escalated a completion already surfaced via .hb-surfaced: $(cat "$state/.subsuper-escalations")" + # Signal path must still escalate a captain-relevant line that only has + # hb-surfaced (not subsuper-seen): the watcher marks hb before the daemon + # classifies, and promoting hb into subsuper would swallow that delivery. + FM_STATE_OVERRIDE="$state" handle_wake "signal: $state/hist-t9.status" "$state" + [ -s "$state/.subsuper-escalations" ] \ + || fail "signal path failed to escalate when only .hb-surfaced matched (would drop the delivery)" + pass "catch-all honours .hb-surfaced without blocking the signal escalate path" +} + test_collapse_newlines_pure() { local out out=$(_collapse_newlines $'line one\nline two\nline three') @@ -1863,6 +1890,7 @@ test_inject_skip_forces_self test_is_wake_reason_distinguishes_status_stdout test_terminal_stale_escalate_leaves_no_marker test_signal_escalate_marks_seen_no_catchall_refire +test_catchall_honours_hb_surfaced_no_refire test_collapse_newlines_pure test_afk_absent_daemon_does_not_inject test_busy_guard_defers_when_supervisor_busy From 8258687e7bb2d37a04bc2d2833dd230e05af7bea Mon Sep 17 00:00:00 2001 From: AG Werschky Date: Mon, 3 Aug 2026 19:02:11 -0600 Subject: [PATCH 2/3] no-mistakes(document): Prune incident chronology from AFK comments --- bin/fm-supervise-daemon.sh | 2 +- tests/fm-daemon.test.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/fm-supervise-daemon.sh b/bin/fm-supervise-daemon.sh index ca521b55df..ed5cb262e8 100755 --- a/bin/fm-supervise-daemon.sh +++ b/bin/fm-supervise-daemon.sh @@ -537,7 +537,7 @@ mark_status_seen() { # # captain-relevant status to firstmate (and also when the afk watcher hands # the same line to this daemon). Without (2), a fresh away-mode session # re-escalates historical done: lines firstmate already handled while afk -# was off - the defect observed 2026-08-02. +# was off. # Does NOT promote hb into subsuper-seen: the always-on/afk watcher marks hb # before the daemon's signal path runs, and promoting would let classify_signal # self-handle as "already escalated" without ever delivering the event. diff --git a/tests/fm-daemon.test.sh b/tests/fm-daemon.test.sh index 67cbbb8620..f73660ca0d 100755 --- a/tests/fm-daemon.test.sh +++ b/tests/fm-daemon.test.sh @@ -718,7 +718,7 @@ test_signal_escalate_marks_seen_no_catchall_refire() { test_catchall_honours_hb_surfaced_no_refire() { # Always-on firstmate already surfaced a done: line (watcher wrote # .hb-surfaced-*), then away mode starts. The catch-all must NOT re-escalate - # that historical completion - the 2026-08-02 production defect. + # that historical completion. local dir state key line dir=$(make_supercase scan-hb-surfaced) state="$dir/state" From 391c609f8dc6704eca2f162220500ed1ab3db69d Mon Sep 17 00:00:00 2001 From: AG Werschky Date: Mon, 3 Aug 2026 19:04:48 -0600 Subject: [PATCH 3/3] no-mistakes(document): Correct catch-all dedup owner comment --- bin/fm-classify-lib.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/fm-classify-lib.sh b/bin/fm-classify-lib.sh index d80840f6a1..a2fe37473b 100755 --- a/bin/fm-classify-lib.sh +++ b/bin/fm-classify-lib.sh @@ -400,7 +400,7 @@ stale_is_terminal() { # # captain-relevant. This is the cheap fleet-scan both supervisors run as a # catch-all backstop for a captain-relevant status the per-wake path might miss. # No dedup is applied here: each consumer dedupes against its own seen-state (the -# daemon against .subsuper-seen-status-*, the watcher against .seen-* signatures). +# daemon via status_line_already_seen in bin/fm-supervise-daemon.sh (including .hb-surfaced-*); watcher via .seen-* signatures). scan_captain_relevant_statuses() { # local state=$1 f last task for f in "$state"/*.status; do