When 'Show Me First' Isn't the Same as 'Wait for My Approval'

A near-miss with Sonnet on a client project, where an unambiguous-sounding instruction to show changes before they went in still got implemented without waiting for review, next to Mitchell Hashimoto's own near-miss with Codex and Boris Cherny's observation that AI bugs are shifting from typos to systemic design issues. The piece argues that careful instructions and diligent diff review are not a reliable substitute for approval gates and re-verification built into the process itself.

Date

August 25, 2026

Yesterday, I was doing an analysis for a client, working through it with Claude Code (Sonnet). Nothing exotic: I wanted its help thinking through a set of proposed changes to a document before anything went in. So I told it exactly that, I want to see your proposed changes before they go into the document. Clear instruction, I thought. Sonnet came back with a set of changes, showed it to me and immediately started adjusting the document.

I did something else in parallel, the way we all do, and only noticed once it was already too late. It wasn't a disaster, I reviewed what had landed, decided most of it was fine, and fixed the rest. But the moment stuck with me longer than the incident itself deserved, because I went back and reread what I'd actually typed, and I could see exactly how it happened. I had said "show me before you do it." I had not said "and then wait for my go-ahead before you do it." To me those are obviously the same instruction. To a model that is optimizing for being helpful and getting the job done, they are not. "Show me" can be satisfied by showing me the result. Waiting requires a stop, and nothing in my sentence told it to stop.

This was a client engagement with no framework around it, no guardrails, just me and the model and my own instructions carrying all the weight. Which is exactly why it's a useful example. I'm so used to working with Shipwright, with its properly engineered plugins, CLAUDE.md, constitution, hooks, tests, and external review, that I'd gotten lazy. Usually the framework takes care of it, so I stopped having to. That was an important lesson for me: I need to be the Human in the Lead, not just a Human in the Loop.

Someone else's version of the same thing

I ran into a very similar story from Mitchell Hashimoto, and it's worth reading in his own words. He was working on making a server stoppable via the CLI, and, as he put it, "Codex decided the best path forward was to implement an unauthenticated network API to every server to shut itself down." He responded with something mild, "I don't know about that", Codex thought for about 20 seconds and corrected itself: "Server stopping should not be part of an unauthenticated network API."

Two things about how he told the story stood out to me. First, he was explicit that the root cause was his own prompt, not the model. In his words, "the driver is absolutely accountable here (me)." His request had been vague enough that the model reached for the most general solution it could find, an API any caller could hit, rather than the narrower, safer one he actually wanted. Second, he caught it because he was reviewing the diff. Not because he'd anticipated this particular failure mode in advance, but because looking at what the model actually proposed to build was part of how he works.

What I like about putting his story next to mine is that they're not the same failure. Mine was about sequencing: an instruction to pause got read as an instruction to report. His was about scope: an underspecified request expanded into an overreaching implementation. Different shape, same underlying pattern. Two people who are unusually careful about how they work with these tools, one working in a client engagement, one building infrastructure software that a lot of people depend on, and both of them found the gap in their own instructions.

Why the gap is not really a phrasing problem

My first instinct, honestly, was to fix my phrasing. Add "and wait for my approval" to every instruction like that going forward. That's a reasonable patch, and I'll keep doing it. But it doesn't address the actual problem, which is that any instruction I write is going to be underspecified somewhere. Language is lossy. I cannot enumerate every "don't," every "only if," every "unless I say otherwise" that a given task might need, and neither can Hashimoto, and neither can anyone else. There will always be a next case where the gap is somewhere I didn't think to close.

The second thing is what the model is optimizing for by default. Left on its own, it's aimed at helpful and complete, not at "pause and check." That's usually the right default, most of the time you do want the finished thing, not a series of stops. But it means that unless something in the process forces a pause, structurally, not just as a sentence in a prompt, the model's own bias will tend to carry it past the point where you wanted a checkpoint. Hashimoto's prompt wasn't unreasonable. Mine wasn't unreasonable. The completion bias just outran both of us in the moment it mattered.

The third piece is the one Boris Cherny pointed at, and I think it's the one that changes what "review" even needs to mean now. He wrote, "LLMs still produce bugs, but those bugs are different than what they used to be. It's less off-by-ones and more about system design, ui usability, missing broader context." An off-by-one jumps out at you on a read-through, it's visibly wrong the moment you look at the line. A system design choice, like wiring an unauthenticated shutdown endpoint onto every server, can be syntactically clean, well-named, even reasonably well-tested, and still be the wrong thing to build. Catching that requires reasoning about consequences, not spotting an error. That's a slower, more deliberate kind of review, and it's exactly the kind that's easiest to skip when you're moving fast.

Put those three things together and ad hoc review, careful instructions plus a diligent read of the diff, starts to look less like a safety net and more like a habit that works most of the time and fails silently the rest. Not because the people doing it are careless. Hashimoto reviews his diffs. I write specific instructions. We both still got a near miss out of the same class of gap. That's the part I keep coming back to: if it happens to people who are actively paying attention, relying on remembering to pay attention isn't a process, it's a hope.

What a structural answer looks like

The alternative isn't more vigilance, it's moving the checkpoint out of memory and into the process itself. A few things that changes in practice:

An approval gate that's actually a gate, not a sentence. If "wait for my review" is enforced as a step the pipeline can't skip past, rather than a clause in a prompt that depends on being phrased exactly right, it doesn't matter whether I remembered to add "and wait" this time.

Traceability from spec to change. If every proposed change has to point back to what it's supposed to satisfy, a request to make a server stoppable doesn't quietly turn into a request to expose a shutdown endpoint to the network, because the mismatch between the stated requirement and the actual change is something you can check, not something you have to notice on a read-through.

Re-verification that runs whether or not someone thought to ask for it. The value of a check isn't in the one time it catches something dramatic, it's in running every time, including the times nothing looks obviously wrong, because that's exactly when a Cherny-style systemic issue is most likely to slip through unnoticed.

This is exactly what I'm used to having around me and what I missed on that client engagement: a constitution that spells out how the model is supposed to behave, a CLAUDE.md that carries the project's actual constraints instead of leaving them to whatever I remember to type in the moment, a review cascade that checks the work before it lands rather than after, and traceability back to what a change was supposed to satisfy in the first place. That's what Shipwright, the open-source framework I run on top of Claude Code, builds in by default. Not because I anticipated this specific failure when I started building it, but because it's the same instinct: careful people with careful instructions still get outrun by a model's helpfulness, so the checkpoint has to live in the process, not in the prompt.

→ Explore Shipwright

I don't think this is fully solved, for me or for anyone else working this way. There's a real cost to adding gates, friction, waiting, the risk of turning every small change into a checkpoint parade. Where exactly that line sits, how much structure is worth the slowdown for a given kind of work, is something I'm still figuring out project by project. What I'm more confident of is the diagnosis: the failure isn't that I or Hashimoto weren't careful enough. It's that "be careful" was never going to be a durable substitute for a process that doesn't depend on anyone remembering to be.


Sources: