Skip to content

fix(sync): failure-backoff so a dead remote isn't probed every command (#133) - #136

Merged
arimxyer merged 1 commit into
mainfrom
fix/sync-failure-backoff-133
Jul 1, 2026
Merged

fix(sync): failure-backoff so a dead remote isn't probed every command (#133)#136
arimxyer merged 1 commit into
mainfrom
fix/sync-failure-backoff-133

Conversation

@arimxyer

@arimxyer arimxyer commented Jul 1, 2026

Copy link
Copy Markdown
Collaborator

Closes #133.

Problem

The pre-unlock TTL-gate (#103) only stamped LastPullCheck on a successful probe. So with sync enabled and the rclone remote down/slow, every command re-probed and blocked for the full 8s probe timeout:

Warning: failed to check remote state: rclone lsjson failed: timed out after 8s

The gate that was supposed to skip re-probing never opened, because a dead remote never records a check.

Fix (issue option 1 — the root cause)

A failed probe now stamps a separate LastPullFailure and enters a backoff (reusing the pull_ttl_seconds window, default 30s):

  • SmartPull skips the probe while the backoff is active and serves the local vault → a dead remote costs the timeout at most once per window instead of every call.
  • A successful contact clears the failure (stampSuccess), so recovery re-enables normal sync immediately.

Data-loss safety (the important part)

A write during the backoff is deferred, not pushed. The advisor flagged the trap: the remote can recover mid-window, so "the remote is down, a push can't overwrite anything" is false. Blind-pushing (or letting rclone sync fail — which is unbounded, not 8s-capped) would silently overwrite a remote that another device updated during the window. Deferring keeps the local change local until the next command after the window expires, when a fresh probe can conflict-check. The existing success-skip path is unchanged (still does its push-time conflict check).

Scope

  • Reuses the existing pull_ttl_seconds knob as the backoff window (one knob, no new config).
  • A configurable probe timeout (issue option 2) is left as a follow-up — a slow-but-alive remote (>8s) is treated as failed.

Tests

5 new tests in internal/sync/sync_test.go:

  • probe error stamps LastPullFailure (not LastPullCheck)
  • backoff skips the re-probe (0 Run calls)
  • backoff expiry re-probes
  • success clears the failure
  • SmartPush defers during backoff: 0 probe calls, 0 push calls, LastPushHash untouched

Full unit + integration suite green; golangci-lint clean.

🤖 Generated with Claude Code

#133)

The pre-unlock TTL-gate (#103) only stamped LastPullCheck on a *successful*
probe, so with sync enabled and the rclone remote down/slow every command
re-probed and blocked for the full 8s probe timeout:

    Warning: failed to check remote state: rclone lsjson failed: timed out after 8s

Fix: a failed probe now stamps a separate LastPullFailure and enters a
backoff (reusing the pull_ttl_seconds window, default 30s). While the backoff
is active SmartPull skips the probe and serves the local vault, so a dead
remote costs the timeout at most once per window instead of on every call. A
successful contact clears the failure (stampSuccess), so recovery re-enables
normal sync immediately.

Data-loss safety: a write during the backoff is DEFERRED, not pushed. Blind-
pushing (or "let rclone fail") would silently overwrite a remote that
recovered mid-window; deferring keeps the local change local until the next
command after the window expires, when a fresh probe can conflict-check. The
success-skip path is unchanged (still does its push-time conflict check).

Reuses the existing pull_ttl_seconds knob as the backoff window (one knob).
A configurable probe timeout (issue option 2) is left as a follow-up.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014kVLjbUL4F4CkoA7RbMYy8
@arimxyer
arimxyer merged commit 79c10a9 into main Jul 1, 2026
7 checks passed
@arimxyer
arimxyer deleted the fix/sync-failure-backoff-133 branch July 1, 2026 18:52
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.

exec/agent: 8s dead-remote sync pull is paid on every call (TTL-gate only stamps on success)

1 participant