lean_ctx/core/gain/
evidence.rs1use serde::{Deserialize, Serialize};
10
11#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
13#[serde(rename_all = "snake_case")]
14pub enum EconomicEvidence {
15 #[default]
17 LocalEstimate,
18 ObservedToolTraffic,
21 ProviderCountedInput,
24 ProviderMeasuredUsage,
27 PairedControl,
30}
31
32impl EconomicEvidence {
33 pub fn label(self) -> &'static str {
34 match self {
35 Self::LocalEstimate => "local estimate",
36 Self::ObservedToolTraffic => "observed tool traffic (provider path not visible)",
37 Self::ProviderCountedInput => "provider-counted input (counterfactual)",
38 Self::ProviderMeasuredUsage => "provider-measured usage (savings estimated)",
39 Self::PairedControl => "paired control/treatment",
40 }
41 }
42
43 pub fn bill_impact_observable(self) -> bool {
46 self >= Self::ProviderMeasuredUsage
47 }
48}
49
50#[derive(Debug, Clone, Copy, PartialEq)]
52pub struct EconomicView {
53 pub evidence: EconomicEvidence,
54 pub net_bill_impact_tokens: Option<i64>,
55 pub net_bill_impact_usd: Option<f64>,
56 pub roi: Option<f64>,
57}
58
59pub fn economic_view(
66 observed_commands: u64,
67 provider_turns: u64,
68 net_tokens_if_observed: i64,
69 net_usd_if_observed: f64,
70 tool_spend_usd: f64,
71) -> EconomicView {
72 let evidence = if provider_turns > 0 {
73 EconomicEvidence::ProviderMeasuredUsage
74 } else if observed_commands > 0 {
75 EconomicEvidence::ObservedToolTraffic
76 } else {
77 EconomicEvidence::LocalEstimate
78 };
79 let observable = evidence.bill_impact_observable();
80 EconomicView {
81 evidence,
82 net_bill_impact_tokens: observable.then_some(net_tokens_if_observed),
83 net_bill_impact_usd: observable.then_some(net_usd_if_observed),
84 roi: (observable && tool_spend_usd > 0.0).then(|| net_usd_if_observed / tool_spend_usd),
85 }
86}
87
88#[cfg(test)]
89mod tests {
90 use super::*;
91
92 #[test]
93 fn integrated_mode_never_reports_a_bill_impact_or_roi() {
94 let v = economic_view(4_200, 0, 1_500_000, 18.75, 3.0);
96 assert_eq!(v.evidence, EconomicEvidence::ObservedToolTraffic);
97 assert_eq!(
98 v.net_bill_impact_tokens, None,
99 "net must not fall back to gross"
100 );
101 assert_eq!(v.net_bill_impact_usd, None);
102 assert_eq!(v.roi, None);
103 }
104
105 #[test]
106 fn gateway_mode_reports_an_estimated_net_and_roi() {
107 let v = economic_view(4_200, 900, -12_000, -0.4, 2.0);
108 assert_eq!(v.evidence, EconomicEvidence::ProviderMeasuredUsage);
109 assert_eq!(
110 v.net_bill_impact_tokens,
111 Some(-12_000),
112 "a negative net is reported"
113 );
114 assert_eq!(v.roi, Some(-0.2));
115 assert_ne!(
116 v.evidence,
117 EconomicEvidence::PairedControl,
118 "measured usage is still not a paired bill claim"
119 );
120 }
121
122 #[test]
123 fn nothing_observed_is_a_local_estimate() {
124 let v = economic_view(0, 0, 0, 0.0, 0.0);
125 assert_eq!(v.evidence, EconomicEvidence::LocalEstimate);
126 assert_eq!(v.net_bill_impact_usd, None);
127 }
128}