Skip to content

fix(bin): make supervision recovery owner-aware and durable - #2392

Open
tiago-peixoto wants to merge 7 commits into
kunchenguid:mainfrom
tiago-peixoto:fm/firstmate-stop-guard-starves-autoarm
Open

fix(bin): make supervision recovery owner-aware and durable#2392
tiago-peixoto wants to merge 7 commits into
kunchenguid:mainfrom
tiago-peixoto:fm/firstmate-stop-guard-starves-autoarm

Conversation

@tiago-peixoto

Copy link
Copy Markdown

Intent

Fix the Claude primary-session supervision failure observed on 2026-08-14, where work was in flight, no watcher was live, and the turn-end guard could repeat forever while the Stop auto-arm did not claim recovery. First add a durable, bounded, self-trimming, best-effort state trace that records every auto-arm entry and selected pre-claim gate without changing hook output, status, or failure behavior. Reproduce only in a throwaway home with real Claude, let the evidence choose among sibling starvation, the foreign live session-lock owner gate, and discarded asyncRewake, and deliberately seek falsification: no entry on the blocked Stop would falsify the identity-gate diagnosis, while an owner claim without delivered Stop feedback would support discarded rewake. The evidence showed the hook ran and took gate-live-session-owner, while lock-owner asyncRewake worked, so preserve the identity gate and its prevention of two sessions arming one home; do not weaken the guard, arm from the read-only session, reorder hooks, or teach the falsified short-circuit mechanism. Align the guard with session ownership so the read-only competing session can finish and the lock-owning session remains the sole mutable supervision owner and restores supervision on its next Stop. Add a portable real-process regression and an env-gated real-Claude live guard, and prove both RED against unfixed code and green after the fix. After two genuinely identical guard blocks, terminate the exchange in exactly one captain-facing question, reset the identical-block count when evidence changes, and suppress repeated escalation. Update the authoritative continuity, turn-end, operating-protocol, volatile-state, and dated verification records, including exact commands and output; keep the private learning corrected to the proven mechanism. Require bin/fm-lint.sh and documentation checks to pass.

What Changed

  • Preserve watcher recovery state through generation-bound post-handling acknowledgements, replaying queued wakes and open decisions across normal, AFK, and daemon re-arm paths.
  • Align Claude Stop handling with live session-lock ownership, add bounded gate tracing, and end repeated identical guard blocks with one captain question while resetting on changed evidence.
  • Restore Pi and pi-signed launches after --tui-mode removal, and add a manual Windows Herdr measurement workflow with expanded regression coverage and documentation.

Risk Assessment

✅ Low: Captain, the final change consistently uses one supervision snapshot, closes the false-X-mode race, and leaves the durable ownership and queue-delivery invariants intact.

Testing

Focused portable suites and a credentialed two-session Claude 2.1.232 flow passed; isolated portable and live ownership counterfactuals failed at the intended symptom, reviewer-visible logs were captured, and all transient test directories were removed. Lint, documentation checks, push, PR, and CI were not run because this assigned phase was test-only.

