Guard Pattern
The guard pattern places policy authority immediately before the side effect.
Pattern
Prepare safe action context, call HaltState with a stable idempotency key, branch on the decision, execute only on allow or approved retry, and report the final execution status. The protected business action should be the smallest unit that creates the side effect: one refund, one customer data export, one database write, one outbound message, one payment authorization.
Python example
try:
with client.guard("refund.create", params={"amount": 126, "currency": "USD"}, idempotency_key="refund:order-1001"):
ledger.write_refund_once()
except ApprovalPending:
return "pending"
except ActionDenied:
return "denied"Fail-closed rule
For money movement, PII access, production writes, and customer communications, network failure or engine failure should stop execution. The risk is not that a refund waits; the risk is that the agent moves money without a policy decision.
Decision table
| Decision | SDK behavior | Worker rule |
|---|---|---|
| ALLOW | Guard block executes or check result is allowed. | Execute once, then report success or error. |
| APPROVAL_REQUIRED | ApprovalPending is raised or check result requires approval. | Pause or exit. Do not execute. |
| DENY | ActionDenied is raised or check result is denied. | Record denial. Never execute. |
| EXPIRED | ActionExpired is raised for stale approval. | Do not execute; create a fresh action only if business policy allows. |
| ERROR | Connection, auth, policy, or malformed response error. | Fail closed for money, PII, production writes, and customer messaging. |
Permit object
When approval is granted, the guard yields a permit. Store the safe permit or action reference in internal audit logs so execution can be connected to approval. Do not expose raw approval IDs publicly.
with client.guard(...) as permit:
log.info('approved action=%s approver=%s approved_at=%s', permit.action, permit.approver, permit.approved_at)
execute_business_action()Direct check versus guard
Use guard() when you want SDK control-flow exceptions and an execution block that only runs with authority. Use check() plus explicit branching when a worker has its own ledger states, queue claims, or multi-step proof creation. In both cases, the side effect happens after the policy decision.
Implementation notes
Keep the HaltState call as close as possible to the side effect. The agent may plan and draft freely, but the wrapper around the actual action should be the place where authority is checked. That wrapper should send only the context required for policy evaluation: safe identifiers, normalized amounts, action names, risk flags, schedule windows, and redaction status. Raw customer payloads and secrets should stay in the business system or protected operator tooling.
Operational evidence
For each action, preserve the decision, the worker outcome, the idempotency key, safe resource references, latency, proof status, and redaction status. This evidence supports incident response and control narratives because it shows what the system did at runtime rather than only describing what the policy document intended. HaltState supports alignment work; it is not a substitute for legal advice or a compliance certification.