use crate::config::ForgeConfig;
use crate::error::ForgeResult;
use crate::lab::evaluate::ScoreVector;
use crate::runtime::novelty;
use crate::runtime::patch::types::StructuredPatch;
use crate::store::ForgeStore;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ArchiveUpdate {
Inserted,
Replaced,
BelowGate,
NoChange,
}
pub fn compute_cell_key(
score_vector: &ScoreVector,
patch: &StructuredPatch,
config: &ForgeConfig,
) -> String {
let novelty_bin = compute_novelty_bin(score_vector.novelty, config);
let stability_bin = compute_stability_bin(score_vector.stability, config);
let tags = novelty::extract_strategy_tags(patch);
let approach_family = novelty::determine_approach_family(&tags);
format!("{novelty_bin}:{stability_bin}:{approach_family}")
}
fn compute_novelty_bin(novelty: f64, config: &ForgeConfig) -> String {
for bin in &config.lab.archive.novelty_bins {
if novelty >= bin.lo && novelty < bin.hi {
return bin.name.clone();
}
}
if let Some(last) = config.lab.archive.novelty_bins.last() {
if (novelty - last.hi).abs() < f64::EPSILON {
return last.name.clone();
}
}
"low".to_string()
}
fn compute_stability_bin(_stability: f64, _config: &ForgeConfig) -> &'static str {
"stable"
}
pub fn compute_score_summary(scores: &ScoreVector) -> f64 {
0.70 * scores.correctness + 0.20 * scores.novelty + 0.10 * scores.stability
}
pub fn archive_insert(
store: &ForgeStore,
candidate_id: &str,
scores: &ScoreVector,
patch: &StructuredPatch,
config: &ForgeConfig,
cea_fingerprint: Option<&str>,
) -> ForgeResult<ArchiveUpdate> {
if scores.correctness < config.lab.archive.correctness_gate {
return Ok(ArchiveUpdate::BelowGate);
}
let cell_key = compute_cell_key(scores, patch, config);
let score_summary = compute_score_summary(scores);
let existing = store.get_archive_cell(&cell_key)?;
match existing {
Some(cell) => {
let existing_summary: f64 =
serde_json::from_str(&cell.score_summary_json).unwrap_or(0.0);
if score_summary > existing_summary {
let summary_json = serde_json::to_string(&score_summary)?;
store.upsert_archive_cell(
&cell_key,
candidate_id,
&summary_json,
cea_fingerprint,
)?;
Ok(ArchiveUpdate::Replaced)
} else {
Ok(ArchiveUpdate::NoChange)
}
}
None => {
let summary_json = serde_json::to_string(&score_summary)?;
store.upsert_archive_cell(&cell_key, candidate_id, &summary_json, cea_fingerprint)?;
Ok(ArchiveUpdate::Inserted)
}
}
}