use std::time::Instant;
use crate::control::state::SharedState;
use super::applied_index::check_applied_index;
use super::integrity::verify_redb_integrity;
use super::registry_verify::verify_registries;
use super::repair_integrity::heal_orphan_rows;
use super::report::VerifyReport;
pub async fn verify_and_repair(shared: &SharedState) -> crate::Result<VerifyReport> {
let start = Instant::now();
let gate = check_applied_index(shared);
if !gate.is_ok() {
tracing::error!(
cache_applied = gate.cache_applied,
watcher_current = gate.watcher_current,
gap = gate.gap,
"catalog sanity check: applied_index gap — metadata replay incomplete"
);
}
let (registry_outcome, integrity, integrity_healed) = {
let catalog = shared.credentials.catalog();
let mut integrity = verify_redb_integrity(catalog);
let mut total_healed = 0usize;
loop {
let (_, healed) = heal_orphan_rows(catalog, integrity);
total_healed += healed;
integrity = verify_redb_integrity(catalog);
if healed == 0 {
break;
}
}
if total_healed > 0 {
tracing::info!(
healed = total_healed,
remaining = integrity.len(),
"catalog sanity check: integrity self-heal pass completed"
);
}
let reg = verify_registries(shared, catalog)?;
(Some(reg), integrity, total_healed)
};
let (registry_divergences, all_repairs_ok) = match registry_outcome {
Some(o) => {
if let Some(metrics) = shared.system_metrics.as_deref() {
for (registry, count) in &o.counts {
let outcome = if count.detected == 0 {
"ok"
} else if count.repaired == count.detected {
"warning"
} else {
"error"
};
metrics.record_catalog_sanity_check(registry, outcome);
}
}
(o.counts, o.all_repairs_ok)
}
None => (Default::default(), true),
};
Ok(VerifyReport {
applied_index_ok: gate.is_ok(),
applied_index_gap: gate.gap,
integrity_violations: integrity,
integrity_repaired: integrity_healed,
registry_divergences,
all_repairs_ok,
elapsed: start.elapsed(),
})
}