Policy Boundaries in Architecture: What Agents Are Allowed to Do

A governed layer that defines and enforces rules: what the agent is allowed to do, what is forbidden, and what requires approval.
On this page
  1. Idea in 30 seconds
  2. Problem
  3. Solution
  4. How Policy Boundaries work
  5. In code, it looks like this
  6. How it looks at runtime
  7. When it fits - and when it does not
  8. Fits
  9. Does not fit
  10. Typical problems and failures
  11. How it combines with other patterns
  12. How this differs from Guarded-Policy Agent
  13. In short
  14. FAQ
  15. What Next

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.

Diagram
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

PYTHON
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

TEXT
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

SituationWhy Policy Boundaries fit
βœ…There are tools that change system stateThe Policy Boundaries layer blocks dangerous operations or requires approval.
βœ…Role-based access model and multi-tenant isolation are requiredUnified policy rules prevent leakage across tenants and roles.
βœ…The agent can spend money or limited resourcesPolicy Boundaries control budget caps, quotas, and require_approval for expensive actions.
βœ…Decision audit is required for complianceThe Policy Boundaries layer records decision, reason_code, and context for every critical action.

Does not fit

SituationWhy Policy Boundaries do not fit
❌One-shot demo with one read-only tool and no side effectsA 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 logicSimple checks in the specific service business logic are usually enough.

In such cases, a basic code check is sometimes enough:

PYTHON
if role != "admin":
    raise PermissionError("denied")

Typical problems and failures

ProblemWhat happensHow to prevent
Policy boundary bypassActions are executed directly, bypassing policy checksSingle entry point for tools, direct access forbidden
Fail-open on policy engine failureDuring failure, the system accidentally allows risky actionsFail-closed for write/high-risk operations, emergency deny by default
Overly strict rulesUseful actions are blocked, the agent cannot complete the taskRisk-tier rules, exceptions with audit, regular policy review
Policy drift (rules out of sync)Documentation says one thing, code checks anotherPolicy-as-code, versioning, and policy tests
No explanation for block reasonThe team does not understand why the action was rejectedStandardized reason_code and audit logs
Incomplete context for policy checkThe policy engine decides without important request attributesPass 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 AgentPolicy Boundaries in architecture
LevelBehavior pattern inside agent logicSystem layer of enforced control at architecture level
What it controlsHow the agent formulates and selects actionsWhether this action is actually allowed to execute
How it reacts to model errorReduces risk, but does not guarantee blockingEnforces blocking or sends for approval
Key resultSafer agent behaviorTechnically 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

Quick take

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:

⏱️ 9 min read β€’ Updated March 7, 2026Difficulty: β˜…β˜…β˜…
Integrated: production controlOnceOnly
Add guardrails to tool-calling agents
Ship this pattern with governance:
  • Budgets (steps / spend caps)
  • Tool permissions (allowlist / blocklist)
  • Kill switch & incident stop
  • Idempotency & dedupe
  • Audit logs & traceability
Integrated mention: OnceOnly is a control layer for production agent systems.
Author

This documentation is curated and maintained by engineers who ship AI agents in production.

The content is AI-assisted, with human editorial responsibility for accuracy, clarity, and production relevance.

Patterns and recommendations are grounded in post-mortems, failure modes, and operational incidents in deployed systems, including during the development and operation of governance infrastructure for agents at OnceOnly.