Skip to main content

kaizen/shell/
outcomes_cmd.rs

1// SPDX-License-Identifier: AGPL-3.0-or-later
2//! `outcomes show` / `outcomes measure` (measure is for ingest; shows in `kaizen help` as hidden if wired).
3
4use crate::collect::outcomes;
5use crate::core::config;
6use crate::store::{SessionOutcomeRow, Store};
7use anyhow::{Context, Result};
8use std::path::Path;
9use std::time::Duration;
10
11/// Internal worker: run configured commands and write `session_outcomes`.
12pub fn cmd_outcomes_measure(workspace: &Path, session_id: &str) -> Result<()> {
13    let cfg = config::load(workspace)?;
14    let out = &cfg.collect.outcomes;
15    if !out.enabled {
16        return Ok(());
17    }
18    let db = workspace.join(".kaizen/kaizen.db");
19    let store = Store::open(&db)?;
20    let Some(session) = store.get_session(session_id).context("session not found")? else {
21        anyhow::bail!("session not in store");
22    };
23    let root = std::path::PathBuf::from(&session.workspace);
24    let tmo = Duration::from_secs(out.timeout_secs.max(1));
25    let m = outcomes::run_outcome_measure(&root, &out.test_cmd, out.lint_cmd.as_deref(), tmo);
26    let now = std::time::SystemTime::now()
27        .duration_since(std::time::UNIX_EPOCH)
28        .map(|d| d.as_millis() as u64)
29        .unwrap_or(0);
30    let row = SessionOutcomeRow {
31        session_id: session_id.to_string(),
32        test_passed: m.test_passed.map(|v| v as i64),
33        test_failed: m.test_failed.map(|v| v as i64),
34        test_skipped: m.test_skipped.map(|v| v as i64),
35        build_ok: None,
36        lint_errors: m.lint_errors.map(|v| v as i64),
37        revert_lines_14d: None,
38        pr_open: None,
39        ci_ok: None,
40        measured_at_ms: now,
41        measure_error: m.measure_error,
42    };
43    store.upsert_session_outcome(&row)?;
44    Ok(())
45}
46
47/// Show one outcome row.
48pub fn cmd_outcomes_show(id: &str, workspace: Option<&Path>) -> Result<()> {
49    let ws = workspace
50        .map(std::path::PathBuf::from)
51        .unwrap_or(std::env::current_dir()?);
52    let store = Store::open(&ws.join(".kaizen/kaizen.db"))?;
53    let r = store
54        .get_session_outcome(id)?
55        .context("no outcome for session")?;
56    print_row(&r);
57    Ok(())
58}
59
60fn print_row(r: &SessionOutcomeRow) {
61    let j = serde_json::json!({
62        "session_id": r.session_id,
63        "test_passed": r.test_passed,
64        "test_failed": r.test_failed,
65        "test_skipped": r.test_skipped,
66        "lint_errors": r.lint_errors,
67        "measured_at_ms": r.measured_at_ms,
68        "measure_error": r.measure_error,
69    });
70    println!("{}", serde_json::to_string_pretty(&j).unwrap_or_default());
71}