DEV Community

Cover image for I let my AI agents merge to production. Once.
Info Inlet
Info Inlet

Posted on

I let my AI agents merge to production. Once.

Judgment versus accountability in AI workflows

I automated almost my entire pipeline this year, and I'd fight you to keep it that way.

An agent writes the first draft of most changes now. A second agent reviews that diff — harder than I do at 5pm on a Friday, and it never gets bored on the four-hundredth one. Tests run themselves. Types, lint, a staging deploy, a smoke check against a real-ish database. By the time a change gets anywhere near me, five separate automated steps have already looked at it and said yes. It is genuinely better than the hand-cranked version I ran three years ago, and the typing was never the part I'll miss.

So one sprint I did the obvious last thing. I looked at a pull request with four green checks and a clean review already on it, and I thought: why is my hand still on the merge button? What exactly am I adding by clicking it myself that four automated yeses haven't already earned? So I gave the agents the last step too. Generate, review, test, stage, merge. Fully closed loop. For about nine days it felt like the future.

Then one of those merges locked a paying customer out of their own account at two in the morning, and I learned — expensively, in production, on a phone call — which single step in that pipeline I should never have handed over. It's not the one most people would guess. It's the one almost everybody automates first.

The diff was clean. That was the whole problem.

Here's what shipped itself.

The change was a write path. It acknowledged the request — sent the "got it" back to the client — before it had actually persisted the row. If you've been doing this a while, your stomach just dropped, because you know this bug. It is clean. It is idiomatic. It is typed, tested, and it reads perfectly right up until the moment a retry hits at the wrong instant: the "got it" goes out, the save never lands, and the system is now confidently certain about something that never happened.

The author agent wrote it. The review agent read it and approved it — and I want to be fair to the review agent, because it's good: it caught two real things that sprint that I'd have missed. It just didn't catch this one, because this one doesn't look like a bug. It looks like finished work. The tests were green because the tests tested the happy path, which is exactly the path where this code is correct. Staging was fine because nobody retried a request at the wrong millisecond against staging. Five automated yeses, all technically honest, all looking at the diff and none of them looking at the two in the morning.

It auto-merged. It auto-deployed. And on an ordinary bad night a retry hit, the ack went out, the write didn't, and a real human who pays us money got locked out of their own account with nothing in the logs to prove they'd ever been there. I can still feel the phone call — not because the bug was exotic, but because there had been no moment, anywhere in that beautiful closed loop, where a person who would get that phone call looked at the thing and owned shipping it.

Two things we call "review" — and they are not the same thing

Here is the distinction it took a locked-out customer to teach me, and it's the whole article.

We use one word, "review," for two completely different jobs.

The first job is judging the code: is this correct, is it idiomatic, does it handle the edge case, is the naming sane. That is a skill. It's pattern-matching against a huge amount of prior code and a mental model of the system. And it is exactly the kind of skill that got cheap this year. A good review agent does it tirelessly, consistently, at 3am, on the four-hundredth diff, without the fatigue that makes a human senior wave through a Friday-afternoon PR. You should absolutely automate this. I did, and it was the right call.

The second job is owning the merge: accepting that this is going to real users now, and if it's wrong, that's on me. That is not a skill. There is no pattern to match. It's the act of a specific accountable party putting their name on an irreversible, outward-facing decision and absorbing the consequence if it goes bad.

For twenty years those two jobs were welded together, because the person reviewing the code was also the person who'd get paged, so we never had to notice they were different things. Automation ripped them apart. The machine can now do the first job better than I can. It cannot do the second one at all — not because it isn't smart enough, but because the second job isn't about intelligence. It's about accountability, and accountability requires something that can actually bear a consequence.

You can automate judgment. You cannot automate accountability.

An agent that merges a bad diff and takes down prod cannot be paged. It can't be sorry. It can't carry the weight of that 2am call into how it makes the next decision, because it isn't making decisions it has a stake in — it's producing confident output at a scale no human can match and signing off in the same even voice whether the diff is a masterpiece or a time bomb. Infinite throughput, zero accountability. The merge is the one place in the whole pipeline where the buck is supposed to stop, and I'd handed it to the one participant that has no buck.

