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:
- 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.) - 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.
- 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.)
- 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).