State ownership, conditional writes and lost acknowledgements
Outcome and mental model
Model what the user intended, what the server accepted, and what the UI currently knows as separate facts. A timeout means the client lacks confirmation; it does not mean the server rolled back.
The workbench uses URL state for shareable filters, component state for transient input, IndexedDB for pending edits, and SQLite for accepted operations. Avoid mirroring every server object into an independent React store. Duplication requires explicit reconciliation and can erase the benefit of a query cache.
Worked submission protocol
Allocate an operation ID once per validated submission intent. Send it with the payload. The server validates identity and fields, then atomically inserts the operation and receipt under a unique scope/key constraint. A retry with the same payload returns the previous receipt. A retry with changed fields is a conflict. A disabled submit button is helpful, but the server must handle concurrent tabs and direct HTTP requests.
The fault toggle destroys the socket after the database commit. The browser retains the operation key and lets the user retry. This gives a concrete way to test a frequently missed distributed-systems boundary without pretending a network exception proves no side effect happened.
Conditional edits and conflicts
A field note carries a server revision. A queued edit includes its base revision and unique edit ID. The server accepts it only if the base revision matches, increments the version, and records the edit's receipt. A repeated edit ID recovers the result. A stale base revision returns a conflict; the browser retains the pending edit instead of clearing it as “synced.”
This is optimistic concurrency control, not a CRDT. It is appropriate when conflicts are infrequent and users can reconcile them. For simultaneous collaborative editing, evaluate operation semantics, merge behavior, tombstones and storage growth before introducing CRDTs or operational transformation. Last-write-wins is a product decision that can destroy legitimate work.
State-machine design
Represent submission states explicitly: editing, submitting, unconfirmed, accepted, rejected. “Unconfirmed” offers retry/reconciliation, not a fresh operation by default. For complex parallel concerns, a statechart can separate connection status from workflow status; disconnecting need not move a completed operation backward.
Keep reducers pure and side effects in commands or services. Derive whether the submit button is disabled from state rather than maintaining another boolean that can disagree. For external stores, use React's supported subscription interface and provide a stable snapshot contract.
Exercise, expected answer and migration
Open two views at the same revision, edit both, and sync. Exactly one should win the revision check; the other retains a visible conflict. Inject a lost response after acceptance and recover the same receipt. A strong answer describes scope, uniqueness, retention and which transaction establishes the invariant. Roll out idempotency by accepting old clients during a defined transition, but do not claim the guarantee for requests lacking an operation key.
Executable reference and sources
Implementation · Behavioral evidence · Primary source.
Run the projects and assessment. Human review is pending.