Skip to content

fix(widgets): hold builds while a dev folder is made again, and ignore folder-only change events - #644

Merged
mrgoonie merged 2 commits into
mainfrom
fix/638-quiet-recreate-builds
Oct 8, 2026
Merged

mrgoonie merged 2 commits into
mainfrom
fix/638-quiet-recreate-builds

Conversation

@mrgoonie

@mrgoonie mrgoonie commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

Fixes #638. Follows #614 (#611).

What changed

1. No failed builds while a dev folder is made again (packages/core/src/widget-dev-engine.ts)

  • Every build now goes through attempt(). A build that would start while the watched folder is missing within the 2 s grace is held, not run.
  • A build that fails while the folder went, or came back under a new id, during the build is also held, and its failure is not reported. A counter of absences and re-arms catches a return that happened mid-build.
  • When the folder is back, the existing re-arm (or, for the same folder returning, a new scheduled build) runs one build. That build is reported once. A held rebuild() (and the runtime's POST .../rebuild) settles with it, after onBuild has been called, so the runtime view already shows it. A held rebuild keeps its rebuild trigger.
  • If the folder does not come back within the grace, nothing changes: the session stops as folder-gone. A waiting rebuild settles with the held failure, or with FILES_UNREADABLE "the folder was missing".
  • An unwatched engine (watch: false) never holds, so the standalone dev host behaves as before.

2. No redundant build after a re-arm (Windows)

  • Root cause, from a probe and a trace of the engine on main: the first directory listing of a freshly made subfolder makes Windows fire a change event that names the folder (change widgets, change widgets\main). This is the NTFS last-access update. Reading the files fires nothing, and a second listing fires nothing. The re-arm build lists the new folders, so it caused the next build, an unchanged one.
  • Fix: the watcher ignores a change event whose name is an existing folder (checked with lstat). Adding, removing or saving a file is still reported under the file's own name, so real saves still build. The tests show this, including a save that adds new nested folders, and a save straight after a re-arm.

The runtime source is not changed. Only the runtime test that encoded the old "rebuild during grace fails" behaviour was updated.

Docs: docs/open-interfaces.md and docs/open-interfaces.vi.md describe the held builds and folder-only change events. The official docs (clarkcant-web) do not describe the grace or build results at this level (docs/cli.html only lists stop reasons), so they are not stale.

Tests

New or changed in packages/core/test/widget-dev-engine.spec.ts:

  • does not call a watched folder gone while it may still come back...: a rebuild during the grace waits, lastBuild is unchanged, nothing is reported, and it settles as failed only once the folder is gone.
  • holds a build that fails because the folder went while it ran...: the folder is deleted exactly as the build reads the manifest (a deterministic readFileSync hook). The result is no failure, and exactly ["rebuild:generation"] once the folder is back.
  • builds a folder another process makes again exactly once...: a child process does rm -rf and then copies 500 ms later, with 4 rebuilds during the gap and a cacheRoot. All 4 settle as the same generation, and built is exactly ["rebuild:generation"].
  • builds a folder another process made again once, though the build's own reading makes the platform report changes: after a child-process recreate, exactly ["change:generation"], and a save right after it still builds.
  • builds a save that adds a folder, once, and a save right after it.

apps/runtime/test/widget-dev-sessions.spec.ts: a rebuild during a child-process recreate now answers live, with lastBuild ok at generation 2 and generation 2 active.

Evidence (Windows 11, Node 24.11)

  • Against main's engine:
    • The 3 grace tests fail. For example, the 4 rebuilds come back as failed ×4.
    • The runtime test fails (ok: false, generation 1).
    • The redundant-build test fails in 3 of 4 instrumented runs with ["change:generation","change:unchanged"]. The trace shows the folder change events arriving during the re-arm build.
  • On the branch:
    • Engine, runtime and widget-cli specs pass in 3 of 3 runs (263/263).
    • The redundant-build test passed 6 of 6 times.
  • pnpm typecheck: exit 0. eslint on the changed files: exit 0. pnpm invariants: 15/15 pass.
  • pnpm verify: exit 0, with 575 files passed and 1 skipped, and 7858 tests passed.

No open PR overlaps these files. #628, which is in progress elsewhere, touches the runtime chosen-folder code. This PR does not change runtime source.

…e folder-only change events

A build that would start while a watched dev folder is missing within the
grace, or one that fails because the folder went or came back while it ran,
is now held instead of reported as failed. The folder is built once when it is
back, and a rebuild asked for meanwhile answers with that build; if the folder
does not return within the grace, the session stops as folder-gone as before
and a waiting rebuild settles with why nothing was built.

The watcher no longer builds on a change event that names a folder. Windows
reports one when a build first lists a folder made a moment ago (its
last-access time is set), which made an extra unchanged build follow each
re-arm. Files added, removed or saved are still reported under their own names.

Refs #638, #611, #614
… late

After a folder made again is watched anew, macOS FSEvents can report the writes that made it. For a short window after the re-arm build is reported, a change build is reported only when it is news; a real save still builds and is reported. The child-process test now waits for the folder to be gone before recreating it.
@mrgoonie

mrgoonie commented Oct 8, 2026

Copy link
Copy Markdown
Contributor Author

Review attestation: ready to merge at 28767ff1ff5876bfd1fd2c6d903be60a8426c1f1, reviewed by agent:code-reviewer.

A push to this PR makes this attestation stale; the new head needs its own review.

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.

Quiet the builds around a dev folder that is deleted and recreated

1 participant