Evidence: Portable ownership-gate RED
ok - fm_supervision_unhealthy: false with no state/*.meta at all
ok - fm_supervision_unhealthy: true with in-flight task and no beacon ever
ok - fm_supervision_unhealthy: true with in-flight task and a beacon far outside the grace window
ok - fm_supervision_unhealthy: false with in-flight task and a fresh beacon
ok - fm_supervision_status: a pending wake needs supervision
ok - fm_supervision_needed: X-mode relay poll needs supervision
ok - fm_supervision_unhealthy: source-only home needs supervision
ok - fm_supervision_status: identity fingerprint tracks exact tasks and process sources only
ok - fm-turnend-guard: silent no-op with nothing in flight
ok - fm-turnend-guard: blocks when a fresh beacon has no live watcher lock
ok - fm-turnend-guard: non-Claude path blocks a source-only home
ok - fm-turnend-guard: non-Claude path blocks a queue-only home
ok - fm-turnend-guard: blocks on a dead watcher lock even when the beacon is fresh
ok - fm-turnend-guard: silent no-op with a live watcher lock and fresh beacon
ok - fm-turnend-guard: healthy non-Claude harness paths ignore Claude episode contention
ok - fm-turnend-guard: blocks on a live watcher lock with an ancient beacon
ok - fm-turnend-guard: blocks with the exact required reason in the primary when unhealthy
ok - fm-turnend-guard: blocks from active FM_HOME state, not only repo-root state
ok - fm-turnend-guard: X-mode repair reason sources the cadence config
ok - fm-turnend-guard: X-mode-only supervision remains guarded in default mode
ok - fm-turnend-guard: ignores stale repo-root state when FM_HOME is set
ok - fm-turnend-guard: uses FM_STATE_OVERRIDE ahead of FM_HOME/state
ok - fm-turnend-guard: stop_hook_active=true always allows the stop (never blocks twice in one turn)
ok - fm-turnend-guard: blocks a blind turn end in a secondmate's own home (.fm-secondmate-home no longer excludes it)
ok - fm-turnend-guard: idle-by-default - silent in a secondmate home with nothing in flight
ok - fm-turnend-guard: stop_hook_active=true allows the stop in a secondmate home (never blocks twice in one turn)
ok - fm-turnend-guard: secondmate deferred-death recovery - silent while watched, forces re-arm once the watcher exits
ok - fm-turnend-guard: inert in a secondmate's own child worktree (linked git worktree) even when unhealthy
ok - fm-turnend-guard: blocks a blind turn end in a treehouse-leased LINKED secondmate home (marker force-include)
ok - fm-turnend-guard: an invalid (empty) marker cannot spoof inclusion; linked worktree stays exempt
ok - fm-turnend-guard: a non-ASCII marker cannot spoof inclusion; linked worktree stays exempt
ok - fm-turnend-guard: inert in a crewmate/scout task worktree (linked git worktree) even when unhealthy
ok - fm-turnend-guard: fails open (never blocks) when jq is missing
ok - fm-turnend-guard: silent no-op on empty stdin
ok - fm-turnend-guard: runs well under the generous timing margin (1s)
ok - fm-turnend-guard-grok: forces one explicitly marked same-session resume when the shared predicate blocks
ok - fm-turnend-guard-grok: legacy environment loop guard prevents a nested resume loop
ok - fm-turnend-guard-grok: native false delegates blocking feedback with zero resume processes
ok - fm-turnend-guard-grok: native true remains bounded and starts no resume process
ok - fm-turnend-guard-grok: both spellings are typed and camelCase has deterministic precedence
ok - fm-turnend-guard-grok: malformed, invalidly typed, and missing-prerequisite payloads start neither path
ok - fm-turnend-guard-grok: missing jq and no-supervision-needed stops stay silent and bounded
ok - tracked .claude/settings.json entries: 5 inert under grok, the documented subagent exception still armed, all live under Claude
ok - .codex/hooks.json: Stop hook uses hook process root when payload cwd is outside
ok - .codex/hooks.json: Stop hook ignores nested git root guard scripts
ok - .opencode primary plugin: guard path is anchored to worktree, not directory
ok - .pi primary extension: no-tool and multi-tool runs each inject exactly one guard follow-up
ok - .pi primary extension: delivery failure resets the logical-run latch
ok - fm-turnend-guard --claude: re-blocks a loop-guarded stop while unhealthy and unclaimed (incident regression)
not ok - a read-only session must not be trapped by a guard whose matching auto-arm cannot own recovery: expected exit 0, got 2

exit_status=1
Evidence: Portable auto-arm green
ok - auto-arm: inert in a linked child worktree even when in-flight
ok - auto-arm: inert with no session lock
ok - auto-arm: best-effort entry trace names the selected gate, self-trims, and cannot become a hook failure
ok - auto-arm: a demonstrably dead recorded session owner is reclaimed through fm-lock.sh before arming
ok - auto-arm: inert without arm, rewake, or lock replacement when another live harness owns the home
ok - auto-arm: inert while AFK owns supervision
ok - auto-arm: stale-owner recovery leaves the AFK and supervision-need gates unchanged
ok - auto-arm: resolves the outermost pid of a nested contiguous claude ancestry (bg-spare chain)
ok - auto-arm: inert with nothing in flight and no X-mode need
ok - auto-arm: queue-only delivery need arms the cycle
ok - auto-arm: actionable close translates to exactly one exit-2 rewake with reason
ok - auto-arm: actionable close survives a healthy successor without duplicate delivery
ok - auto-arm: bounded failure verification emits one automatic-mechanism alarm
ok - auto-arm: consecutive failures keep Stop-owned retry without repeating notice
ok - auto-arm: unverified clean close exhausts retries and fails closed
ok - auto-arm: post-alarm actionable outcomes cannot continue or reset failure state
ok - auto-arm: benign cycle end with a live watcher and fresh beacon stays silent across the next cycle
ok - auto-arm: budget contention preserves the episode and forces a reset retry
ok - auto-arm: X-mode poll need arms the cycle even with no tasks in flight
ok - auto-arm: concurrent firings admit one owner and one rewake translation
ok - auto-arm: need vanishing mid-cycle closes without a rewake
ok - auto-arm: mid-cycle AFK hands triage to the daemon with no rewake
ok - auto-arm: active in a marked secondmate home
ok - fm-lock: shared session-lock lib preserves the status path
Evidence: Portable turn-end guard green
ok - fm_supervision_unhealthy: false with no state/*.meta at all
ok - fm_supervision_unhealthy: true with in-flight task and no beacon ever
ok - fm_supervision_unhealthy: true with in-flight task and a beacon far outside the grace window
ok - fm_supervision_unhealthy: false with in-flight task and a fresh beacon
ok - fm_supervision_status: a pending wake needs supervision
ok - fm_supervision_needed: X-mode relay poll needs supervision
ok - fm_supervision_unhealthy: source-only home needs supervision
ok - fm_supervision_status: identity fingerprint tracks exact tasks and process sources only
ok - fm-turnend-guard: silent no-op with nothing in flight
ok - fm-turnend-guard: blocks when a fresh beacon has no live watcher lock
ok - fm-turnend-guard: non-Claude path blocks a source-only home
ok - fm-turnend-guard: non-Claude path blocks a queue-only home
ok - fm-turnend-guard: blocks on a dead watcher lock even when the beacon is fresh
ok - fm-turnend-guard: silent no-op with a live watcher lock and fresh beacon
ok - fm-turnend-guard: healthy non-Claude harness paths ignore Claude episode contention
ok - fm-turnend-guard: blocks on a live watcher lock with an ancient beacon
ok - fm-turnend-guard: blocks with the exact required reason in the primary when unhealthy
ok - fm-turnend-guard: blocks from active FM_HOME state, not only repo-root state
ok - fm-turnend-guard: X-mode repair reason sources the cadence config
ok - fm-turnend-guard: X-mode-only supervision remains guarded in default mode
ok - fm-turnend-guard: ignores stale repo-root state when FM_HOME is set
ok - fm-turnend-guard: uses FM_STATE_OVERRIDE ahead of FM_HOME/state
ok - fm-turnend-guard: stop_hook_active=true always allows the stop (never blocks twice in one turn)
ok - fm-turnend-guard: blocks a blind turn end in a secondmate's own home (.fm-secondmate-home no longer excludes it)
ok - fm-turnend-guard: idle-by-default - silent in a secondmate home with nothing in flight
ok - fm-turnend-guard: stop_hook_active=true allows the stop in a secondmate home (never blocks twice in one turn)
ok - fm-turnend-guard: secondmate deferred-death recovery - silent while watched, forces re-arm once the watcher exits
ok - fm-turnend-guard: inert in a secondmate's own child worktree (linked git worktree) even when unhealthy
ok - fm-turnend-guard: blocks a blind turn end in a treehouse-leased LINKED secondmate home (marker force-include)
ok - fm-turnend-guard: an invalid (empty) marker cannot spoof inclusion; linked worktree stays exempt
ok - fm-turnend-guard: a non-ASCII marker cannot spoof inclusion; linked worktree stays exempt
ok - fm-turnend-guard: inert in a crewmate/scout task worktree (linked git worktree) even when unhealthy
ok - fm-turnend-guard: fails open (never blocks) when jq is missing
ok - fm-turnend-guard: silent no-op on empty stdin
ok - fm-turnend-guard: runs well under the generous timing margin (1s)
ok - fm-turnend-guard-grok: forces one explicitly marked same-session resume when the shared predicate blocks
ok - fm-turnend-guard-grok: legacy environment loop guard prevents a nested resume loop
ok - fm-turnend-guard-grok: native false delegates blocking feedback with zero resume processes
ok - fm-turnend-guard-grok: native true remains bounded and starts no resume process
ok - fm-turnend-guard-grok: both spellings are typed and camelCase has deterministic precedence
ok - fm-turnend-guard-grok: malformed, invalidly typed, and missing-prerequisite payloads start neither path
ok - fm-turnend-guard-grok: missing jq and no-supervision-needed stops stay silent and bounded
ok - tracked .claude/settings.json entries: 5 inert under grok, the documented subagent exception still armed, all live under Claude
ok - .codex/hooks.json: Stop hook uses hook process root when payload cwd is outside
ok - .codex/hooks.json: Stop hook ignores nested git root guard scripts
ok - .opencode primary plugin: guard path is anchored to worktree, not directory
ok - .pi primary extension: no-tool and multi-tool runs each inject exactly one guard follow-up
ok - .pi primary extension: delivery failure resets the logical-run latch
ok - fm-turnend-guard --claude: re-blocks a loop-guarded stop while unhealthy and unclaimed (incident regression)
ok - fm-turnend-guard --claude: a foreign live session owner cannot trap the read-only session in an unrecoverable Stop loop
ok - fm-turnend-guard --claude: X-mode-only homes re-block when auto-arm recovery is absent
ok - fm-turnend-guard --claude: a live arming epoch advances once and repeated observation is idempotent
ok - fm-turnend-guard --claude: repeated failed-to-arming races make bounded monotonic progress
ok - fm-turnend-guard --claude: terminal owner boundary excludes a concurrent start without deadlock
ok - fm-turnend-guard --claude: fresh rewake epoch prevents a duplicate continuation for the same event
ok - fm-turnend-guard --claude: fresh failed epochs preserve and advance monotonic fail-open progression
ok - fm-turnend-guard --claude: integrated fresh failures reach one bounded fail-open, stop continuation, and reset on recovery
ok - fm-turnend-guard --claude: reset contention preserves all episode state until retry
ok - fm-turnend-guard --claude: concurrent auto-arm and guard resets are idempotent and deadlock-free
ok - fm-turnend-guard --claude: stale rewake epoch does not allow a blind stop
ok - fm-turnend-guard --claude: two identical blocks terminate in one captain escalation instead of an unbounded loop
ok - fm-turnend-guard --claude: changed task identity resets the identical-block escalation count
ok - fm-turnend-guard --claude: changed process-source identity resets the identical-block escalation count
ok - fm-turnend-guard --claude: terminal source wake remains supervised after retirement
ok - fm-turnend-guard --claude: verified fail-open is loud, bounded, attended, and non-repeating
ok - fm-turnend-guard --claude: fail-open requires both exhausted retries and consumed notice
ok - fm-turnend-guard --claude: away ownership excludes the Stop-autoarm fail-open
ok - fm-turnend-guard --claude: positive watcher recovery resets failure episode state
ok - fm-turnend-guard --claude: bounded claim wait avoids a token-consuming forced continuation
ok - fm-turnend-guard --claude: secondmate home re-blocks unclaimed and allows auto-arm-claimed stops
Evidence: Real-Claude ownership-gate RED
not ok - read-only Claude session was trapped by the blind-turn guard: session=1a943f96-5454-4065-a623-72a5a1ab1922
count=1
epoch=
reblocks=1
signature=inflight=1:sources=0:identities=3255371288-174:queue=none:x=0:afk=0:epoch=none:outcome=none

exit_status=1
Evidence: Hardened real-Claude green
ok - Claude 2.1.232 (Claude Code) live E2E let the read-only competing session finish, then restored supervision from the lock-owning Stop hook without human intervention
Evidence: Concise live behavior evidence

Claude 2.1.232: competing session returned COMPETING_READ_ONLY after gate-live-session-owner; owner Stop hook exited 2 with the watcher wake, produced OWNER_RECOVERED, claimed exactly one arm cycle, and recorded outcome=rewake.

claude_version=2.1.232 (Claude Code)
{"event":"competing_response","text":"COMPETING_READ_ONLY"}
at=1786731603 pid=80887 event=gate-live-session-owner
{"event":"owner_stop_hook_response","hook_event":"Stop","exit_code":2,"outcome":"error","output":"firstmate watcher wake - one supervision event needs a handling turn now.\nstale: live-owner-recovery\nRun bin/fm-wake-drain.sh first, handle the wake, then run its exact WAKE_ACK_REQUIRED --ack-through command. Until that post-handling acknowledgement, interruption leaves the wake durable for idempotent re-handling. This Stop hook owns watcher continuity: when the handling turn ends, the next needed cycle arms automatically - do NOT run bin/fm-watch-arm.sh after an ordinary wake.\n"}
{"event":"owner_response","text":"OWNER_RECOVERED"}
at=1786731607 pid=85835 event=claimed
arm_cycles=1
epoch=epoch=2 owner_pid=85835 outcome=rewake updated_at=1786731607

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

⏭️ **Rebase** - skipped
  • ⚠️ .agents/skills/afk/SKILL.md - branch carries 1 commit(s) that exist on your local main branch but were never pushed to origin/main; rebasing would bundle this unrelated work (171 file(s)) into the PR:
  • fe30ee2 fix(spawn): stop passing the removed --tui-mode flag on Pi launches (docs: polish README banner and repo housekeeping #2)

Push main to origin, or rebase your branch onto origin/main, before gating.

🔧 **Review** - 6 issues found → auto-fixed (4) ✅
  • 🚨 bin/fm-wake-lib.sh:428 - A recovery acknowledgement can livelock: _fm_recovery_marker_publish always creates a new generation, so if the watcher closes while generation A is being handled, downtime publication rotates it to B; fm-wake-drain.sh then rejects the exact acknowledgement for A before consuming its queue rows. The marker remains pending and later arms repeatedly re-announce the same recovery instead of supervising. Preserve an outstanding generation during republication and consume rows through the acknowledged sequence even when the episode generation has advanced.
  • 🚨 bin/fm-turnend-guard.sh:495 - The required behavior says to “terminate the exchange in exactly one captain-facing question” after two identical blocks. The second block instructs the model to escalate the choice to the captain, while the next unchanged Stop emits another captain-facing question here; a compliant model therefore asks twice. Choose one question owner—either make the second block terminal, or let the model ask and allow the subsequent Stop silently.
  • 🚨 bin/fm-turnend-guard.sh:226 - The requirement says to reset after evidence changes and count only “genuinely identical” blocks, but this signature records only aggregate counts and modes. If task A completes and task B starts before the next Stop, the signature remains unchanged and the new state inherits A’s block count. Include a stable fingerprint of the actual task and process-source identities, preferably from the shared supervision-status boundary while excluding volatile age fields.
  • 🚨 docs/verification/supervision.md:279 - The intent requires both regressions to be proven RED against unfixed code and green after the fix, with dated records containing exact commands and output. The changed verification record documents only successful live, lint, documentation, and portable-suite runs; it contains no unfixed RED command/output for either the portable real-process regression or the real-Claude guard. Add that evidence or obtain explicit approval to omit it.
  • ⚠️ bin/fm-spawn.sh:1074 - Removing --tui-mode regular unconditionally supports Pi versions that removed the option, but re-enables configured fullscreen mode on older supported Pi executables that still advertise it; fullscreen scrollback can bury steers and make supervision state appear stale. Resolve the selected executable once, probe its --help, and pass the flag only when that exact executable supports it.
  • ⚠️ .pi/extensions/fm-primary-pi-watch.ts:253 - Recovery confirmation runs synchronously without a timeout. If a live but stalled process owns .watcher-down.lock, --handling-delivered waits indefinitely and freezes the Pi event loop immediately after wake delivery; the OpenCode sibling has the same path. Bound the child execution or make it asynchronous, leaving the durable recovery marker pending when confirmation fails.

🔧 Fix: Fix guard escalation and evidence fingerprinting
2 errors still open:

  • 🚨 bin/fm-turnend-guard.sh:223 - The accepted criterion says to “reset the identical-block count when evidence changes,” but this signature uses FM_SUP_* values sampled once at line 165, before the configurable auto-arm wait. A process-source runner can terminal-retire its .source during that wait while the sibling auto-arm observes no remaining need and makes no claim; on the second attempt the guard still compares source A’s stale fingerprint, reaches count 2, and emits the captain question despite changed evidence. Refresh shared supervision status at the block-accounting boundary and pass/reset when the need disappeared before permitting terminal escalation.
  • 🚨 docs/turnend-guard.md:71 - The accepted clarification requires the second identical observation to be terminal, with the guard owning the only question and subsequent unchanged Stops passing silently, and the intent requires authoritative turn-end and operating-protocol records to be updated. These changed docs still say the second banner tells the model to ask and a later Stop emits the question; docs/supervision-protocols/claude.md:15 repeats that obsolete model-owned sequence. Update both authoritative documents to match the implemented second-Stop ownership rule.

🔧 Fix: Refresh guard evidence and escalation documentation
1 error still open:

  • 🚨 bin/fm-turnend-guard.sh:476 - The refreshed predicate can incorrectly declare recovery complete on the supported terminal process-source path. fm-procevent.sh appends the terminal result to .wake-queue before retiring its .source; if that occurs during this wait, auto-arm takes gate-no-supervision and this branch passes because FM_SUP_QUEUE_PENDING is excluded from FM_SUP_NEEDED. With no watcher, the actionable result remains unsurfaced until unrelated activity. Make the shared supervision-needed boundary include pending wake delivery, and update the new regression to retire through the real process path or seed its durable wake before asserting the outcome.

🔧 Fix: Keep queued wake delivery under supervision
1 warning still open:

  • ⚠️ bin/fm-guard.sh:206 - The widened shared predicate is not consumed atomically here: needed can be true solely because line 150 observed a queued wake, but line 166 re-reads the queue separately. If an acknowledgement drains the queue between those reads and no task, source, or Relay poll remains, this changed branch falls through to the X-mode message and claims a false watcher-down episode. Copy FM_SUP_QUEUE_PENDING alongside the other shared status fields instead of re-reading the file.

🔧 Fix: Use one supervision snapshot for queue warnings
✅ Re-checked - no issues remain.

✅ **Test** - passed

✅ No issues found.

  • claude --version2.1.232 (Claude Code)
  • tests/fm-claude-stop-autoarm.test.sh
  • tests/fm-turnend-guard.test.sh
  • Throwaway final-tree portable counterfactual with only the foreign-live-owner exemption removed; tests/fm-turnend-guard.test.sh failed at the intended trapped-session regression
  • FM_CLAUDE_LIVE_E2E=1 tests/fm-claude-stop-autoarm-live-e2e.test.sh
  • Throwaway final-tree real-Claude counterfactual with only the foreign-live-owner exemption removed; the live test failed at the intended blind-turn guard trap
  • Startup-only and real-transcript jq checks proving the hardened assertion rejects startup prose and accepts an actual Stop exit-2 response plus OWNER_RECOVERED
  • Two initial whole-parent rollback controls were diagnosed and discarded because later queued-wake behavior made them fail before the ownership regression
  • git status --short --untracked-files=all and transient-directory audit
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

tiago-peixoto and others added 7 commits August 14, 2026 15:46
* docs(stow): generalize read-before-write in the public stow skill (kunchenguid#2091)

The public installer-facing stow skill scoped its classify-then-replace
discipline to TODO/BACKLOG items only, so findings routed to a memory file
had no stated rule against a blind append or a wholesale overwrite.

Step 6 now classifies every finding against the destination's current
contents as new, duplicate, superseding, or obsolete, and states the
considered replacement each classification implies. The outcomes follow the
tiered-memory contract already in the file: an obsolete entry is refreshed,
archived, or replaced in a way that preserves its fact, a duplicate folds
into the entry that already carries it, and a superseded body worth keeping
leaves through step 7's existing exits rather than a second recovery
mechanism.

* fix: resurface durable supervision work after re-arm (kunchenguid#2065)

* fix(watcher): resurface durable work after downtime

* no-mistakes(review): Make watcher rearm recovery durable and cursor-safe

* no-mistakes(review): Persist safe recovery markers across migration lock recovery

* no-mistakes(review): Retain stale lock when recovery marker publication fails

* no-mistakes(review): Preserve delivery-gap recovery and quarantine malformed markers

* no-mistakes(review): Serialize recovery consumption and report acknowledgment failures

* no-mistakes(review): Centralize recovery publication before clearing watcher evidence

* no-mistakes(review): Guarantee recovery evidence across queue and lock handoffs

* no-mistakes(review): Publish recovery evidence before durable wake commits

* no-mistakes(review): Replace recovery marker Perl dependency with Node

* no-mistakes(review): Keep interrupted wakes durable until handling acknowledgment

* no-mistakes(review): Add post-handling durable wake acknowledgements

* no-mistakes(review): Enforce post-handling acknowledgement across recovery and AFK return

* no-mistakes(review): Bind wake acknowledgements to recovery generations

* no-mistakes(review): Align wake regressions with generation-bound acknowledgements

* no-mistakes(document): Document durable re-arm recovery semantics

* no-mistakes(lint): Resolve ShellCheck warnings in recovery and watcher tests

* no-mistakes: apply CI fixes

* test(watcher): assert post-handling wake replay

* no-mistakes(review): Prevent successor loops and adopt legacy wake generations

* no-mistakes(review): Rearm durable wakes without recursive successor recovery

* no-mistakes(review): Align recovery tests with handling marker state

* no-mistakes(review): Delay handling transition until successor launch is established

* no-mistakes(review): Confirm wake handling only after successful prompt delivery

* no-mistakes(review): Acknowledge AFK wakes only after evidence publication

* no-mistakes(review): Prevent AFK wake loss before post-handling acknowledgement

* no-mistakes(document): Document durable wake acknowledgement semantics

* no-mistakes(lint): Suppress false positive for recovery action output

* no-mistakes: apply CI fixes

* no-mistakes: apply CI fixes

* ci: measure Herdr automation on Windows runners (kunchenguid#2100)

* ci: add Windows Herdr automation spike

* ci: run Windows spike on its pull request

* fix: wait for Windows Herdr command output

* fix: run ANSI probe in pane shell

* ci: keep Windows Herdr spike manually triggered

* docs: clarify Windows Herdr spike verdict

* fix: support Pi 0.83 worker launches

---------

Co-authored-by: Kun Chen <3233006+kunchenguid@users.noreply.github.com>
@tiago-peixoto
tiago-peixoto force-pushed the fm/firstmate-stop-guard-starves-autoarm branch from 6a19ce3 to c47f190 Compare August 14, 2026 18:50
@tiago-peixoto

Copy link
Copy Markdown
Author

For upstream context: this change merged on the personal fork on 2026-08-14 through tiago-peixoto#5, with all 13 checks green, and it has run live in the primary home since that merge.

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