We guard the wrong click

Now here's the part that should bother you, because once you see it you see it everywhere.

Watch what teams instinctively keep manual versus what they happily automate.

They keep a human on code review. "A person has to read every diff." They put mandatory human approvals on pull requests, they argue about review coverage, they feel safe because a human's eyes were on the lines. And then they wire up auto-merge on green and a continuous deploy to prod, and feel clever for it, because that part is just plumbing.

That is exactly backwards.

Code review is the reversible step. If a human misses something in review, nothing has happened yet. You can review it again. You can comment. You can catch it in staging. Nothing has crossed into the real world. It is the single most recoverable point in the entire pipeline — and it's the one we insist stays human, even though it's now the step the machine does better.

The merge to production is the irreversible, outward-facing step. It is the exact moment the change stops being a proposal and starts being something a customer can hit at 2am. It is the one point in the pipeline where a retry can lock someone out of their account. And it's the step we're the most comfortable handing to a robot, precisely because it looks like plumbing — a button, a webhook, a green check — rather than a decision.

We guard the cheap reversible step with a human and automate the expensive irreversible one. We kept our hand on the one place the machine made safe, and took it off the one place it couldn't.

The merge produces nothing — which is the tell

There's a reason the merge button feels so automatable, and it's the same reason the "no" and the prevented outage always get undervalued: it produces nothing visible.

A merge that goes fine is invisible. Nothing happens. No artifact, no graph that goes up, no line of code with your name on it — the author agent wrote the code, the review agent found the bugs, and all you did was click a button that four green checks already justified. It looks like the most deletable job in the building.

A merge that goes bad is a countable event with a name on it: an incident, a postmortem, a locked-out customer, a phone call. So the person on the merge button is sitting in a pure-downside seat — all of the blame when it breaks, none of the credit when it doesn't. By every productivity metric you could point at, that person is doing nothing. Which is exactly why the job can't be automated away: the thing they're actually providing isn't throughput, it's a place for the consequence to land. You don't keep a human on the merge because they're faster or because they read the diff better than the agent did. You keep them there because when it goes wrong at 2am, "the pipeline did it" is not an answer you can give a customer, a regulator, or yourself.

To be clear — this is NOT "keep a human in the loop"

The lazy version of this piece is "see, you always need a human watching the AI," and that's the cope that kills teams, so let me kill it first.

Keeping a human on every step is theater. A human babysitting every generated diff, re-reading what the review agent already read, approving what the tests already proved — that's not safety, it's a slow person pretending to add value by being in the way. The machine reads diffs better than a tired senior now; making a human rubber-stamp each one just launders the robot's judgment through someone who's no longer actually looking. I automated the generation. I automated the review, and then I made the review harder than I used to, because the agent doesn't get tired of being thorough. I'd never go back. Output is table stakes and judgment-on-the-code is now something you can buy by the thousand.

The claim is narrow, and I think it's hard to argue with once the two jobs come apart: exactly one step in the pipeline has to stay human, and it's the merge — not because the human does it better, but because the human is the only participant who can be held to it. Everything upstream of that button is a skill, and skills got cheap. The button itself isn't a skill. It's a signature.

What I actually changed

I didn't rip out the automation. I re-pointed the human at the one seat that needed one:

  • The agents still generate and review. I made both do more, not less. The author drafts, the skeptic tries to break it. That part of the loop is better closed than open — the machine is tireless where I was tired.
  • A human owns every merge to production, and owns nothing else. Not as a code reviewer re-reading the diff — the agent already did that better. As the accountable party who looks at the blast radius: what does this touch, who gets paged if it's wrong, what's the retry story, is 2am going to call me. The question a human asks at the merge is not "is this code correct" — it's "am I willing to put my name on this going live."
  • The one thing I manually added back was the retry question. Every write path now gets one human sentence before it merges: what does this do if the client retries? That's the exact question that would have caught the bug. It's not a skill the agent lacks — it's a question nobody is accountable for asking inside a closed loop.
  • Deploy stayed automated; the decision to deploy didn't. The plumbing is robotic and should be. The choice to send it — the irreversible outward step — has a name attached.

