Repository navigation
fix(web): recover when the web app fails to start - #201
Conversation
- If the app has not started 10 seconds after the page loads, an inline script in the root HTML shows "Switchify Remote didn't start" with a focused Reload button - Reload unregisters the service worker and clears its caches first, so a broken or stale copy cannot leave anyone on the static loading spinner - The root layout marks the app as started, removing the message if a slow connection made it appear; it only runs in production exports and native is unaffected Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
| it('tells the startup watchdog the app has started', () => { | ||
| const started = jest.fn(); | ||
| (window as Window & { __switchifyStarted?: () => void }).__switchifyStarted = started; | ||
| markAppStarted(); | ||
| expect(started).toHaveBeenCalledTimes(1); | ||
| delete (window as Window & { __switchifyStarted?: () => void }).__switchifyStarted; | ||
| expect(() => markAppStarted()).not.toThrow(); |
There was a problem hiding this comment.
Recovery script remains untested
The new test replaces __switchifyStarted with a mock, so it never runs startupWatchdog. All five focused tests still passed when the entire watchdog was replaced with a no-op, even though that removed the recovery screen and Reload button. This is a non-blocking coverage gap: future changes could leave users stuck on the loading screen without failing these tests. Add tests that execute the inline script and cover the 10-second delay, early and late startup, and Reload after successful or failed cleanup.
Artifacts
Executable coverage and browser validation script
- This executed script copies tracked sources into an isolated fixture, runs both Jest conditions, and captures Chromium behavior, making the coverage check reproducible.
Original watchdog source executed in Chromium
- The harness extracted this exact script from the PR source and verified its presence in the production export, tying the baseline browser evidence to the candidate code.
Disabled watchdog source used for the controlled mutation
- The harness substituted this no-op in the isolated source fixture and browser response, removing recovery behavior while the focused tests still passed.
Focused Jest output with the original watchdog
- The original-source run passed all five tests and reported zero HTML statement coverage, establishing the baseline.
Focused Jest output with the watchdog disabled
- The mutated-source run passed all five tests despite removing recovery behavior, demonstrating that the focused suite does not protect the watchdog.
▶ Original watchdog shows recovery and Reload
- Chromium loaded the production HTML with the app bundle blocked, waited through the real deadline, and clicked Reload, showing that baseline recovery works.
Recovery screen after the original watchdog deadline
- The screenshot captures the original watchdog after its timer fired, with DOM assertions confirming the recovery alert and focused Reload button.
▶ Disabled watchdog leaves recovery absent
- Chromium repeated the blocked-startup scenario with only the watchdog disabled and waited past the same deadline, showing the behavior loss that Jest missed.
No recovery screen after the disabled watchdog deadline
- The screenshot captures the controlled mutation after the deadline, with DOM assertions confirming that no recovery alert exists.
Combined executed validation output
- The captured run records both Jest results, exact watchdog sources, Chromium observations, cache cleanup, and restoration assertions, with exit code 0.
Tracked source and fixture restoration check
- The final command verified an empty tracked diff and equality between the restored fixture HTML and repository HTML, confirming no permanent tracked changes.
Ran code and verified through T-Rex
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/platform/installPlatform.web.test.ts
Line: 50-56
Comment:
**Recovery script remains untested**
The new test replaces `__switchifyStarted` with a mock, so it never runs `startupWatchdog`. All five focused tests still passed when the entire watchdog was replaced with a no-op, even though that removed the recovery screen and Reload button. This is a non-blocking coverage gap: future changes could leave users stuck on the loading screen without failing these tests. Add tests that execute the inline script and cover the 10-second delay, early and late startup, and Reload after successful or failed cleanup.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Closes #200
Summary
On a phone, the deployed web app stayed on the static loading spinner because its JavaScript never started. This makes sure nobody is left stuck there.
src/app/+html.tsx(production exports only) shows "Switchify Remote didn't start" with a focused Reload button if the app hasn't started 10 seconds after the page loads.RootLayoutcallsmarkAppStarted()after its first render. That removes the message if a slow connection made it appear late. Native has a no-op.The underlying failure on that phone is still unknown, because a fresh and a returning headless Chrome both start normally.
Validation
npm run validate: lint, typecheck, 692 Jest tests (including a newmarkAppStartedtest) and 21/21 Expo Doctor checks pass.🤖 Generated with Claude Code