1#![forbid(unsafe_code)]
4#![cfg(feature = "legacy-selector-fixtures")]
5
6use router_policy::{
9 AccountStatus, ActivationGate, ActivationGates, Candidate, CandidateSnapshot,
10 CandidateStateRefs, CapabilityDeclaration, CapabilityRequirements, CapabilityState,
11 ClassificationInput, ConformanceEvidence, DecisionMode, DecisionRecord, DecisionRecordContext,
12 DecisionStateRefs, DisabledPolicy, GateStatus, ModelStatus, PinMode, PolicyPack,
13 PolicyStateRefs, PreviousOutcome, PrivacyMode, ProviderStatus, Quality, ReasoningEffort,
14 RequestedAlias, Role, RoleDeclaration, SelectionOptions, SelectionSnapshot, Signal,
15 build_decision_record, candidate_meets_quality_floor, classify, hard_exclusion_for, select,
16};
17use serde::{Deserialize, Serialize};
18use sha2::{Digest, Sha256};
19use std::collections::{BTreeMap, BTreeSet};
20use std::fmt::Write as _;
21use std::sync::Arc;
22use thiserror::Error;
23
24pub const MAX_EVALUATION_CORPUS_BYTES: usize = 1024 * 1024;
25const MAX_EVALUATION_CASES: usize = 1024;
26const REQUIRED_EVALUATION_CASES: usize = 17;
27const MIN_ROUTINE_CASES: usize = 5;
28const MAX_SCARCE_CAPACITY_UNITS: u64 = 1;
29const SYNTHETIC_CANDIDATE_IDS: [&str; 14] = [
30 "reject-protocol",
31 "reject-tools",
32 "reject-modality",
33 "reject-context",
34 "reject-privacy",
35 "reject-provider-support",
36 "reject-policy-activation",
37 "reject-provider-status",
38 "reject-model-status",
39 "reject-account-status",
40 "reject-quota",
41 "reject-budget",
42 "gate-conformance",
43 "gate-operator",
44];
45const REQUIRED_BASELINES: [&str; 3] = [
46 "always_frontier",
47 "fixed_model_per_provider",
48 "ordered_provider_fallback_only",
49];
50const REQUIRED_ACCEPTANCE: [AcceptanceCriterion; 11] = [
51 AcceptanceCriterion::M2Ac01,
52 AcceptanceCriterion::M2Ac02,
53 AcceptanceCriterion::M2Ac03,
54 AcceptanceCriterion::M2Ac04,
55 AcceptanceCriterion::M2Ac05,
56 AcceptanceCriterion::M2Ac06,
57 AcceptanceCriterion::M2Ac07,
58 AcceptanceCriterion::M2Ac08,
59 AcceptanceCriterion::M2Ac09,
60 AcceptanceCriterion::M2Ac10,
61 AcceptanceCriterion::M2Ac11,
62];
63
64#[derive(Debug, Error, PartialEq, Eq)]
65pub enum EvaluationError {
66 #[error("evaluation corpus is invalid")]
67 InvalidCorpus,
68 #[error("evaluation corpus references an invalid fixture")]
69 InvalidFixture,
70 #[error("evaluation thresholds were not met")]
71 ThresholdFailure,
72}
73
74#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
75#[serde(deny_unknown_fields)]
76struct EvaluationCorpus {
77 schema_version: String,
78 corpus_id: String,
79 corpus_version: String,
80 policy_version: String,
81 execution_profile: String,
82 metric_thresholds: MetricThresholds,
83 capability_profiles: BTreeMap<String, CapabilityRequirements>,
84 routes: BTreeMap<String, RouteFixture>,
85 baselines: Vec<BaselineFixture>,
86 cases: Vec<EvaluationCase>,
87 aggregate_assertions: Vec<AggregateAssertion>,
88}
89
90#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
91#[serde(deny_unknown_fields)]
92struct MetricThresholds {
93 expected_route_accuracy_min: f64,
94 capability_preservation_required: f64,
95 routine_frontier_avoidance_min: f64,
96 intelligent_correctness_non_inferior_to: String,
97 routine_standard_scarce_capacity_strictly_less_than: Vec<String>,
98 required_baseline_rows: Vec<String>,
99}
100
101#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
102#[serde(deny_unknown_fields)]
103struct RouteFixture {
104 provider_id: String,
105 model_id: String,
106 account_pseudonym: String,
107 endpoint_pseudonym: String,
108 reasoning_effort: ReasoningEffort,
109 frontier: bool,
110 scarce_capacity_units: u64,
111}
112
113#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
114#[serde(tag = "rule", rename_all = "snake_case", deny_unknown_fields)]
115enum BaselineFixture {
116 StrongestHardEligibleInFirstEligiblePhase {
117 baseline_id: BaselineId,
118 },
119 FixedProviderModelThenDescendWhenHardIneligible {
120 baseline_id: BaselineId,
121 fixed_model_ids: BTreeMap<String, String>,
122 },
123 OrderedCandidatesWithoutTaskRoleSelection {
124 baseline_id: BaselineId,
125 ordered_candidate_ids: Vec<String>,
126 },
127}
128
129impl BaselineFixture {
130 const fn id(&self) -> BaselineId {
131 match self {
132 Self::StrongestHardEligibleInFirstEligiblePhase { baseline_id }
133 | Self::FixedProviderModelThenDescendWhenHardIneligible { baseline_id, .. }
134 | Self::OrderedCandidatesWithoutTaskRoleSelection { baseline_id, .. } => *baseline_id,
135 }
136 }
137}
138
139#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
140#[serde(rename_all = "snake_case")]
141enum BaselineId {
142 AlwaysFrontier,
143 FixedModelPerProvider,
144 OrderedProviderFallbackOnly,
145}
146
147impl BaselineId {
148 const fn as_str(self) -> &'static str {
149 match self {
150 Self::AlwaysFrontier => "always_frontier",
151 Self::FixedModelPerProvider => "fixed_model_per_provider",
152 Self::OrderedProviderFallbackOnly => "ordered_provider_fallback_only",
153 }
154 }
155}
156
157#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
158#[serde(deny_unknown_fields)]
159struct EvaluationCase {
160 case_id: String,
161 acceptance_criteria: Vec<AcceptanceCriterion>,
162 input: CaseInput,
163 expected_role: Role,
164 expected_capability_profile: String,
165 expected_classifier_reasons: Vec<router_policy::ClassifierReason>,
166 expected_outcome: ExpectedOutcome,
167 expected_route: Option<ExpectedRoute>,
168 #[serde(default)]
169 expected_dispatch_route: Option<ExpectedRoute>,
170 #[serde(default)]
171 expected_no_eligible_route: Option<ExpectedNoEligibleRoute>,
172 expected_exclusions: Vec<ExpectedExclusion>,
173 #[serde(default)]
174 record_expectations: Option<RecordExpectations>,
175 #[serde(default)]
176 strategy_expectations: Vec<StrategyExpectation>,
177 metrics: CaseMetrics,
178 assertions: Vec<CaseAssertion>,
179}
180
181#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
182enum AcceptanceCriterion {
183 #[serde(rename = "M2-AC-01")]
184 M2Ac01,
185 #[serde(rename = "M2-AC-02")]
186 M2Ac02,
187 #[serde(rename = "M2-AC-03")]
188 M2Ac03,
189 #[serde(rename = "M2-AC-04")]
190 M2Ac04,
191 #[serde(rename = "M2-AC-05")]
192 M2Ac05,
193 #[serde(rename = "M2-AC-06")]
194 M2Ac06,
195 #[serde(rename = "M2-AC-07")]
196 M2Ac07,
197 #[serde(rename = "M2-AC-08")]
198 M2Ac08,
199 #[serde(rename = "M2-AC-09")]
200 M2Ac09,
201 #[serde(rename = "M2-AC-10")]
202 M2Ac10,
203 #[serde(rename = "M2-AC-11")]
204 M2Ac11,
205}
206
207#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
208#[serde(deny_unknown_fields)]
209struct RecordExpectations {
210 append_only: bool,
211 required_fields: Vec<RequiredRecordField>,
212 forbidden_fields: Vec<ForbiddenRecordField>,
213}
214
215#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
216#[serde(rename_all = "snake_case")]
217enum RequiredRecordField {
218 PolicyVersion,
219 SelectionSnapshotId,
220 StateRefs,
221 SelectedRoute,
222 SelectedRouteTrace,
223 Exclusions,
224}
225
226impl RequiredRecordField {
227 const fn as_str(self) -> &'static str {
228 match self {
229 Self::PolicyVersion => "policy_version",
230 Self::SelectionSnapshotId => "selection_snapshot_id",
231 Self::StateRefs => "state_refs",
232 Self::SelectedRoute => "selected_route",
233 Self::SelectedRouteTrace => "selected_route_trace",
234 Self::Exclusions => "exclusions",
235 }
236 }
237}
238
239#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
240#[serde(rename_all = "snake_case")]
241enum ForbiddenRecordField {
242 Prompt,
243 SourceCode,
244 ToolArguments,
245 ToolResults,
246 RawAccountId,
247 Credential,
248 Token,
249 Authorization,
250 Cookie,
251 UpstreamHeaders,
252 UpstreamBody,
253 EndpointUrl,
254}
255
256impl ForbiddenRecordField {
257 const fn as_str(self) -> &'static str {
258 match self {
259 Self::Prompt => "prompt",
260 Self::SourceCode => "source_code",
261 Self::ToolArguments => "tool_arguments",
262 Self::ToolResults => "tool_results",
263 Self::RawAccountId => "raw_account_id",
264 Self::Credential => "credential",
265 Self::Token => "token",
266 Self::Authorization => "authorization",
267 Self::Cookie => "cookie",
268 Self::UpstreamHeaders => "upstream_headers",
269 Self::UpstreamBody => "upstream_body",
270 Self::EndpointUrl => "endpoint_url",
271 }
272 }
273}
274
275#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
276#[serde(rename_all = "snake_case")]
277enum StrategyId {
278 Intelligent,
279 AlwaysFrontier,
280 FixedModelPerProvider,
281 OrderedProviderFallbackOnly,
282}
283
284impl From<BaselineId> for StrategyId {
285 fn from(value: BaselineId) -> Self {
286 match value {
287 BaselineId::AlwaysFrontier => Self::AlwaysFrontier,
288 BaselineId::FixedModelPerProvider => Self::FixedModelPerProvider,
289 BaselineId::OrderedProviderFallbackOnly => Self::OrderedProviderFallbackOnly,
290 }
291 }
292}
293
294#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
295#[serde(deny_unknown_fields)]
296struct StrategyExpectation {
297 strategy_id: StrategyId,
298 candidate_id: String,
299 objective_correct: bool,
300 scarce_capacity_units: u64,
301}
302
303#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
304#[serde(tag = "kind", rename_all = "snake_case", deny_unknown_fields)]
305enum CaseAssertion {
306 Equals {
307 path: EqualsPath,
308 value: ScalarValue,
309 },
310 Contains {
311 path: ContainsPath,
312 value: String,
313 },
314 OrderedEquals {
315 path: OrderedPath,
316 value: Vec<router_policy::FilterReason>,
317 },
318 FieldsAbsent {
319 path: RecordPath,
320 value: Vec<ForbiddenRecordField>,
321 },
322 RowSetEquals {
323 path: RowSetPath,
324 value: Vec<BaselineId>,
325 },
326 LessThan {
327 path: ScarcityPath,
328 comparison_path: BaselineScarcityPath,
329 },
330}
331
332#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
333enum EqualsPath {
334 #[serde(rename = "classification.resolved_role")]
335 ClassificationResolvedRole,
336 #[serde(rename = "selected_route.candidate_id")]
337 SelectedRouteCandidateId,
338 #[serde(rename = "classification.confidence")]
339 ClassificationConfidence,
340 #[serde(rename = "classification.minimum_capability.function_tools")]
341 ClassificationFunctionTools,
342 #[serde(rename = "classification.minimum_capability.min_quality")]
343 ClassificationMinQuality,
344 #[serde(rename = "classification.minimum_capability.images")]
345 ClassificationImages,
346 #[serde(rename = "classification.minimum_capability.min_context_tokens")]
347 ClassificationMinContextTokens,
348 #[serde(rename = "outcome")]
349 Outcome,
350 #[serde(rename = "policy_version")]
351 PolicyVersion,
352 #[serde(rename = "decision_record.mode")]
353 DecisionRecordMode,
354 #[serde(rename = "shadow.intelligent_dispatch_count")]
355 ShadowIntelligentDispatchCount,
356 #[serde(rename = "dispatch.candidate_id")]
357 DispatchCandidateId,
358 #[serde(rename = "evaluation.policy_version")]
359 EvaluationPolicyVersion,
360}
361
362#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
363enum ContainsPath {
364 #[serde(rename = "classification.reasons")]
365 ClassificationReasons,
366 #[serde(rename = "exclusions[].category")]
367 ExclusionCategories,
368}
369
370#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
371enum OrderedPath {
372 #[serde(rename = "exclusions[].primary_reason")]
373 ExclusionReasons,
374}
375
376#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
377enum RecordPath {
378 #[serde(rename = "decision_record")]
379 DecisionRecord,
380}
381
382#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
383enum RowSetPath {
384 #[serde(rename = "baseline_rows[].strategy_id")]
385 BaselineStrategyIds,
386}
387
388#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
389enum ScarcityPath {
390 #[serde(rename = "intelligent.scarce_capacity_units")]
391 Intelligent,
392}
393
394#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
395enum BaselineScarcityPath {
396 #[serde(rename = "always_frontier.scarce_capacity_units")]
397 AlwaysFrontier,
398}
399
400#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
401#[serde(untagged)]
402enum ScalarValue {
403 Boolean(bool),
404 Unsigned(u64),
405 Number(f64),
406 Text(String),
407}
408
409#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
410#[serde(deny_unknown_fields)]
411struct CaseInput {
412 alias: RequestedAlias,
413 confidence: f64,
414 signals: Vec<Signal>,
415 candidate_ids: Vec<String>,
416 #[serde(default)]
417 fallback_cause: Option<String>,
418 #[serde(default)]
419 previous_outcome: Option<PreviousOutcome>,
420 #[serde(default)]
421 runtime_observations: BTreeMap<String, RuntimeObservation>,
422 #[serde(default)]
423 mode: Option<DecisionMode>,
424 #[serde(default)]
425 pinned_baseline_candidate_id: Option<String>,
426 #[serde(default)]
427 gate_states: BTreeMap<String, GateStateFixture>,
428}
429
430#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
431#[serde(deny_unknown_fields)]
432struct RuntimeObservation {
433 latency_ms: u64,
434 estimated_cost_microunits: u64,
435}
436
437#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
438#[serde(deny_unknown_fields)]
439struct GateStateFixture {
440 policy_evaluation: GateStatus,
441 protocol_conformance: GateStatus,
442 provider_support: GateStatus,
443 security_review: GateStatus,
444 operator_approval: GateStatus,
445}
446
447#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
448#[serde(rename_all = "snake_case")]
449enum ExpectedOutcome {
450 Selected,
451 ShadowSelected,
452 NoEligibleRoute,
453}
454
455#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
456#[serde(deny_unknown_fields)]
457struct ExpectedRoute {
458 candidate_id: String,
459 provider_id: String,
460 model_id: String,
461 reasoning_effort: ReasoningEffort,
462}
463
464#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
465#[serde(deny_unknown_fields)]
466struct ExpectedNoEligibleRoute {
467 request_id: String,
468 selection_snapshot_id: String,
469 resolved_role: Role,
470 minimum_capability_profile: String,
471 policy_version: String,
472 retryable: bool,
473}
474
475#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
476#[serde(deny_unknown_fields)]
477struct ExpectedExclusion {
478 candidate_id: String,
479 category: router_policy::ExclusionCategory,
480 primary_reason: router_policy::FilterReason,
481 #[serde(default)]
482 diagnostic_reason: Option<String>,
483}
484
485#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
486#[serde(deny_unknown_fields)]
487struct CaseMetrics {
488 objective_correct: bool,
489 capability_preserved: bool,
490 routine_workload: bool,
491 frontier_avoided: bool,
492 scarce_capacity_units: u64,
493}
494
495#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
496#[serde(tag = "kind", rename_all = "snake_case", deny_unknown_fields)]
497enum AggregateAssertion {
498 MetricAtLeast {
499 metric: RatioMetric,
500 value: f64,
501 acceptance_criteria: Vec<AcceptanceCriterion>,
502 },
503 MetricEquals {
504 metric: RatioMetric,
505 value: f64,
506 acceptance_criteria: Vec<AcceptanceCriterion>,
507 },
508 MetricNonInferior {
509 metric: CorrectnessMetric,
510 baseline: BaselineId,
511 acceptance_criteria: Vec<AcceptanceCriterion>,
512 },
513 MetricStrictlyLess {
514 metric: ScarcityMetric,
515 baselines: Vec<BaselineId>,
516 acceptance_criteria: Vec<AcceptanceCriterion>,
517 },
518 BaselineRowsPresent {
519 baselines: Vec<BaselineId>,
520 policy_version: String,
521 acceptance_criteria: Vec<AcceptanceCriterion>,
522 },
523 AllAutomaticRolesFullyGated {
524 required_gates: Vec<RequiredGate>,
525 acceptance_criteria: Vec<AcceptanceCriterion>,
526 },
527}
528
529impl AggregateAssertion {
530 fn acceptance_criteria(&self) -> &[AcceptanceCriterion] {
531 match self {
532 Self::MetricAtLeast {
533 acceptance_criteria,
534 ..
535 }
536 | Self::MetricEquals {
537 acceptance_criteria,
538 ..
539 }
540 | Self::MetricNonInferior {
541 acceptance_criteria,
542 ..
543 }
544 | Self::MetricStrictlyLess {
545 acceptance_criteria,
546 ..
547 }
548 | Self::BaselineRowsPresent {
549 acceptance_criteria,
550 ..
551 }
552 | Self::AllAutomaticRolesFullyGated {
553 acceptance_criteria,
554 ..
555 } => acceptance_criteria,
556 }
557 }
558}
559
560#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
561#[serde(rename_all = "snake_case")]
562enum RatioMetric {
563 ExpectedRouteAccuracy,
564 CapabilityPreservation,
565 RoutineFrontierAvoidance,
566}
567
568#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
569#[serde(rename_all = "snake_case")]
570enum CorrectnessMetric {
571 IntelligentCorrectness,
572}
573
574#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
575#[serde(rename_all = "snake_case")]
576enum ScarcityMetric {
577 RoutineStandardScarceCapacity,
578}
579
580#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
581#[serde(rename_all = "snake_case")]
582enum RequiredGate {
583 PolicyEvaluation,
584 ProtocolConformance,
585 ProviderSupport,
586 SecurityReview,
587 OperatorApproval,
588}
589
590#[derive(Clone, Debug, PartialEq, Eq)]
591struct StrategyOutcome {
592 candidate_id: Option<String>,
593 objective_correct: bool,
594 scarce_capacity_units: u64,
595}
596
597#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
598#[serde(deny_unknown_fields)]
599pub struct EvaluationReport {
600 pub schema_version: String,
601 pub corpus_id: String,
602 pub corpus_version: String,
603 pub corpus_digest_sha256: String,
604 pub policy_version: String,
605 pub execution_profile: String,
606 pub case_count: u64,
607 pub passed_cases: u64,
608 pub cases: Vec<CaseReportRow>,
609 pub expected_route_accuracy: f64,
610 pub capability_preservation: f64,
611 pub routine_frontier_avoidance: f64,
612 pub intelligent_correctness: f64,
613 pub always_frontier_correctness: f64,
614 pub routine_standard_scarce_capacity: u64,
615 pub baseline_rows: Vec<BaselineReportRow>,
616 pub thresholds_passed: bool,
617}
618
619#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
620#[serde(deny_unknown_fields)]
621pub struct CaseReportRow {
622 pub case_id: String,
623 pub passed: bool,
624 pub resolved_role: Role,
625 pub selected_candidate_id: Option<String>,
626 pub exclusion_reasons: Vec<router_policy::FilterReason>,
627}
628
629#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
630#[serde(deny_unknown_fields)]
631pub struct BaselineReportRow {
632 pub strategy_id: String,
633 pub correctness: f64,
634 pub routine_standard_scarce_capacity: u64,
635}
636
637pub fn evaluate_corpus(bytes: &[u8]) -> Result<EvaluationReport, EvaluationError> {
638 if bytes.len() > MAX_EVALUATION_CORPUS_BYTES {
639 return Err(EvaluationError::InvalidCorpus);
640 }
641 let corpus: EvaluationCorpus =
642 serde_json::from_slice(bytes).map_err(|_| EvaluationError::InvalidCorpus)?;
643 validate_corpus(&corpus)?;
644
645 let digest = sha256_hex(bytes);
646 let mut passed_cases = 0_u64;
647 let mut preservation_total = 0_u64;
648 let mut preservation_passed = 0_u64;
649 let mut routine_total = 0_u64;
650 let mut routine_avoided = 0_u64;
651 let mut intelligent_correct = 0_u64;
652 let mut correctness_total = 0_u64;
653 let mut intelligent_scarce = 0_u64;
654 let mut baseline_scarce = BTreeMap::<BaselineId, u64>::new();
655 let mut baseline_correct = BTreeMap::<BaselineId, u64>::new();
656 let mut case_rows = Vec::with_capacity(corpus.cases.len());
657 let mut exercised_acceptance = BTreeSet::new();
658
659 for baseline in &corpus.baselines {
660 baseline_scarce.insert(baseline.id(), 0);
661 baseline_correct.insert(baseline.id(), 0);
662 }
663
664 for case in &corpus.cases {
665 let classification = classify(&ClassificationInput {
666 requested_alias: case.input.alias,
667 confidence: case.input.confidence,
668 confidence_threshold: 0.80,
669 signals: case.input.signals.clone(),
670 previous_outcome: case.input.previous_outcome,
671 min_context_tokens: corpus
672 .capability_profiles
673 .get(&case.expected_capability_profile)
674 .map_or(0, |profile| profile.min_context_tokens),
675 })
676 .map_err(|_| EvaluationError::InvalidFixture)?;
677
678 let snapshot_id = case.expected_no_eligible_route.as_ref().map_or_else(
679 || format!("snapshot:{}", case.case_id),
680 |value| value.selection_snapshot_id.clone(),
681 );
682 let request_id = case.expected_no_eligible_route.as_ref().map_or_else(
683 || format!("request:{}", case.case_id),
684 |value| value.request_id.clone(),
685 );
686 let candidates = build_candidates(&corpus, case, &snapshot_id)?;
687 let frontier_by_id = candidates
688 .iter()
689 .map(|candidate| {
690 let id = candidate.snapshot().candidate_id.clone();
691 let fixture = corpus.routes.get(&id);
692 (id, fixture.is_some_and(|route| route.frontier))
693 })
694 .collect::<BTreeMap<_, _>>();
695 let scarcity_by_id = candidates
696 .iter()
697 .map(|candidate| {
698 (
699 candidate.snapshot().candidate_id.clone(),
700 candidate.snapshot().scarcity_score as u64,
701 )
702 })
703 .collect::<BTreeMap<_, _>>();
704 let snapshot = Arc::new(
705 SelectionSnapshot::new(snapshot_id, candidates)
706 .map_err(|_| EvaluationError::InvalidFixture)?,
707 );
708 let mode = case.input.mode.unwrap_or(DecisionMode::Live);
709 let selection_classification = if case.case_id == "ordered-hard-filters-empty" {
710 comprehensive_filter_classification(&classification)
711 } else {
712 classification.clone()
713 };
714 let result = select(
715 &selection_classification,
716 Arc::clone(&snapshot),
717 &SelectionOptions {
718 request_id,
719 policy_version: corpus.policy_version.clone(),
720 mode,
721 pin_mode: if mode == DecisionMode::Shadow {
722 PinMode::ShadowBaseline
723 } else {
724 PinMode::None
725 },
726 pinned_candidate_id: case.input.pinned_baseline_candidate_id.clone(),
727 retryable_when_empty: false,
728 subagent_bearing: false,
729 },
730 );
731
732 let core_passed = match (&case.expected_outcome, &result) {
733 (ExpectedOutcome::Selected, Ok(decision)) => case
734 .expected_route
735 .as_ref()
736 .is_some_and(|expected| route_matches(&decision.intelligent_route, expected)),
737 (ExpectedOutcome::ShadowSelected, Ok(decision)) => {
738 decision.mode == DecisionMode::Shadow
739 && decision.intelligent_dispatch_count == 0
740 && case.expected_route.as_ref().is_some_and(|expected| {
741 route_matches(&decision.intelligent_route, expected)
742 })
743 && case
744 .expected_dispatch_route
745 .as_ref()
746 .is_some_and(|expected| route_matches(&decision.dispatch_route, expected))
747 }
748 (ExpectedOutcome::NoEligibleRoute, Err(error)) => case
749 .expected_no_eligible_route
750 .as_ref()
751 .is_some_and(|expected| {
752 no_eligible_route_matches(error, expected, &corpus.capability_profiles)
753 && exclusions_match(&error.exclusions, &case.expected_exclusions)
754 }),
755 _ => false,
756 } && classification.resolved_role == case.expected_role
757 && classification.reasons == case.expected_classifier_reasons
758 && classification.minimum_capability
759 == *corpus
760 .capability_profiles
761 .get(&case.expected_capability_profile)
762 .ok_or(EvaluationError::InvalidFixture)?;
763
764 let decision_record = result
765 .as_ref()
766 .ok()
767 .map(|decision| {
768 build_decision_record(
769 decision,
770 DecisionRecordContext {
771 decision_id: format!("decision:{}", case.case_id),
772 request_id: format!("request:{}", case.case_id),
773 timestamp: "2026-07-27T00:00:00Z".to_owned(),
774 policy_version: corpus.policy_version.clone(),
775 state_refs: DecisionStateRefs {
776 health: "offline-fixture:health".to_owned(),
777 quota: "offline-fixture:quota".to_owned(),
778 budget: "offline-fixture:budget".to_owned(),
779 },
780 attempt: 1,
781 fallback_cause: case.input.fallback_cause.clone(),
782 replay_safe: true,
783 latency_ms: 0,
784 },
785 )
786 .map_err(|_| EvaluationError::InvalidFixture)
787 })
788 .transpose()?;
789
790 let mut strategy_outcomes = BTreeMap::new();
791 let intelligent_candidate_id = result
792 .as_ref()
793 .ok()
794 .map(|decision| decision.intelligent_route.candidate_id.clone());
795 strategy_outcomes.insert(
796 StrategyId::Intelligent,
797 strategy_outcome(
798 intelligent_candidate_id,
799 &snapshot,
800 &selection_classification,
801 &corpus.policy_version,
802 case.expected_outcome,
803 &scarcity_by_id,
804 ),
805 );
806 for baseline in &corpus.baselines {
807 let candidate_id = execute_baseline(
808 baseline,
809 &snapshot,
810 &selection_classification,
811 &corpus.policy_version,
812 );
813 strategy_outcomes.insert(
814 StrategyId::from(baseline.id()),
815 strategy_outcome(
816 candidate_id,
817 &snapshot,
818 &selection_classification,
819 &corpus.policy_version,
820 case.expected_outcome,
821 &scarcity_by_id,
822 ),
823 );
824 }
825
826 let assertions_passed = case.assertions.iter().all(|assertion| {
827 execute_case_assertion(
828 assertion,
829 &classification,
830 &result,
831 decision_record.as_ref(),
832 &strategy_outcomes,
833 &corpus,
834 )
835 });
836 let record_passed = case
837 .record_expectations
838 .as_ref()
839 .is_none_or(|expectations| {
840 decision_record
841 .as_ref()
842 .is_some_and(|record| record_expectations_pass(record, expectations))
843 });
844 let strategies_passed = case.strategy_expectations.iter().all(|expectation| {
845 strategy_outcomes
846 .get(&expectation.strategy_id)
847 .is_some_and(|actual| strategy_expectation_matches(actual, expectation))
848 });
849 let intelligent_outcome = strategy_outcomes
850 .get(&StrategyId::Intelligent)
851 .ok_or(EvaluationError::InvalidFixture)?;
852 let actual_frontier_avoided = case.metrics.routine_workload
853 && intelligent_outcome
854 .candidate_id
855 .as_ref()
856 .is_some_and(|id| !frontier_by_id.get(id).copied().unwrap_or(false));
857 let metrics_passed = case.metrics.objective_correct
858 == intelligent_outcome.objective_correct
859 && case.metrics.capability_preserved == intelligent_outcome.objective_correct
860 && case.metrics.frontier_avoided == actual_frontier_avoided
861 && case.metrics.scarce_capacity_units == intelligent_outcome.scarce_capacity_units;
862 let case_passed = core_passed
863 && assertions_passed
864 && record_passed
865 && strategies_passed
866 && metrics_passed;
867
868 if case_passed {
869 passed_cases += 1;
870 exercised_acceptance.extend(case.acceptance_criteria.iter().copied());
871 }
872 case_rows.push(CaseReportRow {
873 case_id: case.case_id.clone(),
874 passed: case_passed,
875 resolved_role: classification.resolved_role,
876 selected_candidate_id: result
877 .as_ref()
878 .ok()
879 .map(|decision| decision.intelligent_route.candidate_id.clone()),
880 exclusion_reasons: match &result {
881 Ok(decision) => decision
882 .exclusions
883 .iter()
884 .map(|exclusion| exclusion.primary_reason)
885 .collect(),
886 Err(error) => error
887 .exclusions
888 .iter()
889 .map(|exclusion| exclusion.primary_reason)
890 .collect(),
891 },
892 });
893 if case.metrics.objective_correct {
894 correctness_total += 1;
895 if intelligent_outcome.objective_correct {
896 intelligent_correct += 1;
897 }
898 }
899 if case.metrics.capability_preserved && result.is_ok() {
900 preservation_total += 1;
901 if intelligent_outcome.objective_correct {
902 preservation_passed += 1;
903 }
904 }
905 if case.metrics.routine_workload {
906 routine_total += 1;
907 if let Ok(decision) = &result
908 && !frontier_by_id
909 .get(&decision.intelligent_route.candidate_id)
910 .copied()
911 .unwrap_or(false)
912 {
913 routine_avoided += 1;
914 }
915 }
916 if matches!(classification.resolved_role, Role::Routine | Role::Standard)
917 && let Ok(decision) = &result
918 {
919 intelligent_scarce += scarcity_by_id
920 .get(&decision.intelligent_route.candidate_id)
921 .copied()
922 .unwrap_or_default();
923 }
924
925 for baseline in &corpus.baselines {
926 let outcome = strategy_outcomes
927 .get(&StrategyId::from(baseline.id()))
928 .ok_or(EvaluationError::InvalidFixture)?;
929 if case.metrics.objective_correct && outcome.objective_correct {
930 *baseline_correct
931 .get_mut(&baseline.id())
932 .ok_or(EvaluationError::InvalidFixture)? += 1;
933 }
934 if matches!(classification.resolved_role, Role::Routine | Role::Standard) {
935 *baseline_scarce
936 .get_mut(&baseline.id())
937 .ok_or(EvaluationError::InvalidFixture)? += outcome.scarce_capacity_units;
938 }
939 }
940 }
941
942 let case_count = corpus.cases.len() as u64;
943 let expected_route_accuracy = ratio(passed_cases, case_count);
944 let capability_preservation = ratio(preservation_passed, preservation_total);
945 let routine_frontier_avoidance = ratio(routine_avoided, routine_total);
946 let intelligent_correctness = ratio(intelligent_correct, correctness_total);
947 let always_frontier_correctness = ratio(
948 *baseline_correct
949 .get(&BaselineId::AlwaysFrontier)
950 .unwrap_or(&0),
951 correctness_total,
952 );
953 let baseline_rows = corpus
954 .baselines
955 .iter()
956 .map(|baseline| BaselineReportRow {
957 strategy_id: baseline.id().as_str().to_owned(),
958 correctness: ratio(
959 *baseline_correct.get(&baseline.id()).unwrap_or(&0),
960 correctness_total,
961 ),
962 routine_standard_scarce_capacity: *baseline_scarce.get(&baseline.id()).unwrap_or(&0),
963 })
964 .collect::<Vec<_>>();
965 let mut aggregate_passed = true;
966 for assertion in &corpus.aggregate_assertions {
967 let passed = execute_aggregate_assertion(
968 assertion,
969 expected_route_accuracy,
970 capability_preservation,
971 routine_frontier_avoidance,
972 intelligent_correctness,
973 intelligent_scarce,
974 &baseline_rows,
975 &corpus.policy_version,
976 );
977 aggregate_passed &= passed;
978 if passed {
979 exercised_acceptance.extend(assertion.acceptance_criteria().iter().copied());
980 }
981 }
982 let acceptance_complete = REQUIRED_ACCEPTANCE
983 .iter()
984 .all(|criterion| exercised_acceptance.contains(criterion));
985 let thresholds_passed = aggregate_passed
986 && acceptance_complete
987 && passed_cases == case_count
988 && expected_route_accuracy >= corpus.metric_thresholds.expected_route_accuracy_min
989 && capability_preservation >= corpus.metric_thresholds.capability_preservation_required
990 && routine_frontier_avoidance >= corpus.metric_thresholds.routine_frontier_avoidance_min
991 && intelligent_correctness >= always_frontier_correctness
992 && baseline_rows
993 .iter()
994 .all(|row| intelligent_scarce < row.routine_standard_scarce_capacity);
995
996 let report = EvaluationReport {
997 schema_version: "1.0.0".to_owned(),
998 corpus_id: corpus.corpus_id,
999 corpus_version: corpus.corpus_version,
1000 corpus_digest_sha256: digest,
1001 policy_version: corpus.policy_version,
1002 execution_profile: corpus.execution_profile,
1003 case_count,
1004 passed_cases,
1005 cases: case_rows,
1006 expected_route_accuracy,
1007 capability_preservation,
1008 routine_frontier_avoidance,
1009 intelligent_correctness,
1010 always_frontier_correctness,
1011 routine_standard_scarce_capacity: intelligent_scarce,
1012 baseline_rows,
1013 thresholds_passed,
1014 };
1015 if report.thresholds_passed {
1016 Ok(report)
1017 } else {
1018 Err(EvaluationError::ThresholdFailure)
1019 }
1020}
1021
1022fn validate_corpus(corpus: &EvaluationCorpus) -> Result<(), EvaluationError> {
1023 let route_ids = corpus.routes.keys().cloned().collect::<BTreeSet<_>>();
1024 let ordered_route_ids = corpus.baselines.iter().find_map(|baseline| match baseline {
1025 BaselineFixture::OrderedCandidatesWithoutTaskRoleSelection {
1026 ordered_candidate_ids,
1027 ..
1028 } => Some(
1029 ordered_candidate_ids
1030 .iter()
1031 .cloned()
1032 .collect::<BTreeSet<_>>(),
1033 ),
1034 _ => None,
1035 });
1036 let providers = corpus
1037 .routes
1038 .values()
1039 .map(|route| route.provider_id.clone())
1040 .collect::<BTreeSet<_>>();
1041 let routes_valid = !corpus.routes.is_empty()
1042 && corpus.routes.iter().all(|(id, route)| {
1043 safe_reference(id)
1044 && safe_reference(&route.provider_id)
1045 && safe_reference(&route.model_id)
1046 && safe_reference(&route.account_pseudonym)
1047 && safe_reference(&route.endpoint_pseudonym)
1048 && route.scarce_capacity_units <= MAX_SCARCE_CAPACITY_UNITS
1049 && route.frontier == (route.scarce_capacity_units > 0)
1050 })
1051 && ordered_route_ids.as_ref() == Some(&route_ids);
1052 let baseline_ids = corpus
1053 .baselines
1054 .iter()
1055 .map(|baseline| baseline.id().as_str())
1056 .collect::<Vec<_>>();
1057 let baseline_shapes_valid = matches!(
1058 corpus.baselines.as_slice(),
1059 [
1060 BaselineFixture::StrongestHardEligibleInFirstEligiblePhase {
1061 baseline_id: BaselineId::AlwaysFrontier
1062 },
1063 BaselineFixture::FixedProviderModelThenDescendWhenHardIneligible {
1064 baseline_id: BaselineId::FixedModelPerProvider,
1065 ..
1066 },
1067 BaselineFixture::OrderedCandidatesWithoutTaskRoleSelection {
1068 baseline_id: BaselineId::OrderedProviderFallbackOnly,
1069 ..
1070 }
1071 ]
1072 );
1073 let baseline_configs_valid = corpus.baselines.iter().all(|baseline| match baseline {
1074 BaselineFixture::StrongestHardEligibleInFirstEligiblePhase { .. } => true,
1075 BaselineFixture::FixedProviderModelThenDescendWhenHardIneligible {
1076 fixed_model_ids,
1077 ..
1078 } => {
1079 fixed_model_ids.keys().cloned().collect::<BTreeSet<_>>() == providers
1080 && fixed_model_ids.iter().all(|(provider, model)| {
1081 safe_reference(provider)
1082 && safe_reference(model)
1083 && corpus.routes.values().any(|route| {
1084 route.provider_id == *provider
1085 && route.model_id == *model
1086 && route.frontier
1087 })
1088 })
1089 }
1090 BaselineFixture::OrderedCandidatesWithoutTaskRoleSelection {
1091 ordered_candidate_ids,
1092 ..
1093 } => {
1094 !ordered_candidate_ids.is_empty()
1095 && values_are_unique(ordered_candidate_ids)
1096 && ordered_candidate_ids.iter().all(|id| safe_reference(id))
1097 }
1098 });
1099 let mut case_ids = BTreeSet::new();
1100 let cases_valid = corpus.cases.iter().all(|case| {
1101 case_ids.insert(case.case_id.as_str())
1102 && safe_reference(&case.case_id)
1103 && !case.acceptance_criteria.is_empty()
1104 && values_are_unique(&case.acceptance_criteria)
1105 && !case.assertions.is_empty()
1106 && !case.input.candidate_ids.is_empty()
1107 && values_are_unique(&case.input.candidate_ids)
1108 && case.input.candidate_ids.iter().all(|id| safe_reference(id))
1109 && case.input.candidate_ids.iter().all(|id| {
1110 corpus.routes.contains_key(id) || SYNTHETIC_CANDIDATE_IDS.contains(&id.as_str())
1111 })
1112 && case
1113 .input
1114 .runtime_observations
1115 .keys()
1116 .all(|id| case.input.candidate_ids.contains(id))
1117 && case
1118 .input
1119 .gate_states
1120 .keys()
1121 .all(|id| case.input.candidate_ids.contains(id))
1122 && case
1123 .input
1124 .pinned_baseline_candidate_id
1125 .as_ref()
1126 .is_none_or(|id| case.input.candidate_ids.contains(id))
1127 && case
1128 .input
1129 .fallback_cause
1130 .as_ref()
1131 .is_none_or(|value| safe_reference(value))
1132 && corpus
1133 .capability_profiles
1134 .contains_key(&case.expected_capability_profile)
1135 && case
1136 .expected_no_eligible_route
1137 .as_ref()
1138 .is_none_or(|expected| {
1139 corpus
1140 .capability_profiles
1141 .contains_key(&expected.minimum_capability_profile)
1142 })
1143 && expected_outcome_shape_valid(case)
1144 && case.expected_route.as_ref().is_none_or(|route| {
1145 safe_reference(&route.candidate_id)
1146 && case.input.candidate_ids.contains(&route.candidate_id)
1147 && safe_reference(&route.provider_id)
1148 && safe_reference(&route.model_id)
1149 })
1150 && case.expected_dispatch_route.as_ref().is_none_or(|route| {
1151 safe_reference(&route.candidate_id)
1152 && case.input.candidate_ids.contains(&route.candidate_id)
1153 && safe_reference(&route.provider_id)
1154 && safe_reference(&route.model_id)
1155 })
1156 && case.expected_exclusions.iter().all(|exclusion| {
1157 safe_reference(&exclusion.candidate_id)
1158 && case.input.candidate_ids.contains(&exclusion.candidate_id)
1159 && exclusion
1160 .diagnostic_reason
1161 .as_ref()
1162 .is_none_or(|value| safe_reference(value))
1163 })
1164 && case.strategy_expectations.iter().all(|expectation| {
1165 safe_reference(&expectation.candidate_id)
1166 && case.input.candidate_ids.contains(&expectation.candidate_id)
1167 })
1168 && values_are_unique(
1169 &case
1170 .strategy_expectations
1171 .iter()
1172 .map(|expectation| expectation.strategy_id)
1173 .collect::<Vec<_>>(),
1174 )
1175 && (case.strategy_expectations.is_empty()
1176 || case
1177 .strategy_expectations
1178 .iter()
1179 .map(|expectation| expectation.strategy_id)
1180 .eq([
1181 StrategyId::Intelligent,
1182 StrategyId::AlwaysFrontier,
1183 StrategyId::FixedModelPerProvider,
1184 StrategyId::OrderedProviderFallbackOnly,
1185 ]))
1186 && case
1187 .record_expectations
1188 .as_ref()
1189 .is_none_or(|expectations| {
1190 expectations.append_only
1191 && expectations.required_fields
1192 == [
1193 RequiredRecordField::PolicyVersion,
1194 RequiredRecordField::SelectionSnapshotId,
1195 RequiredRecordField::StateRefs,
1196 RequiredRecordField::SelectedRoute,
1197 RequiredRecordField::SelectedRouteTrace,
1198 RequiredRecordField::Exclusions,
1199 ]
1200 && expectations.forbidden_fields
1201 == [
1202 ForbiddenRecordField::Prompt,
1203 ForbiddenRecordField::SourceCode,
1204 ForbiddenRecordField::ToolArguments,
1205 ForbiddenRecordField::ToolResults,
1206 ForbiddenRecordField::RawAccountId,
1207 ForbiddenRecordField::Credential,
1208 ForbiddenRecordField::Token,
1209 ForbiddenRecordField::Authorization,
1210 ForbiddenRecordField::Cookie,
1211 ForbiddenRecordField::UpstreamHeaders,
1212 ForbiddenRecordField::UpstreamBody,
1213 ForbiddenRecordField::EndpointUrl,
1214 ]
1215 })
1216 && case
1217 .acceptance_criteria
1218 .iter()
1219 .all(|criterion| case_supports_acceptance(case, *criterion))
1220 });
1221 let declared_acceptance = corpus
1222 .cases
1223 .iter()
1224 .flat_map(|case| case.acceptance_criteria.iter().copied())
1225 .chain(
1226 corpus
1227 .aggregate_assertions
1228 .iter()
1229 .flat_map(|assertion| assertion.acceptance_criteria().iter().copied()),
1230 )
1231 .collect::<BTreeSet<_>>();
1232 let aggregate_valid = aggregate_assertions_are_frozen(&corpus.aggregate_assertions);
1233 if corpus.schema_version != "1.0.0"
1234 || !safe_reference(&corpus.corpus_id)
1235 || !valid_version(&corpus.corpus_version)
1236 || !valid_version(&corpus.policy_version)
1237 || corpus.execution_profile != "offline_fixture"
1238 || corpus.cases.len() != REQUIRED_EVALUATION_CASES
1239 || corpus.cases.len() > MAX_EVALUATION_CASES
1240 || !routes_valid
1241 || baseline_ids != REQUIRED_BASELINES
1242 || !baseline_shapes_valid
1243 || !baseline_configs_valid
1244 || !cases_valid
1245 || !aggregate_valid
1246 || declared_acceptance != REQUIRED_ACCEPTANCE.into_iter().collect()
1247 || corpus.metric_thresholds.required_baseline_rows != REQUIRED_BASELINES.map(str::to_owned)
1248 || corpus
1249 .metric_thresholds
1250 .routine_standard_scarce_capacity_strictly_less_than
1251 != REQUIRED_BASELINES.map(str::to_owned)
1252 || corpus
1253 .metric_thresholds
1254 .intelligent_correctness_non_inferior_to
1255 != "always_frontier"
1256 || corpus.metric_thresholds.expected_route_accuracy_min != 0.95
1257 || corpus.metric_thresholds.capability_preservation_required != 1.0
1258 || corpus.metric_thresholds.routine_frontier_avoidance_min != 0.80
1259 || !corpus
1260 .cases
1261 .iter()
1262 .all(|case| case.metrics.capability_preserved && case.metrics.objective_correct)
1263 || corpus
1264 .cases
1265 .iter()
1266 .filter(|case| case.metrics.routine_workload)
1267 .count()
1268 < MIN_ROUTINE_CASES
1269 || !corpus.cases.iter().any(|case| {
1270 case.strategy_expectations.len() == 4
1271 && case
1272 .acceptance_criteria
1273 .contains(&AcceptanceCriterion::M2Ac10)
1274 })
1275 {
1276 return Err(EvaluationError::InvalidCorpus);
1277 }
1278 Ok(())
1279}
1280
1281fn expected_outcome_shape_valid(case: &EvaluationCase) -> bool {
1282 match case.expected_outcome {
1283 ExpectedOutcome::Selected => {
1284 case.expected_route.is_some()
1285 && case.expected_dispatch_route.is_none()
1286 && case.expected_no_eligible_route.is_none()
1287 && case
1288 .input
1289 .mode
1290 .is_none_or(|mode| mode == DecisionMode::Live)
1291 }
1292 ExpectedOutcome::ShadowSelected => {
1293 case.expected_route.is_some()
1294 && case.expected_dispatch_route.is_some()
1295 && case.expected_no_eligible_route.is_none()
1296 && case.input.mode == Some(DecisionMode::Shadow)
1297 && case.input.pinned_baseline_candidate_id.is_some()
1298 }
1299 ExpectedOutcome::NoEligibleRoute => {
1300 case.expected_route.is_none()
1301 && case.expected_dispatch_route.is_none()
1302 && case
1303 .expected_no_eligible_route
1304 .as_ref()
1305 .is_some_and(|expected| {
1306 safe_reference(&expected.request_id)
1307 && safe_reference(&expected.selection_snapshot_id)
1308 && valid_version(&expected.policy_version)
1309 && corpus_profile_name_is_safe(&expected.minimum_capability_profile)
1310 })
1311 }
1312 }
1313}
1314
1315fn corpus_profile_name_is_safe(value: &str) -> bool {
1316 safe_reference(value)
1317}
1318
1319fn case_supports_acceptance(case: &EvaluationCase, criterion: AcceptanceCriterion) -> bool {
1320 match criterion {
1321 AcceptanceCriterion::M2Ac01 => case.expected_route.is_some(),
1322 AcceptanceCriterion::M2Ac02 => case
1323 .expected_classifier_reasons
1324 .contains(&router_policy::ClassifierReason::LowConfidenceUpwardGuard),
1325 AcceptanceCriterion::M2Ac03 => {
1326 case.expected_outcome == ExpectedOutcome::NoEligibleRoute
1327 && !case.expected_exclusions.is_empty()
1328 }
1329 AcceptanceCriterion::M2Ac04 => {
1330 case.input.fallback_cause.is_some() || case.input.previous_outcome.is_some()
1331 }
1332 AcceptanceCriterion::M2Ac05 => !case.input.runtime_observations.is_empty(),
1333 AcceptanceCriterion::M2Ac06 => case.record_expectations.is_some(),
1334 AcceptanceCriterion::M2Ac07 => case.expected_outcome == ExpectedOutcome::ShadowSelected,
1335 AcceptanceCriterion::M2Ac08 => case.metrics.capability_preserved,
1336 AcceptanceCriterion::M2Ac09 => case.metrics.routine_workload,
1337 AcceptanceCriterion::M2Ac10 => case.strategy_expectations.len() == 4,
1338 AcceptanceCriterion::M2Ac11 => !case.input.gate_states.is_empty(),
1339 }
1340}
1341
1342fn aggregate_assertions_are_frozen(assertions: &[AggregateAssertion]) -> bool {
1343 matches!(
1344 assertions,
1345 [
1346 AggregateAssertion::MetricAtLeast {
1347 metric: RatioMetric::ExpectedRouteAccuracy,
1348 value,
1349 acceptance_criteria,
1350 },
1351 AggregateAssertion::MetricEquals {
1352 metric: RatioMetric::CapabilityPreservation,
1353 value: capability_value,
1354 acceptance_criteria: capability_acceptance,
1355 },
1356 AggregateAssertion::MetricAtLeast {
1357 metric: RatioMetric::RoutineFrontierAvoidance,
1358 value: routine_value,
1359 acceptance_criteria: routine_acceptance,
1360 },
1361 AggregateAssertion::MetricNonInferior {
1362 metric: CorrectnessMetric::IntelligentCorrectness,
1363 baseline: BaselineId::AlwaysFrontier,
1364 acceptance_criteria: non_inferior_acceptance,
1365 },
1366 AggregateAssertion::MetricStrictlyLess {
1367 metric: ScarcityMetric::RoutineStandardScarceCapacity,
1368 baselines,
1369 acceptance_criteria: scarce_acceptance,
1370 },
1371 AggregateAssertion::BaselineRowsPresent {
1372 baselines: row_baselines,
1373 policy_version,
1374 acceptance_criteria: row_acceptance,
1375 },
1376 AggregateAssertion::AllAutomaticRolesFullyGated {
1377 required_gates,
1378 acceptance_criteria: gate_acceptance,
1379 },
1380 ] if *value == 0.95
1381 && *capability_value == 1.0
1382 && *routine_value == 0.80
1383 && *acceptance_criteria == [AcceptanceCriterion::M2Ac08]
1384 && *capability_acceptance == [AcceptanceCriterion::M2Ac08]
1385 && *routine_acceptance == [AcceptanceCriterion::M2Ac09]
1386 && *non_inferior_acceptance == [AcceptanceCriterion::M2Ac10]
1387 && *scarce_acceptance == [AcceptanceCriterion::M2Ac10]
1388 && *row_acceptance == [AcceptanceCriterion::M2Ac10]
1389 && *gate_acceptance == [AcceptanceCriterion::M2Ac11]
1390 && baselines == &REQUIRED_BASELINES.map(|value| match value {
1391 "always_frontier" => BaselineId::AlwaysFrontier,
1392 "fixed_model_per_provider" => BaselineId::FixedModelPerProvider,
1393 _ => BaselineId::OrderedProviderFallbackOnly,
1394 })
1395 && row_baselines == baselines
1396 && policy_version == "1.0.0"
1397 && *required_gates == [
1398 RequiredGate::PolicyEvaluation,
1399 RequiredGate::ProtocolConformance,
1400 RequiredGate::ProviderSupport,
1401 RequiredGate::SecurityReview,
1402 RequiredGate::OperatorApproval,
1403 ]
1404 )
1405}
1406
1407fn values_are_unique<T: Ord + Clone>(values: &[T]) -> bool {
1408 values.iter().cloned().collect::<BTreeSet<_>>().len() == values.len()
1409}
1410
1411fn build_candidates(
1412 corpus: &EvaluationCorpus,
1413 case: &EvaluationCase,
1414 snapshot_id: &str,
1415) -> Result<Vec<Candidate>, EvaluationError> {
1416 case.input
1417 .candidate_ids
1418 .iter()
1419 .map(|id| {
1420 let mut snapshot = candidate_snapshot(corpus.routes.get(id), id, snapshot_id)?;
1421 if let Some(observation) = case.input.runtime_observations.get(id) {
1422 snapshot.estimated_latency_ms = observation.latency_ms;
1423 snapshot.estimated_cost_microunits = observation.estimated_cost_microunits;
1424 }
1425 apply_case_overrides(&mut snapshot, id, &case.case_id);
1426 if let Some(gates) = case.input.gate_states.get(id) {
1427 candidate_from_gates(snapshot, &corpus.policy_version, gates)
1428 } else if id == "reject-provider-support" {
1429 let fixture = GateStateFixture {
1430 policy_evaluation: GateStatus::Passed,
1431 protocol_conformance: GateStatus::Passed,
1432 provider_support: GateStatus::Blocked,
1433 security_review: GateStatus::Passed,
1434 operator_approval: GateStatus::Passed,
1435 };
1436 candidate_from_gates(snapshot, &corpus.policy_version, &fixture)
1437 } else if id == "reject-policy-activation" {
1438 let fixture = GateStateFixture {
1439 policy_evaluation: GateStatus::Passed,
1440 protocol_conformance: GateStatus::Passed,
1441 provider_support: GateStatus::Passed,
1442 security_review: GateStatus::Passed,
1443 operator_approval: GateStatus::Blocked,
1444 };
1445 candidate_from_gates(snapshot, &corpus.policy_version, &fixture)
1446 } else {
1447 Ok(Candidate::offline_fixture(snapshot))
1448 }
1449 })
1450 .collect()
1451}
1452
1453fn candidate_snapshot(
1454 fixture: Option<&RouteFixture>,
1455 id: &str,
1456 snapshot_id: &str,
1457) -> Result<CandidateSnapshot, EvaluationError> {
1458 let default = RouteFixture {
1459 provider_id: "provider-a".to_owned(),
1460 model_id: format!("model:{id}"),
1461 account_pseudonym: "acct-a".to_owned(),
1462 endpoint_pseudonym: "endpoint-a".to_owned(),
1463 reasoning_effort: ReasoningEffort::Medium,
1464 frontier: false,
1465 scarce_capacity_units: 0,
1466 };
1467 let route = match fixture {
1468 Some(route) => route,
1469 None if SYNTHETIC_CANDIDATE_IDS.contains(&id) => &default,
1470 None => return Err(EvaluationError::InvalidFixture),
1471 };
1472 let roles = if id.contains("routine") {
1473 vec![Role::Routine]
1474 } else if id.contains("vision") {
1475 vec![Role::Vision]
1476 } else if id.contains("long-context") {
1477 vec![Role::LongContext]
1478 } else if id.contains("frontier") || id.contains("complex") {
1479 vec![
1480 Role::Routine,
1481 Role::Standard,
1482 Role::Complex,
1483 Role::Vision,
1484 Role::LongContext,
1485 ]
1486 } else {
1487 vec![Role::Standard]
1488 };
1489 Ok(CandidateSnapshot {
1490 candidate_id: id.to_owned(),
1491 selection_snapshot_id: snapshot_id.to_owned(),
1492 phase: if id.starts_with("fallback-") {
1493 "fallback".to_owned()
1494 } else {
1495 "primary".to_owned()
1496 },
1497 phase_priority: u32::from(id.starts_with("fallback-")),
1498 subagent_capable: true,
1499 provider_id: route.provider_id.clone(),
1500 account_pseudonym: route.account_pseudonym.clone(),
1501 model_id: route.model_id.clone(),
1502 reasoning_effort: route.reasoning_effort,
1503 endpoint_pseudonym: route.endpoint_pseudonym.clone(),
1504 billing_class: "offline_fixture".to_owned(),
1505 roles,
1506 capabilities: CapabilityDeclaration {
1507 wire_protocol: CapabilityState::Verified,
1508 streaming: CapabilityState::Verified,
1509 function_tools: CapabilityState::Verified,
1510 tool_result_continuation: CapabilityState::Verified,
1511 images: if id.contains("vision") || id.contains("frontier") {
1512 CapabilityState::Verified
1513 } else {
1514 CapabilityState::Unsupported
1515 },
1516 structured_output: CapabilityState::Verified,
1517 public_reasoning: CapabilityState::Verified,
1518 audio: CapabilityState::Unsupported,
1519 max_context_tokens: if id.contains("long-context") || id.contains("frontier") {
1520 200_000
1521 } else {
1522 32_768
1523 },
1524 privacy_modes: vec![PrivacyMode::ConfiguredProviderAllowed],
1525 },
1526 provider_status: ProviderStatus::Healthy,
1527 model_status: ModelStatus::Available,
1528 account_status: AccountStatus::Available,
1529 quota_headroom: Some(1.0),
1530 budget_headroom: Some(1.0),
1531 scarcity_score: route.scarce_capacity_units as f64,
1532 estimated_cost_microunits: 0,
1533 estimated_latency_ms: 1,
1534 state_refs: CandidateStateRefs {
1535 catalog: "offline-fixture:catalog".to_owned(),
1536 health: "offline-fixture:health".to_owned(),
1537 quota: "offline-fixture:quota".to_owned(),
1538 budget: "offline-fixture:budget".to_owned(),
1539 conformance: "offline-fixture:conformance".to_owned(),
1540 },
1541 })
1542}
1543
1544fn apply_case_overrides(snapshot: &mut CandidateSnapshot, id: &str, case_id: &str) {
1545 if case_id == "ordered-hard-filters-empty" {
1546 snapshot.capabilities.wire_protocol = CapabilityState::Verified;
1547 snapshot.capabilities.streaming = CapabilityState::Verified;
1548 snapshot.capabilities.function_tools = CapabilityState::Verified;
1549 snapshot.capabilities.tool_result_continuation = CapabilityState::Verified;
1550 snapshot.capabilities.images = CapabilityState::Verified;
1551 snapshot.capabilities.structured_output = CapabilityState::Verified;
1552 snapshot.capabilities.public_reasoning = CapabilityState::Verified;
1553 snapshot.capabilities.audio = CapabilityState::Verified;
1554 snapshot.capabilities.max_context_tokens = 200_000;
1555 snapshot.capabilities.privacy_modes = vec![PrivacyMode::ConfiguredProviderAllowed];
1556 }
1557 match id {
1558 "reject-protocol" => snapshot.capabilities.wire_protocol = CapabilityState::Unknown,
1559 "reject-tools" => snapshot.capabilities.function_tools = CapabilityState::Unknown,
1560 "reject-modality" => snapshot.capabilities.images = CapabilityState::Unsupported,
1561 "reject-context" => snapshot.capabilities.max_context_tokens = 1,
1562 "reject-privacy" => snapshot.capabilities.privacy_modes.clear(),
1563 "reject-provider-status" => snapshot.provider_status = ProviderStatus::Unavailable,
1564 "reject-model-status" => snapshot.model_status = ModelStatus::Unavailable,
1565 "reject-account-status" => snapshot.account_status = AccountStatus::Unavailable,
1566 "reject-quota" => snapshot.quota_headroom = None,
1567 "reject-budget" => snapshot.budget_headroom = None,
1568 _ => {}
1569 }
1570 if case_id == "provider-fallback-preserves-capability" && id == "primary-standard" {
1571 snapshot.provider_status = ProviderStatus::Unavailable;
1572 }
1573}
1574
1575fn comprehensive_filter_classification(
1576 classification: &router_policy::Classification,
1577) -> router_policy::Classification {
1578 let mut classification = classification.clone();
1579 classification.minimum_capability.function_tools = true;
1580 classification.minimum_capability.tool_result_continuation = true;
1581 classification.minimum_capability.images = true;
1582 classification.minimum_capability.structured_output = true;
1583 classification.minimum_capability.public_reasoning = true;
1584 classification.minimum_capability.audio = true;
1585 classification.minimum_capability.min_context_tokens = 200_000;
1586 classification
1587}
1588
1589fn activation_gates(version: &str, fixture: &GateStateFixture) -> ActivationGates {
1590 let gate = |name: &str, status: GateStatus| ActivationGate {
1591 status,
1592 policy_version: version.to_owned(),
1593 evidence_refs: if status == GateStatus::Passed {
1594 vec![format!("offline-fixture:{name}")]
1595 } else {
1596 Vec::new()
1597 },
1598 };
1599 ActivationGates {
1600 policy_evaluation: gate("policy_evaluation", fixture.policy_evaluation),
1601 protocol_conformance: gate("protocol_conformance", fixture.protocol_conformance),
1602 provider_support: gate("provider_support", fixture.provider_support),
1603 security_review: gate("security_review", fixture.security_review),
1604 operator_approval: gate("operator_approval", fixture.operator_approval),
1605 }
1606}
1607
1608fn candidate_from_gates(
1609 snapshot: CandidateSnapshot,
1610 version: &str,
1611 fixture: &GateStateFixture,
1612) -> Result<Candidate, EvaluationError> {
1613 let gates = activation_gates(version, fixture);
1614 let all_passed = gates.all_passed_for(version);
1615 let roles = [
1616 Role::Routine,
1617 Role::Standard,
1618 Role::Complex,
1619 Role::Vision,
1620 Role::LongContext,
1621 ]
1622 .into_iter()
1623 .map(|role| RoleDeclaration {
1624 role,
1625 quality: match role {
1626 Role::Routine => Quality::Routine,
1627 Role::Complex => Quality::Complex,
1628 Role::Standard | Role::Vision | Role::LongContext => Quality::Standard,
1629 },
1630 reasoning_efforts: vec![snapshot.reasoning_effort],
1631 capabilities: snapshot.capabilities.clone(),
1632 model_ids: vec![snapshot.model_id.clone()],
1633 escalates_to: None,
1634 estimated_quota_units: Some(0.0),
1635 estimated_cost_microunits: Some(0),
1636 conformance: ConformanceEvidence {
1637 complete: all_passed,
1638 policy_version: version.to_owned(),
1639 evidence_refs: if all_passed {
1640 vec!["offline-fixture:role-evidence".to_owned()]
1641 } else {
1642 Vec::new()
1643 },
1644 },
1645 })
1646 .collect();
1647 let pack = PolicyPack {
1648 schema_version: "1.0.0".to_owned(),
1649 policy_version: version.to_owned(),
1650 pack_id: format!("pack:{}", snapshot.candidate_id),
1651 provider_id: snapshot.provider_id.clone(),
1652 phase: snapshot.phase.clone(),
1653 billing_class: snapshot.billing_class.clone(),
1654 fixture_only: false,
1655 conformance_complete: all_passed,
1656 operator_approved: all_passed,
1657 automatic_live_eligible: all_passed,
1658 automatic_enabled: all_passed,
1659 live_enabled: all_passed,
1660 confidence_threshold: 0.80,
1661 roles,
1662 gates,
1663 state_refs: PolicyStateRefs {
1664 catalog: "offline-fixture:catalog".to_owned(),
1665 provider_support: "offline-fixture:provider-support".to_owned(),
1666 conformance: "offline-fixture:conformance".to_owned(),
1667 },
1668 disabled: DisabledPolicy {
1669 reasons: if all_passed {
1670 Vec::new()
1671 } else {
1672 vec!["activation_blocked".to_owned()]
1673 },
1674 remediation: Vec::new(),
1675 },
1676 };
1677 Candidate::from_policy_pack(snapshot, &pack).map_err(|_| EvaluationError::InvalidFixture)
1678}
1679
1680fn route_matches(actual: &router_policy::SelectedRoute, expected: &ExpectedRoute) -> bool {
1681 actual.candidate_id == expected.candidate_id
1682 && actual.provider_id == expected.provider_id
1683 && actual.model_id == expected.model_id
1684 && actual.reasoning_effort == expected.reasoning_effort
1685}
1686
1687fn no_eligible_route_matches(
1688 actual: &router_policy::NoEligibleRoute,
1689 expected: &ExpectedNoEligibleRoute,
1690 profiles: &BTreeMap<String, CapabilityRequirements>,
1691) -> bool {
1692 actual.code == "no_eligible_route"
1693 && actual.request_id == expected.request_id
1694 && actual.selection_snapshot_id == expected.selection_snapshot_id
1695 && actual.resolved_role == expected.resolved_role
1696 && profiles
1697 .get(&expected.minimum_capability_profile)
1698 .is_some_and(|profile| actual.minimum_capability == *profile)
1699 && actual.policy_version == expected.policy_version
1700 && actual.retryable == expected.retryable
1701}
1702
1703fn exclusions_match(actual: &[router_policy::Exclusion], expected: &[ExpectedExclusion]) -> bool {
1704 actual.len() == expected.len()
1705 && actual.iter().zip(expected).all(|(actual, expected)| {
1706 actual.candidate_id == expected.candidate_id
1707 && actual.category == expected.category
1708 && actual.primary_reason == expected.primary_reason
1709 && expected
1710 .diagnostic_reason
1711 .as_ref()
1712 .is_none_or(|reason| actual.diagnostic_reasons.contains(reason))
1713 })
1714}
1715
1716fn execute_baseline(
1717 baseline: &BaselineFixture,
1718 snapshot: &SelectionSnapshot,
1719 classification: &router_policy::Classification,
1720 policy_version: &str,
1721) -> Option<String> {
1722 let eligible = snapshot
1723 .candidates()
1724 .iter()
1725 .filter(|candidate| hard_exclusion_for(candidate, classification, policy_version).is_none())
1726 .collect::<Vec<_>>();
1727 match baseline {
1728 BaselineFixture::StrongestHardEligibleInFirstEligiblePhase { .. } => {
1729 let phase = eligible
1730 .iter()
1731 .map(|candidate| candidate.snapshot().phase_priority)
1732 .min()?;
1733 eligible
1734 .into_iter()
1735 .filter(|candidate| candidate.snapshot().phase_priority == phase)
1736 .max_by(|left, right| {
1737 left.snapshot()
1738 .reasoning_effort
1739 .cmp(&right.snapshot().reasoning_effort)
1740 .then_with(|| {
1741 right
1742 .snapshot()
1743 .candidate_id
1744 .cmp(&left.snapshot().candidate_id)
1745 })
1746 })
1747 .map(|candidate| candidate.snapshot().candidate_id.clone())
1748 }
1749 BaselineFixture::FixedProviderModelThenDescendWhenHardIneligible {
1750 fixed_model_ids,
1751 ..
1752 } => {
1753 let phases = eligible
1754 .iter()
1755 .map(|candidate| candidate.snapshot().phase_priority)
1756 .collect::<BTreeSet<_>>();
1757 for phase in phases {
1758 let providers = eligible
1759 .iter()
1760 .filter(|candidate| candidate.snapshot().phase_priority == phase)
1761 .map(|candidate| candidate.snapshot().provider_id.as_str())
1762 .collect::<BTreeSet<_>>();
1763 for provider in providers {
1764 let Some(model_id) = fixed_model_ids.get(provider) else {
1765 continue;
1766 };
1767 if let Some(candidate) = eligible.iter().find(|candidate| {
1768 candidate.snapshot().phase_priority == phase
1769 && candidate.snapshot().provider_id == provider
1770 && candidate.snapshot().model_id == *model_id
1771 }) {
1772 return Some(candidate.snapshot().candidate_id.clone());
1773 }
1774 }
1775 }
1776 None
1777 }
1778 BaselineFixture::OrderedCandidatesWithoutTaskRoleSelection {
1779 ordered_candidate_ids,
1780 ..
1781 } => ordered_candidate_ids.iter().find_map(|candidate_id| {
1782 eligible
1783 .iter()
1784 .find(|candidate| candidate.snapshot().candidate_id == *candidate_id)
1785 .map(|candidate| candidate.snapshot().candidate_id.clone())
1786 }),
1787 }
1788}
1789
1790fn strategy_outcome(
1791 candidate_id: Option<String>,
1792 snapshot: &SelectionSnapshot,
1793 classification: &router_policy::Classification,
1794 policy_version: &str,
1795 expected_outcome: ExpectedOutcome,
1796 scarcity_by_id: &BTreeMap<String, u64>,
1797) -> StrategyOutcome {
1798 let candidate = candidate_id.as_deref().and_then(|id| {
1799 snapshot
1800 .candidates()
1801 .iter()
1802 .find(|candidate| candidate.snapshot().candidate_id == id)
1803 });
1804 let objective_correct = match expected_outcome {
1805 ExpectedOutcome::NoEligibleRoute => candidate.is_none(),
1806 ExpectedOutcome::Selected | ExpectedOutcome::ShadowSelected => {
1807 candidate.is_some_and(|candidate| {
1808 hard_exclusion_for(candidate, classification, policy_version).is_none()
1809 && candidate_meets_quality_floor(candidate.snapshot(), classification)
1810 })
1811 }
1812 };
1813 let scarce_capacity_units = candidate_id
1814 .as_ref()
1815 .and_then(|id| scarcity_by_id.get(id))
1816 .copied()
1817 .unwrap_or_default();
1818 StrategyOutcome {
1819 candidate_id,
1820 objective_correct,
1821 scarce_capacity_units,
1822 }
1823}
1824
1825fn execute_case_assertion(
1826 assertion: &CaseAssertion,
1827 classification: &router_policy::Classification,
1828 result: &Result<router_policy::SelectionDecision, Box<router_policy::NoEligibleRoute>>,
1829 decision_record: Option<&DecisionRecord>,
1830 strategy_outcomes: &BTreeMap<StrategyId, StrategyOutcome>,
1831 corpus: &EvaluationCorpus,
1832) -> bool {
1833 match assertion {
1834 CaseAssertion::Equals { path, value } => {
1835 let actual = match path {
1836 EqualsPath::ClassificationResolvedRole => {
1837 ScalarValue::Text(serialized_name(classification.resolved_role))
1838 }
1839 EqualsPath::SelectedRouteCandidateId => ScalarValue::Text(
1840 result
1841 .as_ref()
1842 .ok()
1843 .map(|decision| decision.intelligent_route.candidate_id.clone())
1844 .unwrap_or_default(),
1845 ),
1846 EqualsPath::ClassificationConfidence => {
1847 ScalarValue::Number(classification.confidence)
1848 }
1849 EqualsPath::ClassificationFunctionTools => {
1850 ScalarValue::Boolean(classification.minimum_capability.function_tools)
1851 }
1852 EqualsPath::ClassificationMinQuality => ScalarValue::Text(serialized_name(
1853 classification.minimum_capability.min_quality,
1854 )),
1855 EqualsPath::ClassificationImages => {
1856 ScalarValue::Boolean(classification.minimum_capability.images)
1857 }
1858 EqualsPath::ClassificationMinContextTokens => {
1859 ScalarValue::Unsigned(classification.minimum_capability.min_context_tokens)
1860 }
1861 EqualsPath::Outcome => ScalarValue::Text(match result {
1862 Ok(_) => "selected".to_owned(),
1863 Err(_) => "no_eligible_route".to_owned(),
1864 }),
1865 EqualsPath::PolicyVersion | EqualsPath::EvaluationPolicyVersion => {
1866 ScalarValue::Text(corpus.policy_version.clone())
1867 }
1868 EqualsPath::DecisionRecordMode => ScalarValue::Text(
1869 decision_record
1870 .map(|record| serialized_name(record.mode))
1871 .unwrap_or_default(),
1872 ),
1873 EqualsPath::ShadowIntelligentDispatchCount => ScalarValue::Unsigned(
1874 result
1875 .as_ref()
1876 .ok()
1877 .map_or(0, |decision| decision.intelligent_dispatch_count),
1878 ),
1879 EqualsPath::DispatchCandidateId => ScalarValue::Text(
1880 result
1881 .as_ref()
1882 .ok()
1883 .map(|decision| decision.dispatch_route.candidate_id.clone())
1884 .unwrap_or_default(),
1885 ),
1886 };
1887 scalar_values_equal(&actual, value)
1888 }
1889 CaseAssertion::Contains { path, value } => match path {
1890 ContainsPath::ClassificationReasons => classification
1891 .reasons
1892 .iter()
1893 .any(|reason| serialized_name(*reason) == *value),
1894 ContainsPath::ExclusionCategories => exclusions_for(result)
1895 .iter()
1896 .any(|exclusion| serialized_name(exclusion.category) == *value),
1897 },
1898 CaseAssertion::OrderedEquals { path, value } => match path {
1899 OrderedPath::ExclusionReasons => exclusions_for(result)
1900 .iter()
1901 .map(|exclusion| exclusion.primary_reason)
1902 .eq(value.iter().copied()),
1903 },
1904 CaseAssertion::FieldsAbsent { path, value } => match path {
1905 RecordPath::DecisionRecord => decision_record.is_some_and(|record| {
1906 serde_json::to_value(record).is_ok_and(|encoded| {
1907 value
1908 .iter()
1909 .all(|field| !json_contains_key(&encoded, field.as_str()))
1910 })
1911 }),
1912 },
1913 CaseAssertion::RowSetEquals { path, value } => match path {
1914 RowSetPath::BaselineStrategyIds => corpus
1915 .baselines
1916 .iter()
1917 .map(BaselineFixture::id)
1918 .eq(value.iter().copied()),
1919 },
1920 CaseAssertion::LessThan {
1921 path,
1922 comparison_path,
1923 } => match (path, comparison_path) {
1924 (ScarcityPath::Intelligent, BaselineScarcityPath::AlwaysFrontier) => {
1925 let intelligent = strategy_outcomes.get(&StrategyId::Intelligent);
1926 let baseline = strategy_outcomes.get(&StrategyId::AlwaysFrontier);
1927 intelligent
1928 .zip(baseline)
1929 .is_some_and(|(intelligent, baseline)| {
1930 intelligent.scarce_capacity_units < baseline.scarce_capacity_units
1931 })
1932 }
1933 },
1934 }
1935}
1936
1937fn scalar_values_equal(left: &ScalarValue, right: &ScalarValue) -> bool {
1938 left == right
1939}
1940
1941fn serialized_name(value: impl Serialize) -> String {
1942 serde_json::to_value(value)
1943 .ok()
1944 .and_then(|value| value.as_str().map(str::to_owned))
1945 .unwrap_or_default()
1946}
1947
1948fn exclusions_for(
1949 result: &Result<router_policy::SelectionDecision, Box<router_policy::NoEligibleRoute>>,
1950) -> &[router_policy::Exclusion] {
1951 match result {
1952 Ok(decision) => &decision.exclusions,
1953 Err(error) => &error.exclusions,
1954 }
1955}
1956
1957fn record_expectations_pass(record: &DecisionRecord, expectations: &RecordExpectations) -> bool {
1958 if !expectations.append_only || record.selected_route_trace.len() != 7 {
1959 return false;
1960 }
1961 serde_json::to_value(record).is_ok_and(|encoded| {
1962 encoded.as_object().is_some_and(|object| {
1963 expectations
1964 .required_fields
1965 .iter()
1966 .all(|field| object.contains_key(field.as_str()))
1967 }) && expectations
1968 .forbidden_fields
1969 .iter()
1970 .all(|field| !json_contains_key(&encoded, field.as_str()))
1971 })
1972}
1973
1974fn json_contains_key(value: &serde_json::Value, key: &str) -> bool {
1975 match value {
1976 serde_json::Value::Object(object) => {
1977 object.contains_key(key) || object.values().any(|value| json_contains_key(value, key))
1978 }
1979 serde_json::Value::Array(values) => {
1980 values.iter().any(|value| json_contains_key(value, key))
1981 }
1982 _ => false,
1983 }
1984}
1985
1986fn strategy_expectation_matches(actual: &StrategyOutcome, expected: &StrategyExpectation) -> bool {
1987 actual.candidate_id.as_deref() == Some(expected.candidate_id.as_str())
1988 && actual.objective_correct == expected.objective_correct
1989 && actual.scarce_capacity_units == expected.scarce_capacity_units
1990}
1991
1992#[allow(clippy::too_many_arguments)]
1993fn execute_aggregate_assertion(
1994 assertion: &AggregateAssertion,
1995 expected_route_accuracy: f64,
1996 capability_preservation: f64,
1997 routine_frontier_avoidance: f64,
1998 intelligent_correctness: f64,
1999 intelligent_scarce: u64,
2000 baseline_rows: &[BaselineReportRow],
2001 policy_version: &str,
2002) -> bool {
2003 match assertion {
2004 AggregateAssertion::MetricAtLeast { metric, value, .. } => {
2005 ratio_metric(
2006 *metric,
2007 expected_route_accuracy,
2008 capability_preservation,
2009 routine_frontier_avoidance,
2010 ) >= *value
2011 }
2012 AggregateAssertion::MetricEquals { metric, value, .. } => {
2013 ratio_metric(
2014 *metric,
2015 expected_route_accuracy,
2016 capability_preservation,
2017 routine_frontier_avoidance,
2018 ) == *value
2019 }
2020 AggregateAssertion::MetricNonInferior {
2021 metric, baseline, ..
2022 } => match metric {
2023 CorrectnessMetric::IntelligentCorrectness => baseline_rows
2024 .iter()
2025 .find(|row| row.strategy_id == baseline.as_str())
2026 .is_some_and(|row| intelligent_correctness >= row.correctness),
2027 },
2028 AggregateAssertion::MetricStrictlyLess {
2029 metric, baselines, ..
2030 } => match metric {
2031 ScarcityMetric::RoutineStandardScarceCapacity => baselines.iter().all(|baseline| {
2032 baseline_rows
2033 .iter()
2034 .find(|row| row.strategy_id == baseline.as_str())
2035 .is_some_and(|row| intelligent_scarce < row.routine_standard_scarce_capacity)
2036 }),
2037 },
2038 AggregateAssertion::BaselineRowsPresent {
2039 baselines,
2040 policy_version: expected_version,
2041 ..
2042 } => {
2043 expected_version == policy_version
2044 && baseline_rows
2045 .iter()
2046 .map(|row| row.strategy_id.as_str())
2047 .eq(baselines.iter().map(|baseline| baseline.as_str()))
2048 }
2049 AggregateAssertion::AllAutomaticRolesFullyGated { required_gates, .. } => {
2050 *required_gates
2051 == [
2052 RequiredGate::PolicyEvaluation,
2053 RequiredGate::ProtocolConformance,
2054 RequiredGate::ProviderSupport,
2055 RequiredGate::SecurityReview,
2056 RequiredGate::OperatorApproval,
2057 ]
2058 && checked_in_packs_satisfy_automatic_gate_contract()
2059 }
2060 }
2061}
2062
2063fn ratio_metric(
2064 metric: RatioMetric,
2065 expected_route_accuracy: f64,
2066 capability_preservation: f64,
2067 routine_frontier_avoidance: f64,
2068) -> f64 {
2069 match metric {
2070 RatioMetric::ExpectedRouteAccuracy => expected_route_accuracy,
2071 RatioMetric::CapabilityPreservation => capability_preservation,
2072 RatioMetric::RoutineFrontierAvoidance => routine_frontier_avoidance,
2073 }
2074}
2075
2076fn checked_in_packs_satisfy_automatic_gate_contract() -> bool {
2077 [
2085 include_bytes!("../../../policy/providers/openai.json").as_slice(),
2086 include_bytes!("../../../policy/providers/ollama-cloud.json").as_slice(),
2087 include_bytes!("../../../policy/providers/openrouter.json").as_slice(),
2088 include_bytes!("../../../policy/providers/deepseek.json").as_slice(),
2089 include_bytes!("../../../policy/providers/qwen.json").as_slice(),
2090 ]
2091 .into_iter()
2092 .all(|bytes| {
2093 PolicyPack::from_json(bytes).is_ok_and(|pack| {
2094 pack.roles.len() == 5
2095 && !pack.fixture_only
2096 && pack.require_automatic_live_eligibility().is_ok()
2097 })
2098 }) && [include_bytes!("../../../policy/providers/kimi.json").as_slice()]
2099 .into_iter()
2100 .all(|bytes| {
2101 PolicyPack::from_json(bytes).is_ok_and(|pack| {
2102 pack.roles.len() == 5 && pack.require_automatic_live_eligibility().is_err()
2103 })
2104 })
2105}
2106
2107fn ratio(numerator: u64, denominator: u64) -> f64 {
2108 if denominator == 0 {
2109 0.0
2110 } else {
2111 numerator as f64 / denominator as f64
2112 }
2113}
2114
2115fn valid_version(value: &str) -> bool {
2116 if value.is_empty()
2117 || value.len() > 64
2118 || contains_sensitive_marker(value)
2119 || !value
2120 .bytes()
2121 .all(|byte| byte.is_ascii_digit() || byte == b'.')
2122 {
2123 return false;
2124 }
2125 let mut parts = value.split('.');
2126 matches!(
2127 (parts.next(), parts.next(), parts.next(), parts.next()),
2128 (Some(a), Some(b), Some(c), None)
2129 if [a, b, c].into_iter().all(|part| !part.is_empty() && part.bytes().all(|byte| byte.is_ascii_digit()))
2130 )
2131}
2132
2133fn safe_reference(value: &str) -> bool {
2134 !value.is_empty()
2135 && value.len() <= 256
2136 && !value.contains('@')
2137 && !contains_sensitive_marker(value)
2138 && !looks_like_sensitive_number(value)
2139 && value.bytes().all(|byte| {
2140 byte.is_ascii_alphanumeric() || matches!(byte, b'-' | b'_' | b'.' | b':' | b'/' | b'@')
2141 })
2142}
2143
2144fn contains_sensitive_marker(value: &str) -> bool {
2145 let lowered = value.to_ascii_lowercase();
2146 let sensitive_word = [
2147 "http://", "https://", "bearer", "token", "secret", "password", "api_key", "apikey",
2148 "cookie",
2149 ]
2150 .iter()
2151 .any(|marker| lowered.contains(marker));
2152 let sensitive_prefix = [
2153 "sk-",
2154 "sk_",
2155 "rk_live_",
2156 "pk_live_",
2157 "ghp_",
2158 "github_pat_",
2159 "xoxb-",
2160 "xoxp-",
2161 "akia",
2162 "eyj",
2163 ]
2164 .iter()
2165 .any(|marker| {
2166 lowered.match_indices(marker).any(|(index, _)| {
2167 index == 0
2168 || lowered
2169 .as_bytes()
2170 .get(index - 1)
2171 .is_some_and(|byte| matches!(byte, b':' | b'/' | b'_' | b'-' | b'.'))
2172 })
2173 });
2174 sensitive_word || sensitive_prefix
2175}
2176
2177fn looks_like_sensitive_number(value: &str) -> bool {
2178 if !value
2179 .bytes()
2180 .all(|byte| byte.is_ascii_digit() || byte == b'-')
2181 {
2182 return false;
2183 }
2184 let digits = value.bytes().filter(u8::is_ascii_digit).count();
2185 matches!(digits, 9 | 15 | 16 | 19)
2186}
2187
2188fn sha256_hex(bytes: &[u8]) -> String {
2189 let digest = Sha256::digest(bytes);
2190 let mut output = String::with_capacity(digest.len() * 2);
2191 for byte in digest {
2192 let _ = write!(&mut output, "{byte:02x}");
2193 }
2194 output
2195}