forge-engine 0.2.0

Causal edit attribution and structured patch evaluation engine
Documentation
#![allow(clippy::expect_used)]

use forge_engine::config::ForgeConfig;
use forge_engine::lab::emitters::AlgebraSpec;
use forge_engine::lab::evidence::{
    AssessmentCategory, ContradictionState, EvidenceAssessment, SampleSupport,
};
use forge_engine::lab::promote::{promote, CausalFingerprint};
use forge_engine::store::db::PromotionRow;
use forge_engine::{ForgeError, ForgeStore, ScoreVector};
use tempfile::TempDir;

fn open_test_store() -> (TempDir, ForgeStore) {
    let dir = TempDir::new().unwrap();
    let db_path = dir.path().join("forge.db");
    let store = ForgeStore::open(&db_path).unwrap();
    (dir, store)
}

fn insert_candidate(store: &ForgeStore, candidate_id: &str) {
    let spec = serde_json::to_string(&AlgebraSpec::default()).unwrap();
    store
        .insert_candidate(candidate_id, &spec, "[]", "active")
        .unwrap();
}

fn insert_eval_run(
    store: &ForgeStore,
    candidate_id: &str,
    eval_id: &str,
    correctness: f64,
    weighted_total: f64,
    violations_json: &str,
    cea_run_hash: Option<&str>,
) {
    let scores = ScoreVector {
        correctness,
        novelty: 0.1,
        stability: 0.0,
        weighted_total,
        cea_confidence: None,
        cea_predicted_correctness: None,
    };
    store
        .insert_eval_run(
            eval_id,
            candidate_id,
            "task-1",
            "host",
            0,
            &format!("mindstate:{eval_id}"),
            &format!("patch:{eval_id}"),
            &format!("sig:{eval_id}"),
            &serde_json::to_string(&scores).unwrap(),
            violations_json,
            "inline:test",
            cea_run_hash,
        )
        .unwrap();
}

fn insert_evidence_assessment(
    store: &ForgeStore,
    candidate_id: &str,
    bundle_id: &str,
    assessment: EvidenceAssessment,
) {
    store
        .insert_evidence_bundle(
            bundle_id,
            candidate_id,
            &format!("eval:{bundle_id}"),
            "v0001",
            &format!("trace:{bundle_id}"),
            "{}",
            "[]",
            None,
            None,
            Some(&serde_json::to_string(&assessment).unwrap()),
            "[]",
        )
        .unwrap();
}

fn clean_assessment() -> EvidenceAssessment {
    EvidenceAssessment {
        reproducibility: AssessmentCategory::Strong,
        isolation: AssessmentCategory::Strong,
        contradiction_state: ContradictionState::Clean,
        sample_support: SampleSupport::Sufficient,
    }
}

fn latest_promotion(store: &ForgeStore) -> PromotionRow {
    store
        .get_latest_promotion()
        .unwrap()
        .expect("promotion should exist")
}

fn candidate_status(store: &ForgeStore, candidate_id: &str) -> String {
    let conn = rusqlite::Connection::open(store.path()).unwrap();
    conn.query_row(
        "SELECT status FROM candidates WHERE candidate_id = ?1",
        [candidate_id],
        |row| row.get(0),
    )
    .unwrap()
}

#[test]
fn promotion_rejects_missing_verification_evidence() {
    let (_dir, store) = open_test_store();
    let config = ForgeConfig::default();

    insert_candidate(&store, "cand-no-evidence");
    insert_eval_run(&store, "cand-no-evidence", "eval-1", 1.0, 0.20, "[]", None);

    let err = promote(&store, "cand-no-evidence", &config).unwrap_err();
    match err {
        ForgeError::PromotionFailed { criterion, .. } => {
            assert_eq!(criterion, "verification_evidence");
        }
        other => panic!("expected PromotionFailed, got {other:?}"),
    }
}

