Repository navigation
fix: retarget the dev-server watcher instead of replacing it - #622
Mohamed Mansour (mohamedmansour) merged 2 commits into
Conversation
Press replaced its file watcher after every rebuild. Dropping the old notify-debouncer-mini watcher discards events still inside its 50 ms debounce window, and the replacement only sees writes made after it registers. A source edit landing just before the swap was lost, so markdown-state-serve.test.ts intermittently waited 30 s for a rebuild that never came. - Add WatcherHandle::retarget, which updates subscriptions in place. Unchanged recursive roots and the debouncer stay registered; content baselines restart as for a new watcher. - Re-register exact-file parents on every retarget so deleted and recreated generated directories are watched again. They use a separate notify backend so FSEvents stream restarts cannot interrupt source roots. - Register remaining paths when one cannot be watched, then report the first failure. - Retarget the Press watcher after each rebuild instead of replacing it. Validation: on main, an edit just before a swap was lost 5/5 times on both Windows and Linux. The e2e test failed 3/60 under parallel CPU load; with the fix it passed 210/210, and the full Press suite passed 118/118. cargo xtask check passes. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🟡 Changes recommended
Root deduplication can leave a still-requested child directory unwatched when its ancestor cannot be registered, breaking best-effort recovery.
1 open issue
What changed in this PR
This PR fixes lost dev-server rebuild events by letting Press update a running watcher instead of replacing it after each build.
Changes:
- Add in-place watch retargeting while preserving the debouncer and unchanged recursive roots.
- Separate exact-file watches from recursive-root watches and add subscription bookkeeping.
- Update Press integration, regression tests, and watcher API documentation.
| File | Description |
|---|---|
crates/webui-press/src/serve.rs |
Retargets the existing watcher and tests edits during refresh. |
crates/webui-dev-server/src/watch.rs |
Adds retargeting, shared event filtering, and regression tests. |
crates/webui-dev-server/src/watch_subscriptions.rs |
Manages subscription changes, root deduplication, and registration failures. |
crates/webui-dev-server/src/watch_backend.rs |
Separates recursive-root and exact-file notification backends. |
crates/webui-dev-server/src/lib.rs |
Exports WatchTargets and documents retargeting. |
crates/webui-dev-server/README.md |
Explains why callers should retarget rather than replace watchers. |
Review effort: Balanced
Retarget dropped a requested root nested inside another requested root. When the ancestor could not be watched, the nested root was left unwatched, contradicting retarget's best-effort contract. Register requested roots ancestors-first and fall back to nested roots when none of their requested ancestors can be watched. Release whatever a failed recursive registration left behind, so a failed root leaves no untracked subscriptions. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
mddinbox
left a comment
There was a problem hiding this comment.
Target: 6ceba63...cf9179d | Coverage: complete; CI: Desktop npm native (x86_64-apple-darwin) and Desktop npm native (x86_64-pc-windows-msvc) in progress
Challenge: 1 revised after independent challenge; rechecked on pinned code | Compatibility: none
Action: COMMENT | Pending explicit-file changes can leave served output stale.
| self.explicit_files = explicit_files; | ||
| self.content_hashes.clear(); | ||
| for file in &self.explicit_files { | ||
| content_changed(&mut self.content_hashes, file, &mut self.hash_buffer); |
There was a problem hiding this comment.
[Medium] reset re-hashes every explicit file before the debouncer flushes. If a configured projection manifest changes after this build reads it but its event is still pending when retarget runs, the new hash is seeded here and the queued event is dropped as unchanged; source_targets watches these manifests as explicit files, while StateDependencies::changed_paths() does not track them. The just-finished output can therefore remain stale until another watched change. Preserve/forward changes against the prior explicit-file baseline across retarget, and add an explicit-file debounce-window regression test.
## Release Bumps WebUI to `3.0.5`. Previous release tag: `v3.0.4` ## Changes since `v3.0.4` Features: - Press supports reusable, per-page outlines with responsive navigation (feat: redesign the docs site and add reusable page outlines #613 by @mohamedmansour). - Press tabs can synchronize categories and persist selections; package-manager preferences now follow readers across documentation pages and reloads (feat: persist package-manager tabs and simplify documentation #620 by @mohamedmansour). - Press adds independent theme families, shared appearance preferences, System/Light/Dark controls, and extensible header regions with a cacheable pre-paint initializer (feat: add Press theme families and extensible appearance controls #623 by @mohamedmansour). Fixes: - Hydrate conditional and repeated SSR ranges in place, avoiding unnecessary custom-element disconnect/reconnect cycles and correcting trailing formatting whitespace (fix: hydrate SSR block ranges in place #619 by @mohamedmansour). - Preserve pending development-server file events by retargeting the existing watcher instead of replacing it after rebuilds (fix: retarget the dev-server watcher instead of replacing it #622 by @mohamedmansour). - Remove unapproved transitive TLS/WebSocket crypto dependencies while retaining HTTP/2 support. The commerce example now serves HTTP/1.1 and h2c behind a TLS-terminating ingress; its direct TLS flags are removed (fix: remove unapproved crypto dependencies #596 by @mohamedmansour). Docs: - Redesign the documentation homepage and reading experience, organize guides into Start/Learn/Build/Reference with legacy redirects, and simplify installation and package-manager instructions (feat: redesign the docs site and add reusable page outlines #613 by @mohamedmansour, chore: clarify the homepage and organize documentation #617 by @mohamedmansour, feat: persist package-manager tabs and simplify documentation #620 by @mohamedmansour). - Refresh benchmark previews, methodology, and downloadable results around the uniform 15-case official WebUI 3.0.4 campaign, including provenance and unavailable memory-sample disclosures (docs: support the uniform 15-case official benchmark campaign #614 by @mohamedmansour). - Update source-build requirements, Rust writer guidance, and Press appearance configuration and deployment documentation (chore: upgrade Rust to 1.99.1 #612 by @mohamedmansour, perf: reduce renderer output allocations #615 by @mohamedmansour, feat: add Press theme families and extensible appearance controls #623 by @mohamedmansour). Maintenance: - Reduce renderer allocation churn by writing escaped values directly into output buffers, reusing attribute buffers, and borrowing quoted condition text (perf: reduce renderer output allocations #615 by @mohamedmansour). - Share eligible Link stylesheets across component mounts within a document, preserving native first-use loading and avoiding repeated parsing on subsequent uses (perf: share Link stylesheets across component mounts #621 by @mohamedmansour). - Align development and release toolchains on Rust 1.99.1; consumers building from source now require Rust 1.99.1 or newer (chore: upgrade Rust to 1.99.1 #612 by @mohamedmansour). ## Validation - `cargo xtask check` passed, including lint, protocol drift, dependency audit, tests, native/WASM builds, examples, benchmark validation, and docs. - Used `WEBUI_PRESS_BINARY_PATH` to select the freshly built workspace Press 3.0.5 binary instead of an older global installation. - Refreshed the standalone Tauri lockfile with Cargo; all 45 changed files contain only `3.0.4` to `3.0.5` substitutions.
…etargeting (#622) No textual conflicts. Cargo.lock, DESIGN.md, docs/ai.md, the Press bundler and the Press package manifest auto-merged; this branch's explicit runtime.js/styles.js entry imports and main's appearance plugin and alias are independent. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 90a93005-7b2a-4ea4-bce5-902f79da872d

markdown-state-serve.test.tsfails intermittently in the E2E (Ubuntu) job (example). A Markdown edit that starts referencing a missingstateFilenever triggers a rebuild, so the test times out after 30 s. This is a real dev-server bug, not a test problem.Root cause
After every rebuild, Press created a new file watcher and dropped the old one. Dropping a
notify-debouncer-miniwatcher discards events still inside its 50 ms debounce window, and the replacement only sees writes made after it registers. An edit made right after a rebuild finished could fall into that gap and be lost silently. The test hits the gap because it edits as soon as new output appears.Fix
WatcherHandle::retargettowebui-dev-server. It updates a running watcher's roots, exact files, and ignore list in place. The debouncer and unchanged recursive roots stay registered, so events observed during a refresh are still delivered. Content baselines reset the way a new watcher's would, so no-op-save filtering is unchanged.stateFiledirectories, projection manifests) are still re-registered on every retarget. Some backends drop a watch silently when its directory is deleted, and generators routinely recreate these directories. They use a separate notify backend: FSEvents applies each subscription change by restarting its stream, which would otherwise interrupt source-root events on macOS.spawn_watcheris now built onretarget, sowebui serve --watchuses the same registration path. Nested roots are deduplicated, which does not change which files are covered.Review notes
stateFilesupport and currentwebui serve. Generated include directories keep per-rebuild re-registration.watch.rswas already over 700 lines, so the backend wrapper and subscription bookkeeping live inwatch_backend.rsandwatch_subscriptions.rs.Validation
source_edits_during_a_watch_refresh_still_rebuild(Press): failed 5/5 before the fix, passes 10/10 after.retarget_delivers_edits_observed_before_it,retarget_moves_exact_file_subscriptions, and a filter-reset test.cargo xtask checkpasses.The E2E test itself is unchanged. It is the only end-to-end coverage of
servewatching generated JSON includes, atomic replacements, and newly referenced missing files.