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 store = Store::open(&crate::core::workspace::db_path(workspace)?)?;
19    let Some(session) = store.get_session(session_id).context("session not found")? else {
20        anyhow::bail!("session not in store");
21    };
22    let root = std::path::PathBuf::from(&session.workspace);
23    let tmo = Duration::from_secs(out.timeout_secs.max(1));
24    let m = outcomes::run_outcome_measure(&root, &out.test_cmd, out.lint_cmd.as_deref(), tmo);
25    let now = std::time::SystemTime::now()
26        .duration_since(std::time::UNIX_EPOCH)
27        .map(|d| d.as_millis() as u64)
28        .unwrap_or(0);
29    let row = SessionOutcomeRow {
30        session_id: session_id.to_string(),
31        test_passed: m.test_passed.map(|v| v as i64),
32        test_failed: m.test_failed.map(|v| v as i64),
33        test_skipped: m.test_skipped.map(|v| v as i64),
34        build_ok: None,
35        lint_errors: m.lint_errors.map(|v| v as i64),
36        revert_lines_14d: None,
37        pr_open: None,
38        ci_ok: None,
39        measured_at_ms: now,
40        measure_error: m.measure_error,
41    };
42    store.upsert_session_outcome(&row)?;
43    Ok(())
44}
45
46/// Show one outcome row.
47pub fn cmd_outcomes_show(id: &str, workspace: Option<&Path>) -> Result<()> {
48    let ws = workspace
49        .map(std::path::PathBuf::from)
50        .unwrap_or(std::env::current_dir()?);
51    let store = Store::open(&crate::core::workspace::db_path(&ws)?)?;
52    let r = store
53        .get_session_outcome(id)?
54        .context("no outcome for session")?;
55    print_row(&r);
56    Ok(())
57}
58
59fn print_row(r: &SessionOutcomeRow) {
60    let j = serde_json::json!({
61        "session_id": r.session_id,
62        "test_passed": r.test_passed,
63        "test_failed": r.test_failed,
64        "test_skipped": r.test_skipped,
65        "lint_errors": r.lint_errors,
66        "measured_at_ms": r.measured_at_ms,
67        "measure_error": r.measure_error,
68    });
69    println!("{}", serde_json::to_string_pretty(&j).unwrap_or_default());
70}