{"record":{"id":"4d0189f610319248","repo":"astrid-runtime/astrid","slug":"historical-principal-home-receipt-is-missing-or-ch","errorCode":null,"errorMessage":"historical principal home receipt is missing or changed: {}","messagePattern":"historical principal home receipt is missing or changed: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"crates/astrid-kernel/src/legacy_migration_barrier/ledger.rs","lineNumber":458,"sourceCode":"        {\n            return Err(io::Error::new(\n                io::ErrorKind::InvalidData,\n                format!(\n                    \"live principal migration receipt is missing: {}\",\n                    component.name\n                ),\n            ));\n        }\n        if let Some(uid) = principal_component_uid(&component.name)\n            && !directory.contains_uid(uid)\n            && component.name.ends_with(\":home\")\n        {\n            let receipt = home\n                .migrations_dir()\n                .join(format!(\"principal-home-{uid}.json\"));\n            let actual = destination_file_proof(&receipt)?;\n            if actual != component.destination_proof {\n                return Err(io::Error::new(\n                    io::ErrorKind::InvalidData,\n                    format!(\n                        \"historical principal home receipt is missing or changed: {}\",\n                        receipt.display()\n                    ),\n                ));\n            }\n        }\n    }\n    Ok(())\n}\n\nfn non_default_audit_proof(source: Option<&SourceIdentity>) -> io::Result<DestinationProof> {\n    match source {\n        Some(source) if source.present && source.entries == SourceCount::ZERO => {\n            DestinationProof::from_stored(format!(\n                \"verified-empty-v1:source-digest={}\",\n                source.digest","sourceCodeStart":440,"sourceCodeEnd":476,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-kernel/src/legacy_migration_barrier/ledger.rs#L440-L476","documentation":"During validation of an existing migration layout, the library re-computes the content proof of the historical receipt file `principal-home-{uid}.json` in the migrations directory and compares it to the destination proof recorded in the ledger. This error is thrown when that receipt file is either missing or its bytes no longer match the proof recorded at migration time. The library refuses to continue because historical home receipts are immutable evidence that a retired principal's home was migrated; a missing or altered receipt means tampering or corruption.","triggerScenarios":"Calling `resume_existing_layout` (directly or via `existing_layout_requires_receipts_for_live_immutable_components`) when a ledger entry names a principal UID that is no longer present in the principal directory, the entry ends with ':home', and the file `<home>/migrations/principal-home-{uid}.json` does not exist or its content hash differs from `component.destination_proof` (e.g. the file was edited, regenerated by a different tool version, or replaced with an empty/absent file).","commonSituations":"A user or backup-restore tool deleted or overwrote files under the migrations directory; a checkout/restore restored a stale version of the receipt; an operator 'cleaned up' receipts of retired principals; two different library versions produced different canonical receipt bytes.","solutions":["Restore the original `principal-home-{uid}.json` receipt file (from backup) so its content hashes to the ledger's `destination_proof`.","If the receipt is legitimately gone and the principal is fully retired, re-run the migration flow from a clean state so the ledger and receipts are rebuilt consistently.","Do not hand-edit the receipt or the ledger: recompute both together via the library's write path (`write_ledger`) rather than patching one side.","Verify no background sync/backup tool is rewriting the migrations directory and exclude it from mutation."],"exampleFix":"// before: receipt manually edited / missing\n$ rm migrations/principal-home-01HABC....json\n// after: restore from backup so proof matches ledger\n$ cp backup/migrations/principal-home-01HABC....json migrations/\n$ astrid migrate resume  # validation now passes","handlingStrategy":"validation","validationCode":"// Before resuming, verify every historical home receipt exists and matches the ledger proof\nfn historical_home_receipts_intact(home: &Path, ledger: &MigrationLedger) -> io::Result<()> {\n    for c in &ledger.components {\n        if let Some(uid) = c.name.strip_prefix(\"principal:\")\n            .and_then(|r| r.split(':').next())\n            .filter(|_| c.name.ends_with(\":home\")) {\n            let receipt = home.join(\"migrations\").join(format!(\"principal-home-{uid}.json\"));\n            if !receipt.exists() {\n                return Err(io::Error::new(io::ErrorKind::NotFound, format!(\"missing receipt {receipt:?}\")));\n            }\n        }\n    }\n    Ok(())\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Treat the migrations directory as immutable; back it up together with the ledger","Never delete receipts for retired principals during 'cleanup'","Exclude the migrations directory from tools that rewrite/sync files","Restore from the same backup generation as the ledger so proofs stay consistent"],"tags":["migration","integrity-check","file-not-found","rust"],"backgroundTag":"checksum-mismatch","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}