6 min read

Planning and execution agents for safer production changes

Planning and execution agents reduce production risk when research, approval, credentials, and bounded actions stay separate.

Planning and execution agents for safer production changes

A planning agent should not be able to turn its own recommendation into a production change. Give it room to inspect, compare, and argue. Give a separate execution agent a small set of action rights, then make it prove that the requested action matches an approved change.

That separation sounds bureaucratic until you have watched an agent carry a plausible but wrong assumption across an API boundary. Most failures are not cartoon attacks. An agent reads an outdated runbook, mistakes a staging hostname for production, or follows untrusted text in an issue. If the same process holds the credential and the authority to act, the error becomes a change before anyone has time to notice.

A plan is evidence, not authorization

Planning and execution agents need different authority because planning produces a claim about the world, while execution changes the world. A good plan may still rest on stale data, incomplete repository context, or instructions copied from an untrusted source. Treating a plan as permission collapses two decisions that deserve separate scrutiny.

A planner should gather facts, explain uncertainty, propose alternatives, and produce a bounded request. It should not hold a production token merely because it needs to mention an endpoint in a report. If it needs facts from a protected system, expose a purpose-built read operation that returns only the needed facts, or have a human provide the relevant output.

An executor has a different job. It receives a specific request and decides whether that request fits its limited authority. It does not reopen the design discussion, browse arbitrary tickets, or accept a sentence such as "fix the deployment." That sentence might be enough for a conversation. It is useless as a contract for a credentialed process.

This distinction also fixes a habit that teams call "human in the loop" when they mean "a person glanced at a long chat transcript." A reviewer cannot reliably reconstruct every tool call an agent might make from prose. They can review a short, structured request that names the target, operation, inputs, expected effect, and reversal path.

NIST SP 800-53 control AC-5 calls for separating duties to reduce the chance that one person can misuse a system without detection. The wording addresses people, but the reasoning applies cleanly to agents. Do not copy an old approval hierarchy into a prompt. Separate the capabilities in the actual credentials and tool interfaces.

The executor must be narrower than the plan

The executor should have less freedom than the planner, not merely a different prompt. A separate agent that can run arbitrary shell commands with a broad cloud credential has not meaningfully reduced risk. It can still reinterpret a vague request, discover other resources, and make unrelated changes.

Start with an action catalog. Each action should name one operation and accept a small parameter set. For example, deploy_service_revision may accept a service name, an immutable revision identifier, and a target environment. It should not accept a shell fragment or an arbitrary URL.

The most useful restriction is often semantic rather than technical. A credential can permit deployment, but an executor wrapper can still reject a mutable tag such as latest, reject production unless a change ID is present, and reject a service outside its allowlist. Those checks turn assumptions that normally live in a runbook into code that can refuse an unsafe request.

Do not confuse a restricted tool with a restricted outcome. An API token that can update billing-api might also change traffic allocation, environment variables, and autoscaling settings. Split those operations if the API allows it. If the API does not, put a small gateway in front of it that accepts only the operation you are prepared to automate.

A credible executor normally has these boundaries:

  • It uses a separate identity for each environment.
  • It receives only named actions, not a general shell.
  • It validates target names and immutable inputs before it calls a provider.
  • It has a short lifetime and no route to mint broader credentials.
  • It emits a durable record of the request and result.

People often resist this because generic tools are faster to wire up. They are faster for the first demo. A broad run_command tool becomes expensive the first time someone tries to explain why an agent deleted the wrong resource after reading a copied command in a ticket.

A handoff needs a machine-checkable change request

The planner should hand off a structured artifact that an executor can validate without interpreting intent. Free-form plans invite the executor to fill gaps with its own reasoning, which puts planning authority back into the action path.

A practical request can look like this:

