fs/watch debounce only applies to the first event batch

Resolved 💬 0 comments Opened May 27, 2026 by chr1sc2y Closed May 29, 2026

Summary

I found a debounce bug in the app-server fs/watch notification path. The local debounce wrapper in app-server/src/fs_watch.rs stores the next deadline in next_allowance and initializes it only once. After the first event batch is emitted, later batches reuse that stale Instant, so subsequent filesystem changes can be emitted immediately instead of being debounced.

The upstream repository currently only allows collaborators to open pull requests, so I pushed a fix to my fork instead:

Impact

The first burst of file watcher events is coalesced correctly, but later bursts may bypass the intended debounce window. In the app-server path this affects fs/changed notifications, where the debounce interval is currently 200ms.

In practical terms, after the first notification burst, clients can receive noisier and less stable fs/changed notifications than intended.

Fix in the fork

The forked branch:

  • moves the debouncing wrapper into codex-file-watcher as DebouncedWatchReceiver
  • resets the debounce deadline for each event batch
  • uses deterministic path ordering via BTreeSet
  • flushes pending paths if the watcher channel closes during a debounce window
  • updates app-server fs/watch to use the shared debounce receiver

Regression coverage

I added a unit test with a 50ms debounce interval that verifies the second event batch is still debounced:

  1. send a
  2. wait 25ms
  3. send b
  4. receive one batch [a, b]
  5. send c
  6. assert recv() does not return within 25ms
  7. send d
  8. receive one batch [c, d]

Before the fix, step 6 can fail because the stale deadline is already in the past.

I also added coverage for flushing pending debounced paths when the sender closes.

Validation

Ran locally:

just fmt
PATH="$HOME/.cargo/bin:$PATH" just test -p codex-file-watcher
PATH="$HOME/.cargo/bin:$PATH" just test -p codex-app-server fs_watch

Results:

  • codex-file-watcher: 21 tests passed
  • codex-app-server fs_watch: 8 tests passed, 777 skipped
  • the bench-smoke step run by just test also passed

One unrelated existing warning appeared from codex-exec-server/src/client.rs about an unused variable.

View original on GitHub ↗