# Why the Policy Engine has to be a pure function

*Spend authorization that an LLM made is fundamentally unauditable. We trace the same input twice and get different decisions. Pure-function policy is the only path to a decision auditor can sign off on.*

Published 2026-04-18 · 6 min read · policy, architecture

## The temptation to use LLMs for policy

It feels obvious to ask the LLM 'is this spend OK?' alongside 'what should the agent do next?'. They're both judgment calls. The LLM is right there.

And the LLM will reliably say yes or no — until two months later, an audit asks why the agent approved a $200 spend on a Tuesday. We can't replay the decision: same prompt, different output, different reasoning trace.

## Pure-function policy: the rule

Mission Control's Policy Engine takes a typed PolicyDecisionRequest and a typed PolicyEvaluationContext, and returns a typed PolicyDecision. No I/O. No async. No LLM. Same inputs always return the same output.

That's testable: every decision branch has a unit test. That's hashable: the decision goes into the audit ledger as a fixed-shape event. That's auditable: a SOC 2 reviewer can read evaluatePolicy() and reason about every possible decision in finite time.

## What you give up

You give up nuance. The Policy Engine can't say 'this spend looks weird, ask the human'. It can say 'this spend triggers an approval rule, ask the human' — but the rule has to be configured up front. There's no in-context judgment.

That's the right trade. Nuance lives in the approval queue. The deterministic rule decides yes/no/escalate; the human resolves the escalations. The audit trail records both layers.
