DEV Community

Cover image for Why AI Agents Need an Execution Boundary
Ranknod
Ranknod

Posted on

Why AI Agents Need an Execution Boundary

Imagine an AI agent is reviewing a page before publication.

It decides the page is ready, calls publish_page, and the request succeeds. But the response times out.

The agent cannot tell whether publication happened, so it tries again.

Now imagine something else changed between those attempts: the page was edited, the user's permissions changed, or an approval was revoked.

The problem is no longer whether the model made a reasonable decision. The problem is that model intent has been allowed to become a real-world side effect without enough deterministic control around it.

That is why AI agents that can change external systems need an execution boundary.

The model should produce intent. Application code should control authority and side effects.

A useful architecture looks like this:

AI reasoning

Structured action proposal

Execution boundary

External system

The execution boundary is not just a human-approval step. It is the layer responsible for validation, authorization, policy enforcement, workflow state, approvals, idempotency, retries, execution, auditing, and verification.

Why direct tool execution becomes risky

A simple agent prototype often looks like this:

User request

LLM

Tool call

External system

The model decides that publish_page is appropriate, and the application immediately performs the action.

That is convenient during prototyping, but several different responsibilities have now been collapsed into one decision.

Before a page is actually published, the system may still need to ask:

  • Is publish_page a supported operation?
  • Is the payload valid?
  • Is this user allowed to publish this page?
  • Is the page still in a publishable state?
  • Does publication require approval?
  • Is that approval still valid?
  • Has this operation already run?
  • Can it be retried safely?
  • Did publication actually produce the intended result?

Those are not questions the model should resolve by itself.

They belong to deterministic application logic.

Put an execution boundary between intent and action

Instead of allowing the agent to invoke privileged side effects directly, let it propose an action.

For example:

{
  "type": "publish_page",
  "resourceId": "page_284",
  "reason": "draft_ready_for_publication",
  "operationId": "op_8f219"
}
Enter fullscreen mode Exit fullscreen mode

This object represents what the agent wants to happen.

It does not prove that the action is allowed.

A safer flow is:

Agent proposal

Schema validation

Authorization

Policy evaluation

Approval if required

Pre-execution validation

Execution

Verification

Audit record

That middle layer is the execution boundary.

The model remains useful for reasoning. The application remains responsible for deciding whether the proposed action may affect the outside world.

Use structured proposals instead of free-form instructions

Free-form output is difficult to validate reliably.

A bounded action vocabulary gives the application something predictable to inspect:

type AgentAction =
  | {
      type: "publish_page";
      resourceId: string;
      operationId: string;
    }
  | {
      type: "create_ticket";
      projectId: string;
      title: string;
      operationId: string;
    };
Enter fullscreen mode Exit fullscreen mode

This example is illustrative rather than production-tested, but the design principle matters.

The agent can select from known actions. It cannot invent a privileged operation simply by describing one convincingly.

Application code can then own the control path:

async function processAgentAction(
  action: AgentAction,
  actor: Actor
) {
  validateSchema(action);
  await authorize(actor, action);
  await enforcePolicy(action);

  if (requiresApproval(action)) {
    return queueForApproval(action, actor);
  }

  return executeIdempotently(action, actor);
}
Enter fullscreen mode Exit fullscreen mode

The important separation is:

The model produces intent. The application grants authority.

That makes the boundary easier to test, audit, and reason about.

Authorization and policy belong outside the model

Suppose the agent proposes:

{
  "type": "publish_page",
  "resourceId": "page_284",
  "operationId": "op_8f219"
}
Enter fullscreen mode Exit fullscreen mode

The application still needs to decide whether publication is allowed.

That may depend on:

  • the authenticated user;
  • the user's role;
  • ownership of page_284;
  • the current environment;
  • the page's workflow state;
  • publishing policy;
  • risk level;
  • approval requirements.

An agent may understand how publishing works without having authority to publish.

That distinction is useful across agent roles.

A research agent might be allowed to search and summarize. An editing agent might be allowed to create a draft. A publishing agent might be allowed to request publication.

request publication does not have to mean publish immediately.

Capability and permission are separate concerns.

Make approval part of the workflow state

If an action needs human approval, approval should be represented in application state rather than only as an instruction in a prompt.

For a publishing workflow, the states might look like this:

PROPOSED

VALIDATED

AWAITING_APPROVAL

APPROVED

EXECUTING

VERIFYING

COMPLETED

Failure or exception states might include:

