Two changes to the watcher path (the always-on daemon every agent
session uses), which previously paid a flat 2s debounce plus a full-tree
scan-diff on every save even though the OS events name the exact files:
1. Adaptive debounce: a pending set of ≤2 files fires after a 300ms
quiet window; ≥3 keeps the full configured window so agent
multi-file bursts coalesce exactly as before. Re-arming preserves
trailing-edge semantics; a user-set CODEGRAPH_WATCH_DEBOUNCE_MS
remains the authoritative upper bound (quick window never exceeds
it, floor 100ms).
2. Scoped sync: watcher-triggered syncs pass their pending paths, and
the reconciler stats exactly those — per-path logic identical to the
full walk (stat pre-filter, hash confirm, the #1240
removal/resurrection flow) — skipping the O(repo) scan and
tracked-load. Strict fallbacks keep the full scan-diff as ground
truth: directory removals (#1285 — the events can't name the
children), empty pending sets (retry paths), and >500-file storms
(branch checkouts, which also self-heal anything event coalescing
dropped). filesChecked counts examined PATHS so a deletion-only
scoped sync can't mimic the #449 lock-unavailable signature.
Measured (warm in-process, the daemon path): dubbo one-file sync work
512→335ms, Swift compiler (27k files) 884→385ms — save-to-fresh-graph
≈0.6-0.7s end-to-end including the quick debounce, from ~2.5-6s
perceived before. Gates: scoped-vs-full dumps byte-identical on dubbo
AND the Swift compiler; watcher suite 30/30 (3 new: scoped pass-through,
dir-removal fallback, quick-fire timing); sync suite 34/34 (4 new
scoped-parity cases incl. delete-resurrection and the lock signature);
full suite 2,696 ×2 with CODEGRAPH_KERNEL_EXPECT=1.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>