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 serde::Serialize;
use serde_json::Value;
use std::fmt::Write;
use std::path::{Path, PathBuf};

use super::{GoalProof, GoalState, GoalTaskGraph, GOAL_PROOF_FILE};

mod release;
mod sections;

#[derive(Debug, Clone, Serialize)]
pub struct GoalOpenPrDraft {
    pub goal_id: String,
    pub dry_run: bool,
    pub draft: bool,
    pub title: String,
    pub body: String,
    pub head_branch: Option<String>,
    pub existing_pr_url: Option<String>,
    pub proof_path: PathBuf,
}

pub(crate) async fn render_goal_open_pr(
    goal_id: &str,
    draft: bool,
    dry_run: bool,
) -> Result<GoalOpenPrDraft> {
    let state = super::resolve_goal(goal_id).await?;
    let proof_path = state.state_dir.join(GOAL_PROOF_FILE);
    if !tokio::fs::try_exists(&proof_path).await? {
        return missing_evidence(&state, goal_id, "goal proof is missing");
    }

    let proof = GoalProof::load(&state.state_dir)
        .await
        .with_context(|| format!("Failed to load goal proof for {}", state.goal_id))?;
    let task_graph = GoalTaskGraph::load(&state.state_dir)
        .await
        .with_context(|| format!("Failed to load goal task graph for {}", state.goal_id))?;
    let delivery_metadata = super::task_graph::load_task_delivery_metadata(&state.state_dir)
        .await
        .with_context(|| {
            format!(
                "Failed to load goal delivery metadata for {}",
                state.goal_id
            )
        })?;

    if !has_pr_evidence(&proof, &delivery_metadata) {
        return missing_evidence(&state, goal_id, "proof evidence is missing");
    }

    let head_branch = select_head_branch(&proof, &delivery_metadata);
    let existing_pr_url = select_pr_url(&delivery_metadata);
    let title = format!("Goal proof: {}", state.normalized_goal);
    let body = render_pr_body(&state, &proof, &task_graph, &delivery_metadata, draft);
    Ok(GoalOpenPrDraft {
        goal_id: state.goal_id,
        dry_run,
        draft,
        title,
        body,
        head_branch,
        existing_pr_url,
        proof_path,
    })
}

fn missing_evidence(
    state: &GoalState,
    requested_goal_id: &str,
    reason: &str,
) -> Result<GoalOpenPrDraft> {
    let next_goal_id = if requested_goal_id == "latest" {
        "latest"
    } else {
        state.goal_id.as_str()
    };
    anyhow::bail!(
        "Goal '{}' {reason}.\nNext: omk goal execute {next_goal_id}",
        state.goal_id
    );
}

fn has_pr_evidence(proof: &GoalProof, delivery_metadata: &[Value]) -> bool {
    !proof.gates.is_empty()
        || !proof.changed_files.is_empty()
        || !delivery_metadata.is_empty()
        || proof.artifacts.iter().any(|artifact| {
            matches!(
                artifact.kind.as_str(),
                "agent_run" | "review" | "security_review"
            )
        })
}

fn render_pr_body(
    state: &GoalState,
    proof: &GoalProof,
    task_graph: &GoalTaskGraph,
    delivery_metadata: &[Value],
    draft: bool,
) -> String {
    let mut body = String::new();
    let proof_value = serde_json::to_value(proof).unwrap_or(Value::Null);
    push_goal_section(&mut body, state, proof, draft);
    push_task_summary(&mut body, proof, task_graph);
    push_delivery_metadata(&mut body, delivery_metadata);
    push_proof_summary(&mut body, proof);
    push_verification_wall(&mut body, proof);
    release::push_release_candidate_notes(&mut body, state, proof, draft);
    sections::push_review_evidence(&mut body, &proof_value);
    sections::push_integration_evidence(&mut body, &proof_value);
    sections::push_oracle_evidence(&mut body, &proof_value);
    push_known_gaps(&mut body, proof);
    push_changed_files(&mut body, proof);
    push_artifacts(&mut body, proof);
    body
}

