The engines are mature

Many platform teams already run policy as code in their delivery path, with tools such as Open Policy Agent (OPA), Kyverno and HashiCorp Sentinel. A policy engine takes structured data about a proposed change (a Terraform plan, a Kubernetes manifest) and returns allow or deny; the system that asked, such as an admission controller or a pipeline, then acts on the answer. Policy as Code covers those mechanics. This article asks a narrower question: what happens to a policy after the decision behind it changes?

First, what the tools already do.

  • OPA evaluates Terraform plans. The OPA Terraform guide converts a plan to JSON with terraform show -json and hands it to OPA as input, so policy can inspect the resource changes Terraform is about to make before it makes them. The same guide is candid about limits: some values are unknown at plan time.
  • Kyverno has scoped exceptions and policy reports. A PolicyException lets named resources past a specific policy and rule. Exceptions are off by default, and administrators choose which namespaces may hold them. Policy reports record pass, fail, warn, error and skip results per resource, in the open report format of the Kubernetes Policy working group. A resource covered by an exception reports as skip, meaning Kyverno did not evaluate the rule because an applicable PolicyException exists.
  • Sentinel separates enforcement level from policy logic. Sentinel's enforcement levels are advisory, soft-mandatory and hard-mandatory, and the same policy can run at different levels in different places. A soft-mandatory failure can be overridden, and Sentinel's documentation notes that the override provides non-repudiation: someone explicitly overrode a failed policy. In HashiCorp Cloud Platform (HCP) Terraform, overriding a failed policy requires the Manage Policy Overrides permission.

Scoping, exceptions, graded enforcement, permissioned overrides and result reporting all exist. The engines evaluate well. The trouble sits upstream, in the decisions they encode.

A rule that outlived its decision

Take one example, shared with a companion article on Kubernetes scaling. A team decides that the orders service has a PostgreSQL connection budget. The database accepts 100 connections with 3 reserved for superusers, and each pod opens a pool of 12. Eight pods fit (96 connections), but a rolling update can briefly add 25% more pods, so the Horizontal Pod Autoscaler (HPA) for the service must not scale past 6 replicas. The platform team encodes that as a Kyverno policy: reject any HPA in the orders namespace whose maximum replica count exceeds 6. It works. The full arithmetic is in When Kubernetes Works but the Application Still Fails.

Six months later the team puts a connection pooler in front of the database. The architecture review accepts a new decision: the budget is now enforced at the pooler, and application pods may scale on load alone. The old decision is superseded. Nobody changes the Kyverno policy, because nobody connects it to the review.

Now the policy is wrong in a way no test will catch. It still evaluates correctly. It still blocks an HPA set to 10 replicas, which is now an approved configuration. An engineer hits the block during an incident, files a PolicyException for the orders namespace, and moves on. The policy report shows a skip, which is accurate: a deliberate bypass of a rule that still exists.

Run the same story the other way and the failure is quieter. Suppose this time someone did act on the pooler decision and deleted the Kyverno policy. A year later the pooler is removed, the team reverses the decision, and the original budget applies again. Nobody restores the policy, because nothing ties the reversal to it. A 20-replica HPA now passes, and every engine reports green, because nothing in its policy set says otherwise.

Sentinel produces the same pattern on the infrastructure side. A soft-mandatory policy caps the database instance class for one environment. It fails on a legitimate upgrade, a user with override permission overrides it, and the run continues. The override is attributable to the user who made it. What the override does not carry is a link to the decision behind the cap. Without one, nobody can tell from the run history whether this was a one-off exception, as the policy's author intended, or the fifth override since that decision was replaced.

In each case the engine did exactly what it was told. The fault is that what it was told no longer matched what the organization had decided, and the engine had no way to know.

Evaluating a rule is not governing its source

A policy engine answers one question very well: does this input satisfy this rule? It does not, and should not, answer a different set of questions about the rule itself:

QuestionWhere the answer lives today
Which decision does this rule encode?A comment, a commit message, or the memory of whoever wrote it
Who owns that decision now?Often unrecorded; the policy repository's code owners own the file, not the decision
Is that decision still in force?An architecture decision record (ADR) or wiki page with no link to the policy
Did a newer decision replace or narrow it?Review notes, if anyone kept them
Is this exception temporary, permanent, or a sign the rule is obsolete?The exception's author, until they move teams

The engines provide hooks for some of this. OPA's metadata annotations include authors, related resources (a list of URLs to related documentation) and a custom map for arbitrary data. A team can put a decision link in an annotation on a Rego module (Rego is OPA's policy language) today. But a link is a pointer, not a lifecycle. When the decision it points to is superseded, the annotation does not change, and the rule keeps evaluating.

Policy as Code made rules versioned, testable and reviewable. It did not make the decisions behind them versioned, owned and current, because that was never the engine's job. Engineering decision governance is the name for that upstream job.

What a governed decision needs to carry

For a policy to stay honest, the decision it encodes needs five properties that live outside the rule file.

  • Ownership. A named person or team who can say whether the decision still holds. The owner of the policy file is often the platform team; the owner of the decision may be a database or service team.
  • Applicability. Where the decision binds. The Kyverno policy's namespace match is one expression of scope; the decision may also bind pool settings in application config and a deployment review checklist. Scope belongs to the decision first and to each rule second.
  • Precedence. What happens when two decisions overlap, or a narrower one carves out part of a broader one. If this is not explicit, policy authors resolve it by ordering rules, and the resolution is invisible. Precedence semantics covers why that resolution should be deterministic and reviewable.
  • Lifecycle. Whether a decision is proposed, active, superseded or deprecated. A superseded decision should stop being enforced, except where its replacement deliberately carries a rule forward. Every rule that encoded it should be findable at the moment it is superseded, so its owners can keep, rewrite or retire it, not when an engineer hits it during an incident.
  • Exceptions with a reason. Kyverno exceptions and Sentinel overrides already record that a bypass happened. The decision record is where the reason belongs, so that a growing pile of exceptions against one decision reads as a signal to revisit it.

