{"record":{"id":"689f3d930effadb1","repo":"astrid-runtime/astrid","slug":"frozen-summary-deserializes","errorCode":null,"errorMessage":"frozen SUMMARY deserializes","messagePattern":"frozen SUMMARY deserializes","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-gateway/src/routes/sessions_tests.rs","lineNumber":587,"sourceCode":"    assert_eq!(p[\"include_archived\"], false);\n}\n\n#[test]\nfn session_summary_round_trips_frozen_summary() {\n    // The frozen SUMMARY shape, fully populated, then mostly-null.\n    let full = serde_json::json!({\n        \"session_id\": \"default\",\n        \"title\": \"Planning\",\n        \"preview\": \"first user message\",\n        \"last_message_preview\": \"latest line\",\n        \"message_count\": 12,\n        \"created_at\": 1_719_000_000_i64,\n        \"updated_at\": 1_719_000_100_i64,\n        \"archived\": false,\n        \"parent_session_id\": \"old-id\",\n        \"meta\": \"{\\\"k\\\":1}\"\n    });\n    let s: SessionSummary = serde_json::from_value(full).expect(\"frozen SUMMARY deserializes\");\n    assert_eq!(s.session_id, \"default\");\n    assert_eq!(s.title.as_deref(), Some(\"Planning\"));\n    assert_eq!(s.last_message_preview.as_deref(), Some(\"latest line\"));\n    assert!(!s.archived);\n    assert_eq!(s.meta.as_deref(), Some(\"{\\\"k\\\":1}\"));\n\n    // Minimal/sparse element: only the always-present fields.\n    let sparse = serde_json::json!({\n        \"session_id\": \"fresh\",\n        \"message_count\": 0,\n        \"archived\": true\n    });\n    let s: SessionSummary = serde_json::from_value(sparse).expect(\"sparse SUMMARY deserializes\");\n    assert!(s.title.is_none());\n    assert!(s.preview.is_none());\n    assert!(s.last_message_preview.is_none());\n    assert!(s.created_at.is_none());\n    assert!(s.meta.is_none());","sourceCodeStart":569,"sourceCodeEnd":605,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-gateway/src/routes/sessions_tests.rs#L569-L605","documentation":"session_summary_round_trips_frozen_summary builds a full JSON object and deserializes it into SessionSummary with expect(\"frozen SUMMARY deserializes\"). The test pins the frozen wire format of SessionSummary, so a panic means a serde field/type change broke backward compatibility with the frozen shape.","triggerScenarios":"Changing SessionSummary fields or types (session_id, title, last_message_preview, message_count, created_at, updated_at, archived, parent_session_id, meta) so the full frozen JSON no longer deserializes.","commonSituations":"Renaming a field or making an optional field required; changing i64 timestamps to another type; adding a new required field absent from the frozen fixture; serde attribute changes (deny_unknown_fields, default removal).","solutions":["Revert the serde change to SessionSummary or update the frozen fixture deliberately with a documented schema change","Make the new field #[serde(default)] if it must be optional for old payloads","Run the test with the serde error message to identify the offending field","If the format change is intended, update this round-trip test and any frozen-shape docs together"],"exampleFix":"// before\npub meta: String,\n// after\npub meta: Option<String>, // plus #[serde(default)] to keep frozen shape compatible","handlingStrategy":"try-catch","validationCode":"assert!(full.get(\"session_id\").map_or(false, |v| v.is_string()),\n    \"frozen summary fixture malformed\");","typeGuard":"fn has_required_summary(v: &serde_json::Value) -> bool {\n    [\"session_id\", \"message_count\", \"archived\"]\n        .iter().all(|k| v.get(k).is_some())\n}","tryCatchPattern":"let s: SessionSummary = serde_json::from_value(full)\n    .unwrap_or_else(|e| panic!(\"frozen SUMMARY deserializes: {e}\"));","preventionTips":["Keep SessionSummary fields Option/default so old payloads keep deserializing","Update frozen fixtures only alongside a documented wire-format change","Run the round-trip tests in CI on every serde attribute change"],"tags":["tests","serde","schema","round-trip"],"backgroundTag":"json-unmarshal-failed","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}