How to Audit What Your Coding Agent Actually Runs (2026)
Real recording. It ends on the answer this post is about: grep '"event":"pre"' ~/.acp/audit.jsonl — every call the agent made, its decision (deny / ask / allow), and why, in one line each, on-device. That receipt is the hook-path audit log (option #4 below), not a reconstruction from a transcript.
Every incident writeup has the same second act: after the deletion, the force-push, the leaked file — the team tries to reconstruct what the agent actually did, and discovers the answer lives in a scroll of transcript, if it lives anywhere. “If I can’t reconstruct what it did, I don’t trust it yet” is the whole requirement. Four ways to get there, and what each misses.
1. Harness transcripts
Claude Code writes session transcripts (JSONL under ~/.claude/projects/),
Codex and Cursor keep their own equivalents. Real data, already on disk,
free. Three gaps: they’re per-tool (three agents, three formats, three
places), they’re conversation-shaped (finding the twelve commands inside
ten thousand lines of model turns is work), and they’re decision-free —
a transcript says what the agent said it ran; it doesn’t say what was
allowed, denied, or why.
2. Shell history
Tempting and mostly empty: agents execute through their own exec tools, not
your interactive shell, so ~/.zsh_history misses nearly everything. Skip.
3. OS-level logging
script, auditd, EndpointSecurity on macOS — complete by construction, and
the right call in genuinely regulated environments. The costs: setup weight,
firehose output where agent calls are indistinguishable from your own, and
still no decision context. This is a forensics layer, not a daily answer to
“what did it just do”.
4. A hook-path audit log
Every major harness fires a hook before and after each tool call. Log at that choke point and you get the thing the other three can’t produce: one line per call, across every agent you run, with the decision attached. This is the layer we build — one command, on-device, no account:
curl -sf https://agenticcontrolplane.com/install.sh | bash -s -- --local
tail -f ~/.acp/audit.jsonl
Each line: timestamp, client, tool, classified action (Bash.git.push,
Bash.curl.api.stripe.com), decision, the rule that decided, the reason.
The same file whether the call came from Claude Code or Codex. Because the
log and the policy share a path, “audit” and “control” stop being separate
projects — the line that records the force-push is written by the same hook
that blocked it.
Stated limits: it sees what the hooks see — a harness without hook coverage, or a command you run yourself in a bare terminal, isn’t in the file. And locally it’s an honest record, not a tamper-proof one (see the FAQ). Teams that need the agent-can’t-touch-it version keep the same log in a workspace instead — same hook, the copy lands off-box, priced per call.
What we’d actually do
Solo, trusted repos: hook-path log locally, transcripts as backup detail. Anything with untrusted input or more than one person: hook-path log shipping off-box, OS-level capture if an auditor will ever ask. Either way, the test stays the same: can you answer “what did the agent run today” from one file, in one grep, with reasons — or are you re-reading transcripts and hoping? The 60-second recording ends on exactly that grep.
Doesn't Claude Code already log everything?
It keeps session transcripts, and they’re genuinely useful. But they’re per-tool, in a tool-specific format, and they record the conversation — not a decision record. There’s no ‘this call was allowed because rule X’ line, and reconstructing just the commands from a long transcript is an archaeology project.
Why not just read shell history?
Agent subprocesses generally don’t write your shell history — commands run through the harness’s exec tool, not your interactive shell. History files miss most of what an agent does, and they capture nothing about file edits or web fetches.
What does a hook-path audit line contain?
Timestamp, client (which agent), the tool, the classified action (e.g. Bash.git.push), the decision (allow/ask/deny), which rule decided it, and the reason. One JSON line per call, append-only, greppable.
Is a local audit log tamper-proof?
No — a local file is writable by anything running as you, including the agent. For one developer that’s fine; the question it answers is ‘what happened’, not ‘prove it in court’. If you need an audit trail the agent can’t touch, that’s what a workspace copy on a separate system is for.