omk 0.5.0

A Rust runtime for Kimi CLI. Turns prompts into proof-backed engineering runs with gates, worktrees, and replay.
Documentation
use anyhow::{Context, Result};
use chrono::{DateTime, Utc};
use serde::ser::SerializeStruct;
use serde::{Deserialize, Serialize, Serializer};
use serde_json::Value;
use std::path::Path;

use super::state::{
    GoalState, GoalStatus, GOAL_PROOF_FILE, GOAL_REVIEW_TASK_ID, GOAL_SECURITY_REVIEW_TASK_ID,
};
use super::task_graph::{
    goal_agent_execution_done, summarize_task_graph, GoalTaskGraph, GoalTaskGraphSummary,
    GoalTaskStatus,
};
use crate::runtime::gates::{gates_passed, GateResult};

mod artifact;
mod review;
pub(crate) mod sidecar;
mod status;

pub(crate) use artifact::write_json_artifact;
pub(crate) use review::collect_review_artifacts;

#[derive(Debug, Clone, Deserialize)]
pub struct GoalProof {
    pub version: u32,
    pub goal_id: String,
    pub status: GoalStatus,
    pub readiness: String,
    pub summary: String,
    pub generated_at: DateTime<Utc>,
    pub artifacts: Vec<super::state::GoalArtifact>,
    pub task_graph_summary: GoalTaskGraphSummary,
    pub changed_files: Vec<String>,
    pub commits: Vec<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub git: Option<super::evidence::GoalGitEvidence>,
    pub gates: Vec<GateResult>,
    #[serde(default)]
    pub post_mutation_gates_ran: bool,
    pub known_gaps: Vec<String>,
    pub human_decisions_required: Vec<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub recovery_status: Option<String>,
}

impl Serialize for GoalProof {
    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        let delivery_metadata = sidecar::remembered_goal_proof_delivery_metadata(self);
        let review_artifacts = sidecar::remembered_goal_proof_review_artifacts(self);
        let integration_evidence = sidecar::remembered_goal_proof_integration_evidence(self);
        let oracle_evidence = sidecar::remembered_goal_proof_oracle_evidence(self);
        let mut field_count = 14;
        if review_artifacts.is_some() {
            field_count += 1;
        }
        if integration_evidence.is_some() {
            field_count += 1;
        }
        if oracle_evidence.is_some() {
            field_count += 1;
        }
        if self.git.is_some() {
            field_count += 1;
        }
        if delivery_metadata.is_some() {
            field_count += 1;
        }
        if self.recovery_status.is_some() {
            field_count += 1;
        }

        let mut state = serializer.serialize_struct("GoalProof", field_count)?;
        state.serialize_field("version", &self.version)?;
        state.serialize_field("goal_id", &self.goal_id)?;
        state.serialize_field("status", &self.status)?;
        state.serialize_field("readiness", &self.readiness)?;
        state.serialize_field("summary", &self.summary)?;
        state.serialize_field("generated_at", &self.generated_at)?;
        state.serialize_field("artifacts", &self.artifacts)?;
        state.serialize_field("task_graph_summary", &self.task_graph_summary)?;
        if let Some(delivery_metadata) = delivery_metadata {
            state.serialize_field("delivery_metadata", &delivery_metadata)?;
        }
        if let Some(review_artifacts) = review_artifacts {
            state.serialize_field("review_artifacts", &review_artifacts)?;
        }
        if let Some(integration_evidence) = integration_evidence {
            state.serialize_field("integration_evidence", &integration_evidence)?;
        }
        if let Some(oracle_evidence) = oracle_evidence {
            state.serialize_field("oracle_evidence", &oracle_evidence)?;
        }
        state.serialize_field("changed_files", &self.changed_files)?;
        state.serialize_field("commits", &self.commits)?;
        if let Some(git) = &self.git {
            state.serialize_field("git", git)?;
        }
        state.serialize_field("gates", &self.gates)?;
        state.serialize_field("post_mutation_gates_ran", &self.post_mutation_gates_ran)?;
        state.serialize_field("known_gaps", &self.known_gaps)?;
        state.serialize_field("human_decisions_required", &self.human_decisions_required)?;
        if let Some(recovery_status) = &self.recovery_status {
            state.serialize_field("recovery_status", recovery_status)?;
        }
        state.end()
    }
}