{
  "request_id": "chg-2025-0417-redis-timeout",
  "environment": "production",
  "action": "deploy_service_revision",
  "target": {
    "service": "checkout-api",
    "revision": "sha256:8f31c2..."
  },
  "expected_effect": "Run the approved checkout-api revision",
  "rollback": {
    "action": "deploy_service_revision",
    "revision": "sha256:31aa09..."
  },
  "approval": {
    "approved_by": "release-manager",
    "approved_request_hash": "b2c4..."
  }
}

The hash matters. Without it, a reviewer may approve the request they saw while the executor receives a modified version. The approval record should bind to a canonical representation of the exact fields the executor will use. If your system serializes JSON differently in different places, define canonicalization first. A casual string hash is a trap when field ordering or omitted defaults can change the payload.

The executor should reject the request for clear reasons. A useful response shape exposes the failed check without exposing a secret:

{
  "status": "denied",
  "request_id": "chg-2025-0417-redis-timeout",
  "reason": "revision must be an immutable digest",
  "executed": false
}

That refusal is part of the design, not an embarrassing edge case. Teams test whether agents can perform actions and skip tests that prove they cannot exceed their scope. Test both paths.

Do not let the planner choose the executor's action definition. The platform owner should define the catalog, its validation rules, and its credential mapping. The planner selects from supported actions. It cannot invent deploy_anything because a particular task feels urgent.

Separate processes prevent accidental authority sharing

Two agent roles inside one process are still easy to blur. Shared environment variables, token caches, working directories, and tool registrations create accidental paths around your intended boundary. Run the planner and executor as distinct processes with different launch configurations.

The planner's process should receive research tools and perhaps narrowly filtered read interfaces. It should not see executor credentials in its environment, configuration files, or tool descriptions. A model does not need plaintext access to a token to misuse it. If the process can invoke a tool that owns the token, the tool boundary is the relevant capability boundary.

The executor should receive the approved structured request and the smallest possible tool inventory. It should not receive the original ticket body, arbitrary web content, a repository-wide search tool, or the planner's conversation transcript. Those materials can contain prompt injection, bad commands, or casual instructions that the executor has no reason to obey.

This gives you a simple rule for debugging: if an executor needs more context to decide what operation to run, the handoff artifact is underspecified. Do not solve that by giving it broad discovery access. Add the missing field, validation rule, or human decision to the request.

Process separation also helps during incident response. You can revoke the executor session without losing the planner's research record. You can inspect whether the planner proposed a different target than the one approved. If both roles share one session and one identity, that reconstruction becomes guesswork.

Sallyport fits this boundary when an MCP-capable agent needs HTTP or SSH actions without receiving the underlying API or SSH credentials. The app keeps those secrets in its encrypted vault and executes the call itself, so a planner can remain unable to extract a credential even if its instructions go bad.

Approval belongs at the point where consequences change

Stop actions at the vault
Lock the vault to deny every agent action until it is opened through macOS hardware-gated protection.

A human approval should cover a particular decision, not provide a vague blessing for whatever an agent does next. Session approval is useful for establishing that a known agent process may use a bounded set of low-risk actions during one run. It is a poor substitute for review when the action can alter production data, identity, network exposure, or money.

Per-call approval belongs on credentials whose use has material consequences. The extra click is justified when the call is irreversible, unusual, or expensive to unwind. Put it on a production deletion key, a credential that changes access control, or a payment operation. Do not put it on every harmless status request. A prompt that appears constantly becomes background noise, and people approve it without reading.

Use approvals that display what a reviewer can actually judge: the action name, target environment, target resource, immutable input, and any planned rollback. A dialog that says only "Agent requests access" is theater. It tells the reviewer nothing about the consequence.

Approval scope must expire. An approval tied to a process run should end when that process exits. An approval tied to a change request should bind to its content and should not silently authorize a later revision. Long-lived approval grants are attractive because they remove friction, but they recreate the standing privilege that the split was meant to avoid.

