Model or dataset
RickyTong1/audit-harness avatar
RickyTong1/audit-harness

audit-harness: enforcing audit records on Claude Code agents through hooks and format constraints

Three-layer audit enforcement framework for AI agents — hooks, skills, context recovery, and audit-driven daily reports

471 stars3 forksShellMIT

At a glance

What is it?
A shell-based three-layer framework that turns audit logging from a request in CLAUDE.md into a format constraint. It suits engineers already running Claude Code on data or pipeline projects; it is not a general-purpose observability tool.
Who is it for?
Adopt it if you run Claude Code against a project with real data or code changes and you have been relying on a CLAUDE.md sentence to get audit records. Do not adopt it if you want vendor-neutral tracing or if your agent does not run through Claude Code hooks, because the whole enforcement layer lives in PostToolUse, Stop and UserPromptSubmit.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 98 days ago.
What is it written in?
Mainly Shell, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

The failure mode audit-harness was written against

The README opens with three complaints, and the first one is the whole project in miniature: an agent does not follow its audit rules. The proposed fix in most setups is a line in CLAUDE.md asking the agent to remember to write an audit entry. The repository's claim is blunt: the agent will forget. Everything downstream follows from treating that as a structural problem rather than a discipline problem.

The second complaint is context loss. An LLM context window gets compressed or truncated, and whatever the agent had learned about the task disappears with it. The framework's answer is to treat the audit trail as external storage, a disk the agent can read back from. The third complaint is that nobody consumes audit data, so the project ships a daily report skill and a morning self-correction loop to give the records a reader.

The intended user is narrow. This is for someone running Claude Code against a project where data or code changes happen and where a lost correction means the same mistake gets made twice. It is not a logging library for your application, and it does not instrument the agent's model calls. It records what the agent did through the tool calls it made.

Three layers, and why the middle one is the weak one

The framework stacks three enforcement mechanisms, and the README assigns each an explicit reliability figure. Layer one is skill hardcoding: AuditContext has record, finalize and save built in, and the /start and /end commands wrap a session. The README calls this 100 percent reliable for structured tasks, which is fair, because the code path is the code path.

Layer three is hooks. post_tool_audit.sh fires on PostToolUse and writes Write, Edit and Bash activity into audit_buffer.jsonl. stop_flush.sh fires on Stop and archives the buffer plus pending entries into the session audit file. prompt_inject_session.sh fires on UserPromptSubmit and injects the current session_id. This layer is also described as roughly 100 percent reliable, and it has the advantage of being invisible to the agent.

Layer two is the interesting one. CLAUDE.md is supposed to require an [AUDIT] block in replies, and the README's own estimate is about 90 percent reliable. That is an admission that format pressure beats behavioural advice without being a guarantee. The design bet is that a required output shape is harder to skip than an instruction to be diligent. Ten percent of the time, on the project's own numbers, it is skipped anyway. The hooks are what cover the gap, which means the middle layer is really a way to capture intent and reasoning that the hooks cannot see, not a primary control.

What lands on disk under .claude/runs/

All state lives in <project>/.claude/runs/. The layout is worth reading before you install, because it tells you what the framework can and cannot reconstruct. index.json is the unified index at schema_version 1.1. audit_buffer.jsonl holds PostToolUse writes and is cleared every turn by the Stop hook. audit_pending.jsonl holds [AUDIT] blocks the agent wrote itself. .current_session is described as a handshake protocol between /start and the hooks, which is how the injection hook knows which session it is in.

Each session then gets its own directory containing session.json, audit_trail.jsonl, manifest.json, anomalies.json and checksums.json. The manifest is the structured batch produced by AuditContext.save(). The checksums file is where the environment snapshot lands: the config lists CORE_SCRIPTS and CORE_ASSETS, and the README says SHA256 hashes are computed for those, so you can tell whether the inputs changed between runs.

Three record types coexist in the same task. Data records are row-level, one per business record, and the README puts them at 10,000 or more per day. Change records cover code, config and prompt edits at zero to five per day. Conversation records are per-interaction at 10 to 50 per day. That spread is the design constraint: a schema that has to absorb ten thousand rows a day without making the five change records hard to find.

Installing it, and the config file you have to edit

The README gives a two-step install. Clone the repository, then run the installer from the target project:

git clone <repo> ~/src/audit-harness cd /path/to/your/project bash ~/src/audit-harness/install.sh

The bare invocation does both jobs: a global install into ~/.claude/ on first run, covering core code, skills, hooks and a global CLAUDE.md, plus project initialisation that creates .claude/audit_config.py, .claude/runs/ and injects the audit section into the project CLAUDE.md. Running it again in another project detects the global install and only initialises the project. There are also flags: --global, --init [project_dir] and --auto [project_dir].

