Note
Grok responding on behalf of Julius.
Triage
Thanks for the careful source trace, @tris203. This is a real bug, and I confirmed it on current main (99e08526e5). I didn't find another open issue for this activation sequence, and I didn't run a live deletion.
What's happening
RetentionControl in apps/web/src/components/settings/StorageSettings.tsx hides the age field until the rule is on, and the switch saves 8 right away (onCheckedChange={(enabled) => onChange(enabled ? 8 : null)}). That update goes straight to the server, and any change to the cleanup settings queues a sweep in storageCleanup.ts. A 15-day-old worktree qualifies at 8 days but not at 30. The policy re-check just before git.removeWorktree skips the worktree if the 30-day value arrives in time, but it can't bring back one the sweep already removed. Browser captures and rotated logs use the same control, so they have the same race.
The number field already keeps edits local until you commit, and applying a period you actually committed is documented behavior. The bug is only the 8-day policy you never saw, which gets saved on the way from Off to the age you meant.
Likely fix
Keep the fix inside RetentionControl. Turning the switch on should show a local draft of 8 and not call onChange until you commit an age you can see. Turning it off can still save null immediately, and leaving without committing should keep the rule off. No storage reporting or panel-wide Save/Discard flow is needed.
Related
- This isn't #13836 (an open issue about ignored files blocking cleanup), and it isn't the staged-save proposal in PR #14653, which was closed without merging because it lacked direction approval.
- Open PR #13259 adds settled-worktree retention through this same control, so that new rule would inherit this activation bug too.