Skip to content

fix: prevent false wedge escalations and replayed wake status - #88

Merged
dnth merged 2 commits into
mainfrom
fm/fm-port-2524-2331-wedge-noise
Sep 1, 2026
Merged

fix: prevent false wedge escalations and replayed wake status#88
dnth merged 2 commits into
mainfrom
fm/fm-port-2524-2331-wedge-noise

Conversation

@dnth

@dnth dnth commented Sep 1, 2026

Copy link
Copy Markdown
Owner

Intent

Port upstream kunchenguid/firstmate PR kunchenguid#2524 at final head 52f0753 and PR kunchenguid#2331 at final head c6553df faithfully onto dnth/firstmate current main 7b463ae. Remedy repeated false wedge storms by re-arming after proven abandoned Claude auto-arm claims and deferring wedge escalation while an ordinary task worktree is actively written, while preserving the unchanged escalation schedule for a stall that writes nothing and retaining bounded long-cadence resurfacing. Surface buried unread status notes and pending-reply resolutions exactly once with no loss or replay, preserving raw wake rows and OPEN DECISIONS behavior. Keep the port limited to the two upstream file sets with explicit fork adaptations only: preserve the durable local inbox from #68, programmatic OMP inbox wake from #71, pending-reply correlation and autonomous lifecycle behavior from #70, RunPod and OMP branch behavior, and the fork stronger fail-closed status-presentation retry boundary. Do not introduce unrelated fork-absent machinery; the absent shared fm-timeout-lib dependency is adapted with the fork established portable timeout selection and its upstream-only afk fixture is intentionally omitted, with the central shared-timeout owner left as follow-up. Verify abandoned-claim reclaim and live-claim non-reclaim, active-write deferral and bounded resurfacing, no-write escalation, secondmate-home exclusion, buried-note surfacing with no duplication, upstream file-list fidelity, fork compatibility, full Firstmate tests and lint, and green CI on the opened PR. Do not merge the PR. Avoid --yes and escalate ask-user findings through Firstmate. Start validation only when no other no-mistakes run is active because the shared credential must not race.
Firstmate-Validation-Generation: b0ad0d844bb4c1877554ce6660da3d5a

What Changed

  • Reclaim abandoned Claude auto-arm claims using ledger and process-identity checks, preventing stale locks from suppressing watcher recovery.
  • Defer wedge escalation while a task worktree is actively written, while preserving no-write escalation timing and bounded long-cadence resurfacing.
  • Transactionally surface unread status notes, pending-reply resolutions, and OPEN DECISIONS exactly once with fail-closed presentation; add matching supervision/OMP documentation and regression coverage.

Risk Assessment

✅ Low: The changed watcher, auto-arm reclaim, incremental status folding, and presentation cursor paths were inspected end-to-end; no concrete source defect or intent contradiction was substantiated. The added tests assert executable outputs and state rather than implementation text.

Testing

Targeted executable tests for the requested wake/status/auto-arm behavior passed and produced reviewer-visible CLI transcripts; full watcher-triage evidence is incomplete because the suite exceeded the bounded execution window and hit missing coreutils diagnostics in its fallback-path exercise.

Evidence: Focused behavioral test transcripts

Targeted wake-drain, OPEN DECISIONS cursor, and Claude auto-arm test transcripts are saved in the evidence directory.

ok - a note: answer buried under a later routine note: is surfaced with both lines
ok - already-presented note: lines are not re-surfaced on the next drain
ok - a brand-new note: after presentation is surfaced without replaying handled lines
ok - a queued status signal annotates every unread note, not only the newest
WAKE_ACK_REQUIRED: after handling completes run bin/fm-wake-drain.sh --ack-through 1 --recovery-generation 2405569.1788246163.ZIeEAm
WAKE_ACK_REQUIRED: after handling completes run bin/fm-wake-drain.sh --ack-through 1 --recovery-generation 2405569.1788246163.ZIeEAm
WAKE_ACK_REQUIRED: after handling completes run bin/fm-wake-drain.sh --ack-through 1 --recovery-generation 2405569.1788246163.ZIeEAm
ok - a pending-reply resolution buried under a later note surfaces once and closes OPEN DECISIONS
ok - unread status over the former byte cap preserves every line
ok - presentation cursor advances only through its captured endpoint
ok - a reused task id starts its replacement status log unread at byte zero
ok - OPEN DECISIONS still folds needs-decision/blocked independently of unread notes
WAKE_ACK_REQUIRED: after handling completes run bin/fm-wake-drain.sh --ack-through 1 --recovery-generation 2420569.1788246173.FujsW5
ok - an empty-queue drain preserves routine status for a later signal annotation
ok - routine working/done lines still print nothing on an empty-queue drain
Evidence: Claude auto-arm transcript
ok - auto-arm: inert in a linked child worktree even when in-flight
ok - auto-arm: inert with no session lock
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: 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: an abandoned owner claim is reclaimed so a lapsed cycle re-arms
ok - auto-arm: an owner still arming is never reclaimed, however long the cycle runs
ok - auto-arm: a live claim the ledger does not name is never reclaimed
ok - auto-arm: a claim whose pid was reused is reclaimed even while its ledger entry still reads arming
ok - auto-arm: a reused-pid claim is reclaimed even with no ledger entry to prove it
ok - auto-arm: an identity-matched owner still arming is never reclaimed
ok - auto-arm: the guard's terminal-check claim is never reclaimed
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: OPEN DECISIONS cursor transcript
ok - a truncated/rewritten log falls back to a full re-fold instead of dropping or misreading the decision
ok - a same-size file rotation (new inode) is detected and falls back to a full re-fold
ok - a failed presentation read fails closed and preserves status state for retry
ok - a cursor-cache read failure refolds decisions without replaying handled unread status
ok - a legacy unversioned cursor is rebuilt so a corr-tagged decision surfaces
ok - a buried decision survives many growing drains with bounded read cost, and resolution durably clears it at bounded cost too
- Outcome: ⚠️ 1 warning across 1 run (11m21s)

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

✅ **Review** - passed

✅ No issues found.

⚠️ **Test** - 1 warning
  • ⚠️ tests/fm-watch-triage.test.sh - The complete fm-watch-triage.test.sh suite did not finish within a 150-second bounded run; it emitted repeated dirname: command not found/basename: command not found diagnostics during its intentionally restricted-PATH remote deadline fallback cases, so full watcher-suite completion is not demonstrated in this phase.
  • bash tests/fm-wake-drain-unread-status.test.sh
  • bash tests/fm-wake-drain-open-decisions-cursor.test.sh
  • bash tests/fm-claude-stop-autoarm.test.sh
  • bash tests/fm-turnend-guard.test.sh
  • bash tests/fm-wake-queue.test.sh
  • bash tests/fm-gotmp.test.sh
  • timeout 150s bash tests/fm-watch-triage.test.sh (incomplete; timed out with restricted-PATH diagnostics)
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

dnth added 2 commits September 1, 2026 14:50
Port upstream kunchenguid#2524 and kunchenguid#2331 onto the fork. Reclaim abandoned Claude auto-arm claims, defer wedge alarms for active worktree writes, and transactionally surface unread status lines without replaying handled entries.

Preserve durable inbox, OMP doorbell, RunPod, and correlation semantics. Keep status presentation fail-closed, and use the fork's established portable timeout selection because the upstream shared helper is absent.
@dnth
dnth merged commit f283994 into main Sep 1, 2026
15 checks passed
@dnth
dnth deleted the fm/fm-port-2524-2331-wedge-noise branch September 1, 2026 12:50
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