The generated .claude/audit_config.py is where the framework becomes specific to your project. CORE_SCRIPTS takes a list of scripts whose SHA256 goes into the environment snapshot. CORE_ASSETS does the same for assets such as a model file. PROMPT_TEMPLATE_GLOB is a glob such as prompts/*.txt. ALERT_RULES is left as a list to fill in, pointing at templates/audit_config.example.py.

One detail matters more than it looks. audit_context.py searches <project>/.claude/audit_config.py first, then <project>/audit_config.py, and falls back to module defaults if neither exists. The README states those defaults are all empty. So a project that installs and never edits the config gets a working audit trail with an empty environment snapshot and no alert rules. Nothing fails loudly; you just lose the checksum comparison and the anomaly detection.

Context recovery ranks user corrections above everything

The recovery path is the part of the design with the clearest opinion. A new session loads index.json and the most recent daily report at /start. Mid-session, after compaction, the agent is expected to call /recover itself. The priority order is explicit: user corrections come first, then task state, then analysis conclusions, then environment configuration.

The reasoning given is that a lost user correction causes the same error to be repeated, which is a stronger claim than losing a conclusion. It also means the framework's value depends on corrections actually being recorded as corrections, which is a classification the agent makes. If a correction gets filed as an ordinary conversation record, it loses its priority. The README does not describe a validation step for that classification, and that is the thinnest part of the documented design.

The environment-configuration tier is the one tied to checksums.json. Knowing which rule version or model version produced a conclusion is what makes a later re-evaluation meaningful, which connects to the first stated design principle: a correct sample today is not necessarily correct tomorrow, so even correct samples are kept for retrospective review.

Where the framework breaks down

The hooks are the enforcement, and the hooks are Claude Code specific. PostToolUse, Stop and UserPromptSubmit are Claude Code events. If you drive your agent through a different runtime, or through the API directly, none of layer three fires and you are left with the roughly 90 percent format layer and whatever the skill code does when invoked. The README does not present a portability story, and the repository is Shell with a single Python module, so there is no plugin surface to build one against.

The installer edits your CLAUDE.md. That is stated plainly, but it means the install is not purely additive: it modifies a file you probably maintain by hand, and the audit section has to survive your own edits to that file. There is a template at templates/CLAUDE.md.audit-section, so you can inspect the injected content before running the installer, and you should.

The schema is versioned at 1.1 with a single definition shared between lib and hooks, which the design notes call out as a deliberate choice to avoid double-write inconsistency. That is a good decision and also a maintenance obligation: any change to index.json has to move the library and all three hooks together. There are no releases in the repository metadata, so upgrades are a git pull plus a re-run of install.sh, and you are tracking main.

The daily report is generated from audit data and the README describes a morning self-correction loop, but the material does not specify what the report contains or how the loop is triggered beyond the /report-daily skill. Treat that as the least documented surface here.

Compared with running your own trace store

The obvious alternative is to skip the framework and write your own trace records from the agent loop, or to use an existing agent tracing tool that captures model calls and tool calls as spans. The difference in approach is where the record is produced. A tracing tool instruments the runtime and emits events; audit-harness intercepts at the tool boundary with shell hooks and then asks the agent to annotate its own reasoning in an [AUDIT] block.

That split is the real trade-off. The hook layer gives you a record of what was executed without trusting the agent, which a tracing system also does, and arguably does better because it captures more context per event. What a generic tracer will not give you is the recovery priority order, the checksum snapshot of named scripts and assets, or the daily report keyed to a session. Those are the project-specific parts, and they are the reason to pick this over a general tracer.

Conversely, if you want spans across services, sampling, a query language or a hosted UI, audit-harness gives you JSONL files under .claude/runs/ and a Python module. There is no query layer described in the README. You read the files.

Licence and the cost of keeping it current

The repository is MIT licensed, which permits commercial use, modification and redistribution provided the copyright notice and permission notice are retained. That is the whole of what the material supports; it is not legal advice, and if you redistribute a modified installer you should read the licence text yourself rather than rely on this summary.

The practical maintenance cost is lower than a service dependency and higher than a library. There is no package to pin and no release to follow, so an upgrade is a git pull of the repository plus re-running install.sh. Because the installer writes into ~/.claude/ and into each project's CLAUDE.md, an upgrade touches both the shared global install and every project you have initialised. The schema version in index.json is the compatibility marker to watch: if a future revision bumps it past 1.1, existing runs directories are the thing that might need attention, and the README does not describe a migration path.

The recurring human cost is the config file. CORE_SCRIPTS, CORE_ASSETS and PROMPT_TEMPLATE_GLOB are only useful if they list the files that actually determine your outputs. A stale list produces checksums that match when nothing meaningful is unchanged and miss when something is. That is a file you re-read whenever your pipeline's entry points move.

Editorial conclusion

Adopt it if you run Claude Code against a project with real data or code changes and you have been relying on a CLAUDE.md sentence to get audit records. Do not adopt it if you want vendor-neutral tracing or if your agent does not run through Claude Code hooks, because the whole enforcement layer lives in PostToolUse, Stop and UserPromptSubmit. Before installing, verify three things: that python3 is on PATH for the hooks, that your project directory is writable because install.sh creates .claude/runs/ and injects into CLAUDE.md, and that you have edited CORE_SCRIPTS, CORE_ASSETS and PROMPT_TEMPLATE_GLOB in .claude/audit_config.py, since the generated file leaves the environment snapshot empty by default.

Official sources

  1. Issues
  2. License: MIT
  3. README
  4. RickyTong1/audit-harness on GitHub
Community notes

Community notes