Problem
FileWatcher.flush() currently treats any resolved syncFn() result as a successful sync.
However, CodeGraph.sync() can return an all-zero result when it fails to acquire the cross-process file lock, instead
of throwing. In that case, the watcher still clears pendingFiles and considers the edit handled.
This can incorrectly mark locally edited files as fresh even though they were never indexed.
Impact
A possible sequence:
- watcher sees a file change and adds it to pendingFiles
- syncFn() runs
- another process holds the CodeGraph file lock
- CodeGraph.sync() returns a zero-result no-op instead of throwing
- FileWatcher.flush() treats it as success and clears the pending entry
- the graph remains stale until some later file event happens
Expected behavior
If there are pending files and syncFn() resolves with:
- filesChanged === 0
- durationMs === 0
then the watcher should treat that sync as no progress, not success:
- keep pendingFiles
- do not call onSyncComplete
- let the existing retry path reschedule sync
Suggested fix
A minimal fix is in src/sync/watcher.ts only:
- detect pendingFiles.size > 0 + zero-result sync
- treat it as unfinished
- preserve pendingFiles
- rely on the existing retry scheduling in finally
Notes
The shared MCP daemon in v0.9.5 reduces one common source of lock contention, but does not fully eliminate this bug
because other writers can still contend for the lock.
Problem
FileWatcher.flush() currently treats any resolved syncFn() result as a successful sync.
However, CodeGraph.sync() can return an all-zero result when it fails to acquire the cross-process file lock, instead
of throwing. In that case, the watcher still clears pendingFiles and considers the edit handled.
This can incorrectly mark locally edited files as fresh even though they were never indexed.
Impact
A possible sequence:
Expected behavior
If there are pending files and syncFn() resolves with:
then the watcher should treat that sync as no progress, not success:
Suggested fix
A minimal fix is in src/sync/watcher.ts only:
Notes
The shared MCP daemon in v0.9.5 reduces one common source of lock contention, but does not fully eliminate this bug
because other writers can still contend for the lock.