Why this is the shape of what I build

One level down, this is the entire architecture of the agent platform I work on, and now you know why.

There's an author — the thing that generates the diff. Cheap, fast, endless, the abundant thing. Automate it all the way.

There's a separate skeptic — whose only job is to try to break what the author made, not admire it. The "no," given its own seat, tireless where a human reviewer gets tired. Automate this too, and make it mean, because the machine is better at being consistently skeptical than a person at 5pm.

And there's a human on the merge. Not reviewing the lines — the skeptic did that harder than the human could. Owning the call. Seeing the blast radius. Being the place the consequence lands when a retry hits at 2am. The author has infinite throughput and I let it run. The skeptic has infinite patience and I let it run. The human has neither — and is the only one of the three who can be held to the outcome. That's the one seat I will never automate again.

That's the shape of xenition, and it's also what it does for you: you describe what you want in one chat — a doc, a tracker, a whole working app — and it builds the thing, runs the skeptic over it, and then stops for your approval before anything goes live. The two cheap jobs run themselves; you keep the one click that can't be undone. Automate the skills, keep a human on the signature.

I closed the loop all the way once. It cost a customer their account at 2am and me a phone call I can still hear. The lesson wasn't "don't trust the agents" — I trust them more than ever to write and to check. It was that there's exactly one button in the pipeline that was never a skill in the first place, and it's the one I was most eager to automate because it looked like the easiest.

Top comments (50)

Collapse
 
cailab profile image
CAI •

The architecture you landed on, author then skeptic then human on the merge, is the right shape. Three layers: propose, challenge, authorize. It is the same pattern showing up across agent systems that actually work.

What resonates is the distinction between judgment and accountability. Most teams start by automating the skills, generation and review, and then notice the merge button is the last thing they want to give up. Not because the machine cannot click it, but because clicking it is not a skill. It is owning the consequence.

I see the same split in agent payment and authorization flows. The model proposes a transaction, a separate capability evaluates it, and a distinct authorizer commits. The merge button in your pipeline and the approve action in an agent payment flow are the same seat structurally: the point where a proposal becomes irreversible and consequence lands somewhere.

There are platforms built around exactly this separation today. The proposing context and the authorizing context stay deliberately apart. Infinite throughput on one side, yes or no on the other, with the blast radius visible. It is the same shape you found, generalized.

Collapse
 
infoinlet1 profile image
Info Inlet •

CAI, you named the layer I was circling without quite saying it out loud: propose, challenge, authorize. That's cleaner than my author/skeptic/human framing because it separates the role from the implementer. The human isn't special because it's human — it's special because it's sitting in the authorize seat. That's the generalization, and it's right.

The payment-flow parallel is the one that makes it click for me, because money is where this was always obviously true. Nobody ever thought "the agent proposed a $40k transfer and a second check approved it, so why is a person still signing" — the irreversibility is too loud to ignore. What my 2am outage taught me is that a production merge is a transaction; it just doesn't look like one until a customer is locked out. The commit-to-prod and the commit-the-payment are the same structural moment: a proposal stops being a proposal and the consequence has to land on something that can bear it. You're right that it's one seat wearing two costumes.

The one place I'd put a caveat — and it's the thread running through this whole comment section — is that the authorize seat is only real if what reaches it is shaped for authorizing, not for judging. A couple of other commenters pushed me hard on this: the authorizer can't just see the same green checks the challenger saw, or you've built the three layers and quietly collapsed two of them. Authorize has to be fed blast radius and the gaps the challenger couldn't close — including, in my case, a fault-injected retry result rather than another happy-path yes. Otherwise the third layer is a costume too.

