Red is a verdict. Your agent needs a direction.
An AI agent is about to sign a transaction. It asks a security check first, and gets back one word: red. Then what?
Most pre-sign checks stop there: approve or deny; approve, warn or block; a score. That verdict matters, because it keeps the agent from signing something dangerous. But an agent that only hears "stop" does one of two things: it tries the same request again, or it gives up and hands the problem back to a person. m4lkxx's piece on harnesses, loops and graphs puts it in one line: red is not feedback, it is a verdict. Feedback says what is wrong, where, and what to try next.
So we changed what presign-guard answers. Since 3 November 2026, every orange or red reason it returns carries a fix: what to change to get to green. As far as we could find, it is the first pre-sign check for AI agents that does this.
What the answer looks like
A real Uniswap v4 swap on Base with no minimum anywhere in the transaction (no amountOutMinimum, no TAKE_ALL minimum, no SWEEP amountMin), checked with POST https://presign-guard.fizzl.eu/v1/check. Shortened: the info reasons and the subject are left out.
{
"verdict": "orange",
"fixable": true,
"reasons": [{
"code": "V4_SWAP_NO_LIMIT",
"severity": "orange",
"fix": {
"expected": "a minimum you receive",
"saw": "amountOutMinimum 0 and no other check on what arrives",
"change": "Set amountOutMinimum to your quote minus your slippage (for example 0.5%); a TAKE_ALL minAmount or the router's SWEEP amountMin works too.",
"retry": "change",
"set": { "amountOutMinimum": "quote × (1 − slippage)" }
}
}]
}
Each fix has expected (what a safe request looks like), saw (what this one does), change (what to do), set (the field to set, where there is one) and retry. The whole answer gets fixable: true when every orange and red reason can be changed away.
Four kinds of retry
| retry | Meaning | Examples |
|---|---|---|
change | The request can be rebuilt into a green one. Apply the change and check again. | An exact approval amount instead of unlimited; an expiry within the hour; the seller's own payTo and amount from its 402; the real token instead of a copy; a swap minimum. |
elsewhere | Nothing in this request fixes it: the contract, pool or site is the problem. | An unverified contract; a Uniswap v4 hook that can block withdrawals or be upgraded; a look-alike site. |
ask | Only the user can decide. | A smart account to delegate the wallet to (EIP-7702); a token's own transfer tax; a brand-new wallet as spender. |
never | Don't sign. No version of this is safe. | A sanctioned address, a phishing site, a signature that lets a plain wallet take your tokens, a honeypot token. |
A retry loop for your agent
let req = buildRequest();
for (let i = 0; i < 3; i++) {
const r = await check(req); // POST /v1/check
if (r.verdict === "green") return sign(req);
if (!r.fixable) return askUser(r); // elsewhere, ask or never
req = applyFixes(req, r.reasons.map((x) => x.fix));
}
return askUser(); // the same result twice: stop retrying
Two rules make it safe. The fix never changes the verdict: every rebuilt request is checked again in full, and only green gets signed. And when the same request comes back the same way twice, stop: more attempts won't help, the missing piece is something the agent can't change.
And green? Read what it covered
The same problem has a mirror image. A green check is easy to over-read: it means nothing wrong was found in what was checked, never more. Addy Osmani put it in one line in his piece on critical thinking with agents: a green check means one thing. What did it check? What did it not check?
So every presign-guard answer now also says that, green or not:
"checks_complete": true,
"checked": [
{ "check": "permissions", "what": "What signing grants: allowances, transfer rights and payments…" },
{ "check": "reputation", "what": "GoPlus address and contract security for 2 addresses…" },
{ "check": "sanctions", "what": "The addresses against the OFAC SDN sanctions list." }
],
"not_checked": [
{ "check": "simulation", "because": "not_sent", "send": "from",
"why": "No from: the transaction wasn't simulated, so hidden approvals or transfers inside the call weren't looked for." },
{ "check": "site", "because": "not_sent", "send": "origin", "why": "No origin: …" }
]
Each not_checked item says why: unavailable (a source didn't answer this time, and checks_complete turns false), not_sent (the request lacks a field; send names it, so the agent can ask again for a fuller check) or out_of_scope (something presign-guard doesn't check, and what does). An agent that reads it can treat a green with checks_complete: false as a green on less than usual: ask again, or keep the amount small.
x402 Doctor's pre-payment check, for the seller side, answers the same way. Its "go" lists what it read and leaves two things out of scope every time: whether the seller answers your own request (Doctor doesn't pay), and what your agent signs to pay (that is presign-guard's job).
What it covers
- Allowances and signatures: exact amounts instead of unlimited, an expiry within the hour, the canonical Permit2 contract, one NFT instead of a whole collection, personal_sign or typed data instead of
eth_sign. - Tokens: the real contract for a copy (
set.token); honeypots and scam airdrops arenever. - Recipients and sites: the address a look-alike imitates (
set.to), or the real site. - x402 payments, checked against the seller's own 402: its payTo, amount, asset, timeout and EIP-712 domain.
- Uniswap v4: the swap limit to set (
amountOutMinimumoramountInMaximum) and withdrawal minimums; a hook problem iselsewhere. - EIP-7702: a delegation valid on every chain gets its chain id; a delegate the user didn't choose is
ask.
It runs on every chain presign-guard checks: Ethereum, Base, Arbitrum, Optimism, Polygon, BNB Chain, Avalanche, Monad, X Layer and Robinhood Chain, plus Solana, the XRP Ledger, Algorand, Stellar and NEAR, over HTTP and over MCP.
Try it
The quick verdict is free and returns only green, orange or red:
curl -s https://presign-guard.fizzl.eu/v1/check/quick -H 'content-type: application/json' \
-d '{"type":"approval","chainId":8453,"token":"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913","spender":"0x6ff5693b99212da76ad316178a184ab56d299b43","amount":"115792089237316195423570985008687907853269984665640564039457584007913129639935"}'
The same body at POST /v1/check returns the reasons, the details, the fix and what was and wasn't checked for $0.01, paid by the agent itself over x402 (USDC on Base, Solana, Monad, Algorand or Stellar, RLUSD on the XRP Ledger, USD₮0 on X Layer) or MPP. No account, no API key. Agents connect over MCP with:
claude mcp add --transport http presign-guard https://presign-guard.fizzl.eu/mcp
More on the check itself: presign-guard.fizzl.eu and its llms.txt.
Questions
What should an AI agent do when a pre-sign check says red?
Not retry the same request, and not sign. Read each reason's fix: with retry change, rebuild the request as the fix says and check it again, signing only on green; with elsewhere or ask, use another contract, pool or site, or ask the user; with never, don't sign at all.
Does the fix change the verdict?
No. The verdict is the check's. The fix is advice for the next attempt, and the rebuilt request goes through the full check again.
Does a green verdict mean the transaction is safe?
It means nothing wrong was found in what was checked. The answer lists checked and not_checked: what ran, what couldn't run this time, which fields were missing and what is out of scope.
What does it cost?
The quick verdict is free. The full answer with the reasons, the details and the fix is $0.01 per check, paid by the agent over x402 or MPP.
How we checked "first"
Before writing "as far as we could find, the first", we looked at what other pre-sign and transaction checks for agents return, from their public pages on 3 November 2026: approve or deny with a simulation and balance changes, a verdict with threat data, or approve, warn or block with a score and an explanation. None of them, as described, returns what to change to get an approval. If you know one that does, tell us at frits@fizzl.eu and we will correct this page.