Skip to content
On this page

What sync guarantees ​

What happens when a fetch, a write, or a process fails: the guarantees behind sync as the repair step, the failure table, and the lock. The troubleshooting page owns the symptoms as you see them; this page owns the mechanisms behind them, and the move page owns carrying an install elsewhere or removing it.

Idempotency ​

Running the same add twice against an unchanged source, or sync any number of times, produces byte-identical files and makes zero writes after the first.

  • Store. The recorded sha is compared to the remote's before any download; equal means the fetch is skipped entirely. A live local source has no sha, so sync reads its tree and lets the output comparison decide.
  • Bodies are written only when the file's content differs from the recorded content hash.
  • Rule file. Regenerated from intent plus store, then compared; identical output means no write, so mtime does not churn.
  • State. addedAt is set once and intent changes only when the user changes it; a sync writes state only to record a refresh it performed.
  • Hook. Keyed by harness, not by source; the registry is rewritten only when the constructed entry differs.
  • Ordering. Rule lines sort by memory name, so the "nothing changed" fast path fires across machines.

There is one durable commit point, the state write, done as temp file plus rename. Every artifact after it is derived, so an interruption anywhere past that write is repaired by the next sync, which is what the next session start runs anyway.

Failure paths ​

failurebehavior
fetch fails before any writekeep the last good store, exit 2 (0 with --quiet)
fetch succeeds, some files fail the contractinstall the valid ones, warn per bad file
fetch succeeds, every file fails the contracttreat as an empty source; the existing block survives, exit 3
write fails partway through linking bodiesintent is already correct, nothing is stranded, exit 4
write fails on the rule filebodies stay, block unchanged, exit 4
process killed between store swap and rule writethe next sync re-derives everything from intent
hook fires while a manual add holds the lockthe hook exits 0 immediately without waiting
two manual adds at oncethe second polls, then exits 5
source repo deleted upstreamkeep the last good copy, warn at every start, never auto-remove
store copy missing on a new machinesync refetches on the spot, cooldown or not
a live source's directory moved or deletedthe symlink dangles, no copy exists: keep the block, report the error, never wipe
a live edit breaks a wikilink or crosses the capthat source's block keeps its previous content; other sources are unaffected

Every write is a temp file plus a rename, so a partial rule file has no representation. Never auto-removing on a fetch failure is deliberate. A rate limit and a deleted repo look alike from the client, and dropping a commit-review rule because GitHub returned 403 is the failure class maxims exists to prevent.

Concurrency ​

The store is single-writer. A writer creates state.json.lock atomically, holding its pid, host, start time, and command line. Reads never take the lock, and every write is temp plus rename, so a session starting mid-sync sees the old rule file or the new one, never a partial one.

situationbehavior
two adds, different sourcesthe second polls up to 5 seconds, then exits 5 naming the holder's command line
a hook fires during a manual addthe hook does not wait: exit 0 at once, and refresh.log names the holder
two syncs at onceone wins, the other exits 0; both would compute the same output
the holder crashed and left the locka lock older than 60 seconds is stolen, and the theft is logged
NFS or a container where pid checks lieage alone breaks the lock at 60 seconds; the worst case is a redundant rewrite

The line a skipped hook leaves in log/refresh.log, with an illustrative holder:

text
2026-10-09T12:00:00.000Z sync --quiet: skipped, store is locked by "maxims add @octocat/rules" (pid 4242 on example.com, since 2026-10-09T00:00:00.000Z)

The theft log records whether the holder's pid was still alive.

Built from main at 5f6e985Source: docs/guarantees.md