feat(coordinator): add the LLM-as-coordinator baseline (SPEC R11, Table 8) - #418
Merged
Merged
Conversation
This was referenced Jul 22, 2026
…le 8) SPEC §1.3 invariant R11 — "Trained coordinator > LLM-as-coordinator" — is, as the merged verifier `analysis/coordinator_vs_llm.py` puts it, "the whole thesis of TRINITY": a tiny (<20K-param) SLM + linear head trained with sep-CMA-ES should route better than simply prompting an LLM to act as the coordinator. If a frozen LLM picking the models and roles matched the trained head, the evolved coordinator would not be earning its keep. The R11 verifier is merged (James-CUDA#360) and so is its report script, but nothing in `src/` could ever *produce* an LLM-as-coordinator run, so the baseline accuracy it grades had to be typed in from the paper. This is the producer. It is a drop-in and changes nothing. `orchestration/session.py` already routes through a `Policy` protocol — `decide(transcript_text, *, sample, rng) -> (agent_idx, Role)` — precisely so alternative coordinators can be swapped in ("tests pass a mock"). `LLMCoordinatorPolicy` implements that protocol, so measuring R11 needs no change to the session loop, the trained policy, or the submission path. A test drives it through the REAL `run_trajectory` to prove it. Two integrity properties the comparison depends on, both enforced here: - **A parse failure is not a routing decision.** On an unparseable reply the policy falls back deterministically but COUNTS it, and an agent outside the pool (or a 0/out-of-range index) is a parse failure, never a guess — coercing an unknown name to index 0 would fabricate a decision the model never made. A run that mostly fell back is a constant policy wearing the LLM's name, so `DecisionStats.is_representative()` gates whether it may be compared at all. - **The baseline is not free.** The trained head costs ~0 output tokens per decision; an LLM coordinator spends a full completion per turn on top of the answering call. `DecisionStats` accumulates that spend so R11/R12 can be budget-matched rather than accuracy-only, which would flatter the baseline by ignoring the tokens it burns to choose. The client is synchronous because `Policy.decide` is (the trained coordinator is a local forward pass, and `run_trajectory` does not await it), so this takes a plain `chat` callable and a live run supplies a sync bridge. `StubDecider` backs offline tests, mirroring `fugu.conductor.StubConductor` behind `PromptedConductor`. Purely additive; no torch, no network at module scope. Full suite: 1798 passed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
James-CUDA
force-pushed
the
minion_llm_coordinator
branch
from
July 23, 2026 11:37
baaca29 to
415cd1e
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The gap
SPEC §1.3 invariant R11 — "Trained coordinator > LLM-as-coordinator" — is, in the words of the merged verifier
analysis/coordinator_vs_llm.py, "the whole thesis of TRINITY": a tiny (<20K-param) SLM + linear head trained with sep-CMA-ES should route better than simply prompting an LLM to act as the coordinator. If a frozen LLM picking the models and roles matched the trained head, the evolved coordinator would not be earning its keep.The R11 verifier is merged (#360), and so is
scripts/coordinator_vs_llm_report.py. But nothing insrc/could ever produce an LLM-as-coordinator run — so the baseline accuracy it grades had to be typed in from the paper. This PR is the producer.It's a drop-in, and changes nothing
orchestration/session.pyalready routes through aPolicyprotocol:…precisely so alternative coordinators can be swapped in — its own docstring notes "tests pass a mock".
LLMCoordinatorPolicyimplements that protocol, so measuring R11 needs no change to the session loop, the trained policy, or the submission path.test_it_satisfies_the_session_policy_protocol_end_to_enddrives it through the realrun_trajectoryrather than asserting the shape by hand.Both coordinators receive the identical
session._transcript_textcontext, or the comparison would not be fair.Two integrity properties the comparison depends on
1. A parse failure is not a routing decision. On an unparseable reply the policy falls back deterministically, but it counts every fallback. Crucially, an agent outside the pool — or a
0/out-of-range menu index — is a parse failure, never a guess: coercing an unknown name to index 0 would fabricate a routing decision the model never made, and R11 would then grade that fabrication.A run that mostly fell back is not an LLM-as-coordinator at all, it's a constant policy wearing the name.
DecisionStats.is_representative()gates whether the run may be compared, so R11 can't pass for the wrong reason.2. The baseline is not free. The trained head costs ~0 output tokens per decision; an LLM coordinator spends a full completion per turn, on top of the answering call.
DecisionStatsaccumulates that spend so an R11/R12 comparison can be budget-matched instead of accuracy-only — an accuracy-only read flatters the baseline by ignoring the tokens it burns to make each choice.Design notes
Policy.decideis — the trained coordinator is a local forward pass, andrun_trajectorycalls it without awaiting. So this takes a plainchatcallable and a live run supplies a sync bridge, rather than forcing an async signature onto the protocol.StubDeciderbacks the offline tests, mirroring howfugu.conductor.StubConductorbacksPromptedConductor— the established pattern in this repo for a prompted baseline with a no-network counterpart.sample/rngare accepted and ignored — the baseline's stochasticity lives in the model's own decoding, not in a policy-side categorical.Verification
pytest tests/ -q→ 1798 passed, 0 failed (35 new)ruff check src/ scripts/repo_governance/→ clean;mypyon the new module → cleanimport trinity.coordinator.llm_policyleavestorchout ofsys.modulesPurely additive: no existing caller changes behaviour, and nothing is wired into the submission path.
🤖 Generated with Claude Code