Skip to main content

kaizen/retro/
types.rs

1// SPDX-License-Identifier: AGPL-3.0-or-later
2//! Pure data for the retro engine (`Report`, `Bet`, `Inputs`).
3
4use crate::core::event::{Event, SessionRecord};
5use crate::feedback::types::FeedbackRecord;
6use crate::metrics::types::{FileFact, ToolSpanView};
7use serde::{Deserialize, Serialize};
8use std::collections::{HashMap, HashSet};
9
10/// Workspace-local facts assembled once at the IO boundary.
11#[derive(Debug, Clone)]
12pub struct Inputs {
13    pub window_start_ms: u64,
14    pub window_end_ms: u64,
15    /// Joined rows time-ordered.
16    pub events: Vec<(SessionRecord, Event)>,
17    pub files_touched: Vec<(String, String)>,
18    pub skills_used: Vec<(String, String)>,
19    pub tool_spans: Vec<ToolSpanView>,
20    /// Skills referenced in the last `usage_lookback_ms` window (for H1).
21    pub skills_used_recent_slugs: HashSet<String>,
22    pub usage_lookback_ms: u64,
23    pub skill_files_on_disk: Vec<SkillFileOnDisk>,
24    /// `.cursor/rules/*.mdc` stems (same shape as [`SkillFileOnDisk`]).
25    pub rule_files_on_disk: Vec<SkillFileOnDisk>,
26    pub rules_used_recent_slugs: HashSet<String>,
27    pub file_facts: HashMap<String, FileFact>,
28    pub aggregates: RetroAggregates,
29    /// LLM-as-Judge eval scores for sessions in the window: (session_id, score 0..1).
30    pub eval_scores: Vec<(String, f64)>,
31    /// Sessions with a recorded prompt fingerprint: (session_id, fingerprint).
32    pub prompt_fingerprints: Vec<(String, String)>,
33    /// Human feedback records in the window.
34    pub feedback: Vec<FeedbackRecord>,
35}
36
37#[derive(Debug, Clone)]
38pub struct SkillFileOnDisk {
39    pub slug: String,
40    /// Bytes of frontmatter + body (rough token proxy).
41    pub size_bytes: u64,
42    pub mtime_ms: u64,
43}
44
45#[derive(Debug, Clone, Default)]
46pub struct RetroAggregates {
47    pub unique_session_ids: HashSet<String>,
48    pub tool_event_counts: HashMap<String, u64>,
49    pub tool_cost_usd_e6: HashMap<String, i64>,
50    pub model_session_counts: HashMap<String, u64>,
51    pub total_cost_usd_e6: i64,
52}
53
54/// One ranked improvement bet.
55#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
56pub struct Bet {
57    /// Stable id for dedup (`H2:foo.rs|bar.rs`).
58    pub id: String,
59    pub heuristic_id: String,
60    pub title: String,
61    pub hypothesis: String,
62    pub expected_tokens_saved_per_week: f64,
63    pub effort_minutes: u32,
64    pub evidence: Vec<String>,
65    pub apply_step: String,
66    #[serde(default)]
67    pub evidence_recency_ms: u64,
68}
69
70impl Bet {
71    pub fn score(&self) -> f64 {
72        self.expected_tokens_saved_per_week / (self.effort_minutes as f64 + 1.0)
73    }
74}
75
76#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
77pub struct RetroMeta {
78    pub week_label: String,
79    pub span_start_ms: u64,
80    pub span_end_ms: u64,
81    pub session_count: u64,
82    pub total_cost_usd_e6: i64,
83}
84
85#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
86pub struct RetroStats {
87    pub sessions: u64,
88    pub total_cost_usd_e6: i64,
89    pub top_model: Option<String>,
90    pub top_model_pct: Option<u64>,
91    pub top_tool: Option<String>,
92    pub top_tool_pct: Option<u64>,
93    pub median_session_minutes: Option<u64>,
94}
95
96/// JSON + markdown source of truth for CLI `--json` and reports.
97#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
98pub struct Report {
99    pub meta: RetroMeta,
100    pub top_bets: Vec<Bet>,
101    pub skipped_deduped: Vec<String>,
102    pub stats: RetroStats,
103}