Import every provenance graph and attestation carried by `pack`. Shared by `atomic pull` and `atomic clone`: both receive the same `SyncPack` shape from `/code`, and both must land the sidecars or the cloned/pulled repository cannot answer provenance queries until some later pull happens to carry them again. Failures warn and continue — valid change ingestion is never blocked by a corrupt sidecar
(repo: &Repository, pack: &SyncPack)
| 48 | /// later pull happens to carry them again. Failures warn and continue — |
| 49 | /// valid change ingestion is never blocked by a corrupt sidecar. |
| 50 | pub fn import_sidecars(repo: &Repository, pack: &SyncPack) -> SidecarStats { |
| 51 | let mut stats = SidecarStats::default(); |
| 52 | |
| 53 | for (key, data) in objects_by_key(pack, ObjectFamily::Provenance) { |
| 54 | let Some(hash) = Hash::from_base32(key.as_bytes()) else { |
| 55 | continue; |
| 56 | }; |
| 57 | let already_present = repo.has_provenance_graph(&hash); |
| 58 | match atomic_core::change::ProvenanceGraph::deserialize(&data) { |
| 59 | Ok((graph, computed)) => { |
| 60 | if computed != hash { |
| 61 | print_warning(&format!( |
| 62 | "Provenance {} failed hash verification — skipped", |
| 63 | short(&key) |
| 64 | )); |
| 65 | continue; |
| 66 | } |
| 67 | // Always replay repository registration, even when the object |
| 68 | // file exists. This repairs metadata after an interrupted save |
| 69 | // and is safe because node/dependency writes are idempotent. |
| 70 | match repo.save_provenance_graph(&graph) { |
| 71 | Ok(_) if !already_present => stats.provenance += 1, |
| 72 | Ok(_) => {} |
| 73 | Err(e) => print_warning(&format!( |
| 74 | "Failed to register provenance {}: {}", |
| 75 | short(&key), |
| 76 | e |
| 77 | )), |
| 78 | } |
| 79 | } |
| 80 | Err(e) => print_warning(&format!("Corrupt provenance {}: {}", short(&key), e)), |
| 81 | } |
| 82 | } |
| 83 | |
| 84 | for (key, data) in objects_by_key(pack, ObjectFamily::Attest) { |
| 85 | let Some(hash) = Hash::from_base32(key.as_bytes()) else { |
| 86 | continue; |
| 87 | }; |
| 88 | let already_present = repo.has_attestation(&hash); |
| 89 | match atomic_core::change::Attestation::deserialize(&data) { |
| 90 | Ok((attestation, computed)) => { |
| 91 | if computed != hash { |
| 92 | print_warning(&format!( |
| 93 | "Attestation {} failed hash verification — skipped", |
| 94 | short(&key) |
| 95 | )); |
| 96 | continue; |
| 97 | } |
| 98 | // Re-register existing files as well, repairing pristine |
| 99 | // metadata left incomplete by an earlier interrupted import. |
| 100 | match repo.save_attestation(&attestation) { |
| 101 | Ok(_) if !already_present => stats.attestations += 1, |
| 102 | Ok(_) => {} |
| 103 | Err(e) => print_warning(&format!( |
| 104 | "Failed to register attestation {}: {}", |
| 105 | short(&key), |
| 106 | e |
| 107 | )), |