fn push_goal_section(body: &mut String, state: &GoalState, proof: &GoalProof, draft: bool) {
    push_heading(body, "Goal");
    push_line(body, &format!("- Goal id: `{}`", state.goal_id));
    push_line(body, &format!("- Objective: {}", state.original_goal));
    push_line(body, &format!("- Status: `{}`", proof.status));
    push_line(body, &format!("- Readiness: {}", proof.readiness));
    push_line(body, &format!("- Draft: `{draft}`"));
    push_line(
        body,
        &format!(
            "- Proof path: `{}`",
            state.state_dir.join(GOAL_PROOF_FILE).display()
        ),
    );
    push_blank(body);
}

fn push_task_summary(body: &mut String, proof: &GoalProof, task_graph: &GoalTaskGraph) {
    push_heading(body, "Task Summary");
    push_line(
        body,
        &format!("- Total: {}", proof.task_graph_summary.total_tasks),
    );
    push_line(
        body,
        &format!("- Done: {}", proof.task_graph_summary.done_tasks),
    );
    push_line(
        body,
        &format!("- Pending: {}", proof.task_graph_summary.pending_tasks),
    );
    push_line(
        body,
        &format!("- Blocked: {}", proof.task_graph_summary.blocked_tasks),
    );
    for task in &task_graph.tasks {
        let owner = task
            .owner_role
            .as_deref()
            .map(|owner| format!(" owner={owner}"))
            .unwrap_or_default();
        push_line(
            body,
            &format!("- `{}` `{}`{}: {}", task.id, task.status, owner, task.title),
        );
    }
    push_blank(body);
}

fn push_delivery_metadata(body: &mut String, delivery_metadata: &[Value]) {
    push_heading(body, "Delivery Metadata");
    if delivery_metadata.is_empty() {
        push_line(body, "- No owner/write-scope delivery metadata recorded.");
        push_blank(body);
        return;
    }

    for delivery in delivery_metadata {
        if let Some(task_id) = value_str(delivery, "task_id") {
            push_line(body, &format!("- task: `{task_id}`"));
        } else {
            push_line(body, "- task: `unknown`");
        }
        if let Some(slice_id) = value_str(delivery, "slice_id") {
            push_line(body, &format!("  - slice: {slice_id}"));
        }
        if let Some(owner) = value_str(delivery, "owner") {
            push_line(body, &format!("  - owner: {owner}"));
        }
        if let Some(branch) = value_str(delivery, "branch") {
            push_line(body, &format!("  - branch: {branch}"));
        }
        if let Some(worktree) = value_str(delivery, "worktree_path") {
            push_line(body, &format!("  - worktree: {worktree}"));
        }
        if let Some(pr_link) =
            value_str(delivery, "pr_url").or_else(|| value_str(delivery, "pr_link"))
        {
            push_line(body, &format!("  - pr: {pr_link}"));
        }
        if let Some(summary) = value_str(delivery, "verification_summary") {
            push_line(body, &format!("  - verification: {summary}"));
        }
        if let Some(write_scope) = string_array(delivery.get("write_scope")) {
            push_line(body, "  - write scope:");
            for path in write_scope {
                push_line(body, &format!("    - `{path}`"));
            }
        }
    }
    push_blank(body);
}

fn push_proof_summary(body: &mut String, proof: &GoalProof) {
    push_heading(body, "Proof Summary");
    push_line(body, &format!("- Summary: {}", proof.summary));
    push_line(body, &format!("- Generated: `{}`", proof.generated_at));
    if let Some(git) = &proof.git {
        push_line(body, &format!("- Branch: `{}`", git.branch));
        push_line(body, &format!("- Head: `{}`", git.head));
        push_line(body, &format!("- Dirty: `{}`", git.dirty));
    }
    if proof.commits.is_empty() {
        push_line(body, "- Commits: none recorded");
    } else {
        push_line(body, "- Commits:");
        for commit in &proof.commits {
            push_line(body, &format!("  - `{commit}`"));
        }
    }
    push_blank(body);
}