On the platforms built around this separation — I believe it, and I think the architecture is correct independent of any particular one. The part I'd watch for is the same trap in new clothing: a platform that keeps the proposing and authorizing contexts physically apart but still pipes happy-path evidence into the authorize seat has the right diagram and the wrong content. The shape is necessary. It isn't sufficient. What flows across the gap is the whole game.

Good comment — you abstracted the thing one level up and it held.

Collapse
 
luke_bennett_ed52405c798e profile image
Luke Bennett •

I think as that can in your case and you think as peple can help you but attrmperd thinks right for you. As you think that too. And thinkning was good for you doesnt it

Thanks luke

Thread Thread
 
infoinlet1 profile image
Info Inlet •

Thanks, Luke — appreciate you reading and taking the time to comment. The thinking did help, and honestly writing it all out helped even more. 🙏

Thread Thread
 
luke_bennett_ed52405c798e profile image
Luke Bennett •

Your wellcome. We have got a touthbrush for you
You can have it i dont want it its to dray

Thread Thread
 
infoinlet1 profile image
Info Inlet •

Ha, appreciate the thought, Luke! 😄 Glad the piece gave you something to chew on either way — thanks again for stopping by and commenting.

Thread Thread
 
luke_bennett_ed52405c798e profile image
Luke Bennett •

Thanks.. a bit of a wink 😆😆😆😆😆😆
Thats fine and you take care

Collapse
 
panthpatel profile image
Panth Patel •

Same split here, with one difference in where the human signs. Our developer doesn't approve the diff; they test the ticket's running preview and then drag it to Git Push. From there no agent touches it: a script merges, and if the health checks fail it reverts the merge on its own. That still wouldn't have caught your ack-before-write bug, since nobody retries at the wrong millisecond on a preview. Is the retry question a PR template field now, or something the person merging asks?

Collapse
 
infoinlet1 profile image
Info Inlet •

I like your setup — the human signs on the running preview, not the diff, which is already more honest than approving lines. And the auto-revert on failed health checks is the right instinct: you've made the deploy more reversible, which is exactly where automation belongs. The gap you already spotted is the real one — a preview never retries at the wrong millisecond, so that whole bug class is invisible right up to production.

On your question: it's both, deliberately, because one without the other fails.

It's a required PR template field first — the author has to write, in one sentence, what the change does if the client retries. That forces the thinking to happen while the code is fresh, not at the merge. But a template field alone just becomes a box people type "N/A" into. So the second half is that the person merging has to actually read that answer and be unsatisfied with it — their job at the merge isn't to re-review the code, it's to decide whether that retry answer is good enough to put a customer behind. If it says "N/A" on a write path, that's not a merge, that's a conversation.

The part I'd add for your flow specifically: the answer shouldn't be prose, it should be a fault-injected test result. "Here's the run where the ack fired and the write was dropped, and here's what the customer saw." That's the thing that would have turned my preview green into a preview red. A sentence in a template is where it starts; a forced-failure run is what actually earns the signature.

Collapse
 
panthpatel profile image
Panth Patel •

A forced-failure run is the better receipt, agreed. A sentence can be wrong and still read fine. Our ticket stacks each get their own database copy, so dropping the write after the ack there would cost nothing. How do you inject it: a proxy that kills the connection, or a flag in the write path itself?

Thread Thread
 
infoinlet1 profile image
Info Inlet •

Proxy, not a flag — and for a specific reason. A flag in the write path tests a code path that only exists in tests. You're proving your fault-injection branch works, not that the real branch fails safely; the moment the ack and the persist live in the same function behind an if (injectFailure), you've built a bug that can't reproduce the bug. The proxy kills the connection the real client uses, so the code under test is byte-for-byte the code that ships — it doesn't know it's being attacked, which is the whole point.

Thread Thread
 
panthpatel profile image
Panth Patel •

That settles it. Once the failure sits behind an if (injectFailure), the test proves the if, not the code that ships. The proxy also leaves nothing in the write path to forget about later, and test-only branches are the ones nobody removes. Thanks for spelling out both halves.

