forge-engine 0.2.0

Causal edit attribution and structured patch evaluation engine
Documentation
use forge_engine::{ForgeStore, ForgeToolReceiptSink};
use llm_tool_runtime::{
    ToolApprovalState, ToolBackendKind, ToolBudgetContext, ToolPlannerStage, ToolReceipt,
    ToolReceiptSink, ToolRetryOwner,
};
use semantic_memory_forge::ForgeToolReceiptV2;
use stack_ids::{AttemptId, ContentDigest, TraceCtx, TrialId};
use std::sync::Arc;
use tempfile::TempDir;

fn sample_receipt() -> ToolReceipt {
    ToolReceipt {
        receipt_id: "receipt-1".into(),
        tool_run_id: "tool-run-1".into(),
        tool_name: "run_verification".into(),
        tool_version: "1".into(),
        backend_kind: ToolBackendKind::LocalFunction,
        input_digest: ContentDigest::compute_str("input"),
        output_digest_or_refs: serde_json::json!({ "job": { "job_id": "job-1" } }),
        policy_hash: ContentDigest::compute_str("policy"),
        approval_state: ToolApprovalState::Approved,
        host_identity: "host-1".into(),
        started_at: "2026-03-09T00:00:00Z".into(),
        finished_at: "2026-03-09T00:00:01Z".into(),
        trace_ctx: TraceCtx::generate(),
        attempt_id: AttemptId::new("attempt-1"),
        trial_id: TrialId::new("trial-1"),
        planner_stage: ToolPlannerStage::Execution,
        deadline: Some("2026-03-12T00:00:05Z".into()),
        workload_class: Some("verification".into()),
        budget_context: Some(ToolBudgetContext {
            budget_kind: Some("control_plane".into()),
            max_steps: Some(4),
            time_budget_ms: Some(2_000),
            cost_budget_units: Some(8),
        }),
        parent_receipt_id: Some("dispatch-1".into()),
        family_receipt_id: Some("attempt-family-1".into()),
        replay_parent_receipt_id: Some("retry-0".into()),
        remote_oracle_lease_id: None,
        remote_slice_result_id: None,
        attestation_envelope_id: None,
        cross_runtime_replay_ticket_id: None,
        error_class: None,
        retry_owner: ToolRetryOwner::LlmPipeline,
        replay_link: Some("replay://tool-run-1".into()),
        provider_call_id: Some("provider-call-1".into()),
    }
}

#[tokio::test]
async fn forge_tool_receipt_sink_persists_raw_receipt() {
    let dir = TempDir::new().unwrap();
    let store = Arc::new(ForgeStore::open(&dir.path().join("forge.db")).unwrap());
    let sink = ForgeToolReceiptSink::new(store.clone());
    let receipt = sample_receipt();

    sink.persist(&receipt).await.unwrap();

    let stored = store.get_tool_receipt("receipt-1").unwrap().unwrap();
    assert_eq!(stored.tool_name, "run_verification");
    assert_eq!(stored.attempt_id, "attempt-1");
    assert_eq!(stored.trial_id, "trial-1");
    assert_eq!(stored.provider_call_id.as_deref(), Some("provider-call-1"));
    assert_eq!(stored.trace_id, receipt.trace_ctx.trace_id);
    assert!(stored.raw_payload_json.contains("run_verification"));

    let raw: ForgeToolReceiptV2 = serde_json::from_str(&stored.raw_payload_json).unwrap();
    assert_eq!(raw.deadline.as_deref(), Some("2026-03-12T00:00:05Z"));
    assert_eq!(raw.workload_class.as_deref(), Some("verification"));
    assert_eq!(
        raw.budget_context
            .as_ref()
            .and_then(|budget| budget.max_steps),
        Some(4)
    );
    assert_eq!(raw.parent_receipt_id.as_deref(), Some("dispatch-1"));
    assert_eq!(raw.family_receipt_id.as_deref(), Some("attempt-family-1"));
    assert_eq!(raw.replay_parent_receipt_id.as_deref(), Some("retry-0"));
    assert_eq!(raw.planner_stage, "Execution");
}