ADR-0053 · Policy Engine — Priority-Ordered Rule Composition
Status: Accepted · Date: 2026-05-17
Context
Agent frameworks treat "guardrails" as a single prompt block ("don't do X"), a hardcoded list of disallowed tools, or an after-the-fact moderation pass. All three fail when compliance requires contextual rules ("transfers above $10k between 18:00–06:00 UTC require dual approval"), when the same rule must apply across many tools and agents, or when enterprise customers need to author and version their own policies without forking the framework.
Treasury, healthcare, legal, and government use cases require declarative, composable, auditable policy — closer to OPA/Rego than to a system prompt.
Decision
A PolicyEngine evaluates every consequential proposal against a priority-ordered list of
rules. Each rule is a pure function:
type PolicyRule = {
id: string;
priority: number; // lower = earlier
evaluate(ctx: PolicyContext): PolicyCheckResult; // allow | deny | escalate
};PolicyCheckResult carries a verdict, a human-readable reason, structured evidence, and
(for escalate) an approval route hint.
Composition
- Rules evaluate in priority order. First non-
allowwins (deny short-circuits; escalate short-circuits unless a later deny exists). - Rules can be composed from policy packs: versioned bundles (e.g.,
@veridex/agents-treasuryships a treasury pack with sanctions, ceilings, time-lock, and dual-approval rules). Customers can ship their own packs via@veridex/agents-control-plane. - Built-in factories:
blockSafetyClass,requireApproval,capSpend,capTokens,restrictTimeWindow,rateLimit,denylist,allowlist. - Custom rules are functions; the registry holds them and exposes them via dependency injection — there is no global state.
Evaluation context
PolicyContext includes the agent definition, run metadata (user, tenant, turn), proposal
(tool call / memory write / handoff), live counters (spend so far, tool calls so far), and
arbitrary ctx.metadata (e.g., pre-screened sanction result, KYC tier). Rules are pure
functions of context — no I/O — so they replay deterministically.
Verdicts
allow— proceed.deny— abort the proposal; emitpolicy_violation; the model sees a structured error and may retry or apologise.escalate— suspend, route toApprovalManager, checkpoint (ADR-0054).
Every evaluation emits a policy_decision event with rule id, verdict, reason, and the
evidence the rule consumed.
Consequences
Positive. Policy is testable, replayable, and composable. Enterprise customers extend via packs, not forks.
Negative. Rule authors must keep evaluate pure. The contract enforces this at the type
level.
Source
Internal ADR: docs/architecture/decisions/0053-policy-engine-rule-composition.md