How to Build an AI Agent Your Auditor Will Actually Approve
Hash-chained audit logs, framework_ref schemas, pure-function policy gates — and the three things a real auditor checks first.
Most AI-agent posts open with the LLM. This one opens with the auditor — because by the time an agent touches a live trade, the auditor’s questions are the ones that matter.
What follows is a walkthrough of what an auditor actually checks when an AI agent is in scope of a fixed-income desk review, and how to build the system so the answers are pre-baked.
Why the auditor sets the bar
A buy-side desk under SR 11-7, MAS, the FCA, or the SEC is already audited for model risk. Adding an agent does not relax that bar — it raises it, because the agent is a new actor making decisions in a chain that was previously fully human.
The auditor’s working assumption is that the operator is hostile, the network is hostile, and the future regulator has more time than the desk does. Every control claim has to survive that assumption.
A system designed for governance has to assume the operator is hostile, the network is hostile, and the future auditor has more time than you do.
The three things they check first
1. Can you produce the verbatim rule text on every blocked action?
Not “what model decided” — the policy clause. The first thing an experienced AI-systems auditor does is pick a blocked event from the log and ask to see the rule that caused it, in the language the policy team drafted.
If the answer is “the LLM judged it risky,” the conversation pauses. Auditors are not philosophically opposed to LLMs; they are opposed to decision logic that cannot be inspected. The control claim is “this action was blocked because rule X says so,” and rule X has to be a concrete string the policy committee approved.
The fix is simple in shape and hard in practice: route every governed action through a pure function that returns, alongside the verdict, the rule_id, the verbatim rule text, and the tier. We documented the function signature in our policy gate deep-dive; the relevant piece is that the verbatim text is part of the response payload, not generated by the model.
2. Show me an audit row. Now show me you cannot edit it.
Postgres triggers? App-layer immutability? Or “trust us”? This is the question that separates audit logs from audit theater.
We learned this one the hard way. Our first iteration enforced immutability in application code. The auditor asked, “What if someone has psql access?” We said, “They wouldn’t.” She said, “That’s not an answer.”
The control moved into Postgres:
CREATE TRIGGER audit_immutable
BEFORE UPDATE OR DELETE ON audit_log
FOR EACH ROW EXECUTE FUNCTION reject_mutation();
The application layer doesn’t get a vote. Even a database admin with full credentials has to drop the trigger to mutate a row — and dropping the trigger itself writes to a meta-audit log that is monitored.
3. Tamper-evidence: hash chain across rows, or a single row signature?
A signature on each row stops modification of an existing row. It does not stop insertion of a new row between two existing ones — which, on a financial audit, is the same crime in a different shape.
The answer is a sha256 chain over (previous_hash || serialized_payload || sequence_number). Any insert between two rows breaks the chain at the join point, and verification is a single SQL query.
The framework_ref column
The hash chain proves the log is intact. It does not prove that every row is mapped to a control.
This is where most “we did the FINOS AIGF mapping” claims fall apart. The mapping lives in a spreadsheet someone updated once; production rows go in with no framework reference; the coverage report is generated from the spreadsheet, not from the rows. A mis-mapped tool call is invisible.
The fix is a framework_ref column on the audit table, NOT NULL, with a CHECK constraint validating against the live taxonomy:
ALTER TABLE audit_log
ADD COLUMN framework_ref text[] NOT NULL
CHECK (cardinality(framework_ref) > 0
AND framework_ref <@ (SELECT array_agg(ref) FROM aigf_risks));
Now a mis-mapped row fails the write. The coverage report is derived from the rows themselves, not a separate spreadsheet. The auditor reads the same data your CI does.
Tier routing: route by reversibility, not by category
Auditors care about what can go wrong, not what bucket the action sits in.
Reversibility is the right axis:
- T0 — read-only. Agent executes; the action is auditable but the consequence is observation, not state change.
- T1 — reversible writes. Agent executes; rolling back is straightforward.
- T2 — single-HITL above materiality. Human approval before commit.
- T3 — dual-HITL irreversible. Two distinct approvers, both distinct from the caller, with manager role on at least one.
A flat “everything needs approval” model fatigues the trader and gets bypassed. A flat “everything is fine” model fatigues the auditor and gets fined. Tier routing acknowledges that approval is a finite resource the system should spend on the things that genuinely cannot be undone.
For a worked example with a $5M corporate-bond trade, see Materiality-Driven Access Control.
HMAC envelopes — the unforgeable approval
When the gate routes to T2 or T3, the agent has to request an approval envelope. Most implementations make this a boolean the agent flips itself. That is not an approval. That is the agent marking its own homework.
A real envelope is generated server-side, HMAC-signed with a key the agent never sees, and scoped to this exact ISIN, notional, and side:
scope_hash = sha256(action || isin || notional || side)
hmac = HMAC_SHA256(server_secret, canonical_payload)
Re-using the envelope for a different ISIN fails verification. The envelope expires 90 seconds after issuance. SoD is enforced at the API: the approver identity must differ from the agent caller; T3 requires two distinct approvers, both distinct from the caller, with manager role on at least one.
A self-approval attempt produces a 403 — and writes an audit row that itself counts toward coverage on AIR-OP-18.
The CI gate
Governance that lives in a quarterly review is not governance. It is a snapshot.
The strongest signal we ship is the AIGF coverage gate in CI: every PR runs the four-dimension eval harness and the coverage report. If coverage drops below 85% or any AIGF v2.0 risk has zero passing cases, the build fails. The control is enforced on the engineering pull-request graph — the place where regressions actually happen — not on a quarterly evidence pack.
bondfoundry-finos coverage --threshold 0.85
bondfoundry-finos evidence-pack --period 30d
bondfoundry-finos verify-chain
The evidence pack runs against the live audit table in under ten minutes for a 30-day window. The auditor reads the same output the CI reads.
The auditor’s exit interview
When the auditor wraps, they want one thing: a summary they can sign that maps each control claim to a verifiable artifact. The artifacts our packs ship are:
- Coverage matrix — 23 of 23 AIGF v2.0 risks with passing eval count per dimension
- Chain verification report — every row in the window, sha256-walked, with mismatches called out
- Materiality ledger summary — session-aggregate notional vs threshold, per portfolio
- Framework-ref distribution — every framework reference written, with row counts
- HITL trace — every T2/T3 action with envelope_id, approver(s), and dispatch path
There is no negotiation about whether the controls work. There is the data.
Where to start
If you are building this from scratch:
- Make the policy gate a pure function before anything else. Same inputs → same decision, every time.
- Move audit immutability into the database. Application enforcement does not survive an incident.
- Add a chain. The chain catches inserts; a row signature does not.
- Make framework_ref NOT NULL, CHECK-constrained, and write it from the gate output.
- Build the coverage gate before the policy team needs it for a quarterly review.
If you are evaluating a vendor, the 9-question compliance checklist is the form to send before the second meeting.
The auditor is not the enemy. The auditor is the user.
BondFoundry is the working reference implementation of FINOS AIGF v2.0. The full source is MIT-licensed on GitHub. Book a 20-minute walkthrough of the policy gate, HITL queue, and audit chain.