Idea in 30 seconds
The Policy Boundaries layer is the system boundary that defines what an agent can do and what it cannot.
The agent can propose actions, but the final decision always belongs to the system: allow, deny, or require_approval.
When needed: when the agent has access to tools, data, or operations that can change state, spend budget, or create risk.
The LLM must not control side effects (state changes) on its own. The Policy Boundaries layer enforces this at the architecture level.
Problem
Without clear Policy Boundaries, an agent starts acting "too freely."
This creates typical risks:
- the agent performs an action without required permissions;
- reads or sends sensitive data;
- the agent exceeds budget, quotas, or allowed step count;
- launches irreversible operations without approval;
- different services apply different rules, so behavior becomes unpredictable.
As a result, incident risk grows, audits become harder, and mistakes become more expensive.
Solution
Add Policy Boundaries as a separate governed layer in the system: it checks every risky action before execution.
This layer follows "deny by default": only what is explicitly in the allowlist and matches context is allowed.
Analogy: like an access-control system in an office center.
A person may want to enter the server room, but the door opens only with the right access level.
The Policy Boundaries layer checks access to actions and resources the same way before execution.
How Policy Boundaries work
The Policy Boundaries layer is a governed layer between Agent Runtime and execution/data layers that decides whether to allow an action, block it, or send it for approval.
Full flow description: Classify β Check β Decide β Enforce β Log
Classify
The system classifies the action type: read, write, delete, export, code execution, budget spend.
Check
The policy engine checks role, tenant, allowlist, scopes, budget/step limits, and risk level.
Decide
The decision must be explicit: allow, deny, or require_approval.
Enforce
The action is either blocked or executed with restricted permissions via Tool Execution Layer.
Log
The log stores the decision, reason_code, request context, and the execution outcome or block event.
This cycle is applied to every risky action and gives predictable behavior even when the model fails.
In code, it looks like this
class PolicyBoundaryLayer:
def __init__(self, policy_engine, approval_queue):
self.policy_engine = policy_engine
self.approval_queue = approval_queue
def authorize(self, action, context):
request = {
"actor_role": context["role"],
"tenant_id": context["tenant_id"],
"action": action["name"],
"resource": action.get("resource"),
"risk_level": action.get("risk_level", "medium"),
}
decision = self.policy_engine.evaluate(request)
mode = decision.get("mode")
if mode == "allow":
return {"ok": True, "mode": "allow", "scopes": decision.get("scopes", [])}
if mode == "require_approval":
ticket_id = self.approval_queue.create(request=request, reason=decision["reason_code"])
return {"ok": False, "mode": "pending_approval", "ticket_id": ticket_id}
# Fail-closed for deny and for unexpected/unavailable policy modes.
return {
"ok": False,
"mode": "deny",
"reason_code": decision.get("reason_code", "policy_unavailable_or_invalid"),
}
How it looks at runtime
Request: "Delete invoice INV-991 and send customer data to an external CRM"
Step 1
Agent Runtime: builds action -> delete_invoice + export_customer_data
Runtime: sends actions to Policy Boundaries
Step 2
Policy Boundaries: Check -> role support_agent, risk high
Policy Engine: delete_invoice -> require_approval
Policy Engine: export_customer_data -> deny (reason_code: cross_system_export_blocked)
Step 3
Runtime: creates an approval ticket for deletion
Runtime: blocks export and returns a safe response with reason
Policy Boundaries do not just "advise"; they actually stop dangerous actions.
When it fits - and when it does not
Policy Boundaries are needed where there are real risks of access, state changes, or cost. For a local read-only prototype, this may be unnecessary complexity.
Fits
| Situation | Why Policy Boundaries fit | |
|---|---|---|
| β | There are tools that change system state | The Policy Boundaries layer blocks dangerous operations or requires approval. |
| β | Role-based access model and multi-tenant isolation are required | Unified policy rules prevent leakage across tenants and roles. |
| β | The agent can spend money or limited resources | Policy Boundaries control budget caps, quotas, and require_approval for expensive actions. |
| β | Decision audit is required for compliance | The Policy Boundaries layer records decision, reason_code, and context for every critical action. |
Does not fit
| Situation | Why Policy Boundaries do not fit | |
|---|---|---|
| β | One-shot demo with one read-only tool and no side effects | A separate Policy Boundaries layer may add more complexity than value. |
| β | The agent does not decide on its own - the whole flow is deterministically coded in business logic | Simple checks in the specific service business logic are usually enough. |
In such cases, a basic code check is sometimes enough:
if role != "admin":
raise PermissionError("denied")
Typical problems and failures
| Problem | What happens | How to prevent |
|---|---|---|
| Policy boundary bypass | Actions are executed directly, bypassing policy checks | Single entry point for tools, direct access forbidden |
| Fail-open on policy engine failure | During failure, the system accidentally allows risky actions | Fail-closed for write/high-risk operations, emergency deny by default |
| Overly strict rules | Useful actions are blocked, the agent cannot complete the task | Risk-tier rules, exceptions with audit, regular policy review |
| Policy drift (rules out of sync) | Documentation says one thing, code checks another | Policy-as-code, versioning, and policy tests |
| No explanation for block reason | The team does not understand why the action was rejected | Standardized reason_code and audit logs |
| Incomplete context for policy check | The policy engine decides without important request attributes | Pass complete request context: actor, tenant, resource, action type, risk, budget |
Most of these problems are solved through a single policy control point, a fail-closed approach, and high-quality audit.
How it combines with other patterns
The Policy Boundaries layer does not replace Agent Runtime or Tool Execution Layer. It is system-level access control above them.
- Agent Runtime - Runtime controls steps, while the Policy Boundaries layer checks which actions are allowed in those steps.
- Tool Execution Layer - execution layer runs the action, while the Policy Boundaries layer decides whether it can be run.
- Human-in-the-Loop Architecture - risky actions can be moved to
require_approval. - Multi-Tenant - the Policy Boundaries layer enforces access isolation between tenants.
In other words:
- Agent Runtime defines when the agent takes a step
- The Policy Boundaries layer defines which actions in that step are allowed at all
How this differs from Guarded-Policy Agent
| Guarded-Policy Agent | Policy Boundaries in architecture | |
|---|---|---|
| Level | Behavior pattern inside agent logic | System layer of enforced control at architecture level |
| What it controls | How the agent formulates and selects actions | Whether this action is actually allowed to execute |
| How it reacts to model error | Reduces risk, but does not guarantee blocking | Enforces blocking or sends for approval |
| Key result | Safer agent behavior | Technically enforced access boundaries + audit |
Guarded-Policy Agent makes agent behavior more cautious.
The Policy Boundaries layer keeps action execution controlled even if the agent makes mistakes.
In short
Policy Boundaries:
- define what is allowed, forbidden, or requires approval
- enforce checks for role, tenant, allowlist, and action risk
- block dangerous actions before execution
- record decisions and reason_code for audit
FAQ
Q: If there is a Guarded-Policy Agent, are Policy Boundaries still needed?
A: Yes. Guarded-Policy Agent improves behavior, but the Policy Boundaries layer is what enforces rules at system level.
Q: Where is it best to store policy rules?
A: Most often in a dedicated policy engine or policy-as-code layer with versions and tests.
Q: What should we do if the policy engine is unavailable?
A: For risky write operations, use fail-closed: deny execution while checks are unavailable.
Q: Can Policy Boundaries depend on environment (dev, staging, prod)?
A: Yes. The same actions often have different policy rules by environment, risk level, and data type.
What Next
Policy Boundaries define the rules of the game. Next, it helps to see where those rules are enforced in the system:
- Tool Execution Layer - how policy is applied during real tool calls.
- Human-in-the-Loop Architecture - how to design approval for critical actions.
- Agent Runtime - how runtime stops or continues the loop based on rules.
- Production Stack - how policy becomes part of operational discipline.