Recover site running state to fix a stuck Start/Stop indicator#4253
Open
gcsecsey wants to merge 8 commits into
Open
Recover site running state to fix a stuck Start/Stop indicator#4253gcsecsey wants to merge 8 commits into
gcsecsey wants to merge 8 commits into
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
This PR adds a “ground truth” reconciliation path for site running state so Studio’s Start/Stop indicator can recover when the CLI _events stream misses/drops transitions (e.g., daemon crash, renderer teardown, racy restart sequences).
Changes:
- Adds main-process reconciliation that re-queries the CLI site list and updates in-memory running state.
- Exposes a new IPC method (
reconcileSites) and uses it from the renderer to correct running state after stop actions and via a periodic/focus-triggered safety net. - Adds tests for main-process reconciliation and renderer adoption after stopping a server.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| apps/studio/src/tests/reconcile-running-state.test.ts | Adds unit tests for main-process running-state reconciliation. |
| apps/studio/src/site-server.ts | Implements reconcileSitesRunningState() plus SiteServer.adoptRunningState() to update only running/url fields. |
| apps/studio/src/preload.ts | Exposes reconcileSites() on the preload IPC API. |
| apps/studio/src/ipc-handlers.ts | Adds reconcileSites IPC handler to reconcile then return updated site details. |
| apps/studio/src/hooks/use-site-details.tsx | Adds renderer-side reconciliation (after stops + periodic/visibility-based polling). |
| apps/studio/src/hooks/tests/use-site-details.test.tsx | Adds a test ensuring the hook adopts authoritative running state after stopping a server. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…t-site' into gcsecsey/stu-2047-cant-stop-start-site
Collaborator
📊 Performance Test ResultsComparing 5a2327f vs trunk app-size
site-editor
site-startup
Results are median values from multiple test runs. Legend: 🟢 Improvement (faster) | 🔴 Regression (slower) | ⚪ No change (<50ms diff) |
gcsecsey
enabled auto-merge (squash)
July 22, 2026 11:05
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.
Related issues
How AI was used in this PR
Used Claude Code to trace how Studio derives a site's running state, and reproduced the desync with an isolated CLI harness that forks
_eventsexactly like the main process does. The fix was then written test-first.Proposed Changes
A site's Start/Stop indicator can get permanently stuck out of sync with the actual server: showing "running" when the site is already down, or "stopped" when it's actually up. The reason is that Studio's running indicator is driven exclusively by a push stream of
site-events emitted by the CLI's_eventsprocess. There is no reconciliation. This works when every transition emits a correct event, but it has no way to recover when an event is either:In all of these cases, the indicator sticks with nothing to correct it, and it survives an app restart because the stale state is re-derived the same way on launch.
This PR adds a re-query safety net: the renderer periodically asks both on window refocus and periodically the main process to re-query the CLI for the authoritative running state and updates the indicator to match. Because it re-queries ground truth rather than replaying events, it recovers regardless of which specific event went missing. The poll only updates the running state for sites it already knows about, site creation/deletion stays owned by the event stream.
Notes
This PR addresses the persistent desync. It intentionally does not smooth the brief sub-second flip visible during a debug/Xdebug restart (that needs restart coalescing) or resolve orphaned-server port conflicts (separate daemon-reaping work).
Testing Instructions
Automated
npm test -- apps/studio/src/tests/reconcile-running-state.test.ts: main process reconcilesnpm test -- src/hooks/tests/use-site-details.test.tsx: renderer reconciles after stopManual
Setup
npm start, then create and start a local site (a custom domain is optional). Confirm it shows Running and the site opens in the browser.(You can check with
pgrep -fl "main.mjs _events"for running processes)Test 1 — the button corrects on the first click (the main fix)
Before this change:
CleanShot.2026-07-20.at.15.54.15.mp4
After this change:
CleanShot.2026-07-20.at.15.58.16.mp4
Test 2 — the safety net catches changes you didn't trigger
Regression checks
_eventsalive) still works and feels instant.