Away mode: hand the watcher to the post-return owner with no blind gap #32

Merged
john merged 3 commits from fm/fm-afk-return-watcher-handoff into main 2026-09-24 10:14:24 +00:00
Owner

What was wrong

When the captain returned from away mode, fm-afk-return.sh tore down the away-mode supervisor, and the watcher with it, since the supervisor owns the watcher as a child. Nothing was watching until the primary's next turn-end hook armed a new cycle. If that first turn was long or failed, wakes queued unread. That is the "landing kills supervision" class of failure, and part of why the captain came back on 09/23 to hours of inactivity.

What changed

  • The supervisor now runs its watcher detached (setsid), so the watcher can outlive the supervisor's shutdown.
  • fm-afk-return.sh hands that live watcher to the post-return supervision owner (a handoff marker, then verify), and falls back to a fresh arm if verification fails. There is never a moment with no watcher while supervision is needed. The singleton lock is respected, so no second cycle is created, and the supervisor still exits cleanly.
  • docs/watcher-continuity.md and the afk skill describe the handoff.

Validation

A new colocated end-to-end test (tests/fm-afk-return-watcher-handoff.test.sh) covers the handoff and the fallback. The existing afk, daemon, launch and watcher suites pass, and shellcheck and the doc-audience check are clean. The no-mistakes pipeline passed at 21a7665.

Author's commit message

fix(afk): hand the daemon's watcher to the post-return owner with no blind gap

Away-mode return tore the daemon down (its cleanup killed the watcher child)
and the primary's next Stop-hook auto-arm did not fire until the captain's
first turn ended, so on a long or failing first turn wakes queued unread.
The daemon's watcher is now setsid-detached so it survives the daemon
terminal's teardown, and on a return-ordered shutdown
state/.afk-return-watcher-handoff tells the daemon's cleanup to LEAVE its
watcher alive and holding the singleton lock. bin/fm-afk-return.sh verifies
a live singleton watcher after the daemon exits and falls back to arming a
fresh detached watcher only if the survivor did not hold, so the primary's
next auto-arm attaches instead of running blind. The handoff is gated on
fm_supervision_needed and the singleton lock stays the only definition of a
live watcher, so a second cycle is never created and no watcher is left
running when nothing needs supervising.

Open

Nothing. This is the firstmate repo, so there is no Plane story. It overlaps bin/fm-supervise-daemon.sh with the usage-limit recovery change now in validation; whichever lands second rebases.

## What was wrong When the captain returned from away mode, `fm-afk-return.sh` tore down the away-mode supervisor, and the watcher with it, since the supervisor owns the watcher as a child. Nothing was watching until the primary's next turn-end hook armed a new cycle. If that first turn was long or failed, wakes queued unread. That is the "landing kills supervision" class of failure, and part of why the captain came back on 09/23 to hours of inactivity. ## What changed - The supervisor now runs its watcher detached (`setsid`), so the watcher can outlive the supervisor's shutdown. - `fm-afk-return.sh` hands that live watcher to the post-return supervision owner (a handoff marker, then verify), and falls back to a fresh arm if verification fails. There is never a moment with no watcher while supervision is needed. The singleton lock is respected, so no second cycle is created, and the supervisor still exits cleanly. - `docs/watcher-continuity.md` and the afk skill describe the handoff. ## Validation A new colocated end-to-end test (`tests/fm-afk-return-watcher-handoff.test.sh`) covers the handoff and the fallback. The existing afk, daemon, launch and watcher suites pass, and shellcheck and the doc-audience check are clean. The no-mistakes pipeline passed at `21a7665`. ## Author's commit message fix(afk): hand the daemon's watcher to the post-return owner with no blind gap Away-mode return tore the daemon down (its cleanup killed the watcher child) and the primary's next Stop-hook auto-arm did not fire until the captain's first turn ended, so on a long or failing first turn wakes queued unread. The daemon's watcher is now setsid-detached so it survives the daemon terminal's teardown, and on a return-ordered shutdown state/.afk-return-watcher-handoff tells the daemon's cleanup to LEAVE its watcher alive and holding the singleton lock. bin/fm-afk-return.sh verifies a live singleton watcher after the daemon exits and falls back to arming a fresh detached watcher only if the survivor did not hold, so the primary's next auto-arm attaches instead of running blind. The handoff is gated on fm_supervision_needed and the singleton lock stays the only definition of a live watcher, so a second cycle is never created and no watcher is left running when nothing needs supervising. ## Open Nothing. This is the firstmate repo, so there is no Plane story. It overlaps `bin/fm-supervise-daemon.sh` with the usage-limit recovery change now in validation; whichever lands second rebases.
Away-mode return tore the daemon down (its cleanup killed the watcher child)
and the primary's next Stop-hook auto-arm did not fire until the captain's
first turn ended, so on a long or failing first turn wakes queued unread.
The daemon's watcher is now setsid-detached so it survives the daemon
terminal's teardown, and on a return-ordered shutdown
state/.afk-return-watcher-handoff tells the daemon's cleanup to LEAVE its
watcher alive and holding the singleton lock. bin/fm-afk-return.sh verifies
a live singleton watcher after the daemon exits and falls back to arming a
fresh detached watcher only if the survivor did not hold, so the primary's
next auto-arm attaches instead of running blind. The handoff is gated on
fm_supervision_needed and the singleton lock stays the only definition of a
live watcher, so a second cycle is never created and no watcher is left
running when nothing needs supervising.
no-mistakes(document): Away-mode return watcher handoff docs already accurate
Some checks failed
CI / Test coverage guard (pull_request) Successful in 14s
CI / Behavior portable parallel 1 (pull_request) Successful in 3m21s
CI / Lint shell scripts (pull_request) Successful in 5m3s
CI / Behavior portable parallel 2 (pull_request) Successful in 3m7s
CI / Behavior portable serial 4 (pull_request) Successful in 10m49s
CI / Behavior portable serial 2 (pull_request) Failing after 11m26s
CI / Repo invariants (pull_request) Successful in 6s
CI / Behavior portable serial 3 (pull_request) Successful in 13m58s
CI / Behavior portable serial 1 (pull_request) Successful in 19m9s
CI / Behavior tests (Herdr) (pull_request) Successful in 7m30s
21a7665098
no-mistakes(review): Deflake the AFK handoff test and classify it as afk
All checks were successful
CI / Test coverage guard (pull_request) Successful in 15s
CI / Behavior portable parallel 2 (pull_request) Successful in 3m1s
CI / Behavior portable parallel 1 (pull_request) Successful in 3m11s
CI / Lint shell scripts (pull_request) Successful in 4m45s
CI / Behavior portable serial 2 (pull_request) Successful in 11m30s
CI / Behavior portable serial 4 (pull_request) Successful in 10m47s
CI / Repo invariants (pull_request) Successful in 5s
CI / Behavior portable serial 3 (pull_request) Successful in 13m54s
CI / Behavior portable serial 1 (pull_request) Successful in 18m55s
CI / Behavior tests (Herdr) (pull_request) Successful in 7m45s
37fe71dbb0
john merged commit 236c176f3d into main 2026-09-24 10:14:24 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
john/firstmate!32
No description provided.