Skip to content
Agentic Control Plane

How to Audit What Your Coding Agent Actually Runs (2026)

David Crowe David Crowe · · Updated · 5 min read
claude-code codex cursor audit tool-policy
Share X HN LinkedIn

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.

Share X HN LinkedIn
Get the next data drop
What agents actually cost, new tool-surface captures, and the occasional incident post-mortem — sent when we publish something worth your inbox, not on a schedule. Unsubscribe anytime.
Share: Twitter LinkedIn
Related posts

← back to blog