Offline tablet app · work orders and job completion · 2024–2026
Offline drafts had no owner, so the newest work lost
Not sync frequency. Offline drafts had no ownership, so a device that had been away could land stale state on top of newer work — and nothing in the model could make that write fail.
How it was reported
“Records keep reverting.”
What it actually was
The obvious reading was sync frequency — devices at sea go days without a connection. Syncing more often would have shrunk the window and left the class of bug intact.
The real fault was that a draft said nothing about who owned it or when it stopped being valid. Two devices could hold the same record, and whichever reconnected last won — not whichever held the newer work.
So it presented as reverting rather than as a conflict. Nothing errored, the write succeeded, and the model had no concept that would have let it fail.
What shipped
- Generic draft-sync DTOs, so every module inherits one ownership contract instead of inventing its own and drifting.
- A late-draft-sync guard: a draft arriving after its job has completed is rejected, because by then it provably describes a state that no longer exists.
- Soft delete with cascade cleanup, so a removal propagates instead of leaving orphans that resurface on the next sync as new.
- A versioned migration for the draft tables, plus resume-on-open — a half-finished job is still there when the app comes back, which is what the crew needed and why drafts existed.
- Then the recovery: scripts that cleared duplicated work orders and, after a cleanup cost records it should not have, restored and backfilled them. Separately, the crashes, endless loads and local-database misuse after completion events, and batch-write and list-caching work on device.
Evidence
- vessels running the platform
- 100+
- customer organisations in production
- 3
The class is closed rather than the ticket. A stale write cannot land at all now, rather than landing less often.
- React Native
- Expo
- WatermelonDB
- SQLite
- TypeScript
- NestJS