95: exit 0 lied — a real dispatch never reached Ornith
Where we left off
PR #672 fixed two independent gaps in the local-agent stack: the port literal (8080→8000,
carried over from PR #665) and oMLX's mandatory Authorization: Bearer auth, which
synlynk local doctor now sends correctly. With that PR merged, synlynk local doctor
went fully green — oMLX reachable, roster matched, aider installed.
What moved the goalpost
Doctor passing looked like "done." It wasn't. The first real end-to-end test — actually
dispatching a task to the local agent (synlynk dispatch local --task "Scan this repo and give me a summary") — reported OK (exit 0), 0 tokens in / 0 tokens out, 0 files touched. That last number should have been the tell: Aider ran, but did nothing.
The captured log told the real story:
litellm.BadRequestError: LLM Provider NOT provided. Pass in the LLM provider you
are trying to call. You passed model=Ornith-1.0-9B-4bit
Aider doesn't talk to the OpenAI-compatible endpoint directly — it goes through a library
called litellm, which infers the request format from the --model string. Known OpenAI
model names resolve automatically. An arbitrary local name like Ornith-1.0-9B-4bit does
not, even with --openai-api-base pointed at oMLX — litellm needs an explicit provider
prefix (openai/Ornith-1.0-9B-4bit) to route the call at all. Without it, litellm errors
before sending anything, and Aider's subprocess still exits 0 because from its own
perspective, nothing crashed.
This bug has existed since the local agent's original ship (PR #204/205/207) — every
real dispatch to it has been silently failing at the litellm layer the entire time. It
was only caught now because this was the first time anyone actually ran a live dispatch
against a real, authenticated oMLX instance instead of just checking doctor.
What this PR ships
One functional line, in _local_dispatch_model_flags() (synlynk/local_agent.py):
"--model", f"openai/{model_id}",
.agents/local.json's roster IDs are untouched — they still store the bare oMLX-native
name (Ornith-1.0-9B-4bit), matching what /v1/models actually returns, which is what
cmd_local_doctor()'s roster check depends on. The prefix is added only at the point the
Aider CLI flags are built, so doctor and dispatch each use the form the tool they're
talking to actually needs.
Caught a real regression along the way: a stale assertion in
tests/test_dispatch_local_agent.py (a different test file than the one the first
dispatch touched) still expected the un-prefixed model flag. The full project suite
(1575 tests) surfaced it; a second small Codex dispatch fixed the one-line assertion.
Verified directly — not from job-status alone — before merging either commit.
Backfilled as Addendum 3 / Task Group 8 into the same design spec and implementation plan that already carry Addenda 1 and 2, continuing this project's pattern of folding narrow post-ship gaps into the original document rather than spawning new ones.
Where this leaves the local-agent track
Doctor going green was necessary but not sufficient — this is the fix that makes a real
dispatch actually reach the model. Next verification step: re-run the same test dispatch
("Scan this repo and give me a summary") on main after this merges, and confirm Ornith
produces an actual response this time.
Next goalpost
Same two open threads as before this PR: user review of the fleet-parity reliability cluster spec, and the not-yet-started herdr-integration brainstorm.