use tracing::{info, warn};
use crate::{config::ReviewConfig, pipeline::runner::ReviewDeps};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum GateOutcome {
Proceed,
Skip(String),
Degraded(String),
}
pub async fn preflight_context(config: &ReviewConfig, deps: &ReviewDeps) -> GateOutcome {
let search_url = &config.search_url;
let analyzer_url = &config.analyzer_url;
let index = &config.search_index;
let search_fut = async { deps.search.health().await };
let analyze_fut = async {
match deps.analyze.as_ref() {
Some(a) => a.has_analysis(index).await,
None => false,
}
};
let (search_health, analyze_ready) = tokio::join!(search_fut, analyze_fut);
let search_ok = match &search_health {
Ok(h) if h.is_healthy() => true,
Ok(h) => {
warn!(status = %h.status, "trusty-search health is not 'ok'");
false
}
Err(e) => {
warn!("trusty-search health probe failed: {e}");
false
}
};
if !search_ok {
if config.context.require_search {
return GateOutcome::Skip(format!(
"trusty-search unreachable at {search_url} — start it (`trusty-search start`); \
refusing to review without code context (set \
TRUSTY_REVIEW_REQUIRE_SEARCH=false or [context] require_search=false to opt \
into a degraded, non-authoritative review)"
));
}
info!(
"trusty-search unavailable but require_search=false — proceeding DEGRADED (non-authoritative)"
);
return GateOutcome::Degraded(format!(
"trusty-search unavailable at {search_url}; review produced WITHOUT code context"
));
}
if !analyze_ready {
if config.context.require_analyze {
return GateOutcome::Skip(format!(
"trusty-analyze unreachable/not-ready at {analyzer_url} — start it \
(`trusty-analyze serve`) and index `{index}`; refusing to review without \
static-analysis context (set TRUSTY_REVIEW_REQUIRE_ANALYZE=false or \
[context] require_analyze=false to opt into a degraded, non-authoritative review)"
));
}
info!(
"trusty-analyze unavailable but require_analyze=false — proceeding DEGRADED (non-authoritative)"
);
return GateOutcome::Degraded(format!(
"trusty-analyze unavailable at {analyzer_url}; review produced WITHOUT \
static-analysis context"
));
}
GateOutcome::Proceed
}
pub fn degraded_banner(reason: &str) -> String {
format!(
"> ⚠️ **DEGRADED REVIEW — NOT AUTHORITATIVE**\n>\n> {reason}.\n> \
This review ran WITHOUT required project context and must not be treated \
as a trustworthy verdict. Start the missing daemon and re-run for an \
authoritative review.\n\n"
)
}
#[cfg(test)]
#[path = "context_gate_tests.rs"]
mod gate_tests;