omk 0.5.0

A Rust runtime for Kimi CLI. Turns prompts into proof-backed engineering runs with gates, worktrees, and replay.
Documentation
use chrono::Utc;
use omk::runtime::goal::{
    evaluate_task_budget, GoalAgentTaskProposal, GoalPhase, GoalState, GoalStatus,
    PerTaskBudgetSnapshot,
};
use std::path::PathBuf;

fn test_proposal(id: &str, budget_secs: u64) -> GoalAgentTaskProposal {
    GoalAgentTaskProposal {
        id: id.to_string(),
        title: format!("Task {id}"),
        description: format!("Description {id}"),
        dependencies: vec![],
        read_set: vec![],
        write_set: vec!["README.md".to_string()],
        risk: "low".to_string(),
        acceptance: vec!["accept".to_string()],
        budget_secs,
        priority: 0,
    }
}

fn test_state(budget_time: Option<String>) -> GoalState {
    GoalState {
        version: 1,
        goal_id: "goal-test".to_string(),
        original_goal: "test".to_string(),
        normalized_goal: "test".to_string(),
        status: GoalStatus::Running,
        phase: GoalPhase::Execution,
        created_at: Utc::now(),
        updated_at: Utc::now(),
        completed_at: None,
        until_ready: false,
        budget_time,
        budget_tokens: None,
        budget_usd: None,
        max_agents: Some(1),
        terminal_criteria: Default::default(),
        artifacts: vec![],
        failure: None,
        state_dir: PathBuf::from("/tmp/goal-test-does-not-exist"),
        cost_tracker_path: None,
        delivery_policy: Default::default(),
        merge_policy: Default::default(),
        slice_execution: Default::default(),
    }
}

#[tokio::test]
async fn task_rejected_when_budget_exceeded() {
    let state = test_state(Some("10s".to_string()));
    let proposal = test_proposal("task-a", 120);

    let result = evaluate_task_budget(&state, &proposal).await;
    assert!(result.is_err());
    let err = result.unwrap_err();
    assert!(
        err.contains("would exceed goal time budget"),
        "unexpected error: {}",
        err
    );
}

#[tokio::test]
async fn task_accepted_when_budget_available() {
    let state = test_state(Some("1h".to_string()));
    let proposal = test_proposal("task-b", 120);

    let result = evaluate_task_budget(&state, &proposal).await;
    assert!(result.is_ok());
    let snapshot: PerTaskBudgetSnapshot = result.unwrap();
    assert_eq!(snapshot.task_budget_secs, 120);
    assert!(snapshot.remaining_budget_secs.unwrap_or(0) > 0);
}

#[tokio::test]
async fn task_accepted_when_budget_unbounded() {
    let state = test_state(None);
    let proposal = test_proposal("task-c", 120);

    let result = evaluate_task_budget(&state, &proposal).await;
    assert!(result.is_ok());
    let snapshot: PerTaskBudgetSnapshot = result.unwrap();
    assert_eq!(snapshot.total_budget_secs, None);
    assert_eq!(snapshot.remaining_budget_secs, None);
}