Context
RecoveryV1 path validation failed the same ownership invariant across four implementation rounds. On the current APFS volume, casing-only and firmlink spellings can identify the same directory while Foundation preserves the raw spelling for a missing leaf. String-based standardized/resolved URL overlap checks therefore cannot authorize creation or deletion.
This issue supersedes the rejected Wave 0 RecoveryV1 implementation on branch codex/recovery-wave-0-1-pr at c83e499. That branch remains evidence only and must not be merged.
Parent: #96
Prerequisite: reviewed Wave 3 runtime/process close authority (#106)
Blocks: first Wave 4 GRDB database open (#102)
Consumed by: Wave 5 login and account recovery (#97, #107, #108)
Required owner model
- RecoveryEnvironmentPlan prepares one PreparedRecoveryEnvironment.
- DirectoryCapability walks each component from a trusted descriptor using openat with O_DIRECTORY, O_NOFOLLOW, and O_CLOEXEC.
- Identity and ancestry use fstat volume/device/inode facts; canonical URLs are diagnostics only.
- Creation, atomic replacement, reads, and recursive removal are descriptor-relative.
- Managed subtrees reject mount crossing, unknown permissions, shared ownership, inherited ACL policy violations, and unexpected identity changes.
- Existing explicit homes are never chmod-mutated; unsafe homes fail closed.
- Process launch and GRDB open perform last-moment capability revalidation. The threat model must state the residual limitation of path-only external consumers and whether same-UID adversaries are excluded.
Login staging follow-up
LoginStagingLease belongs to Wave 5 after throwing runtime close and LoginSession exist. It owns prepared staging, runtime/client/writer tasks, one joined close result, and descriptor-safe removal. RegistryV2 auth persistence owns pending authentication and cleanup debt through a versioned atomic JSON manifest; RecoveryEnvironment must not create a second manifest source of truth.
Acceptance criteria
- Case-only aliases, Unicode aliases, firmlink spellings, reverse ancestor overlap, symlink components, mount crossing, wrong UID, shared mode, and ACL violations fail before unsafe mutation.
- Missing-leaf creation is parent-descriptor anchored and reopens/revalidates the created leaf.
- Identity swaps between prepare and Process/GRDB handoff are rejected.
- Cleanup removes only a manifested, identity-matching, unreferenced owned lease.
- No arbitrary source/destination URL remains in registry or cleanup APIs.
- The public makeLiveStore declarations remain source-compatible.
- Tests use temporary/injected filesystem seams and never mutation-capable global paths such as /Library.
Sequencing
Land the descriptor capability core after Wave 3 and before Wave 4 opens review-history.sqlite. Add LoginStagingLease, RegistryV2 pending/debt, and startup reconciliation in Wave 5.
Context
RecoveryV1 path validation failed the same ownership invariant across four implementation rounds. On the current APFS volume, casing-only and firmlink spellings can identify the same directory while Foundation preserves the raw spelling for a missing leaf. String-based standardized/resolved URL overlap checks therefore cannot authorize creation or deletion.
This issue supersedes the rejected Wave 0 RecoveryV1 implementation on branch codex/recovery-wave-0-1-pr at c83e499. That branch remains evidence only and must not be merged.
Parent: #96
Prerequisite: reviewed Wave 3 runtime/process close authority (#106)
Blocks: first Wave 4 GRDB database open (#102)
Consumed by: Wave 5 login and account recovery (#97, #107, #108)
Required owner model
Login staging follow-up
LoginStagingLease belongs to Wave 5 after throwing runtime close and LoginSession exist. It owns prepared staging, runtime/client/writer tasks, one joined close result, and descriptor-safe removal. RegistryV2 auth persistence owns pending authentication and cleanup debt through a versioned atomic JSON manifest; RecoveryEnvironment must not create a second manifest source of truth.
Acceptance criteria
Sequencing
Land the descriptor capability core after Wave 3 and before Wave 4 opens review-history.sqlite. Add LoginStagingLease, RegistryV2 pending/debt, and startup reconciliation in Wave 5.