Collapse
 
griffonknox profile image
Scott Thompson •

I want to ask you a specific question that you pointed out at the beginning of your article. Automating your entire workflow and you mentioned it felt like the future. May I ask how your steps and outlook have been and or changed while working towards this point? I question because I personally am having a hard time still feel satisfied as a software developer in this age of automating as much as we can. I feel like my expertise is being taken away, along with the emphasis of developing our individual developers within a company. I understand the use of AI greatly increases productivity, but I have a hard time overlooking what I view as the harm its doing to developers to be supportive of it. Would love to hear your thoughts on how you came to a positive approach within your career.

Collapse
 
infoinlet1 profile image
Info Inlet •

Scott, this is the comment I most wanted someone to leave, so I'm going to answer it honestly rather than briefly.

The short version of how my outlook changed: I used to locate my value in the typing — in being the person who could hold the system in my head and produce the correct lines. When the machine got good at producing the correct lines, that version of my identity took a real hit, and for a while it felt exactly like what you're describing: expertise being quietly deleted. I don't think you're wrong to feel it. I think a lot of people are feeling it and papering over it with productivity talk.

What changed for me wasn't deciding the feeling was irrational. It was noticing that the thing being automated was never actually the expertise — it was the output of the expertise. The part of me that knows an ack-before-write is a time bomb didn't come from typing fast. It came from having been burned, from having a model of how systems fail at 2am, from judgment about what matters. The machine can now write the code my judgment would have written. It cannot have the judgment. And the whole post is really about discovering that there's a seat — the accountability seat — that the machine structurally cannot sit in no matter how good it gets. That seat is where the expertise lives now. It didn't disappear; it moved up a level, from "can you write it" to "can you own whether it should ship."

On the harm to developers — I want to be careful here, because I don't think you're wrong and I don't want to sell you comfort. There is a real risk, and it's not the one people name. The risk isn't that AI writes the code. It's that a generation of developers never gets burned, never gets the 2am call, and so never builds the judgment — because the machine handled all the happy paths and nobody was ever accountable for the failure path. Expertise comes from owning consequences, and if we automate away every consequence, we do actually stop producing senior people. That's the harm worth fighting. But the fight isn't "stop using AI." It's "keep humans in the accountable seats so they keep developing." A company that automates the skills and removes the accountability is hollowing out its developers. A company that automates the skills and deliberately keeps its people owning the irreversible decisions is doing the opposite — freeing them from the typing to spend their attention on the judgment, which is the part that actually makes a senior engineer.

So where I landed, career-wise: I stopped trying to be the fastest producer and started being the person who owns the blast radius. That turned out to be more satisfying, not less, because it's the part of the work that was always the real work — I just couldn't see it when it was buried under the typing. Your expertise isn't being taken away. The market for "writes correct code" is collapsing, yes. The market for "can be trusted to decide what ships to real humans and carry it when it's wrong" is not — it's growing, and it's underserved, and almost nobody is explicitly training for it.

If you're feeling the loss, my honest advice: don't fight to keep doing the part the machine now does well. Move deliberately toward the seat it can't take. Volunteer to own the merge. Own the on-call. Own the postmortem. Be the name written down early. That's not a demotion from "real developer" — in this age it's the most developer thing left, and it's the one that still compounds.

Collapse
 
griffonknox profile image
Scott Thompson •

thank you for the honest and thorough response. I appreciate your time and outlook on addressing my concerns.

Thread Thread
 
infoinlet1 profile image
Info Inlet •

Anytime, Scott — it's the kind of question worth taking seriously. If you do end up moving toward those accountable seats, I'd genuinely like to hear how it goes. Good luck out there.

Collapse
 
urion profile image
Matteo Poli •

The ack-before-persist bug is such a good example, because it's correct on every path the tests know about.

