Formal Verification + Vectors
Determinism isn't tested — it's proven.
There are two kinds of determinism: the determinism tests cover, and the determinism mathematics proves. Sampled tests only prove the inputs you happened to write; in regulated industries (finance, insurance, government), the audit question is singular: Does this rule hold for every input?
Why now: LLMs have brute force. They don't have direction.
LLMs are probabilistic — same input, different answers. Hand enterprise decisions to a probability distribution, and auditors will eventually ask: "on what basis was this decision made?" The industry's answer is converging: let the LLM understand, let a deterministic rule engine decide. But a rule engine's "determinism" is usually underwritten by unit tests — and tests only prove the inputs you happened to write.
Sampled tests can't answer "every input". Only a proof can. erdl-formal compiles the ERDL expression kernel into SMT (Z3) and verifies a rule never errors, never fails open, never misses a block.
Comparison
| Approach | Formal semantics | Openness |
|---|---|---|
| Cedar Analysis | Lean + SMT (used inside AWS) | Not public; serves only AWS's own policy language |
| OPA / Rego | None — "the implementation is the spec" | Open source, but semantics defined only by the implementation |
| erdl-formal | Z3 / SMT, full 34-node coverage | Open source, ERDL-specific semantics (money, time, decision objects, bidirectional gloss) |
Vectors: cross-implementation consistency
Vectors prove the inputs you sampled; formal verification proves the rest. erdl-vectors is a cross-implementation benchmark — 318 frozen vectors (78 audit-layer + 240 expression-layer) belonging to no single implementation: any language, any stack, can implement JCS (RFC 8785) + SHA-256 from the spec alone and recompute byte-for-byte.
Who, on what date, passed how many vectors — measured, not claimed.
ERDL determinism self-evidence: three rule types Rego drifts on, ERDL doesn't
One-line conclusion
Rego is a powerful query language, but its semantic model is “compute the set of satisfying cases”, not “make a verdict”. When a policy sentence must express “deny by default”, “universal quantifier”, or “semantic stability”, Rego’s semantics drift — while ERDL’s “verdict model + frozen semantics” turn these three into built-in invariants.
Each of the three types below gives: the policy sentence → Rego’s drifting form → ERDL’s corresponding form → citable, real evidence.
Type 1: Deny by default (fail-closed) — Rego leaves it to the caller, ERDL builds it into the semantics
Policy sentence: “Any operation not explicitly allowed is denied.”
Rego’s drift: in Rego, allow / deny are not keywords at all (OPA’s official FAQ, verbatim); fail-open vs fail-closed is the calling software’s responsibility. OPA’s official Operations doc states it plainly:
“this logic is entirely the responsibility of the software asking OPA for a policy decision.”
Worse, Rego’s “missing path” returns undefined, not an error (Rego Basics guide, verbatim: “When a path is missing, the result is undefined, which is not an error!”). If the caller forgets to handle undefined, it fails open — allowing what should have been denied. To fail closed, you must hand-write the default allow := false boilerplate; miss one line and you have a hole.
ERDL’s counterpart: deny-by-default is part of evaluation semantics, not boilerplate. Any evaluation error → DENY (fail-closed), guaranteed at the engine level, independent of whether the caller “remembers to write default”.
Evidence:
- OPA official Operations doc (fail-open/fail-closed is the caller’s responsibility): https://www.openpolicyagent.org/docs/operations
- OPA official FAQ (
allow/denyare not keywords): https://www.openpolicyagent.org/docs/faq - Rego Basics guide (
undefinedis not an error): https://www.datocms-assets.com/75231/1674577890-rego-guide.pdf
Type 2: Universal quantifier (FOR ALL) — Rego needs “negate + exist + re-negate”, ERDL has native all
Policy sentence: “Employees must not send customer data to any external domain.” (core semantics: the universal quantifier “any”)
Rego’s drift: this policy is a FOR ALL, and Rego’s way of expressing FOR ALL is counter-intuitive — you must “complement + negate + exist + re-negate”. OPA’s own policy-language doc admits this is a “common mistake”:
“A common mistake is to try encoding the policy with a rule named
no_bitcoin_minerslike so:app.name != "bitcoin-miner"# THIS IS NOT CORRECT.”
That is, app.name != "bitcoin-miner" means “there exists one that isn’t”, not “all aren’t”. To correctly express “all recipients are internal domains”, you have to turn it into “there does not exist any external domain”, combined with not’s negation-as-failure semantics — two negations, extremely easy to get backwards.
ERDL’s counterpart: native all / any / none quantifiers, semantics frozen, a literal translation of the policy:
# ERDL: DENY when "there exists any external-domain recipient" (any literally translates "any")
- quantifier:
kind: any
over: $.recipients
predicate:
compare: { op: ne, left: { field: domain }, right: "internal.example.com" }
The policy says “any”, ERDL writes any, word for word; Rego needs “negate → exist → re-negate”, two translations.
Evidence:
- OPA official Policy Language doc (the FOR ALL “common mistake” + negation-as-failure): https://openpolicyagent.org/docs/policy-language
Type 3: Semantic stability — Rego’s own “formatting” can change logic, ERDL’s semantics are frozen
Policy sentence: (any rule) — here the “drift” is not in the writing, but in the toolchain.
Rego’s drift: real CVE-2022-23628 — OPA’s AST pretty-print (formatting) reorders array literals and thereby changes the logic of the policy:
“pretty-printing an abstract syntax tree (AST) that contains synthetic nodes could change the logic of some statements by reordering array literals.”
That is: the same Rego policy, run through opa build -O=1 (optimization) and then formatted, can have its logic changed. This is iron-clad evidence of “semantic instability” — even “formatting”, an operation that should be side-effect-free, can drift the semantics.
ERDL’s counterpart: S-expression canonical serialization (cross-implementation hash identity) + FREEZE-2 freeze (existing node semantics never change) + 318 cross-implementation verification vectors (3 independent implementations, byte-for-byte identical). The semantics are frozen and verified by third-party reproduction — no “formatting changes logic” is possible.
Evidence:
- CVE-2022-23628 (OPA pretty-print changes logic): https://opencve.alliance.unm.edu/cve?product=open_policy_agent&vendor=openpolicyagent
- Corroborating CVE-2025-46569 (OPA allows Rego-code-injection DoS via HTTP requests): https://www.sentinelone.com/vulnerability-database/cve-2025-46569
Conclusion: it’s not “ERDL is better”, it’s “the semantic model is different”
The three types point to the same root:
| Rego | ERDL | |
|---|---|---|
| Semantic model | Query (compute a set) | Verdict (allow/deny) |
| Fail-closed | Caller’s responsibility (boilerplate) | Built into semantics |
| Universal quantifier | Negate + exist + re-negate (easy to invert) | Native all/any/none |
| Semantic stability | Formatting can change logic (CVE) | Frozen + canonical + cross-impl vectors |
When a governance rule enters the “regulatory proof” scenario — where you must prove “this verdict is deterministic, not misread, not tampered with by the toolchain” — Rego’s “query model + caller fallback + driftable semantics” are exactly three risk points; ERDL’s “verdict model + built-in fail-closed + frozen semantics” are the three corresponding eliminations.
That is ERDL’s self-evidence: not that it has “more features” than Rego, but that in the single dimension of “determinism of rule semantics”, it turns what Rego must defend with human discipline and luck into a mathematical invariant of the engine.
ERDL vs ACS: why a team already using AGT still needs ERDL
I. First, credit what ACS already does right
Before “why ERDL on top”, be honest about what ACS is and what it already does well — otherwise this has no credibility.
ACS (Agent Control Specification) is the “stateless, deterministic, deny-by-default (fail-closed) policy decision runtime” of Microsoft’s Agent Governance Toolkit (AGT). Microsoft positions it as “the MCP or A2A of agent safety”: MCP standardized how agents connect to tools, A2A standardized how agents communicate, ACS standardizes how agents are safely controlled.
It defines three iron rules (spec §1.1):
| Iron rule | Meaning |
|---|---|
| Stateless | The runtime keeps no mutable state that affects a verdict; each host passes a full snapshot |
| Deterministic | Same manifest + snapshot + mode + dispatcher → same verdict |
| Deny by default | Any evaluation error → deny, never allow |
It slices the agent lifecycle into 8 intervention points (agent_startup / input / pre_model_call / post_model_call / pre_tool_call / post_tool_call / output / agent_shutdown), each producing a normalized verdict (allow / warn / deny / escalate / transform).
What ACS gets right, and we must admit it:
- Orchestration contract — it standardizes the “dirty work”: where in the agent lifecycle to check, how to pass inputs, how to enforce the verdict, with multi-language SDKs (Python / Node / .NET / Rust / Go).
- Breadth of engineering practice — 45+ tutorials covering sandboxing, prompt-injection detection, MCP gateways, SPIFFE identity, cost governance, chaos testing — an engineering system that’s already “usable out of the box”.
- Ecosystem positioning — the “pluggable policy engine” (Rego / Cedar / custom) gives it a seat in any framework, on any policy language.
If a team wants “wrap an existing agent in a standard, portable policy check”, ACS is enough. We are not competing for that.
II. ACS’s three “holes” (the ones it deliberately gave up)
But ACS has one structural feature that is the key to “why ERDL on top”:
ACS is a “policy orchestration contract”, not a “policy language”. It standardizes “where to block and how to enforce”, but it outsources “the semantics of the rule itself” to Rego / Cedar.
That is exactly ACS’s three holes:
Hole 1: policy semantics are neither frozen nor verified
ACS’s “determinism” is orchestration determinism — same manifest + snapshot + dispatcher → same verdict. But the “dispatcher” is the policy engine (Rego / Cedar / custom), which is host-supplied and pluggable.
In other words: ACS guarantees “the same policy engine on the same input gives the same result”, but not “your rule’s semantics are themselves deterministic and correct”. Rego / Cedar’s evaluation semantics are not formally frozen — the same rule may have subtle semantic differences across versions and implementations. ACS does not promise this, because it was never its job.
Hole 2: audit is “after-the-fact reconstruction”, not “decision-time cryptographic fixation”
ACS’s audit capability (Decision BOM, Tutorial 50) says it verbatim:
“Reconstructible View……the BOM is reconstructed on demand by querying existing data sources……No new data collection required.” — plus a completeness score (0.0–1.0) indicating “how many required fields were filled”.
That is: ACS’s audit reconstructs the decision context after the fact from logs, telemetry, and OTel traces — and it may be incomplete (completeness score < 1).
This is a different thing from ERDL’s Decision Object (DO) — a DO is cryptographically fixed with JCS + SHA-256 at the moment the decision is produced; any independent party recomputes from the public spec and gets the same hash bytes. Tamper-proof, non-repudiable, independently recomputable.
When audit/compliance asks “prove this decision really was computed this way and wasn’t changed”, “after-the-fact reconstruction” and “decision-time fixation” are two answers of different strength.
Hole 3: delegated authorization lives in “code”, not in “spec”
ACS’s delegation chain (Tutorial 23) does “monotonic scope narrowing” — a child agent’s capabilities must be a strict subset of the parent’s. But its implementation is:
“The SDK enforces this at the API level —
delegate()throws an error if the requested capabilities are not a subset.”
The narrowing rule is written in SDK code (in the delegate() method), not in a semantic specification that can be formally verified and independently tested by third parties. Meaning: this safety property is “guaranteed by Microsoft’s implementation”, not “proven mathematically”.
III. What ERDL fills, and how they compose
The three holes are exactly ERDL’s territory:
| ACS’s hole | What ERDL fills |
|---|---|
| Policy semantics unfrozen, unverified | Semantic spec: 34-node expression tree, FREEZE-2 frozen (add-only, never change), full Z3 formal verification |
| Audit is after-the-fact reconstruction | Decision Object (DO): JCS + SHA-256 fixed at decision time, independently recomputable, tamper-proof, non-repudiable |
| Delegation narrowing in code | Delegated-authorization security model: 5 invariants + 8 adversarial vectors; narrowing is “provable, testable” semantics, not one vendor’s implementation |
And the two are not either/or — they compose.
ACS’s “custom dispatcher” is precisely the hook left for plugging in a custom policy engine. A team already using AGT can:
- Keep all of ACS: the 8 intervention points, verdict normalization, multi-framework SDKs, and the sandbox / prompt-injection / identity engineering practices;
- Mount ERDL in: as that “custom dispatcher”, upgrading “rules” from “a Rego/Cedar block with no formal guarantee” to “ERDL expressions with frozen semantics + formal verification + independently recomputable decision evidence”.
IV. The one-line answer
ACS solves “where to block and how to enforce”; ERDL solves “whether the rule’s semantics are deterministic and independently verifiable”.
A team already using AGT already has its “orchestration” well covered by ACS; but it still needs ERDL if and only if it starts facing these three questions:
- Rule semantics must be right — not “I tested this Rego”, but “this rule’s semantics are formally frozen and Z3-proven”;
- Decision evidence must be hard — not “reconstruct a possibly-incomplete BOM from logs after the fact”, but “produce independently recomputable cryptographic evidence at the moment of decision”;
- Safety properties must be provable — not “Microsoft’s SDK happens to implement it this way”, but “delegation narrowing is an invariant a third party can independently reproduce and stress with adversarial vectors”.
ACS gave the agent “checkpoints”; ERDL gives the checkpoints “deterministic, verifiable rule semantics”. Together, they form an AI-agent governance system that can both block and prove.