Three additional fixes found via live device logs:
1. Startup race window: FileObserver fires immediately after
startWatching() before catchupScan coroutine runs, starting a 5s
debounce with cooldown=0. Fixed by setting a 15s startup cooldown
in watchPath() BEFORE calling watchDirRecursive.
2. Stale debounce bypass: debounce job started with cooldown=0 fires
5s later even after catchupScan has already set cooldown and started
a catchup sync. Fixed by re-checking cooldown after the 5s delay
and aborting if already active.
3. Debounce not cancelled by catchupScan: if a debounce was queued
before catchupScan ran, catchupScan would enqueue a catchup sync
AND the old debounce would fire 5s later enqueuing a second sync.
Fixed by cancelling pending debounce in catchupScan before enqueue.
Icon: four thick arcs (blue/red/green/orange) in a 4-way pinwheel
with over/under ordering. White sync-arrow circle at center.
Pure black background.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>