{"record":{"id":"dc69b1acd1d9bff6","repo":"astrid-runtime/astrid","slug":"legacy-audit-retirement-source-is-outside-the-defa","errorCode":null,"errorMessage":"legacy audit retirement source is outside the default principal audit path","messagePattern":"legacy audit retirement source is outside the default principal audit path","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-kernel/src/legacy_migration_barrier/host_fs.rs","lineNumber":450,"sourceCode":"                format!(\n                    \"unsupported legacy audit source {}; only the default principal source is admitted\",\n                    audit_source.display()\n                ),\n            ));\n        }\n    }\n    Ok(default_source_present)\n}\n\n/// Retire the imported default audit tree through a private staging rename.\n/// The rename makes interrupted deletion resumable, while every pre/post\n/// traversal revalidates no-follow, regular-entry, device, and mount bounds.\n#[cfg(not(unix))]\npub(super) fn retire_legacy_audit_dir(home: &AstridHome, source: &Path) -> io::Result<()> {\n    let retired = home.migrations_dir().join(\"audit-principal-home.retired\");\n    let expected = home.principal_home(&PrincipalId::default()).audit_dir();\n    if source != expected {\n        return Err(io::Error::new(\n            io::ErrorKind::InvalidInput,\n            \"legacy audit retirement source is outside the default principal audit path\",\n        ));\n    }\n    astrid_core::platform_fs::verify_no_redirects(source.parent().ok_or_else(|| {\n        io::Error::new(\n            io::ErrorKind::InvalidInput,\n            \"legacy audit source has no parent\",\n        )\n    })?)?;\n    astrid_core::platform_fs::ensure_private_directory(&home.migrations_dir())?;\n    astrid_core::platform_fs::verify_no_redirects(&home.migrations_dir())?;\n    match fs::symlink_metadata(source) {\n        Ok(metadata) if metadata.file_type().is_symlink() || !metadata.is_dir() => {\n            return Err(io::Error::new(\n                io::ErrorKind::InvalidData,\n                format!(\n                    \"legacy audit source is not a regular directory: {}\",","sourceCodeStart":432,"sourceCodeEnd":468,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-kernel/src/legacy_migration_barrier/host_fs.rs#L432-L468","documentation":"retire_legacy_audit_dir only retires the exact default principal audit directory (home.principal_home(&PrincipalId::default()).audit_dir()). If the caller passes any other source path, the function rejects it with InvalidInput before performing any traversal or deletion, as a guard against retiring arbitrary directories. The message carries no path since it is a programmatic argument check.","triggerScenarios":"Calling retire_legacy_audit_dir(home, source) (via migrate_legacy_audit) with a source path that is not byte-identical to the default principal audit dir — e.g. a trailing slash difference, a non-default principal's audit dir, or a hand-constructed path (host_fs.rs:449).","commonSituations":"Custom migration scripts passing their own path; tests exercising retirement with a fabricated source; path normalization differences (//, trailing separator, symlinked parents) between the caller's path and the computed expected path.","solutions":["Pass exactly home.principal_home(&PrincipalId::default()).audit_dir() as the source argument.","Normalize the source path (canonicalize or construct it from AstridHome APIs) instead of assembling it from strings.","If migrating a non-default principal's audit dir, do not use this API — it only supports the default principal.","Check for trailing slashes or duplicated separators in the path you pass and remove them."],"exampleFix":"// before\nretire_legacy_audit_dir(&home, &Path::from(\"/home/u/.astrid/principals/other/audit\"))?;\n// after\nlet source = home.principal_home(&PrincipalId::default()).audit_dir();\nretire_legacy_audit_dir(&home, &source)?;","handlingStrategy":"type-guard","validationCode":"fn source_is_default(home: &AstridHome, source: &std::path::Path) -> bool {\n    source == home.principal_home(&PrincipalId::default()).audit_dir()\n}","typeGuard":"fn expected_retirement_source(home: &AstridHome) -> std::path::PathBuf {\n    home.principal_home(&PrincipalId::default()).audit_dir()\n}","tryCatchPattern":"match result {\n    Err(e) if e.kind() == std::io::ErrorKind::InvalidInput && e.to_string().contains(\"outside the default principal audit path\") => {\n        // rebuild the path via AstridHome APIs and retry\n    }\n    other => other?,\n}","preventionTips":["Always derive the source from AstridHome accessors, never from raw strings.","Avoid path normalization drift (trailing slashes, .., symlinked parents) when passing paths.","Only the default principal's audit dir is retirable — route other paths elsewhere."],"tags":["migration","api-misuse","path","audit"],"backgroundTag":"invalid-argument-value","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"}