DEV Community

Mahad Ansar
Mahad Ansar

Posted on

What I Changed After My Multi-Agent Coding Orchestrator Kept Choosing Zero Workers

Last week, I wrote about a result I was not expecting.

I had built a multi-agent coding orchestrator, deliberately created workloads that should have favored parallelism, and then watched the supervisor choose zero workers in all six free-choice scale runs.

When I forced delegation, the workers still did not beat the supervisor working alone on the workloads I measured.

At the time, my main takeaway was:

The optimal number of workers can be zero.

I still believe that.

But after continuing to build and actually use the system, I realized there was another problem hiding behind the same symptom.

Sometimes "zero workers" meant:

The supervisor saw the orchestration tools and decided delegation was not worth it.

Other times it meant:

The supervisor never discovered the orchestrator in the first place.

Those are completely different failures.

That distinction changed the project more than another round of benchmarking would have.

Two very different kinds of zero workers

In the benchmarks from my previous article, discovery was never in question.

The prompts explicitly told the supervisor that delegate_task and delegate_tasks existed.

So when it chose zero workers, that was a routing decision.

It had the option to delegate.

It chose not to.

And on the workloads I measured, forcing it to delegate did not produce evidence that the decision was wrong.

But normal usage is different.

I do not want to start every Codex session with:

Use sol-luna-orchestrator.
Use three workers.
Run them in parallel.
Give one High effort and two Medium.
Enter fullscreen mode Exit fullscreen mode

If I have to manually design the worker topology every time, I have not really built an orchestrator.

The goal is to install the tool once, open a normal Codex session, describe the engineering task, and let the parent decide:

Task
  |
  v
Discover orchestration guidance
  |
  v
Is delegation worth it?
  |
  +--> No  -> work solo
  |
  +--> Yes -> one bounded task
  |          dependent tasks in sequence
  |          or independent tasks in parallel
  |
  v
Review evidence
Enter fullscreen mode Exit fullscreen mode

In fresh sessions, that was not consistently happening.

The original discovery hint installed by the CLI effectively said:

Consider the orchestrator when delegation might help.

The problem with "consider" is that the parent first has to know there is something to consider.

If it never looks at the MCP server, it never sees the detailed routing guidance.

So this was not a delegation-policy problem.

It was a discovery problem.

Discovery has to happen before the routing decision

I changed the onboarding behavior.

sol-luna-orchestrator init now installs a small managed instruction that tells a fresh Codex session to discover the configured orchestrator first, and then use the actual guidance to choose between:

  • working solo
  • delegating one task
  • running dependent tasks sequentially
  • running independent tasks in parallel

The important part is what the hint does not say.

It does not say:

You must delegate.

Zero workers remains explicitly valid.

That order matters:

Bad:

Should I delegate?
    |
    v
Maybe I should look for a tool?


Current:

Discover the orchestration policy
    |
    v
Understand the available execution modes
    |
    v
Now decide whether delegation makes sense
Enter fullscreen mode Exit fullscreen mode

I wanted discovery to be stronger without turning it into forced fan-out.

Those are two separate decisions.

Then I tried it on a real task

After releasing v0.8.0, I wanted to test this outside the benchmark harness.

So I opened a fresh Codex session in another repository.

The repository was my GitHub profile README.

It had become outdated, especially around the open-source work I had done on Sol-Luna, and I wanted the agent to research my current public work and rewrite the profile into a concise landing page.

The important part was the prompt.

I did not mention:

  • Sol-Luna
  • MCP
  • delegation
  • workers
  • Luna
  • delegate_task
  • delegate_tasks

I simply asked it to inspect the repository, research the relevant public work from primary sources, verify claims, update the README, check the links, and leave the changes uncommitted.

The parent discovered the orchestration guidance on its own.

Then it made a fairly reasonable split.

The broad public-source investigation was one substantial, bounded research task, so it delegated that to a Luna worker at High effort.

Fresh Codex session: the parent discovered the orchestration guidance and chose one bounded research worker without being told to delegate.
Fresh Codex session: the parent discovered the orchestration guidance and chose one bounded research worker without being told to delegate.

The worker was explicitly read-only.

The parent kept the actual editorial decisions, synthesis, README editing, source checking, and final review.

Later, when I asked it to refine the README it had just written, it explicitly chose zero workers.

Its reasoning was basically that the remaining change involved one short, tightly coupled file and was already understood.

So within one real workflow I got both:

Broad independent research
        |
        v
1 bounded worker


Tightly coupled final refinement
        |
        v
0 workers
Enter fullscreen mode Exit fullscreen mode

That is much closer to what I wanted when I started this project.

Not "multi-agent all the time."

Adaptive execution.

I am not claiming this run proved delegation was faster or cheaper.

There was no solo control.

It was not a benchmark.

What it demonstrated was narrower: a fresh session discovered the orchestrator without being prompted toward it, used a worker where it saw a clean seam, and later declined to use one where it did not.

That is a much more useful acceptance test than simply checking whether the tool call technically works.

The next problem was context

