LLM Panel Orchestration in OpenClaw: Config-Backed Routing, Timeout Classes, and Honest Dissent Without Chaos
The current public workflow exposes one config-backed Standard mode. Routine questions skip the panel rather than inventing a second public tier.
The Hard Part Was Never “Ask More Models”
Running a full panel of LLMs in parallel is not the interesting problem. Spawning the children is easy. Keeping the result coherent is the real work.
The failures I kept hitting were never glamorous:
- one expected panelist quietly did not get launched
- a distinct executor lane finished, but the answer did not auto-surface to the parent thread
- a partial result arrived, then the orchestrator went silent
- a slow non-core model held the whole panel hostage
- a zero-quorum dissent looked louder than it deserved because the authority rules stayed implicit
- late completions arrived after the real answer was already delivered
The real unit of work is not “query many models.” It is “finish one user-visible review exactly once, with lineup integrity, evidence provenance, visible weighting, and bounded latency.”
That is the problem the consult-panel workflow now solves inside my OpenClaw setup.
The Design Decision That Saved the Whole Thing
The best decision was architectural, not prompt-related.
The public invocation surface stays deliberately small. Natural requests like consult the panel, panel review, or have the consultants weigh in map into the same Standard orchestration layer, while the routing truth stays in durable config and ops docs rather than inside the prompt wrapper.
The canonical grounding for current behavior lives in three places: the routing config, the consult-panel skill, and the routing operations guide. That keeps the live policy inspectable without hardcoding model/version logic into the user-facing orchestration layer.
Keep the skill thin. Keep the routing truth in config and ops docs.
The user-facing skill exists to do orchestration:
- detect that the user wants a panel review
- resolve the configured lineup
- shape the review packet
- spawn the panel
- track completions and synthesize the result
Every panelist gets the same review packet. That sounds trivial, but it is one of the most important fairness rules in the whole workflow: differences in output should come from the models, not from packet drift.
What it does not do is hardcode model/version policy. That lives in durable config and the ops guide instead:
routing configoperations guidelineup resolverpacket-size gate
That split matters because it keeps model churn from leaking into workflow prose. If a route, authority family, local corroborator, or timeout policy changes, I want to edit and validate config—not rewrite orchestration logic in six places.
consult-panel. The only public config-backed mode is standard; retired or internal labels do not create extra public tiers.
What the Current Panel Exposes Publicly
The exact roster is mutable config, not article content. The stable public contract is a set of role classes with different authority and blocking behavior.
| Role class | Quorum effect | Blocking behavior | Public meaning |
|---|---|---|---|
| Authority-family lane | At most one credit per configured family | Counts toward the minimum family quorum | Independent decision evidence with verified route/model identity. |
| Attested direct lane | Only through its configured family | Must carry a receipt bound to task, model, and report bytes | A non-session executor can contribute without weakening provenance. |
| Nonblocking local corroborator | Zero authority quorum | May be cancelled after its opportunity window | Can add evidence or dissent but cannot manufacture validity. |
| Degraded or unusable lane | No credit | Recorded with an operational reason code | Missing evidence, timeout, or shape failure is degradation—not a substantive vote. |
The important thing is that the current lineup is config-resolved, not reconstructed from memory. A dormant route definition is not an active lane, and several models from the same provider family do not create several independent quorum credits.
If the resolved lineup and the accepted launch set differ, the run is degraded immediately. Here, the configured minima are the minimum usable evidence families and the minimum independent authority-family quorum. If both minima still pass, the result may remain effective; if either fails, the run cannot promote partial evidence into a valid panel.
resolve_panel_shape(mode="standard")
That distinction matters: degraded describes missing or unusable operational evidence. effective answers whether the configured evidence and authority-family gates still passed. They are related, not opposites.
The Schema That Matters More Than the Prompt
The routing config now carries three layers of truth:
- role mapping — what each semantic lane currently points to
- Standard shape — authority families, minimum usable family quorum, direct executors, and nonblocking corroborators
- panel policy — timeouts, evidence requirements, opportunity windows, completion, and degraded behavior
A simplified shape looks like this:
{
"publicModes": ["standard"],
"standard": {
"authorityFamilies": ["<family-a>", "<family-b>", "<family-c>"],
"minimumUsableFamilies": "<configured quorum>",
"directExecutors": ["<attested direct lane>"],
"localCorroborators": {
"quorumCredit": 0,
"blocking": false
}
},
"evidencePolicy": {
"requestedAndServedIdentityRequired": true,
"atomicLaneArtifactsRequired": true,
"effectiveStatusDerivedFromConfiguredGates": true
}
}
Why Timeout Policy Had To Change
One of the most useful corrections was embarrassingly simple:
Parallel panel timeout is tail-latency control, not additive latency math.
That sounds obvious once you say it. But it matters operationally.
If authority-family quorum is complete and only nonblocking corroborators remain, I should not keep acting as if the panel is incomplete in the same way it would be when required authority families are missing. Those are different situations.
| Class | Quorum credit | Tail policy | Practical rule |
|---|---|---|---|
authority_family |
At most one per family | Wait through the configured authority deadline. | Required evidence until family quorum is satisfied or impossible. |
attested_direct |
Through its family only | Use the configured direct-executor deadline. | Admit only when task, model, report path, bytes, and receipt bind. |
local_nonblocking |
Zero | Cancel after the configured opportunity window. | Useful corroboration or dissent, never validity by itself. |
operational_failure |
Zero | Terminalize with a reason code. | Degradation is recorded separately from substantive dissent. |
This was the shift from “wait for everyone because fairness” to “wait intelligently because the panel has structure.”
Weighting Helped, But Only After I Put It in Its Place
I do use capability and evaluation evidence as a rough reference band. I do not use it as a vote-counting formula.
The useful signal is structural rather than brand-specific:
- authority is counted by independent configured family, not raw model count;
- a direct lane matters only with an executor receipt and verified identity;
- local corroborators may improve the review but remain zero-quorum;
- operational failures never become negative votes.
The Async Rules Are Why the Workflow Finally Feels Trustworthy
The strongest part of the current setup is no longer the model lineup. It is the coordination layer around it.
The workflow now explicitly tracks:
- expected panelists
- pending vs completed lanes
- first substantive result arrival
- whether interim checkpoint updates have already been sent
- which panelists are eligible for timeout-drop
- whether a distinct executor surface needs targeted read-back
- whether the panel is merely closed or fully superseded
The practical control loop looks more like this now:
resolve lineup from config
spawn all expected panelists
track expected vs completed lanes
send one bounded checkpoint when the first real result arrives
send one second checkpoint only if state changes meaningfully
if one executor surface remains, inspect its own receipt before waiting blindly
after the second checkpoint, timeout-drop non-core stragglers
synthesize immediately once all expected lanes are complete or dropped
That is not glamorous. It is also the part that stops the panel from becoming a reliability anti-feature.
Final Delivery Needs a Bridge Back, Not Just a Background Thread
The long-running-work version of this bug has the same shape as the panel bug: the worker can finish correctly while the user-facing conversation still never receives the final result. Moving a task into the background protects the main turn budget, but it does not automatically solve final delivery.
The reliable pattern is a small delivery contract created before the detached work starts. It records the origin conversation, the work conversation, the single update target, and the delivery mode. Default detached work reports to its bound work thread. Bridge-back to an origin thread is explicit, not something the worker infers from memory or a nearby chat summary.
A background worker should never guess where its final answer belongs.
The practical loop now looks like this:
persist a delivery contract before launch
launch the worker with the contract reference and final-ready marker
wait for an explicit final-ready signal
dry-run the delivery plan against the persisted contract
send the final answer once through an idempotent ledger
split oversized finals deterministically instead of truncating them
record delivered message identifiers when the channel returns them
That is deliberately boring distributed-systems hygiene: stable identity, explicit target selection, idempotent side effects, and observable delivery state. The agent-specific twist is that the final answer is prose, not a database row, so duplicate suppression and oversized-message handling need to preserve the user-visible text rather than silently summarize or drop it.
June 2026 Follow-Up: Artifact-Backed Completion Beats Transcript Luck
The later failure mode was subtler: panelists could finish and write useful results, but the parent thread was not always the healthiest place to notice, validate, synthesize, and deliver them. The failure was not “the panel had no answer.” It was “the answer existed in artifacts, while the user-facing orchestration path was under context and delivery pressure.”
That pushed the design one step past status-line completion. A child saying “ready” is now provisional. The durable proof is an expected result artifact in the run directory, validated against the panel ledger. The finalizer reads the ledger, checks each expected lane, and only then decides whether the run is waiting, ready, blocked, or explicitly partial.
resolve expected panel lanes
write a panel-run ledger
record result paths before launch
accept compact child status lines as hints
validate authoritative result artifacts
finalize from the ledger, not from transcript vibes
deliver once through the recorded target
This matters most when the parent conversation is already large. A huge thread can be slow, compacting, or temporarily write-locked. The panel should not lose functional quality just because the parent transcript is an overloaded coordination surface. The safer pattern is to promote the important state into a small artifact ledger and let the parent thread become one consumer of that ledger, not the only place the truth can live.
If a panel result is important, make it recoverable from artifacts before trusting the chat transcript to remember it.
The general lesson is simple: if a task is long enough to detach, it is long enough to deserve an explicit final-delivery path. Otherwise “background work” just moves the failure from timeout to silence.
August 2026 Follow-Up: Terminal Outcomes Are Data
Blind-first evidence gathering exposed another lifecycle bug: three very different failures were all surfacing as generic command failures. A malformed lane report, a source snapshot that changed after planning, and an operator-typed invalid command do not have the same scope or meaning.
The durable fix was to record expected operational failures at the narrowest correct scope:
| Failure | Scope | Recorded result | Next-phase eligibility |
|---|---|---|---|
| Report path, shape, or size is invalid | One lane | Terminal, non-usable lane with a reason code | Recomputed from the remaining evidence |
| Allowed source fingerprint changed after planning | Whole evidence run | Atomically invalidated terminal run with changed-root diagnostics | False; no synthesis bundle is created |
| Configured evidence or authority quorum is not met | Whole panel | Insufficient evidence | False |
| Some evidence is missing but configured minima pass | Whole panel | Operationally degraded, potentially effective | Determined by the explicit gates |
Structured controllers should receive a normal terminal payload with fields such as terminal=true and eligibleForNextPhase=false. Human-oriented CLI mode may still use a nonzero exit to make the stop visible. The point is that orchestration code should not have to scrape a process error to discover state the workflow already recorded durably.
freeze evidence
if source fingerprint drifted:
record run = invalidated
record terminal = true
record eligible_for_next_phase = false
create no synthesis artifact
return the same terminal state on repeated freeze
Repeated freeze after invalidation must be read-only and idempotent. Otherwise a controller retry can turn one observed source drift into several competing terminal stories.
Snapshot Mutable Inputs Before Planning
A live managed config tree is a bad evidence root because normal unrelated work can change it while gatherers run. The safer pattern is to materialize a point-in-time snapshot outside the live tree, fingerprint the source before and after copying, fingerprint the copy, and refuse the snapshot if any identity differs.
Runtime bytecode and cache files should not invalidate a source tree when they are outside the source-code claim surface. Ignoring those known runtime artifacts reduces false drift, but it creates an explicit boundary: fingerprint equality does not prove that stale compiled bytecode could not affect execution. A clean build or separate runtime test must own that behavior claim.
Why Watchdog Coverage Had To Become a Launch Invariant
The ugliest real failure was not “a model said something weird.” It was simpler: the panel could emit a clean waiting on X checkpoint and then never speak again.
That is why I no longer treat “children launched” as a valid waiting state by itself. A multi-child run is only allowed to settle into waiting after two things are true:
- the resolved lineup matches the accepted spawn set
- real follow-up wakes exist to carry the panel to the next checkpoint or terminal synthesis
In my implementation that means a small launch guard plus a watchdog planner. The planner defaults those wakes to visible delivery and rejects internal-only coverage by default, because a hidden watchdog is not much comfort if the user still experiences silence.
Just as important: generated plans are not proof. I only count coverage as real once the follow-up jobs actually exist. When watchdog creation fails, the run is degraded immediately instead of being presented as a trustworthy “waiting” state.
A second late bug was older queued checkpoints leaking out after a terminal answer. The local fix there was much smaller than the bug report made it sound: a tiny monotonic per-run delivery ledger that suppresses stale intermediate checkpoints while still allowing a legitimate retry of the final answer if delivery itself needs one more shot.
Watchdog Hardening: The Bug Was Not Just Silence, It Was Repeated Almost-The-Same Progress
The next reliability problem was more embarrassing than dramatic. The panel was no longer always going silent, but it could still emit repeated user-facing checkpoint blocks that looked meaningfully new only because they arrived on different async branches.
The important diagnosis was that this was not a transport-duplication issue. The real bug lived in orchestration: child completions and later watchdog/finalization wakes could each recompute the same visible non-terminal state and both decide it was worth sending.
The hardening rule that actually helped was small and specific:
For non-terminal updates, validate not only run identity and closure state, but also whether the same visible progress state for this panel run has already been delivered.
In plain English, the panel now remembers the shape of the last user-visible checkpoint for the current run. If a later async branch only regenerates the same visible progress state, the watchdog is supposed to do nothing and return quietly instead of spamming the thread with a cosmetically fresh duplicate.
- duplicate same-state checkpoint → suppress it
- watchdog recomputes the same visible state → suppress it
- material state actually changes → allow the next bounded checkpoint through
I trust the fix more because it stopped being just a theory. The deterministic regression suite now passes the full targeted duplicate-state cases, including the same-state duplicate case, the watchdog-recomputation case, and the “state really changed, so the next checkpoint is legitimate” case.
The Two Delivery Bugs That Taught Me the Most
1. Different executors do not share one completion surface
One of the nastier failure modes was: a direct or external executor finished, useful text existed, but the parent thread still looked silent. The fix was not “wait harder.” The fix was to require an executor-specific receipt and targeted read-back when that surface is the last blocker.
2. Fresh user follow-up is not a late duplicate
Another failure mode was subtle: after a final synthesis was already delivered, a true late completion and a fresh user-authored follow-up could land in the same general window. If the orchestrator treated the whole situation as late-result cleanup, it could accidentally suppress the new user instruction.
That is why the current workflow keeps panel-closed and superseded as separate states.
What I Learned About User-Facing Synthesis
I used to think the final answer should hide as much orchestration detail as possible. That turned out to be half-right.
The user does not need raw routing trivia. But they do need visible weighting.
So the synthesis now tries to make one thing explicit:
Did the disagreement come from independent authority families, or only from zero-quorum corroborators?
That one distinction dramatically improves the trustworthiness of the result. “There was disagreement” is weak. “The authority families aligned; only nonblocking corroborators dissented” is actionable.
What Other People Can Steal From This Pattern
You do not need my exact stack to steal the shape.
If you run any multi-model or multi-agent review workflow, the parts worth copying are:
- semantic roles, not hardcoded versions
- config-resolved lineup before spawn
- same packet for fair comparison
- authority-family and zero-quorum distinctions visible in synthesis
- watchdog coverage as a launch invariant, not a hopeful afterthought
- explicit final-delivery contracts for detached work
- timeout as tail-latency control
- executor-specific receipts and completion handling
- clear distinction between closed, degraded, and superseded runs
What I would not copy is “always ask all models.” Standard review is expensive. It is worth it when the user explicitly asks, or when the decision is ambiguous or costly enough to justify the latency. Routine work should skip the panel instead of inventing a weaker public mode.
The Design Rule I Trust Most Now
If I had to compress the whole thing into one line, it would be this:
A good panel workflow does not just collect opinions. It defines which opinions count how much, how long they are allowed to block, and how the final answer reaches the user without ambiguity.
That is what changed the workflow from an interesting prompt trick into a reusable piece of operating infrastructure.