Skip to main content
Back to blog

Let an AI Agent Author Policy-as-Code, Never Override Its Verdict

Riya Mittal
Riya Mittal Engineer · Zop.Dev
8 min read
Let an AI Agent Author Policy-as-Code, Never Override Its Verdict

Let an AI Agent Author Policy-as-Code, Never Override Its Verdict

An AI assistant writing Terraform is now unremarkable. It reads the module, proposes the change, opens the pull request, and does it faster than the engineer who asked. The interesting question moved a layer up: what checks that output, and who controls the check. Terraform is the declarative provisioning tool that turns a written description of infrastructure into API calls, which is exactly why a pull request against it is the last place a human reliably sees the change. Fourteen new tools address that gate.

ZopNight added fourteen MCP tools for infrastructure-as-code governance, taking the catalogue from 275 tools to 289, of which 43% are writes. Eleven of them let an agent create, edit, list and dry-run the Terraform and Terraform and OpenTofu pull-request gates, switch per-repository scanning on, manage scan tokens, and read validation runs with their audit trail. Two obvious companions were deliberately left out, and those two are where the design lives.

The Question Is Not Whether an Agent Writes Terraform, It Is Who Owns the Gate

A policy gate is worth something only if the thing it gates cannot lift it. That is the entire property, and it is easy to lose by accident when the same agent holds both the authoring tools and the override.

Architecture diagram

The bypass boundary is the line between authoring a control and lifting its result: an agent may propose, write, dry-run and read a governance policy, and may never be the actor that waives an outcome that policy produced. Everything on the authoring side is reviewable after the fact, because the policy is an artefact somebody can read. An override is a single act that leaves the artefact intact and the outcome reversed.

Two tools sit on the wrong side of that line, and both were withheld. start_iac_validation needs a full Terraform plan and state blob supplied by the caller, which means the agent would be handing in the evidence its own change is judged on. override_iac_validation is a governance bypass outright, and the release notes say what it needs before it ships: actor-attributed, audited elevation. Not a scope. An elevation with a name attached.

Fourteen Tools, and the Two Left Out Define the Boundary

GroupToolsWhy it is safe to hand an agent
Policy authoringCreate, update, list IaC policiesProduces a reviewable artefact; nothing is enforced until enabled
Policy dry runValidate an IaC policyPersists nothing, returns a verdict and errors
Repository wiringEnable scanning, check statusTurns the gate on, which fails toward more checking rather than less
Token managementList and revoke scan tokensRevocation is the safe direction; minting is where care is needed
EvidenceRead validation runs and their audit trailRead-only, and the audit trail is the record the agent cannot alter
Catalogues and lookupIaC policy catalogue, tag policy catalogue, Jira assignable usersReference data an agent needs to write a valid policy or ticket

Read the middle column as a risk ordering and one entry stands out. Enabling scanning on a repository fails toward more governance, not less. An agent that wires up a gate nobody asked for creates a review conversation. An agent that removes one creates an incident a month later.

That asymmetry is the practical rule for deciding which governance tools an agent may hold. Ask what the worst plausible mistake produces. If it produces a stricter estate and an annoyed team, the tool is a reasonable thing to hand over. If it produces a quieter estate and no visible change, it is not.

A Dry Run Is What Makes Agent-Authored Policy Safe

validate_iac_policy takes a policy, checks it, persists nothing, and returns whether it is valid along with any errors. It is the direct analogue of the already-shipped watch-policy validator.

Architecture diagram

Without a dry run, an agent authoring policy has exactly one feedback channel: enable it and watch whose pull request fails. That is a bad loop for a human and a worse one for an agent, which will iterate faster and therefore break more pull requests per hour of good intentions.

There is a schema wrinkle worth knowing if you build MCP surfaces. The validator is declared mutating, even though it changes nothing, because the read guard forbids binding a GET to a POST route and a policy body has to be posted. The declaration is a protocol artefact, not a claim about effect. It is worth flagging in your own tool descriptions when it happens, because an agent reasoning about which tools are safe to call speculatively will read that declaration literally and avoid a tool that was safe all along.

Not Idempotent, and Labelled That Way

