Skip to content

E2E WelcomePortal entry remains nondeterministic across startup states #532

Description

@qnbs

Current status — 2026-09-01

This issue remains OPEN. The supported-UI ensureWelcomePortalEntry() harness hardening reduced the original precondition problem but has not fully eliminated this nondeterministic startup/navigation failure class.

A recurrence was observed on unrelated docs-only PR #564 at exact head bf684034afe82d838dcd9bc62bfd9de0aecafbe6 in CI/CD run 33478269659 / Playwright job 99764976401:

117 passed
9 skipped
2 failed
2 flaky

The failures/flakes again converged on ensureWelcomePortalEntry() / WelcomePortal startup navigation in onboarding-entry-precondition.spec.ts and export.spec.ts, across Chromium and Mobile Chrome. PR #564 changes only the R-15 design contract / migration-ledger documentation, so there is no demonstrated S5/R-15 source relationship.

Current classification:

startup/harness determinism owner = #532
settings-only seed-authority ambiguity = RESOLVED by #546
underlying double-boot / unexpected startup-state root cause = OPEN
#527 regression = NO evidence

Preserve first-attempt failures as evidence. A same-head targeted rerun may classify intermittent behavior, but repeated retries must not substitute for root-cause analysis.

Summary

export.spec.ts's beforeEach originally assumed waitForSpaReady(page) always resolves via WelcomePortal, and clicked "Start a New Project" unconditionally. waitForSpaReady() actually races three success conditions (#sidebar, [data-tour="nav-mobile"], or the "Start a New Project" button) and succeeds on any of them — it does not distinguish a fresh boot from a returning-user boot. On the main post-merge run for PR #530 (run 33088546994, job 98579015311), all 3 Playwright retries of this test failed identically (byte-identical screenshot/error-context hashes) waiting on that locator, because the app had already booted into the main shell (Outline Generator view) instead of WelcomePortal. A same-SHA rerun (job 98582879363) passed cleanly, establishing a startup-state determinism gap rather than the already-fixed #527 portal-activation auto-seed race.

This is not a regression of #527/#530. Do not reopen #527 unless its exact mechanism is independently reproduced.

Evidence gathered from the original failure

From the failed job's attached Playwright trace (retry 1, chromium):

  • waitForSpaReady()'s internal action log shows the #sidebar wait resolving successfully before the "Start a New Project" wait ever got its own resolution — i.e. the race resolved via the main-chrome branch, not WelcomePortal.
  • Frame-snapshot URLs recorded across the trace: the page loaded at http://127.0.0.1:3000/WorldScript-Studio/ with no hash, then ~700ms later the hash became #/outline and stayed there for the remainder of the 20s timeout — i.e. the app's own in-app router (deepLinkService.pushHash) navigated to Outline Generator on its own, which per useApp.ts's readInitialView() priority (URL hash → ?view=localStorage['worldscript-last-view'] → dashboard) only happens when one of the first three sources was already non-empty.
  • Console log timeline shows the full boot sequence (Vite HMR connect, Using IndexedDB storage backend, SW registration, WorkerBus init) occurring twice within this single trace, with [WorldScript:DEBUG:app] Hydrating flat project state into Redux-Undo envelope. appearing only on the second occurrence. Per index.tsx's bootApp(), this log line only fires when preloadedState.project is truthy and in flat (non-envelope) format — i.e. a real, non-blank persisted project existed in IndexedDB by the second boot cycle, not merely a default/blank placeholder.
  • export.spec.ts is a single-test file using Playwright's stock, fully-isolated per-test browser context (no custom fixtures, no storageState, no globalSetup in playwright.config.ts) — cross-test-file storage leakage is ruled out as the mechanism.

Open root cause

The exact trigger for the double-boot cycle and/or unexpected WelcomePortal-entry state is not yet pinned down.

Candidate mechanisms requiring evidence include:

  1. register-sw.ts's unconditional controllerchangelocation.reload() (the existing DA-02 finding), which can explain a mid-boot reload but does not by itself explain every persisted-state observation.
  2. Early autosave / project persistence during boot, including whether a project can become durable before the WelcomePortal flow is semantically settled.
  3. A separate startup/navigation race in the supported-UI reset/entry helper itself, now made more plausible by the docs(core): define R-15 secure storage contract (#445) #564 recurrence where the helper sometimes fails to reach nav-mobile or welcome-portal.

The previously noted settings-only isNewUser ambiguity is no longer an outstanding candidate: PR #546 changed seed authority to depend on an actually hydratable/normalized persisted project rather than any truthy preloaded state. That correction does not explain or close the remaining double-boot / startup-navigation behavior.

Harness hardening already landed

ensureWelcomePortalEntry() in tests/e2e/helpers.ts establishes the WelcomePortal entry through supported UI: if "Start a New Project" is not visible after waitForSpaReady(), it uses Settings → Data & Backups → Factory Reset rather than touching storage/React internals directly. tests/e2e/onboarding-entry-precondition.spec.ts regression-tests multiple startup shapes.

This remains the correct direction for deterministic preconditions, but the #564 recurrence proves the current helper is not yet a complete closure of the startup/navigation failure class. Any further remediation should be performed in a dedicated #532 scope, not opportunistically inside unrelated PRs.

Recommended investigation / acceptance criteria

A dedicated #532 remediation should establish which of these is authoritative:

  • a real application startup-state/double-boot defect;
  • a deterministic harness/navigation defect;
  • or a combination of both.

Before closing #532, require evidence that:

  • ensureWelcomePortalEntry() reliably reaches its target from each supported startup shape used by CI;
  • Chromium and Mobile Chrome no longer fail because nav-mobile / welcome-portal never becomes reachable;
  • retries/timeouts are not merely masking the behavior;
  • any underlying application startup persistence/reload cause discovered is either fixed or separately tracked with a precise owner;
  • unrelated documentation-only PRs no longer intermittently fail this required gate for the same startup-state reason.

Non-goals

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions