test(ui): fix Node 25+ localStorage crashes in jsdom suites; document PR-open CI drops#110812
Conversation
Node 25+ ships a default-on global localStorage that is dead without --localstorage-file (undefined on 26.5; throwing or inert elsewhere). It shadows jsdom's Storage during vitest global population, so storage-touching UI tests crash on newer local Node while Linux CI (Node 24) passes — six files failed locally on Node 26.5. The shared setup now capability-probes (round-trip, throw-safe), prefers jsdom's own window Storage when only the global alias is dead, and otherwise installs one in-memory Storage on both window and globalThis. Also documents the PR-open CI drop pattern in AGENTS.md: fresh PRs race GitHub's merge-ref computation and the open-event CI run can drop or die as startup_failure/BuildFailed (not rerunnable); verify attach and close/reopen to re-fire.
|
Codex review: found issues before merge. Reviewed July 18, 2026, 12:55 PM ET / 16:55 UTC. Summary PR surface: Source +67, Docs +1. Total +68 across 2 files. Reproducibility: yes. The PR supplies a concrete current-main Node 26.5 path: affected jsdom tests fail when Review metrics: none identified. Stored data model Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Risk before merge
Maintainer options:
Copy recommended automerge instructionNext step before merge
Maintainer decision needed
Security Review findings
Review detailsBest possible solution: Keep the capability-based UI test shim, but revise the CI recovery guidance to verify the applicable pull-request workflow/run against the merge commit or its PR association before recommending a close/reopen. Do we have a high-confidence way to reproduce the issue? Yes. The PR supplies a concrete current-main Node 26.5 path: affected jsdom tests fail when Is this the best way to solve the issue? No. The capability-based storage shim is an appropriate narrow fix, but the accompanying CI recovery instruction should not use the head SHA as the only proof that a pull-request workflow attached. Full review comments:
Overall correctness: patch is incorrect AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 837db49ebb24. Label changesLabel changes:
Label justifications:
Evidence reviewedPR surface: Source +67, Docs +1. Total +68 across 2 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
Review history (1 earlier review cycle)
|
|
Merged via squash.
|
… PR-open CI drops (openclaw#110812) * test(ui): shim dead Node 25+ WebStorage localStorage in jsdom test setup Node 25+ ships a default-on global localStorage that is dead without --localstorage-file (undefined on 26.5; throwing or inert elsewhere). It shadows jsdom's Storage during vitest global population, so storage-touching UI tests crash on newer local Node while Linux CI (Node 24) passes — six files failed locally on Node 26.5. The shared setup now capability-probes (round-trip, throw-safe), prefers jsdom's own window Storage when only the global alias is dead, and otherwise installs one in-memory Storage on both window and globalThis. Also documents the PR-open CI drop pattern in AGENTS.md: fresh PRs race GitHub's merge-ref computation and the open-event CI run can drop or die as startup_failure/BuildFailed (not rerunnable); verify attach and close/reopen to re-fire. * fix(test): drop unnecessary String() conversions in localStorage shim
What Problem This Solves
Developers on current Node (25/26) get 60+ spurious UI test failures locally: any jsdom test touching
localStoragecrashes withCannot read properties of undefined (reading 'clear')while the same tests pass on Linux CI (Node 24). Six files fail on Node 26.5 (lobster-pet,lobster-dex,dock-panel-layout,activity-page,chat-realtime,chat-pull-requests), which makes real local red herrings out of green code and masks genuine failures.Separately, three consecutive fresh PRs this week (#110472, #110528, #110623) had their pull_request-open CI run drop or die as
startup_failure, each needing a close/reopen; repo history shows ~10–16 such runs daily.Why This Change Was Made
Node 25+ enables WebStorage by default:
globalThis.localStoragebecomes an own-property getter that is dead without--localstorage-file(measuredundefinedon Node 26.5; other releases reportedly throw or return an inert proxy). During vitest jsdom global population the dead getter shadows jsdom's Storage — probed in-environment:globalThis === windowand bothlocalStoragebindings areundefined. Node 24 has no default WebStorage, so CI never sees it.The shared UI test setup (
lit-warnings.setup.ts, already the home for jsdom gap shims) now capability-probes the global with a throw-safe set/get/remove round-trip, prefers jsdom's ownwindow.localStoragewhen only the global alias is dead, and otherwise installs a single in-memoryStorageon bothwindowandglobalThisso the two names stay one object.The CI-drop pattern was diagnosed to a GitHub-side race: PRs created immediately after their branch push fire the open event before the merge ref is computed (
mergeable: null), and the CI run either never attaches or is created asstartup_failure/BuildFailed/(Unknown event)— whichgh run rerunrefuses to retry. The workflow triggers inci.ymlare correct; the fix is operational, soAGENTS.mdnow instructs verifying the CI attach after PR creation and re-firing via close/reopen.User Impact
Contributor/local-dev only: UI tests pass on Node 24, 25, and 26 without flags; no runtime, product, or CI behavior changes. Agents stop losing PR cycles to silently missing CI runs.
Evidence
undefined; in vitest jsdom,globalThis === windowwith both storage bindings dead.node scripts/run-vitest.mjs ui/src/components/lobster-pet.test.ts— 44/44 failing on Node 26.5 (reproduced on cleanmain). After: all six previously failing files pass; fullui/srcsuite exit 0 on Node 26.5.gh apion run 29634070586 showspath: BuildFailed,display_title: "(Unknown event)", empty referenced workflows; 100 most recentstartup_failureruns span five days at ~10–16/day, allpull_requestevents across many authors' branches.