feat(daemon): add sync backoff and consolidate hints into tips

Daemon sync improvements:
- Adds exponential backoff on sync failures (30s → 1m → 2m → 5m → 10m → 30m cap)
- Tracks sync state in .beads/sync-state.json (NeedsManualSync, FailureCount, BackoffUntil)
- Resets backoff on daemon start and manual bd sync
- Adds sync-state.json to default .gitignore

Tips consolidation (following ox-cli pattern):
- Moves sync conflict hint from hints.go into tips.go
- Proactive health checks trump educational tips
- Uses InjectTip() with high priority (200) and 100% probability for urgent warnings
- Removes deprecated hints.go
This commit is contained in:
Ryan Snodgrass
2025-12-26 19:09:31 -05:00
parent 721ae70ccb
commit 252de1cdba
8 changed files with 240 additions and 2 deletions

View File

@@ -529,6 +529,19 @@ func performAutoImport(ctx context.Context, store storage.Storage, skipGit bool,
if skipGit {
mode = "local auto-import"
}
// Check backoff before attempting sync (skip for local mode)
if !skipGit {
jsonlPath := findJSONLPath()
if jsonlPath != "" {
beadsDir := filepath.Dir(jsonlPath)
if ShouldSkipSync(beadsDir) {
log.log("Skipping %s: in backoff period", mode)
return
}
}
}
log.log("Starting %s...", mode)
jsonlPath := findJSONLPath()
@@ -579,14 +592,16 @@ func performAutoImport(ctx context.Context, store storage.Storage, skipGit bool,
// Try sync branch first
pulled, err := syncBranchPull(importCtx, store, log)
if err != nil {
log.log("Sync branch pull failed: %v", err)
backoff := RecordSyncFailure(beadsDir, err.Error())
log.log("Sync branch pull failed: %v (backoff: %v)", err, backoff)
return
}
// If sync branch not configured, use regular pull
if !pulled {
if err := gitPull(importCtx); err != nil {
log.log("Pull failed: %v", err)
backoff := RecordSyncFailure(beadsDir, err.Error())
log.log("Pull failed: %v (backoff: %v)", err, backoff)
return
}
log.log("Pulled from remote")
@@ -622,6 +637,8 @@ func performAutoImport(ctx context.Context, store storage.Storage, skipGit bool,
if skipGit {
log.log("Local auto-import complete")
} else {
// Record success to clear backoff state
RecordSyncSuccess(beadsDir)
log.log("Auto-import complete")
}
}