feat(missions): choose the independent validator per mission (#53)
`CLAWMATES_VALIDATOR_MODEL` is deployment-wide, so proving Slice 2 put a second
provider on the critical path of EVERY phase verdict. `cross_provider_judge`
deliberately does not fall back when the independent judge fails — a verdict
quietly produced by a same-family model would claim a property it does not have —
so a z.ai outage makes phases unmeetable rather than merely unverified. That is a
per-mission trade, not a per-deployment one.
`missions.validator_model` (0068), settable at create, with three distinct states
because an empty string and NULL mean opposite things in a nullable text column:
NULL use the deployment default
'' explicitly NO independent validator — judge with the house model.
The default must not quietly reinstate independence a mission was
told to skip.
'glm:glm-4.7' this spec, subject to the same three refusals as before:
same-family rejected, unregistered provider rejected, and a failed
independent judge does not fall back.
Whitespace counts as empty: a column hand-set to " " meant to say nothing.
478 tests pass, clippy clean. Behaviour is unchanged for existing missions — they
have NULL and so keep following the deployment default.
This commit is contained in:
@@ -137,6 +137,9 @@ pub struct NewMission<'a> {
|
||||
/// rootfs. Deliberately unconstrained in the schema: which images exist is a
|
||||
/// property of the NODES, not of the database.
|
||||
pub backend: Option<&'a str>,
|
||||
/// Independent validator for this mission's verdicts. `None` = deployment
|
||||
/// default; `Some("")` = explicitly none. See migration 0068.
|
||||
pub validator_model: Option<&'a str>,
|
||||
pub phases: Vec<NewMissionPhase>,
|
||||
}
|
||||
|
||||
@@ -165,9 +168,9 @@ pub async fn insert(pool: &PgPool, m: NewMission<'_>) -> Result<Uuid, DbError> {
|
||||
"INSERT INTO missions
|
||||
(id, workspace_id, title, template_kind, team_id,
|
||||
team_template_id, repo_id, schedule, status, description, config,
|
||||
runtime_kind, target_node_id, backend)
|
||||
runtime_kind, target_node_id, backend, validator_model)
|
||||
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,'draft',$9,$10,
|
||||
COALESCE($11,'zeroclaw'),$12,$13)",
|
||||
COALESCE($11,'zeroclaw'),$12,$13,$14)",
|
||||
)
|
||||
.bind(mission_id)
|
||||
.bind(m.workspace_id)
|
||||
@@ -182,6 +185,7 @@ pub async fn insert(pool: &PgPool, m: NewMission<'_>) -> Result<Uuid, DbError> {
|
||||
.bind(m.runtime_kind)
|
||||
.bind(m.target_node_id)
|
||||
.bind(m.backend)
|
||||
.bind(m.validator_model)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user