C.W.K.
Stream
Lesson 04 of 04 · published

Interruption Changes Status, Not Input

~11 min · state-machine, immutability, resilience, status

Level 0Dead Zone
0 XP0/32 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
"The payload is frozen the instant you hit submit. After that, the only thing the world can touch is a status flag."

Two Kinds of State in One Turn

A captured turn has two very different kinds of state, and keeping them separate is what makes interruptions harmless:

  • The payload — the question text and its image blobs. This is immutable from the moment of capture. Nothing about a network event, an app kill, or a retry ever rewrites it.
  • The sync statuspending → sending → answered | failed. This is the only mutable part, and every transition is a durable write.

Once you see a turn this way, "what happens when the app is killed mid-send?" has a boring, correct answer: the payload is untouched (it was frozen and durable), and the status is either still sending or already flipped to failed. Either way, the next launch knows exactly what to do. Interruption is not a data-loss event. It's a status-transition event.

Why Immutability of the Payload Matters

If interruptions could mutate the payload, every failure would be a potential corruption. A retry that re-encodes the question, a background sync that re-reads a half-written blob, a crash between two field updates — each becomes a way for the user's actual words to change without consent. By freezing the payload at capture and only ever transitioning status, you collapse a huge space of failure modes into one small, well-understood state machine. The scary question "is my data still correct after that crash?" becomes "which status is it in?" — a question with four possible answers, all safe.

The Status Machine Is the Contract

Design the status transitions explicitly and the resilience follows. There is no transition that deletes a payload. There is no status that means "lost." The worst status is failed, which still holds the full turn and simply invites a retry. That's the promise made visible: an interrupted turn is never gone — it's just somewhere on a small, safe map, waiting.

Code

Payload frozen; only status transitions·typescript
// The payload is readonly after capture. Interruptions touch status only.
type CapturedTurn = {
  readonly deviceTurnId: string;
  readonly question: string;          // frozen at submit
  readonly attachmentIds: string[];   // frozen at submit
  syncStatus: "pending" | "sending" | "answered" | "failed"; // the only knob
  answer?: string;                    // set once, on success
};

// The legal transitions form a tiny, safe map:
//   pending  -> sending
//   sending  -> answered | failed
//   failed   -> sending        (retry)
// No transition ever deletes question/attachmentIds. 'Lost' is not a state.

External links

Exercise

Draw the full status state machine for a captured turn: every state, every legal transition, and what triggers each. Then try to add a transition that loses the payload — and notice you can't, because none of the safe states point to deletion. Finally, mark which transitions are durable writes (all of them). That diagram is the capture-first promise in one picture.
Hint
States: pending, sending, answered, failed. Triggers: submit, drain start, delivery success, delivery error, retry. There is deliberately no 'lost' node — the absence of that node is the whole point.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.