Field note · May 26, 2026 · compliance
The audit log your agent doesn't keep
Three months into shipping the agent, the company gets the email every finance team eventually gets: please share, for March of this year, every refund your AI agent issued — the customer, the amount, the reason, and the policy that authorized it.
Application logs exist. Stripe records exist. What doesn't exist is a single record per agent decision saying what fired and why. The reconstruction takes a week, the answer is best-effort, and the next conversation with the regulator starts with apologies. The gap is the finding.
What auditors actually ask for
A reviewer doesn't ask for "explainable AI". They ask three questions and they want a CSV at the end of each:
- Show me every time the agent did this. A filterable record by action type, by date range, by customer or transaction id.
- Show me which policy was in effect at the time. Not the current policy — the policy version stamped on the decision when it was made.
- Show me that this log wasn't edited after the fact. A chain that breaks visibly if a row was modified.
Three primitives. If the agent doesn't emit them at the moment of the decision, you build them later from logs that weren't designed for the question.
The evidence event, written at decision time
Every supervised action emits one record. The fields that matter for the regulator are policy_version, reasons, and the link to the prior event:
{
"event_id": "ev_2026-03-18T09:14:02Z_b1f9",
"prev_event_hash": "sha256:7d…ae", // hash chain
"self_hash": "sha256:91…02", // verifiable
"action_type": "refund",
"actor": { "agent_id": "billing-agent-v3", "tenant_id": "t_842" },
"input_fingerprint": "sha256:c8…44", // not the raw payload
"decision": "allow",
"risk_score": 0.22,
"reasons": [
"refund_velocity_24h <= 3",
"customer_age_days >= 30",
"amount <= 500"
],
"policy_version": "refund.base@v1.4",
"policy_ref": "packages/policies/refund.base.v1.yaml#L42-L78",
"enforcement_mode": "enforce",
"occurred_at": "2026-03-18T09:14:02.314Z"
}Three months later, the March CSV is one filter on occurred_at and action_type away. The policy column maps each row to the historical YAML file under packages/policies/ at the version that fired. The hash chain is verifiable; a row edited later breaks the next read.
The hash chain (why this isn't just append-only)
Append-only is necessary and not sufficient. A reviewer's standard question is how do I know nobody quietly edited row 17 in row 23's favor? Each evidence event stores a hash of the previous event alongside a hash of its own contents. Verification is a linear scan: at row N, recompute the hash of row N-1; if it doesn't match the stored pointer, the chain broke somewhere in between, and the mismatch tells you where.
# python check
def verify_chain(events: list[Event]) -> int | None:
prev = None
for i, ev in enumerate(events):
if prev is not None and ev.prev_event_hash != prev.self_hash:
return i # first broken link
if ev.self_hash != sha256(canonical_bytes(ev)):
return i # row itself was modified
prev = ev
return None # chain intactVibefixing's evidence endpoint exposes the same verification. When the answer is the chain is intact at row 47,294, the reviewer accepts the rest of the conversation differently.
Replay: would today's policy decide the same?
The next regulator question is usually if you ran your policy on this case today, what would happen? The supervisor exposes the same decision endpoint in dry-run mode, so any past decision can be re-evaluated against the current policy without committing to anything:
curl -X POST https://api.vibefixing.me/v1/actions/evaluate \
-H 'authorization: Bearer …' \
-d '{
"action_type": "refund",
"input": { /* original payload, retrieved by input_fingerprint */ },
"dry_run": true
}'
# response
{
"decision": "allow",
"risk_score": 0.22,
"reasons": [
"refund_velocity_24h <= 3",
"customer_age_days >= 30",
"amount <= 500"
],
"policy_version": "refund.base@v1.7"
}The dry-run isn't a do-over; the original decision still stands. It's the document the reviewer needs to see that you can answer the counterfactual the same way every time.
What this is not
Not a SOC 2 report. Not a NIST AI RMF certification. We map the artifacts to those frameworks in the crosswalk, but the report your auditor stamps still comes from your compliance team. The evidence chain gives them the underlying data; the binder is theirs to write.
Not a guarantee that the log is court-admissible by default. Hash chains are verifiable; whether your jurisdiction treats that as evidence is a question for your counsel, not for the supervisor.
related
The compliance overview and the NIST AI RMF crosswalk.
The compliance page collects the three artifacts a reviewer actually asks for. The crosswalk maps each one to a NIST AI RMF sub-category so your security questionnaire response doesn't start from a blank page.