use std::time::Duration;
use super::CheckResult;
use crate::prompt_facts::{
render_stale_tier_s_report, stale_tier_s_facts, TierSFact, PROMPT_FACTS_METHOD,
TIER_S_MAX_FACTS, TIER_S_REAFFIRM_DAYS,
};
const FETCH_TIMEOUT: Duration = Duration::from_secs(5);
pub async fn check_tier_s_reaffirmation() -> CheckResult {
let label = "Tier S re-affirmation".to_string();
let answer = match crate::client::call_with_timeout(
PROMPT_FACTS_METHOD,
serde_json::json!({}),
FETCH_TIMEOUT,
)
.await
{
Ok(answer) => answer,
Err(e) => {
return CheckResult::unknown(
label,
format!(
"{PROMPT_FACTS_METHOD} did not answer ({e:#}) — the Tier S surface lives in \
the daemon's palace registry, so its freshness is UNKNOWN while the daemon \
is unreachable. Start it with `trusty-memory service start` and re-run."
),
);
}
};
let facts: Vec<TierSFact> = match answer.get("facts").cloned() {
Some(rows) => match serde_json::from_value(rows) {
Ok(facts) => facts,
Err(e) => {
return CheckResult::unknown(
label,
format!(
"{PROMPT_FACTS_METHOD} answered, but the rows could not be decoded as \
Tier S facts: {e}. A daemon predating #4890 does not report \
`affirmed_at`; upgrade it to get a real answer."
),
);
}
},
None => {
return CheckResult::unknown(
label,
format!(
"{PROMPT_FACTS_METHOD} answered without a `facts` array: {answer}. Tier S \
freshness is UNKNOWN."
),
);
}
};
interpret_tier_s_facts(label, &facts, chrono::Utc::now())
}
pub(super) fn interpret_tier_s_facts(
label: String,
facts: &[TierSFact],
now: chrono::DateTime<chrono::Utc>,
) -> CheckResult {
let stale = stale_tier_s_facts(facts, now);
if stale.is_empty() {
return CheckResult::pass(
label,
format!(
"{} of {TIER_S_MAX_FACTS} standing facts active, all affirmed within \
{TIER_S_REAFFIRM_DAYS} days",
facts.len()
),
);
}
CheckResult::warn(
label,
format!(
"{} of {} active standing fact(s) have not been re-affirmed in {TIER_S_REAFFIRM_DAYS} \
days (ADR-0028 D8). Tier S is injected into every turn of every session, so a rule \
that stopped being true is paid for on every turn until someone notices. Nothing was \
removed — retirement is a deliberate human act. Re-affirm a rule by asserting it \
again with `kg_assert` (re-asserting it verbatim counts), or retire it with \
`remove_prompt_fact` passing its `subject` and `predicate`. Overdue:{}",
stale.len(),
facts.len(),
render_stale_tier_s_report(&stale),
),
)
}
#[cfg(test)]
#[path = "tier_s_tests.rs"]
mod tests;