Repository navigation
[Add] Run the watcher and accept the terminal scripts of the site's own target (#251) - #470
Merged
juanmaguitar merged 2 commits intoSep 17, 2026
Conversation
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .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 |
This was referenced Sep 15, 2026
juanmaguitar
added this pull request to stack #477
September 15, 2026 09:12
juanmaguitar
force-pushed
the
juanmaguitar/251-build-per-type
branch
from
September 16, 2026 10:42
34d4005 to
a7a4ff4
Compare
juanmaguitar
added a commit
that referenced
this pull request
Sep 16, 2026
…251) The create-site dialog gains a "Contribute to" choice, read off the registry, Core by default; the choice goes to wordpress:setup as the type the stack already accepts. The button and the dialog are "Create a site" now, since the old name is false, and the two specs and the screenshot script that click it follow, as do the two screenshots that show it. Docs: creating a site, getting started, the setup wizard, the terminal (Gutenberg's script list and why bare `test` is not on it), running the site (a Gutenberg site is a plugin inside a stock WordPress, and what the file-modification lock means). The opt-in real-setup lane runs once per target. The Gutenberg variant logs in and reads the Plugins screen (Gutenberg active, no Delete links), and checks that stopping the watch leaves no process of its tree alive, which a signal to npm alone does not manage. The name is filled after the choice and read back until right: in the first seconds of a fresh app the filled value came back with letters added or dropped, not reproduced by hand or in a warmed-up app. Deferred, from #470's review: on a never-built Gutenberg site the start builds twice (npm run build, then npm run dev's own first build), and the watcher tab reads "watching" during that first build. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…wn target (#251) The dev-server planner takes the type's `build` entry and returns its watcher: `grunt -- _watch` for Core as before, `npm run dev` for Gutenberg, whose own script is the incremental watcher and must not receive Core's passthrough separator. The `grunt -- _watch` constants and the reasoning behind them move from the planner into the registry, so the fact has one copy (the follow-up from #469's review). The terminal's `npm run <script>` list is the type's too. The card reads the type from `site:status` and defaults to Core, so every existing site starts the same watcher and accepts the same scripts it did. Both types build with `npm run build`, so the registry's `buildScript` field, which nothing needed to vary, is gone. No visible change until the create-site dialog offers the choice, in the pull request that also serves a Gutenberg site. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
From the pre-PR review. The terminal's help and `npm run` guard closed over the type's script list, and a new array identity there would have recreated `printHelp`, whose identity the xterm creation effect depends on: the first status reporting a type would have disposed the terminal, scrollback included. The handlers read the list through a ref now, the same indirection the input handler already uses, so their identities never change with the type. The card also stops re-defaulting the reported type; the registry answers Core for a missing one. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
juanmaguitar
force-pushed
the
juanmaguitar/251-build-per-type
branch
from
September 17, 2026 06:57
a7a4ff4 to
d3bdaff
Compare
juanmaguitar
added a commit
that referenced
this pull request
Sep 17, 2026
…251) The create-site dialog gains a "Contribute to" choice, read off the registry, Core by default; the choice goes to wordpress:setup as the type the stack already accepts. The button and the dialog are "Create a site" now, since the old name is false, and the two specs and the screenshot script that click it follow, as do the two screenshots that show it. Docs: creating a site, getting started, the setup wizard, the terminal (Gutenberg's script list and why bare `test` is not on it), running the site (a Gutenberg site is a plugin inside a stock WordPress, and what the file-modification lock means). The opt-in real-setup lane runs once per target. The Gutenberg variant logs in and reads the Plugins screen (Gutenberg active, no Delete links), and checks that stopping the watch leaves no process of its tree alive, which a signal to npm alone does not manage. The name is filled after the choice and read back until right: in the first seconds of a fresh app the filled value came back with letters added or dropped, not reproduced by hand or in a warmed-up app. Deferred, from #470's review: on a never-built Gutenberg site the start builds twice (npm run build, then npm run dev's own first build), and the watcher tab reads "watching" during that first build. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
juanmaguitar
added a commit
that referenced
this pull request
Sep 17, 2026
…k WordPress (#251) (#471) ## Why A Core site's `build/` is a whole WordPress, and the Playground runner mounts it as the docroot before install and skips the download. A Gutenberg checkout is not a WordPress at all; it is a plugin, and there is nothing to mount as a docroot. #251 needs the served site to be a stock WordPress with the checkout mounted and activated as its Gutenberg plugin. ## What changes The stock WordPress installation now belongs to the site's lifecycle: stopping and restarting preserves posts, settings and uploads; deleting the registered Gutenberg site stops its processes and removes the persistent installation before removing the checkout. The runner and cleanup canonicalize the same directory, including Windows casing and macOS path aliases. Failed cleanup keeps the checkout and registry entry retryable. `src/playground-plan.cjs` (ported from the closed #261) turns a serve strategy into the `runCLI` options: `docroot` is what Core always sent (mount before install as `/wordpress`, `install-from-existing-files-if-needed`), `plugin-mount` uses Playground's persistent `start` command to retain its WordPress installation across restarts, mounts the checkout at `wp-content/plugins/gutenberg` and adds an `activatePlugin` step. It also names the constants a strategy needs on top of the shared debug and SMTP set: the plugin mount is the contributor's working tree, `.git` and uncommitted work included, so `plugin-mount` sets `DISALLOW_FILE_MODS` and `DISALLOW_FILE_EDIT`; without them **Plugins > Delete** in the served site removes the checkout. The cost is deliberate: the served site cannot install a second plugin or theme, and has no file editor. Losing an afternoon of work is worse than restarting a preview. The runner takes one JSON argument instead of a bare build directory. `playground:start` reads the site's type, writes the config, and runs the runner from the checkout for a plugin mount, from `build/` as before for a docroot. The map `playground:stop` and the quit sweep walk is keyed as it was. Deliberately not in this PR: anything a contributor can see. Nothing can create a Gutenberg site yet; the choice arrives in the next PR, now that a Gutenberg site can be cloned, built, watched and served. ## How to test this Persistence regression, **macOS verified; Windows supported by the same opt-in lane**, current head. From the repository root, run `TOOLKIT_REAL_SETUP=1 npx --no-install playwright test --config tests/e2e/real-setup.config.js -g 'keeps posts'` (PowerShell: set `$env:TOOLKIT_REAL_SETUP = '1'` first, then run the command without the POSIX assignment). This boots real WordPress, saves a post and upload, restarts and checks both, deletes the installation and checks that the same location starts fresh. It fails with the previous `server` command (empty title, missing upload) and passes with the correction. The IPC suite additionally covers registered/Core/unregistered deletion and a cleanup failure. The fixture and its persistent installation are removed after the test. Platforms: any for the Core path; **macOS** for the Gutenberg pass below (run by the author, 15 Sep, on `3c7d13c`; the fixes since do not touch the plugin-mount path). Core, no visible change, from the repository root: ``` node --test tests/unit/playground-plan.test.cjs node --test tests/unit/runner-wiring.test.cjs node --test tests/unit/ipc-wiring.test.cjs ``` The runner test pins the Core options byte for byte against what the runner sent before (`mount-before-install` of the build directory, the install mode, no plugin mount, no steps, no file-mods constants), and the plugin mount separately. Gutenberg, by hand, since the app cannot make such a site yet: with a built `WordPress/gutenberg` checkout on disk, run `src/server-runner.js` on Electron's Node with the argument `{"strategy":"plugin-mount","pluginDir":"<checkout>","pluginSlug":"gutenberg"}` and the checkout as the working directory, then open the `SERVER_URL:` it prints. 1. The front page loads. Expected: its HTML references `wp-content/plugins/gutenberg/` assets. 2. Log in (`admin` / `password`) and open **Plugins**. Expected: Gutenberg is listed with a **Deactivate** link; no plugin has a **Delete** link; there is no **Add New Plugin**. 3. Open **Posts > Add New**. Expected: the editor loads its scripts from `wp-content/plugins/gutenberg/build/`. Observed on the author's run: up in 5 to 14 s, all three as expected. **What must not have happened:** a Core site's Playground options changing in any way (the runner test is the guard); the served Gutenberg site offering a Delete link on its own plugin; anything written into the checkout by the served site. ## Risks and limitations The restart/deletion regression passed locally on macOS; its Windows network run has not been executed in this correction pass. Cleanup follows the installed Playground CLI's `~/.wordpress-playground/sites/<sha256(canonical cwd)>` convention, pinned by the real CLI test. A checkout removed externally cannot be resolved safely, so deleting its stale registry entry does not guess which persistent directory to remove. Previously discarded temporary content cannot be recovered. - Review outcome: 1 [fix here] fixed, 4 [follow-up] (three taken, the start race deferred). - The Gutenberg pass is on macOS only. Windows runs the same runner on the same JSON, with backslashes escaped by `JSON.stringify` and no shell in the spawn; the next PR, which exposes the choice, carries the Windows pass through its Buildkite artifact. - Adminer's SQLite path (`/wordpress/wp-content/database/.ht.sqlite`) is Playground's stock install's under `plugin-mount`, the same as under `docroot`; verified by the front page and admin loading, not by opening Adminer. - `DISALLOW_FILE_MODS` also blocks plugin and theme installs and core updates inside the served site. Deliberate, and documented in the next PR alongside the choice. ## Related Part of #251. Stacked on #470. --- <details> <summary>Design decisions and alternatives considered</summary> - One JSON argument rather than a second positional one: the runner's contract becomes "what to serve", and a third strategy is a new value, not a new argument. - The strategy's constants are spread last into the blueprint's constants so nothing shared can override the guard. - Mounting the checkout read-write with WordPress told not to modify files, rather than a read-only mount: Playground's NODEFS mount has no read-only mode, and the two constants are WordPress's own switch for the same thing. </details> <details> <summary>Review outcome (required — see AGENTS.md)</summary> 1 [fix here] · 4 [follow-up] — the [fix here] and three of the follow-ups fixed in `fee96e5`. - 🔴 Tests / cross-platform [fix here] `tests/unit/runner-wiring.test.cjs`: the new mount assertions compared `hostPath` to POSIX literals while the runner resolves them, which on Windows gives a drive-rooted path; four red CI jobs. Fixed: expected values resolved the same way. - 🔵 Security [follow-up, taken] `src/playground-plan.cjs`: `pluginSlug` went into the VFS path unvalidated; only the registry supplies it today, but the module is the seam. Fixed: a plain-name guard, with a test. - 🔵 Tests [follow-up, taken]: "byte for byte" was not literal, the docroot plan sent two empty arrays the old runner never did (inert in the installed CLI, verified by the reviewer, but unpinned). Fixed: the docroot plan sends exactly the old options and the test asserts the keys are absent. - 🔵 Performance [follow-up, taken as a comment]: the first Gutenberg serve needs the network for the WordPress release; Playground caches it afterwards. Recorded beside the strategy. - 🔵 Failure paths [follow-up, deferred] `src/main.js` `playground:start`: two starts for one site can both pass the running-server guard, since awaits precede it (pre-existing, the SMTP await was already there; this PR adds a store read). Deferred as its own fix: the cheap change is a placeholder in the map at the guard. - Style, taken: `isPluginMount` once instead of three strategy comparisons; the runner harness comment no longer claims the Playground packages are not installed. Verified clean by the reviewer: the serve config is built from app-owned values and passed as argv with no shell (Windows backslashes survive `JSON.stringify` / `JSON.parse`, `path.resolve` is a no-op on an absolute path); `playground-plan.cjs` has no requires or side effects so the loopback patch still precedes the CLI load; the `DISALLOW_FILE_*` trade-off is recorded and no remaining WordPress write path reaches an active plugin's directory with both set; running the runner from the checkout writes nothing into it; a plan failure reaches the renderer through the existing log and exit path; the Gutenberg wiring test fails if the handler stops reading the type. - **Review:** completed — fresh Explore subagent context (local `/self-review`); reviewed head `3c7d13c` / base `34d4005`; deterministic layer (lint, `npm test`) run by the author; Playground option handling verified by the reviewer against the installed `@wp-playground/cli` bundle; outcome above - **Since review:** `3c7d13c → fee96e5` checked: only the fixes above; lint and `npm test` re-run green (1480 pass, 2 pre-existing skips) </details> <details> <summary>Screenshots or recording</summary> Nothing on screen changes in the app. The served Gutenberg site's Plugins page, from the manual pass above: Gutenberg active, no Delete links. </details> 🤖 Generated with [Claude Code](https://claude.com/claude-code) <details> <summary>Stack correction review — 2026-09-17</summary> 0 remaining [fix here] · 0 new [follow-up] in this correction pass. Persistence and deletion lifecycle fixed; the original data-loss finding and the additional deletion/recreation finding are resolved. - Current PR head: `244ca76c360e183a8da9f2343d7cf05fc05f5924`; configured base head: `d3bdaff0b805b55ef8c9a8f0c4093ef68064ab24`. Previous PR head: `1283b18bf0946eca9b5d1810b299eca1177baa69`. - Reviewer: fresh Codex context `/root/review_fixes`. Completed five-dimension review of the correction delta from previously reviewed integrated head `bc8c4fda3fb78d51cd6f3abd5fc1b0f1e89c0095` to `c8c86677491012f9c9d4e99fe43b4ceff1c9647b`, then re-reviewed through final stack head `34550eec7a9031d2d5f03116801a60e313fa0559`. The additional runtime-deletion finding was fixed and verified; the original three findings remain resolved. Earlier review records above are historical. - Validation on the combined stack: lint; Node unit/integration/IPC suite (1,547 passed, two runtime skips); Electron suite (1,549 passed); 33 macOS journeys; documentation build. The real Playground restart/deletion regression passed on macOS. Windows CI results must be read on these new heads; this pass does not claim a fresh Windows real-setup run. - CodeRabbit's earlier check is not review coverage for these new heads. This local correction review is the review of record for the delta; no new bot review was requested. </details> --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
juanmaguitar
added a commit
that referenced
this pull request
Sep 17, 2026
…251) (#476) ## Why A contributor who wants to work on the block editor has no way in: every site the app creates is a `wordpress-develop` checkout. The three PRs under this one taught the app to clone, build, watch and serve a Gutenberg checkout; none of them let anyone ask for one. This is the PR that does, now that the whole path works. #251 has the case. ## What changes The create-site dialog gains a **Contribute to** choice, **WordPress Core** or **Gutenberg**, each with a line saying what the app does with such a site today, read off the registry so the copy lives in one place. Core is selected by default. The choice goes to `wordpress:setup` as the `projectType` the stack already accepts, and the button and dialog are renamed **Create a site**, since "Create WordPress Core site" is now false. The rename reaches the two specs and the screenshot script that click it, and the two screenshots that show it are regenerated. A Gutenberg site is told apart on screen and shown only what applies. The sidebar row and the site header carry a **Gutenberg** tag; the checklist's words are the target's (the repository it clones, the packages it compiles, the WordPress it launches with the checkout as its plugin), from a `setup` block in the registry, and the type reaches the row from the moment the dialog closes because the placeholder carries the choice. The Trac-shaped cards show only where the work item is a Trac ticket: on a Gutenberg site the ticket card gives way to a line saying that working on a GitHub issue from here, with its branch and its pull request, comes in a later version; the apply card is **Check out a pull request** with no patch-file path; **Attach to Trac** is not offered while **Hand it to a mentor** still is; the pull-request destination says the flow is not available yet before it invites a sign-in; a `wpct://ticket` link that arrives while a Gutenberg site is in front is turned away by name, kept for a Core site, and the note hides on that site only. Every one of those strings lives in the registry's `cards` block, and a test pins that no card of a non-Trac target speaks of Trac. Docs: creating a site (the new field, what each choice clones, the tag and what a Gutenberg page leaves out), getting started, the setup wizard, the terminal (the Gutenberg script list, and why `test` is not on it), running the site (a Gutenberg site is a plugin inside a stock WordPress; what the file-modification lock means), Trac tickets and applying patches (which cards are Core-only). The opt-in real-setup lane now runs one job per target and platform (a Core setup alone takes half an hour on the Windows runner, and two in one job hit its limit): the Gutenberg variant also logs in and reads the Plugins screen (the checkout is the active Gutenberg, and cannot be deleted), and checks that stopping the watch leaves no process of its tree alive (POSIX; Windows is probed by the workflow only for the flow). Its name field is filled after the choice and read back until right: in the first seconds of a freshly started app the value Playwright filled came back with letters added or dropped (`real-setupes`, `real-setu`), which sent the site to a folder the test was not watching. Not reproduced by hand, nor in a warmed-up app across a dozen tries; noted here rather than hidden. Deliberately not in this PR: a GitHub issue as the work item (the ticket card still asks for a Trac ticket on a Gutenberg site, and its linked-PR list searches `wordpress-develop`), opening a pull request against `WordPress/gutenberg`, and `wpct://issue/N`. They are the next three PRs; until they land a Gutenberg site is for building, running and trying pull requests by checkout (which already fetches from the site's own origin), not for filing work. So that a Gutenberg site cannot open a pull request against `wordpress-develop` in the meantime, `github:open-pr` refuses any site that is not Core, with a sentence that points at the patch file; the choice's copy and the docs say only what is wired. ## How to test this Platforms: **macOS and Windows**; the Gutenberg path spawns npm and Playground through the platform shims. Build: the Buildkite artifact for the current head. The **Real WordPress setup** workflow is the pass: it now creates one site per target and, for Gutenberg, logs in and reads the Plugins screen (Gutenberg active, no **Delete** link anywhere) and checks that Stop leaves no watcher process behind (POSIX). Run locally by the author on macOS, 15 Sep, current head: the Gutenberg variant passed in 4.0 minutes end to end. Runs on this branch: https://github.com/WordPress/contributor-toolkit/actions/runs/34955889102 on `ecab2bd` with one job per target and platform: **Core green on macOS (2.7 min) and Windows; Gutenberg green on Windows end to end; Gutenberg on macOS red at the last step only**, the survivor check, which is #479's fix (see Risks). The two earlier runs (34948704875, 34949992162) ran both targets in one job and were cancelled on Windows at the 60-minute limit, which is why the jobs are split now. By hand, either platform, current head, in a folder you do not mind deleting: 1. Click **Create a site**. Expected: the dialog has three fields, and **Contribute to** is on **WordPress Core**, with a line under each choice. 2. Choose **Gutenberg**, name the site `gb`, pick a folder, click **Create site**. Expected: the folder created is `gb`, and the terminal shows the clone of `WordPress/gutenberg`. 3. Wait for the checklist to run install and build on its own. Expected: about a minute for the install and half a minute for the build on a fast machine; **This site is ready to work on**. 4. Click **Start dev server and finish the wizard**. Expected: the **Build watcher** tab shows `npm run dev` building the packages once and then `Watching for changes…`; the server URL appears; the site opens. 5. Log in (`admin` / `password`), open **Plugins**. Expected: Gutenberg is active with a **Deactivate** link; no plugin has **Delete**; there is no **Add New Plugin**. 6. Open **Posts > Add New**. Expected: the editor loads. 7. Click **Stop build watch**, then **Stop dev server**. Expected on macOS: `pgrep -f gutenberg-trunk` in a terminal (anywhere) prints nothing within a few seconds. 8. In the terminal panel, type `npm run grunt`. Expected: refused, with the Gutenberg list (`build`, `dev`, `test:unit`, `lint`, `lint:js`). 8a. Look at the sidebar and the header. Expected: a **Gutenberg** tag on the row and next to the status (a Core site wears **Core** in the same places, since `00181bf` (was `df4f989` before the rebase onto trunk at `576d218`): JuanMa's ask on 2026-09-16, a mixed list read as one tagged and the rest bare); the checklist steps read "Download Gutenberg", "Compile the Gutenberg packages…", "Launch a WordPress with this checkout as its Gutenberg plugin…"; no **Trac ticket** card, a **GitHub issue** card saying it comes later; the apply card reads **Check out a pull request** with no `.diff` link. 8b. With the Gutenberg site in front, open `wpct://ticket/62281` from a browser (or `open -a "<build>/dist/mac-arm64/WordPress Contributor Toolkit.app" "wpct://ticket/62281"` with every other copy quit). Expected: "Ticket #62281 cannot be linked to <site>", no **Link ticket** button, `git branch` in the checkout shows no `ticket/62281`; click **Hide**, switch to a Core site: the question is asked there. 9. On the Gutenberg site, open **Review & submit changes** and try **Open a pull request** (sign in if asked). Expected: refused with "Opening a pull request from this site is not supported yet — save the patch file instead."; nothing reaches GitHub. 10. Create a second site with the default choice. Expected: exactly what a site was before this stack: `grunt -- _watch` in the watcher tab, `grunt` accepted in the terminal, a pull request opens as before. **What must not have happened:** a Core site changing in any way (step 10); a Gutenberg site reaching GitHub's fork or pull-request endpoints (step 9); the served Gutenberg site offering **Delete** on its plugin (step 5); a watcher process outliving Stop (step 7); the checkout gaining files from the served site (`git status` in it is clean apart from `build*`, which Gutenberg ignores). ## Risks and limitations - Review outcome across six passes: 15 [fix here] fixed, 12 [follow-up] (8 taken, 4 deferred: `pgrep -f` misses argv-less helpers, the no-Delete assertion's strength, and two pre-existing Core sentences in the Review & submit modal's staleness banner and dirty-tree detail). - On a Gutenberg site that has never been built, the dev-server start runs `npm run build` and then `npm run dev`, which builds everything again before watching: about twenty seconds twice on a fast machine. And the watcher tab reads "watching" while `dev` is still in its first build. Both are the #470 review's follow-ups; deferred here because collapsing the two needs the watcher's own "Watching for changes" line as the ready signal, which is its own change. - `sites:add` (adopting a folder) still records every adopted checkout as Core. Adopting a Gutenberg checkout gives a site that builds with grunt; detection (`package.json` `name`) is a small follow-up. - **Stop does not end Gutenberg's watcher tree on a slow machine.** On the macOS runner, 3 of 3 runs left `tsc --build` (the native TypeScript binary Gutenberg's `npm run dev` runs) alive after Stop: it ignores the SIGTERM the app sends the process group while it is mid-build, and the app's escalation to SIGKILL after 3 s reaches only the direct child (`npm:kill` in `src/main.js`). Locally, where the first build had finished, it died 3 of 3 times. The fix belongs to `kill-tree.js` and `npm:kill` on trunk, so it is its own pull request (#479); this PR's real-setup lane is what found it, and stays red on macOS until that lands. - The process-tree check runs on POSIX only, and by argv (`pgrep -f <site path>`), so a helper whose arguments carry no path, such as esbuild's service, is not seen by it; on Windows the workflow proves the flow, not the kill (its `taskkill /T` path has injected unit coverage in `kill-tree.test.cjs`). - The Plugins-screen assertion that no **Delete** link exists only bites if the stock install ships an inactive plugin; a stronger probe (`plugin-install.php` refusing under `DISALLOW_FILE_MODS`) is a follow-up. - A first Gutenberg serve needs the network for the WordPress release Playground then caches. - The sidebar heading still reads "WordPress Core"; it is the app's title, so a follow-up rather than this PR. - The name-field mutation seen by the real-setup lane in a fresh app (above) is unexplained. If a contributor ever reports a site named with letters they did not type, this is where to look. - The sidebar row's accessible name is now `<label> Core` / `<label> Gutenberg`; the engine journeys' helper matches the label plus that word. Any other test that finds a row by exact name has to do the same. ## Related Part of #251. Stacked on #471. --- <details> <summary>Design decisions and alternatives considered</summary> - A radio, not a dropdown: two options, both worth reading, and the help line under it is where a first-time contributor learns the choice is permanent. - "Create a site" rather than "Create a contributor site": the sidebar button is short and the dialog says the rest. - The choice is exposed here, after serve, rather than in #469 with the field: a Gutenberg site the app could create but not run is a state the app must not produce. </details> <details> <summary>Review outcome (required — see AGENTS.md)</summary> Six passes, each by a fresh Explore subagent context (local `/self-review`), each on the head that moved since the previous one. Totals: **15 [fix here] fixed, 12 [follow-up] (8 taken, 4 deferred and recorded under Risks)**. - **Pass 1, `3c0c4ba`**: 4 fix / 4 follow-up. The choice's copy promised "where its pull requests go" while `github:open-pr` targeted wordpress-develop for every site (fixed: the handler refuses non-Core sites, copy narrowed); a docs claim about empty names (fixed); no PR-time coverage of the radio (fixed: `legacy-site.spec.js`); stale comment and unused `description` field (fixed: per-option descriptions). Follow-ups taken: mutation comment reworded, three docs pages and the clone string; deferred: `pgrep -f` misses argv-less helpers, the no-Delete assertion's strength. - **Pass 2, `28978e7`**: reconciled; (this pass was the CodeRabbit-style re-check of the first's fixes, no new findings beyond the ones carried). - **Pass 3, `ecab2bd`** (screen adaptations): 3 fix / 3 follow-up. Four card strings decided in `index.jsx` (fixed: registry `cards` block); the `wpct://ticket` prompt not gated by provider, which could put a Gutenberg site on a Trac ticket branch with no card naming it (fixed: `deepLinkNotice` takes the provider, `unsupported` state); PR preview URL hardcoded to wordpress-develop and the modal pointing at a card the page lacks (fixed). Follow-ups taken: mentor destination back outside the gate, a journey with a seeded Gutenberg site, two docs pages. - **Pass 4, `94a12cb`**: 2 fix / 3 follow-up. "Not now" on the refusal note cleared the ticket app-wide while the text promised to keep it (fixed: hidden per site); the submit sidebar still said "fork wordpress-develop" and counted "the other two" (fixed: upstream-aware, neutral counts). Follow-ups taken: journey asserts no ticket branch, unused `workItemNoun` removed, the last inline string moved to `cards`. - **Pass 5, `e4057cd`**: 2 fix / 3 follow-up. The hide reset keyed on the ticket number, so the same ticket twice did nothing (fixed: keyed on the arrival); the signed-out sign-in pitch survived the refusal (fixed: refusal first). Follow-ups taken: `prAfter` and the sign-in sentence into the registry; deferred: the reset re-firing on the null-and-back prop (taken in pass 6). - **Pass 6, `2f2ded7`**: 3 fix / 2 follow-up. No data-side guard that a non-Trac target's cards say nothing of Trac (fixed: registry test); a dead Gutenberg `signInCannot` (fixed: removed); the destination's `cost` line still promised the fork (fixed: `prCost` per type). Follow-ups: the reset keyed on the arrival stamp (taken); pre-existing Core wording in the staleness banner and the dirty-modal detail (deferred, not introduced here). - **Review:** completed — heads `3c0c4ba`, `28978e7`, `ecab2bd`, `94a12cb`, `e4057cd`, `2f2ded7` / base `fee96e5`; deterministic layer (lint, `npm test`, journeys) and the opt-in real setup run by the author on every head; outcome above - **Since review:** `2f2ded7 → 3023bd3` reviewed by a seventh fresh-context pass: all four of pass 6's findings resolved, no new findings; lint, bundle, `npm test` (1484) and 30/30 journeys green on this head </details> <details> <summary>Screenshots or recording</summary> `docs/public/screenshots/empty-state.png` and `create-site-modal.png` regenerated from the fixture tier (`npm run shots -- --only=…`). Before: "Create WordPress Core site" and two fields. After: "Create a site" and the **Contribute to** radio between the name and the location. </details> 🤖 Generated with [Claude Code](https://claude.com/claude-code) <details> <summary>Stack correction review — 2026-09-17</summary> 0 remaining [fix here] · 0 new [follow-up] in this correction pass. No PR-local behavior changed; git range-diff reports every original commit unchanged (=) against its rebased base. - Current PR head: `08315156268f34ab94f6f0c81192f02642d48ce0`; configured base head: `244ca76c360e183a8da9f2343d7cf05fc05f5924`. Previous PR head: `ab2cfa708e325d58f8895113e1737bf20b76b3be`; only the inherited stack corrections and commit identities changed. - Reviewer: fresh Codex context `/root/review_fixes`. Completed five-dimension review of the correction delta from previously reviewed integrated head `bc8c4fda3fb78d51cd6f3abd5fc1b0f1e89c0095` to `c8c86677491012f9c9d4e99fe43b4ceff1c9647b`, then re-reviewed through final stack head `34550eec7a9031d2d5f03116801a60e313fa0559`. The additional runtime-deletion finding was fixed and verified; the original three findings remain resolved. Earlier review records above are historical. - Validation on the combined stack: lint; Node unit/integration/IPC suite (1,547 passed, two runtime skips); Electron suite (1,549 passed); 33 macOS journeys; documentation build. The real Playground restart/deletion regression passed on macOS. Windows CI results must be read on these new heads; this pass does not claim a fresh Windows real-setup run. - CodeRabbit's earlier check is not review coverage for these new heads. This local correction review is the review of record for the delta; no new bot review was requested. </details> --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
A Gutenberg checkout does not build or watch the way
wordpress-developdoes. Core's watcher isgrunt -- _watch, reached through npm's passthrough separator so Grunt'swatchwrapper does not redo the full build first; Gutenberg's isnpm run dev, its own incremental watcher, which would treat that separator's_watchas an argument it does not know. The terminal'snpm run <script>list is Core's too (grunt,watch,build:devexist only there). #251 needs both read from the site's type.What changes
planDevServerStarttakes the type'sbuildentry and returns its watcher; with no entry it returns Core's, so a caller that does not know the type gets what every site got before. Thegrunt -- _watchconstants and the reasoning behind them move from the planner into the registry, so the fact has one copy (the follow-up from #469's review). The card readsprojectTypefromsite:status, derives the type'sbuildentry once per render, and hands it to the watcher start and the terminal's script list.Both targets build with
npm run build, so the registry'sbuildScriptfield, which nothing needed to vary, is gone rather than threaded through five call sites as a constant.Deliberately not in this PR: anything a contributor can see. Nothing sends a type at creation yet, so every site is Core and behaves as it did. The choice appears in the PR that also serves a Gutenberg site through Playground, so that a Gutenberg site is never offered before it can be built, watched and run.
How to test this
Platforms: any. No user-visible surface: an existing site starts the same watcher and accepts the same scripts.
From the repository root:
The planner test is the demonstration: Core's plan is byte-for-byte what it was (
grunt,['--', '_watch']), and Gutenberg's isdevwith no arguments. The journeys drive the real card through the watcher start on a Core site.What must not have happened: a Core site's watcher gaining or losing the
--separator (the planner test pins both halves), or the terminal refusinggrunton a Core site.Risks and limitations
projectBuildlookup is untestable by construction (index.jsx, see TESTING.md); the planner test covers the decision, the journeys cover the Core path through the card.npm run devtree (tsc --watch,wp-build --watch, an esbuild service) outlived a plain SIGTERM to npm in the 15 Sep spike. The app stops watchers throughkill-tree.js(process group on macOS,taskkill /Ton Windows), which this PR does not touch; the PR that exposes the choice must confirm Stop ends that tree by hand on both platforms.Related
Part of #251. Stacked on #469.
Design decisions and alternatives considered
buildScriptrather than keeping it for symmetry: a field with one value across every target is a constant, and the review standard asks for complexity proportional to what is needed now.Review outcome (required — see AGENTS.md)
1 [fix here] · 4 [follow-up] — the [fix here] fixed in
34d4005, plus one style note taken.src/renderer/index.jsx: the terminal's help andnpm runguard closed over the type's script list, and the xterm creation effect depends onprintHelp's identity, so the first status reporting a non-Core type would have disposed and recreated the terminal (scrollback gone, a streaming command writing into a disposed instance). Invisible today, exactly what the picker PR would have tripped. Fixed: the handlers read the list through a ref, the indirectionterminalInputHandlerRefalready uses.dev-server-command.cjs/index.jsx:planDevServerStart'sneedsBuildhas no production consumer; the build-first decision is made inline instartBuildWatch. Pre-existing; collapse when the picker lands.index.jsxwatcher start: on an unbuilt Gutenberg site the flow runsnpm run buildand thennpm run devbuilds again (about 20 s), and the tab reads "watching" whiledevis still building. Not reachable until a Gutenberg site can exist; for the PR that exposes the choice.src/kill-tree.js: SIGTERM with no SIGKILL escalation; the Gutenberg watcher tree (tsc --watch,wp-build --watch, esbuild) needs a manual stop-and-pgreppass on both platforms once reachable. Nothing here touches it.docs/guide/terminal.md: lists Core's fixed script list, true until the picker; the picker PR updates it.s?.projectType || DEFAULT_PROJECT_TYPEduplicated the registry's own defaulting.Verified by the reviewer: the component holds a lookup, not a decision; every hook that reads
projectBuildlists it; there is no second builder of the watch command; the registry is in the esbuild bundle; the planner test pins Core's plan byte for byte and Gutenberg's separately./self-review); reviewed head0f8809c/ base85f8047; deterministic layer (lint,npm test, 29/29 journeys) run by the author; outcome above0f8809c → 34d4005checked: only the fix and the style note; lint, bundle andnpm testre-run greenScreenshots or recording
Nothing on screen changes.
🤖 Generated with Claude Code