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