Skip to content

fix(e2e): root the live storage-state write and both storageState configs on their own file - #9636

Merged
os-try-charles merged 1 commit into
mainfrom
claude/issue-9519-live-e2e-storage-state-write-v2
Sep 16, 2026
Merged

os-try-charles merged 1 commit into
mainfrom
claude/issue-9519-live-e2e-storage-state-write-v2

Conversation

@os-try-charles

Copy link
Copy Markdown
Collaborator

Supersedes #9631 — same commit content, one line of commit message different. That pull request's single commit carried a co-author trailer naming a model; this branch carries the repository's model-free spelling instead. Nothing in the tree moved: both commits hash the same tree, 55f8b4770b2682a6ba0f7ebcd28458f60d45c3e8. A new branch rather than an amended one because AGENTS.md forbids force-pushing outright, and says in as many words that the ban is not graded by whether a branch has a single user. The superseded pull request is left for the dispatching seat to close.


Fixes #9519

The asymmetry this repairs

objectui#9188 rooted the live storage-state READ on the spec's own file. The WRITE, and the storageState option that names the same file to Playwright, still carried a bare relative path — which names no root, so it takes the ambient cwd.

participant before after
e2e/live/global-setup.ts (writes it) const STATE_PATH = 'e2e/live/.auth/state.json' join(REPO_ROOT, 'e2e/live/.auth/state.json'), REPO_ROOT from bare import.meta.url
playwright.live.config.ts (names it) storageState: 'e2e/live/.auth/state.json' storageState: STATE_PATH (absolute, rooted on the config file)
playwright.import-console.config.ts (names it) same bare string — a third instance, see below same repair
e2e/live/inline-edit-polish-2572.spec.ts (reads it) already repo-root rooted (objectui#9188) unchanged

Measured, not inferred

All three readings below come from real runs on this tree, with the launch directory as the only variable, a fake sign-in endpoint standing in for the backend, and the preinstalled Chromium.

1. The doubled path reproduces. Real playwright.live.config.ts + real global setup, started from e2e/, before the repair:

[live-e2e] authenticated as admin@objectos.ai; storageState written to e2e/live/.auth/state.json
PROBE cwd=/…/e2e configStorageState=e2e/live/.auth/state.json repoRootedExists=false
  Error: ENOENT (repo-root read): /…/e2e/live/.auth/state.json
on disk: e2e/e2e/live/.auth/state.json

Same command from the repository root: green, file at e2e/live/.auth/state.json — so the instrument discriminates and the failure is the cwd, not the probe.

2. The config half was unmeasured; now it is measured, and it needed the repair. Playwright 1.62.1 resolves a relative use.storageState against the process cwd, never the config directory. Probe with both copies present, run from a subdirectory of the config dir: the cwd copy won. Control, with only the config-dir copy on disk: Error reading storage state from state/probe-state.json: ENOENT. Two neighbouring options behave the other way and were left alone: testDir and globalSetup both resolved against the config directory in the same runs.

3. After the repair, from e2e/:

[live-e2e] ... storageState written to /…/objectui-issue-9519/e2e/live/.auth/state.json
PROBE cwd=/…/e2e configStorageState=/…/e2e/live/.auth/state.json repoRootedExists=true
  1 passed

No e2e/e2e/ tree is created. Control from the repository root: also green.

4. Ablation (one-shot, restored). With the fix committed, git checkout of the base revision put the two bare strings back on disk (grep-confirmed before the run); the probe went red with the doubled path again, and the new content pin went 2 failed | 3 passed — the two reverted files failing, playwright.import-console.config.ts still green because it was not reverted. Restored with git checkout HEAD -- …; git diff HEAD empty and both blob hashes equal to their HEAD blobs.

The third instance (⭐ reported, and repaired here)

playwright.import-console.config.ts carried a byte-identical storageState: 'e2e/live/.auth/state.json' and shares the same globalSetup. It is the same defect class, the same mechanical repair, and no open pull request holds that file (checked over all open PRs, 1765 filenames examined; positive control on the same instrument: .github/workflows/ci.yml is held by PR #9584). Repairing the live config and leaving this one would have left the card reproducible one config over.

What is NOT done here, deliberately

The path-roots gate's population is TEST_FILE — *.test.* / *.spec.* — so a global-setup.ts and a *.config.ts match neither, which is how its registry could reach zero while these instances sat one directory away. Widening that population is not in this pull request: its pin test is held by PR #9584, and the scope decision belongs on a card of its own. Instead, scripts/__tests__/live-e2e-storage-state-roots-9519.test.ts holds the four known ends of this one path together by content, and states in its header that the gate-population question is open.

Verification

  • pnpm exec vitest run scripts/__tests__/check-test-path-roots.test.ts scripts/__tests__/e2e-type-check.test.ts scripts/__tests__/live-e2e-storage-state-roots-9519.test.ts — 3 files, 42 tests, passed.
  • pnpm type-check:e2e — exit 0 (this project compiles e2e/** and the root playwright*.config.ts files).
  • pnpm lint:root — exit 0 (32 pre-existing warnings, 0 errors); this task's scope covers e2e/, scripts/ and the root configs.
  • pnpm check:test-path-roots — OK. pnpm check:control-bytes — OK. pnpm check:new-line-citations — 0 new citations.
  • node scripts/check-changeset-presence.mjs — exit 0, verdict line: "No source or published contract of a released package changed in this range, so no changeset is owed." (the diff touches no packages/** file.)
  • Not measured here: a full live run against a real backend and console — this branch changes only where the state file is rooted, and CI's live lane runs pnpm test:e2e:live:ci from the repository root, where the bare and the rooted spelling are the same file.

Acceptance notes

  • Noted, not filed: e2e/live/ci/better-auth-pin.mjs resolves its app directory from a CLI argument with path.resolve. That is an explicit parameter, the documented behaviour of a CLI, and the gate names "a root that arrives as a function parameter" as a deliberate blind spot rather than this defect. Carrier if anyone revisits it: whoever next edits that script.
  • The seat asked for a third cwd-rooted path if one existed. It did — playwright.import-console.config.ts, repaired above rather than left for later.

This work was generated by Claude Code in session session_015h79niBMyoB1xcaQje3uiz.

🤖 Generated with Claude Code

https://claude.ai/code/session_015h79niBMyoB1xcaQje3uiz


Generated by Claude Code

…igs on their own file

objectui#9188 repaired the READ of `e2e/live/.auth/state.json`; the write in
`e2e/live/global-setup.ts` and the `storageState` option in two Playwright
configs still carried a bare relative path, so they took the ambient cwd.

Measured on this tree, only the launch directory varying: a run started from
`e2e/` really created `e2e/e2e/live/.auth/state.json`, and the repo-root-rooted
read then found nothing. Playwright 1.62.1 resolves a relative
`use.storageState` against the process cwd, not the config directory (probed
both ways with a control), so the config half needed the same repair, and
`playwright.import-console.config.ts` carried a third copy of the same string.

All three now take the repository's one rooting spelling, bare
`import.meta.url` taken apart by hand. A content pin holds the four ends of
that path together, since a `global-setup.ts` and a `*.config.ts` match neither
half of the path-roots gate's `TEST_FILE` population.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015h79niBMyoB1xcaQje3uiz
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

2 participants