One thing I'd add to "own the merge": owning it only means something if the person actually read the part that can hurt. With four green checks the temptation is to skim the summary and click. What works for me is one question before merging: which lines in this diff touch something irreversible (writes, auth, money, deletes)? Those I read slowly, every time, and the rest I leave to the review agent. It turns owning the merge from a click into ten focused minutes, and those are the ten minutes that would have caught this one.

Collapse
 
infoinlet1 profile image
Info Inlet •

This is the mechanic I wish I'd put in the post, because it answers the obvious objection to "own the merge" — that owning it just becomes a fancier rubber stamp. Your version makes it real: not "read the whole diff again" (that's the theater I was arguing against) but "find the irreversible lines and read those slowly." Writes, auth, money, deletes — that's the blast radius, and it's usually a small fraction of the diff. You've turned accountability from a feeling into a repeatable ten-minute act, and crucially you left the rest to the agent, which is exactly where it belongs.

The one thing I'd add: the irreversible lines are often not where the change is. The ack-before-write diff touched the write path, sure — but the danger was in the ordering between lines that individually looked fine. So I'd stretch your question slightly: not just "which lines are irreversible," but "what's the worst interleaving of these irreversible lines under a retry or a crash halfway through." Same instinct as yours, one level meaner. The lines tell you where to look; the failure story tells you what you're looking for.

Collapse
 
urion profile image
Matteo Poli •

"One level meaner" is going in my checklist. A trick that helps me with exactly that: before merging, write the failure story down as a note on the line where it would start ("retry lands here after the ack, row never written"). If I can't write one, I haven't found the risky interleaving yet. And once it's written, it's also the spec for the test the agent should add.

Collapse
 
mrsaynothing profile image
Mr Say Nothing •

The ack-before-persist bug is the exact class green pipelines cannot see — every test asserts the response, none assert the disk. "The diff was clean. That was the whole problem" is the whole post in one line. Which step did you take back — and did the fix land as an assertion (a persisted-row check in the smoke stage) or as a policy (human hands stay on write paths)?

Collapse
 
infoinlet1 profile image
Info Inlet •

Good question, and the honest answer is both — but in a specific order, because landing only one of them would have fooled me again.

The step I took back was the merge on write paths, specifically the authorize seat — not the review. The agents still read the diff; I didn't take that back because they read it better than I do. What I took back was the click that turns a write-path proposal into something a customer can hit.

But the policy alone would have been theater. A human whose hand is on the write-path merge button, staring at the same five green checks that lied to me the first time, catches nothing — I'd just be a slower rubber stamp. So the policy only works because of the assertion that feeds it. The fix landed as both, and the assertion is the part that makes the policy real.

Concretely, on the assertion side: write paths now get a fault-injected test in the smoke stage — the request is acked, then persistence is forced to fail or the client retries at the wrong moment, and the test asserts the row is actually on disk and the ack was not sent prematurely. You nailed it: every test asserted the response, none asserted the disk. Now the disk gets asserted, specifically under failure, not under the happy path.

And the policy is what sits on top: the human at the merge doesn't re-read the lines, they read that fault-injection result and decide if it's good enough to put a customer behind. The assertion gives the authorize seat something real to authorize on instead of another happy-path yes. Assertion without policy and nobody owns it; policy without assertion and the owner is blind. The bug needed both gaps closed.

Collapse
 
parsa_m profile image
Parsa Mohammadi •

the staging point is what got me. a happy path test can prove the write works, while saying nothing about ordering between the ack, persistence, and retry. that class of bug is hard to catch if every check is centered on the diff.

Collapse
 
infoinlet1 profile image
Info Inlet •

Exactly — that's the heart of it. The diff was correct; the ordering was the bug, and ordering only shows up under concurrency and failure, not in a line-by-line read. Every check in my pipeline was centered on "is this code right," and none of them asked "what happens if the client retries at the wrong millisecond." That's why every write path now gets one mandatory question before merge: what does this do on a retry? It's the one thing the happy path can never answer. Great catch, Parsa.

Collapse
 
parsa_m profile image
Parsa Mohammadi •

