[eric] outputs: serve-static apps' WS status frame no longer nulls frontend_url on a process that never exists; the Starting-preview wedge for built apps

This commit is contained in:
ciregenz
2026-08-16 11:20:28 -07:00
parent 3def185bf1
commit 3a56b99b0b
3 changed files with 29 additions and 1 deletions
+1
View File
@@ -92,6 +92,7 @@ P_RELEASES: List[ReleaseNote] = [
"Workflow run history shows each run's workflow name instead of the word \"Workflow\" on every row. The name now travels with the run, so it survives renames and deleted workflows.",
"An expired provider login heals itself mid-chat: the first failure rebuilds the connection and retries your message with zero clicks, and only a second failure asks you to reconnect. It used to take six manual steps every time a token aged out.",
"A newly connected ChatGPT or Gemini subscription works immediately. The routing layer restarts itself the moment a connect completes, so new subscriptions no longer sit dead behind rate-limit errors until you restart the app.",
"Finished apps opened from the dock no longer sit on \"Starting preview\" forever. Apps served straight from their built files have no server process by design, and the preview was waiting for one that would never exist.",
"Heavy sessions no longer vanish without a trace. When memory climbs past the safe line the app now sheds weight itself: preview thumbnails pause and refetchable caches drop, instead of growing until the operating system kills it mid-task.",
],
),
+2 -1
View File
@@ -312,7 +312,8 @@ async def websocket_runtime_logs(websocket: WebSocket, workspace_id: str, instan
"port": rt.port,
"backend_url": f"http://127.0.0.1:{rt.port}" if rt.running and rt.port else None,
"frontend_port": rt.frontend_port,
"frontend_url": rt.frontend_url if rt.running else None,
# The property is the one honest gate (crashed/suspended vite -> None, static -> serve URL); a duplicate running check here nulled every serve-static app's URL, whose card then waited forever on a process that never exists (the "Starting preview" wedge).
"frontend_url": rt.frontend_url,
"is_new_mode": rt.is_new_mode,
},
}
@@ -0,0 +1,26 @@
"""A serve-static app's card sat on "Starting preview" forever (found live 2026-08-16 on packaged
exp.9: runtime HTTP status said ready and its URL served 200/10.8KB, while the runtime-logs WS
status frame carried frontend_url null). Mechanism: the WS frame re-gated frontend_url on
rt.running, but serve-static runtimes have NO process by design, so running is False forever, and
with no process there are no log lines to trigger a status re-push: structurally stuck. The
frontend_url property is the one honest gate (crashed/suspended vite -> None, static -> serve
URL); these pin that the WS frame trusts it.
"""
import re
def test_ws_status_frame_uses_the_property_not_a_running_gate():
src = open("backend/main.py").read()
frame = src[src.index("def p_build_status_frame"):src.index("Initial status frame")]
assert '"frontend_url": rt.frontend_url,' in frame, "the property is the single source of truth"
assert "frontend_url if rt.running" not in frame, "the duplicate running gate is the Starting-preview wedge"
def test_property_still_refuses_dead_and_frozen_vite():
# Both directions: dropping the WS gate must not resurrect the dead-port ERR_FAILED class the
# gate was imitating; the property's own conditions are what protect that.
src = open("backend/apps/outputs/runtime.py").read()
prop = src[src.index("def frontend_url"):src.index("async def start")]
assert re.search(r"self\.frontend_port and self\.p_frontend_ready and self\.running and not self\.p_suspended", prop), \
"vite URLs must still require a live, unfrozen process"
assert "serve_static" in prop, "static apps get their serve URL with no process at all"