Once workers actually start receiving useful tasks, another obvious question appears:

How much of the supervisor's context should they get?

The naive answer is "all of it."

I increasingly think that is the wrong answer.

The parent may know:

  • the full conversation
  • architectural history
  • rejected approaches
  • unrelated files
  • debugging attempts
  • user preferences
  • prior decisions
  • broader product context

A worker handling one bounded task usually does not need all of that.

But giving it too little context creates another problem: it has to rediscover information the parent already knows.

So I added structured Context Capsules.

A worker can receive selected fields such as:

relevant context
interfaces
dependencies
invariants
upstream decisions
known pitfalls
Enter fullscreen mode Exit fullscreen mode

The goal is not context maximization.

It is context allocation.

Give the worker the smallest useful view of the problem.

That turned out to apply in the other direction too.

The supervisor does not need every byte back either

Delegated tasks return evidence.

Originally, successful verification could send a lot of routine command output back into the parent's context.

But once a check passed, most of that output was rarely useful.

What the parent normally needs is:

  • what changed
  • what was verified
  • whether verification passed
  • whether scope was violated
  • whether the worker's claims matched observed changes
  • any discrepancy or failure that needs attention

So v0.7.0 added Compact Evidence Packets.

Successful verification stdout/stderr can be removed from the structured result while failures remain intact.

That gave me a more general rule:

Good multi-agent systems need to optimize context in both directions.

The worker should not receive the entire supervisor history.

The supervisor should not receive every routine byte the worker produced.

The useful information is somewhere in the middle.

A worker's PASS is still just a claim

This is probably the design principle I care about most in the project now.

Suppose a worker returns:

PASS

Changed:
- src/auth.ts
- src/auth.test.ts

Tests passed.
Enter fullscreen mode Exit fullscreen mode

That looks reassuring.

But it is still generated text.

So Sol-Luna treats the worker's report as a claim, not the final truth.

A worker-reported PASS is treated as evidence to inspect, not as the final conclusion.
A worker-reported PASS is treated as evidence to inspect, not as the final conclusion.

The orchestrator can independently:

  • observe which files actually changed
  • compare observed edits with claimed edits
  • check declared file scope
  • detect integration conflicts
  • re-run allowlisted verification commands
  • report discrepancies back to the parent

And then the parent still owns the final review.

The trust chain becomes:

Worker does the task
      |
      v
Worker reports what happened
      |
      v
Orchestrator independently observes and verifies
      |
      v
Parent reviews the evidence
      |
      v
Decision
Enter fullscreen mode Exit fullscreen mode

That is intentionally different from:

Worker says PASS
      |
      v
Ship it
Enter fullscreen mode Exit fullscreen mode

The real-world README run even found a small flaw in this logic.

The research worker had an explicitly read-only contract.

It correctly changed zero files.

But the orchestrator still emitted a generic warning:

Worker claimed PASS but no file changes were recorded.

For an implementation task, that warning can be useful.

For an investigation that was explicitly forbidden from editing anything, zero changed files is exactly what should happen.

So that run produced a new backlog item: classify zero-change results from the task contract's intent, not from the result alone.

That is the kind of bug I wanted real usage to expose.

Parallel agents eventually turn into normal concurrency engineering

There was another lesson from building the worker path that had very little to do with language models.

Parallel workers use isolated Git worktrees.

At one point, batches would occasionally fail even though the worker itself had done nothing wrong.

The problem was git worktree add.

Multiple processes were modifying shared metadata under .git/worktrees concurrently.

The failure was rare. I reproduced it at roughly once per thousand worktree creations.

That made it annoying enough to be real and infrequent enough to hide during casual testing.

The fix was not better prompting.

It was synchronization.

Operations that mutate the shared worktree metadata are now serialized.

Worker execution remains parallel.

That experience was a useful reminder that once you build enough infrastructure around AI agents, a lot of the difficult bugs become very ordinary software-engineering bugs:

  • race conditions
  • shared state
  • cleanup
  • cancellation
  • process trees
  • conflicting writes
  • malformed logs
  • stale configuration
  • backwards compatibility

The model is only one part of the system.

If workers run in parallel, I also need to know what they are doing

Another problem became obvious during longer delegations.

From the normal Codex interface, a worker could be running for a while and the parent could appear to be doing nothing.

So I added:

sol-luna-orchestrator activity --watch
Enter fullscreen mode Exit fullscreen mode

It shows things like:

  • execution mode
  • active and total workers
  • model and reasoning effort
  • elapsed duration
  • verification state
  • changed-file counts
  • failures
  • concurrency

Initially this sounds like a straightforward observability feature.

Then the privacy question appears:

What exactly should an activity log contain?

Earlier activity output exposed far more of the delegated task than was necessary.
Earlier activity output exposed far more of the delegated task than was necessary.

The redesigned view keeps the operational signal: worker, effort, state, duration, and verification status.
The redesigned view keeps the operational signal: worker, effort, state, duration, and verification status.

A useful activity feed does not need the worker's entire objective, prompt, context, source code, or successful command output.

