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 controllerchange → location.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

Activity

  1. qnbs commented on Aug 31, 2026

    @qnbs
    OwnerAuthor

    Related startup-authority update from PR #546 — 2026-08-31

    One candidate observation documented in this issue has now been resolved independently by the S2/#531-A work in PR #546:

    settings restored
    project absent
    

    is no longer treated as sufficient evidence that a real persisted project exists for initial metadata-seed authority. The boot path now derives that authority from an actually hydratable/normalized persisted project payload, so a settings-only state remains eligible to initialize the synthetic fresh project.

    This removes the previously noted const isNewUser = !preloadedState / settings-only conflation as an outstanding candidate for this class of startup behavior.

    It does not close #532.

    The core unresolved question here remains the separate startup-state/double-boot mechanism that can result in a real project being present before an E2E test expects WelcomePortal. The existing supported-UI ensureWelcomePortalEntry() harness fix remains the correct deterministic test precondition; #546 should not be interpreted as proving or fixing the underlying Service Worker reload / autosave / other startup-state cause.

    Recommended current classification:

  2. qnbs commented on Sep 1, 2026

    @qnbs
    OwnerAuthor

    Recurrence on unrelated docs-only PR #564 — 2026-09-01

    New evidence materially strengthens this issue's classification as an independent startup-state / WelcomePortal-entry determinism problem rather than a regression caused by the current feature work.

    Exact recurrence

    On PR #564 (docs(core): define R-15 secure storage contract (#445)), exact head:

    bf684034afe82d838dcd9bc62bfd9de0aecafbe6
    

    CI/CD run 33478269659 / Playwright job 99764976401 failed while the PR itself changes only the R-15 design contract / migration-ledger documentation and no application, onboarding, export, storage-runtime, or E2E source.

    Playwright result:

    117 passed
    9 skipped
    2 failed
    2 flaky
    

    The failures/flakes again converge on the existing ensureWelcomePortalEntry() / WelcomePortal startup precondition path:

    • onboarding-entry-precondition.spec.ts failed in both Chromium and Mobile Chrome waiting for [data-tour="nav-mobile"] → More;
    • export.spec.ts was flaky waiting for getByTestId('welcome-portal') after ensureWelcomePortalEntry();
    • the second onboarding precondition case was likewise flaky waiting for welcome-portal.

    This recurrence is especially useful because PR #564 is semantically unrelated documentation-only work. It therefore provides additional evidence against treating this class as an R-15/S5 regression.

    Current interpretation

    The existing issue ownership remains correct:

    S5 / PR #564 relation = NONE demonstrated
    startup/harness owner = #532
    settings-only seed ambiguity = already resolved by #546
    underlying double-boot / unexpected startup-state root cause = still OPEN
    

    The prior ensureWelcomePortalEntry() supported-UI hardening remains useful, but this recurrence shows that it has not fully eliminated the nondeterministic startup/navigation failure class under CI.

    Anti-cascade / next evidence rule

    Do not fix this inside PR #564 and do not reopen #527. Preserve the first-attempt failure. A single same-head targeted rerun is useful only to distinguish intermittent CI/startup behavior from a consistently reproducible gate failure. Repeated retry loops should not substitute for root-cause evidence.

    If the same-head rerun passes, retain this recurrence as flake evidence and continue investigating #532 separately. If it fails again with the same path, treat that as stronger reproducibility evidence for a dedicated #532 remediation rather than absorbing onboarding/export changes into unrelated PRs.

  3. changed the title [-]export.spec.ts E2E precondition assumes WelcomePortal unconditionally (startup-state determinism)[/-] [+]E2E WelcomePortal entry remains nondeterministic across startup states[/+] on Sep 1, 2026
  4. qnbs commented on Sep 1, 2026

    @qnbs
    OwnerAuthor

    Fresh recurrence on PR #564 exact head 0865895f2c3070b8923556f61134013a0d077eba, CI/CD #2571, Playwright job 99798975581: onboarding-entry-precondition.spec.ts again traversed ensureWelcomePortalEntry() and failed to reach welcome-portal after navigating to #/settings. In this run Playwright ultimately classified that test as flaky; 119 tests passed, 9 skipped. This remains separate from the run's final blocking failure, which was a reproducible Axe contrast defect now tracked in #565. No #564/S5 source ownership is inferred.

  5. qnbs commented on Sep 2, 2026

    @qnbs
    OwnerAuthor

    Another recurrence on PR #564 (still docs-only: docs/native/R15-SECURE-STORAGE-CONTRACT.md + docs/native/CORE-MIGRATION-LEDGER.md), this time at head 1212d4bb4b1f... (verify via PR), CI/CD run 33588480169.

    Identical signature across all 3 attempts in this run:

    • [Mobile Chrome] › tests/e2e/onboarding-entry-precondition.spec.ts:18:3 › ... reaches the entry point when a non-English language is already persisted
    • Error: expect(locator).toBeVisible() failed / element(s) not found at onboarding-entry-precondition.spec.ts:24:5
    • 120 passed / 1 failed each time, same test, same project, all 3 in-run Playwright retries also failed identically

    No demonstrated relationship to this PR's content (2 markdown files only). Consistent with this issue's existing classification. Continuing with additional reruns per the existing "same-head targeted rerun to classify intermittent behavior" guidance rather than treating this as a PR #564 blocker.

  6. added a commit that references this issue on Sep 2, 2026
  7. qnbs commented on Sep 2, 2026

    @qnbs
    OwnerAuthor

    Tracked a plausible additional contributing mechanism separately in #585 (SW clients.claim() causes an unconditional reload on a brand-new browser context's first page load) — found while verifying PR #583's fixes. Not folded into #583/#532's own fix, since it's a distinct production service-worker behavior question needing its own review, not a test-harness fix.

  8. 18 remaining items

  9. qnbs commented on Sep 12, 2026

    @qnbs
    OwnerAuthor

    Integration note — #736 required-gate hardening must preserve #532 failure evidence

    #736 now defines the lean required Playwright product spine and includes startup/Onboarding → Blank Project as a protected critical journey.

    This does not supersede #532. The WelcomePortal/double-boot/startup-state nondeterminism here remains its own source/harness correctness owner.

    When #736 refactors or isolates required E2E setup:

    • do not mask E2E WelcomePortal entry remains nondeterministic across startup states #532 with broader retries, arbitrary sleeps, timeout inflation, .catch(() => {}), conditional skips, or raw storage surgery;
    • preserve first-attempt failure evidence;
    • distinguish PRODUCT_UI_PRECONDITION from deterministic supported seeding for tests that are not themselves testing onboarding;
    • avoid forcing every Writer/Settings/Export spec through WelcomePortal setup, because that turns one startup failure into a cascade and obscures independent evidence;
    • retain at least one genuine supported-UI startup/Onboarding contract in the required lane.

    A better-isolated suite may reduce cascading failures without reducing #532's visibility. If #736 changes test topology and the same startup failure still reproduces, keep the evidence here rather than reclassifying it as generic E2E flakiness.

  10. qnbs commented on Sep 23, 2026

    @qnbs
    OwnerAuthor

    Housekeeping closure — successor PR #596 landed the deferred root-cause work (2026-09-23)

    This issue was deliberately reopened after #590 because #590 only extracted the locale-independent recovery-navigation slice. The reopening comment stated the intended closure condition clearly: close again once the broader #583 reset/startup work lands and post-merge evidence is green.

    That broader work did land via #583's authoritative successor PR #596, merged as:

    a8131b624433bd79c818bb6780050c5cb2f7fec4

    PR #596 implemented the async generation/epoch-based IDB reset-quiescence and open-admission contract across the long-lived stores, fixed the confirmed WelcomePortal recovery/harness mechanisms, hardened Factory Reset deletion ownership/blocked/error behavior, and added focused regression coverage.

    Since that merge:

    • no later E2E WelcomePortal entry remains nondeterministic across startup states #532-specific recurrence has been recorded;
    • multiple subsequent main/release/feature/dependency waves have exercised the repository;
    • current resulting-main CI/CD #3463 on 2de980db... is green;
    • both 🎭 E2E Tests (Playwright) and 🔬 E2E Deep Coverage (feature-flag matrix) are SUCCESS on that current main.

    The remaining distinct Service Worker / multi-context lifecycle risks are not reason to keep #532 open; they already have specialist owners (#480/#485/#614 and related lifecycle work). If the exact WelcomePortal/startup signature reappears after #596 on a future head, open/reopen from fresh evidence rather than preserving this historical P0 indefinitely.

    Closing as completed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions