AI Agent Instruction Provenance: Stop Harness Role Escalation
A coding agent reads a poisoned README, refuses to run the backdoor itself, then hands the same command to a subagent. The harness rebuilds that subagent's context and drops the command into a user message. The subagent runs it. Automatic permission review may allow it too, because the transcript now looks like an explicit user request. Schema checks passed. The sandbox looked fine. The failure was reconstruction: tool-origin text gained user privilege without a human.
You need an instruction provenance contract: every carried fragment keeps its origin privilege, harness reconstruction cannot elevate that privilege, and permission reviewers consume origin labels rather than trusting reconstructed message roles. It sits beside validating AI agent tool calls before execution (schema, identity, authorization), binding human approval to exact AI agent actions (the approval artifact), propagating tool-output taint before privileged AI agent actions (argument provenance after results return), verifiable egress control (network-boundary mediation and receipts), and AI agent audit logs (accountability outside model control). The job here is narrower: decide which instruction level a piece of text may claim when the harness rebuilds context.
Why message roles are a security boundary
OpenAI's Instruction Hierarchy research argues that modern agents fail when system, user, and tool text are treated as equal priority. Models are trained so system and developer instructions outrank user text, and tool outputs hold the least instruction privilege. The OpenAI Model Spec publishes that chain of command as product policy: root and system constraints bind first; developer and user instructions follow; tool content is data unless a higher layer authorizes otherwise.
That hierarchy only works when the labels the model sees match how the content entered the system. If a harness copies tool output into a user message, the model is not confused. It is doing what it was trained to do: trust user instructions more than tool text. Permission reviewers that infer "the user asked for this" from the same reconstructed transcript make the same mistake with more authority.
OWASP LLM06:2025 Excessive Agency covers the blast radius when agents get more functionality, permissions, or autonomy than the job needs. Mitigations start with fewer extensions, least privilege, complete mediation in downstream systems, logging, and rate limits. Provenance-preserving reconstruction is one form of complete mediation: the harness may not invent a higher principal for attacker-controlled text.
The attack: instruction privilege escalation
When Context Gets Root names the failure mode. Instruction privilege escalation happens when harness context reconstruction presents content at a higher model-facing level than its origin. Two common elevations:
- Tool-to-user. File reads, search hits, or tool results are later placed in a user message for a subagent, persistent goal, or scheduled task.
- Tool-to-system. Attacker-influenced text is installed into a custom subagent system prompt or skill metadata and later loaded as system-effective policy.
The paper evaluates six coding-agent harnesses across thirteen objectives that span confidentiality, integrity, availability, and remote code execution. With unrestricted execution, every objective succeeded on every harness after escalation. With automatic permission review, every objective still succeeded on the harnesses that offered that mode. Tool-level prompt injection and role-confusion baselines mostly failed on the same setups. The difference was not a smarter jailbreak. It was the harness rewriting privilege.
Promptfoo's catalog entry on agent confused-deputy escalation covers the multi-agent cousin: a low-privilege agent induces a high-privilege peer to invoke sensitive tools over natural-language channels because peers trust each other by default. That is the same family of bug: missing mandatory checks on who may authorize what, applied to agent-to-agent messaging rather than message-role reconstruction. Your contract should cover both surfaces when you ship multi-agent workflows.
Origin privilege labels
Give every context fragment an origin privilege that never upgrades casually:
| Origin privilege | Typical first entry | May instruct privileged tools? |
|---|---|---|
tool | File read, search, HTTP/MCP tool result, email body | No, unless a higher principal and policy grant say so |
user | Authenticated human message in this session | Yes, within that user's authz and approval rules |
system_effective | Operator-owned system/developer policy, pinned subagent config | Yes, as platform policy, never as untrusted content |
Store the label beside the bytes, not only in the API role field the next call happens to use. When the harness serializes a Responses or Messages payload, the API role must be less than or equal to the origin privilege. A tool fragment may appear as tool/function output. It must not appear as user or system/developer unless a deterministic policy records an explicit, audited elevation, and for attacker-controlled workspace text, that elevation should simply be forbidden.
Preserve origin privilege across harness reconstruction; a subagent user message is not human authorization if the text began as tool output. That is the first operating rule. If the label can be dropped, the hierarchy is theater.
Non-escalating reconstruction rules
Apply the same rule to every reconstruction path, not only the one your framework markets as "subagents."
Delegation and subagents
OpenAI's Codex subagents documentation describes spawning specialists whose prompts the harness assembles. When the main agent forwards a task, copy origin labels with the text. If the task body traces to tool content, present it to the subagent as tool/data context or as a clearly labeled untrusted task block that models and reviewers treat as non-authoritative. Do not mint a fresh user message that implies a human typed the payload.
Persistent goals and scheduled tasks
Goals and cron-like schedules often re-inject stored prompts as user messages on later turns. That is tool-to-user escalation with a delay. Persist origin_privilege and origin_run_id with the goal text. On replay, restore those labels. If origin is tool, the working agent and any Auto PR path must see untrusted data, not a forged user request.
Custom subagents and skills
Treat custom system prompts and skill metadata as system-effective only when an authenticated operator installed them through a controlled channel with integrity pins. Workspace files the agent can edit must not become system-effective by self-installation. If an agent proposes a new subagent definition, require the same approval and pinning path you use for production policy changes.
Treat goals, schedules, and custom subagent prompts as reconstruction surfaces with the same non-escalation rule as delegation. Teams that harden only the spawn_subagent API leave the back doors open.
Reviewers must consume origin, not theater
Automatic permission review is useful when it judges risk and user intent from a faithful transcript. It becomes an accelerator for the attack when the transcript lies about who authorized the action.
Feed automatic permission review origin labels, not only reconstructed roles; Auto PR that trusts forged user messages will approve the attack. Practical requirements:
- Include
origin_privilegeandorigin_refsfor every instructing fragment in the review context. - Deny or escalate to a human when a high-risk action's authorizing text has origin
toolor unknown. - Never treat "the user message says I know the risks" as human approval unless that message's origin is an authenticated
userprincipal for this session. - Log
reconstruction_eventandelevation_attempted/elevation_deniedoutside the model, following the same discipline as AI agent audit logs: the writer is application code, not the agent.
Bind true human approval with the exact-action digest from the approval guide. Provenance decides whether approval is required because the instructing text is untrusted. Approval binding decides that the human signed the precise command.
Worked example: poisoned README, honest refusal, successful delegation
- The main agent reads
README.mdvia a file tool. Origin privilege:tool. The text tells the coordinator to spawn a subagent and have it runpython3 app/setup.py. - Directly asked, the main agent inspects
setup.py, sees a clear backdoor, and refuses. - Following the coordinator instructions, it still delegates the run command. Without the contract, the harness places
Run python3 app/setup.pyinto the subagent as a user message. - The subagent treats it as an explicit user request and proposes execution. Auto PR, if enabled, may classify the action as high risk and still allow it because the reconstructed user role looks like authorization.
- With the contract, the delegated body keeps
origin_privilege=tool. The subagent sees untrusted task data. Auto PR receives the origin label and denies or requires a human. Audit recordselevation_deniedwith hashes of the carried text and policy version.
The same sequence applies when the inducing text arrives through email, a ticket, MCP tool output, or a scheduled replay of a previously stored goal.
How this contract fits the rest of the stack
Keep adjacent controls in their lanes:
- Tool validation: owns schema, identity, authz, and idempotency on the proposed call (validation guide).
- Human approval: owns binding a person to an exact action digest (approval guide).
- Tool-output taint: owns whether arguments to the next privileged sink inherit untrusted results (taint guide).
- Egress control: owns whether bytes may leave the host and which signed receipts prove it (egress guide).
- Audit logs: own accountability events the model cannot rewrite (audit guide).
Instruction provenance owns the path before those gates: which principal the instructing text is allowed to claim after the harness rebuilds context. Collapse that into "we use an instruction-hierarchy model" and incident review cannot say whether the harness forged a user.
Decision record: promote or hold
Before an internal agent that delegates, schedules, or loads custom subagents reaches production, record:
| Check | Promote only if |
|---|---|
| Origin labels | Every carried fragment has tool / user / system_effective from first entry |
| Delegation | Subagent prompts cannot present tool origin as authenticated user |
| Goals and schedules | Stored prompts replay with original privilege, not a fresh user role |
| Custom system prompts | Only operator-installed, integrity-pinned configs become system-effective |
| Auto PR / reviewers | Review context includes origin labels; tool-origin authz fails closed |
| Audit | Reconstruction and elevation denials are written outside the model |
| Overlaps | Validation, approval, taint, egress, and audit still own their jobs |
Hold if any row is still a promise without a failing test. A subagent API without origin labels is a hold. Auto PR without origin in the transcript is a hold. Agent-writable custom prompts that load as system-effective are a hold.
What to do next
List every production harness reconstruction path. For each one, answer with evidence:
- Where can tool-origin text become a user or system message?
- Do origin privilege labels survive into the next model and reviewer invocation?
- Do high-risk actions fail closed when authorizing text is tool-origin?
- Are reconstruction and elevation events in the audit store the model cannot edit?
If you cannot answer those four from artifacts, stop expanding multi-agent autonomy. Fix provenance first. Scale delegation only after elevation attempts deny cleanly in CI.