Skip to content

Recover site running state to fix a stuck Start/Stop indicator#4253

Open
gcsecsey wants to merge 8 commits into
trunkfrom
gcsecsey/stu-2047-cant-stop-start-site
Open

Recover site running state to fix a stuck Start/Stop indicator#4253
gcsecsey wants to merge 8 commits into
trunkfrom
gcsecsey/stu-2047-cant-stop-start-site

Conversation

@gcsecsey

@gcsecsey gcsecsey commented Jul 20, 2026

Copy link
Copy Markdown
Member

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 _events exactly 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 _events process. 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:

  • never emitted — eg. the process-manager daemon crashes, tearing down (or orphaning) the site server without any "stopped" event
  • dropped before the renderer sees it — eg. the window isn't ready / is mid-teardown when the event fires
  • the last event in a racy sequence is wrong — eg. the stop→start restart triggered by a settings change.

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 reconciles
  • npm test -- src/hooks/tests/use-site-details.test.tsx: renderer reconciles after stop

Manual

Setup

  1. 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.
  2. Kill the events streamer Studio spawned — it is not restarted, so no further running-state events reach the UI:
    pkill -f "main.mjs _events"
    

(You can check with pgrep -fl "main.mjs _events" for running processes)

Test 1 — the button corrects on the first click (the main fix)

  1. Click Stop.
    • Before this change: after a brief spinner, the button snaps back to Running and stays stuck — the site is actually stopped (it won't open), but the UI never finds out.
    • After this change: it goes Stopping… → Start and stays correct. Click StartStarting… → Running. No repeated clicking needed.

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

  1. Start the site again. Then, without clicking anything in Studio, stop it from a terminal so the UI gets no event:
    node apps/cli/dist/cli/main.mjs site stop --path "<your site's folder>"
    
  2. Leave Studio untouched: within ~10s — or immediately if you click to another window and back — the indicator corrects to Start on its own.

Regression checks

  • Normal Start/Stop (with _events alive) still works and feels instant.
  • Creating a site still works; the in-progress site is not dropped from the list.
  • Edit site → Debugging → toggle Enable debug log → Save: the site restarts and ends in the correct state.

@gcsecsey gcsecsey changed the title Reconcile site running state to recover a stuck Start/Stop indicator Recover site running state to fix a stuck Start/Stop indicator Jul 20, 2026
@gcsecsey
gcsecsey marked this pull request as ready for review July 20, 2026 14:59
@gcsecsey
gcsecsey requested review from a team and Copilot July 20, 2026 14:59

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread apps/studio/src/tests/reconcile-running-state.test.ts
Comment thread apps/studio/src/hooks/use-site-details.tsx
@wpmobilebot

wpmobilebot commented Jul 20, 2026

Copy link
Copy Markdown
Collaborator

📊 Performance Test Results

Comparing 5a2327f vs trunk

app-size

Metric trunk 5a2327f Diff Change
App Size (Mac) 1361.91 MB 1361.91 MB +0.00 MB ⚪ 0.0%

site-editor

Metric trunk 5a2327f Diff Change
load 1074 ms 1035 ms 39 ms ⚪ 0.0%

site-startup

Metric trunk 5a2327f Diff Change
siteCreation 6484 ms 6507 ms +23 ms ⚪ 0.0%
siteStartup 2395 ms 2390 ms 5 ms ⚪ 0.0%

Results are median values from multiple test runs.

Legend: 🟢 Improvement (faster) | 🔴 Regression (slower) | ⚪ No change (<50ms diff)

@gcsecsey
gcsecsey enabled auto-merge (squash) July 22, 2026 11:05
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.

Can't stop/start a site

3 participants