fix(gatorwalk-factory): the studio watches a replaced directory again, and the page resyncs after a reconnect (swamp-club #2841) #425

Merged
seth merged 3 commits from cue/2841-gatorwalk-factory-studio into main 2026-10-01 18:53:04 +00:00
Owner

Fixes swamp-club #2841: studio file-watch and page edge cases left over from #2806's reviews.

#2884 rewrote the watch after this issue was filed. So, after triage, items 3, 4 and 6 no longer apply (the scenarios dir, the path refusal and refollow are gone), and item 5 is won't-fix. This PR fixes the two items that remain.

A replaced definition directory silences its watch (item 1). studio_watch.ts now keeps one watch per directory, along with the directory's inode.

  • When a relist finds a directory with a new inode, or one it doesn't watch, the old watch is closed first. The refresh then waits up to a second for that watch's loop to end before opening a new one. Deno 2.9 keeps one OS watch per path, so a watch opened before the old one has finished closing stays on the deleted directory and sees nothing.
  • If the wait times out, the directory is left unwatched and the next relist tries it again.
  • A definition event goes out, after the new watches open, for the factories in a replaced directory and for any factory whose file newly resolves.
  • A close() that throws no longer blocks later refreshes.

The page does not resync after its event stream reconnects (item 2). listen() remembers an error, and the next open runs reloadFactories() and then loadDefinitionFile(). A failure shows as a source error.

Tests:

  • New integration/engine/studio_watch_test.ts (a real temp dir; the unit tests have no --allow-write) covers an edit, a delete and recreate, and a file missing at one relist and back at the next. It failed before the fix.
  • New state_test.ts case with a stubbed EventSource. It fails without the fix.

Also: the page bundle is rebuilt, and DESIGN.md's Live reload paragraph is updated.

Follow-up: swamp-club #2913. On ext4/XFS a recreated directory can reuse the inode number, which this check misses; the suggested fix is to also treat the watched directory's own remove event as a replacement.

Verified at d47af95d9 (verify-build 2de09478, verify-reviews 277ef76e): all checks passed, and both reviews passed.

🤖 Generated with Claude Code

Fixes swamp-club #2841: studio file-watch and page edge cases left over from #2806's reviews. #2884 rewrote the watch after this issue was filed. So, after triage, items 3, 4 and 6 no longer apply (the scenarios dir, the path refusal and refollow are gone), and item 5 is won't-fix. This PR fixes the two items that remain. **A replaced definition directory silences its watch (item 1).** `studio_watch.ts` now keeps one watch per directory, along with the directory's inode. - When a relist finds a directory with a new inode, or one it doesn't watch, the old watch is closed first. The refresh then waits up to a second for that watch's loop to end before opening a new one. Deno 2.9 keeps one OS watch per path, so a watch opened before the old one has finished closing stays on the deleted directory and sees nothing. - If the wait times out, the directory is left unwatched and the next relist tries it again. - A `definition` event goes out, after the new watches open, for the factories in a replaced directory and for any factory whose file newly resolves. - A `close()` that throws no longer blocks later refreshes. **The page does not resync after its event stream reconnects (item 2).** `listen()` remembers an error, and the next `open` runs `reloadFactories()` and then `loadDefinitionFile()`. A failure shows as a source error. Tests: - New `integration/engine/studio_watch_test.ts` (a real temp dir; the unit tests have no `--allow-write`) covers an edit, a delete and recreate, and a file missing at one relist and back at the next. It failed before the fix. - New `state_test.ts` case with a stubbed EventSource. It fails without the fix. Also: the page bundle is rebuilt, and DESIGN.md's Live reload paragraph is updated. Follow-up: swamp-club #2913. On ext4/XFS a recreated directory can reuse the inode number, which this check misses; the suggested fix is to also treat the watched directory's own remove event as a replacement. Verified at d47af95d9 (verify-build 2de09478, verify-reviews 277ef76e): all checks passed, and both reviews passed. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
A watched directory deleted and made again left its watch silent until the
page reloaded. The watch now keeps one watch per directory with the
directory's inode, closes a replaced one and waits for its loop to end before
opening the new one (Deno shares one watch per path, so a watch opened before
the old has closed sees nothing), and sends a definition event for the
factories in it and for any factory whose file newly resolves.

The page now lists the factories and reads its definition again when its
event stream reconnects, since a change made while it was down sent nothing.

Items 3, 4 and 6 of the issue no longer apply after #2884; item 5 is
won't-fix.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
From the verification reviews: a closed watch whose loop never ends no longer
holds up every later refresh (the wait is capped at a second); the
definition events for a replaced directory go out once its new watch is
open, so no edit falls between them; and an error in the page's reconnect
reload shows as a source error instead of going unhandled.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
fix(gatorwalk-factory): a studio watch still closing is retried, and a failed close no longer blocks the watch (swamp-club #2841)
All checks were successful
CI / Validate Attestation (pull_request) Successful in 1m52s
CI / Review Integrity (pull_request) Successful in 1m57s
d47af95d96
From the second verification reviews: when the wait for a replaced
directory's old watch times out, the directory is left unwatched and the next
refresh tries it again, instead of opening a watch that sees nothing and is
never retried. A watcher.close() that throws no longer leaves the stale entry
behind to fail every later refresh.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
seth merged commit 3b0823186a into main 2026-10-01 18:53:03 +00:00
seth deleted branch cue/2841-gatorwalk-factory-studio 2026-10-01 18:53:04 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
swamp-club/swamp-extensions!425
No description provided.