ci_patterns/
ci_patterns.rs1use crackle_runtime::{CrackleTask, Kiln, ThermalProfile, TaskOutput};
4
5struct CiBuild {
6 duration_secs: f64,
7 test_count: f64,
8 branch: String,
9}
10
11impl CrackleTask for CiBuild {
12 type Output = f64;
13
14 fn fire(&self) -> TaskOutput<Self::Output> {
15 TaskOutput::new(
16 self.duration_secs,
17 vec![
18 ("duration".into(), self.duration_secs),
19 ("test_count".into(), self.test_count),
20 ("duration_per_test".into(), self.duration_secs / self.test_count.max(1.0)),
21 ],
22 )
23 }
24
25 fn label(&self) -> String {
26 self.branch.clone()
27 }
28}
29
30fn main() {
31 let mut kiln = Kiln::new(ThermalProfile::default());
32
33 kiln.fire_and_record(CiBuild { duration_secs: 45.0, test_count: 200.0, branch: "feature/auth".into() }).unwrap();
35 kiln.fire_and_record(CiBuild { duration_secs: 42.0, test_count: 195.0, branch: "fix/typo".into() }).unwrap();
36 kiln.fire_and_record(CiBuild { duration_secs: 48.0, test_count: 210.0, branch: "feature/ui".into() }).unwrap();
37
38 kiln.fire_and_record(CiBuild { duration_secs: 95.0, test_count: 200.0, branch: "feature/cache".into() }).unwrap();
40 kiln.fire_and_record(CiBuild { duration_secs: 102.0, test_count: 198.0, branch: "chore/deps".into() }).unwrap();
41
42 let patterns = kiln.cool();
43 println!("CI Build Pattern Analysis");
44 println!("=========================\n");
45
46 for p in &patterns {
47 println!("[{}] {}", p.kind().to_string().to_uppercase(), p.description());
48 println!(" confidence: {:.2}", p.confidence());
49 println!(" branches: {:?}\n", p.involved_tasks());
50 }
51
52 let phase_shifts: Vec<_> = patterns.iter()
54 .filter(|p| format!("{}", p.kind()) == "phase transition")
55 .collect();
56
57 if !phase_shifts.is_empty() {
58 println!("⚠️ Build duration shifted significantly — investigate recent changes!");
59 }
60}