Audit Logs That Survive an Incident
Audit logs get designed for compliance and used during incidents. One question — who caused what, to whom, under what authority — decides every field, and most logs can't answer the authority part.
The audit log gets designed for compliance and used during incidents. Those are different requirements, and the gap shows up at the worst moment: someone asks what the agent did to a customer's account on Tuesday, and the answer available is a trace that has since been downsampled, a summary of a tool call rather than its arguments, and no record of which credential authorized anything.
The question the log has to answer
Not "what happened" in general — one specific question, asked under pressure:
Who caused what, to whom, when, under what authority, and on what basis?
Every field earns its place by contributing to that sentence. Working through it:
Who — the end user, not the service account. If your log records agent-service as the actor for every action, it cannot answer this at all.
What — the tool, the full arguments, and the actual outcome. Not "updated the account" but which fields, from which values to which values.
To whom — the affected entity, in a form that's queryable. "Show me everything that touched account 8821" needs to be a query, not a log grep.
When — timestamps on the action, and separately on the run that produced it. A run started Monday can act Tuesday.
Under what authority — which credential or grant, which scopes, and whether a human approved. This is what distinguishes authorized-and-wrong from unauthorized, and it's the field most often absent.
On what basis — a reference to the run and the specific turn, so the reasoning can be reconstructed. Not the reasoning inline; a stable pointer to it.
Separate the audit log from the trace
They have different retention, different access controls, and different volume, and conflating them means one set of compromises serving neither.
The trace is high-volume, debugging-oriented, and can be sampled or aged out. Every model call, every array, every token count.
The audit log is low-volume — only consequential actions — append-only, retained for years, and access-controlled. It should be small enough that nothing about it is ever downsampled.
⚠️ The failure mode is discovering that your only record of a consequential action was in a trace subject to a thirty-day retention policy, and the question arrives on day forty.
What must not happen to it
Append-only. Not updatable, not deletable by the application. A log the agent's own service can rewrite is not evidence.
Written before the effect, confirmed after. Record the intent, act, record the outcome. A crash between the two leaves an intent with no outcome, which is exactly the state you want recorded rather than silence.
Independent of the primary datastore. If the audit log lives in the database the incident is about, an incident affecting that database takes the log with it.
Free of the data itself, where possible. Reference the entity and the fields changed rather than embedding customer data, so the audit log doesn't become another copy of sensitive data with its own retention obligations. ✅ Field names plus a reference beats field values for both privacy and volume.
Agent-specific fields worth having
Beyond a standard audit record:
- Run ID and turn index, so the action links to its reasoning.
- Prompt version and model version. When a class of wrong actions is discovered, the first question is which configuration produced them, and this makes the blast radius a query.
- Whether the action was gated, and by whom if so.
- Whether the run was autonomous or user-initiated, and if triggered, by what.
- Any untrusted content in scope at the time. If the run had fetched external content before acting, that's the first thing an investigation wants to know.
That last one is unusual and valuable: it turns "was this injection-related?" from a manual transcript review into a filter.
🔍 Test it against a real question
Once, before you need it, pick a consequential action from last month and try to answer the full sentence from the audit log alone — no transcripts, no memory, no asking the person who built it.
The gaps you find are the fields to add. This exercise takes twenty minutes and is the only way to know whether the log works, because every audit log looks adequate until it's queried in anger.
The takeaway
Design the audit log around one question — who caused what, to whom, when, under what authority, on what basis — and check that every field contributes. Keep it separate from traces, append-only, outside the primary datastore, and small enough never to be sampled. Add run and turn references, prompt and model versions, and a flag for untrusted content in scope. Then rehearse a real query against it, because the day you need it is the wrong day to discover what's missing.