I published a security tool to NPM that didn’t work. It looked correct, the tests passed, and the agent that built it argued for every design decision with complete confidence. I’d been moving fast and trusting the output. It wasn’t until I actually sat with the code that I understood what we’d built: something that looked like a functioning library and functionally did nothing. I deprecated it three days after publishing, that’s not a story I enjoy telling, but I’m telling it because it changed how I work.
The Full Autonomy Period
For a while, I was producing projects at a clip most engineers would envy. Agent in the driver seat, subagents in worktrees, spec to shipped in a day or two. The velocity felt like the future.
What I didn’t notice at first was how much I was giving up to get it. My ability to write code was dulling - not because I was busy, but because I’d stopped doing it. This is a well-documented phenomenon called de-skilling: the gradual atrophy of a capability through disuse. GPS navigation does it to spatial reasoning. Calculators do it to arithmetic. Agentic coding does it to the mental model you spent years building. I was reviewing agent output the way most people read terms of service: present, technically looking, not really reading. The map that tells you where to look when something breaks was quietly dissolving.
There’s a subtler failure mode underneath all of that: agents fill specification gaps. They just quietly make decisions about how things should work and keep building. You don’t know it’s happening. By the time you look up, the structure of your project has been partly defined by decisions you never made. Sometimes those decisions are fine. Sometimes they go on to define structural problems that take weeks to untangle.
What Actually Went Wrong
In the case of the security tool, all of it happened at once.
I had spec’d the project. The agent planned it, built it, and shipped it. I was deep in the speed mindset - reviewing at a surface level, trusting the confidence of the output, not reading the code with any real rigor. The library looked correct. The interfaces made sense. The agent defended every decision I questioned. What I didn’t do was verify that the implementation actually did what we said it would do. When I finally went line by line, I found code that went through all the right motions without producing any real effect. The library was a well-structured facade.

That was the moment I understood the real problem with fully handing the wheel over. An agent building something and that something actually working are two different things. And the only way to tell them apart is to look at the code yourself.
Putting the Brakes On
I stopped YOLOing everything - and I want to be clear about what that means, because I’m not describing a vibe-coder who finally learned caution. I was running Claude with --dangerously-accept-permissions because I was confident in my ability to provide context and guardrails. I knew what I was doing. The problem wasn’t recklessness. It was that I’d let confidence in the tools tip over into complacency about my own involvement. Speed had become the goal, and the goal had crowded out the judgment.
Owning a Ferrari doesn’t make driving it everywhere at full throttle the right call. Some roads have a speed limit for good reason.
So I changed the constraint. Agents still move fast. They scaffold, handle the straightforward pieces, and build out everything that isn’t structurally critical. But when we hit a function or class that actually matters - business logic, a key state decision, something that defines how the software actually behaves - the agent drops a TODO(HUMAN) placeholder and stops. Not a code comment. A literal gap in the build that the agent will not fill.

The agent may have other things it can work on in the meantime, but it won’t advance down any path that depends on that open decision. Bad downstream choices compound fast when the foundation is wrong, and letting the agent fill in the blanks is exactly how you end up with a well-structured facade. It waits for me to show up, work through it, and fill the gap myself.
What “critical” means in practice is somewhat fluid - sometimes I define it upfront (“I want to write the cache invalidation logic”), sometimes the agent flags it as it works through the build. The real point isn’t the classification. It’s that I’m touching the codebase at checkpoints, reviewing what’s happening, asking questions, and building an actual mental map of the system rather than receiving a finished product I didn’t participate in creating.
It’s a little slower, but that’s the whole idea.
How It Plays Out in Practice
I’m currently building an educational coding agent. At a certain point in the user flow, they need to run their test suite against their codebase. My initial instinct was to expose a /test command - let the agent detect the environment and handle it. But I also wanted users to write the test command themselves at least once before that shortcut was available. Force the learning moment before the automation kicks in.
The agent flagged it as a TODO(HUMAN).
I started working through the logic and the edge cases stacked up immediately. The courses run in TypeScript or Python, but students have real variation in how they manage their environments - which package manager they prefer, how their scripts are configured, what assumptions they’ve baked into their local setup. “Detect the right test command” sounds like a small problem until you’re staring at the actual surface area. The longer I sat with it, the clearer it became: this wasn’t a clever feature, it was a maintenance problem that would announce itself loudly when real users hit edge cases we hadn’t anticipated. The only version that works reliably is the one where /test always runs and the agent handles environment detection, without the prerequisite hurdle.
I cut the feature. If the agent had owned that decision, it would have built the original version - confidently - and we’d have discovered the problems in production.
The Argument
The case for full agentic autonomy is essentially a speed argument. But speed compounds in both directions, and the shortcuts you take while moving fast compound too. The spec gaps you don’t catch. The decisions you don’t review. The mental model you’re not building because you’re never really in the code. I know exactly where that path leads.
There’s also a practical dimension that’s harder to ignore as the major providers begin winding down their token subsidies and costs normalize: if you can’t read the code your agent is writing, you can’t prompt it precisely, you can’t catch the quiet failures, and you’ll spend a significant number of tokens fixing problems that a closer pass earlier would have caught. The investment in staying sharp pays back in quality and in cost.
TODO(HUMAN) is how I stay sharp.