Operator model policy: haiku ONLY for genuine yes/no questions; anything
requiring thinking is opus-5; coding is sonnet-5.
The mission AGENTS were running haiku, and nothing in the product said so.
`provider_alias_for` maps every `claude-*` binding onto the single alias
`claude_cli.default`, so a crew whose `model_binding` reads `claude-sonnet-5`
— as this deployment's does — still ran whatever that alias pointed at, which
was `model = "haiku"` in the runtime config. The binding is cosmetic; the
alias is the truth.
Measured consequence on mission 01a00bbb: the coding agents claimed six INT
items complete and had committed three, and the done_when judge caught it by
auditing git history against the claims.
Model assignments, by what the component actually does:
evaluator (done_when judge) haiku -> opus-5 reads evidence, audits it
against the repo, writes
guidance. The verdict is a
boolean; the work is not —
and this is the one component
whose failure mode is passing
work that was never done.
judge_model 4-8 -> opus-5
mission_refiner 4-8 -> opus-5 composition
phase_summarizer 4-8 -> opus-5 composition
swarm planner 4-8 -> opus-5 planning
subscription preflight head 4-8 -> opus-5
fallback chain head 4-6 -> sonnet-5 haiku stays BELOW it as a
last-resort link, never a peer
Every value stays env-overridable; only the shipped defaults move.
Co-Authored-By: Claude Opus 5 <[email protected]>
Two changes so an empty metered account stops being a platform outage.
1. `build_provider` prefers the subscription token over ANTHROPIC_API_KEY.
A bare model name resolves to whatever this returns, so making it the
subscription means no server-side call can reach the metered key by
construction — rather than by a source-grep test that already missed four
call sites once. The metered key remains a fallback and now warns loudly
when it is the one in use; boot no longer requires it at all.
2. `complete_with_fallback` walks a declared chain when a model has no
capacity: opus -> haiku -> glm:glm-4.7 by default, overridable via
CLAWMATES_MODEL_FALLBACK, empty to disable. Measured on gw-04 today: opus
and sonnet return 429 on the subscription while haiku, GLM and Kimi all
return 200, so a capped window no longer means "the planner is gone".
The chain returns the model that ANSWERED, and every caller persists it —
mission_plan_proposals.author_model, mission_team_proposals.author_model, and
the swarm's step role. A plan drafted by the third link and filed as an opus
plan is a silent quality change, which is the failure shape this project keeps
paying for. Two negative controls hold the design: the chain never retries the
model that just failed as its own fallback, and it steps down ONLY for a
capacity failure — walking it on a malformed prompt would ask three models the
same bad question and report the third one's confusion.
Co-Authored-By: Claude Opus 5 <[email protected]>
The test that was supposed to prevent this grepped for the literal
`runtime.complete(` and passed while the phase planner (`mission_plan.rs`),
both swarm calls, and a second enhance path in `claws.rs` still billed the
pay-as-you-go account. They spell the receiver `state.runtime` or wrap the
call across lines, so the receiver name was never the thing to match. The
test now matches the METHOD, and covers all five files.
`complete_or` gains the rule that makes it safe to apply everywhere: a
`name:model` spec is an operator's explicit provider choice — the swarm
worker model is configured exactly that way — and is passed straight to
`Runtime::resolve_provider` untouched. Only a bare name is ambiguous, and a
bare name is precisely what resolves to the default provider. Hijacking a
chosen Kimi or GLM model onto Anthropic would be the same silent-substitution
bug pointed the other way.
`validator_preflight` and the evaluator judge keep calling the runtime
directly, on purpose: both exist to exercise the CONFIGURED spec.
Co-Authored-By: Claude Opus 5 <[email protected]>
Survey + fixes so the pipeline passes at the Docker level (no k8s).
- Remove k8s: drop the `sandbox-k8s` job (kind/Calico/--features k8s-tests) and the
"Helm chart lints" gate step. release.yml was already k8s-clean.
- Rust job:
- `cargo fmt --all` — fix pre-existing formatting drift (fmt --check was failing).
- clippy -D warnings: fix 3 lib warnings (cm-brain sort_by_key→Reverse, cm-api
fleet.rs doc list indentation, node_rules map_or→is_none_or).
- Regenerate the .sqlx offline cache (was missing the cm-runtime run_loop test
query → offline compile failed). DB-backed tests use testcontainers at runtime.
- Set SQLX_OFFLINE=true on the rust + e2e jobs so query! macros compile against
the committed cache deterministically (no DB needed at compile time).
- Frontend job:
- Fix the 1 ESLint error (useAgentTelemetry: no setState-synchronously-in-effect;
tag the slice with agentId + derive null on mismatch).
- Fix 2 stale panel-params tests (`terminal` is a valid app id now; assert the
current APP_IDS + use a genuinely-unknown id for the reject case).
Verified locally: fmt clean, clippy --all-targets -D warnings clean (offline),
frontend lint 0 errors, tsc clean, 86/86 frontend tests pass, build OK.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>