DEV Community

Cover image for 964 hours of agent work in six days. Here's where my own hours went.
Dominique Siacci
Dominique Siacci

Posted on

964 hours of agent work in six days. Here's where my own hours went.

On October 7 I dictated the plan to an agent: we would start an autonomous loop, I would close my session, come back a few days later, and the 200 templates of our app designer would be done.

The last check came back green on the morning of October 15: 12,074 end-to-end cases, zero gaps. In between, 28 coding agents worked on it, up to 20 at a time, for 964 hours of session time in total, with more than 6,000 commits and 717 deployments to our dev environment. The 200 templates turned out to be 569 units of work. And I never really closed the session.

I'm the CTO of GoodBarber, an app platform. The agents wrote the code. This post is about where my own hours went, because that's the part I had wrong.

The job

In our back-office, the logic that applies a setting sits with the part of the UI that edits it. The refactor moves that logic into documented functions with typed options, which the UI and a new API both call, and proves along the way that nothing changes for the people using it.

The unit of work is an abstraction: something that exists in the UI and defines a set of controls over one kind of entity, in one layout variant. A script cut the whole app designer into 552 units 13 minutes after I dictated the rules for the cut, then 569 after a second pass.

The numbers
Design 2 weeks, Sep 23 to Oct 7
Execution 6 days, Oct 9 to Oct 15
Units 569: 445 done, 124 dropped or merged, mostly duplicates
Controls about 20,600
Agents 28 workers, up to 20 at a time, and one integrator
Session time 964 h
Commits more than 6,000
Deployments to dev 717
Final campaign 12,074 cases, 0 gaps

Two weeks before the six days

Before those six days, I spent two weeks on the design, from September 23 to October 7. Most of it ran at Claude Opus 5.5's highest effort level (max), with around 200 sub-agents exploring the code and reviewing proposals. For the first week I also had ultracode on, a setting where the model plans a multi-agent workflow for each substantial task. On the first day, 32 agents spent five hours on the strategy alone: 18 explored, 5 wrote competing proposals, 3 judged them, 1 combined the best of them, 2 attacked the result and 3 ran the final checks.

I used to think of the loop as the project. In practice it was the last part, and it held because the two weeks before had settled what a finished unit looks like.

What "done" meant

The checks were the review. A unit went to our dev environment when they passed, and merging into production code stays a human step.

All the checks prove one thing from different sides: for every control and every test value, the new code path leaves the application in exactly the same state as the old one, the change can be undone exactly or is listed as an exception, and the API behaves like the UI. This is how often each one caught something:

Check Units where it caught something at least once
A headless browser performs the real gesture, and the test bench must send exactly the same request 140
A snapshot of the whole application before and after each test value, old path against new, then an exact undo and redo 160
The API must do what the UI does, read back what it wrote, and reject invalid input without side effects 114
A plain-language request written by one agent, run by another that only sees the documentation, must end in the expected state 40
The final campaign on the deployed code, through real gestures and through the API 0 gaps in 12,074 cases

When a check failed three times, the unit was parked on a question or marked as blocked, and the worker moved on. In the final campaign, 48 cases were impossible, each one traced to the line of code that makes it so.

One unit, then ten, then twenty

I didn't start with twenty.

When Step Scale What came out of it
Oct 7–8 Step 0 1 unit, in a guided session 11 changes to the process
Oct 8 Step 1 10 units in one loop, one after the other 8 more changes
Oct 9 Pilot 4 workers and an integrator 16 improvements, live during the first wave
Oct 10 Step 2 8 workers first weekend: 183 units, 930 commits
Oct 11, evening Step 2 20 workers overnight into Monday: 173 units in 11 hours

The conditions to go from four to eight were written down before Saturday: the same proofs as when units ran one after the other, no worker touching another worker's test app, no two deployments overlapping. Between the two waves of the pilot, the same four workers got much faster, mostly thanks to fixes made during the first wave, like a pre-check that runs the tests on a throwaway copy before asking for integration:

Pilot Wave 1 Wave 2
Median time per unit 2 h 54 min
Integration rejections 9 0
Median wait for integration 6 min 0.8 min

How it was organized

One session, the integrator, never takes a unit. It's the only one allowed to merge and deploy, and it fills the queue, reopens units and keeps the dashboard up to date. The workers pull: each one asks the queue for the next unit compatible with its own test app, works on its own branch in its own worktree, and asks for integration when its checks pass. Integration runs in batches of up to six, with one pre-check and one deployment per batch.

Diagram: workers, up to 20 at a time, pull units from a queue and ask one integrator for integration. The integrator is the only one that merges into git and deploys to the dev environment, and it keeps the queue and the dashboard where I answer questions.