Giving existing engines decision context

None of this argues for a new policy engine. OPA, Kyverno and Sentinel are the right enforcement points for admission, plans and runs. They would be more trustworthy if each policy carried a stable reference to the decision it encodes, and if that decision's state could flow back to the policy.

One version of that integration:

  1. Each policy, Rego module or Sentinel policy names a decision identifier and version in its metadata.
  2. When the decision is superseded or deprecated, a check lists every policy that references it, across engines, so the owners can retire or rewrite them in the same change.
  3. Exceptions and overrides cite the decision they bypass, so their count per decision becomes visible.
  4. A verdict from any engine can be traced to the decision, the decision version, the rule, the artifact and the result.

Step 1 is possible today with annotations and naming conventions. Steps 2 to 4 need a decision record with versions and lifecycle, plus integration work on each engine. This is a proposal, not a shipped feature.

Traceability matters most at the moment of change. When the connection budget decision is superseded, the question that should take one query is "what enforced the old decision?" The answer spans a Kyverno policy, a pool setting in application config, a monitoring alert on database connection count and perhaps a Sentinel policy on the database tier. Without decision references, finding that set is archaeology. Programmable AI Policy Is Not Enough for Coding Agents makes the same point for coding agents: without a record of what was enforced under the old decision, an override is indistinguishable from a violation.

Why agents make this urgent

Agents now write the Kubernetes manifests, Terraform modules and application config these engines evaluate. They also write policy. An agent asked to fix a blocked deployment can write a well-formed PolicyException in seconds, and it will be correct by every check the engine runs. Whether that exception reflects the team's current decision is a question the agent cannot answer from the policy repository alone, because the repository does not contain the decision.

The rate of change is the problem. A team that changes HPA limits twice a year can keep a policy aligned by hand. A team where agents can propose replica, pool and exception changes in any pull request will struggle to, and stale rules pile up as exceptions rather than as visible failures.

Where Mneme fits

Mneme is open-source software that records a team's engineering decisions and enforces the checkable parts of them while coding agents and developers change code (its own term is a decision and control layer for agentic software development). It works on the decision side of this problem, inside the repository. It does not enforce infrastructure policy, provision cloud resources or manage identity and access; that remains the job of OPA, Kyverno, Sentinel and the platforms that run them.

What ships in mneme-hq 0.10.0 (Mneme's open-source package; 0.10.0 is the current release) is narrower and specific. The Decision Index is Mneme's persisted, versioned record of decisions. Each decision has immutable versions and, for decisions imported from architecture decision records (ADRs), the source file and its content hash. Each also has a lifecycle status: active, superseded, deprecated, or inactive, which Mneme uses for an accepted decision that lost to another one in the same scope on priority or date.

Supersession comes from the ADRs themselves: a new ADR lists the one it replaces in a supersedes field, you mark the old ADR status: superseded, and Mneme records both when it imports them. Only active decisions are enforced.

A typed rule is the machine-checkable part of a decision, such as an exact term that must not appear in code, optionally limited to certain paths. When a decision is superseded, the typed rules bound to it stop being enforced in the repository while staying in the index with their history. When a typed rule is violated, the verdict names the decision behind it. The Decision Index records no approver identity; who approved a change belongs where identities are verified, such as pull request review on the ADR files.

Supplying that decision context to external policy engines, and linking verdicts across application and infrastructure layers, is conceptual. Mneme has not shipped it.

The useful question for an engineering leader is not which policy engine to choose. It is whether, for any rule your engines enforce today, you can name the decision it encodes, who owns that decision, and whether it is still in force.

Frequently asked questions

Can OPA, Kyverno or Sentinel handle exceptions and overrides?+
Yes. Kyverno PolicyExceptions let named resources past a specific policy and rule, and policy reports show them as skip results. Sentinel's soft-mandatory level allows a failed policy to be overridden, and HCP Terraform requires the Manage Policy Overrides permission to do it. OPA policies can carry metadata annotations and are evaluated against structured input such as Terraform plan JSON. What these tools do not decide is whether the decision behind a rule is still current.
What is a stale policy?+
A policy that still evaluates correctly but no longer represents an approved decision, because the decision it encoded was superseded or reversed. It keeps blocking changes the team now allows, or a deleted policy stops blocking changes the team again forbids. Engines cannot detect this, because the rule itself has not changed.
How do you link a policy to the decision behind it?+
Start by putting a decision identifier and version in each policy's metadata, for example in OPA's related resources or custom annotations. Then give each decision an owner, a scope and a lifecycle, and make superseding a decision trigger a review of every policy that references it. Exceptions and overrides should cite the decision they bypass.
Does Mneme replace OPA or Kyverno?+
No. Mneme does not enforce infrastructure policy. In its current release, mneme-hq 0.10.0, it records versioned decisions with lifecycle and source in its Decision Index and enforces the machine-checkable rules attached to those decisions in the repository, so a superseded decision's rules stop being enforced there. Supplying that decision context to external policy engines is a conceptual integration, not a shipped feature.