Guardrails, connection scopes, spend ceilings, and approval thresholds are security controls. They determine what agents can do and what content can pass. In most deployments they are edited through an admin UI, take effect immediately, and leave a row in a table with a timestamp.

Compare that to how the same organization handles a change to an authorization check in application code: branch, review, CI, staged deploy, revertible, with a full history. The control that matters more has the weaker process.

Policy as code closes that gap — with an important qualification about what "as code" should mean.

Policy Is Data, Not Application Code

The instinct is to put policy in the application repository and deploy it with releases. That gets you review and history, and it introduces a problem: enforcement changes now require a deployment.

That is wrong for two reasons. During an incident you need to tighten a scope or disable a connection in seconds, not through a release pipeline. And policy legitimately changes on a different cadence than code — new tenants, new rules, threshold adjustments — so coupling them means either slow policy or reckless deploys.

The workable model treats policy as versioned data with code-like discipline:

  • Stored in the platform, not in the application binary.
  • Every change produces a new immutable version with a stable identifier.
  • Changes are reviewable, with a diff, before taking effect.
  • The active version is a pointer, so activation and rollback are atomic.
  • Full history is retained, including versions that were never activated.
  • Applying a version takes effect in seconds, without a deployment.

Whether the authoring interface is a UI, an API, or a file synced from a repository is a workflow preference. What matters is that every version is immutable, identified, and attributable.

Every Record Names Its Version

This is the requirement that makes everything else useful, and it is the one most often missing.

When an enforcement decision is recorded, it must include the identifier of the policy version that produced it. Without that, an investigation reads the current policy and reasons about a decision made under a different one — and reaches wrong conclusions confidently.

The record should carry: the policy version, the specific rule that matched, the disposition, and the evaluated inputs. Then the question "was this agent restricted at the time?" has a definitive answer from the record rather than an inference from configuration history.

This also makes compliance evidence tractable. Demonstrating that a control was in force over a period means showing the version active during that period and the records referencing it. See the AI agent audit trail guide and post-incident forensics for AI agents.

Asymmetric Review

Uniform review friction is wrong, because the risk of a policy change depends entirely on its direction.

Tightening — narrowing a scope, lowering a ceiling, adding a guardrail rule, disabling a connection — reduces what agents can do. Worst case it breaks a workflow, which is visible and quickly reversible. This should be fast: one person, applied immediately, recorded.

Loosening — widening a scope, raising a ceiling, disabling a guardrail, adding a connection — increases what agents can do. Worst case is a security incident. This should require a second approver.

The asymmetry matters operationally. If loosening and tightening carry the same friction, people batch changes and push them together, which means a tightening you wanted urgently waits behind a loosening under review. And an emergency tightening path must exist that no approval can block, because during an incident the containment action cannot wait for a reviewer. See maker-checker for platform administration.

Emergency loosening is the awkward case — sometimes a legitimate workflow is blocked and someone needs it working now. Handle it as break-glass: allowed with a single approver or self-approval, time-limited with automatic reversion, and flagged for retrospective review. See break-glass and emergency access.

Test Against Recorded Traffic

A policy change's real effect is invisible from its diff. A regex tightened to catch one pattern may match a thousand legitimate requests. A scope narrowing may break a workflow nobody documented.

The mechanism that resolves this is evaluating a candidate version against recorded traffic before activating it:

  1. Take a window of recorded requests — a day, a week.
  2. Evaluate the candidate policy against them without enforcing.
  3. Report what would have changed: newly blocked, newly allowed, newly redacted.
  4. Review the deltas. Sample the newly-blocked and confirm they are what you intended.

This turns policy review from reading a diff into examining consequences. For guardrail rule changes specifically it is close to essential, because false-positive rates on content rules are impossible to estimate by inspection.

It requires that you retain enough request content to replay evaluation against, which intersects retention and privacy decisions. A short window with redacted content is usually enough, and it is worth designing for. See staged guardrail rollout and shadow mode for the production-side version of the same idea.

Rollback