impl GoalProof {
    pub async fn load(goal_dir: &Path) -> Result<Self> {
        if let Some(db) = crate::runtime::db::global_db() {
            if let Some(goal_id) = goal_dir.file_name().and_then(|n| n.to_str()) {
                if let Some(proof) =
                    crate::runtime::goal::state::db_store::load_proof_from_db(&db, goal_id).await?
                {
                    return Ok(proof);
                }
            }
        }

        let path = goal_dir.join(GOAL_PROOF_FILE);
        let json = tokio::fs::read_to_string(&path)
            .await
            .with_context(|| format!("Failed to read goal proof: {}", path.display()))?;
        let value: Value = serde_json::from_str(&json)
            .with_context(|| format!("Failed to parse goal proof: {}", path.display()))?;
        let delivery_metadata = sidecar::proof_delivery_metadata_from_value(&value);
        let review_artifacts = sidecar::proof_review_artifacts_from_value(&value);
        let integration_evidence = sidecar::proof_integration_evidence_from_value(&value);
        let oracle_evidence = sidecar::proof_oracle_evidence_from_value(&value);
        let proof: Self = serde_json::from_value(value)
            .with_context(|| format!("Failed to parse goal proof: {}", path.display()))?;
        sidecar::remember_goal_proof_delivery_metadata(&proof, delivery_metadata);
        sidecar::remember_goal_proof_review_artifacts(&proof, review_artifacts);
        sidecar::remember_goal_proof_acceptance_evidence_for_value(
            &serde_json::to_value(&proof)?,
            integration_evidence,
            oracle_evidence,
        );
        Ok(proof)
    }

    /// Validate that the proof is ready for a merge into the base branch.
    /// Checks:
    /// - proof status is `Ready`
    /// - all verification gates passed
    /// - review wall artifacts exist and all passed
    pub(crate) fn validate_for_merge(&self) -> Result<()> {
        if self.status != GoalStatus::Ready {
            anyhow::bail!("proof status is not Ready (current: {:?})", self.status);
        }
        if !gates_passed(&self.gates) {
            anyhow::bail!("verification gates have not all passed");
        }
        let review_artifacts =
            sidecar::remembered_goal_proof_review_artifacts(self).unwrap_or_default();
        if !review_artifacts.is_empty() && !review::review_artifacts_passed(&review_artifacts) {
            anyhow::bail!("review wall has failures or blockers");
        }
        Ok(())
    }
}

pub(crate) fn build_scaffold_proof(
    state: &GoalState,
    task_graph: &GoalTaskGraph,
    git: Option<super::evidence::GoalGitEvidence>,
    generated_at: DateTime<Utc>,
) -> GoalProof {
    let commits = proof_commits(&git);
    let controlled_by_state = status::state_status_controls_proof(state.status);
    let known_gaps = if controlled_by_state {
        Vec::new()
    } else {
        vec![
            "agent execution has not run for this goal yet".to_string(),
            "verification gates have not run for this goal".to_string(),
            "proof cannot claim readiness until agent-owned execution evidence exists".to_string(),
        ]
    };
    let mut proof = GoalProof {
        version: 1,
        goal_id: state.goal_id.clone(),
        status: GoalStatus::NotReady,
        readiness: "not ready: controller scaffold has not executed agents or verification gates"
            .to_string(),
        summary: format!(
            "Goal '{}' has durable planning artifacts, but no local verification or agent execution evidence yet.",
            state.normalized_goal
        ),
        generated_at,
        artifacts: state.artifacts.clone(),
        task_graph_summary: summarize_task_graph(task_graph),
        changed_files: Vec::new(),
        commits,
        git,
        gates: Vec::new(),
        post_mutation_gates_ran: false,
        known_gaps,
        human_decisions_required: Vec::new(),
        recovery_status: None,
    };
    status::reconcile_with_goal_state(&mut proof, state);
    proof
}