Sallyport's decision ladder has a useful shape for this model: its vault gate denies every action while locked, session authorization identifies a new agent process, and selected per-call keys can ask for confirmation on each use. That is intentionally narrower than a policy engine. Teams still need to decide which credentials deserve per-call scrutiny.

Prompt injection reaches planners before it reaches executors

Prompt injection often enters through the planner's normal work. A repository comment says to run a command. A support ticket includes a fake instruction to exfiltrate configuration. A web page tells the agent to ignore earlier directions. Planning agents see more untrusted text than executors should ever see.

The usual bad response is to spend months trying to write a perfect instruction that tells the planner not to be fooled. Model behavior can improve, but text instructions cannot substitute for authority boundaries. Assume the planner may repeat a bad instruction in its plan. Then make the executor reject any request outside its catalog, scope, and approval binding.

Consider a familiar failure. A planner investigates latency and reads an old incident note that recommends setting a service's replica count to zero before draining traffic. It writes a plan that names the wrong environment because the note used a copied hostname. If that planner can call a general deployment tool, it may turn a stale suggestion into downtime.

With a split design, the failure stops at several places. The handoff request must name production explicitly. The executor only accepts a deployment revision action, not a replica-count mutation. The human reviewer sees the actual target and requested effect. The executor log records the denial if the request does not fit. None of those controls requires the planner to perfectly recognize poisoned or obsolete text.

Keep planner output labeled as untrusted input to the execution path. That phrase should affect data handling, not just a warning in a UI. Do not interpolate planner prose into shell commands. Do not let it populate HTTP paths, headers, or query fields without type checks and allowlists. A JSON schema helps, but schema validation alone does not tell you whether production is an authorized target.

Read access can still expose a path to damage

Trace the action path
Compare agent runs and individual calls through Sessions and Activity journals projected from one audit log.

Teams often give planners broad read access because "it cannot change anything." That phrase has caused plenty of avoidable trouble. Read access can reveal customer data, internal hostnames, deployment histories, feature flags, access patterns, and the names of privileged systems. It can also provide exactly the information an attacker needs to frame a convincing action request.

Classify reads by sensitivity and by what they enable. A health endpoint that returns service status differs from a database export endpoint. A service inventory that lists public names differs from a credential manager API that returns secret identifiers and metadata. Do not put both behind the same generic read_only tool.

Give the planner derived facts when possible. Instead of access to every deployment event, offer an operation that returns the current revision, health state, and last approved change ID for one named service. Instead of broad database query rights, provide a metric that answers the diagnostic question. You reduce both accidental disclosure and the volume of material that can carry hostile instructions.

This is where teams overcorrect and cripple useful planning. The answer is not to make the agent blind. The answer is to decide what facts the task needs and create a read interface for those facts. If a planner regularly needs an extra field, add it deliberately after reviewing the use case. Do not solve every gap by handing it a production console.

Logs must let you reconstruct a disagreement

Constrain HTTP credential use
Inject bearer, basic, or custom-header credentials into HTTP calls without giving them to the executor.

An audit trail should answer more than "did an API call happen?" After a disputed change, you need to compare the planner's proposed artifact, the artifact a human approved, the executor's validated request, the exact external call, and the response. Missing any one of those leaves room for a story rather than evidence.

Record a stable request ID through the whole path. The planner assigns or receives it. The approval binds to it and its content hash. The executor writes it with its process identity and action result. The external gateway records it alongside the outbound destination and response status. Avoid logging bearer tokens, passwords, private keys, or full sensitive payloads merely to make correlation easy.

Tamper evidence matters because ordinary application logs often sit in stores that administrators can edit. A hash-linked event sequence makes later alteration detectable if you retain the expected chain state. It does not magically establish that every event is true. It does make a quiet rewrite of recorded history harder to hide, which is the property you need when access to the log store overlaps with the people under review.

Sallyport projects its Sessions and Activity journals from an encrypted, hash-chained audit log, and sp audit verify checks the chain offline over ciphertext without a vault key. That is useful for an investigation because verification does not require handing the reviewer action credentials.

