The veto list: the agent decides, a human can overrule
Which calls must be surfaced, how to format the list, and how to write a rollback someone can actually run.
There's a rule in my rules file that goes like this:
User-facing copy, pricing and billing, restricting users, deleting data: go with the recommended option, but list it under "Open to veto" in the PR description and the wrap-up report, stating the choice, the alternative, and the rollback. Never bury these decisions in the change summary.
Before that rule, an agent that hit a decision a person should make did one of two things. It stopped and asked, and since I wasn't there, the task sat stuck on step three until the next morning. Or it decided on its own and mentioned "tweaked the copy" somewhere in a dozen other changes, and I found out when a user did.
Neither is great. The rule takes a third path: act first, put the decision on the table, and let a person overrule it.
Building pdd.bot over the last two days ran the whole thing end to end.
A real list
Before bed I said, "I want to see the site live tomorrow." The report Claude Code handed in the next day had a section called "Decisions open to veto," five entries. In summary:
- The hero still promises templates you can copy, but every template is marked "coming soon." The approved copy stayed as is; only the stats bar now says "6 templates (coming soon)." Alternative: change the hero to "templates on the way" and hide the "Get the templates" button.
- The repo is private, so every GitHub button on the page is hidden. Alternative: make the repo public, flip one switch, redeploy.
- Code line numbers are one shade brighter than the design, to meet the contrast standard. Alternative: revert to the design color, which fails contrast.
- Chinese text uses system fonts with no web font, so headings are a little lighter than the design. Alternative: self-host Source Han Sans, a few hundred KB more.
- Only pdd.bot is bound;
www.pdd.botisn't set up. Alternative: add www and 301 it to pdd.bot.
I didn't reply to any of them.
Under the rule, no reply means the original choice stands. The site shipped on time, and nothing waited on me.
The one I did overrule came earlier, during the copy round. The agent had put a line in the footer: "PDD stands for Prompt-Driven Development. This is an independent project, not affiliated with any brand, company, or individual using the same name." It was on the veto list too. When I saw it on the live site, I replied, "Let's not say any of this." The agent removed it, opened a PR, merged, redeployed, and marked that entry in the decision log as settled by me.
The work keeps moving, the person keeps the right to say no, and saying no takes one sentence. That's the point.
Which decisions go on the list
Not everything. Variable names and how to split a function are the agent's call; listing them buries the ones that matter. My rule names four categories: user-facing copy, pricing and billing, restricting users, and deleting data.
What they share: they touch users directly, there's no technically correct answer, and they're product calls. The agent can make a reasonable one, but a person should have the final say.
The five entries above fit that bar. A promise on the home page, how heavy the headings look, whether GitHub shows up at all: users see every one of them.
Deleting data needs a caveat. The agent can write the deletion code and list it, but running it against production waits for explicit approval. Once data is gone, there's nothing left to veto.
Three things per entry
The choice, the alternative, the rollback.
The usual shortcut is "improved the error message." The reader can't tell what it says now, so they can't decide whether to change it back. Each of the five entries above names a concrete choice and a real alternative, not filler like "or leave it as is." A good alternative shows where the trade-off is: entry 3 is "matches the design" versus "easy to read," side by side.
The rollback is the part most likely to be vague. "Can be changed back any time" isn't one. Get down to the file, the flag, or the command. For entry 3, the rollback is setting --code-gutter back to #6E7782 in app/globals.css and redeploying. For the footer line, it's reverting that merge commit and redeploying.
If you can't write concrete rollback steps, the decision shouldn't be made first. A notification already sent or money already charged can't be taken back. Ask before doing those; they don't belong on a veto list.
Whether the agent can act first depends on whether it can be undone.
Where the list goes
The PR description and the wrap-up report, as its own section in each, not mixed into the list of changes. Number the entries, so the reader can reply "use the alternative for 2" and the agent follows rollback step 2. One round and it's settled.
No reply means the original choice stands. That's the real difference from approval: approval asks first and the task waits; a veto acts first and takes effect by default.
A version to copy
Paste this into your project's CLAUDE.md (Codex reads AGENTS.md) and the agent reads it every session:
## Open to veto
- User-facing copy, pricing and billing, restricting users: go with the
recommended option, then list it under "Open to veto" in the PR
description and the wrap-up report
- Deleting data: write the code and list it, but get explicit approval
before running it against production
- Each entry states the choice, the alternative, and the rollback
(down to the file, flag, or command)
- Anything that can't be undone (notifications sent, charges made): ask first
- Don't bury these decisions in the change summary
The first few lists may run long or short. Adjust as you go: drop the categories you always wave through, and add the ones you keep discovering after the fact.
Of those five entries, only the first changed later, when a copy cleanup removed the promise from the hero. The other four still stand as chosen, and the site has stayed up.