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,
)
| 201 | /// This is what makes an attestation observable: `validate`/`show`/`verify` |
| 202 | /// consult it instead of always re-lifting the un-attested vault entry. |
| 203 | pub fn load_attestation( |
| 204 | repo: &Repository, |
| 205 | id: &str, |
| 206 | inputs: &LiftInputs, |
| 207 | ) -> CliResult<Attestation> { |
| 208 | // 1) Tracked vault entry (new authoritative source). The body is the pretty |
| 209 | // JSON-LD node (+ a trailing '\n'); parse it straight to a CanonicalNode. |
| 210 | let vpath = attestation_vault_path(repo, id)?; |
| 211 | if let Some(entry) = repo.vault_retrieve(&vpath).map_err(CliError::Repository)? { |
| 212 | let raw = String::from_utf8_lossy(&entry.content_bytes); |
| 213 | match serde_json::from_str::<CanonicalNode>(raw.trim_end()) { |
| 214 | Ok(node) => { |
| 215 | // Staleness: prefer the frontmatter anchor `sourceContentHash`; |
| 216 | // frontmatter_json round-trips as a flat-scalar JSON object. |
| 217 | let recorded = serde_json::from_str::<Value>(&entry.frontmatter_json) |
| 218 | .ok() |
| 219 | .and_then(|v| { |
| 220 | v.get("sourceContentHash") |
| 221 | .and_then(Value::as_str) |
| 222 | .map(str::to_owned) |
| 223 | }); |
| 224 | let current = source_content_hash(inputs); |
| 225 | return Ok(match recorded { |
| 226 | Some(h) if h == current => Attestation::Fresh(Box::new(node)), |
| 227 | _ => Attestation::Stale(Box::new(node)), |
| 228 | }); |
| 229 | } |
| 230 | Err(e) => { |
| 231 | eprintln!("warning: ignoring malformed tracked attestation {vpath}: {e}"); |
| 232 | // fall through to the legacy sidecar |
| 233 | } |
| 234 | } |
| 235 | } |
| 236 | |
| 237 | // 2) Legacy sidecar fallback (pre-upgrade attestations). Probe the |
| 238 | // normalized-id path first (what the current `attest` writes), then the |
| 239 | // raw-arg path an M1a-era build used (it sanitized the RAW CLI arg without |
| 240 | // normalizing), so a sidecar written via `attest 1` / `attest pimo-1` is |
| 241 | // still found after upgrade regardless of the id form (critic #4). |
| 242 | let path = match attested_sidecar_candidates(repo, id)? |
| 243 | .into_iter() |
| 244 | .find(|p| p.exists()) |
| 245 | { |
| 246 | Some(p) => p, |
| 247 | None => return Ok(Attestation::None), |
| 248 | }; |
| 249 | let raw = std::fs::read_to_string(&path).map_err(CliError::Io)?; |
| 250 | let artifact: Value = match serde_json::from_str(&raw) { |
| 251 | Ok(v) => v, |
| 252 | Err(e) => { |
| 253 | eprintln!( |
| 254 | "warning: ignoring unreadable attestation sidecar {}: {e}", |
| 255 | path.display() |
| 256 | ); |
| 257 | return Ok(Attestation::None); |
| 258 | } |
| 259 | }; |
| 260 | let node_val = match artifact.get("node") { |