One Expensive Night, and the Junior/Senior Split It Forced
I want to talk about how I split the work between my junior and senior developer agents. But it's worth starting with the night this design came from, because the whole thing only exists because I made an expensive mistake.
I'd been running long overnight tasks — write out the whole job, hand it to the orchestrator to plan, and let it run while I slept. Twenty-four hours plus, no drama. One night I sent through a task that wasn't even that complex, just a bit ambiguous. And I'd made one error: I never put a limit on the code reviews.
What happened next is the part that cost me. The junior would write the code, the reviewer would look at it, and because of the ambiguity it kept landing on the opposite of what it wanted. Picture it: the code uses a for loop. The reviewer says, no, use a while loop. The junior switches to a while loop. The reviewer comes back and says, well, use a for loop. Round and round it went, all night, and I burned through every token I had on that one task. Something that should have been a simple pass got stuck in a loop — because there was no hard out, and the reviewer had no real way to escalate to the senior. That was the expensive night. It forced me to redesign the split properly, and that's what the rest of this is.
The first design (and why it failed)
When I first built the harness, I'd worded it so a junior developer handled the small and medium tasks, and a senior developer handled the more complex ones. I left the decision of how to split a given task between them up to the orchestrator.
It didn't work. The orchestrator, trying to solve the task as quickly as possible for me — which is in its design — started leaning on the senior developer far more than the junior. It wasn't what I was after at all. I was paying for the expensive model on work the cheap one could probably have done, and the senior was becoming the default rather than the exception. So I came up with a new idea.
The redesign: everything goes to the junior
Instead of having both agents available at the start, I made the junior the only one in the room at the beginning of a task. Every task — regardless of how easy or how hard — goes to the junior developer first. No exceptions.
Then I added a hard rule: every coding round gets a code review. The junior writes the code, the reviewer reads it back. If the reviewer finds problems, the task goes back to the junior to fix, and then it goes back to the reviewer. That worked fine for a while, and I was pretty happy with it.
A separate reviewer catches what the writer misses because it's a fresh context — it didn't write the code, so it has no investment in the choices. It reads the work the way a stranger would, before I ever see it.
The night it still broke
This is where that expensive night comes back in. Even with the review step, there was no real ceiling. The reviewer could send work back, but if the task was ambiguous enough, the two of them could argue with each other forever — for loop, while loop, for loop — and never stop. There was no tripwire that said "this has gone far enough, bring in someone with more judgement." So I built one.
The escalation rule
Now the loop is: the junior codes, the reviewer checks. If there's a problem, it goes back to the junior to fix — that's fix round one. The reviewer checks again; if the problem's still there, it goes back to the junior — fix round two. If it comes back from the reviewer a third time with the same problem still present, the task escalates automatically to the senior developer.
Two failed fix rounds is the tripwire. It's enough to tell the difference between "made a typo" and "doesn't understand the problem." One failure is normal. Two failures on the same critical issue means the junior is spinning, and the right move is a different context with more judgement — not a third try with the same one. And it's enforced, not suggested. The system honours the stop; the junior can't talk itself past it on the third attempt.
The senior runs the same loop. It gets the task, does its round, the reviewer checks it. If it gets stuck again, the whole task stops and waits for me to give it direction. That's the real safety net: when even the expensive model is blocked, a human decides, rather than the machine looping into the void.
I've come to think of escalation as a ceiling rather than a workhorse. It's a backstop for the work that escapes the junior — not something you lean on by default.
The rule I like best
Here's the part of the design I'm fondest of. The orchestrator cannot call the senior developer. It's not in its makeup to do that. Escalation only ever happens through the junior-and-reviewer cycle — the tripwire fires, and the senior arrives because the loop demanded it, not because the orchestrator decided to reach for it.
That matters more than it sounds. If the orchestrator could just pull in the senior whenever it liked, it would — it's built to solve fast, and the senior is the fast answer. Then you're back to the original problem: the expensive model becomes the default, and the cheap one never learns to struggle. Keeping the senior out of the orchestrator's hands is what keeps escalation a ceiling instead of a habit.
The public bishop-harness repo has a similar role it calls Vasquez — the heavier agent that only turns up when the lighter ones can't close the task. Different name, same shape.
The model economics
The reason this stays affordable is the model assignment. My junior developer runs on a very cheap model — Sonnet — and does the actual work there. My code reviewer runs on Opus, but not at high effort; it's checking the code, not really thinking hard about it. And the senior developer also runs on Opus, with a bit more effort — and it generally closes the issue in the first round.
So the cheap model produces, the expensive model judges. Production is high-volume and only needs to be good; judgement is low-volume and needs to be right. Paying top-tier rates for production is the expensive mistake — and this split is how I avoid it.
The numbers, as I've seen them
I've probably run well over a thousand primary tasks through this system. "Task" here means an orchestrator run, which then breaks down into a lot of sub-tasks — lots of junior dev rounds, lots of code reviews. So the actual volume of work is much higher than a thousand; that's just the count of top-level jobs.
Of those, it escalates from junior to senior maybe... I don't really want to say one out of ten, but it's probably about one to two out of twenty. Which probably makes a little more sense — so, roughly 5 to 10% of the time. The rest never leaves the junior.
And two rounds of a junior dev cost far less than one round of a senior dev. You can run quite a few junior rounds for the same money as a single senior round. A code review, remember, is just checking the code — it's not really actually thinking.
The senior itself? In all those thousand-odd tasks, I think it's only been twice that the senior couldn't solve it. And even then — if there's something it needs to find out from me, it still asks that question. If it hits something completely blocked, I read through what it's tried, give it a suggestion, and let it go forward. The point is it stops and waits, instead of burning the night.
The principle underneath
If you want to run real long-term tasks — the kind that run for a day or more — you have to think about the little nuances. Things can get caught in a loop, and there has to be a way out. A way to escalate from a lower model to a higher one, on a rule rather than on a hope. That's been the secret to making long-term tasks actually work for me: not a smarter agent, but a loop with a real exit.
Next: Running Agents Locally Instead of Through the API — why running agents loopback-only on your own machine changes the cost and control story, and the one case where the cloud API still wins.
More in this series
- Up: Bishop: building an AI agent harness for real development work
- Sideways: "Why My Orchestrator Isn't Allowed to Write Code" (
orchestrator-does-not-write-code, now live) and "Running Agents Locally Instead of Through the API" (running-ai-agents-locally, to be published) — URLs resolve at publish time. - Repo: bishop-harness