Load an intent's attestation sidecar and classify it against the current source (`inputs`). A missing sidecar is `None`; a corrupt/malformed one is treated as `None` with a warning (so a read still works on the raw node). This is what makes an attestation observable: `validate`/`show`/`verify` consult it instead of always re-lifting the un-attested vault entry.
(
repo: &Repository,
id: &str,
inputs: &LiftInputs,
)
| 220 | /// This is what makes an attestation observable: `validate`/`show`/`verify` |
| 221 | /// consult it instead of always re-lifting the un-attested vault entry. |
| 222 | pub fn load_attestation( |
| 223 | repo: &Repository, |
| 224 | id: &str, |
| 225 | inputs: &LiftInputs, |
| 226 | ) -> CliResult<Attestation> { |
| 227 | // 1) Tracked vault entry (new authoritative source). The body is the pretty |
| 228 | // JSON-LD node (+ a trailing '\n'); parse it straight to a CanonicalNode. |
| 229 | let vpath = attestation_vault_path(repo, id)?; |
| 230 | if let Some(entry) = repo.vault_retrieve(&vpath).map_err(CliError::Repository)? { |
| 231 | let raw = String::from_utf8_lossy(&entry.content_bytes); |
| 232 | match serde_json::from_str::<CanonicalNode>(raw.trim_end()) { |
| 233 | Ok(node) => { |
| 234 | // Staleness: prefer the frontmatter anchor `sourceContentHash`; |
| 235 | // frontmatter_json round-trips as a flat-scalar JSON object. |
| 236 | let recorded = serde_json::from_str::<Value>(&entry.frontmatter_json) |
| 237 | .ok() |
| 238 | .and_then(|v| { |
| 239 | v.get("sourceContentHash") |
| 240 | .and_then(Value::as_str) |
| 241 | .map(str::to_owned) |
| 242 | }); |
| 243 | let current = source_content_hash(inputs); |
| 244 | return Ok(match recorded { |
| 245 | Some(h) if h == current => Attestation::Fresh(Box::new(node)), |
| 246 | _ => Attestation::Stale(Box::new(node)), |
| 247 | }); |
| 248 | } |
| 249 | Err(e) => { |
| 250 | eprintln!("warning: ignoring malformed tracked attestation {vpath}: {e}"); |
| 251 | // fall through to the legacy sidecar |
| 252 | } |
| 253 | } |
| 254 | } |
| 255 | |
| 256 | // 2) Legacy sidecar fallback (pre-upgrade attestations). Probe the |
| 257 | // normalized-id path first (what the current `attest` writes), then the |
| 258 | // raw-arg path an M1a-era build used (it sanitized the RAW CLI arg without |
| 259 | // normalizing), so a sidecar written via `attest 1` / `attest pimo-1` is |
| 260 | // still found after upgrade regardless of the id form (critic #4). |
| 261 | let path = match attested_sidecar_candidates(repo, id)? |
| 262 | .into_iter() |
| 263 | .find(|p| p.exists()) |
| 264 | { |
| 265 | Some(p) => p, |
| 266 | None => return Ok(Attestation::None), |
| 267 | }; |
| 268 | let raw = std::fs::read_to_string(&path).map_err(CliError::Io)?; |
| 269 | let artifact: Value = match serde_json::from_str(&raw) { |
| 270 | Ok(v) => v, |
| 271 | Err(e) => { |
| 272 | eprintln!( |
| 273 | "warning: ignoring unreadable attestation sidecar {}: {e}", |
| 274 | path.display() |
| 275 | ); |
| 276 | return Ok(Attestation::None); |
| 277 | } |
| 278 | }; |
| 279 | let node_val = match artifact.get("node") { |