Most prompt-injection defenses try to catch the attack. This one arranges the system so the attack never reaches the part that can act on it. The idea fits in a sentence: never feed raw untrusted content to the model that makes decisions or holds tools. Read it somewhere else first, turn it into a validated structure, and let the privileged step reason only against that structure.
It's the same principle behind Dual-LLM and CaMeL — separate the reading from the deciding — but without the second privileged interpreter or the information-flow engine. You add a pass and a schema. That's the whole trade: less infrastructure, a little less protection, and a control most teams can actually ship this quarter.
The lens: this attacks leg two
Simon Willison's lethal trifecta is the map. A prompt injection turns catastrophic only when an agent simultaneously has access to private data, exposure to untrusted content, and an external channel to send it out. Remove any leg and the injection has nowhere to go.
Two-pass, plan-then-execute, and action-selector all attack leg two — exposure to untrusted content — but at a very specific point: the decision step. The model that plans and calls tools never reads the attacker's text. It reads a schema-shaped intermediate that a separate, powerless pass produced. The injection still gets written; it just lands upstream of everything that could make it dangerous.
Pass 1 extracts, Pass 2 decides
Split the job into two passes. Pass 1 reads the untrusted input — a retrieved document, a tool result, a web page — and does one narrow thing: extract it into a validated, structured intermediate. It has no tools and no secrets. Pass 2 — the prose, the plan, the tool calls — runs only against that validated intermediate. The attacker's string never reaches the privileged reasoning step, because by the time Pass 2 runs, the raw text is out of scope.
The schema is not decoration — it is the boundary. Constrain fields to enums, types, and lengths. A validated intermediate that says sentiment: "negative" and amount: 4200 can't carry an instruction. A field that says notes: "..." and accepts arbitrary text can carry anything the attacker wants.
Plan-then-execute and action-selector
Two variants push the same idea further. Plan-then-execute commits the agent to a plan before any untrusted content is in scope, then executes the fixed steps. A later injection can't rewrite a decision that was already made — the control flow is settled before the data flow begins.
Action-selector narrows the vocabulary. Instead of open-ended tool calls, the decision step picks from a fixed menu of pre-approved actions. Even a reasoning step that has been nudged off course has no way to express the attacker's goal — it can only choose something already on the list. Fix the plan, then constrain the actions, and you've removed most of the room an injection needs to operate.
The honest limitations
We are not going to oversell the lighter cousin. This mitigates instruction-override and directive-injection at a medium cost, but it is not free of exposure. Three things matter in practice:
- Pass 1 is still exposed. The extraction pass reads the attacker's text, so it can be coaxed into mislabeling data — dropping an instruction into a field that Pass 2 trusts. The schema and its validation are what hold the line, not the model's good intentions. Constrain hard and reject anything off-schema.
- A loose schema is a free-text hand-off in disguise. The most common failure is a single wide
summaryornotesstring that carries the whole document — attacker prose included — into Pass 2. That reunites the passes and re-opens the hole. The security lives in how tightly the structure is constrained. - Plan rigidity is the trade. Fixing the plan before reading content is the defense, but it also limits adaptation. Genuinely dynamic tasks tempt teams to re-read untrusted content mid-plan, which quietly puts attacker text back in the decision loop.
What we tell clients
If a full CaMeL-style interpreter is more than your team can justify right now, start here. You get the same principle — untrusted text stays out of the model that decides — for a fraction of the build. Add an extraction pass, define a tight schema, run every decision against the validated intermediate, and prefer an action allow-list over open-ended tool calls.
Then treat it as one layer, not the whole stack. Because Pass 1 is still exposed, pair this with least-privilege tooling and egress control to remove the exfiltration leg, and keep a human on the high-risk writes. When a mislabeled field slips through — and eventually one will — you want the plan already fixed and the action menu already short, so there's nowhere for the injection to go.
This is the lighter-weight cousin of privilege separation in our Defense Architecture catalog. The pattern page for Plan-Then-Execute & Action-Selector has the trust boundaries, threat surface, and architecture diagram in full.