omk 0.5.0

A Rust runtime for Kimi CLI. Turns prompts into proof-backed engineering runs with gates, worktrees, and replay.
Documentation
mod apply;
mod rollback;
mod types;

pub use types::GoalTaskGraphSummary;

pub(crate) use apply::{
    apply_agent_execution_task_result, apply_agent_followup_task_results,
    apply_agent_proposed_task_mutations, apply_agent_task_result_by_id, goal_agent_execution_done,
    goal_task_done, pending_goal_agent_followup_proposals, summarize_task_graph,
};
pub(crate) use rollback::{
    merge_concurrent_slice_task_graphs, spawn_cleanup_task, spawn_refactor_task_from_slop_findings,
};

#[cfg(test)]
mod tests {
    use std::path::PathBuf;

    use chrono::Utc;

    use super::*;
    use crate::runtime::goal::evidence::GoalAgentRunEvidence;
    use crate::runtime::goal::review::slop::{SlopFinding, SlopKind};
    use crate::runtime::goal::task_graph::model::{GoalTask, GoalTaskGraph, GoalTaskStatus};
    use crate::runtime::scheduler::runner::RunSummary;

    fn task(id: &str, status: GoalTaskStatus) -> GoalTask {
        GoalTask {
            id: id.to_string(),
            title: format!("Task {id}"),
            description: format!("Task {id} description"),
            status,
            owner_role: None,
            completed_at: None,
            evidence: vec![],
            retry_count: 0,
            max_retries: 0,
            lease_expires_at: None,
            dependencies: vec![],
            read_set: vec![],
            write_set: vec!["src".to_string()],
            risk: "low".to_string(),
            acceptance: vec![format!("Task {id} acceptance")],
        }
    }

    fn graph(tasks: Vec<GoalTask>) -> GoalTaskGraph {
        GoalTaskGraph {
            version: 1,
            goal_id: "goal-test".to_string(),
            generated_at: Utc::now(),
            tasks,
        }
    }

    fn evidence(task_id: &str, completed: usize, failed: usize) -> GoalAgentRunEvidence {
        GoalAgentRunEvidence {
            summary: RunSummary {
                run_id: format!("run-{task_id}"),
                completed,
                failed,
                cancelled: 0,
                total: completed + failed,
            },
            run_path: PathBuf::new(),
            task_policy_path: PathBuf::new(),
            agent_task_proposals_path: PathBuf::new(),
            worker_outbox_path: PathBuf::new(),
            wire_events_path: PathBuf::new(),
            mutation_diff_path: PathBuf::new(),
            changed_files_path: PathBuf::new(),
            changed_files: vec![],
            accepted_task_count: 0,
            rejected_task_count: 0,
            accepted_task_ids: vec![task_id.to_string()],
            agent_proposed_tasks: vec![],
            worker_results: vec![],
            worker_summary: None,
        }
    }

    #[test]
    fn merge_updates_status_to_done() {
        let mut main = graph(vec![
            task("t1", GoalTaskStatus::Pending),
            task("t2", GoalTaskStatus::Pending),
        ]);
        let mut delta1 = graph(vec![task("t1", GoalTaskStatus::Done)]);
        delta1.tasks[0].completed_at = Some(Utc::now());
        let mut delta2 = graph(vec![task("t2", GoalTaskStatus::Done)]);
        delta2.tasks[0].completed_at = Some(Utc::now());

        merge_concurrent_slice_task_graphs(&mut main, &[delta1, delta2]);

        assert_eq!(main.tasks[0].status, GoalTaskStatus::Done);
        assert_eq!(main.tasks[1].status, GoalTaskStatus::Done);
    }

    #[test]
    fn merge_prefers_blocked_over_pending() {
        let mut main = graph(vec![task("t1", GoalTaskStatus::Pending)]);
        let delta = graph(vec![task("t1", GoalTaskStatus::Blocked)]);

        merge_concurrent_slice_task_graphs(&mut main, &[delta]);

        assert_eq!(main.tasks[0].status, GoalTaskStatus::Blocked);
    }

