fix(calm): stop terminal-completed crews and Pi Calm from spamming acknowledgement rows - #1999
Open
AbdullahPesteli wants to merge 4 commits into
Open
Conversation
added 4 commits
August 9, 2026 11:55
…knowledgement spam A burst of terminal or long-idle task notifications produced dozens of visible exact "Captain, shipshape." acknowledgement rows even with config/calm=on. The defect had two correct owners, fixed at each without broad dedupe or weakened monitoring. Watcher/classifier: crew_absorb_class now recognizes a genuinely completed terminal state (state: done) as its own absorb class via the authoritative reconciler, and the stale-pane terminal path absorbs a done crew whose endpoint has gone idle or agent-free instead of re-surfacing a pane-stale wake on top of the signal and heartbeat paths that already deliver its done: transition. No wedge timer is armed for a completed crew, and terminal truth comes from the reconciler, never the last status event. Failure delivery, actionable gates, working-task wedge detection, paused cadence, heartbeat semantics, checks, PR merge polls, and durable wake-queue delivery are unchanged. Pi Calm presentation: the assistant-layout adapter's render override collapses the single no-action acknowledgement AGENTS.md section 9 defines, bound to a typed operational-input provenance and the exact text together, never the string alone. The operational-user adapter records that provenance on every user message. A captain-authored reply, a real outcome such as a PR URL, a decision, a failure, and a superficially similar sentence all stay visible, and the acknowledgement renders in full whenever Calm is off and in stock export/share rendering. Session data is unchanged.
…s dead-agent reclass stays paused
The review-fix commit compared $class against an unquoted done, which ShellCheck 0.11.0 flags as SC1010 (done is a shell keyword). The pipeline's lint step never ran because the test step failed first, so the warning was left in the tree. Quote it to match the same convention used in fm-classify-lib.sh; behavior is unchanged.
This was referenced Aug 9, 2026
[firstmate#1620] fix(pi): hide queued Firstmate notifications under Calm
kunchenguid/wheelhouse#2085
Open
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 a Firstmate/Pi supervision-and-presentation defect: a burst of terminal or long-idle task notifications produced dozens of visible exact 'Captain, shipshape.' acknowledgement rows even with config/calm=on. Remove the noise at its two correct owners without adding broad dedupe or disabling monitoring, preserving supervision strength.
Watcher/classifier owner (bin/fm-classify-lib.sh, bin/fm-watch.sh): crew_absorb_class now recognizes a genuinely COMPLETED terminal state (fm-crew-state.sh reports state: done) as its own absorb class, and the stale-pane terminal path absorbs a done crew whose old endpoint has gone idle or agent-free instead of re-surfacing a pane-stale wake on top of the signal and heartbeat paths that already deliver its done: transition. No wedge timer is armed for a completed crew. Terminal truth comes from the reconciler, never the last status event. A follow-up commit scopes the new done token out of pause_state_class so the paused/captain-held non-terminal cadence stays byte-identical to before, keeping the done absorb only in the terminal-stale path. Preserved: failure delivery, actionable blockers, working-task wedge detection, paused external-wait cadence, heartbeat semantics, custom checks, PR merge polls, and durable wake-queue delivery.
Pi Calm presentation owner (.pi/extensions/lib/*): the assistant-layout render override collapses ONLY the exact no-action acknowledgement AGENTS.md section 9 defines ('Captain, shipshape.'), bound to a typed operational-input provenance AND the exact text together, never string-only. A captain reply, a real outcome such as a PR URL, a decision, a failure, and a superficially similar sentence all stay visible; the acknowledgement renders in full whenever Calm is off and in stock /export and /share; session data is unchanged.
Kept both fixes at their existing owners; did NOT expand AGENTS.md. Updated docs/calm.md, docs/calm-mode-feasibility.md, docs/architecture.md, docs/configuration.md. Regression tests assert behavior via public interfaces, never source bytes. The shell watcher tests use live tmux subprocesses so full files exceed two minutes; run the change's own functions selectively and do not build extra evidence artifacts. Pushing to a fork and opening a PR against kunchenguid/firstmate:main. Scoped OUT as a follow-up: a Pi MCP-approval-modal false-working detection.
What Changed
doneabsorb class tocrew_absorb_class(bin/fm-classify-lib.sh) plus acrew_is_terminal_donehelper, so the stale-pane terminal path inbin/fm-watch.shnow reconciler-checks each new-hash wake and absorbs a genuinely completed crew (state: done) whose endpoint has gone idle/agent-free instead of re-surfacing a pane-stale wedge on top of the signal/heartbeat paths that already deliver itsdone:transition; failed/parked/blocked/unknown verdicts still surface.donetoken out ofpause_state_class(bin/fm-watch.sh) so the paused/captain-held non-terminal reclassification cadence for a dead agent stays byte-identical, keeping the done absorb confined to the terminal-stale path.Risk Assessment
✅ Low: The change is tightly scoped to two named owners, precisely matches the authoritative intent, preserves the paused/captain-held cadence byte-identically (verified across every branch), binds the Calm collapse to provenance-plus-exact-text, and ships comprehensive public-interface regression tests; the only finding is a harmless docstring inaccuracy.
Testing
Exercised both fix owners selectively (never the full >2-min live-tmux suites, per intent) and all targeted tests passed. For the Pi Calm presentation owner I ran the render/lifecycle test against the actually-installed Pi 0.84.1 and, via an instrumented copy of the same fixture, captured a rendered TUI transcript proving theCaptain, shipshape.ack collapses only under typed operational provenance + exact text, while a genuine captain reply, a real PR-URL outcome, a near-miss (Captain, shipshape!), and stock /export all render in full. For the watcher/classifier owner I ran the new done-absorb-class functions and drove a live fm-watch.sh subprocess, capturing two artifacts: a completed crew's stale pane is absorbed silently (empty wake-queue, no wedge timer armed, status not marked surfaced, triage log lineabsorbed stale (terminal completed, endpoint idle/agent-free)), whereas an unknown/torn-down crew on the same terminal status still surfaces a wake — demonstrating the spam is removed without weakening failure delivery or monitoring. Transient test-driver files were removed; the worktree is clean.Evidence: Pi Calm rendered TUI transcript — ack collapses only for typed operational provenance + exact text; genuine reply, PR URL, near-miss, and /export stay visible
Calm: ON FIRSTMATE_OPERATIONAL_ACK = "Captain, shipshape." Scenario 1: routine ack answering a typed operational (watcher) input -> (row collapsed / not shown) Scenario 2: same text after a GENUINE captain message -> "Captain, shipshape." (visible) Scenario 3: real operational OUTCOME -> "Captain, PR https://example.test/pr/9 checks green." (visible) Scenario 4: near-miss "Captain, shipshape!" -> visible Scenario 5: stock /export - /share of the collapsed ack -> "Captain, shipshape." (shown in full)Evidence: Watcher triage — completed crew (state: done) stale pane is ABSORBED (no spam)
reconciler verdict: state: done · source: run-step · checks green wake reason to captain (stdout): (none - absorbing) durable .wake-queue: (empty - no acknowledgement/wake enqueued) triage log: absorbed stale (terminal completed, endpoint idle/agent-free): test:fm-shipped wedge timer: (not armed) status marked surfaced: (not marked)Evidence: Watcher triage CONTRAST — unknown/torn-down crew still SURFACES (supervision preserved)
reconciler verdict: state: unknown · source: none · worktree gone wake reason to captain (stdout): stale: test:fm-torndown durable .wake-queue: 1786266474\t1\tstale\ttest:fm-torndown\tstale: test:fm-torndownPipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ No issues found.
✅ **Rebase** - passed
✅ No issues found.
bin/fm-classify-lib.sh:648- crew_is_terminal_done's docstring states "The stale-pane terminal path uses this," but the production terminal-stale path in bin/fm-watch.sh:1002 inlines crew_absorb_class directly in a case statement and never calls crew_is_terminal_done. The helper is referenced only by tests (mirroring the test-only crew_is_paused). The comment overstates a caller relationship that does not exist, which could mislead a future maintainer in this heavily comment-driven codebase. Purely a documentation-accuracy nit; behavior is correct.✅ **Test** - passed
✅ No issues found.
Selective driver overtests/fm-watch-triage.test.sh:test_crew_absorb_class_classifier,test_terminal_stale_surfaced,test_terminal_completed_stale_absorbed,test_stale_terminal_status_overridden_by_active_run,test_exited_declared_pause_is_bounded_but_live_gate_surfaces,test_nonterminal_stale_pause_transitions_reclassify_unchanged_hash— allokSelective driver overtests/fm-calm-pi-extension.test.sh:test_rendering_and_session_lifecycle(contains the new operational-ack collapse assertions) against installed Pi 0.84.1 —ok, EXIT=0Evidence capture via instrumented copy of the calm node fixture driving the realAssistantMessageComponent.renderfor 5 provenance/text scenarios + stock export toggleEvidence capture via a realbin/fm-watch.shsubprocess for the completed-crew (state: done) stale-pane absorb path and the unknown/torn-down surface contrast✅ **Document** - passed
✅ No issues found.
✅ **Lint** - passed
✅ No issues found.
✅ **Push** - passed
✅ No issues found.