1mod policy;
8mod result;
9
10pub use policy::BudgetPolicy;
11pub use result::{PolicyResult, PolicyStatus, Severity};
12
13use crate::model::Measurement;
14
15#[must_use]
19pub fn evaluate(
20 measurement: &Measurement,
21 policy: &BudgetPolicy,
22 baseline_units: Option<u64>,
23) -> Vec<PolicyResult> {
24 let mut out = Vec::new();
25 let actual = measurement.total_cu;
26
27 if let Some(max) = policy.absolute_max_cu {
28 out.push(eval_absolute(actual, max));
29 if let Some(warn_pct) = policy.warn_at_budget_pct {
30 if let Some(r) = eval_warn_threshold(actual, max, warn_pct) {
31 out.push(r);
32 }
33 }
34 if let Some(min_margin) = policy.min_margin_pct {
35 out.push(eval_min_margin(actual, max, min_margin));
36 }
37 }
38
39 if let Some(base) = baseline_units {
40 if let Some(pct) = policy.max_regression_pct {
41 out.push(eval_regression_pct(actual, base, pct));
42 }
43 if let Some(units) = policy.max_regression_units {
44 out.push(eval_regression_units(actual, base, units));
45 }
46 }
47
48 if let Some(max_count) = policy.max_cpi_count {
49 out.push(eval_cpi_count(measurement.cpi_count, max_count));
50 }
51 if let Some(max_depth) = policy.max_cpi_depth {
52 out.push(eval_cpi_depth(measurement.cpi_depth, max_depth));
53 }
54 if let Some(max_unattr) = policy.max_unattributed_pct {
55 out.push(eval_unattributed(measurement.unattributed_pct, max_unattr));
56 }
57 if let (Some(warn), Some(overhead)) = (
58 policy.instrumentation_overhead_warn_pct,
59 measurement.instrumentation_overhead_pct,
60 ) {
61 out.push(eval_overhead(overhead, warn));
62 }
63
64 out
65}
66
67#[must_use]
69pub fn overall_status(results: &[PolicyResult]) -> PolicyStatus {
70 results
71 .iter()
72 .fold(PolicyStatus::Pass, |acc, r| acc.max(r.status))
73}
74
75fn pct(part: u64, whole: u64) -> f64 {
76 if whole == 0 {
77 0.0
78 } else {
79 (part as f64 / whole as f64) * 100.0
80 }
81}
82
83fn eval_absolute(actual: u64, max: u64) -> PolicyResult {
84 let (status, severity) = if actual > max {
85 (PolicyStatus::Fail, Severity::Error)
86 } else {
87 (PolicyStatus::Pass, Severity::Info)
88 };
89 PolicyResult {
90 policy_id: "absolute_max_cu".into(),
91 status,
92 severity,
93 actual: Some(actual as f64),
94 expected: Some(max as f64),
95 message: format!("{actual} CU against an absolute maximum of {max} CU"),
96 remediation: (status == PolicyStatus::Fail).then(|| {
97 "Reduce hot-path compute: move cheap validation before CPIs and cut event emission."
98 .to_string()
99 }),
100 }
101}
102
103fn eval_warn_threshold(actual: u64, max: u64, warn_pct: f64) -> Option<PolicyResult> {
104 if actual > max {
105 return None; }
107 let used = pct(actual, max);
108 if used < warn_pct {
109 return None;
110 }
111 Some(PolicyResult {
112 policy_id: "warn_at_budget_pct".into(),
113 status: PolicyStatus::Warn,
114 severity: Severity::Warning,
115 actual: Some(used),
116 expected: Some(warn_pct),
117 message: format!("{used:.1}% of budget used (warns at {warn_pct:.0}%)"),
118 remediation: Some("Approaching the budget ceiling; profile the hottest scope.".into()),
119 })
120}
121
122fn eval_min_margin(actual: u64, max: u64, min_margin: f64) -> PolicyResult {
123 let margin = 100.0 - pct(actual, max);
124 let ok = margin >= min_margin;
125 PolicyResult {
126 policy_id: "min_margin_pct".into(),
127 status: if ok {
128 PolicyStatus::Pass
129 } else {
130 PolicyStatus::Warn
131 },
132 severity: if ok {
133 Severity::Info
134 } else {
135 Severity::Warning
136 },
137 actual: Some(margin),
138 expected: Some(min_margin),
139 message: format!("{margin:.1}% margin below budget (minimum {min_margin:.0}%)"),
140 remediation: (!ok).then(|| "Thin margin; a small regression could breach budget.".into()),
141 }
142}
143
144fn eval_regression_pct(actual: u64, base: u64, max_pct: f64) -> PolicyResult {
145 let delta = actual as i64 - base as i64;
146 let delta_pct = if base == 0 {
147 0.0
148 } else {
149 (delta as f64 / base as f64) * 100.0
150 };
151 let ok = delta_pct <= max_pct;
152 PolicyResult {
153 policy_id: "max_regression_pct".into(),
154 status: if ok {
155 PolicyStatus::Pass
156 } else {
157 PolicyStatus::Fail
158 },
159 severity: if ok { Severity::Info } else { Severity::Error },
160 actual: Some(delta_pct),
161 expected: Some(max_pct),
162 message: format!(
163 "regression {delta_pct:+.2}% vs baseline {base} CU (allowed +{max_pct:.2}%)"
164 ),
165 remediation: (!ok)
166 .then(|| "Inspect the CPI count and recently changed validation path.".into()),
167 }
168}
169
170fn eval_regression_units(actual: u64, base: u64, max_units: u64) -> PolicyResult {
171 let delta = actual as i64 - base as i64;
172 let ok = delta <= max_units as i64;
173 PolicyResult {
174 policy_id: "max_regression_units".into(),
175 status: if ok {
176 PolicyStatus::Pass
177 } else {
178 PolicyStatus::Fail
179 },
180 severity: if ok { Severity::Info } else { Severity::Error },
181 actual: Some(delta as f64),
182 expected: Some(max_units as f64),
183 message: format!("regression {delta:+} CU vs baseline (allowed +{max_units} CU)"),
184 remediation: (!ok)
185 .then(|| "Compute grew beyond the unit budget; bisect recent changes.".into()),
186 }
187}
188
189fn eval_cpi_count(actual: u32, max: u32) -> PolicyResult {
190 let ok = actual <= max;
191 PolicyResult {
192 policy_id: "max_cpi_count".into(),
193 status: if ok {
194 PolicyStatus::Pass
195 } else {
196 PolicyStatus::Fail
197 },
198 severity: if ok { Severity::Info } else { Severity::Error },
199 actual: Some(actual as f64),
200 expected: Some(max as f64),
201 message: format!("{actual} CPIs (maximum {max})"),
202 remediation: (!ok).then(|| "Check for duplicate ATA creation or redundant CPIs.".into()),
203 }
204}
205
206fn eval_cpi_depth(actual: u32, max: u32) -> PolicyResult {
207 let ok = actual <= max;
208 PolicyResult {
209 policy_id: "max_cpi_depth".into(),
210 status: if ok {
211 PolicyStatus::Pass
212 } else {
213 PolicyStatus::Fail
214 },
215 severity: if ok { Severity::Info } else { Severity::Error },
216 actual: Some(actual as f64),
217 expected: Some(max as f64),
218 message: format!("CPI depth {actual} (maximum {max})"),
219 remediation: (!ok).then(|| "Deep CPI nesting risks the runtime invoke-depth limit.".into()),
220 }
221}
222
223fn eval_unattributed(actual_pct: f64, max_pct: f64) -> PolicyResult {
224 let ok = actual_pct <= max_pct;
225 PolicyResult {
226 policy_id: "max_unattributed_pct".into(),
227 status: if ok {
228 PolicyStatus::Pass
229 } else {
230 PolicyStatus::Warn
231 },
232 severity: if ok {
233 Severity::Info
234 } else {
235 Severity::Warning
236 },
237 actual: Some(actual_pct),
238 expected: Some(max_pct),
239 message: format!("{actual_pct:.1}% unattributed CU (maximum {max_pct:.0}%)"),
240 remediation: (!ok).then(|| {
241 "Add scope markers around account validation and math to attribute CU.".into()
242 }),
243 }
244}
245
246fn eval_overhead(actual_pct: f64, warn_pct: f64) -> PolicyResult {
247 let ok = actual_pct <= warn_pct;
248 PolicyResult {
249 policy_id: "instrumentation_overhead_warn_pct".into(),
250 status: if ok {
251 PolicyStatus::Pass
252 } else {
253 PolicyStatus::Warn
254 },
255 severity: if ok {
256 Severity::Info
257 } else {
258 Severity::Warning
259 },
260 actual: Some(actual_pct),
261 expected: Some(warn_pct),
262 message: format!("instrumentation overhead {actual_pct:.1}% (warns at {warn_pct:.0}%)"),
263 remediation: (!ok).then(|| "Reduce the number of profiler markers in the hot path.".into()),
264 }
265}
266
267#[cfg(test)]
268mod tests {
269 use super::*;
270 use crate::model::Measurement;
271
272 fn measurement(total: u64) -> Measurement {
273 Measurement {
274 total_cu: total,
275 ..Measurement::empty()
276 }
277 }
278
279 #[test]
280 fn absolute_budget_fails_when_exceeded() {
281 let m = measurement(120_000);
282 let policy = BudgetPolicy {
283 absolute_max_cu: Some(100_000),
284 ..Default::default()
285 };
286 let results = evaluate(&m, &policy, None);
287 assert_eq!(overall_status(&results), PolicyStatus::Fail);
288 assert!(results[0].remediation.is_some());
289 }
290
291 #[test]
292 fn warn_threshold_triggers_below_ceiling() {
293 let m = measurement(96_000);
294 let policy = BudgetPolicy {
295 absolute_max_cu: Some(100_000),
296 warn_at_budget_pct: Some(90.0),
297 ..Default::default()
298 };
299 let results = evaluate(&m, &policy, None);
300 assert_eq!(overall_status(&results), PolicyStatus::Warn);
301 }
302
303 #[test]
304 fn regression_pct_fails_over_allowance() {
305 let m = measurement(96_812);
306 let policy = BudgetPolicy {
307 max_regression_pct: Some(5.0),
308 ..Default::default()
309 };
310 let results = evaluate(&m, &policy, Some(91_204));
311 assert_eq!(overall_status(&results), PolicyStatus::Fail);
312 }
313
314 #[test]
315 fn within_all_limits_passes() {
316 let m = measurement(50_000);
317 let policy = BudgetPolicy {
318 absolute_max_cu: Some(100_000),
319 warn_at_budget_pct: Some(90.0),
320 max_regression_pct: Some(5.0),
321 ..Default::default()
322 };
323 let results = evaluate(&m, &policy, Some(49_500));
324 assert_eq!(overall_status(&results), PolicyStatus::Pass);
325 }
326}