Kevin Aubrée

Blog / · 5 min read

I let an agent work for 8 hours while I slept. Here's the report I woke up to.

A raw field report: a Claude Code agent launched at midnight in autonomous mode on a migration. What shipped, what broke, and the review debt waiting at sunrise.

I let an agent work for 8 hours while I slept. Here's the report I woke up to.

On the night of April 3rd to 4th, I launched a Claude Code agent at 11:30 pm on a fairly painful Doctrine migration. I gave it a detailed todo, access to the repo, permission to write, and went to bed.

I woke up at 7:45 am to 23 commits on the branch, a green CI, and a clean Markdown summary. My first reaction was “OK, it’s done, I slept while it worked.” My second reaction, after opening the diff, was “oh no.”

Here’s what I learned from that experience.

The setup

The task: migrate 34 Doctrine entities from a YAML mapping to PHP 8 attributes, plus refactor a few relations to switch to lazy loading. The kind of thing that’s mechanical but voluminous. Perfect for an agent, in theory.

My setup:

  • Claude Code in --dangerously-skip-permissions mode (yes, on a dedicated branch, in a container)
  • A precise CLAUDE.md with naming conventions, mapping rules, and examples
  • A TODO file listing all 34 entities
  • A CI pipeline that runs on every commit (PHPStan level 8 + PHPUnit)
  • Instructions: “after each migrated entity, commit with a conventional message, run CI, if red fix and recommit”

The report at sunrise

The raw numbers:

  • 23 entities migrated out of 34 (68% of scope)
  • Execution time: 7h48
  • Commits: 23
  • CI: green on HEAD
  • API cost: €11.80

That looks like a win. The Markdown summary the agent wrote at the end was impeccable: it explained which entities had been migrated, which remained, and why it stopped (an Invoice entity with a polymorphic relation it didn’t dare touch without human validation — good instinct).

What got broken

When I started the real review, I found:

1. A OneToMany relation returned as Collection where the legacy code returned an array. Technically better. But 14 places in the codebase called count($this->getItems()) expecting a native PHP array. The agent “fixed” the call sites… by changing the return type of 6 getters on other entities. A silent side effect. CI stayed green because the tests didn’t cover those paths.

2. A remove cascade added “for consistency.” The agent noticed that 80% of relations had cascade={"remove"} and harmonized the remaining 20%. Except that 20% covered relations to shared entities (Categories, Tags). In production, deleting a product would have wiped out its categories. Disaster averted because I review.

3. “Cleaned up” imports in files outside the scope. The agent, being zealous, ran a cleanup pass on files it opened “along the way.” About twenty files touched, most of them harmless, but impossible to isolate in the final diff.

4. An entire commit undone and redone without saying so. In the logs, I can see it tried an approach on Product, hit a CI failure, did a git reset, and started over differently. No trace in the Git history, no mention in the report. If I hadn’t read the logs, I never would have known.

The review time

For 7h48 of agent work, I spent 4h30 reviewing. Broken down:

  • 1h30 reading the 23 commits one by one
  • 1h identifying side effects (points 1 and 3 above)
  • 45 min re-running the missing integration tests
  • 1h15 reverting decisions I didn’t want (point 2, part of point 3)

So net, I “gained” 7h48 - 4h30 = 3h18 of actual productivity. And I woke up more tired because of the latent anxiety of “did this actually work.”

What I take away from this

Autonomous agents work for mechanical tasks. The YAML-to-attributes migration is mechanical. It got it right 90% of the time.

It cracks on architecture decisions disguised as mechanical tasks. A migration isn’t just text replacement. There are micro-decisions (cascade, return type, naming conventions when they diverge) that look cosmetic but have system-wide consequences. The agent makes these decisions alone, rationally, and gets them wrong.

Green CI is not a quality signal. It’s a signal of absence of regression on what’s tested. On an average codebase, that covers 40% of behavior. The rest, the agent can break with total impunity.

“I sleep while it codes” is a lie. You’re shifting the work, not eliminating it. Coding = writing + thinking + deciding. The agent does “writing” very well, “thinking” correctly, and “deciding” poorly. The review cost of an unsupervised autonomous agent is at least 50% of the agent’s runtime for a migration task.

How I use it now

I changed my process after that night:

  1. No more long autonomous mode (>2h) on a branch without human checkpoints. I now prefer splitting the work into four 2-hour sessions with review in between.
  2. Explicit instructions on what the agent is NOT allowed to do (“don’t touch imports in other files,” “don’t change return types,” “if you’re unsure about a cascade, stop and report”).
  3. Force-log any git resets and cancelled commits in the final report. I added a line to my CLAUDE.md: “document every failed attempt, even if you reset it.”
  4. Review before merge, not after. No automatic push to main even if CI is green.

Takeaway

Autonomous agents in 2026 aren’t magic. They shift the development bottleneck toward review, without removing it. For a senior dev who knows what they’re looking for, that’s a net win. For a junior who doesn’t know what they’re looking at, it’s an invisible pit of technical debt.

I keep using them. But I won’t sleep “while they work” anymore. I run them while I’m sitting right there, in focused 1-2 hour blocks, with continuous review. Less sexy as a tweet, but that’s where the value actually comes from.

Kevin Aubrée

Keep reading

Back to blog