that one question is a good rule. at scale the hard part is knowing which write paths to ask it on first, and the ones that already broke on a retry are usually the answer. that’s the kind of history a PRI (Production Reliability Index) score puts on each PR.

Thread Thread
 
infoinlet1 profile image
Info Inlet •

Yes — "the ones that already broke are usually the answer" is exactly right. Past incidents are the cheapest signal you've got; the system already told you where it's fragile, you just have to let it. I like the idea of carrying that history onto the PR itself so the question fires automatically on the risky paths. My only caution: a score is a prioritizer, not a gate — it tells you where to point the human, but someone still has to own the merge. Where it earns its keep is making sure that attention lands on the right diff. Good thread, Parsa.

Collapse
 
ascendra_ventures_92c21f2 profile image
Ascendra Ventures •

"You can automate judgment. You cannot automate accountability" is the clearest way I have seen this put. The detail that makes it convincing is that the bug was an acknowledgement sent before the write was persisted. Nothing about that diff looks wrong, so no reviewer, human or agent, was going to catch it by reading lines.

The reversible versus irreversible framing is also how I would explain it to a client. We let automation run freely up to the point where something reaches a customer, a payment or a record that cannot be cleanly undone, and we name the person who owns that step before the pipeline is built. Writing that name down early changes the conversation, because it forces the team to say which steps are actually irreversible. A deploy behind a feature flag with a tested rollback is far less irreversible than a schema migration or an outbound email.

One addition: the merge owner is more effective with a small, explicit list of things automated review cannot see, such as retries, ordering and partial failure. Most of what goes wrong at two in the morning lives in that list.

Collapse
 
infoinlet1 profile image
Info Inlet •

This is a sharp addition, thank you. Two things you said I want to underline.

First, "write the name down early." That's better than my framing, because it moves the accountability decision before the pipeline exists instead of after the outage. The act of naming the owner forces the team to classify each step as reversible or not — and most teams have never actually done that audit. A deploy behind a flag with a tested rollback and a schema migration are both "the deploy step" on a diagram, but they are not the same irreversibility, and you only find that out if someone is made to own them out loud.

Second, the explicit list of what automated review cannot see — retries, ordering, partial failure. That's exactly the content the authorize seat needs, and it's the gap another thread here kept circling: the merge owner is only real if what reaches them is shaped for authorizing, not just the same green checks the reviewer already saw. Your list is the shape. My "what happens on a retry?" question was me discovering one line of it the expensive way; you've generalized it to the whole category. I'm going to steal "retries, ordering, partial failure" as the standing checklist. That's where the 2am calls live.

Collapse
 
madhabcoder profile image
Nilamadhab Senapati •

"You can automate judgment. You cannot automate accountability." Saving that one. The scary part is that all five checks were technically honest, and they were just all looking at the diff and not at 2am. As a solo dev with a day job, I'm tempted every week to close the loop and let the bots merge. This post just bought my merge button a few more years.

Collapse
 
infoinlet1 profile image
Info Inlet •

Ha — "bought my merge button a few more years" is a great line, I'm keeping that.

The solo-dev-with-a-day-job case is actually the one I worry about most, because you're exactly the person for whom closing the loop is most tempting and most dangerous. Tempting because there's no one to hand the merge to, so automating it feels like just removing a bottleneck of one. Dangerous because you're also the person who gets the 2am call, and "the pipeline did it" is even less of an answer when the pipeline is a thing you alone built and alone maintain.

But don't read this as "stay in the loop everywhere" — that's the trap that burns out solo devs. Let the bots generate. Let them review harder than you would at 11pm after your day job. Automate all of that aggressively; that's where the leverage is for someone with no time. Just keep the one click. And make it cheap to keep: for anything that isn't a write path or an outward-facing change, let it auto-merge freely — the signature only has to be yours on the stuff that can actually lock a customer out. Guard the irreversible step, automate the rest without guilt. That's the version that's sustainable for one person.

Collapse
 
glenallen profile image
Glen Allen •

