feat(daemon): durable health state, divergence escalation, staleness checks, watcher pre-filter - #188
Merged
Merged
Conversation
…checks, watcher pre-filter Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011L7s33UCjpQkreXW7amAw2
This was referenced Jun 20, 2026
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.
Why
For a backup daemon, the worst failure is a silent one. Today the daemon's failure state lived only in memory (lost on restart), there was no notification or staleness signal, a vault divergence made it retry a doomed push on every keystroke forever, and high-churn agent dirs (sessions/history) woke pushes that snapshot nothing. A user could go weeks with zero successful backups and no signal.
Fifth PR in the series (follows #184, #185, #186, #187).
What
daemon-state.json(last success, last error, consecutive failures, stuck flag) and updates it on every success/failure, so health survives a crash/restart.daemon statusnow reports last-successful-sync age, failures, and a stuck flag, and falls back to the durable file when the daemon is down — you can still see when sync last worked.doctorgains a Daemon sync health row: fails when stuck, warns when installed but the last success is >24h old (or never), passes on a recent success. A silent backup failure is now loud.DIVERGED_HISTORYthe daemon latchesstuck, fires one desktop notification (osascript/notify-send, best-effort), and backs off watcher retries to once per 5 min instead of hammering. A manualagentsync pushis never throttled; the next success clears it.Review fixes (caught in senior review)
Atomic temp+rename for the state file (no torn write on crash); ISO-
datetime()validation on stored timestamps (a corrupt value degrades to empty instead of a misleadingDate.parseNaN); isolatedAGENTSYNC_DIRin the daemon test harness so tests never touch the real~/.config/agentsyncor fire real notifications.Tests
state.test.ts(pure transitions incl. the stuck latch, persistence round-trip, corrupt-file degrade, formatAge); watchershouldSkipfilter;doctorhealth (stuck/never/not-installed/stale/recent);daemon statusstuck-escalation, last-sync age, and offline durable-file fallback; daemon-index persistence-on-failure, startup-preserves-stuck, and health-field exposure. Full suite: 966 pass / 0 fail.CI note
bun testexits non-zero on the per-file coverage floor; CI treats 0-fail as success.🤖 Generated with Claude Code