REJECTED
VALIDATION_FAILED
EXECUTION_FAILED
RETRY_PENDING
CANCELLED

This gives the application explicit answers to operational questions:

  • Which actions are waiting for approval?
  • Which exact proposal was approved?
  • Who approved it?
  • Can it still be cancelled?
  • What should happen after execution fails?
  • Which state transitions are legal?

It also prevents "approval required" from becoming a vague behavioral suggestion to the model.

Recheck important assumptions before execution

Approval itself should not automatically make an action safe forever.

Imagine page_284 is approved for publication at 10:00.

Before the executor runs, one of these things changes:

  • the page is edited;
  • the user loses publishing permission;
  • the resource is deleted;
  • policy changes;
  • the approval is revoked;
  • the approved version no longer matches the current version.

The executor should not assume that an earlier approval proves the current action is still valid.

For important side effects, recheck authorization, relevant state, and approval validity immediately before execution.

An approval should authorize a specific action under specific conditions, not create unlimited future authority.

Make retries safe with idempotency

Now return to the original publishing example.

The executor sends a request to publish page_284.

The request succeeds.

The response times out.

From the application's point of view, the result is ambiguous.

Retrying blindly may cause the same side effect twice.

That is where idempotency becomes part of the execution boundary.

The proposal may contain an operation identifier:

{
  "type": "publish_page",
  "resourceId": "page_284",
  "operationId": "op_8f219"
}
Enter fullscreen mode Exit fullscreen mode

Before execution, the application checks whether op_8f219 has already completed or is already in progress.

There is one important detail here: an identifier should not automatically become trustworthy just because the model supplied it.

Ideally, trusted orchestration or application code should create the operationId, or at minimum validate it before use.

The model should not control the transactional identity of privileged operations without checks.

Once the operation is tracked by trusted code, the execution layer can safely handle:

  • duplicate requests;
  • retries;
  • timeouts;
  • in-progress operations;
  • completed operations;
  • recovery or compensation logic.

The model does not need to remember transactional state. The system does.

Verify the outcome, not just the API call

Even successful execution is not necessarily the end of the workflow.

Suppose the publishing API returns 200 OK.

Does that prove the new page is live?

Not always.

The request may have been accepted while a downstream process later fails. A public page may still display an older cached version. A deployment may start successfully but fail during rollout.

For meaningful side effects, verification should be explicit.

A workflow record might contain:

  • proposed action;
  • validation result;
  • authorization result;
  • approval record;
  • operation ID;
  • execution response;
  • verification result;
  • final state.

That gives COMPLETED a stronger meaning.

It means the system observed the intended result, not merely that it sent a request.

Test the execution boundary without the LLM

Separating reasoning from execution also makes the system much easier to test.

The executor should be testable independently from the model.

You can submit:

  • a valid authorized proposal;
  • a malformed proposal;
  • an unsupported action;
  • an unauthorized action;
  • an action missing required approval;
  • an action whose approval became stale;
  • a duplicate operation ID;
  • an execution failure;
  • a verification failure.

Then assert the expected result and state transition.

For example:

Unauthorized proposal
→ rejected before execution

Approved but stale proposal
→ returned for revalidation

Duplicate operation
→ not executed twice

Execution succeeds, verification fails
→ not marked COMPLETED

The reasoning layer can remain probabilistic.

The side-effect controls do not have to be.

Implementation checklist

Before allowing an AI agent to change a real system, check:

  • Does the agent propose actions instead of receiving unrestricted execution access?
  • Is every action represented by a narrow, validated schema?
  • Is the action vocabulary explicitly bounded?
  • Is authorization enforced outside the model?
  • Are policy checks deterministic where possible?
  • Are approval requirements represented in workflow state?
  • Is the approved action tied to the correct resource and version?
  • Are authorization and relevant state rechecked before important execution?
  • Are operation IDs created or validated by trusted application code?
  • Are retries protected by idempotency?
  • Are failures observable and recoverable?
  • Is the external result independently verified where necessary?
  • Is enough audit data recorded to reconstruct what happened?
  • Can the execution layer be tested without invoking the model?

The core idea is simple:

Let the model decide what it wants to do. Let deterministic application logic decide whether that action is valid, authorized, approved, safe to execute, and actually completed.

That execution boundary is what turns tool use into a controlled system rather than a direct path from model output to real-world side effects.

Where would you place that boundary in your current agent architecture?

AI disclosure: This article was created with AI assistance. The human author is responsible for validating the technical content before publication.

Top comments (0)