Repository navigation
[Fix] Let a work-item switch leave the build watch running instead of restarting it - #517
Conversation
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository: WordPress/contributor-toolkit/.coderabbit.yaml Review profile: CHILL Plan: Advanced Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
8a72ac9 to
4cdedc8
Compare
|
@coderabbitai review |
|
… restarting it Linking an issue, switching work items or unlinking planned itself as a whole-tree change, so a running build watch was paused for the checkout and resumed after. On Gutenberg a resumed `npm run dev` removes build/ and rebuilds every package, so a switch that moved two files paid a full rebuild: about 28 s on macOS, about 8 minutes on Windows, with nothing on screen saying why. Core's grunt _watch resumes for free, which is why it never showed there. The rebuild such a switch needs is the selective one a running watch already does, the same bargain a src-only patch makes (#262). planWatchImpact knew how to answer that; the switch only had to stop claiming to be whole-tree. planTicketSwitchImpact asks it with the switch's real shape, from the pull request checked out now and the one parked on the work item being switched to, and only that restore still pauses the watch, because it chains an install and a build of its own (#506). The decision is made before the checkout starts, so branches:list now reports the parked pull request per work item, read from the same record ticketCheckoutRef consults. A restore begin did not foresee still pauses late, before its install. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… right for the item in hand Two findings from the pre-PR review. The lookup that decides whether a switch restores a pull request sat in index.jsx, where nothing in the suite can reach it (AGENTS.md section 1): every combination of its output was tested through planTicketSwitchImpact while the code producing that output had no test at all. It moves to ticket-branch-list.cjs, beside the other readings of the same branch list. And it was wrong for one switch. A branch records its parked pull request only when it is left, so re-linking the work item you are already on, which a ticket link can do (#464), read the row, found nothing, and took "leaving a pull request" from the one checked out on it. That paused the watch, and main performed the switch as a no-op, so the resumed watch rebuilt from scratch for a switch that moved nothing: the exact cost this branch removes, in the one case it was adding it back. Linking the item in hand now answers with the pull request already checked out, so the two sides agree. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…tem switch Found on the Windows pass: nothing disables Link issue while the watch rebuilds, and on Windows that window is minutes. A switch taken there was planned as live-watch, so the checkout landed under a watch that is not watching yet but rebuilding build/ from scratch. The packages it had already written would keep the old tree's output and the tab would reach (watching) over a build/ that mixes both. planTicketSwitchImpact now takes the watch's state rather than a boolean and treats 'building' as whole-tree, so that switch takes the pause it took on trunk: one rebuild more in a narrow window, against a build/ that cannot be trusted. Only a watch that is actually watching is handed the change. The guide says so on both pages. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
068b19a to
781e3be
Compare
Both follow-ups from the review of 781e3be. applied-layer.cjs called update-plan.cjs dependency-free, which stopped being true when the switch decision took watchOccupiesBuild from its single definition; the comment now says what the family actually shares, which is being DOM-free. And the Core half of the new switch test claimed the switch waits for the one-shot build, when the terminal lock refuses it before the decision is reached: the case pins the decision, not a path the app can walk. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Why
On a Gutenberg site with the build watch running, linking an issue, switching work items or unlinking paused the watch for the checkout and resumed it after. Gutenberg's
npm run devremovesbuild/and rebuilds every package when it starts, so a branch switch that changed nothing underpackages/cost a full rebuild: about 28 s on macOS, about 8 minutes on Windows, with nothing on screen saying why. Core'sgrunt _watchresumes for free, which is why this never showed there.Seen on the 21 Sep 2026 pass of #508: a number typed into the GitHub issue field linked issue #83258 and the watch rebuilt for five minutes.
What changes
The root cause is that every work-item switch planned itself as a whole-tree change (
planWatchImpact({ wholeTree: true })inticketSwitchLifecycleRef.begin), and a whole-tree change always pauses a live watch. That is right for the switch that puts back a pull request parked on a work item, because that one chains an install and a build of its own (#506). It is wrong for a plain link, unlink or switch, which only moves the checkout.The rebuild such a switch needs is the selective one a running watch already does: Git writes the files of the new branch, the watch recompiles those packages. That is the same bargain the app makes for a src-only patch (#262), and
planWatchImpactalready knew how to answer it. The switch only had to stop claiming to be whole-tree.planTicketSwitchImpact(src/renderer/update-plan.cjs) asks it with the switch's real shape, from two numbers: the pull request checked out now and the one the target work item has parked. It also takes the watch's state rather than a boolean, because a watch that has started and not reached its ready line is rebuildingbuild/from scratch, not watching: a checkout landing under it would leave the packages it had already written on the old tree's output. Such a switch keeps the pause it had on trunk. Only a watch that is actually watching is handed the change.prTransition = fromPr !== toPrmirrorsticketPrImpactin main, which decides the same thing from the two refs after the fact.sites:set-ticketonly reportsprTransitionafterwards. Sobranches:listnow reportssavedPrper work item, fromsavedPrRefinsrc/main.js: the ruleticketCheckoutRefalready applied, extracted so both sides read the record the same way. No extra Git read, since the existence check reuses the ref list the handler already has.completehands the change to it (handOffToWatch), so the Build watcher tab reads (compiling) until it goes quiet (Nothing on screen says the build watch is still compiling a pull request just applied #492), the same signal an applied patch gets.begincould not foresee (a retry of a failed switch, where main reads the ref the switch was leaving and the renderer cannot) still pauses, late, before its install.Deliberately not in it: a switch still runs no
npm installwhen the two branches disagree onpackage-lock.json, exactly as before.How to test this
Platforms: either, but macOS or Windows on a real Gutenberg site is what exercises the bug. The unit suite cannot reach it (no journey starts a build watch), so this is the pass that matters. Build: current head.
Starting state: a Gutenberg site with
node_modulesinstalled and a first build done, no work item linked, no pull request checked out. From the worktree,npm run build:oncebefore launching, sincesrc/renderer/index.jsis a build product.npm run devstart banner and no from-scratch rebuild of every package.What must not have happened: no full rebuild on steps 2 and 3 (the whole point: on Windows that is eight minutes), the watch not left (paused) on any path, the dev server not interrupted, the parked pull request in step 4 still restored with its install and build, and a
build/that mixes two trees after step 6.The unit tests that carry the decisions fail on the old code:
planTicketSwitchImpactdid not exist, andbranches:listreturned nosavedPr(checked). What they cannot see is the watch process itself, which is what the steps above are for.Done on macOS 27 (M5 Pro), on the Buildkite artifact for
4cdedc8, on a built Gutenberg site, with issue #83341 for steps 2 to 4, pull request #83350 for step 4, and #83324 for step 5. All five steps as written.The evidence for steps 2 and 3 is the Build watcher log itself: one
Running npm run dev…for the whole run, the one from Start build watch at 15:04:31, ending in Watching for changes. Linking the issue, unlinking it and switching back to it added nothing to that log: no second start banner, no Paused while another operation uses the build, no from-scratch rebuild of every package. On trunk each of those three switches costs one.Done again on Windows 11 (UTM VM, arm64 host), on the Buildkite artifact for
781e3be, the rebased head, on a built Gutenberg site atC:\Users\JuanMa\Desktop\demo-gutenberg-3. All six steps pass, and Windows is what answers the two questions macOS could not:rebundled/rebuiltper package in its tab,block-librarythe longest at 19.7 s), rather than running the clean from-scratch cycle it runs on trunk, where the same switch costs about eight minutes.Steps 4, 5 and 6 behaved as on macOS: the plain switch back to a second work item kept the watch, the parked pull request paused it and handed the rebuild to the resumed watch, and with the watch stopped nothing rebuilt.
Two lines the watch prints on Windows are worth knowing and are not this PR's: Gutenberg's own
dev.mjsstarts by reporting'ps' is not recognized as an internal or external command, and a watch killed mid-TypeScript-build (which is what a pause is) printsTypeScript compilation failedbefore it exits. The state machine is unaffected — the watch stayedpausedand resumed correctly — but the tab reads like a build error when nothing is wrong.Step 4 behaved as it must: applying the pull request paused the watch and handed the rebuild to it, the applied banner sat amber reading PR #83350 is applied. The site is rebuilding. while the tab rebuilt, and turned green with the Checked out the pull request toast when the watch was watching again (#508, #511). Step 5, with the watch stopped, rebuilt nothing.
Risks and limitations
build/is the same family as Start dev server and Start build watch are reachable while a build is running outside the wizard #503 and is not done here.Windows file handles.Answered by the Windows pass above: the checkout rewrote files under a live watch without an error on either side.TypeScript compilation failedwhen a pause kills it mid-build, so the tab reads like a failure although the pause is deliberate and the resume is clean. Cosmetic, pre-existing, and not made more likely by this PR, which pauses in one more case than before.package-lock.jsondiffer still installs nothing. Unchanged from trunk, and out of scope here.pullRequestfromsite:status,savedPrfrombranches:list, both reloaded after every switch) rather than asking main. A prediction that is wrong can only be wrong towards a needless pause, and the one case where it was is fixed in the second commit.Related
Fixes #510. Follow-up to #508 and #506. Part of #251.
Design decisions and alternatives considered
A new IPC handler that previews the switch would have been more faithful, since main would answer with the same function that later performs the switch. Rejected: it adds a round trip in front of a switch that already has one, and it does not remove the disagreement, it only moves it, since the record can still change between the preview and the switch. Reporting
savedPron a list the panel already reloads after every switch costs nothing and is read from the same record.Asking Gutenberg's watch not to rebuild on start is the other way to make the pause cheap. Out of scope:
npm run devhas no such flag, and the upstream--no-cleanwork is not this repository's.A journey (layer 4) would replace the manual pass, per AGENTS.md. Nothing in
tests/e2e/starts a build watch today, so it would mean building that fixture first: a fakedevscript that prints the ready line and stays up, and an assertion that the process was never restarted. Worth doing, and deliberately not done here, so the fix can ship in beta.2.Review outcome (required — see AGENTS.md)
4 findings: 2 [fix here] · 2 [follow-up]. Both
[fix here]fixed in4cdedc8, one[follow-up]fixed with them, one recorded above.🟡 architecture/tests,
[fix here]: the saved-pull-request lookup lived inindex.jsx, where the suite cannot reach it (§1). Fixed: moved tosavedPrForSwitchinsrc/renderer/ticket-branch-list.cjs, with its own tests.🟡 performance,
[fix here]: re-linking the work item already in hand while its pull request is checked out read the row, found nothing (a branch records its parked pull request only when it is left), and paused the watch for a switch main performs as a no-op. The exact cost this PR removes, in the one case it was adding back. Fixed: linking the item in hand answers with the pull request already checked out.🔵 tests,
[follow-up]: thebranches:listtest covered both negative reasons but not the ordinary row with no parked pull request. Fixed in the same commit.🔵 cross-platform,
[follow-up]: the watch compiling a half-checked-out tree, and the Windows handle question. Recorded under Risks; no code change.Manual pass: completed on macOS, on
4cdedc8; see How to test this.Review: completed, by a fresh agent context (
Exploresubagent, review standard in.github/instructions/code-review.instructions.md); reviewed head964f651(now9fc3315, same tree, commit message reworded) / baseorigin/trunk;npm run lintclean andnpm test1625 pass / 0 fail / 2 skipped at that head.Since review:
964f651to4cdedc8, the review's own findings, plus their tests.npm run lintclean,npm test1630 tests / 1628 pass / 0 fail / 2 skipped at the new head. CodeRabbit requested on4cdedc8by comment once the PR left draft.Screenshots or recording
Nothing on screen changes shape. What changes is what the Build watcher tab says during a work-item switch, and above all what its log does not say: on this branch a link, unlink or switch adds nothing to it, where on trunk each one starts
npm run devagain and rebuilds every package. The log excerpt in How to test this is that evidence.