Every trading agent that reads news, filings, or MCP tool descriptions is reading text it doesn't control. In most products a successful prompt injection is an annoyance. In trading it pays the attacker directly: force a buy into whatever it already holds, strip a stop-loss, or exfiltrate account state through an order comment. Detecting injected text is an arms race you lose the moment a judge invents an attack outside your denylist. CLEANROOM doesn't try to detect injections. It makes them structurally unable to matter. Three planes, connected only by typed data, never text: - Perception (untrusted): a Gemini call with zero tools bound to it. Its only output is a Pydantic-validated struct — symbol, sentiment, extracted claims. Fully compromised, it can still only hand back a lying JSON object. It cannot place an order because it has nothing to place one with. - AIRLOCK: deterministic Python. Validates the struct, tags provenance, quarantines anomalies. No model calls. - Execution Controller: deterministic Python, sole path to submit_order. Enforces position sizing, stop-loss presence, a symbol whitelist, and daily notional caps against real Alpaca market data — never against raw text. VETO is a code decision, not a model judgment. Second artifact: a named prompt-injection corpus (direct instruction, fake system authority, MCP tool-description poisoning, sizing manipulation, stop-loss removal, symbol spoofing, exfiltration, memory poisoning, obfuscation) run as a reproducible ablation. Hardened: 0/15 attacks land. Same agent, isolation removed: 5/15 land. Benign false-positive rate: 0/4. 53 unit tests pass. Stack: Python/FastAPI/Pydantic v2, Gemini 2.5 Flash, alpaca-py on Alpaca paper trading, Next.js dashboard, Rich TUI for the autonomous news daemon. Stated, not buried: the "structural isolation" bench check is a source-grep, not a type-level proof, and the corpus is hand-built against the classes it targets.
Category tags: