diff --git a/crates/gateway/src/execution_loop/mod.rs b/crates/gateway/src/execution_loop/mod.rs index a45e7af..e3da7b7 100644 --- a/crates/gateway/src/execution_loop/mod.rs +++ b/crates/gateway/src/execution_loop/mod.rs @@ -82,7 +82,7 @@ pub struct Fill { pub enum RespondOutcome { Ok { artifact: serde_json::Value, log: Vec }, Failed { reason: String, log: Vec }, - #[allow(dead_code)] // reserved for Phase 42 truth-gate (step 6) + // Constructed by the truth-gate check in run_inner (step 6, 2026-04-24). Blocked { reason: String, log: Vec }, } @@ -153,7 +153,21 @@ impl ExecutionLoop { .as_deref().unwrap_or(DEFAULT_REVIEWER_MODEL).to_string(); let max_turns = self.req.max_turns.unwrap_or(DEFAULT_MAX_TURNS); - // --- (6) TRUTH GATE — PORT FROM Phase 42 (TODO) --- + // --- (6) TRUTH GATE — Phase 42 wiring (2026-04-24) --- + // Evaluate truth rules for the request's task_class against a + // ctx built from the spec. Any rule whose condition holds AND + // whose action is Reject/Block short-circuits to Blocked before + // the executor loop runs. Mirrors queryd/service.rs SQL gate. + let truth_store = truth::default_truth_store(); + for outcome in truth_store.evaluate(&self.req.task_class, &self.req.spec) { + if !outcome.passed { continue; } + if let truth::RuleAction::Reject { message } | truth::RuleAction::Block { message } = &outcome.action { + let reason = format!("truth rule {} blocked: {message}", outcome.rule_id); + self.append(LogEntry::new(0, "system", "truth", "block", + serde_json::json!({ "rule_id": outcome.rule_id, "reason": reason.clone() }))); + return Ok(RespondOutcome::Blocked { reason, log: self.log.clone() }); + } + } // --- (1) PLAYBOOK BOOST --- let boost = self.fetch_playbook_boost(&self.req.operation).await.unwrap_or_default();