#[test]
fn promotion_rejects_when_improvement_does_not_clear_baseline() {
    let (_dir, store) = open_test_store();
    let config = ForgeConfig::default();

    insert_candidate(&store, "baseline");
    insert_eval_run(
        &store,
        "baseline",
        "eval-base-1",
        1.0,
        0.30,
        "[]",
        Some("edge-a"),
    );
    insert_eval_run(
        &store,
        "baseline",
        "eval-base-2",
        1.0,
        0.30,
        "[]",
        Some("edge-b"),
    );
    let baseline_fp = CausalFingerprint {
        dominant_edge_hashes: vec!["edge-a".into(), "edge-b".into()],
        checksum: blake3::hash(b"edge-a,edge-b").to_hex().to_string(),
    };
    store
        .insert_promotion(
            "v0001",
            "baseline",
            &serde_json::to_string(&AlgebraSpec::default()).unwrap(),
            "{}",
            "{}",
            "baseline-checksum",
            Some(&serde_json::to_string(&baseline_fp).unwrap()),
        )
        .unwrap();

    insert_candidate(&store, "cand-low-improvement");
    insert_eval_run(
        &store,
        "cand-low-improvement",
        "eval-cand-1",
        1.0,
        0.33,
        "[]",
        Some("edge-a"),
    );
    insert_eval_run(
        &store,
        "cand-low-improvement",
        "eval-cand-2",
        1.0,
        0.31,
        "[]",
        Some("edge-b"),
    );
    insert_evidence_assessment(
        &store,
        "cand-low-improvement",
        "bundle-low-improvement",
        clean_assessment(),
    );

    let err = promote(&store, "cand-low-improvement", &config).unwrap_err();
    match err {
        ForgeError::PromotionFailed { criterion, .. } => {
            assert_eq!(criterion, "weighted_improvement");
        }
        other => panic!("expected PromotionFailed, got {other:?}"),
    }
}

#[test]
fn promotion_rejects_contradicted_evidence_assessment() {
    let (_dir, store) = open_test_store();
    let config = ForgeConfig::default();

    insert_candidate(&store, "cand-contradicted");
    insert_eval_run(&store, "cand-contradicted", "eval-1", 1.0, 0.20, "[]", None);
    let mut assessment = clean_assessment();
    assessment.contradiction_state = ContradictionState::HasContradictions;
    insert_evidence_assessment(
        &store,
        "cand-contradicted",
        "bundle-contradicted",
        assessment,
    );

    let err = promote(&store, "cand-contradicted", &config).unwrap_err();
    match err {
        ForgeError::PromotionFailed { criterion, .. } => {
            assert_eq!(criterion, "contradiction_state");
        }
        other => panic!("expected PromotionFailed, got {other:?}"),
    }
}

#[test]
fn promotion_succeeds_with_deterministic_eval_and_evidence_gate() {
    let (_dir, store) = open_test_store();
    let config = ForgeConfig::default();

    insert_candidate(&store, "cand-promote");
    insert_eval_run(
        &store,
        "cand-promote",
        "eval-1",
        1.0,
        0.22,
        "[]",
        Some("edge-a"),
    );
    insert_eval_run(
        &store,
        "cand-promote",
        "eval-2",
        1.0,
        0.21,
        "[]",
        Some("edge-b"),
    );
    insert_evidence_assessment(&store, "cand-promote", "bundle-promote", clean_assessment());

    let promoted = promote(&store, "cand-promote", &config).unwrap();

    assert_eq!(promoted.candidate_id, "cand-promote");
    assert_eq!(promoted.version_id, "v0001");
    assert_eq!(candidate_status(&store, "cand-promote"), "promoted");

    let stored = latest_promotion(&store);
    assert_eq!(stored.candidate_id, "cand-promote");
    assert_eq!(stored.version_id, "v0001");
    assert!(stored.cea_fingerprint_json.is_some());
}

#[test]
fn promotion_reads_assessment_from_canonical_bundle_not_legacy_split_column() {
    let (_dir, store) = open_test_store();
    let config = ForgeConfig::default();

    insert_candidate(&store, "cand-canonical-assessment");
    insert_eval_run(
        &store,
        "cand-canonical-assessment",
        "eval-1",
        1.0,
        0.22,
        "[]",
        Some("edge-a"),
    );
    insert_eval_run(
        &store,
        "cand-canonical-assessment",
        "eval-2",
        1.0,
        0.21,
        "[]",
        Some("edge-b"),
    );
    insert_evidence_assessment(
        &store,
        "cand-canonical-assessment",
        "bundle-canonical-assessment",
        clean_assessment(),
    );

    let stale_assessment = serde_json::to_string(&EvidenceAssessment {
        reproducibility: AssessmentCategory::Weak,
        isolation: AssessmentCategory::Weak,
        contradiction_state: ContradictionState::HasContradictions,
        sample_support: SampleSupport::Insufficient,
    })
    .unwrap();

    let conn = rusqlite::Connection::open(store.path()).unwrap();
    conn.execute(
        "UPDATE evidence_bundles SET assessment_json = ?1 WHERE bundle_id = ?2",
        rusqlite::params![stale_assessment, "bundle-canonical-assessment"],
    )
    .unwrap();
    drop(conn);

    let promoted = promote(&store, "cand-canonical-assessment", &config).unwrap();
    assert_eq!(promoted.candidate_id, "cand-canonical-assessment");
    assert_eq!(
        candidate_status(&store, "cand-canonical-assessment"),
        "promoted"
    );
}