feat(missions): say at boot whether the independent judge can be reached

The z.ai credential expired mid-session and the first symptom was a two-phase
mission failing after BOTH its VMs had run — the phase completed, delivered,
pushed, and then one evaluation row said "the independent validator could not be
reached this pass".

`cross_provider_judge` refusing to fall back to the agent's own provider is
correct: a verdict from the same family is not an independent check, and
producing one quietly would claim a property the verdict does not have. The cost
of that refusal is that a dead validator makes EVERY `done_when` phase
unmeetable — and the information needed to know that existed from the moment the
server booted. Nobody was told until it was expensive.

The sibling of `runtime_preflight`, and the same stance: a report, not a gate.
The server must still boot with a broken validator — refusing to start turns a
degraded deployment into a dead one, and a mission that opts out
(`validator_model = ''`) is unaffected.

Two faults, kept distinguishable because they send an operator to different
places: `Unregistered` (no provider by that name — the evaluator will refuse it
rather than judge with the default, so register one) versus `Unreachable` (it
resolved and the call failed — fix the credential). Collapsing them into "the
validator is broken" is the kind of merge that costs an hour.

The probe is a real completion through `Runtime::complete` — the same
resolve-then-stream path the judge itself takes. A models-list or a HEAD would
pass for an expired key, a revoked key, and a key with no quota, which are
exactly the cases worth catching; and a probe that dialled the provider its own
way could pass while the real call fails.

`NotConfigured` is reported too, and not as an error: a deployment may choose the
house model. It is still worth saying out loud that the check running is not an
independent one.

551 tests pass, clippy clean.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
Omar Sobh
2026-08-07 05:16:54 -07:00
co-authored by Claude Opus 5
parent 08dd227a45
commit 3300c9d149
3 changed files with 164 additions and 0 deletions
+5
View File
@@ -332,6 +332,11 @@ async fn run() -> Result<(), String> {
}
}
}
// The other half of runtime_preflight's question: the runtime has the TOOLS,
// but can the independent JUDGE be reached? A dead validator makes every
// done_when phase unmeetable, and without this the first symptom is a
// mission failing after its VMs have already run.
cm_api::validator_preflight::report_at_boot(runtime.clone());
cm_api::phase_runner::spawn(pool.clone(), runtime.clone(), node_hub.clone());
// Per-mission runtime container sweeper (C3): tears down mission
// runtime containers 30 min after the mission reaches a terminal