Route a claim through a policy, not through a threshold
Run the chapter's policy engine over six states and see the commitment, the next action, the rule that matched and the rules it suppressed.
A policy that answers “allow or not” is a threshold with extra steps. The chapter’s engine answers a different question: given a candidate and what has already happened, what is the commitment, what is the next action, which rule matched, and which rules were suppressed but still contributed their reasons and obligations. The example is the engine itself, exercised over six states.
Run it
The engine is the chapter’s own reference implementation, reproduced below
exactly. Save it as policy_engine.py, save the driver as demo.py beside it,
and run the driver with Python 3.13 or later. The standard library only: no
model, no network, no service, no external calls.
Expected outcome
clean
PERMIT / NONE
provenance = UNVERIFIED
HOLD / VERIFY
matched = VERIFIED_PROVENANCE_REQUIRED
suppressed =
policy_restricted = TRUE + sensitivity = FAIL
DENY / REFUSE_REQUEST
matched = POLICY_PROHIBITED
suppressed = SENSITIVITY_REVIEW
recoverable evidence gap, first attempt
HOLD / RETRIEVE
matched = RECOVERABLE_EVIDENCE_GAP
suppressed =
recoverable evidence gap, retrieval budget exhausted
HOLD / REVIEW
matched = RETRIEVAL_BUDGET_EXHAUSTED
suppressed =
user information gap, clarification budget exhausted
HOLD / REVIEW
matched = ASK_BUDGET_EXHAUSTED
suppressed = These are the routes the chapter states. The two that carry a suppressed line
are the interesting ones: a prohibition and a sensitivity review both matched,
the prohibition won on priority, and the suppressed rule’s reasons are still
carried.
The engine
The chapter’s reference implementation, in full. It is deliberately small: a handful of enums, a frozen dataclass for the context, one function that collects candidate rules and one that picks a winner.
from dataclasses import dataclass
from enum import Enum
class Commitment(str, Enum):
PERMIT = "PERMIT"
HOLD = "HOLD"
DENY = "DENY"
class NextAction(str, Enum):
NONE = "NONE"
REFINE = "REFINE" # revise the candidate; executed as repair in Chapter 13
RETRIEVE = "RETRIEVE"
VERIFY = "VERIFY"
ASK = "ASK"
REVIEW = "REVIEW"
ABSTAIN = "ABSTAIN"
REFUSE_REQUEST = "REFUSE_REQUEST"
@dataclass(frozen=True)
class History:
step_index: int = 0
retrieve_attempts: int = 0
verify_attempts: int = 0
ask_attempts: int = 0
@dataclass(frozen=True)
class Context:
risk_tier: str = "high"
max_steps: int = 4
max_retrievals: int = 1
max_verifications: int = 1
max_asks: int = 2
human_review_available: bool = True
def terminal_route(ctx):
if ctx.human_review_available:
return "REVIEW"
return "ABSTAIN"
def evaluate_policy(record, ctx, history):
hits = []
def hit(name, priority, commitment, action,
reasons=(), obligations=()):
hits.append({
"name": name,
"priority": priority,
"commitment": commitment,
"action": action,
"reasons": reasons,
"obligations": obligations,
})
if record.get("policy_restricted") is True:
hit("POLICY_PROHIBITED", 100,
"DENY", "REFUSE_REQUEST",
reasons=("POLICY_PROHIBITED",))
if history.step_index >= ctx.max_steps:
hit("MAX_STEPS_EXCEEDED", 95,
"HOLD", terminal_route(ctx),
reasons=("MAX_STEPS_EXCEEDED",))
adequacy = record.get("epistemic_adequacy")
if adequacy == "USER_GAP":
if history.ask_attempts >= ctx.max_asks:
hit("ASK_BUDGET_EXHAUSTED", 91,
"HOLD", terminal_route(ctx),
reasons=("USER_INPUT_MISSING",
"ASK_BUDGET_EXHAUSTED"))
else:
hit("USER_INPUT_REQUIRED", 90,
"HOLD", "ASK",
reasons=("USER_INPUT_MISSING",),
obligations=("request_missing_user_field",))
if adequacy == "RECOVERABLE_EVIDENCE_GAP":
if history.retrieve_attempts >= ctx.max_retrievals:
hit("RETRIEVAL_BUDGET_EXHAUSTED", 91,
"HOLD", terminal_route(ctx),
reasons=("EVIDENCE_GAP",
"RETRIEVAL_BUDGET_EXHAUSTED"))
else:
hit("RECOVERABLE_EVIDENCE_GAP", 90,
"HOLD", "RETRIEVE",
reasons=("EVIDENCE_GAP",),
obligations=("retrieve_missing_evidence",))
if ctx.risk_tier == "high" and record.get("provenance") in {
"UNVERIFIED", "NOT_MEASURED", "UNAVAILABLE"
}:
if history.verify_attempts >= ctx.max_verifications:
hit("VERIFICATION_BUDGET_EXHAUSTED", 86,
"HOLD", terminal_route(ctx),
reasons=("PROVENANCE_UNRESOLVED",
"VERIFICATION_BUDGET_EXHAUSTED"))
else:
hit("VERIFIED_PROVENANCE_REQUIRED", 85,
"HOLD", "VERIFY",
reasons=("PROVENANCE_UNRESOLVED",),
obligations=("verify_provenance",))
if record.get("sensitivity") == "FAIL":
hit("SENSITIVITY_REVIEW", 70,
"HOLD", "REVIEW",
reasons=("SENSITIVITY_FAIL",),
obligations=("review_context_dependence",))
if not hits:
required = {
"containment": "PASS",
"relation_fidelity": "PASS",
"epistemic_adequacy": "ANSWERABLE",
"provenance": "VERIFIED",
}
if all(record.get(k) == v for k, v in required.items()):
hit("CLEAN_ACCEPT", 0, "PERMIT", "NONE",
reasons=("ALL_REQUIRED_CONDITIONS_SATISFIED",))
else:
hit("UNHANDLED_OR_UNKNOWN_STATE", 60,
"HOLD", terminal_route(ctx),
reasons=("REQUIRED_STATE_NOT_SATISFIED",),
obligations=("inspect_unhandled_state",))
hits.sort(key=lambda x: x["priority"], reverse=True)
winner = hits[0]
reasons = list(dict.fromkeys(
r for h in hits for r in h["reasons"]
))
if winner["commitment"] == "DENY":
obligations = list(winner["obligations"])
else:
obligations = list(dict.fromkeys(
o for h in hits for o in h["obligations"]
))
return {
"commitment": winner["commitment"],
"next_action": winner["action"],
"matched_rule": winner["name"],
"suppressed_rules": [h["name"] for h in hits[1:]],
"reason_codes": reasons,
"obligations": obligations,
"step_index": history.step_index,
"policy_version": "policy-v2",
}The driver
The driver is not part of the chapter. It constructs one candidate per state and prints what the engine returned.
"""Drive the chapter's policy engine and check it reproduces the chapter's routes."""
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent))
from policy_engine import Context, History, evaluate_policy
ctx = Context()
clean = {
"containment": "PASS",
"relation_fidelity": "PASS",
"epistemic_adequacy": "ANSWERABLE",
"provenance": "VERIFIED",
}
def show(label, record, history=None):
result = evaluate_policy(record, ctx, history or History())
print(f"{label}\n {result['commitment']} / {result['next_action']}")
if result["matched_rule"] != "CLEAN_ACCEPT":
print(f" matched = {result['matched_rule']}")
print(f" suppressed = {', '.join(result['suppressed_rules'])}")
return result
show("clean", clean)
unverified = dict(clean, provenance="UNVERIFIED")
show("provenance = UNVERIFIED", unverified)
restricted = dict(clean, policy_restricted=True, sensitivity="FAIL")
show("policy_restricted = TRUE + sensitivity = FAIL", restricted)
gap = dict(clean, epistemic_adequacy="RECOVERABLE_EVIDENCE_GAP")
show("recoverable evidence gap, first attempt", gap)
show("recoverable evidence gap, retrieval budget exhausted", gap, History(retrieve_attempts=1))
ask = dict(clean, epistemic_adequacy="USER_GAP")
show("user information gap, clarification budget exhausted", ask, History(ask_attempts=2))Mechanism and limitations
Documented: the policy profile, the rule set, the priority order and the expected routes are in the chapter’s own text, and the engine it describes is the block above. The chapter is explicit that the implementation is intentionally small and that a production engine would add schema validation, policy profiles, explicit response modes, durable logs and stronger rule composition.
Observed: the run above. The six states produced exactly the routes the chapter states, including the case where two rules matched and one was suppressed.
Not shown. These routes are one explicit, testable profile, and the chapter says so: they are not universal. The engine has no execution memory of its own; the retry and cycle-detection behaviour belongs to the next chapter’s recovery demo. The obligations the engine returns are names, not actions, and nothing here shows that a person who was asked actually answered or that a retrieval found anything. The engine does not call a model, so it says nothing about model quality or about whether these routes are the right ones.
Understand this example
Copy this prompt into your AI tool. No code runs here.
Apply this example
Copy this prompt into your AI tool. No code runs here.