use crate::digest::Digest256;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
pub const MEDIATYPE_V1: &str = "application/vnd.processfork.image.v1+json";
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Manifest {
pub schema_version: u32,
pub media_type: String,
pub agent: AgentInfo,
pub model: ModelLayer,
pub cache: CacheLayer,
pub world: WorldLayer,
pub effects: EffectsLayer,
pub trace: TraceLayer,
pub created_at: DateTime<Utc>,
#[serde(default)]
pub parents: Vec<Digest256>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct AgentInfo {
pub kind: String,
pub version: String,
pub fingerprint: String,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ModelLayer {
pub base: Digest256,
pub diff: Digest256,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct CacheLayer {
pub layout: String,
pub manifest: Digest256,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct WorldLayer {
pub fs: Digest256,
pub env: Digest256,
pub procs: Digest256,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct EffectsLayer {
pub ledger: Digest256,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct TraceLayer {
pub messages: Digest256,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn round_trip_through_json() {
let d = Digest256::of(b"x");
let m = Manifest {
schema_version: 1,
media_type: MEDIATYPE_V1.to_owned(),
agent: AgentInfo {
kind: "claude-code".into(),
version: "0.1.0".into(),
fingerprint: "test".into(),
},
model: ModelLayer {
base: d.clone(),
diff: d.clone(),
},
cache: CacheLayer {
layout: "paged-batchinvariant-v1".into(),
manifest: d.clone(),
},
world: WorldLayer {
fs: d.clone(),
env: d.clone(),
procs: d.clone(),
},
effects: EffectsLayer { ledger: d.clone() },
trace: TraceLayer {
messages: d.clone(),
},
created_at: Utc::now(),
parents: vec![],
};
let s = serde_json::to_string(&m).unwrap();
let back: Manifest = serde_json::from_str(&s).unwrap();
assert_eq!(back.schema_version, 1);
assert_eq!(back.media_type, MEDIATYPE_V1);
}
}