I opened the generated execution policy JSON and the first thing I saw was the kind of permission shape that looks fine at 2% test size and insane the second there is real liquidity behind it.
The agent had route resolution, bridge state, vault target, signer path, and a write policy that was basically pretending “contract_call” is a normal permission. It is not. It is the permission creep field. The one that starts as deposit testing and later becomes the place where someone forgets to lock the selector, widens the IAM role, adds retry logic, and suddenly an autonomous agent can do more than the route ever needed.
I only wanted OctoClaw to touch one bridged asset, one approved ERC 4626 vault, one function selector, one capped amount, with manual review if gas jumped or the token mapping came back weird. Not a generic router call. Not a strategy executor role with a friendly name. Not direct signer access because “the model already knows the route.”

The selector was the whole fight.
deposit() was fine. In raw policy terms, that meant allowing 0xb6b55f25 and nothing else. I did not want withdraw(), redeem(), rebalance(), helper calls, vault sweeping, auto-exit, or some later “safety” routine that gets added because a dev thinks the agent should recover from a bad fill by itself. If the agent needs anything beyond deposit to complete the route, I want it to fail loudly before funds move.
The first policy was too permissive around the signer boundary. Read market data, read bridge state, read vault state, fine. Write through the constrained wrapper only. The wrapper checks APPROVED_ASSET, APPROVED_VAULT, ALLOWED_SELECTOR, MAX_DEPOSIT, GAS_LIMIT_WEI, and whether AUTO_RETRY is false before the call gets anywhere near execution. If any of those are missing, null, stale, or filled by the agent instead of the config, the call dies.
I had to stare at that longer than I expected because the route itself looked correct. EVM Bridge had the asset landing where expected, OctoClaw had the signal, the vault accepted deposits, and the simulation returned green. That is exactly the kind of setup that makes people loosen permissions too early. Everything works, so the boundary gets treated like cleanup.
The bridge mapping is where I got more annoying. If the wrapped asset identifier does not match the approved token after bridge settlement, I do not care if the strategy is right. Block it. If the vault address resolves but the chain ID is not the one I pinned, block it. If gas estimate spikes after settlement and the agent wants to retry with a wider ceiling, block it and make me approve the retry manually. I do not want an automated recovery path turning a small route failure into wallet-level state manipulation.

ERC 4626 being standardized almost makes this worse because it tricks you into thinking the vault surface is tidy. The interface is tidy. The permissions are not. deposit and redeem sitting near each other in the same mental bucket is how you end up giving a trading agent exit capability when all you needed was capped entry.
The ugly version I left in place is simple enough to audit while tired. OctoClaw can read wide, prepare the route, and propose the deposit, but execution is dumb and narrow. Asset must match. Vault must match. Selector must match. Amount must stay under cap. Gas must stay under ceiling. Retry stays manual. Anything else gets rejected.
I am still not fully comfortable with it, which is probably the correct state to be in.
The log I wanted to see was not success. It was this:
execution_rejected | reason=cap_exceeded | selector=0xb6b55f25 | vault=approved | asset=approved | auto_retry=false | manual_review=true
Leaving it running overnight with that boundary still feels dumb, but less dumb than letting a model decide what “vault access” means.