Nothing goes through chat. The queue, git and a dashboard carry the whole protocol. The protocol itself is a file in git that every session re-reads at each round, so I could change the workers' instructions 53 times during the project without restarting anyone. When a worker hasn't sent a heartbeat for 45 minutes, its unit goes back to the queue and someone else picks it up.

Where my hours went

Two weeks of design, first. Then the questions.

A worker that hit a case it couldn't decide alone didn't wait for me. It wrote a question on the dashboard with the context, two or three options, what each one would cost, and its recommendation, then parked that unit and took the next one. It decided alone on anything reversible that users don't see. It had to ask about an architecture choice, a behavior it couldn't prove identical, a proof that needed to break the loop's own rules, or the scope.

There were 88 questions, from the first loop on October 8 to the end. Of the 59 that had both a recommendation and an answer from me, I followed the recommendation 47 times. One I didn't follow: proving one control meant calling a third-party service, which the loop's rules don't allow from a test. The recommended option was to simulate the service, another was to leave that control out of the API, and the third was to call the real service from a test app and break the rule. The question came in at 13:41, I picked the third option at 13:48, and it was applied at 13:53. That's the kind of decision I wanted to keep for myself.

From the first evening, a case that looked like one I had already answered stopped waiting too. The worker applied the precedent, logged it, and I validated or rejected it afterwards. There were 185 of those: 166 validated, 19 rejected. Ten of the 19 rejections were about the same thing, an agent excluding something that resisted. That one deserves its own post.

Decision fatigue

Put end to end, my answers would fit in an afternoon, somewhere between three and a half and five hours. That number is the misleading part.

Bar chart of my answers and checks on the dashboard per day, Oct 8 to Oct 15 (Paris time): 9, 11, 37, 3, 108, 69, 17, 2. 63 of Monday's 108 came in one hour.

They didn't come in one afternoon. They came all day, every day, and they piled up whenever I looked away. On Sunday I spent the day unblocking workers by hand and barely touched the dashboard. On Monday I cleared the pile, 63 decisions in one hour, about one a minute, most of them precedents to check. Each one was small and well framed, with its options and a recommendation, and that's what makes them tiring in bulk: by the fiftieth, the recommendation is the easy way out.

I rejected more precedents that Monday than on any other day, 13 out of 95, so I don't think I was rubber-stamping. But over the week I followed the recommendation 47 times out of 59, and from the logs I can't tell which of those were judgment and which were fatigue. I'll come back to that in a separate post.

What broke

  • The test bench lied on day one. It built its own requests instead of sending what the UI sends. I tested a migrated control by hand and it rejected every color coming from the color picker, which sends them without a "#". Since then a real browser replays the gestures, and the bench has to send exactly what the UI sends. That browser found format differences on about a hundred more controls along the way.
  • The single integrator became the bottleneck. Every integration went through one session, and the wait grew with the load (table below).
  • A worker went silent for almost three days, and nothing told me. Heartbeats were tracked for units in progress, so a worker holding no unit was invisible to the integrator. The others kept going.
  • Agents drop what resists. On Tuesday, an audit run by one of the workers found 516 controls left on the old path without any decision. I had all of them brought back into scope.
  • A rule nothing checked got broken. One worker removed a line from a file that the rules say is append-only.
Day Integration requests Median wait
Fri Oct 9 46 0.9 min
Sat Oct 10 48 1.0 min
Sun Oct 11 145 2.9 min
Mon Oct 12 251 11.0 min
Tue Oct 13 196 17.5 min
Wed Oct 14 29 1.6 min

At the worst hour, early on Sunday morning, the median wait passed an hour.

What's still true

  • The merge into our production code is still mine to do, after my own tests by hand. A loop never merges into production.
  • 132 changes that can't be undone automatically are listed, not solved, and I have to read them before anything ships.
  • What I'd do again: design at the highest effort, one automatic proof per unit and an end-to-end final campaign, scaling in steps with a review at each one, and questions that ask for a rule rather than a case. What I'd watch: the single integrator, the scope, my own decision load, and the pulse of every session, not just of every unit.

If you've run several coding agents in parallel on something real, where did your own hours go? Mine went into two weeks of design and more than 250 small decisions. I'll answer in the comments with how the queue and the dashboard work.

Top comments (12)

Collapse
 
omyvnss profile image
Om Yaduvanshi •

the integrator bottleneck is the finding i keep coming back to. twenty-eight workers in parallel, one sequential gate that absorbed all the variance: median wait went from under a minute on friday to over an hour at peak on sunday morning. a good reminder that parallel agents don't remove queueing theory, they just move the queue somewhere less visible.

Collapse
 
dsiacci profile image
Dominique Siacci •

