Make It Survive: Journals, Resume, and a UI That Only Watches
Explore how to design an AI agent harness that survives process interruptions by persisting events in an append-only journal. Understand managing uncertain states and session resumption with explicit refusal rules. Gain hands-on experience creating robust message handling, event ordering, and resumption logic that maintain consistency across crashes and restarts.
We'll cover the following...
- What has to survive, exactly?
- What does a crash actually look like on disk?
- What may a resumed session assume?
- Who is allowed to draw the screen?
- Can you terminate it and start it again?
- How do you test something that only happens during a crash?
- What did the real harnesses need that this skips?
- Can you talk to it yourself?
- What’s next?
Wren can talk, act, and refuse. Terminate the process halfway through, and all of that is gone.
Worse than gone. The last thing the agent did was call write, and nothing on disk says whether that write finished. A second session that assumes the safe answer either redoes a completed change or skips one that never happened, and it cannot tell which case it is in.
This lesson adds two files and 108 lines. When you are done, you run the demo, stop it, run it again, and watch it refuse to guess.
What has to survive, exactly?
Enough to rebuild the conversation and an honest record of what is uncertain.
The temptation is to save the transcript because that is what the next run needs. It is also what goes stale first: a transcript is a summary of events, so saving it means maintaining two representations that can disagree, and the one you saved is the one that gets out of date.
Wren saves events instead. The kernel already emits everything needed to rebuild the transcript, so the journal is the source of truth, and the messages are derived:
Read it as four questions the journal answers, not as one persistence utility.
Lines 14 to 32 answer what happened. Line 18 reads the journal; lines 19 to 21 treat a missing file as an empty session, so the first run and a lost journal share one path. Lines 23 to 30 parse one line at a time and
breakon the first malformed one, turning a torn final line into a non-event.Lines 34 to 37 answer how it grows. Line 35 updates memory, then line 36 appends one JSON line to disk. No rewrite means a crash can lose only the tail.
Lines 47 to 55 answer what the conversation was.
run_startbecomes the user message,turn_endthe assistant's, andresultthe tool message.messages()rebuilds rather than trusting a second copy.Lines 59 to 66 answer what remains uncertain. The next section makes that refusal rule explicit.
This is the checkpoint lesson's argument in code. A summary is an interpretation, and an interpretation of a run cannot be checked against the run. Events can be checked because they are what happened. The cost is that a journal is larger and less readable than a summary, and the benefit is that a fresh session never has to trust it.
Why append-only, and why before anything else?
Because the failure you are designing for happens between two writes.
Every event is appended to a JSON Lines file, one line per event, and ...