Context
Let It Brew stores session state in:
~/Library/Application Support/LetItBrew/sessions
Active sessions use one JSON file per session. Completed sessions are moved into timestamped tombstones under .locks so delayed lifecycle events cannot recreate an ended session. Per-session lock files also remain. Stop Tracking suppressions persist in UserDefaults until the same session produces a new working event.
Completed sessions are excluded from the once-per-second scan, and active records older than 12 hours cannot appear or keep the Mac awake. There is no confirmed memory leak.
The remaining problem is unbounded local storage:
- Tombstones accumulate as unique sessions end.
- A missed terminal event can leave an active record that is read and decoded every second, even after the 12-hour activity TTL excludes it.
- Stop Tracking suppressions can remain after their sessions disappear.
- Stale active records retain local project paths.
This is post-release hardening, not a release blocker.
Proposed behavior
Add conservative session maintenance that:
- Runs after launch and no more than once every 24 hours.
- Removes active records only when both the stored activity and unchanged file age exceed 24 hours.
- Retains terminal tombstones for at least 7 days before removing them.
- Removes Stop Tracking suppressions after 7 days when no matching session remains.
- Uses the existing per-session lock for every deletion.
- Never follows symlinks or removes files outside the exact sessions directory.
- Treats cleanup as best effort so a failure cannot interrupt polling or hook processing.
- Leaves the existing 12-hour activity TTL unchanged.
Do not delete per-session lock files in this change. Unlinking a lock file while another process holds it can split mutual exclusion across different file inodes. A fixed lock-shard design can be considered separately if lock-file growth becomes material.
Acceptance criteria
- Fresh active records are preserved.
- Active records older than the retention threshold are removed safely.
- Tombstones remain during the retention window and are removed afterwards.
- A concurrent newer lifecycle event wins over cleanup.
- Delayed events cannot recreate an eligible completed session.
- Symlinks, directories, malformed files, and unowned entries are never deleted.
- Expired Stop Tracking suppressions are removed only when their session is absent.
- Existing session-ordering and 100-session pressure tests continue to pass.
- Add focused cleanup and concurrency tests.
- Run the complete Swift test suite and a clean Xcode build.
Context
Let It Brew stores session state in:
~/Library/Application Support/LetItBrew/sessionsActive sessions use one JSON file per session. Completed sessions are moved into timestamped tombstones under
.locksso delayed lifecycle events cannot recreate an ended session. Per-session lock files also remain. Stop Tracking suppressions persist in UserDefaults until the same session produces a new working event.Completed sessions are excluded from the once-per-second scan, and active records older than 12 hours cannot appear or keep the Mac awake. There is no confirmed memory leak.
The remaining problem is unbounded local storage:
This is post-release hardening, not a release blocker.
Proposed behavior
Add conservative session maintenance that:
Do not delete per-session lock files in this change. Unlinking a lock file while another process holds it can split mutual exclusion across different file inodes. A fixed lock-shard design can be considered separately if lock-file growth becomes material.
Acceptance criteria