Every DAO framework has to answer one question about its proposals: what is a passed vote allowed to do? There are two families of answer. In an arbitrary-execution design, a proposal carries raw transaction data and the governance contract will execute whatever that data says. In a typed design, a proposal selects from a fixed list of action kinds that the contract itself understands, and nothing outside that list can be expressed at all.
This axis is rarely stated explicitly, but it decides more about a DAO's real risk profile than quorum or threshold settings do. It determines whether a voter can know what they are voting for by reading the ballot, and it is the axis on which the most expensive governance failures turn.
What arbitrary execution actually looks like
The dominant Ethereum pattern is arbitrary. OpenZeppelin's Governor declares its entry point as propose(address[] targets, uint256[] values, bytes[] calldatas, string description). The first three arrays are the proposal; the fourth argument, the human-readable description, is the only part a voter reads and the only part the contract does not enforce. Compound's Governor Bravo takes the same shape with an extra signatures array and caps a proposal at ten actions (proposalMaxOperations = 10).
Nothing in either contract relates the description to the calldata. A proposal titled “Fund the grants program for Q3” and a proposal that drains the treasury are, to the governance contract, the same kind of object: a list of addresses, values and byte strings. Verifying that the two agree is off-chain social work, and it is work almost nobody does — decoding calldata against an unverified target contract is a skill most delegates do not have and most voters do not attempt.
The upside is real and is why the pattern won. An arbitrary-execution DAO can do things its designers never anticipated: call a protocol that did not exist when the governance contract shipped, adopt a new standard, or wind itself down. A typed DAO can only ever do what its enumeration allows, and widening the enumeration means changing the contract.
The failure mode: a proposal that changed after it passed
In May 2023 Tornado Cash lost governance control of itself to a proposal that looked like an earlier, legitimate one. Per Halborn's analysis, the attacker submitted a proposal that claimed to implement the same logic as a previously approved proposal, but whose deployed contract carried extra self-destruct logic. Voters, comparing it to the version they already trusted, passed it. The attacker then destroyed the proposal contract and redeployed different code at the same address. When the proposal executed — via delegatecall, so in governance's own storage context — it minted the attacker 1.2 million votes against roughly 70,000 legitimate ones, and about 483,000 TORN (~$2.17 million) was drained.
The instructive part is not the self-destruct trick. It is that every governance rule worked correctly. Quorum was met, the vote was honest, the timelock elapsed, and the contract executed exactly the bytes it was given. The failure was entirely in the gap between what the proposal said and what its calldata did — the gap that arbitrary execution creates by design and that no voting parameter can close.
Narrowing the surface without giving up generality
Most of the tooling built around Governor over the past few years is, read charitably, an attempt to recover legibility that arbitrary execution gave away.
- Timelocks do not make a proposal legible; they buy time for someone else to notice. They convert an execution problem into a monitoring problem, which is an improvement only if somebody is monitoring.
- Scoped permissions. Zodiac's Roles Modifier sits between a module and a Safe and, in its own description, lets holders of a role “unilaterally make calls to any approved addresses, approved functions, and using approved parameters.” That is typing applied at the permission layer rather than the proposal layer: the calldata stays arbitrary, but the set of calls that will be accepted is enumerated in advance.
- Simulation. Front-ends increasingly simulate a proposal's calls against forked state and show the resulting balance changes. This is the closest thing to making calldata readable, and it is why proposal tooling and governance data services matter more than their unglamorous positioning suggests.
- Procedural checks. DAOhaus's published vote-decision flow for autonomous agent members is the clearest written statement of the problem: it classifies every proposal into one of six buckets, of which the last is plainly “arbitrary execution”, and requires the agent to ask “does action data match proposal text?” before voting. When a procedure has to name that question explicitly, the design has already conceded that the answer is not obvious.
Why typed designs are rarer than they should be
Typing a proposal system has an obvious cost: the enumeration is a ceiling. Adding a genuinely new action means changing the contract that holds the state, which is exactly the kind of change a DAO is slowest at making and most reluctant to authorise. Frameworks that expect to serve arbitrary future protocols therefore default to arbitrary calldata, and the burden of understanding a proposal shifts to the voter.
The counter-argument is that most DAOs do not need generality. A treasury that in practice only pays contributors, buys assets, sells assets and votes in other organizations has a small action vocabulary, and paying for unlimited expressiveness it never uses buys nothing but attack surface. The right question is not “which design is safer” but how wide the DAO's action vocabulary genuinely is — and, if it is narrow, whether the framework will let it say so on-chain. Most will not. This is one of the places where framework choice, made once and early, quietly sets a ceiling on how minimal a DAO's trust assumptions can ever become.
The enumeration is an ABI, so it has a version
The generality trade-off above is the one this axis is usually argued on. There is a second, quieter one that only typed designs face: a fixed set of action kinds is an interface, and interfaces change. Arbitrary calldata has no version to break – a Governor proposal is bytes, and bytes mean whatever the target contract makes of them. A typed proposal is a discriminator plus typed fields, so retiring or renumbering a kind is a breaking change to every client that builds proposals, every indexer that reads them, and every archived proposal that already carries the old number.
The dangerous handling is silent reinterpretation. If a removed discriminant is recycled for a new action, a proposal encoded by an old client still decodes cleanly – into the wrong action. Nothing aborts, nothing looks malformed, and the mismatch surfaces only at execution, which is precisely the gap between stated intent and performed action that typing was adopted to close. The safe handling is the opposite: make the stale encoding fail loudly and early, at creation rather than at execution.
Frameworks solve this a layer up, at the plugin rather than the action. Aragon OSx versions each plugin as a release and a build, and its documentation draws the line in exactly these terms: “Releases contain breaking changes, which are incompatible with preexisting installations”, and updates across releases are not possible at all – a DAO must uninstall the old plugin and install the new one, while builds are reserved for compatible upgrades. That is the same instinct expressed as deployment policy: an incompatible interface change is made impossible to apply by accident.
The practical question for anyone evaluating a typed DAO is therefore not only how wide its action vocabulary is, but what its enumeration does when it narrows. A design that reuses freed discriminants is trading a legibility guarantee it advertised for a byte of encoding space it does not need.
How Caper approaches this
A caper sits at the typed end, and deliberately. An executive proposal carries a kind discriminator, and the logic package live at the time of writing recognises six, five of which execute: pay an address from the treasury (PAYOUT), buy another caper's token (INVEST), sell one back down its curve (DIVEST), rewrite the mutable metadata of the caper's own token (METADATA), or – on the platform's own caper only – swap the shared logic tier that every caper runs (UPGRADE) – plus a sixth that executes nothing at all, DEBATE, a ranked position in an argument that is terminal at the tally. There is no calldata field. A proposal cannot encode a call the contract does not already know how to make.
Read on-chain again on 13 September 2026: the CaperMain the live CaperRegistry (component_tdx_2_1cr7l4u0c…) resolves to today (component_tdx_2_1cq64v5tw…, published by the genesis redeploy of 11 September 2026) exposes twenty-two methods and one instantiation function – the same count as component_tdx_2_1cr8psn5… after the redeploy of 7 September and component_tdx_2_1czm6lcn… on 5 September. Neither redeploy moved a method: the first retuned the governance windows and the price measurement, and the second changed what existing methods do – exit, for one, now takes amounts rather than buckets – without adding or removing one. Six of the methods are executors – one per executable kind, plus the second leg of the upgrade – and none of the twenty-two is a generic call entry point of any shape. That count is worth stating because it did not move when the enumeration did: DEBATE, the kind added on 5 September 2026, executes nothing, so it needed no executor, and a diff of the method surface would not have shown the change at all.
The consequences run both ways, and both are worth stating. A reader can tell what a proposal does from its kind and its typed fields, so the Tornado failure mode – a description that does not match the payload – has no room to occur. But the enumeration really is a ceiling: adding a kind needs a new logic package, not merely a passed proposal. Caper's answer to that is the UPGRADE kind, which swaps the logic tier and is itself a proposal subject to the same resolution rule.
Its enumeration has now moved in both directions, which is where the versioning question above stops being hypothetical. The discriminants run 0 PAYOUT, 1 INVEST, 3 UPGRADE, 4 DIVEST, 5 METADATA and 6 DEBATE: the gap at 2 was VOTE, an action that let one caper cast its treasury’s ballot inside another, and it died with the escrow voting system it belonged to. The number was retired rather than recycled, and the creation-time whitelist – 0 | 1 | 3 | 4 | 5 | 6 – still names VOTE in its abort message, so a proposal built against the old interface fails at creation with a message that says why, instead of decoding into some later kind. That is the loud-and-early handling, chosen at the cost of a permanently unusable discriminant.
The widening is the more instructive half, because it puts a price on the ceiling described above. DEBATE was written into the contract source on 30 August 2026 and could not be used for six days. The app half shipped first, so the create-proposal form, the published OpenAPI document and the MCP tool manifest all advertised a kind the deployed component refused, and a proposer who picked it got an abort naming the long-retired VOTE. Closing the gap took what a typed design always requires and an arbitrary-execution design never does: a newly published logic package and a governed swap of the registry’s current_main, both completed on the morning of 5 September 2026. A Governor DAO wanting the same thing – a proposal that is voted on and deliberately executes nothing – expresses it with an empty targets array and deploys nothing. That is the trade in one worked example: legibility bought at the price of a contract release, and a six-day window in which the interface and the ledger disagreed about what a proposal was allowed to say.
One thing this page's axis does not anticipate: a caper's proposal has to be legible twice, to voters and then to the market. It is a ranked ballot over two to five options – at least one a “Do nothing”, at least one an answer other than it – and clearing that ballot only opens the optimistic market window; the winning action executes if the token's time-weighted price across the window holds at or above the trailing average locked at the trigger (optimistic governance is the wider family). Typing therefore does its work on both readers: a voter ranks options whose payloads the enumeration has already constrained, and a holder who wants to veto one is selling against an action they can read rather than a title they have to trust.