Skip to main content

spatialrust_platform/
vision.rs

1//! SpatialRust Vision 1.0 release evidence and mandatory gate policy.
2
3use crate::{
4    BudgetKind, ConformanceReport, ConformanceStatus, LtsPolicy, PerformanceBudget,
5    PerformanceBudgetReport, ReleaseGate, ReleaseGateDecision, SecurityChecklist,
6    StabilityRegistry,
7};
8
9const REQUIRED_CASES: &[&str] = &[
10    "vision-full-rust",
11    "vision-properties",
12    "vision-python-bindings",
13    "vision-opencv-correctness",
14    "vision-opencv-performance",
15    "vision-gpu-explicit-transfers",
16    "vision-linux",
17    "vision-windows",
18    "vision-macos",
19    "vision-unsafe-audit",
20];
21
22const REQUIRED_COMPARISONS: &[&str] = &[
23    "opencv-vision-correctness",
24    "opencv-vision-performance",
25    "opencv-rgbd-performance",
26    "opencv-calibration",
27    "opencv-video",
28    "opencv-odometry",
29    "opencv-photography",
30];
31
32const REQUIRED_EXAMPLES: &[&str] = &["vision_1_cpu", "vision_1_release_gate"];
33
34/// Fixed-dimension performance measurements consumed by the Vision 1.0 gate.
35#[derive(Clone, Copy, Debug, PartialEq, Eq)]
36pub struct Vision1Measurements {
37    /// VGA CPU end-to-end latency in microseconds (budget: 50 ms).
38    pub cpu_vga_latency_us: u64,
39    /// 1080p CPU end-to-end latency in microseconds (budget: 250 ms).
40    pub cpu_1080p_latency_us: u64,
41    /// 4K resident GPU chain latency in microseconds (budget: 20 ms).
42    pub gpu_4k_chain_latency_us: u64,
43    /// Maximum explicit bytes copied by one canonical graph run (budget: 64 MiB).
44    pub explicit_copy_bytes: u64,
45}
46
47/// Evidence gathered by CI/release tooling for a Vision 1.0 candidate.
48#[derive(Clone, Debug)]
49pub struct Vision1ReleaseEvidence {
50    /// Required test/audit cases and their status.
51    pub conformance: ConformanceReport,
52    /// Satisfied security audit evidence.
53    pub security: SecurityChecklist,
54    /// Measured canonical performance values.
55    pub measurements: Vision1Measurements,
56    /// Comparison suite identifiers whose reports passed.
57    pub passed_comparison_suites: Vec<String>,
58    /// Cargo example names compiled and exercised by the candidate.
59    pub verified_examples: Vec<String>,
60    /// Migration policy identifier; must equal `vision-1`.
61    pub migration_policy: String,
62}
63
64/// Mandatory SpatialRust Vision 1.0 release policy.
65#[derive(Clone, Copy, Debug, Default)]
66pub struct Vision1ReleaseGate;
67
68impl Vision1ReleaseGate {
69    /// Returns conformance ids that must be present with `Pass` status.
70    pub const fn required_conformance_cases() -> &'static [&'static str] {
71        REQUIRED_CASES
72    }
73    /// Returns OpenCV comparison suite ids required by the release receipt.
74    pub const fn required_comparison_suites() -> &'static [&'static str] {
75        REQUIRED_COMPARISONS
76    }
77    /// Returns examples that must compile and run for the release candidate.
78    pub const fn required_examples() -> &'static [&'static str] {
79        REQUIRED_EXAMPLES
80    }
81
82    /// Evaluates all mandatory evidence and returns every denial reason.
83    pub fn evaluate(evidence: &Vision1ReleaseEvidence) -> ReleaseGateDecision {
84        let budgets = vision_budgets(evidence.measurements);
85        let base = ReleaseGate {
86            stability: Some(StabilityRegistry::vision_v1_surface()),
87            conformance: Some(evidence.conformance.clone()),
88            security: Some(evidence.security.clone()),
89            lts: Some(LtsPolicy::spatialrust_v1()),
90            budgets: Some(budgets),
91            reject_experimental: true,
92        };
93        let mut decision = base.evaluate();
94        for required in REQUIRED_CASES {
95            let matching = evidence
96                .conformance
97                .cases()
98                .iter()
99                .filter(|case| case.id == *required)
100                .collect::<Vec<_>>();
101            match matching.as_slice() {
102                [case] if case.status == ConformanceStatus::Pass => {}
103                [case] => decision
104                    .reasons
105                    .push(format!("required conformance `{required}` is {:?}", case.status)),
106                [] => {
107                    decision.reasons.push(format!("required conformance `{required}` is missing"))
108                }
109                _ => decision
110                    .reasons
111                    .push(format!("required conformance `{required}` is duplicated")),
112            }
113        }
114        require_names(
115            &mut decision.reasons,
116            "comparison suite",
117            REQUIRED_COMPARISONS,
118            &evidence.passed_comparison_suites,
119        );
120        require_names(
121            &mut decision.reasons,
122            "example",
123            REQUIRED_EXAMPLES,
124            &evidence.verified_examples,
125        );
126        if evidence.migration_policy != "vision-1" {
127            decision.reasons.push("migration policy `vision-1` was not acknowledged".into());
128        }
129        decision.allowed = decision.reasons.is_empty();
130        decision
131    }
132}
133
134fn require_names(reasons: &mut Vec<String>, kind: &str, required: &[&str], actual: &[String]) {
135    for name in required {
136        if !actual.iter().any(|value| value == name) {
137            reasons.push(format!("required {kind} `{name}` is missing"));
138        }
139    }
140}
141
142fn vision_budgets(measurements: Vision1Measurements) -> PerformanceBudgetReport {
143    let mut report = PerformanceBudgetReport::new();
144    for (id, kind, ceiling, observed) in [
145        ("vision-cpu-vga-us", BudgetKind::LatencyMicros, 50_000, measurements.cpu_vga_latency_us),
146        (
147            "vision-cpu-1080p-us",
148            BudgetKind::LatencyMicros,
149            250_000,
150            measurements.cpu_1080p_latency_us,
151        ),
152        (
153            "vision-gpu-4k-chain-us",
154            BudgetKind::LatencyMicros,
155            20_000,
156            measurements.gpu_4k_chain_latency_us,
157        ),
158        (
159            "vision-explicit-copy-bytes",
160            BudgetKind::BytesCopied,
161            64 * 1024 * 1024,
162            measurements.explicit_copy_bytes,
163        ),
164    ] {
165        report.declare(PerformanceBudget { id: id.into(), kind, ceiling });
166        report.sample(id, observed);
167    }
168    report
169}
170
171#[cfg(test)]
172mod tests {
173    use super::{Vision1Measurements, Vision1ReleaseEvidence, Vision1ReleaseGate};
174    use crate::{ConformanceReport, ConformanceStatus, SecurityChecklist};
175
176    fn passing() -> Vision1ReleaseEvidence {
177        let mut conformance = ConformanceReport::new();
178        for &id in Vision1ReleaseGate::required_conformance_cases() {
179            conformance.record(id, ConformanceStatus::Pass, None);
180        }
181        Vision1ReleaseEvidence {
182            conformance,
183            security: SecurityChecklist::north_star_baseline_satisfied(),
184            measurements: Vision1Measurements {
185                cpu_vga_latency_us: 10_000,
186                cpu_1080p_latency_us: 100_000,
187                gpu_4k_chain_latency_us: 13_523,
188                explicit_copy_bytes: 8 * 1024 * 1024,
189            },
190            passed_comparison_suites: Vision1ReleaseGate::required_comparison_suites()
191                .iter()
192                .map(ToString::to_string)
193                .collect(),
194            verified_examples: Vision1ReleaseGate::required_examples()
195                .iter()
196                .map(ToString::to_string)
197                .collect(),
198            migration_policy: "vision-1".into(),
199        }
200    }
201
202    #[test]
203    fn vision1_complete_evidence_is_allowed() {
204        assert!(Vision1ReleaseGate::evaluate(&passing()).allowed);
205    }
206
207    #[test]
208    fn vision1_rejects_missing_case_and_over_budget() {
209        let mut evidence = passing();
210        evidence.conformance = ConformanceReport::new();
211        evidence.measurements.gpu_4k_chain_latency_us = 20_001;
212        let decision = Vision1ReleaseGate::evaluate(&evidence);
213        assert!(!decision.allowed);
214        assert!(decision.reasons.iter().any(|reason| reason.contains("vision-linux")));
215        assert!(decision.reasons.iter().any(|reason| reason.contains("budget")));
216    }
217
218    #[test]
219    fn vision1_rejects_skipped_mandatory_case() {
220        let mut evidence = passing();
221        let mut conformance = ConformanceReport::new();
222        for &id in Vision1ReleaseGate::required_conformance_cases() {
223            let status = if id == "vision-macos" {
224                ConformanceStatus::Skip
225            } else {
226                ConformanceStatus::Pass
227            };
228            conformance.record(id, status, None);
229        }
230        evidence.conformance = conformance;
231        let decision = Vision1ReleaseGate::evaluate(&evidence);
232        assert!(!decision.allowed);
233        assert!(decision
234            .reasons
235            .iter()
236            .any(|reason| reason.contains("vision-macos") && reason.contains("Skip")));
237    }
238}