4b20697bb1
Root cause: manual sync triggered from the UI had no cooldown set in
FileWatchService, so file writes during any manual sync fired FileObserver
→ debounce → another sync → loop.
Fix: startSyncMonitor() subscribes to getWorkInfosByTagFlow("sync_$pairId")
and watches ALL sync work for each pair — manual, catchup, onchange — via
the tag that SyncWorker.buildOneTimeRequest() always adds.
- When any sync is RUNNING or ENQUEUED: cooldown extended to now+120s
- When sync transitions from running to finished: 60s settle cooldown
- Monitor job stored in syncMonitorJobs map and cancelled in clearWatchers()
This means no matter what triggers a sync, FileObserver events from the
resulting file writes are always suppressed until the folder settles.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>