Give Your AI Agents an Append-Only Event Log
An append-only event log lets you replay exactly what your AI agent did, and catches the crashed runs a status field hides.
Your AI agent finished its run. The exit code was zero. The log says done. But did it actually do the work? Most agent setups cannot answer that. They store the last state and nothing about how they got there.
An append-only event log records every start, finish, and lock as a separate timestamped line that is never edited or deleted. Because the log is immutable, you can replay it to reconstruct exactly what your agent did at any point in time. This catches crashed runs and stuck locks that a status field alone hides.
Why can't you tell what your agent actually did?
Status fields lie by omission. A row that says status: done tells you the final answer, not the path. If the agent crashed halfway and a later run overwrote the row, the crash is gone. You see green. You shipped nothing.
I hit this building the brain that runs my one-person company. A scheduled task would die mid-run, the next run would stamp a fresh status, and the morning report showed everything fine. The state was current. The history was a lie.
What is an append-only event log?
It is a file you only ever add lines to. Never edit. Never delete. Each line is one event: what happened, when, and which run or lock it belongs to. I write one JSONL file per day at Reports/Events/2026-06-22.jsonl. One event per line. New events go at the bottom.
The rule is the whole point. Mutable state answers "what is true now." An immutable log answers "what happened, in order." You need both, and most setups only keep the first.
Isn't this just logging?
Logs are for humans to read after something breaks. An event log is structured data a program reads to make a decision. The difference is the schema. Each event has fixed fields (type, timestamp, run id) so a checker can fold them without parsing prose.
Plain logs also get rotated, truncated, and edited. The append-only rule is a promise: this record is complete and ordered. You can build automated checks on that promise. You cannot build them on a log file that some cleanup job trims every week.
What events should an agent emit?
Start with two pairs. Every run emits run.start when it begins and run.finish when it ends. Every lock emits lock.acquire when taken and lock.release when freed. That is four event types and it already covers the failures that hurt most.
I wired this into one shared include that every scheduled task already loads, so I added zero lines to any individual agent. The instrumentation lives in one place. Every runner got it for free.
How do you reconstruct state from events?
You read the log top to bottom and fold each event into a running picture. A run.start with no matching run.finish means a run that never ended. A lock.acquire with no lock.release and a dead process means a stuck lock.
I built a replay --at <timestamp> command that stops folding at any instant and prints the system as it stood right then. When something broke at 3am, I do not guess. I replay 3am and look.
What does this catch that a status field never could?
Orphaned runs. A task that started, crashed, and left no finish line. The status field had already moved on. The event log still had the dangling start, so the check flagged it.
Leaked locks. A run grabbed a lock, died, and never released it. The next run blocked forever and looked "pending," not failed. The acquire-with-no-release pattern made it obvious.
Parity gaps. The finish-only ledger and the event stream should agree on how many runs happened. When they disagree, one of them is missing reality. The log is the tiebreaker because it cannot be rewritten.
Start small
You do not need a queue, a message broker, or a new dependency. A flat JSONL file and an append are enough to begin. Emit start and finish for your runs. Add lock events when you have shared resources. Write a tiny reader that flags any start without a finish.
The discipline that makes it work is the append-only rule. The second you let code edit old events, you are back to a status field with extra steps. Keep it immutable and the log will tell you the truth your dashboard hides.
If you want runtime limits on top of that visibility, AgentGuard caps token, budget, and rate spend per agent so a stuck loop stops before it drains your account: https://bmdpat.com/tools/agentguard
Want more like this?
AI agent builds, real costs, what works. M-F only when there is something worth sending. No fluff.
Patrick Hughes
Building BMD HODL — a one-person AI-operated holding company. Nashville, Tennessee. Twenty-Two agents.
More writing
- 4 min
Missing AI agent cost data is not zero
A spend ledger that counts missing billing data as $0 hides exactly the unattended agent spend you built it to catch.
- 5 min
The Silent-Success Trap: Your Monitoring Is Green and You Still Shipped Nothing
Every dashboard was green and zero blog posts went live. Exit codes tell you the job ran, not that the outcome happened. Here is how to check the real artifact instead.
- 5 min
Microsoft Told Engineers to Ease Off Claude Code
If Microsoft can't absorb agent inference costs, neither can you. Make the cap a config change, not a memo.