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) = match shared.credentials.catalog() {
Some(catalog) => {
let reg = verify_registries(shared, catalog)?;
let raw = verify_redb_integrity(catalog);
let (remaining, healed) = heal_orphan_rows(catalog, raw);
if healed > 0 {
tracing::info!(
healed,
remaining = remaining.len(),
"catalog sanity check: integrity self-heal pass repaired \
orphan rows by reconstructing StoredOwner entries from \
primary rows' in-band owner fields"
);
}
(Some(reg), remaining, healed)
}
None => (None, Vec::new(), 0),
};
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(),
})
}