Skip to content

Fix rewatch losing watches when an overlapping source folder is removed - #8839

Open
cknitt wants to merge 2 commits into
masterfrom
rewatch-overlapping-unwatch
Open

cknitt wants to merge 2 commits into
masterfrom
rewatch-overlapping-unwatch

Conversation

@cknitt

@cknitt cknitt commented Oct 12, 2026 •

Copy link
Copy Markdown
Member

rescript watch registers each source folder as its own watch, and source folders may overlap. Since #8730, a full rebuild (after a rescript.json change, but also when a source file is added, removed or renamed) unwatches the paths that went away and watches only the new ones. With inotify on Linux that loses watches: notify 5 keeps one watch per directory, shared by every registered path that reaches it, so unwatching one path can drop another that is still configured. For example, with sources [{"dir": "src", "subdirs": true}, "src/retained"] changed to ["src/retained"], unwatching the recursive src also dropped src/retained, and edits there were no longer picked up (#8825). The same happens for other spellings of a folder (.., symlinks) and for directory symlinks inside a recursive source folder.

update_watches now still unwatches only the stale watches (paths no longer configured, or whose directory was replaced), and then depends on the platform:

  • macOS (FSEvents): every watch/unwatch call restarts the event stream, and events during a restart are lost; that is why Fix watcher missing watch.lock removal during full rebuilds #8730 made updates incremental. FSEvents watches are independent paths, so unwatching one never drops another. As before, only new paths are registered, so a full rebuild that keeps the same paths doesn't restart the stream.
  • Linux (inotify) and other platforms: every configured path is registered again after the unwatches. Registering a directory that is already watched only updates its existing watch, so watches that weren't dropped keep reporting events without a gap, and those an unwatch dropped are restored. In the common case of a full rebuild with unchanged paths, nothing is unwatched at all.
  • Windows takes the same path. It has no stable directory identity, so every watch counts as stale and is unwatched and registered again, as before.

The doc comment of update_watches explains these differences.

New test: keeps_watching_after_an_overlapping_watch_is_removed (Linux only) registers watches with a real notify watcher, updates them, and checks that a write in a folder that is still configured is reported. It covers a full rebuild that keeps the same paths, a folder kept below a removed recursive folder (the case from #8825), a folder removed below a kept recursive one, a folder that stops being recursive, other spellings of a folder (.., a symlink), and a recursive folder reaching a separately watched one through a directory symlink. Registering only the new paths on Linux, as master does, fails the #8825 case.

Fixes #8825

🤖 Generated with Claude Code

https://claude.ai/code/session_01BC5F5QPeQh5cHBjGBnvJ2T

@cknitt
cknitt marked this pull request as ready for review October 12, 2026 19:21
@cknitt
cknitt force-pushed the rewatch-overlapping-unwatch branch from 010c1ae to f456adc Compare October 12, 2026 19:21
@pkg-pr-new

pkg-pr-new Bot commented Oct 12, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

rescript

npm i https://pkg.pr.new/rescript@8839

@rescript/belt

npm i https://pkg.pr.new/@rescript/belt@8839

@rescript/darwin-arm64

npm i https://pkg.pr.new/@rescript/darwin-arm64@8839

@rescript/darwin-x64

npm i https://pkg.pr.new/@rescript/darwin-x64@8839

@rescript/linux-arm64

npm i https://pkg.pr.new/@rescript/linux-arm64@8839

@rescript/linux-x64

npm i https://pkg.pr.new/@rescript/linux-x64@8839

@rescript/runtime

npm i https://pkg.pr.new/@rescript/runtime@8839

@rescript/win32-x64

npm i https://pkg.pr.new/@rescript/win32-x64@8839

commit: e3ada4d

@github-actions

Copy link
Copy Markdown

@cknitt

cknitt commented Oct 13, 2026

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-15T07:38:28.595708Z e3ada4d Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f456adc3e9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread changes/rewatch-overlapping-unwatch.fix.md
Comment thread rewatch/src/watcher.rs Outdated
@cknitt

cknitt commented Oct 13, 2026

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 94a90ef802

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread rewatch/src/watcher.rs Outdated
Comment thread rewatch/src/watcher.rs Outdated
@cknitt

cknitt commented Oct 13, 2026

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a1c8248246

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread rewatch/src/watcher.rs Outdated
@cknitt

cknitt commented Oct 13, 2026

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cd0c132ece

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread rewatch/src/watcher.rs Outdated
@cknitt

cknitt commented Oct 13, 2026

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ccacd4b908

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread rewatch/src/watcher.rs Outdated
@cknitt

cknitt commented Oct 13, 2026

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep it up!

Reviewed commit: 765184e889

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@cknitt cknitt changed the title Rewatch: re-register watches dropped by an overlapping unwatch Re-register all watches outside macOS Oct 13, 2026
@cknitt
cknitt force-pushed the rewatch-overlapping-unwatch branch from 765184e to a5af682 Compare October 13, 2026 11:43
@cknitt

cknitt commented Oct 13, 2026

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a5af682777

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread rewatch/src/watcher.rs Outdated
cknitt and others added 2 commits October 15, 2026 05:25
Unwatching one path can drop an overlapping watch with inotify, so only macOS,
where FSEvents restarts its stream on every change, updates watches incrementally.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BC5F5QPeQh5cHBjGBnvJ2T
Signed-off-by: Christoph Knittel <ck@cca.io>
Outside macOS, every path is registered again after the stale ones are
unwatched, which restores watches an unwatch dropped without a gap for the
others.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BC5F5QPeQh5cHBjGBnvJ2T
Signed-off-by: Christoph Knittel <ck@cca.io>
@cknitt
cknitt force-pushed the rewatch-overlapping-unwatch branch from a5af682 to e3ada4d Compare October 15, 2026 07:33
@cknitt cknitt changed the title Re-register all watches outside macOS Fix rewatch losing watches when an overlapping source folder is removed Oct 15, 2026
@cknitt

cknitt commented Oct 15, 2026

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e3ada4d2cb

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread rewatch/src/watcher.rs
let _ = watcher.unwatch(&watch.path);
}
if !REGISTER_ONLY_NEW_WATCHES {
return register_watches(watcher, next);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve recursive mode when replaying overlapping watches

On Linux, when next contains a recursive parent and a non-recursive child or alias of a directory it covers, replaying every entry can overwrite notify 5.2's single inotify record for that directory with is_recursive = false if the non-recursive entry is registered last. Because compute_watch_paths returns fresh HashMap iteration order, a full rebuild can downgrade a previously recursive watch; directories subsequently created below that point are not added to inotify, so edits inside them are missed. Register non-recursive targets before recursive targets, or otherwise preserve the strongest mode for shared directory identities.

Useful? React with 👍 / 👎.

Comment thread rewatch/src/watcher.rs
Comment on lines 256 to +260
for watch in stale {
let _ = watcher.unwatch(&watch.path);
}
if !REGISTER_ONLY_NEW_WATCHES {
return register_watches(watcher, next);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Rescan after restoring indirectly removed watches

When a stale recursive watch overlaps a retained child, unwatch removes the child's shared inotify descriptor and the child is restored only later by register_watches. An edit during that interval occurs after initialize_build has already scanned the sources and produces no event, leaving its output stale. Although the prior gap report was answered by retaining unchanged entries, the final stale-only loop still drops these particular retained entries indirectly; rescan after registration or otherwise reconcile changes made during this gap.

Useful? React with 👍 / 👎.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

rewatch watch on Linux stops watching a source folder when an overlapping recursive source folder is removed from rescript.json

1 participant