# Helper Page Lifecycle Fix v2 — Same-Connection Close + Open **Date:** 2026-04-14 **Status:** Approved ## Problem Two issues remain after v1: 1. **Process restart leaves orphaned helper pages**: When the sg_claw process restarts, the old helper page tab remains open in the browser. The new process opens another one. 2. **Helper page is visible**: Uses `sgBrowerserOpenPage` (visible tab API) instead of `sgHideBrowerserOpenPage` (hidden domain API). ## Root Cause of v1 Failure The v1 `close_helper_page` function created a **second** WebSocket connection to the browser during `Drop`. This likely conflicted with the existing bootstrap connection, causing the browser's WebSocket state to become confused. ## Solution Send the close command on the **same** WebSocket connection used for bootstrap, before sending the open command: 1. Connect to browser WS 2. Register as "web" role 3. **Blindly send** `sgHideBrowerserClosePage(helper_url)` — closes any orphaned page from a previous process run 4. Send `sgHideBrowerserOpenPage(helper_url)` — opens the new helper page 5. Poll `/sgclaw/callback/ready` for page readiness Both `use_hidden_domain = true` and the close+open logic are combined into a single change. ## Why This Works - **Same connection**: Only one WebSocket connection to the browser. No conflict with existing connections. - **Best-effort close**: If no orphaned page exists (first run ever), the close command is silently ignored by the browser. This does not affect the subsequent open command. - **Fire-and-forget**: Both close and open commands use the same fire-and-forget semantics as the existing bootstrap command. ## API Reference | API | Wire format | Effect | |-----|------------|--------| | `sgHideBrowerserOpenPage` (API #6) | `[requesturl, "sgHideBrowerserOpenPage", url]` | Opens in hidden domain | | `sgHideBrowerserClosePage` (API #68) | `[requesturl, "sgHideBrowerserClosePage", url]` | Closes hidden domain page | ## Affected Files | File | Change | |------|--------| | `src/browser/callback_host.rs` | In `bootstrap_helper_page`: add close command before open command | | `src/service/server.rs` | Change `use_hidden_domain` from `false` to `true` | ## What Does NOT Change - `callback_backend.rs` — `SHOW_AREA`, `build_command` unchanged - `sgBrowserExcuteJsCodeByDomain` area parameter — stays `"show"` - Helper page HTML content — unchanged - `Drop for LiveBrowserCallbackHost` — remains simple (shutdown only, no close attempt) - `cached_host` in `mod.rs` — remains lifted to outer loop