Example

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.

Read the chapter .

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.