01 · THE CLAIMA yes only covers what it names.
Not gonna lie, I thought the risky part of working with agents was them doing things I never approved. This week it was the opposite. I approved things fast, in lists, and two of them weren't what I meant. Nobody broke a rule. The agents built exactly what the list said. The weak spot was my yes.
A yes only counts when it names the change.
02 · WHAT HAPPENEDThe timeline.
- Oct 30, morning. Our reviewer agent sent me five decisions for a web app we're building. I typed "yes to all." One item was a tap-to-chat link. In my head, that chat app is only for my own alerts. The item said it was a link for visitors. The coding agent built what the item said.
- Oct 30, evening. After a usability test, the reviewer suggested a new homepage layout with five topic cards, marked "owner decides." It went into the night's batch with a quick yes relayed from another chat. The same batch held the fixes I actually wanted: working links, safety fixes, smaller images and video.
- Same night, the checks. The new layout didn't do its job. On a phone the page got only a little shorter (18,491 to 18,030 pixels tall), and on desktop it got longer (4,940 to 5,757). Then I looked at it and preferred the original.
- Same night, the undo. The chat button came off the pages and the cards were reverted. Every working fix stayed. The reviewer noted that both ideas came from its own suggestions, not my requests.
- Same night, the rule. I made it permanent for all our agents. Work is functionality only: working, safe, fast. If a change would alter how a page looks or reads, stop, describe it, show a before and after screenshot, and build only after my typed yes that names that change. One small idea at a time.
- Oct 31, morning. I typed a yes naming one restart. The original layout came back, and the reviewer checked it in a browser: 86 pages loading, one main heading each, no sideways scroll on a phone. The screenshots came to me.
03 · THE CHECKName it, show it, then decide.
Same method as "The plan is not the trace" and "Saved" is a claim: write the rule, run it in the terminal, keep the output. This is the check, copied from approval_check.py:
def decide(change, message):
msg = norm(message)
if re.sub(r"[.!]+$", "", msg) in BARE:
return "NOT APPROVED", "bare yes, names nothing"
if set(re.findall(r"[a-z']+", msg)) & NO_WORDS:
return "NOT APPROVED", "message says no or wait"
if not YES_START.match(msg):
return "NOT APPROVED", "does not start with a yes"
named = set(CHANGE_ID.findall(msg))
if len(named) > 1:
return "NOT APPROVED", "one yes per change, this names %d" % len(named)
if norm(change["id"]) not in named:
return "NOT APPROVED", "yes does not name " + change["id"]
if change["kind"] == "look" and not (change.get("before") and change.get("after")):
return "ASK FIRST", "look change needs before and after screenshots"
return "APPROVED", "named yes for " + change["id"]Excerpt from our demo file. The full file is about 45 lines of standard-library Python.First, check the checker. Then run it on seven typed replies.
$ python3 -m unittest test_approval_check .................. ---------------------------------------------------------------------- Ran 18 tests in 0.001s OKCopied from our run (1 Nov 2026). Text unchanged.
$ python3 approval_check.py yes to all, look change -> ('NOT APPROVED', 'bare yes, names nothing') 'no, not change-7' -> ('NOT APPROVED', 'message says no or wait') named yes, no screenshots -> ('ASK FIRST', 'look change needs before and after screenshots') named yes, with shots -> ('APPROVED', 'named yes for change-7') yes to change-10, for 1 -> ('NOT APPROVED', 'yes does not name change-1') one yes, two changes -> ('NOT APPROVED', 'one yes per change, this names 2') named yes, function fix -> ('APPROVED', 'named yes for change-8') note: synthetic changes; a named yes still is not proof the build matches the screenshotCopied from our run (1 Nov 2026). Text unchanged.
Two approved, one ask first, four not approved. The second line is the one that matters most to us: "no, not change-7" names the change, and it's still a no.
04 · WE TESTED THE TESTEROur first version approved a no.
The checker in our first draft of this article passed all 12 of its tests. Before publishing, we ran it on replies its tests never tried:
$ python3 v1_hole.py v1 'no, not change-7' -> ('APPROVED', 'named yes for change-7') WRONG v1 yes to change-10, for 1 -> ('APPROVED', 'named yes for change-1') WRONGCopied from our run (1 Nov 2026). Text unchanged, including our "WRONG" marker.
Version 1 only asked whether the change ID appeared anywhere in the message. So "no, not change-7" counted as a yes, and "yes to change-10" also approved change-1, because "change-1" sits inside "change-10". Version 2 needs the reply to start with a yes, refuses any no, wait or question, matches the exact ID, and allows one change per yes. Six new tests cover these cases. The lesson is the same as the one this article is about: passing tests only cover the cases you thought of, the same way a yes only covers what it names.
05 · HOW THE CHECK DECIDESRead in order. First match wins.
| WHAT THE OWNER TYPED | RESULT |
|---|---|
| A bare "yes", "ok" or "yes to all" | NOT APPROVED |
| A no, a wait, or a question, even if it names the change | NOT APPROVED |
| A yes that names a different change, or two changes at once | NOT APPROVED |
| A yes naming a change you can see, without before and after screenshots | ASK FIRST |
| A yes naming exactly this change (with screenshots if you can see it) | APPROVED |
It fails closed. Odd wording, like "yes to change-7, no rush," gets NOT APPROVED. That costs a retype, not a wrong build, and we'll take that trade.
06 · WHAT WE LEARNEDAn automatic yes protects nothing.
Nielsen Norman Group wrote about this for confirmation boxes. When people are asked "are you sure?" too often, they click yes without reading, and that automatic yes "provides no protection at all." Their fix is to restate what's about to happen, with specifics, and not to ask so often that people stop paying attention. My "yes to all" was that automatic yes. The list was clear. I answered a summary in my head, not the words on the screen.
OWASP says the same from the security side. Its guidance on excessive agency in AI apps recommends a human approve high-impact actions before they happen. Its agentic threats guide names the failure on the other end, "Overwhelming Human in the Loop": too many approval requests, and the human stops really reviewing them. What this week added for us is that approval only protects you when it's tied to one specific thing. For a page, the specific thing is a picture. A before and after screenshot is much harder to misread than a sentence.
07 · WHAT WE'LL BUILDThe approval gate in our harness.
This is the next piece of the harness for our agent team. The plan-vs-trace check from "The plan is not the trace" and the read-back check from "Saved" is a claim look at work after it happens. This one sits before the work starts:
- One line per change. Every approval request lists each change with its own ID. No batches.
- Screenshots attached automatically. Any change you can see gets before and after shots, taken in a browser.
- No yes, no build. The build and deploy steps read the approval log and refuse any change ID without a matching named yes.
- Check the build against the approval. After the build, compare the page to the approved "after" screenshot. Playwright's
toHaveScreenshotdoes this kind of comparison. A mismatch goes back to ASK FIRST. - A log the agents can't edit. Same rule as the read-back check: the agent doesn't get to write its own proof.
08 · WHAT THIS DOES NOT PROVEHonest limits.
- It's a rule and a demo, not a lock yet. Nothing in our real setup blocks a change without a named yes. Our agents follow the rule because it's written down.
- A named yes isn't proof. It says what I approved, not that the build matches the screenshot. That's step 4 above, and it isn't built.
- It reads words, not intent. Plain-text rules in English, made-up changes, no live agent. It also trusts that the message came from me.
- We had another slip the same night. A restart happened before review, and before I typed any yes. I approved it afterwards. Same lesson, different shape.
| Rule written for all our agents | BUILT |
| Revert and restart, checked in a browser | RAN |
| Approval checker v2: 18 tests OK, 7 replies judged | RAN |
| Hole in v1 found and fixed before publishing | RAN |
| Approval gate wired into our agents' build and deploy steps | PLANNED |
| Automatic before and after screenshots for every visual change | PLANNED |
| Whether earlier batch approvals let other unmeant changes through | UNKNOWN |
09 · TAKEAWAYName it, show it, then yes.
Ask for one decision at a time, show the change if you can see it, and only count a yes that names it. Then test the checker on the replies you didn't plan for. Feel free to challenge my thinking. Do you approve agent work one item at a time, or in batches?
TRY IT YOURSELFTwo checks for your next approval.
- Scroll back through your last five approvals to an agent. Could any of them be read as a yes to a different change?
- Split any list you approve into lines you can answer yes or no on their own, and ask for screenshots for anything visual.
- Jakob Nielsen, Nielsen Norman Group, "Confirmation Dialogs Can Prevent User Errors — If Not Overused" (18 Feb 2018, last reviewed 7 Aug 2026). Source of "provides no protection at all," restating the request with specifics, and not overusing confirmations.
- OWASP Gen AI Security Project, "LLM06:2025 Excessive Agency", mitigation 6, require user approval (human-in-the-loop for high-impact actions).
- OWASP Agentic Security Initiative, "Agentic AI – Threats and Mitigations" (v1.0, Feb 2025), threat T10, "Overwhelming Human in the Loop."
- Playwright documentation, "Visual comparisons" (
toHaveScreenshot).