Do a reconstruction drill before an incident forces one. Pick an approved change and ask a colleague to answer five questions from the records: which agent proposed it, who approved the exact request, which executor ran it, what outbound operation occurred, and what result came back. If any answer depends on memory or a chat transcript, improve the records.

The first automated action should be boring and reversible

Start the split with an action that has a clear owner, a narrow target set, and a reversal you have already practiced. A deployment to a non-production environment using an immutable revision is a better first case than changing access rules or deleting stale accounts. Boring work gives you a chance to find design holes without betting production on a demo.

Run the same task through the workflow until the request schema stops changing for trivial reasons. Watch for the predictable failures: planners omit a target, reviewers approve a generic description, executors need undeclared context, and logs cannot associate the approval with the call. Each failure tells you where authority still leaks across roles.

Do not measure success by how few approval clicks remain. Measure whether the executor refuses a wrong target, an unapproved revision, and an unsupported action, while it completes the intended one. A system that makes every action easy has probably made too many actions possible.

When the workflow holds up, expand one action family at a time. Keep the planner curious and the executor boring. Production automation earns trust when its refusals are as deliberate as its successes.

FAQ

What is a planning agent in an AI workflow?

Use a planner when the job needs broad reading, repository inspection, or competing design options. Keep that agent away from credentials and mutation tools. It can still produce a concrete plan, commands for a human to review, and a list of assumptions.

Can a planning agent have read-only production access?

A planning agent may call read-only services if those services contain no capability to alter production state. Treat read access as sensitive when it exposes customer data, tokens, internal topology, or deploy metadata. Read-only is a permission class, not an automatic safety label.

Does separating planner and executor agents make production changes safe?

No. The split reduces the blast radius of a mistaken or manipulated plan, but the executor can still make a bad approved change. You need narrow credentials, target validation, human review for consequential actions, and an audit record.

How should a planner hand work to an execution agent?

Make the executor's instructions a structured change request with a target, operation, parameters, expected result, rollback condition, and approval reference. Reject free-form prose as an execution contract. The executor should fail when a required field is absent or differs from the approved request.

Should every agent action require human approval?

It depends on the action and the cost of reversal. Sending a preview request to a staging service may be acceptable under a session approval, while deleting data or changing identity settings should require approval at the call that performs it. Repeated prompts for harmless operations train people to approve blindly.

Can one AI agent act as both planner and executor?

Separate identity means more than two prompts in the same chat. Run the planner and executor as separate processes with separate credentials and distinct tool inventories. The executor must not inherit the planner's environment variables, cached tokens, or shell history.

How do I restrict an execution agent to one environment?

Create separate machine identities for each environment and scope them to the smallest real task. A production executor should not receive a credential that can alter staging, and a staging executor should not be able to reach production by changing a URL parameter. Test the denial paths, not only the happy path.

What should an audit trail for AI agent changes include?

Record the planner's artifact, the approval decision, the executor identity, the exact request, the returned response, and the final outcome. Timestamps alone do not prove much if an administrator can edit the event stream later. Use append-only or cryptographically linked records when the audit trail must settle disputes.

Who is accountable when an execution agent makes a bad change?

A human should own intent, risk acceptance, and the decision to authorize an irreversible or high-impact action. The executor can carry out a bounded operation after that decision. If the organization cannot name the accountable human for a production change, it has not solved the approval problem.

What is the safest first use case for an execution agent?

Do not begin by granting an agent broad production access. Pick one repetitive, reversible action with a known owner, define a narrow request schema, and force the executor through a non-production environment first. Expand only after the logs show that the requests, approvals, and outcomes match your expectations.

Sallyport

Sallyport runs API calls and SSH commands for your AI agent. The keys stay in a local vault on your Mac; you approve each run and every action lands in a sealed journal.

© 2026 Sallyport · Open source under Apache-2.0 · Oleg Sotnikov