1pub mod author;
34pub mod calibrate;
35pub mod catalog;
36pub mod config;
37mod error;
38pub mod finding;
39pub mod harness_assert_graph;
40pub mod harness_citation_integrity;
41pub mod harness_concordance;
42pub mod harness_corpus;
43pub mod harness_falsify;
44pub mod harness_graph;
45pub mod harness_import;
46pub mod harness_index;
47pub mod harness_markdown;
48pub mod harness_membership;
49pub mod harness_ontology_registry;
50pub mod harness_project;
51pub mod harness_reconcile;
52pub mod harness_relationship_targets;
53pub mod harness_release;
54pub mod harness_render;
55pub mod harness_shippable_typing;
56pub mod harness_synthesize;
57pub mod harness_toggle;
58pub mod harness_topic_metadata;
59pub mod harness_validate_concordance;
60pub mod harness_wrap;
61pub mod index;
62pub mod lock;
63pub mod ontology_pack;
64pub mod queue;
65pub mod resolve;
66pub mod review;
67pub mod suggest;
68pub mod vendor;
69
70pub use author::{DraftReport, draft_from_clusters, draft_from_topic};
71pub use calibrate::{
72 CONFUSION_REPRESENTATIVES, CalibrateOptions, CalibrationSample, ConfusionPair, ConfusionReport,
73 collect_topic_samples, confusions, packs_carry_negatives, subsample, sweep,
74};
75pub use catalog::Catalog;
76pub use config::HarnessConfig;
77pub use error::MifRhError;
78pub use finding::Finding;
79pub use harness_assert_graph::{CheckResult, GraphAssertion, assert_graph_mif_file};
80pub use harness_citation_integrity::{CitationIntegrityReport, check_citation_integrity};
81pub use harness_concordance::build_concordance;
82pub use harness_corpus::{CorpusSynthesis, synthesize_corpus};
83pub use harness_falsify::{FalsifyResult, falsify, falsify_with_now};
84pub use harness_graph::build_graph;
85pub use harness_import::{ImportReport, import_corpus};
86pub use harness_index::build_index;
87pub use harness_membership::{MembershipReport, resolve_membership};
88pub use harness_ontology_registry::{RegistryValidation, validate_ontology_registry};
89pub use harness_project::project_report;
90pub use harness_reconcile::{ReconcileReport, reconcile_session, sort_object_keys};
91pub use harness_relationship_targets::{
92 Orphan, RelationshipTargetsReport, check_relationship_targets,
93};
94pub use harness_release::{
95 BumpOptions, BumpReport, ChangelogLinkChange, ChangelogLinkReport, VersionGateFailure,
96 VersionGateReport, bump_version, check_version_bump, goal_version_id,
97 reconcile_changelog_links,
98};
99pub use harness_render::{RenderInputs, render_artifact};
100pub use harness_shippable_typing::{ShippableTypingReport, check_shippable_typing};
101pub use harness_synthesize::synthesize_artifact;
102pub use harness_toggle::{SITE_PLUGINS, pack_toggle, site_toggle_plugin, site_toggle_primary};
103pub use harness_topic_metadata::{TopicMetadata, topic_metadata};
104pub use harness_validate_concordance::{ConcordanceValidation, validate_concordance};
105pub use harness_wrap::{WrapSourceInputs, read_source_content, wrap_source};
106pub use index::{FindingIndex, IndexStats, IndexedFinding, Miss, SearchMatch, SimilarFinding};
107pub use lock::ReviewLock;
108pub use ontology_pack::{EntityType, OntologyPack};
109pub use queue::{
110 ClusterMember, ExpansionCandidate, SuggestionEntry, SuggestionQueue, expansion_candidates,
111 upsert_suggestions,
112};
113pub use resolve::{Basis, MapRecord, ResolveContext, build_allowed, resolve_finding};
114pub use review::{
115 FollowupBacklog, FollowupEntry, ReviewOptions, ReviewReport, TopicSummary, review,
116 write_followup,
117};
118pub use suggest::{TypeSuggestion, suggest_type};
119pub use vendor::{
120 CatalogSyncReport, DriftEntry, FetchReport, LockCheckReport, LockEntry, LockFile,
121 NewlyRequiredField, PinSafetyGap, PinSafetyReport, RegistrySyncReport, VendoredOntology,
122 check_pin_safety, fetch, lock_check, resolve_source, sync_catalog, sync_registry,
123};
124
125pub fn build_search_index(
141 reports_dir: &std::path::Path,
142 topic_ids: &[String],
143 index: &mut FindingIndex,
144) -> Result<(), MifRhError> {
145 let embedder = mif_embed::Embedder::load()?;
146 let mut indexed = Vec::new();
147
148 for topic in topic_ids {
149 let findings_dir = reports_dir.join(topic).join("findings");
150 if !findings_dir.is_dir() {
151 continue;
152 }
153 for file in review::list_finding_files(&findings_dir)? {
154 let finding = Finding::load(&file)?;
155 let text = index_text(&finding);
156 let vector = embedder.embed(&text)?;
157 indexed.push(IndexedFinding {
158 finding_id: finding.id,
159 topic: topic.clone(),
160 content: text,
161 vector,
162 });
163 }
164 }
165
166 index.rebuild(&indexed)
167}
168
169pub fn write_json_atomic<T: serde::Serialize>(
183 path: &std::path::Path,
184 value: &T,
185) -> Result<(), MifRhError> {
186 let json = serde_json::to_string_pretty(value).map_err(|source| MifRhError::JsonSerialize {
187 path: path.display().to_string(),
188 source,
189 })?;
190 let tmp_path = path.with_extension("json.tmp");
191 std::fs::write(&tmp_path, json).map_err(|source| MifRhError::Io {
192 path: tmp_path.display().to_string(),
193 source,
194 })?;
195 std::fs::rename(&tmp_path, path).map_err(|source| MifRhError::Io {
196 path: path.display().to_string(),
197 source,
198 })
199}
200
201#[must_use]
208pub fn index_text(finding: &Finding) -> String {
209 let discovery_text = finding.discovery_text();
210 if !discovery_text.is_empty() {
211 return discovery_text;
212 }
213 finding
214 .entity
215 .as_ref()
216 .and_then(|entity| entity.get("name"))
217 .and_then(serde_json::Value::as_str)
218 .unwrap_or_default()
219 .to_string()
220}