agentleFS
chinthakatchinthakat/agent-skills/skills/offline-first-sync/SKILL.md

offline-first-sync

Skill0 starsChanged 52 days agoMITView on GitHub

Build or debug an app that has to keep working with no network — local writes, a queue, and reconciliation on reconnect. Use when adding offline support, when work is lost on a dropped connection, when sync produces duplicates or silently drops records, or when photos and other large files need to survive being captured out of coverage.

1 other project uses a version of this.

Adopted by

Most recently taken up by

The file

We split this skill into the rules it gives an agent

4 rules. About 0 are common, 3 are distinctive, and 1 is very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈3

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 1

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Every user action commits to local storage before anything is attempted over the network.

    line 14
  2. Distinctive:

    If you do need multiple writers, choose the model explicitly and up front: last-write-wins per field with timestamps, an append-only log of …

    line 44
  3. Distinctive:

    Never let the server allocate ids for offline-created records.

    line 77
  4. Very specific:

    Keep the device time as captured_at — it is genuinely useful, it is what the user experienced — but order, expire and reconcile on a …

    line 113

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Offline-first sync

Offline support is not a feature bolted onto a networked app. It is a different ownership model: the device owns the work until the server acknowledges it. Retrofitting is expensive, so decide early.

1. Write locally first, always

Every user action commits to local storage before anything is attempted over the network. The network call is a consequence of the local write, never a precondition for it.

user taps -> local store (durable) -> UI updates -> queue entry -> sync attempt

Not:

user taps -> POST -> wait -> on success, update UI      <- fails in a lift

The test for whether you got this right: turn the radio off mid-session, force quit the app, reopen it. Everything the user did should still be there. Not "most of it" — a field worker who loses one form stops trusting the app and starts keeping paper.

2. Name the writer, and prefer one

Most conflict misery comes from not having decided who owns a record while it is in flight. The cheap, robust answer for form-and-checklist style apps is single writer: the device that started the draft owns it until submission, and the server does not modify it.

That is a real constraint, not a deficiency — but write it down, because the first feature request that breaks it (a reviewer editing someone's in-progress form from the web) needs a conflict model you do not currently have.

If you do need multiple writers, choose the model explicitly and up front: last-write-wins per field with timestamps, an append-only log of operations, or CRDTs. Each has a different data shape. You cannot retrofit one onto a design that assumed a different one.

3. Binary files need their own queue

Photos, audio, signatures and attachments behave differently from JSON records and should not share a queue with them:

  • They are large, so they need progress, resumability and a size cap.
  • They usually upload to a different place (object storage, a presigned URL) than the record does.
  • They fail differently: the record can succeed while the photo is still uploading, which means the record has to reference a file that does not exist yet and the reader has to tolerate that.
  • They fill the device. Cap the local cache, downscale on capture, and evict once uploaded and confirmed.

Store the local file reference in the record, swap it for the remote reference when the upload is acknowledged, and make the UI show the local copy until then so the user sees their own photo immediately.

4. Every queued operation carries an idempotency key

Generate the id on the device, at the moment of creation, and use it as the idempotency key for the whole lifetime of the record.

This one decision removes a whole category of bug. The retry after a timeout whose response was actually a success, the double-tap, the resumed upload — all of them collapse into the same request, and the server's job is just "create or return the existing one".

Never let the server allocate ids for offline-created records. A device-side temporary id that gets swapped for a server id means every local reference has to be rewritten, and the rewrite runs while new records are still being created.

5. "Synced" means acknowledged

Three states, not two:

StateMeaningShown as
localWritten on device, not sent"Saved on this device"
pendingSent, no acknowledgement yet"Sending..."
syncedServer confirmed and durable"Saved"

A request that has been sent is not saved. Do not delete the local copy, mark the record green, or let the user leave a screen believing the work is safe until the server has said so. If the acknowledgement never comes, the record goes back to local and retries — it does not vanish.

6. Reconnect is a stampede

The moment coverage returns, every queued item on every device tries at once, often on a weak edge connection that dropped for a reason.

  • Send serially per device, or with a small concurrency cap. Not all at once.
  • Exponential backoff with jitter. Without jitter, every device on the site retries on the same tick.
  • Cap the retry count, then surface the failure to the user rather than retrying forever in silence.
  • Make sync resumable: it will be interrupted halfway through, routinely.

7. Trust the server's clock for ordering

Device clocks are wrong. They are wrong by minutes on cheap hardware, by hours after a timezone change, and by whatever the user set them to.

Keep the device time as captured_at — it is genuinely useful, it is what the user experienced — but order, expire and reconcile on a server-assigned timestamp. Anything security-relevant (token expiry, a window in which an action is allowed, an audit trail) uses server time only.

8. Show the state, in the user's words

  • A persistent, quiet indicator for offline. Not a toast that disappears before it is read.
  • A count of what is waiting, and a way to see the list.
  • A manual "sync now", because someone will need it and its absence reads as the app being stuck.
  • Plain language for failure: what failed, whether the work is safe, and what to do. "Sync error" tells a person on a site with no signal nothing at all.

9. Testing it

Unit tests will not find these bugs. The ones that do:

  • Airplane mode on for the whole flow, then on again.
  • Toggle mid-submit, at the exact moment the request is in flight.
  • Force quit with a full queue; reopen.
  • Throttle to slow 2G rather than cutting the connection cleanly — half-open connections and timeouts behave differently from a clean offline, and are more common in the field.
  • Fill the storage quota and keep going.
  • Two devices, the same record, if you allow that at all.

Checklist

  • Local write happens before the network call, durably
  • Writer model chosen and written down
  • Binary uploads in their own resumable, capped queue
  • Device-generated ids used as idempotency keys end to end
  • Three sync states; local copy kept until acknowledged
  • Serial or capped sync with backoff and jitter
  • Server time for ordering and expiry; device time kept as capture time
  • Offline state and pending count visible; manual sync available
  • Tested with airplane mode, force quit, throttling and a full quota

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.