ensure_iac_ci reads like the safest possible verb. Ensure. Call it twice, get the same state.

It is not idempotent, and it is now marked that way. In some non-wired states a retry mints a new scan token and revokes the previous one. A caller that retries on a timeout can therefore invalidate a token that was working, and the failure surfaces later as a CI job that cannot authenticate.

Assumption about retriesWhat a retry actually doesWhere it surfaces
Idempotent, safe to retry on timeoutNew token minted, prior token revokedCI authentication failure on the next pull request
Not idempotent, declaredCaller surfaces the timeout instead of retryingImmediately, to whoever is watching

The fix was not to make retries safe. It was to declare the truth and move the tool onto the not-idempotent list, so the retry logic above it stops retrying. That is the right call when making an operation genuinely idempotent would require holding a token in a half-created state, which is its own security problem.

For agent-facing APIs this labelling matters more than for human ones. A human who calls something twice usually remembers doing it. An agent’s retry is a policy in a client library it never reasoned about, applied to a tool whose name promised it was fine.

A Filter That Can Never Match Teaches the Agent a Wrong Model

list_iac_policies was first built by reusing the schema from the alerting and watch policy lists. That inherited a target_type filter offering values of organisation and cloud account.

Every IaC policy is repository-scoped. Neither value can ever match anything.

The repair replaced it with a repo_ref filter mapped to the underlying target parameter. But the bug is worth sitting with, because it is not a filter bug. It is a documentation bug that an agent reads as domain knowledge.

An agent given a schema advertising organisation-scoped IaC policies will believe IaC policies can be organisation-scoped. It will offer that as an option when a human asks how to apply a rule estate-wide. It will call the tool, receive an empty list, and reason about why this organisation has no organisation-scoped policies rather than concluding the category does not exist. Every one of those steps is competent reasoning over a false premise handed to it in the schema.

Schema fields are not hints for an agent. They are the domain model, and an inherited field nobody removed is a claim the system is making about itself.

Redaction Anchored to Arguments Drops the Whole Response Body

One last footgun, and it only ever shows up in the audit log. The gateway’s redaction list is anchored to a tool’s arguments, not to fields in its response. A non-empty redaction list therefore causes the entire response body to be dropped from the audit record.

That is defensible as a default: if something in this call is sensitive enough to redact, do not store the response. It is also surprising enough that comments across the tool definitions were corrected, and an integration test now pins that the audit response body for ensure_iac_ci is empty rather than leaving it to a future reader to rediscover.

The general shape is familiar to anyone who has built an audit trail. Redaction rules are written once, by someone thinking about arguments, and applied forever, by a pipeline handling responses. The only reliable defence is a test that asserts what the audit record actually contains, for the specific tools where it matters, rather than a comment describing what somebody intended.

This works when the policy an agent authors is reviewed before it is enabled. It fails if enabling is automated too, because the dry run then checks only that the policy parses, not that anyone agreed with it. Fourteen tools is a small number against a 289-tool catalogue. The reason this particular fourteen is worth writing about is that they are the first set where the agent is being handed the controls rather than the estate, and the discipline that shows up is the one you would want: author freely, dry-run often, read the audit trail, and never hold the override.

Tagged
Riya Mittal

Riya Mittal

Engineer · Zop.Dev

Riya is an AI engineer at ZopDev, working on production LLM pipelines behind the company's content and account-intelligence platforms. She works on the engineering that makes these systems reliable and repeatable, from multi-provider orchestration and structured output validation to evals, idempotent pipelines, and automated recovery. She writes about what it takes to make AI systems reliable enough to run in production.

Stop watching the waste.
Start cutting it.

See. Find. Fix. Automatic.

Connect your first cloud account in under 5 minutes. See your first remediation in under 7. No credit card required.

CDCR connect detect classify remediate
full audit every action traceable
read-only default access
Multi-cloud automation· Production-ready in 30 min· SOC 2 · ISO 27001· 20–60% off the bill, first month· 4 platforms · 1 console· Multi-cloud automation· Production-ready in 30 min· SOC 2 · ISO 27001· 20–60% off the bill, first month· 4 platforms · 1 console·