My Agent Closed Most of the Open Projex Issues in One Round Without Me Reading the Code
On a Saturday afternoon in August, I told my agent to fix every open issue in the Projex repo and prepare a release. There were ten open. Some were HIGH priority. Some were LOW.
I went for a walk.
When I came back, the work had landed across three branches. Each subagent had worked in its own worktree on its own branch. The build was green on each one. The tests were green. The lint was green. The typecheck was green. The release manager subagent had drafted the changelog. Nine of the ten issues were closed. The tenth stayed open — it needed a real union restructure, not a mechanical fix.
I had not read a single line of the diff yet.
What I actually asked for
I had a backlog of small things in the Projex repo. Tagged-union cleanups. Type tightening. Documentation gaps. A bug where one of the smart-grid props was a documented prop but a no-op at runtime. A redundancy where two functions with slightly different spellings did the same thing.
I described this to my main agent. The main agent looked at the issue tracker, saw the labels (bug, enhancement, documentation), grouped them by file area, and dispatched three subagents in parallel.
One subagent got the package.json + bundling issues. One got the type-system + tagged-union issues. One got the documentation + test-coverage issues. Each one worked in a separate worktree on a separate branch. Each one committed locally and reported back. A fourth subagent, the release manager, handled release prep alongside them: the version bump and the changelog.
I watched the transcript. Mostly I stayed out of the way. I made tea.
What the subagents actually did
I saw the dispatch messages. I saw the report-back messages. I did not read the intermediate diffs. The subagents were set up to commit per-issue. Each commit was meant to be independently reviewable.
A few things I noticed in the report-backs:
- One subagent caught a redundancy I hadn't seen. Two exported functions,
normalizeStatsandnormaliseStats, with the American and British spellings. Both did the same thing. The codebase had drifted to the British spelling in the actual logic. The American spelling was the alias. Both were exported. The subagent deprecated the American one with a JSDoc tag and updated the docs to point at the British spelling. - One subagent found a related issue while fixing another one. While narrowing the
ProjectStatsunion, it noticedFetchProjectDataResult.commitswas usingundefinedwhile sibling fields usednull. The subagent opened a new issue and included the fix in the same branch. - One subagent flagged a peer-dependency problem I had been ignoring for two months. The CLI packages (
ts-morph,chalk,@inquirer/prompts,commander) were installed by every consumer, even ones who only imported the components. The subagent moved them to optionalpeerDependenciesso consumers importing only components stopped dragging in the CLI bundle.
None of these were in my original brief. The subagents went past the edges of what I asked for, in the direction of "things that were obviously wrong in the same file area."
Where I read the code
The first time I read any of the code was after all three subagents finished and opened their PRs. I skimmed the diffs before merging. Not a line-by-line review. A sanity check.
I was looking for decisions the AI made without asking me. Function names I wouldn't have picked. Behaviour that wasn't in the brief. Edits that touched code outside the file area I asked about. The kind of things a real code review catches, except I was reviewing the decisions, not the code.
Some of the diffs were four lines. Some were thirty. None of them were complex enough to need a real review. They were tagged-union narrowings, JSDoc additions, dependency relocations. The kind of work where you skim it once, you understand it, you move on.
If I'd skimmed each PR as it landed, I'd have read the rename with no idea the docs were about to change under it. Reading the batch, I could see the normaliseStats deprecation and the docs update pointing at it in the same sitting. The batch skim was faster than piecemeal would have been.
Where I did intervene
I didn't push back on any of the code. The three branches each shipped clean. I steered the architecture around the loop, not the code inside it.
The dispatch went out as three parallel opencode run invocations, not three subagents. I asked for that change when the opencode TUI failed on the first attempt and the right path was to skip the interactive layer. I also argued for splitting the release prep out from the fix work, because trying to do both in the same dispatch kept blocking on the release-manager hitting its timeout before the fix branches landed.
What this loop replaced
My previous loop was one PR at a time. I'd describe an issue to the agent. The agent would open a PR. I'd skim it, sanity-check the decisions, merge or push back. One issue, one PR, one round of skimming. Repeat.
For this kind of small mechanical work, that's fine. It works. But the context switching adds up. Each PR is its own session — its own dispatch, its own transcript, its own review pass. The overhead is small per PR and large per backlog.
The new loop:
- Describe the backlog.
- Wait.
- Skim the batch of PRs.
- Push the release prep.
The release prep runs alongside the fix work instead of after it. One description covers all of them.
I want to be careful about what I'm claiming here. I'm not claiming the subagents did better work than the agent would have done one PR at a time. Most of these issues were mechanical. The interesting decisions — which redundancy to deprecate, which naming to standardize — the agent would have surfaced them either way, given the brief. What I'm claiming is that the per-PR overhead moved out of my hands and the interesting decisions stayed in my hands.
Reading at the end, not in the middle
I did not read the code while it was being written.
In the old loop, I skimmed each PR after the agent opened it. One PR at a time.
In the new loop, the skim happened after the writing finished across all three PRs. The subagents were the feedback loop during the work. I was the feedback loop at the end.
There's a different cost structure. A wrong fix in the old loop was caught in the per-PR skim, or it shipped. A wrong fix in the new loop is caught in the batch skim, or it ships.
I shipped none of the wrong fixes in this batch. The issues were mechanical and the code area was small. If I'd asked the subagents to redesign the normalise function, I would have read every line, pushed back, rewritten pieces.
The loop works for the kind of work that has a clear right answer. The loop does not work for the kind of work that needs taste. I haven't found the line yet.
For this batch, the line was "moves stuff around, adds JSDoc, narrows types." Below the line, I delegated. Above the line, I didn't. The line is in a different place than I would have guessed.
Running it again
I'm going to run this loop again. On a different repo. On a different kind of work.
I want to see what happens when the issues aren't mechanical. I want to see where the line moves. I want to see what kinds of work I delegate that I later wish I hadn't, and what kinds I keep that the loop could have handled.
I'm not going to delegate design decisions. I'm not going to delegate "what should this library do". I'm going to delegate "make this library do what it already says it does, correctly."
The batch skim at the end is non-negotiable. That's the part I own.