Rollback must be atomic and fast: change the active-version pointer, and every enforcement point picks up the previous version within seconds. Not a re-edit that reconstructs the old configuration by hand, which is slow and error-prone under pressure.

Requirements worth being explicit about:

Rollback is itself a recorded event, with who, when, and from which version to which. A rollback that leaves no trace is indistinguishable from a policy change.

Rollback must not silently discard a subsequent change. If someone else edited the policy after the version you are rolling back from, reverting the pointer discards their change too. Warn on it.

Cached evaluations must invalidate. Enforcement points that cache the compiled policy need a propagation mechanism with a bounded staleness window, and you should know what that window is.

Rollback across coupled configuration. A prompt version and a policy version may need to move together. Where changes span both, they should be grouped so rollback restores a consistent state. See versioning and rollback for AI agents.

What Belongs Under Version Control

The scope is broader than guardrail rules:

  • Guardrail rule sets — patterns, classifiers, thresholds, dispositions.
  • Connection scopes — which task types, models and tools a connection may carry, and when it may carry them.
  • Spend ceilings and rate limits per agent, connection, team, and organization.
  • Approval thresholds — which actions require a human and at what values.
  • Trust-level requirements on connections.
  • System prompts and tool definitions, since these determine behavior and are security-relevant. Tool descriptions in particular enter the model's context.
  • Model routing rules, which determine which provider and jurisdiction handles data.
  • Retention and redaction settings — with the additional constraint that reducing audit retention should sit in a higher privilege tier than other policy changes.

Anything that changes what an agent can do or what evidence you retain belongs in the versioned set.

Common questions

Should policy live in Git or in the platform?

Either can work, and the properties matter more than the location. Git gives you familiar review tooling and a natural home for policy that is genuinely static; a platform store gives you fast activation, per-tenant policy, and no deployment coupling. The common hybrid is authoring in Git for the stable baseline, synced into the platform, with per-tenant and emergency changes made directly in the platform — with both paths producing immutable identified versions.

How do I handle per-tenant policy variation?

As layered versions: a baseline policy plus tenant overrides, each independently versioned, with the effective policy resolved and recorded per decision. Recording the effective resolution rather than just the layers is what keeps this debuggable — otherwise reconstructing what applied to a specific request means re-running the resolution logic against historical layer versions. See per-org feature overrides and canary.

Does this apply to guardrail model updates too?

Yes, and it is easy to miss. If a guardrail uses a classifier model, that model's version is part of the policy. A classifier update changes enforcement behavior without any policy edit, and if the version is not recorded on decisions, a behavior change becomes unexplainable. Pin the classifier version and treat updating it as a policy change requiring the same review and shadow evaluation.

Who should be able to change policy?

Separate the roles: building agents, granting their authority, and configuring guardrails and audit settings should not all be the same permission. The people who build agents will always want more scope; the approval should sit elsewhere. And the ability to weaken a control or reduce retention deserves the narrowest grant of all. See RBAC and custom roles for AI operations and insider threat in the age of AI agents.

How Praesidia approaches policy versioning

In Praesidia, guardrails, agent policies and an agent's tool rules are platform configuration rather than deployed code, so a change takes effect without a release. Updating one writes a version row in the same transaction, with the full configuration before and after and the user who made the change, and the API offers that history for reading only. A decision made by an agent policy or a tool rule records which one decided and its version, which the snapshots in that history carry, so "what was in force at the time" can be looked up rather than inferred; a guardrail's log row names the guardrail but not its version, so for a guardrail it is reconstructed from the version history by time. Connection policies, such as rate limits and spend caps, have no version history.

Praesidia does not require a second approver to change a guardrail, an agent policy or an agent's tool rules. What it separates is permission: creating, changing and deleting a guardrail each take their own permission, apart from the permissions to create or change an agent, so the people who build agents need not be able to change the guardrails on them. Guardrail creation, changes and deletion, and rollout rollbacks, are written to the audit trail with the actor: a change records which fields changed, and a rollback records the previous stage and the reason. See guardrails versus policies and the Praesidia documentation.