Skip to main content

secunit_core/evidence/
manifest.rs

1//! Strongly-typed prepare / result / manifest payloads. Each maps 1:1
2//! to its JSON Schema under `schemas/`.
3
4use std::collections::BTreeMap;
5use std::path::PathBuf;
6
7use chrono::{DateTime, Utc};
8use serde::{Deserialize, Serialize};
9
10use crate::model::ResolvedSystem;
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
13#[serde(rename_all = "kebab-case")]
14pub enum ScopeLayout {
15    BySystem,
16    Flat,
17}
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
20#[serde(rename_all = "lowercase")]
21pub enum RunOutcome {
22    Complete,
23    Partial,
24    Failed,
25}
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
28#[serde(rename_all = "lowercase")]
29pub enum SystemOutcome {
30    Complete,
31    Skipped,
32    Failed,
33}
34
35// ---------- prepare context -------------------------------------------------
36
37#[derive(Debug, Clone, Serialize, Deserialize)]
38pub struct PrepareContext {
39    pub schema_version: u32,
40    pub control_id: String,
41    pub run_id: String,
42    pub run_dir: PathBuf,
43    pub started_at: DateTime<Utc>,
44    #[serde(skip_serializing_if = "Option::is_none")]
45    pub operator: Option<String>,
46    #[serde(skip_serializing_if = "Option::is_none")]
47    pub note: Option<String>,
48    pub scope_layout: ScopeLayout,
49    pub resolved_scope: Vec<ResolvedSystem>,
50    pub registry_git_sha: String,
51    #[serde(default, skip_serializing_if = "Option::is_none")]
52    pub period_id: Option<String>,
53}
54
55// ---------- result (skill output) ------------------------------------------
56
57#[derive(Debug, Clone, Serialize, Deserialize)]
58pub struct RunResult {
59    pub schema_version: u32,
60    pub control_id: String,
61    pub run_id: String,
62    pub status: RunOutcome,
63    #[serde(default)]
64    pub by_system: Vec<SystemResult>,
65    #[serde(default)]
66    pub draft_risks: Vec<serde_json::Value>,
67    #[serde(default)]
68    pub draft_issues: Vec<serde_json::Value>,
69    #[serde(default)]
70    pub external_links: Vec<serde_json::Value>,
71}
72
73#[derive(Debug, Clone, Serialize, Deserialize)]
74pub struct SystemResult {
75    pub name: String,
76    pub status: SystemOutcome,
77    #[serde(default, skip_serializing_if = "Option::is_none")]
78    pub note: Option<String>,
79}
80
81// ---------- finalized manifest ---------------------------------------------
82
83#[derive(Debug, Clone, Serialize, Deserialize)]
84pub struct AgentInfo {
85    pub model: String,
86    pub skill: String,
87    pub skill_sha256: String,
88    pub control_sha256: String,
89}
90
91#[derive(Debug, Clone, Serialize, Deserialize)]
92pub struct PriorRun {
93    pub run_id: String,
94    pub manifest_sha256: String,
95}
96
97#[derive(Debug, Clone, Serialize, Deserialize)]
98pub struct Artifact {
99    pub path: String,
100    pub sha256: String,
101    pub bytes: u64,
102}
103
104#[derive(Debug, Clone, Serialize, Deserialize)]
105pub struct BySystemBlock {
106    pub name: String,
107    pub status: SystemOutcome,
108    #[serde(default, skip_serializing_if = "Option::is_none")]
109    pub summary: Option<BTreeMap<String, serde_json::Value>>,
110    pub artifacts: Vec<Artifact>,
111}
112
113#[derive(Debug, Clone, Serialize, Deserialize)]
114pub struct Manifest {
115    pub schema_version: u32,
116    pub control_id: String,
117    pub run_id: String,
118    pub started_at: DateTime<Utc>,
119    pub completed_at: DateTime<Utc>,
120    #[serde(default, skip_serializing_if = "Option::is_none")]
121    pub operator: Option<String>,
122    pub agent: AgentInfo,
123    pub registry_git_sha: String,
124    pub scope_layout: ScopeLayout,
125    pub resolved_scope: Vec<ResolvedSystem>,
126    #[serde(default, skip_serializing_if = "Option::is_none")]
127    pub prior_run: Option<PriorRun>,
128    pub artifacts: Vec<Artifact>,
129    #[serde(default, skip_serializing_if = "Vec::is_empty")]
130    pub by_system: Vec<BySystemBlock>,
131    pub status: RunOutcome,
132    #[serde(default, skip_serializing_if = "Option::is_none")]
133    pub failure_reason: Option<String>,
134    #[serde(default)]
135    pub draft_risks: Vec<serde_json::Value>,
136    #[serde(default)]
137    pub draft_issues: Vec<serde_json::Value>,
138    #[serde(default)]
139    pub external_links: Vec<serde_json::Value>,
140    #[serde(default, skip_serializing_if = "Option::is_none")]
141    pub period_id: Option<String>,
142}