fix(door): say so when the security governor is failing open

`Runtime::judge` returns "governor unreachable (fail-open)" whenever the
provider never answers, and the door caller drops `reason` on every allow —
so a judge model that is rate limited or uncredited turns the governor into a
rubber stamp with nothing anywhere saying so. Fail-open stays (a governor
outage must not halt agents), but it is now loud.

Found while removing the metered key as a dependency: the governor reads
CLAWMATES_JUDGE_MODEL, which was `claude-opus-4-8` — a model that is 429 on
this deployment's subscription. gw-04's .env now points it at `glm:glm-4.7`,
matching CLAWMATES_VALIDATOR_MODEL: funded separately, uncapped, and a
different family from the agent it judges.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
Omar Sobh
2026-08-08 23:03:30 -07:00
co-authored by Claude Opus 5
parent 9c9439a271
commit 52500a689c
+12
View File
@@ -166,6 +166,18 @@ async fn policy_decide(
} else {
state.runtime.judge(system, &request).await
};
// Fail-open is deliberate, but a governor that is failing open on EVERY
// request is a security control that has quietly stopped existing —
// and the caller drops `reason` whenever it allows, so nothing said so.
// `judge()` returns this exact prefix when the provider never answered,
// which a rate-limited or uncredited judge model does on every call.
if allow && reason.starts_with("governor unreachable") {
eprintln!(
"mcp_door: WARNING — the door governor is FAILING OPEN for {mcp_tool} \
({reason}). Every outbound action is being approved unjudged. Point \
CLAWMATES_JUDGE_MODEL at a reachable model."
);
}
if !allow {
return PolicyOutcome::Deny(format!("governor agent vetoed — {reason}"));
}