Walk the graph nodes and build: - `files` — per-file `FileEntry` list (from `Commitment` nodes) - `dev_atomic` — the Sherpa-specific metadata object
(graph: &ProvenanceGraph)
| 239 | /// - `files` — per-file `FileEntry` list (from `Commitment` nodes) |
| 240 | /// - `dev_atomic` — the Sherpa-specific metadata object |
| 241 | fn build_files_and_metadata(graph: &ProvenanceGraph) -> (Vec<FileEntry>, Value) { |
| 242 | // Collect nodes by kind |
| 243 | let goal_node = graph |
| 244 | .nodes |
| 245 | .iter() |
| 246 | .find(|n| n.kind == ProvenanceNodeKind::Goal); |
| 247 | |
| 248 | let commitment_nodes: Vec<_> = graph |
| 249 | .nodes |
| 250 | .iter() |
| 251 | .filter(|n| n.kind == ProvenanceNodeKind::Commitment) |
| 252 | .collect(); |
| 253 | |
| 254 | let execution_nodes: Vec<_> = graph |
| 255 | .nodes |
| 256 | .iter() |
| 257 | .filter(|n| n.kind == ProvenanceNodeKind::Execution) |
| 258 | .collect(); |
| 259 | |
| 260 | let verification_node = graph |
| 261 | .nodes |
| 262 | .iter() |
| 263 | .find(|n| n.kind == ProvenanceNodeKind::Verification); |
| 264 | |
| 265 | // Parse detail JSON strings into typed structs (best-effort — None on any |
| 266 | // parse error so a malformed node never blocks the export). |
| 267 | let goal_detail: Option<GoalDetail> = goal_node |
| 268 | .and_then(|n| n.detail.as_deref()) |
| 269 | .and_then(|s| serde_json::from_str(s).ok()); |
| 270 | |
| 271 | let commitment_details: Vec<CommitmentDetail> = commitment_nodes |
| 272 | .iter() |
| 273 | .filter_map(|n| n.detail.as_deref()) |
| 274 | .filter_map(|s| serde_json::from_str(s).ok()) |
| 275 | .collect(); |
| 276 | |
| 277 | let verification_detail: Option<VerificationDetail> = verification_node |
| 278 | .and_then(|n| n.detail.as_deref()) |
| 279 | .and_then(|s| serde_json::from_str(s).ok()); |
| 280 | |
| 281 | // Extract turn/session identifiers from GoalDetail |
| 282 | let session_id = goal_detail |
| 283 | .as_ref() |
| 284 | .map(|g| g.session_id.as_str()) |
| 285 | .unwrap_or(&graph.session_id) |
| 286 | .to_string(); |
| 287 | |
| 288 | let turn_id = goal_detail |
| 289 | .as_ref() |
| 290 | .map(|g| g.intent_turn_id as u64) |
| 291 | .unwrap_or(0); |
| 292 | |
| 293 | let model = goal_detail |
| 294 | .as_ref() |
| 295 | .map(|g| g.model.as_str()) |
| 296 | .unwrap_or("") |
| 297 | .to_string(); |
| 298 |