Cordon treats one agent task as a database-style transaction. Local file changes run in a shadow copy, outbound actions wait in an outbox, and a validator checks the full chain of tool results before anything commits. In the authors' 45 risky multi-tool workflows it blocked every unsafe effect before commit; adapters built from existing per-call defenses blocked 14. On two benign benchmarks the agent's scores did not drop.
An agent runtime gives the model a set of tools. Today it checks each tool call on its own, runs it against the real system, and puts the result into the model's context. The check sees one call at a time.
The authors give an incident-response example. The agent reads application logs that contain an API key. It summarizes the failures, writes a remediation note, and prepares a Slack message for the on-call channel. Each call is reasonable on its own. The unsafe behavior exists only in the chain: a secret enters a summary, and the summary leaves in an external message. The authors call this a cross-step semantic side effect. The evidence needed to block it is spread over several earlier steps.
Each current defense sees a partial view. Tool-call gates and permission prompts (the paper names Claude Code, Codex CLI, the OpenAI Agents SDK, and LangChain) see one call but not where its arguments came from. A sandbox protects the host but does not decide what reaches the real workspace. An output filter inspects the outbound text, but a summary of a secret no longer contains the secret string. A filesystem snapshot restores files but cannot recall a sent message.
Cordon groups everything one task does into one semantic transaction. "Semantic" means the transaction tracks how tool results depend on each other in meaning, not only which bytes changed on disk. Like a database transaction, it does the work in a private copy, checks the result, and then either makes it real or throws it away. It has three phases:
Prepare. The runtime accepts each tool call as a typed intent: a declaration of what the call reads, writes, or sends. Local writes go to a private, reversible copy of the workspace, called shadow state. External actions go to a holding area and are not sent.
Validate. The runtime checks the whole transaction as one unit: where every result came from, the permissions the agent holds, the held external actions, and the current policy.
Commit or abort. On pass, the runtime copies the local changes into the real workspace and sends the approved actions. On fail, it discards the local changes, cancels the held actions, and writes an audit record.
The transaction sorts objects into three classes. A result is any value a tool returns or the agent derives. A mutation is a local write, delete, or configuration change; the runtime can undo it, so it may run ahead and roll back later. An effect is an action that shows information to the outside, such as a message, an API call, or the final reply to the user; the runtime cannot undo it, so it stays held until validation passes.
A lineage graph links these objects. Each edge records that one result was summarized, decoded, or extracted from another, that a result justified a mutation, or that a result supplied the content or destination of an effect. Lineage is broader than string matching: a result counts as secret-derived if it came from a secret, even when the secret text is gone.
The validator enforces nine invariants before commit. Examples: no lineage path may connect a secret-derived result to an unauthorized external destination; every external effect stays held until commit; an abort must restore every reversible file; a human approval applies to one object, one action, one destination, and one time window.
Cordon sits at the tool-dispatch point of the agent framework, where the tool name and arguments are known but no side effect has happened yet. A wrapper replaces the framework's tool-invocation function and forwards each call to a transaction manager; the model loop stays unchanged.
The shadow-state engine applies file writes, deletes, and command-produced changes to a view that only this transaction can see. In the prototype the view is a manifest: a directory of staged file contents plus a JSON map from workspace paths to staged files or delete markers. To run a command, Cordon copies the working tree into a temporary directory, overlays the staged files, and runs the command in a sandbox with network blocked by default. It then records the created, modified, and deleted paths as staged writes.
The effect outbox holds each external action with its destination, content, lineage, approval state, idempotency key, and release status. An idempotency key is a unique ID that lets a receiver recognize a repeated send as a duplicate. This is the transactional outbox pattern from microservices: record the action first, send it in a separate step. Commit has two parts, tracked by a commit manifest and a recovery log: local changes are copied to the workspace only if the manifest still matches the staged state, and external release is recorded separately. After a crash, the runtime resends a dispatched effect only when it has idempotency evidence that the first send did not complete.
The prototype is about 14,400 lines of Python. The authors integrated it into a commercial agent runtime they call Agent-H. Porting to another Python framework needs the tool wrapper plus a specification that maps each tool to its declared reads, writes, effects, and approval requirements.
The containment test is a suite of 45 risk-bearing workflows the authors built themselves. It crosses nine defense-boundary categories with five risk families (sensitive writes, writes hidden inside commands, session secrets sent externally, derived secrets leaked through commands, and deletes that touch many files) across six task domains. The plain agent committed the unsafe effect in all 45. Adapters that reproduce existing defenses blocked 14 before commit, missed 26, and caught 5 only after the effect had landed. Cordon blocked all 45 before commit.
Mediation has a cost. With approval wait included, mean task time rose from 25.55 seconds to 31.35 seconds under an approve-all policy, a 22.7% increase. Under a reject-on-risk policy it fell to 23.64 seconds, because rejected transactions end unsafe runs early. For the same reason, all mediated modes cut token use by 23.6% to 28.4%. With approval wait excluded, the transaction machinery took 22% to 23% of measured time; model latency took about 63%.
Rollback took 4.17 milliseconds (median) on five repeatable failed agent runs, left zero residual file differences, and passed all 15 resume checks. Git restore and reset left a median of 73 stray differences and passed none, because untracked files stay behind. Git reset plus clean passed 12 of 15 and failed the case where file permissions had changed.
On benign work, Cordon scored 90.0% against 87.5% for the plain agent on a τ-bench subset, and both scored 100% on a Terminal-Bench subset. The authors call both differences measurement noise. The guarantees cover only operations the runtime can mediate: a plugin with side effects the runtime cannot observe gets audit metadata, not rollback.
Split your agent's actions into two classes. Reversible local mutations can run ahead. Irreversible external effects (Slack posts, emails, pull-request comments, HTTP POSTs to third parties) wait in a buffer until the task ends, then pass one check as a group. Most harnesses can add this buffer without changing the model loop.
Track tool results as handles with provenance, not as strings. Tag any result that came from a secret-bearing source and carry the tag through summaries. A validator that sees the tag can block a leak that no regex will match.
Give every outbound effect an idempotency key and a release record before you send it. Scope human approvals to one object, one destination, and one time window, not to a tool name.
If git is your rollback, know its limits: it misses untracked files and permission changes. A staged-manifest overlay like Cordon's restores exactly what the transaction touched.