    #[test]
    fn apply_agent_task_result_by_id_sets_done() {
        let mut tg = graph(vec![task("t1", GoalTaskStatus::Pending)]);
        let ev = evidence("t1", 1, 0);
        let result = apply_agent_task_result_by_id(&mut tg, "t1", &ev, Utc::now());
        assert!(result.is_some());
        assert_eq!(tg.tasks[0].status, GoalTaskStatus::Done);
    }

    #[test]
    fn spawn_refactor_task_from_empty_findings_returns_none() {
        let mut tg = graph(vec![task("slice-a", GoalTaskStatus::Done)]);
        let result = spawn_refactor_task_from_slop_findings(
            &mut tg,
            "slice-a",
            &[],
            &["src/main.rs".to_string()],
            Utc::now(),
        );
        assert!(result.is_none());
        assert_eq!(tg.tasks.len(), 1);
    }

    #[test]
    fn spawn_refactor_task_creates_task_and_wires_dependency() {
        let mut tg = graph(vec![task("slice-a", GoalTaskStatus::Done)]);
        let findings = vec![SlopFinding {
            kind: SlopKind::BannedPattern,
            file: PathBuf::from("src/main.rs"),
            line: Some(42),
            message: "unwrap() is banned".to_string(),
        }];
        let result = spawn_refactor_task_from_slop_findings(
            &mut tg,
            "slice-a",
            &findings,
            &["src/main.rs".to_string()],
            Utc::now(),
        );
        assert_eq!(result, Some("goal-agent-refactor-slice-a".to_string()));
        assert_eq!(tg.tasks.len(), 2);

        let refactor = tg
            .tasks
            .iter()
            .find(|t| t.id == "goal-agent-refactor-slice-a")
            .expect("refactor task exists");
        assert!(refactor.description.contains("unwrap() is banned"));
        assert!(refactor.description.contains("line 42"));
        assert_eq!(refactor.status, GoalTaskStatus::Pending);
        assert_eq!(refactor.read_set, vec!["src/main.rs"]);

        let slice = tg
            .tasks
            .iter()
            .find(|t| t.id == "slice-a")
            .expect("slice task exists");
        assert!(slice
            .dependencies
            .contains(&"goal-agent-refactor-slice-a".to_string()));
        assert_eq!(slice.status, GoalTaskStatus::Pending);
    }

    #[test]
    fn spawn_refactor_task_updates_existing_task_when_findings_change() {
        let mut tg = graph(vec![task("slice-a", GoalTaskStatus::Done)]);
        let first = vec![SlopFinding {
            kind: SlopKind::TodoFixmeHack,
            file: PathBuf::from("src/lib.rs"),
            line: Some(10),
            message: "TODO fix this".to_string(),
        }];
        let id1 = spawn_refactor_task_from_slop_findings(
            &mut tg,
            "slice-a",
            &first,
            &["src/lib.rs".to_string()],
            Utc::now(),
        );
        assert!(id1.is_some());

        let second = vec![SlopFinding {
            kind: SlopKind::BannedPattern,
            file: PathBuf::from("src/lib.rs"),
            line: Some(20),
            message: "expect() is banned".to_string(),
        }];
        let id2 = spawn_refactor_task_from_slop_findings(
            &mut tg,
            "slice-a",
            &second,
            &["src/lib.rs".to_string()],
            Utc::now(),
        );
        assert_eq!(id1, id2);
        assert_eq!(tg.tasks.len(), 2);

        let refactor = tg
            .tasks
            .iter()
            .find(|t| t.id == "goal-agent-refactor-slice-a")
            .expect("refactor task exists");
        assert!(refactor.description.contains("expect() is banned"));
        assert!(!refactor.description.contains("TODO fix this"));
        assert_eq!(refactor.evidence.len(), 2);
    }
}