Agent-tool reversibility is 93% decidable from the signature — the 7% that isn't is your shell
We labeled 330 real agent tools (ToolEmu, two models, Cohen's kappa 0.82): reversibility is ~93% decidable from the tool signature. The undecidable 7% are universal executors — shell, SQL, eval — and that's where memory-poisoning routes irreversible harm. inspeximus 1.2.0 ships the gate.
Reversibility gates are a standard last line of defense for tool-using agents: before an action runs, check whether it can be undone, and pause the ones that cannot. Severity graders like the Action-Graded Severity Scale (arXiv:2607.07474) encode this by fixing each tool's reversibility in a lookup table — delete_file is irreversible, read_file is not. That table only works if reversibility is actually decidable from the tool. Nobody had measured whether it is. So we did.
We labeled every tool in the ToolEmu corpus (38 toolkits, 330 tools in the released asset; the paper reports 36/311) for whether its reversibility is decidable from the signature alone, using two independent models as labelers and reporting their agreement.
Reversibility is 93% decidable from the signature — and the 7% that isn't is your shell
The per-tool assumption mostly holds. Reversibility is decidable from the signature for about 93% of real tools (Cohen's κ = 0.82, "almost perfect" agreement). The interesting part is the residual. The ~7% of tools where reversibility is not decidable from the signature are all the same kind: verb-polymorphic universal executors — a shell that runs arbitrary commands, an arbitrary-SQL runner, a code eval. The same Terminal.Execute is both ls (reversible) and rm -rf (irreversible); the reversibility lives in the argument, not the tool. A per-tool label is meaningless for them.
| what we measured (ToolEmu, 330 tools, 2 labelers) | value |
|---|---|
| reversibility decidable from the signature | ~93% (κ = 0.82) |
| the undecidable residual (universal executors) | ~7% |
| universal executors as a share of the corpus | ~0.3% (a lower bound) |
| dedicated-irreversible tools (a per-tool gate catches these) | ~16% |
The reach of a universal executor is set by containment, not by the tool
An obvious follow-up: how much harm can that one shell actually reach? The honest answer is that it depends entirely on the environment, and that dependence is easy to get wrong. Under an isolated shell — no network, no ambient credentials — the shell reproduces about half of the local irreversible harms (delete, corrupt, overwrite) but essentially none of the external ones (send an email, move money, post publicly), because it has nothing to reach them with. Give the same shell a realistic production environment — curl, injected API keys, installed CLIs like aws and git — and external reach jumps from ~0% to ~66%.
So the thing that bounds a memory-poisoned agent's irreversible external harm through a universal executor is executor containment, not a per-tool reversibility flag. We want to be precise about what is and isn't new here: the local-vs-external split of harm is not our idea — it is exactly the scope × reversibility grid of arXiv:2607.07474, measured on a different corpus. What we add is the measured decidability rate (the 93%, with a κ), the isolation of universal executors as the residual class, and the isolated → networked reach flip.
What we shipped: the universal-executor gate in inspeximus 1.2.0
A memory-poisoning attack does not need to forge provenance or reach for a flagged tool. It only has to steer the arguments of a benign-looking universal executor toward an irreversible outcome. So inspeximus 1.2.0 adds an opt-in gate that refuses to trust a per-tool reversibility label for these tools:
```python from inspeximus import Inspeximus, is_universal_executor
is_universal_executor("Terminal.Execute") # True — reversibility undecidable from the signature is_universal_executor("SendEmail", ["to","body"]) # False — a dedicated, signature-decidable tool
m = Inspeximus()
m.spend_irreversible(ids, tool="Terminal.Execute") # allowed = False
m.spend_irreversible(ids, tool="Terminal.Execute", contained=True) ```
tool=None keeps the 1.1.0 behavior byte-for-byte, so the gate is opt-in. It is honest about its own limits: the detector is a heuristic, and contained is a caller assertion the library cannot verify — it forces the declaration, it does not enforce the sandbox.
The honest limits
Two models as labelers measure shared model priors, not ground truth; on the networked-external question they agree only ~0.32, which is itself the finding — that reach is genuinely CLI- and credential-dependent. ToolEmu under-weights the shell and code-interpreter tools now standard in production agents (OpenAI's code interpreter, Claude computer-use), so the ~0.3% prevalence of universal executors is a floor, not a typical rate. And the headline here confirms the consensus rather than overturning it: per-tool reversibility metadata is sound for most tools; it is the universal-executor slice, and executor containment, that needs the attention.
The full run — REPRODUCED, with a runnable probe — is on the Crucible.
FAQ
Is reversibility of an agent action decidable from the tool? For about 93% of real tools, yes (measured on ToolEmu, 330 tools, Cohen's κ = 0.82). The ~7% where it is not are verb-polymorphic universal executors — shell, arbitrary SQL, code eval — whose reversibility is set by the argument, not the tool.
Why can't a per-tool reversibility gate stop shell-based harm? Because the same shell tool is both reversible (ls) and irreversible (rm -rf) depending on its arguments, a single per-tool label is unsound for it. The gate has to move to the argument level, and the external reach has to be bounded by containment.
What does inspeximus 1.2.0 add? is_universal_executor() to detect these tools, and spend_irreversible(tool=, contained=), which denies an uncontained universal executor outright instead of trusting a per-tool reversibility flag. It is opt-in and backward-compatible.