pub(crate) fn build_verified_proof(
    state: &GoalState,
    task_graph: &GoalTaskGraph,
    gates: Vec<GateResult>,
    changed_files: Vec<String>,
    git: Option<super::evidence::GoalGitEvidence>,
    post_mutation_gates_ran: bool,
    generated_at: DateTime<Utc>,
) -> GoalProof {
    let gates_ok = !gates.is_empty() && gates_passed(&gates);
    let agent_execution_done = goal_agent_execution_done(task_graph);
    let review_done = task_graph
        .tasks
        .iter()
        .any(|task| task.id == GOAL_REVIEW_TASK_ID && task.status == GoalTaskStatus::Done);
    let security_review_done = task_graph
        .tasks
        .iter()
        .any(|task| task.id == GOAL_SECURITY_REVIEW_TASK_ID && task.status == GoalTaskStatus::Done);
    let review_artifacts =
        collect_review_artifacts(review_done, security_review_done, &gates, &changed_files);
    let review_artifacts_ok = review::review_artifacts_passed(&review_artifacts);
    let commits = proof_commits(&git);
    let mut known_gaps = Vec::new();
    if !agent_execution_done {
        known_gaps.push("agent execution has not run for this goal yet".to_string());
        known_gaps.push(
            "proof cannot claim readiness until agent-owned execution evidence exists".to_string(),
        );
    }
    if agent_execution_done && !review_done {
        known_gaps.push("review evidence has not run for this goal yet".to_string());
    }
    if agent_execution_done && !security_review_done {
        known_gaps.push("security review evidence has not run for this goal yet".to_string());
    }
    known_gaps.extend(review::review_artifact_known_gaps(&review_artifacts));
    if agent_execution_done && !changed_files.is_empty() && !post_mutation_gates_ran {
        known_gaps
            .push("verification gates have not rerun after agent execution changes".to_string());
    }
    if agent_execution_done && review_done && security_review_done && changed_files.is_empty() {
        known_gaps.push(
            "project mutation and integration loop has not produced changed-file evidence yet"
                .to_string(),
        );
    }
    if agent_execution_done && review_done && security_review_done && !changed_files.is_empty() {
        known_gaps.push(
            "integration loop has not committed, opened a PR, or accepted the agent changes yet"
                .to_string(),
        );
    }

    if gates.is_empty() {
        known_gaps.push("no verification gates were detected or configured".to_string());
    } else if !gates_ok {
        known_gaps.push("required verification gates failed".to_string());
    }

    let proof_status = if status::state_status_controls_proof(state.status) {
        state.status
    } else {
        GoalStatus::NotReady
    };
    let readiness = if state.status == GoalStatus::Paused {
        "paused: execution was interrupted by operator request and can resume later".to_string()
    } else if state.status == GoalStatus::Cancelled {
        "cancelled: execution was interrupted by operator cancellation".to_string()
    } else if state.status == GoalStatus::NeedsMoreBudget {
        "needs more budget: execution stopped before spending beyond the configured budget"
            .to_string()
    } else if gates_ok
        && agent_execution_done
        && review_done
        && security_review_done
        && review_artifacts_ok
        && !changed_files.is_empty()
        && post_mutation_gates_ran
    {
        "not ready: agent changes passed verification, review, and security evidence, but integration acceptance is missing".to_string()
    } else if gates_ok
        && agent_execution_done
        && review_done
        && security_review_done
        && review_artifacts_ok
        && !changed_files.is_empty()
    {
        "not ready: agent changes exist, but verification and integration have not rerun after the mutation".to_string()
    } else if gates_ok
        && agent_execution_done
        && review_done
        && security_review_done
        && review_artifacts_ok
    {
        "not ready: verification, agent execution, review, and security evidence passed, but no project mutation was captured".to_string()
    } else if gates_ok && agent_execution_done && review_done && security_review_done {
        "not ready: required reviewer artifacts are incomplete or blocked".to_string()
    } else if gates_ok && agent_execution_done {
        "not ready: verification gates and bounded agent execution passed, but review/security evidence is missing".to_string()
    } else if gates_ok {
        "not ready: verification gates passed, but agent execution evidence is missing".to_string()
    } else {
        "not ready: required verification evidence is incomplete or failing".to_string()
    };

    let mut proof = GoalProof {
        version: 1,
        goal_id: state.goal_id.clone(),
        status: proof_status,
        readiness,
        summary: format!(
            "Goal '{}' has {} gate result(s) and remains not ready until all required execution and review evidence exists.",
            state.normalized_goal,
            gates.len()
        ),
        generated_at,
        artifacts: state.artifacts.clone(),
        task_graph_summary: summarize_task_graph(task_graph),
        changed_files,
        commits,
        git,
        gates,
        post_mutation_gates_ran,
        known_gaps,
        human_decisions_required: Vec::new(),
        recovery_status: None,
    };
    status::reconcile_with_goal_state(&mut proof, state);
    sidecar::remember_goal_proof_review_artifacts(&proof, review_artifacts);
    proof
}

pub(crate) fn reconcile_goal_proof_with_state(proof: &mut GoalProof, state: &GoalState) {
    status::reconcile_with_goal_state(proof, state);
}

fn proof_commits(git: &Option<super::evidence::GoalGitEvidence>) -> Vec<String> {
    git.as_ref()
        .map(|evidence| vec![evidence.head.clone()])
        .unwrap_or_default()
}

pub(crate) fn carry_goal_proof_sidecars(
    from: &GoalProof,
    to: &GoalProof,
    integration_evidence: Value,
    oracle_evidence: Value,
) {
    if let Some(delivery_metadata) = sidecar::remembered_goal_proof_delivery_metadata(from) {
        sidecar::remember_goal_proof_delivery_metadata(to, delivery_metadata);
    }
    if let Some(review_artifacts) = sidecar::remembered_goal_proof_review_artifacts(from) {
        sidecar::remember_goal_proof_review_artifacts(to, review_artifacts);
    }
    sidecar::remember_goal_proof_acceptance_evidence(to, integration_evidence, oracle_evidence);
}

#[cfg(test)]
mod tests;