fn push_verification_wall(body: &mut String, proof: &GoalProof) {
    push_heading(body, "Verification Wall");
    if proof.gates.is_empty() {
        push_line(body, "- No verification gates recorded.");
    } else {
        for gate in &proof.gates {
            let status = if gate.passed { "passed" } else { "failed" };
            let exit_code = gate
                .exit_code
                .map(|code| code.to_string())
                .unwrap_or_else(|| "unknown".to_string());
            push_line(
                body,
                &format!(
                    "- `{}`: {} required={} exit={} duration={}ms",
                    gate.name, status, gate.required, exit_code, gate.duration_ms
                ),
            );
            if let Some(path) = gate.output_path.as_deref() {
                push_line(body, &format!("  - artifact: `{path}`"));
            }
        }
    }
    push_line(
        body,
        &format!(
            "- Post-mutation gates ran: `{}`",
            proof.post_mutation_gates_ran
        ),
    );
    push_blank(body);
}

fn push_known_gaps(body: &mut String, proof: &GoalProof) {
    push_heading(body, "Known Gaps");
    if proof.known_gaps.is_empty() {
        push_line(body, "- None recorded.");
    } else {
        for gap in &proof.known_gaps {
            push_line(body, &format!("- {gap}"));
        }
    }
    if !proof.human_decisions_required.is_empty() {
        push_line(body, "- Human decisions required:");
        for decision in &proof.human_decisions_required {
            push_line(body, &format!("  - {decision}"));
        }
    }
    push_blank(body);
}

fn push_changed_files(body: &mut String, proof: &GoalProof) {
    push_heading(body, "Changed Files");
    if proof.changed_files.is_empty() {
        push_line(body, "- None recorded.");
    } else {
        let mut changed_files = proof.changed_files.clone();
        changed_files.sort();
        for path in changed_files {
            push_line(body, &format!("- `{path}`"));
        }
    }
    push_blank(body);
}

fn push_artifacts(body: &mut String, proof: &GoalProof) {
    push_heading(body, "Artifacts");
    if proof.artifacts.is_empty() {
        push_line(body, "- None recorded.");
    } else {
        for artifact in &proof.artifacts {
            push_line(
                body,
                &format!("- `{}`: `{}`", artifact.kind, display_path(&artifact.path)),
            );
        }
    }
}

fn display_path(path: &Path) -> String {
    path.display().to_string()
}

fn select_head_branch(proof: &GoalProof, delivery_metadata: &[Value]) -> Option<String> {
    delivery_metadata
        .iter()
        .find_map(|delivery| value_str(delivery, "branch").map(str::to_string))
        .or_else(|| {
            proof
                .git
                .as_ref()
                .map(|git| git.branch.clone())
                .filter(|branch| !branch.trim().is_empty())
        })
}

fn select_pr_url(delivery_metadata: &[Value]) -> Option<String> {
    delivery_metadata.iter().find_map(|delivery| {
        value_str(delivery, "pr_url")
            .or_else(|| value_str(delivery, "pr_link"))
            .map(str::to_string)
    })
}

pub(super) fn push_heading(body: &mut String, heading: &str) {
    let _ = writeln!(body, "## {heading}");
}

pub(super) fn push_line(body: &mut String, line: &str) {
    let _ = writeln!(body, "{line}");
}

pub(super) fn push_blank(body: &mut String) {
    body.push('\n');
}

pub(super) fn value_str<'a>(value: &'a Value, key: &str) -> Option<&'a str> {
    value.get(key).and_then(Value::as_str)
}

pub(super) fn string_array(value: Option<&Value>) -> Option<Vec<String>> {
    let values = value?.as_array()?;
    Some(
        values
            .iter()
            .filter_map(Value::as_str)
            .map(str::to_string)
            .collect(),
    )
    .filter(|values: &Vec<String>| !values.is_empty())
}