A rule that runs after the fact is a report. Auditant evaluates before the action executes — allow it, refuse it, or hold it for a named human — and writes the decision into the same chain as the action it governed.
A policy engine that logs somewhere else can tell you a rule existed. It cannot prove the rule was in force at the moment that specific action ran, in its version from that day, and that nobody rewrote the verdict afterwards.
So the decision is an event: the rule id, its version, the effect, the reasons, and whether it was enforcing or only watching — hashed and chained beside the action itself. Reconstructing that later from two systems is exactly the thing nobody can do when asked.
{
"actionType": "tool_call",
"action": "wire_transfer",
"outcome": "pending_approval",
"policy": {
"decision": "pending_approval",
"policyId": "human-signoff-above-threshold",
"policyVersion": "1",
"engine": "cedar-4.7",
"reasons": ["amount 50000 exceeds threshold 10000"],
"monitorMode": false
}
}Recorded anyway. “Nothing stopped it” is a finding, and a log that only contains refusals cannot show that a control was actually running.
The agent gets a structured refusal naming the policy and the reasons — not a generic error it will retry into.
The approval, when it comes, is its own event carrying approvedBy. Years later, “who signed off” is answerable without an inbox search.
In monitor mode a rule evaluates and records what it would have done, and the action proceeds. You get a week of evidence about how often it would have fired, against real traffic, before it can break anything.
This is not a nicety. A compliance control that halts production on the day it ships gets switched off the day after, and a switched-off control protects nobody.
In an early version each rule hardcoded monitorMode: falseinto the verdict it built, so flipping the flag changed the label and nothing else. A rule marked “monitoring” was still blocking live traffic.
That is precisely the failure the feature exists to prevent, and it was invisible from the outside. The rule definition now owns the flag, and a test flips it and asserts the action goes through.
Deny-by-default, forbid-wins — the same evaluation model AWS chose for Bedrock AgentCore Policy. These are the defaults; the interesting rules are the ones specific to your business.
Forbid-wins over every other rule, including allows. The control you need at 2am, which is the worst possible time to discover it is subtle.
EU AI Act Article 14 human oversight; financial supervision duties. The approval becomes an event carrying who signed off.
Colorado SB 26-189. A declined application with no recorded reason is not defensible, and the gap is invisible until someone asks.
Bounded by the invoking user's own rights, so an agent cannot become a privilege-escalation path for the person who ran it.
The FinOps rule that is also a compliance control. A ceiling reports the damage afterwards; burn rate stops it in the first minute.
A condition tree over a fixed set of fields — no loops, no recursion, no expression evaluator. Partly because a policy engine that runs customer-supplied code inside the process holding the audit chain is a bad trade whatever the sandbox story is, and partly because rules being data is what gives you the next three things.
The person who signs off on a policy is usually not the person who wrote the JSON, and “trust me, that’s what it says” is not a review.
A version identifier is the hash of the rule’s own bytes, so it can only ever refer to one rule. Nobody can edit a rule and leave old decisions pointing at the new text.
A threshold compared against a string is refused at save time. In JavaScript “5000” > 400 is true, and a limit that quietly means something other than it says is the worst bug available here.
{
"id": "refund-cap",
"description": "Refunds above 500 need sign-off.",
"effect": "pending_approval",
"monitorMode": true,
"condition": {
"all": [
{ "field": "action", "op": "eq", "value": "refund" },
{ "field": "amount", "op": "gt", "value": 500 }
]
}
}When the action is "refund" and the amount is
above 500, hold the action for a named human
(monitoring only — nothing is blocked).Rules are read on every decision, not loaded at boot. A compliance officer who arms a rule and is told to wait for a deploy will simply not arm it.
A change writes a new version; nothing is ever overwritten and nothing is ever deleted. A rule that governed a real decision is part of that decision's evidence, so retiring it removes it from evaluation and from nothing else.
Which means a decision recorded nine months ago, naming a policy id and a version, resolves back to the exact rule that made it — in the words it was written in, with who wrote them and when.
And every change is itself an event in the chain. Arming a rule is the change most likely to be made quietly, and a policy table nobody audits is the obvious place to hide one.
seq 22 policy_created armed=false augusta@…
refund-cap: When the action is "refund"
and the amount is above 500, hold the
action for a named human (monitoring only).
seq 23 policy_updated armed=true augusta@…
refund-cap: "…(monitoring only)"
→ "…hold the action for a named human."The agent is the only thing that knows what it is about to do, so it supplies that. Everything the decision is checked against — spend so far, loop count, whether a human actually approved, whether the tenant is halted — is computed by the server from the chain and from stored configuration, and overwrites anything the caller sent.
This is not an adversarial edge case. An agent under a budget it wants to exceed is the ordinary case, and an agent that has been prompt-injected will send whatever it is told to send.
A caller that sends these has them replaced, silently and every time.
The first version of the decision endpoint read the entire context out of the request body. So an agent could send tenantHalted: false and walk through the kill switch, or approvalId: "x" and satisfy the human-signoff rule with no human anywhere near it. The control you reach for at 2am was switchable by the thing you were trying to stop.
We record those rather than pretending to stop them. Claiming to block a cloud API we do not sit in front of would be the kind of thing that survives exactly until the first incident review.
A spend ceiling is a compliance control wearing a different hat. Same engine, same record.