The distinction between reviewing code and owning the merge is useful, but I think there’s another design question underneath it: what evidence should the accountable person actually see before accepting that responsibility? At IT Path Solutions, we’ve found that a green pipeline and clean diff aren't always enough for production-facing changes. The approval context becomes much stronger when it also shows expected blast radius, affected data paths, rollback confidence, and any verification gaps that automated checks couldn't resolve. That makes human approval more than another review step—it becomes an explicit risk decision based on evidence. The goal isn't simply to keep a human in the loop, but to give that person enough context to make the responsibility meaningful.

Collapse
 
infoinlet1 profile image
Info Inlet •

Glen, this is exactly the right second question, and honestly it's the one I'm still working out.

You've named the gap I only half-closed. I moved the human to the merge seat, but for the first few weeks I left that person staring at the same artifacts everyone else was — green checks and a diff. Which is absurd, because I'd just finished arguing that the diff is the thing the agent already reads better than any human. Asking a person to own the merge while handing them only the evidence optimized for judging code is setting them up to rubber-stamp. The accountability is real but the context is wrong-shaped for it.

The reframe I landed on is close to yours: the approval screen shouldn't answer "is this code correct" — the skeptic settled that upstream — it should answer "what am I actually signing for." So the things you listed are exactly the things I started surfacing at the button: blast radius (what does this touch, who pays if it's wrong), the data paths it writes, rollback confidence, and — the one that would've saved me — the verification gaps the automated checks couldn't close. That last one is the quiet hero. A green pipeline tells you what was tested; it says nothing about what wasn't, and the 2am bug always lives in what wasn't.

My one addition to your list is to make the gaps load-bearing rather than informational. The retry question I mentioned isn't a note on the approval screen — it's a field that has to be answered before the merge unlocks. Evidence you can skim still lets a tired human wave it through; a gap you're forced to resolve turns the approval into the risk decision you're describing instead of another review step wearing one.

Appreciate this — you pushed the idea further than I had it.

Collapse
 
marcusykim profile image
Marcus Kim •

That acknowledgment-before-persistence bug gives the retry question a concrete test: interrupt the write around the commit boundary, resend the same request, then inspect the durable state rather than only the response. I would keep that regression case alongside the named release owner. Human accountability matters, but the click itself is not evidence that the retry is safe; a failure-injection result gives the person accepting the blast radius something stronger than another green happy-path check.

Collapse
 
infoinlet1 profile image
Info Inlet •

Marcus, you just turned the retry question from a conversation into a test, and that's the upgrade it needed.

You're right that I under-sold it. As I described it, the retry question is still a sentence a human answers — and a sentence is only as good as the person's imagination at the moment they write it. What you're describing is the thing that makes the answer checkable: interrupt the write around the commit boundary, resend the identical request, then go look at the durable state instead of trusting the response the client got back. That's the whole bug reproduced on purpose. The code that locked my customer out would have failed that test on the first run, in CI, in daylight — instead of at 2am against a human who pays us.

And the detail that matters most in what you wrote is inspect the durable state rather than only the response. That's exactly the trap the original bug lived in: the response said "got it," and every green check in my pipeline was ultimately reading some version of that same cheerful acknowledgment. The ack is the thing that lies. The row is the thing that tells the truth. A test that asserts on the response would have passed and still shipped the outage; only one that asserts on what actually persisted catches it.

Keeping that as a standing regression case alongside the named owner is the part I want to steal outright. It fixes the asymmetry you put your finger on — the click is accountability, not evidence. The human at the merge can own the blast radius all day, but ownership isn't proof the retry is safe; it's just someone willing to be wrong about it. A failure-injection result sitting on the approval gives that person something the happy-path green never could: evidence that the one path the bug actually lives on has been walked, on purpose, and came back clean.

So the amended rule, thanks to you: every write path gets a fault-injected retry case before it merges, and it asserts on durable state, not the response. That's not another happy-path yes. That's the first check in my whole pipeline that was actually looking at 2am.

Some comments may only be visible to logged-in visitors. Sign in to view all comments.