Event sourcing an LLM application means storing every step an agent takes as an immutable event in an append-only log. Derive state by folding that log through a projection function. Record the model's response as an event. Replay rebuilds the run without calling the model again. State stops being a running total you trust and becomes a fact you recompute.
Ross Jones — Founder, The Hopium Lab. Last modified 22 July 2026.
Engineering commentary, not legal advice.
What actually goes in the log — commands or events?
Events only. A command is a request that can be rejected: IssueRefund. An event is a fact that already happened and cannot be rejected: RefundToolReturned. The agent emits commands. The log stores what came back.
Lineage matters. Bertrand Meyer separated commands from queries at the method level; Greg Young moved the separation to two whole models as CQRS; Fowler wrote it up crediting Young. A run reads RunStarted, ModelResponded, ToolReturned, OutputCommitted. Past tense, always. Naming an event in the imperative is the most common error in agent logs, and it survives review because it reads like the code that emitted it.
ESAA (arXiv:2602.23193, 26 February 2026) draws the boundary: agents emit JSON intentions with no write access to the repository or the event store, and a deterministic orchestrator validates, appends, applies effects and projects a view. Single author, two self-evaluated case studies — prior art, not validation.
Is event sourcing the same as event-driven architecture?
No. Event sourcing is also not the practice of feeding event streams to a model as context. Event-driven architecture governs how services talk to each other; event sourcing governs how one service persists its own state; CQRS governs whether reads and writes share a model. Any one runs without the other two, and most write-ups on agent architecture slide between them mid-article.
Both leading pages are vendor content: a platform pitch calling event sourcing the supporting column of memory, RAG and vector embeddings, and a heise piece (2 April 2026) arguing event streams as better model context. Retrieval substrate is one thesis. Agent runtime is another, and the runtime is the subject here.
Why does a non-deterministic writer need an immutable log?
Because the past cannot be recomputed when a model wrote it. A deterministic writer lets you discard state and derive it again from inputs. A model does not: the response was a one-time observation, and a running total updated in place is the only copy of a fact nobody can regenerate.
Fowler settled the mechanism in Event Sourcing, 12 December 2005. His External Query section warns that rebuilding state requires the external query responses made in the past, and prescribes recording the response into the stream. His External Update section wraps outbound systems in a gateway that emits nothing during a replay. An LLM call is precisely Fowler's External Query, and the pattern is twenty years old.
Event sourcing buys reproducibility. It never buys correctness. Record the wrong fact and replay reproduces the wrong fact perfectly, forever.
The one-line version is the Replay Test: can you rebuild the answer without calling the model? If not, you don't have a system. You have a demo with a try/except around it.
What is the difference between replay, re-execution and forking?
Replay reads the log, re-execution calls the model again, and a fork does one then the other. Three operations, three guarantees, and conflating any pair loses the expert reader.
Replay folds the recorded log through a projection function — no model call, no tool call, no network. Re-execution runs the agent again from the same inputs, and every external call happens again. Fork replays to event N, then executes live from N onward.
| Operation | Calls the model | Reproducible | Broken by | Use for |
|---|---|---|---|---|
| Replay | Never | Byte-identical if the projection is pure | Map order, float formatting, collation, clock reads, hash seed | Audit, crash recovery, read models |
| Re-execution | Every call, again | Never, on hosted inference | Model retirement, weight updates, batch non-invariance, tool drift | Evals, regression suites |
| Fork | After the fork point | Deterministic prefix, live suffix | Both rows above | Counterfactuals, fault injection |
Model version drift cannot break replay, because replay never asks the model anything. Drift breaks re-execution instead, a weaker guarantee.
Re-execution stays irreproducible even with pinned weights, temperature 0 and a fixed seed. Thinking Machines Lab traced the cause to batch non-invariance (10 September 2025): kernels return different results depending on the batch a request landed in, and batch size moves with server load. Their batch-invariant kernels produced 1,000 identical completions across 1,000 runs — on hardware they own.
How do you correct a mistake without rewriting history?
Append a correction event and let the projection decide what wins. No UPDATE, ever, on a stored fact.
State the rule honestly, though. Fowler's Retroactive Event (12 December 2005) handles a rejected event with a flag on the stored event, ignored by all further processing so that replay skips it — and a flag is not a pure append. Overeem and colleagues studied 19 event-sourced systems and 25 engineers (arXiv:2104.01146, Journal of Systems and Software Vol. 178, 2021) and found five schema-evolution tactics: versioned events, weak schema, upcasting, in-place transformation and copy-and-transform. The last two rewrite the log outright. Defensible formulation: append-only at the domain level, with schema migration as a governed exception carrying its own change record.
For agents the correction event is the most valuable row in the log. A human overturning a model's classification is a fact, with an author, a timestamp and a reason. Overwrite the row and the disagreement vanishes with it.
What breaks a byte-identical rebuild?
The projection, not the model. Purity is the whole requirement.
- The re-execution conflation. Replay whose code path can still reach a provider. Assert on the socket, not in a comment.
- Clock reads in the fold.
now()inside a projection means the answer changes at midnight. - Self-referential hashing. Including the hash field in the bytes being hashed. ESAA excludes it, and the run metadata with it.
- The dual write. State in Postgres, events written alongside. Divergence guaranteed, log decorative.
Canonicalise before hashing. Name RFC 8785 if you name a scheme — Informational, not Standards Track, and calling it a standard costs you the reader. JCS sorts properties by UTF-16 code unit, requires UTF-8, and pins numbers to IEEE-754 doubles, which quietly forbids exact decimals: money needs integers of minor units. Canonical form, trust-boundary hashing and a runnable verifier is most of what I build as an evidence layer.
Is event sourcing just durable execution with extra steps?
Frequently, yes. For plenty of agent teams Temporal is the right answer, and a bespoke event store is a distributed-systems problem they chose to acquire.
Temporal shipped the mechanism years ago. Its workflow execution model keeps a per-execution Event History and pushes non-deterministic work into Activities whose results are recorded in history and served from history on replay. The correct treatment of an LLM call, written before the agent boom.
Scope is the real difference. Durable execution gives replay in service of executing a workflow to completion. Event sourcing makes the log the domain source of truth: queryable, projectable into read models that outlive the run.
The overhead is affordable. The OpenHands Software Agent SDK (arXiv:2511.03690, 11 authors, 5 November 2025) appends all interactions as immutable events, reporting 0.20 ms median per-event persistence, 4.1 ms full state replay and 380 KB median storage per conversation — negligible against round trips of 1 to 30 seconds. Read the design honestly, though: mutable metadata lives in base_state.json beside the append-only log. A hybrid, in production — the pure form is rarer than the blogs suggest.
When is event sourcing the wrong tool?
More often than vendor pages admit, and the two people most associated with the pattern say so. Fowler's caution is aimed at CQRS — risky complexity for most systems, be very cautious — and it applies here because teams adopt the pair together. Greg Young, who originated CQRS and co-founded the event store now trading as Kurrent, gave a 2022 talk called Event Sourcing: The bad parts and argues against event-sourcing an entire system.
Practitioners in the 19-system study named five costs: event system evolution, the steep learning curve, lack of available technology, rebuilding projections, and data privacy. Rebuilding projections is no abstraction for agents: 380 KB and hundreds of events per conversation, across a fleet, makes a schema change an operations project.
Skip it for single-turn stateless completion, where no history exists to rebuild; where the read model is the write model; for high-volume telemetry, where a trace store solved this years ago; and wherever nobody will fund supporting every schema you ever published, forever.
Erasure earns a line. Every prompt recorded may hold personal data somebody is later obliged to delete. The EDPB's Guidelines 02/2025, adopted 7 July 2026, hold that personal data should as a general rule stay off an immutable ledger where storage conflicts with data-protection principles — written for blockchain rather than application event stores, so the transfer is analogy and nothing more. Crypto-shredding mitigates: one key per subject, destroyed on request, key management as the price. Retention floors sit in Article 12 logging.
Presenting event sourcing as free is how the pattern loses arguments it should win. The bill is permanent compatibility: every event schema ever published, supported forever or migrated deliberately.
What does a minimal event-sourced agent runtime look like?
An event envelope, a pure projection, and a scheduled rebuild that fails loudly. Snapshots are a later optimisation.
THE AGENT EVENT ENVELOPE — schema and projection contract
The Hopium Lab · v1.0 · 22 July 2026
Supports a conformity case. Never confers one.
# REPLAY fold the log through a projection. No model, tool or network call.
# RE-EXEC run the agent again. Never byte-identical on hosted inference.
# FORK replay to event N, then run live from N.
event:
stream_id: one run, one stream # the identity replay folds over
seq: monotonic, gapless, within the stream
event_id: sha256(0x01 || canonical(stream_id, seq, type, schema_ver,
occurred_at, payload)) # RFC 8785 canonical form
type: past tense, always # ModelResponded, not CallModel
schema_ver: integer, bumped on payload change, never reused
occurred_at: RFC 3339 UTC, written by the writer, never read in a projection
payload: the fact, verbatim as observed
correction_of: event_id | null # corrections append
PROJECTION CONTRACT
[ ] Pure function of (state, event). No clock, no network, no randomness.
[ ] event_id binds stream and seq, not payload alone — identical payloads recur.
[ ] Sorted keys, stable sort, canonical form; hash excludes the hash field.
[ ] Pin ICU, pin tzdata, set PYTHONHASHSEED or the runtime equivalent.
[ ] Replay mode has no code path that can reach a model provider.
[ ] Drop the read store and rebuild from the log, in CI, every week.
[ ] Every schema_ver published still has a reader, or a migration record.
The rebuild box is the discriminator. A team that has never dropped its read store and rebuilt from the log has an audit log with ceremony, not an event-sourced system.
My own auditor scores seventeen categories before a product ships (full list); Infrastructure is where a rebuild that has never actually been run gets caught. Schema detail sits in reproducible LLM systems.
Write the fact down when you observe it. Everything else is a fold.
Ross Jones, Founder, The Hopium Lab. Last modified 22 July 2026. Engineering commentary, not legal advice.