Skip to main content

aidens_contracts/
app_status.rs

1use super::*;
2
3#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
4pub struct AiDENsAppPlanV1 {
5    pub app_id: String,
6    pub profile_id: String,
7    pub provider_required: bool,
8    pub memory_mode: MemoryModeV1,
9    pub receipt_level: ReportLevelV1,
10    pub dangerous_auto_approval: bool,
11    #[serde(default, skip_serializing_if = "Vec::is_empty")]
12    pub risk_disclosures: Vec<RiskDisclosureV1>,
13    pub enabled_tool_bundles: Vec<String>,
14    pub disabled_tool_bundles: Vec<String>,
15}
16
17#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
18pub struct AiDENsCompiledPlanV1 {
19    pub plan_id: ArtifactId,
20    pub plan: AiDENsAppPlanV1,
21    pub provider_route: ProviderRouteReportV1,
22    pub tool_exposure: ToolExposureSetV1,
23    pub doctor: AiDENsDoctorReportV1,
24    pub config_apply_receipt: ConfigApplyReportV1,
25    pub parity_report: PlanRuntimeParityReportV1,
26}
27
28#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
29pub struct AiDENsDoctorReportV1 {
30    pub report_id: ArtifactId,
31    pub app_id: String,
32    pub sections: BTreeMap<String, Vec<RuntimeCapabilityTruthV1>>,
33}
34
35impl AiDENsDoctorReportV1 {
36    pub fn new(
37        app_id: impl Into<String>,
38        sections: BTreeMap<String, Vec<RuntimeCapabilityTruthV1>>,
39    ) -> Self {
40        Self {
41            report_id: display_only_unstable_id("doctor-report"),
42            app_id: app_id.into(),
43            sections,
44        }
45    }
46}
47
48#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
49#[serde(rename_all = "kebab-case")]
50pub enum ApiHonestyOutcomeV1 {
51    Honored,
52    Rejected,
53    Blocked,
54}
55
56#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
57pub struct ApiHonestyReportV1 {
58    pub receipt_id: ArtifactId,
59    pub kind: ArtifactKindV1,
60    pub surface: String,
61    pub accepted_inputs: Vec<String>,
62    pub honored_inputs: Vec<String>,
63    pub rejected_inputs: Vec<String>,
64    pub outcome: ApiHonestyOutcomeV1,
65    #[serde(default, skip_serializing_if = "Vec::is_empty")]
66    pub reason_codes: Vec<String>,
67    pub checked_at: DateTime<Utc>,
68}
69
70impl ApiHonestyReportV1 {
71    pub fn honored(
72        surface: impl Into<String>,
73        accepted_inputs: Vec<String>,
74        honored_inputs: Vec<String>,
75    ) -> Self {
76        Self {
77            receipt_id: display_only_unstable_id("api-honesty"),
78            kind: ArtifactKindV1::ApiHonesty,
79            surface: surface.into(),
80            accepted_inputs,
81            honored_inputs,
82            rejected_inputs: Vec::new(),
83            outcome: ApiHonestyOutcomeV1::Honored,
84            reason_codes: Vec::new(),
85            checked_at: Utc::now(),
86        }
87    }
88
89    pub fn rejected(
90        surface: impl Into<String>,
91        accepted_inputs: Vec<String>,
92        rejected_inputs: Vec<String>,
93        reason: impl Into<String>,
94    ) -> Self {
95        Self {
96            receipt_id: display_only_unstable_id("api-honesty"),
97            kind: ArtifactKindV1::ApiHonesty,
98            surface: surface.into(),
99            accepted_inputs,
100            honored_inputs: Vec::new(),
101            rejected_inputs,
102            outcome: ApiHonestyOutcomeV1::Rejected,
103            reason_codes: vec![reason.into()],
104            checked_at: Utc::now(),
105        }
106    }
107
108    pub fn blocked(
109        surface: impl Into<String>,
110        accepted_inputs: Vec<String>,
111        rejected_inputs: Vec<String>,
112        reason: impl Into<String>,
113    ) -> Self {
114        let mut receipt = Self::rejected(surface, accepted_inputs, rejected_inputs, reason);
115        receipt.outcome = ApiHonestyOutcomeV1::Blocked;
116        receipt
117    }
118
119    pub fn all_inputs_honored(&self) -> bool {
120        self.outcome == ApiHonestyOutcomeV1::Honored
121            && self
122                .accepted_inputs
123                .iter()
124                .all(|input| self.honored_inputs.contains(input))
125            && self.rejected_inputs.is_empty()
126    }
127}
128
129#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
130pub struct ConfigApplyReportV1 {
131    pub receipt_id: ArtifactId,
132    pub kind: ArtifactKindV1,
133    pub app_id: String,
134    pub config_source: String,
135    pub provider_route: ProviderRouteReportV1,
136    pub tool_exposure: ToolExposureSetV1,
137    pub memory_mode: MemoryModeV1,
138    pub receipt_level: ReportLevelV1,
139    pub enabled_tool_bundles: Vec<String>,
140    pub sandbox_root: Option<String>,
141    pub applied: bool,
142    #[serde(default, skip_serializing_if = "Vec::is_empty")]
143    pub reason_codes: Vec<String>,
144    pub applied_at: DateTime<Utc>,
145}
146
147#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
148pub struct ConfigApplyReportDraftV1 {
149    pub app_id: String,
150    pub config_source: String,
151    pub provider_route: ProviderRouteReportV1,
152    pub tool_exposure: ToolExposureSetV1,
153    pub memory_mode: MemoryModeV1,
154    pub receipt_level: ReportLevelV1,
155    pub enabled_tool_bundles: Vec<String>,
156    pub sandbox_root: Option<String>,
157    pub applied: bool,
158    #[serde(default, skip_serializing_if = "Vec::is_empty")]
159    pub reason_codes: Vec<String>,
160}
161
162impl ConfigApplyReportV1 {
163    pub fn new(draft: ConfigApplyReportDraftV1) -> Self {
164        Self {
165            receipt_id: display_only_unstable_id("config-apply"),
166            kind: ArtifactKindV1::ConfigApply,
167            app_id: draft.app_id,
168            config_source: draft.config_source,
169            provider_route: draft.provider_route,
170            tool_exposure: draft.tool_exposure,
171            memory_mode: draft.memory_mode,
172            receipt_level: draft.receipt_level,
173            enabled_tool_bundles: draft.enabled_tool_bundles,
174            sandbox_root: draft.sandbox_root,
175            applied: draft.applied,
176            reason_codes: draft.reason_codes,
177            applied_at: Utc::now(),
178        }
179    }
180}
181
182#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
183#[serde(rename_all = "kebab-case")]
184pub enum PlanRuntimeParityCheckKindV1 {
185    ProviderRoute,
186    ToolExposure,
187    MemoryMode,
188    ScaffoldState,
189}
190
191impl fmt::Display for PlanRuntimeParityCheckKindV1 {
192    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
193        f.write_str(match self {
194            Self::ProviderRoute => "provider-route",
195            Self::ToolExposure => "tool-exposure",
196            Self::MemoryMode => "memory-mode",
197            Self::ScaffoldState => "scaffold-state",
198        })
199    }
200}
201
202#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
203pub struct PlanRuntimeParityCheckV1 {
204    pub check: PlanRuntimeParityCheckKindV1,
205    pub expected: String,
206    pub observed: String,
207    pub passed: bool,
208}
209
210impl PlanRuntimeParityCheckV1 {
211    pub fn new(
212        check: PlanRuntimeParityCheckKindV1,
213        expected: impl Into<String>,
214        observed: impl Into<String>,
215    ) -> Self {
216        let expected = expected.into();
217        let observed = observed.into();
218        Self {
219            check,
220            passed: expected == observed,
221            expected,
222            observed,
223        }
224    }
225}
226
227#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
228pub struct PlanRuntimeParityReportV1 {
229    pub report_id: ArtifactId,
230    pub kind: ArtifactKindV1,
231    pub app_id: String,
232    pub checks: Vec<PlanRuntimeParityCheckV1>,
233    #[serde(default, skip_serializing_if = "Vec::is_empty")]
234    pub mismatches: Vec<String>,
235    pub generated_at: DateTime<Utc>,
236}
237
238impl PlanRuntimeParityReportV1 {
239    pub fn new(app_id: impl Into<String>, checks: Vec<PlanRuntimeParityCheckV1>) -> Self {
240        let mismatches = checks
241            .iter()
242            .filter(|check| !check.passed)
243            .map(|check| {
244                format!(
245                    "{} expected '{}' but observed '{}'",
246                    check.check, check.expected, check.observed
247                )
248            })
249            .collect();
250        Self {
251            report_id: display_only_unstable_id("plan-runtime-parity"),
252            kind: ArtifactKindV1::PlanRuntimeParity,
253            app_id: app_id.into(),
254            checks,
255            mismatches,
256            generated_at: Utc::now(),
257        }
258    }
259
260    pub fn is_passing(&self) -> bool {
261        self.mismatches.is_empty() && self.checks.iter().all(|check| check.passed)
262    }
263}
264
265#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
266#[serde(rename_all = "kebab-case")]
267pub enum CrateImplementationStatusV1 {
268    Implemented,
269    Partial,
270    ScaffoldOnly,
271}
272
273impl fmt::Display for CrateImplementationStatusV1 {
274    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
275        f.write_str(match self {
276            Self::Implemented => "implemented",
277            Self::Partial => "partial",
278            Self::ScaffoldOnly => "scaffold-only",
279        })
280    }
281}
282
283#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
284pub struct SourceBasisLockV1 {
285    pub lock_id: ArtifactId,
286    pub snapshot_date: String,
287    pub source_archive: String,
288    pub research_archive: String,
289    pub extraction_root: String,
290    pub workspace_crates: u32,
291    pub rust_files: u32,
292    pub approximate_rust_loc: u32,
293    pub scaffold_only_files: u32,
294    pub research_files: u32,
295    pub locked_at: DateTime<Utc>,
296}
297
298impl SourceBasisLockV1 {
299    pub fn current_20260426() -> Self {
300        Self {
301            lock_id: display_only_unstable_id("source-basis-lock"),
302            snapshot_date: "2026-04-26".into(),
303            source_archive: "libraries-source-clean-20260426.zip".into(),
304            research_archive: "Full Provenance+ Research 4/24/26.zip".into(),
305            extraction_root: "/mnt/data/aidens_20260426".into(),
306            workspace_crates: 31,
307            rust_files: 37,
308            approximate_rust_loc: 5126,
309            scaffold_only_files: 15,
310            research_files: 52,
311            locked_at: Utc::now(),
312        }
313    }
314}
315
316#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
317pub struct CrateSurfaceStatusV1 {
318    pub crate_name: String,
319    pub status: CrateImplementationStatusV1,
320    pub rust_files: u32,
321    pub approximate_rust_loc: u32,
322    pub note: String,
323}
324
325impl CrateSurfaceStatusV1 {
326    pub fn new(
327        crate_name: impl Into<String>,
328        status: CrateImplementationStatusV1,
329        rust_files: u32,
330        approximate_rust_loc: u32,
331        note: impl Into<String>,
332    ) -> Self {
333        Self {
334            crate_name: crate_name.into(),
335            status,
336            rust_files,
337            approximate_rust_loc,
338            note: note.into(),
339        }
340    }
341
342    pub fn allows_scaffold_marker(&self) -> bool {
343        self.status == CrateImplementationStatusV1::ScaffoldOnly
344    }
345}
346
347#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
348pub struct ScaffoldSurfaceReportV1 {
349    pub report_id: ArtifactId,
350    pub generated_at: DateTime<Utc>,
351    pub crates: Vec<CrateSurfaceStatusV1>,
352}
353
354impl ScaffoldSurfaceReportV1 {
355    pub fn new(crates: Vec<CrateSurfaceStatusV1>) -> Self {
356        Self {
357            report_id: display_only_unstable_id("scaffold-surface-report"),
358            generated_at: Utc::now(),
359            crates,
360        }
361    }
362
363    pub fn scaffold_only_crates(&self) -> Vec<&str> {
364        self.crates
365            .iter()
366            .filter(|surface| surface.status == CrateImplementationStatusV1::ScaffoldOnly)
367            .map(|surface| surface.crate_name.as_str())
368            .collect()
369    }
370
371    pub fn allows_scaffold_marker_for(&self, crate_name: &str) -> bool {
372        self.crates
373            .iter()
374            .find(|surface| surface.crate_name == crate_name)
375            .is_some_and(CrateSurfaceStatusV1::allows_scaffold_marker)
376    }
377}
378
379#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
380pub struct FakeReadyFindingV1 {
381    pub finding_id: ArtifactId,
382    pub surface: String,
383    pub pattern: String,
384    pub reason: String,
385    pub blocked: bool,
386    pub found_at: DateTime<Utc>,
387}
388
389impl FakeReadyFindingV1 {
390    pub fn blocking(
391        surface: impl Into<String>,
392        pattern: impl Into<String>,
393        reason: impl Into<String>,
394    ) -> Self {
395        Self {
396            finding_id: display_only_unstable_id("fake-ready-finding"),
397            surface: surface.into(),
398            pattern: pattern.into(),
399            reason: reason.into(),
400            blocked: true,
401            found_at: Utc::now(),
402        }
403    }
404}
405
406#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
407#[serde(rename_all = "kebab-case")]
408pub enum SuperPassDispositionV1 {
409    Pending,
410    InProgress,
411    Done,
412    Blocked,
413    Deferred,
414}
415
416#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
417pub struct SuperPassStatusV1 {
418    pub status_id: ArtifactId,
419    pub current_pass: String,
420    pub disposition: SuperPassDispositionV1,
421    pub source_basis: SourceBasisLockV1,
422    pub scaffold_report: ScaffoldSurfaceReportV1,
423    #[serde(default, skip_serializing_if = "Vec::is_empty")]
424    pub fake_ready_findings: Vec<FakeReadyFindingV1>,
425    #[serde(default, skip_serializing_if = "Vec::is_empty")]
426    pub blockers: Vec<String>,
427    pub updated_at: DateTime<Utc>,
428}
429
430impl SuperPassStatusV1 {
431    pub fn new(
432        current_pass: impl Into<String>,
433        disposition: SuperPassDispositionV1,
434        source_basis: SourceBasisLockV1,
435        scaffold_report: ScaffoldSurfaceReportV1,
436        fake_ready_findings: Vec<FakeReadyFindingV1>,
437        blockers: Vec<String>,
438    ) -> Self {
439        Self {
440            status_id: display_only_unstable_id("super-pass-status"),
441            current_pass: current_pass.into(),
442            disposition,
443            source_basis,
444            scaffold_report,
445            fake_ready_findings,
446            blockers,
447            updated_at: Utc::now(),
448        }
449    }
450
451    pub fn is_blocked(&self) -> bool {
452        self.disposition == SuperPassDispositionV1::Blocked
453            || !self.blockers.is_empty()
454            || self
455                .fake_ready_findings
456                .iter()
457                .any(|finding| finding.blocked)
458    }
459}
460
461impl AiDENsAppPlanV1 {
462    pub fn validate(&self) -> Result<(), String> {
463        if self.app_id.trim().is_empty() {
464            return Err("app_id must not be empty".into());
465        }
466        if self.dangerous_auto_approval {
467            return Err("dangerous_auto_approval requires explicit advanced override".into());
468        }
469        Ok(())
470    }
471
472    pub fn human_summary(&self) -> String {
473        format!(
474            "{} [{}]: provider_required={}, memory_mode={}, receipt_level={}",
475            self.app_id,
476            self.profile_id,
477            self.provider_required,
478            self.memory_mode,
479            self.receipt_level
480        )
481    }
482
483    pub fn risk_summary(&self) -> String {
484        if self.risk_disclosures.is_empty() {
485            return "No risky capabilities are granted by default.".into();
486        }
487        self.risk_disclosures
488            .iter()
489            .map(|risk| {
490                format!(
491                    "{}: granted_by_default={}, permit_required={} ({})",
492                    risk.risk_class, risk.granted_by_default, risk.permit_required, risk.reason
493                )
494            })
495            .collect::<Vec<_>>()
496            .join("; ")
497    }
498}
499
500#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
501pub struct RiskDisclosureV1 {
502    pub risk_class: CanonicalToolSideEffectClass,
503    pub granted_by_default: bool,
504    pub permit_required: bool,
505    pub reason: String,
506}
507
508#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
509#[serde(rename_all = "kebab-case")]
510pub enum MemoryModeV1 {
511    Disabled,
512    Optional,
513    Required,
514}
515
516impl fmt::Display for MemoryModeV1 {
517    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
518        f.write_str(match self {
519            Self::Disabled => "disabled",
520            Self::Optional => "optional",
521            Self::Required => "required",
522        })
523    }
524}
525
526#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
527#[serde(rename_all = "kebab-case")]
528pub enum ReportLevelV1 {
529    Minimal,
530    Standard,
531    Full,
532}
533
534impl fmt::Display for ReportLevelV1 {
535    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
536        f.write_str(match self {
537            Self::Minimal => "minimal",
538            Self::Standard => "standard",
539            Self::Full => "full",
540        })
541    }
542}