Yes, and the worst hour didn't come at twenty workers. It came early Sunday morning, when eight were running, with a median wait above an hour, and on Monday, at twenty, the median for the day was eleven minutes. Batching kept it from being worse, up to six integrations behind one pre-check and one deployment.

Collapse
 
omyvnss profile image
Om Yaduvanshi •

the eight-worker sunday being worse than the twenty-worker monday is the detail that stuck with me. headcount wasn't the driver, arrival timing was, and batching six integrations behind one pre-check was the pressure valve.

Thread Thread
 
dsiacci profile image
Dominique Siacci •

Arrival timing is the right split. Eight workers on Sunday dumped work into one integrator that still took them one by one. Twenty on Monday still went through that same gate, but six integrations sat behind one pre-check, so 251 requests that day had an 11-minute median instead of another hour. Adding workers added arrivals. The valve was how many I let into one check.

Collapse
 
panthpatel profile image
Panth Patel •

The precedent step is the part I'd want to copy. My agents raise each question on the ticket as a card with options, then end their turn, and the answer is written into a memory file the next run reads, so the same question shouldn't come back. What I don't keep is your 166/19 split: a log of where an agent applied an old answer without asking. When you rejected a precedent, did the rule the workers re-read change too, or only that unit?

Collapse
 
dsiacci profile image
Dominique Siacci •

Only that unit. A precedent I validated became one the next workers could match against, and one I rejected reopened the unit at its worker and stayed out of the precedents, so the bad answer didn't spread. Changing the rule for everyone was a separate step, me editing the protocol file the workers re-read. Each one was logged with the case, the precedent the worker used and its solution, which is what made checking them afterwards possible. One rejection, for example: a Favorites button in the toolbar was missing because the test app had no Favorites section, and the worker reused an earlier precedent about a control absent from the test app to skip it, so I had it add the section to the test app instead.

Collapse
 
panthpatel profile image
Panth Patel •

So a rejection fixed the case, and the rule only moved when you edited the protocol yourself. Mine has no such split: every answer I give goes straight into the memory file the next run reads, so one wrong answer reaches every later run. Did the ten "excluded what resisted" rejections end in a protocol edit?

Thread Thread
 
dsiacci profile image
Dominique Siacci •

Yes. Those ten hours were me editing the protocol, not more rejects. A reject only reopened that unit and kept the bad answer out of the precedents. The rule that workers don't drop a control because it resists existed only after I wrote it, after an audit found 516 still on the old path with no decision. That's the split you're missing: a wrong answer on a card stays on the card, instead of becoming the file the next run reads.

Collapse
 
ascendra_ventures_92c21f2 profile image
Ascendra Ventures •

The part I found most useful is that two weeks of design came before six days of execution, and that the design mostly settled what a finished unit looks like. Once done meant the same application state before and after for every control, an exact undo, and an API that behaves like the UI, the loop had something it could not argue with.

The questions dashboard is the other piece. Each question came with options, costs and a recommendation, and you followed it 47 times out of 59. That suggests most of the human time goes into drawing boundaries rather than answering technical details: what is reversible, what users can see, what counts as scope.

We scope client work the same way, one clearly defined problem with the finish line written before anyone starts, and the share of effort that goes into defining it surprises people every time. Looking back, which of the 53 protocol changes would you have moved into the design phase?

Collapse
 
dsiacci profile image
Dominique Siacci •

Most of them, I wouldn't. The two weeks had already locked what "done" meant; the 53 edits were the loop hitting cases the design hadn't seen. Three I would have written on day one: the test bench has to send what the UI sends, not its own requests; a worker holding no unit still has a pulse; and "don't drop what resists" as a rule, not a hope, because an audit found 516 controls still on the old path with no decision. The rest were cheaper as protocol edits than as another week of design.

Collapse
 
reidmarlow profile image
Reid Marlow •

The decision to keep chat out of worker coordination and put the protocol in a versioned file re-read per round is the reason this scaled without drifting. Multi-agent refactors usually choke when teams try to coordinate via inter-agent messaging, where context windows bloat and message ordering rots over dozens of turns. Forcing workers to pull from a typed queue into isolated worktrees, prove state equivalence on throwaway clones, and hand off clean commits turns the integrator into an admission controller rather than a referee.

Collapse
 
dsiacci profile image
Dominique Siacci •

Right, and chat never crept back in because a worker always had somewhere else to put a question. It wrote a card on the dashboard with the context, two or three options and a recommendation, parked the unit and pulled the next one from the queue, and the dashboard refreshed every five minutes. The part the queue didn't cover was a worker holding nothing: heartbeats came every 10 to 20 minutes, but the 45-minute timeout only watched units in progress, so an idle worker could go quiet without anyone seeing it.