Skip to main content

lean_ctx_workgraph/outcome/
fingerprint.rs

1// SPDX-License-Identifier: Apache-2.0
2
3//! Stable task metadata used to group outcome observations.
4
5use serde::{Deserialize, Serialize};
6
7use super::contracts::TaskClass;
8
9/// Task context captured alongside an outcome evaluation.
10#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
11pub struct TaskFingerprint {
12    pub language: Option<String>,
13    pub task_class: TaskClass,
14    pub complexity: String,
15    pub repo_size: Option<String>,
16    pub context_warmth: Option<String>,
17    pub changed_loc: Option<u32>,
18    pub tests_available: bool,
19    pub risk_class: Option<String>,
20    pub agent: Option<String>,
21    pub model: Option<String>,
22    pub provider: Option<String>,
23}
24
25impl TaskFingerprint {
26    /// Create a fingerprint with only its class and complexity known.
27    pub fn new(task_class: TaskClass, complexity: impl Into<String>) -> Self {
28        Self {
29            language: None,
30            task_class,
31            complexity: complexity.into(),
32            repo_size: None,
33            context_warmth: None,
34            changed_loc: None,
35            tests_available: false,
36            risk_class: None,
37            agent: None,
38            model: None,
39            provider: None,
40        }
41    }
42
43    /// Produce a deterministic, human-readable grouping key.
44    pub fn grouping_key(&self) -> String {
45        format!(
46            "{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}",
47            self.language.as_deref().unwrap_or("unknown"),
48            self.task_class,
49            self.complexity,
50            self.repo_size.as_deref().unwrap_or("unknown"),
51            self.context_warmth.as_deref().unwrap_or("unknown"),
52            self.changed_loc
53                .map_or_else(|| "unknown".to_owned(), |value| value.to_string()),
54            self.tests_available,
55            self.risk_class.as_deref().unwrap_or("unknown"),
56            self.agent.as_deref().unwrap_or("unknown"),
57            self.model.as_deref().unwrap_or("unknown"),
58            self.provider.as_deref().unwrap_or("unknown"),
59        )
60    }
61}
62
63impl Default for TaskFingerprint {
64    fn default() -> Self {
65        Self::new(TaskClass::BugFix, "unknown")
66    }
67}