Skip to main content

How the Decision Engine Decides

An explanation of the deterministic policy engine: the five rules it evaluates, how rule results resolve to approve, block, or escalate, and why the same inputs always produce the same decision.

TrustRelay returns one of three outcomes for a change: APPROVE, BLOCK, or ESCALATE. This document explains how the engine reaches that outcome, why the same inputs always produce the same result, and where a model is and is not involved.

This is an explanation, not a how-to. It describes why the engine is built the way it is. For the verification procedure, see the offline verification guide.

The engine is a pure function

The decision engine takes a sealed evidence snapshot and a pinned policy version, and returns a decision. It has no other inputs. It does not read the clock, call the network, query a database, or consult a model. Everything it needs is in the arguments.

This constraint is enforced, not merely intended. An architecture test fails the build if any file under the engine's src imports the AWS SDK, a database driver, an HTTP client, a model SDK, or a secrets manager. The Lambda handlers that wrap the engine may import those, because they are responsible for fetching the inputs. The engine itself may not.

The practical consequence is that a decision can be reproduced years later by anyone holding the same snapshot and policy version, without access to TrustRelay's infrastructure. That is what makes an independent verification meaningful.

The five rules

The engine evaluates five rules. Each returns a result, and the results combine into one outcome.

RuleQuestion it answersResult when satisfiedResult when not
EVD_ALL_REQUIRED_VALIDDoes the snapshot contain evidence at all?satisfiedmissing
SPONSORSHIP_REQUIREDIs there a human sponsor for this agent-authored change?satisfiedmissing
ATT_HIGH_CONFIDENCE_SUPPORTEDDoes at least one commit carry high-confidence attribution?satisfiedstale when all attribution is unknown, conflicting otherwise
ATT_NO_CONFLICTING_PROVIDERSDo the coding agents agree on what produced this change?satisfiedconflicting when more than one provider is implicated
POL_OPERATING_MODEIs the decision consistent with the pinned policy template?satisfiedrecorded when the outcome is not APPROVE

A rule result is not a boolean. It is one of satisfied, missing, pending, invalid, unsupported, stale, or conflicting, and the specific value determines how much weight the result carries.

How results resolve to an outcome

Rule results combine by precedence, and the most restrictive result wins.

Rule resultContributes
missing, invalid, conflictingBLOCK
stale, pending, unsupportedESCALATE
satisfiedNothing; it does not push the outcome upward

The precedence order is BLOCK over ESCALATE over APPROVE. The engine scans for a blocking result first. If none exists, it scans for an escalating result. If neither exists, the outcome is APPROVE.

The asymmetry is deliberate. A missing sponsor blocks the change even if every other rule is satisfied, because the engine has no way to establish accountability for the change. Unknown attribution escalates rather than blocks, because an absence of attribution evidence is not evidence that the change is unsafe; it is a reason for a human to look. The design keeps BLOCK for conditions that are definitively disqualifying and reserves ESCALATE for conditions that are indeterminate.

Why the same inputs give the same answer

The engine sorts nothing, iterates over ordered collections, and has no source of randomness or ambient state. Given the same snapshot and policy version, it produces the same rule results, the same outcome, and the same hash.

That hash covers the snapshot hash, the policy id and hash, the outcome, and each rule paired with its result. It is computed over a canonical serialization of exactly those values, so two runs over equal inputs produce equal hashes and any difference in the inputs changes the hash. The hash is the value the signed passport binds to, which is why verification can confirm that a decision has not been altered.

The engine also emits reason codes. Each evaluated rule produces a code of the form <RESULT>_<RULE_ID>, and the outcome adds a leading system code: SYS_DECISION_APPROVED, SYS_DECISION_BLOCKED, or SYS_DECISION_ESCALATED. The reason codes are what a developer sees on the pull request, so the check can explain itself without exposing the raw rule objects.

Where AI is and is not involved

No AI makes or modifies an authorization decision. The engine has no model dependency, and the architecture test prevents one from being added without the build failing.

AI is used elsewhere in the system. A coding agent produces the change, and attribution analysis uses provider signals to determine which agent produced which part of it. Those signals become evidence. Evidence informs the decision. The decision itself is made by the rules above, and a model cannot approve, block, or escalate anything.

This separation matters for two reasons. A model's output is not reproducible in the way a rule evaluation is, so a decision that depended on one could not be replayed exactly. And a model cannot be held accountable in the way a policy can, so a decision that a model made could not be explained to an auditor by pointing at the rule that produced it.

What the engine does not decide

The engine decides whether a change satisfies a policy. It does not decide whether the policy is right, whether the evidence was complete when it was collected, or whether the change is a good idea. Those are properties of the inputs and of the policy author, not of the engine.

This is why a verified decision is a statement about the authorization process rather than about the change. The engine's job is to apply the policy consistently and reproducibly, and to record what it did in a form someone else can check. Everything else belongs to the people who wrote the policy and gathered the evidence.