pub mod catalog;
pub mod config;
mod error;
pub mod finding;
pub mod index;
pub mod lock;
pub mod ontology_pack;
pub mod resolve;
pub mod review;
pub use catalog::Catalog;
pub use config::HarnessConfig;
pub use error::MifRhError;
pub use finding::Finding;
pub use index::{FindingIndex, IndexStats, IndexedFinding, SearchMatch, SimilarFinding};
pub use lock::ReviewLock;
pub use ontology_pack::OntologyPack;
pub use resolve::{Basis, MapRecord, ResolveContext, build_allowed, resolve_finding};
pub use review::{
FollowupBacklog, FollowupEntry, ReviewOptions, ReviewReport, TopicSummary, review,
write_followup,
};
pub fn build_search_index(
reports_dir: &std::path::Path,
topic_ids: &[String],
index: &mut FindingIndex,
) -> Result<(), MifRhError> {
let embedder = mif_embed::Embedder::load()?;
let mut indexed = Vec::new();
for topic in topic_ids {
let findings_dir = reports_dir.join(topic).join("findings");
if !findings_dir.is_dir() {
continue;
}
for file in review::list_finding_files(&findings_dir)? {
let finding = Finding::load(&file)?;
let text = index_text(&finding);
let vector = embedder.embed(&text)?;
indexed.push(IndexedFinding {
finding_id: finding.id,
topic: topic.clone(),
content: text,
vector,
});
}
}
index.rebuild(&indexed)
}
pub fn write_json_atomic<T: serde::Serialize>(
path: &std::path::Path,
value: &T,
) -> Result<(), MifRhError> {
let json = serde_json::to_string_pretty(value).map_err(|source| MifRhError::JsonSerialize {
path: path.display().to_string(),
source,
})?;
let tmp_path = path.with_extension("json.tmp");
std::fs::write(&tmp_path, json).map_err(|source| MifRhError::Io {
path: tmp_path.display().to_string(),
source,
})?;
std::fs::rename(&tmp_path, path).map_err(|source| MifRhError::Io {
path: path.display().to_string(),
source,
})
}
fn index_text(finding: &Finding) -> String {
let discovery_text = finding.discovery_text();
if !discovery_text.is_empty() {
return discovery_text;
}
finding
.entity
.as_ref()
.and_then(|entity| entity.get("name"))
.and_then(serde_json::Value::as_str)
.unwrap_or_default()
.to_string()
}