While following up on #867, a real macOS user (Mike, Sep 2026) reported that his fully-migrated opensak.json disappeared entirely after upgrading to 1.19.0 — not just the stale-path issue #867 fixed, but the entire settings file (username, home coordinates, etc.), even though nothing in migrate_macos_default_paths() should delete it outright.
Mike's own diagnosis (his words): "OpenSAK deleted [opensak.json] when the new version ran." He was only able to recover fully because he happened to have a Time Machine backup that included the hidden ~/.local/share/opensak/ path — his other backup methods (Syncthing, iDrive) didn't cover it.
We have not yet root-caused why the migrated file went missing — the file-move logic itself was traced step-by-step and behaves correctly for his scenario in isolation, so something after a successful migration is the more likely culprit (e.g. a second run under different conditions). That investigation is ongoing and needs real macOS hardware to pursue properly (see #867's discussion).
What this issue covers
Independent of root-causing the disappearance, there's no reason a user should ever need a system backup to recover from it: the migration already has the original file in hand for a brief moment before it does anything destructive. This issue is about making that moment count.
Fix implemented
Before migrate_macos_default_paths() moves or touches anything, it now leaves a permanent copy of the original opensak.json behind, in place, in the old directory (opensak.json.pre-825-migration-backup) — explicitly excluded from the move/cleanup logic so it can never be swept away by the same process, or by whatever happens afterward. Idempotent (won't overwrite an existing backup on a second/interrupted run). Scoped to opensak.json only (typically <1MB) — not database files, which are already handled by the existing move logic and can be far larger.
Testing
Reproduced Mike's scenario end-to-end, including simulating the migrated copy being lost afterward — confirmed the backup survives untouched
Full unit suite: 3270 passed; mypy clean across 229 files
4 new dedicated tests for the backup helper, plus one existing test updated (it previously asserted the old directory gets fully removed once empty — that's now intentionally no longer the case, since the backup keeps it non-empty)
Not covered by this issue — tracked separately once we have real hardware to investigate: the actual root cause of why the settings file disappeared for Mike in the first place.
While following up on #867, a real macOS user (Mike, Sep 2026) reported that his fully-migrated opensak.json disappeared entirely after upgrading to 1.19.0 — not just the stale-path issue #867 fixed, but the entire settings file (username, home coordinates, etc.), even though nothing in migrate_macos_default_paths() should delete it outright.
Mike's own diagnosis (his words): "OpenSAK deleted [opensak.json] when the new version ran." He was only able to recover fully because he happened to have a Time Machine backup that included the hidden ~/.local/share/opensak/ path — his other backup methods (Syncthing, iDrive) didn't cover it.
We have not yet root-caused why the migrated file went missing — the file-move logic itself was traced step-by-step and behaves correctly for his scenario in isolation, so something after a successful migration is the more likely culprit (e.g. a second run under different conditions). That investigation is ongoing and needs real macOS hardware to pursue properly (see #867's discussion).
What this issue covers
Independent of root-causing the disappearance, there's no reason a user should ever need a system backup to recover from it: the migration already has the original file in hand for a brief moment before it does anything destructive. This issue is about making that moment count.
Fix implemented
Before migrate_macos_default_paths() moves or touches anything, it now leaves a permanent copy of the original opensak.json behind, in place, in the old directory (opensak.json.pre-825-migration-backup) — explicitly excluded from the move/cleanup logic so it can never be swept away by the same process, or by whatever happens afterward. Idempotent (won't overwrite an existing backup on a second/interrupted run). Scoped to opensak.json only (typically <1MB) — not database files, which are already handled by the existing move logic and can be far larger.
Testing
Reproduced Mike's scenario end-to-end, including simulating the migrated copy being lost afterward — confirmed the backup survives untouched
Full unit suite: 3270 passed; mypy clean across 229 files
4 new dedicated tests for the backup helper, plus one existing test updated (it previously asserted the old directory gets fully removed once empty — that's now intentionally no longer the case, since the backup keeps it non-empty)
Not covered by this issue — tracked separately once we have real hardware to investigate: the actual root cause of why the settings file disappeared for Mike in the first place.