So the current event stream deliberately excludes those.

Task IDs are opaque rather than derived from task descriptions.

A parent can optionally provide a short activity label, but that is documented as locally revealing because it is persisted.

There is also a separate diagnostic log with more detail, and that file is explicitly treated as sensitive.

Nothing is remotely transmitted by Sol-Luna.

Both files are local.

The broader lesson for me was:

Agent observability becomes a privacy-design problem almost immediately.

"Log everything" is not a good default when prompts and source code may be involved.

I also had to correct my own architecture description

The earlier version of the project described GPT-5.6 Sol as the supervisor.

That was how I personally used it, but it was not actually an architectural requirement.

The parent is now explicitly model-agnostic.

Any compatible Codex parent with access to the MCP can supervise.

The worker side remains GPT-5.6 Luna.

That correction also forced me to clean up another argument I had been making around cost.

I had been thinking in terms of:

expensive parent
      +
cheaper workers
      =
possible savings
Enter fullscreen mode Exit fullscreen mode

But that only makes sense when the parent model the user selected is actually priced above the worker under the current pricing schedule.

It is not a universal property of the architecture.

Raw token counts are not cost either.

A system can use more raw tokens but fewer credits, or the reverse, depending on the models involved.

So the project now makes the narrower claim:

Delegation has a coordination cost. Worker economics depend on the models you actually selected. No realized cost saving has been measured.

I would rather weaken a claim than keep one the implementation or data no longer supports.

What the project looks like now

The basic philosophy is still simple:

Not every task should be delegated.

But the machinery around that idea has grown quite a bit.

The parent can now choose:

0 workers       -> solo
1 worker        -> one bounded task
N sequential    -> dependent/shared work
N parallel      -> independent/disjoint work
Enter fullscreen mode Exit fullscreen mode

Each worker gets its own reasoning effort.

Parallel workers use isolated worktrees.

Tasks have declared scopes.

Verification is independently re-run.

Claimed and observed changes are compared.

Workers receive structured context instead of a full conversation dump.

Successful evidence can be compacted before returning to the parent.

Fresh sessions can discover the orchestration policy without the user naming the tool.

Single and batch delegations are observable through the CLI.

And importantly, the parent still owns the final judgment.

v0.8.0 is now live on npm.

At the time I am writing this, npm is showing 659 downloads in the last seven days.

I am deliberately calling those downloads, not users. Package downloads can include CI, reinstalls, automated activity, and other things that do not map one-to-one to humans.

Still, for something I originally built because I wanted it for my own Codex workflow, seeing other people pull the package has been pretty cool.

What I want to build next

I do not think the next step is "add more workers."

The roadmap currently goes in a different direction.

Worker continuation

If a worker already investigated or implemented something, the parent should be able to continue that worker's thread instead of starting from zero every time.

Bounded repair

If verification finds a narrow problem, the same worker should be able to receive the evidence and make a bounded correction.

Not an infinite autonomous loop.

A controlled one.

Reasoned retries and effort escalation

A timeout, a misunderstood requirement, an implementation failure, and an environment problem should not all trigger the same retry behavior.

The system should classify the failure before deciding what to do next.

Adaptive worker routing

Eventually I want the supervisor to choose not just effort and worker count, but among a user-authorized pool of worker capabilities.

But I deliberately put this later in the roadmap.

Routing a failed task to a more expensive model before continuation and bounded repair exist would mostly be a sophisticated way to spend more money on the same failure.

The recovery loop needs to become smarter first.

The thing I changed my mind about

When I started building Sol-Luna, the interesting problem looked like:

How do I run several coding agents at once?

I do not think that is the interesting problem anymore.

Running several agents is relatively easy.

The harder questions are:

  • Did the supervisor even discover the orchestration policy?
  • Should this task be delegated?
  • Where is the clean task boundary?
  • What context does the worker actually need?
  • What evidence does the parent actually need back?
  • Can the worker's claims be independently checked?
  • What happens when verification fails?
  • How much information should observability persist?
  • When is the coordination overhead simply not worth it?

The fresh-session README task was a tiny example, but I liked what happened.

One part of the work had a clean research seam.

The supervisor used a worker.

The next part was tightly coupled and already understood.

The supervisor used zero workers.

No swarm.

No fixed workflow.

No rule saying more agents must be better.

Just two different execution decisions for two different pieces of work.

That is increasingly what I think orchestration should mean.

Try it

The project is open source on GitHub:

mahadansar/sol-luna-orchestrator

Install it from npm:

npm install -g sol-luna-orchestrator
sol-luna-orchestrator init
Enter fullscreen mode Exit fullscreen mode

Then open a fresh Codex session and work normally.

You should not need to choose a worker count or manually call the orchestration tools.

And if the parent decides the right worker count is zero, that may be exactly what you want.

Final thought

My first experiment taught me:

More agents are not automatically better.

The work since then taught me something slightly broader:

Delegation itself is the easy part. Making delegation worth its complexity is the real engineering problem.

I think that is the direction I want to keep pushing.

Top comments (0)