1use crate::digest::Digest256;
7use chrono::{DateTime, Utc};
8use serde::{Deserialize, Serialize};
9
10pub const MEDIATYPE_V1: &str = "application/vnd.processfork.image.v1+json";
12
13#[derive(Clone, Debug, Serialize, Deserialize)]
15pub struct Manifest {
16 pub schema_version: u32,
18 pub media_type: String,
20 pub agent: AgentInfo,
22 pub model: ModelLayer,
24 pub cache: CacheLayer,
26 pub world: WorldLayer,
28 pub effects: EffectsLayer,
30 pub trace: TraceLayer,
32 pub created_at: DateTime<Utc>,
34 #[serde(default)]
36 pub parents: Vec<Digest256>,
37}
38
39#[derive(Clone, Debug, Serialize, Deserialize)]
41pub struct AgentInfo {
42 pub kind: String,
44 pub version: String,
46 pub fingerprint: String,
48}
49
50#[derive(Clone, Debug, Serialize, Deserialize)]
52pub struct ModelLayer {
53 pub base: Digest256,
55 pub diff: Digest256,
57}
58
59#[derive(Clone, Debug, Serialize, Deserialize)]
61pub struct CacheLayer {
62 pub layout: String,
64 pub manifest: Digest256,
66}
67
68#[derive(Clone, Debug, Serialize, Deserialize)]
70pub struct WorldLayer {
71 pub fs: Digest256,
73 pub env: Digest256,
75 pub procs: Digest256,
77}
78
79#[derive(Clone, Debug, Serialize, Deserialize)]
81pub struct EffectsLayer {
82 pub ledger: Digest256,
84}
85
86#[derive(Clone, Debug, Serialize, Deserialize)]
88pub struct TraceLayer {
89 pub messages: Digest256,
91}
92
93#[cfg(test)]
94mod tests {
95 use super::*;
96
97 #[test]
98 fn round_trip_through_json() {
99 let d = Digest256::of(b"x");
100 let m = Manifest {
101 schema_version: 1,
102 media_type: MEDIATYPE_V1.to_owned(),
103 agent: AgentInfo {
104 kind: "claude-code".into(),
105 version: "0.1.0".into(),
106 fingerprint: "test".into(),
107 },
108 model: ModelLayer {
109 base: d.clone(),
110 diff: d.clone(),
111 },
112 cache: CacheLayer {
113 layout: "paged-batchinvariant-v1".into(),
114 manifest: d.clone(),
115 },
116 world: WorldLayer {
117 fs: d.clone(),
118 env: d.clone(),
119 procs: d.clone(),
120 },
121 effects: EffectsLayer { ledger: d.clone() },
122 trace: TraceLayer {
123 messages: d.clone(),
124 },
125 created_at: Utc::now(),
126 parents: vec![],
127 };
128 let s = serde_json::to_string(&m).unwrap();
129 let back: Manifest = serde_json::from_str(&s).unwrap();
130 assert_eq!(back.schema_version, 1);
131 assert_eq!(back.media_type, MEDIATYPE_V1);
132 }
133}