feat(watch): replace 250 ms watch polling with evented filesystem observation#531
Draft
elucid wants to merge 12 commits into
Draft
feat(watch): replace 250 ms watch polling with evented filesystem observation#531elucid wants to merge 12 commits into
elucid wants to merge 12 commits into
Conversation
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
Member
|
Nice |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Replace
--watch's 250 ms Git-backed polling loop with a hybrid evented observer. Filesystem events are change hints, the existing authoritative watch signature verifies them, and a slow safety poll covers missed events and unusual filesystems. Watch remains continuous—no user interaction is required to keep a passively viewed session fresh—while idle sessions stop spawning roughly four Git subprocesses per second.PR #520 identified the same important cost problem and proposed pausing polling after UI inactivity. Thank you to its contributor for surfacing the unattended-session impact. This PR takes a different approach that preserves passive freshness and keeps watch infrastructure independent of keyboard and mouse activity.
Scalability fix
The original evented implementation still asked Chokidar to traverse every worktree directory. On dependency-heavy repositories that could concentrate substantial CPU and watcher setup at startup. The revised policy bounds that work by platform:
fs.watchtree registration per worktree target. Native event paths are filtered against Git-derived ignored roots before they can trigger a signature check.git ls-files --others --ignored --exclude-standard --directory -z. This honors nested ignore files, negations,.git/info/exclude, global excludes, and forced tracked files without hard-coding names such asnode_modulesordist.ENOSPCandEMFILEuse the same safe fallback.Git metadata-only observation is not substituted for worktree observation: ordinary tracked saves, atomic replacements, and relevant untracked creation remain event-covered. Linked worktrees continue to observe worktree content, per-worktree metadata, and shared Git metadata.
How it works
src/core/watchPlan.ts) describe backend-independent directory-tree and exact-entry targets. Direct files and patches watch parent entries, so atomic editor saves survive.src/core/vcs/git.ts) resolve the worktree, Git dir, common Git dir, and Git-derived ignored roots. Working-tree reviews observe content plus metadata; staged/range/show/stash reviews observe only relevant metadata.src/core/watchController.ts) serializes signature checks and refreshes, coalesces in-flight events, retains retryable baselines after failures, applies the startup deadline, and owns healthy/degraded safety checks.src/core/watchObserver.ts) selects native recursive watching on macOS/Windows and Git-pruned Chokidar on portable platforms.src/ui/hooks/useWatchedInput.ts) declaratively supplies reload context; the previous 250 mssetIntervalpath is removed.Timing policies
Verification
Deterministic and integration coverage
dist/hunkPTY watch regressions: 2 passed.check:pack, andcheck:prebuilt-packpass.bun run install:binrebuilt and installed the branch binary.The repository-wide unit/package run reached 1,071 passing tests; session integration cases are blocked on this macOS host by the known BSD
scriptincompatibility with the harness's-foption. The same incompatibility blockstest:tty-smoke. Focused watch PTY coverage passes from both source and the compiled binary.Real modem-scale macOS smoke
Tested the installed binary in
~/DEV/modem/modem, a checkout with 25,967 directories and a large ignored dependency tree:node_modulesThe checkout is a normal worktree, so linked-worktree behavior was exercised by the focused real-Git tests and passive PTY regression instead.
Linux evidence
On Ubuntu 6.8 x86_64 with Bun 1.3.14 and
max_user_watches=61504, the dependency-heavy synthetic fixture previously measured:fs.watch: 0.33 s / ~46 MB RSS, but roughly 24k inotify registrations per Hunk processThat resource behavior is why Linux uses Git-pruned Chokidar rather than native recursion, while macOS/Windows use bounded native recursive registration.
Scope
Notes
chokidar@^4.0.3as a runtime dependency and includes it in npm, compiled, and Nix packaging..changeset/calm-files-watch.mdand.changeset/native-recursive-watch.md.