Skip to content

ADR-0013: Offline-ready by design, build online-first

ADR-0013: Offline-ready by design, build online-first

Status

Accepted

Date

2026-06-24

Context

The proof loop includes “capture field survey → submit evidence,” POC 7 is a full offline stack (offline working-set cache, action queue, local evidence vault, offline map package, conflict inbox), and the final proof standard says “a field worker can submit evidence offline.” Offline-first is effectively a distributed-systems sub-project; for a 2–3 person team it could swallow the entire proof budget.

But the kernel features offline depends on — idempotency keys and expected-version semantics — are being built anyway for Actions (POC 3). So the offline groundwork can be laid without building the offline client stack. And offline is not merely a feature to bolt on: it is a forcing function on the kernel’s core that retroactively justifies several of its most important design choices.

Decision

Build online-first; design the kernel to be offline-ready from day one. Prove the as-built loop from a connected device. Defer the offline client stack (cache, queue, local evidence vault, offline maps, conflict inbox) to post-proof field hardening.

The following are non-negotiable kernel invariants from day one, because retrofitting them later is prohibitively expensive:

  1. Actions are coarse semantic intents (SubmitSurvey{…}, ApproveAsBuilt{…}), never field-level column patches — so a queued offline action replays meaningfully after long gaps. (This is the strongest argument for the Action-as-intent model generally.)
  2. Client-minted IDs + idempotency keys. Entities get client-generated UUIDs and Actions carry client idempotency keys, minted on-device before sync. The “immutable internal ID” is client-generatable, not a server sequence. Reconnect dedups; it never duplicates.
  3. Expected-version + conflict-as-first-class-outcome. Optimistic concurrency on every Action; a stale edit surfaces as a modeled reconciliation result feeding a conflict inbox — never a raw error. (POC 6’s GIS-vs-survey case is the offline conflict case; see ADR-0004.)
  4. Working Set = the sync/cache boundary. The offline package is a bounded, materializable working-set snapshot (a crew’s assigned work + neighbors). Working sets must be definable and size-bounded by design so they can be cached and synced.

Bitemporal time (validTime = when it happened in the field vs transactionTime = when it synced) rides along for free in the Statement model and is part of this readiness.

Alternatives Considered

Offline-first in the proof (build POC 7 fully)

  • Rejected: highest fidelity, but a distributed-systems sub-project that could consume the whole proof.

Thin offline (queue + retry only)

  • Not chosen as the build target, but invariants #1–#2 effectively make a thin queue trivial to add later.

Drop field capture from the proof

  • Rejected: cuts the field-evidence differentiator entirely.

Treat offline as a pure client concern, decide kernel later

  • Rejected: client-minted IDs and conflict-as-outcome are kernel contracts; deciding them late forces a painful rewrite of identity and the Action result model.

Consequences

  • “A field worker submits offline” is reframed as a product proof point delivered later, not a kernel-validity proof point — but the kernel can never assume a server-minted ID, a connected client, a field-level patch, or an unbounded working set.
  • The identity scheme is fixed early: client-generatable UUIDs. This is expensive to reverse, hence its place as an invariant.
  • Interacts with ADR-0003 (expected-version against current state) and ADR-0004 (conflict resolution reuses statement/authority machinery).