test(e2e): assert a boot state the console actually settles in — Build & E2E main red (#4086) - #4095
Conversation
…ly settles in (#4086) `Build & E2E` went red on `main` at 9154d9e and green again at 8497579 with the suspected commit (#4081) still fully in place. Nothing in either diff can explain either transition, because the cause was never in the diff: the smoke test at `e2e/smoke.spec.ts:78` asserted `nav` OR the text /Initializing|Loading|Connecting/ — and against the harness's own artifact (a production bundle served by `vite preview` with no backend behind it) the app settles into neither. Measured on that exact artifact at 30 ms polling granularity, three consecutive boots agreeing to within 10 ms: + ~70 ms "Initializing application... Connecting to data source" splash + ~105 ms redirected to the signed-out sign-in screen, and stays there So the only state the old assertion could ever match was on screen for roughly 35 ms. The test passed by catching that window, and failed for the full 30 s timeout — identically on all three retries, since each retry re-runs the same race — whenever Playwright's first poll landed after it. That is a coin flip on runner speed, which is precisely the observed behaviour: identical code red at 06:15Z, green at 07:40Z. The app is not at fault. With `/api/v1/auth/get-session` unable to resolve a session, the shell is never entitled to render a `nav`, and `/login` is the correct destination; the test simply never listed it. The assertion now names all three recognised boot destinations, each of them terminal-stable, so it no longer depends on winning a race — while staying a closed set, so a blank page, a crashed render or an error boundary still fails it. Also fixes the missing-artifact rider named in the issue. CI selects Playwright's `github` reporter, which writes annotations and no `playwright-report/` directory at all, so the upload step warned `No files were found with the provided path` and every red E2E job discarded its own evidence. The screenshot, trace and `error-context.md` land in `test-results/`; both paths are uploaded now. Claude-Session: https://claude.ai/code/session_017Qqyix2QcnpUC9XeYVDzx3 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
The race re-fired on
|
| Commit | Build & E2E |
|---|---|
8aad9fd50 |
green |
9154d9e90 (#4081 — the suspect) |
red |
8497579db (#4081 still present) |
green |
47737ecb3 (a next bump) |
red |
Red, green, red across four unrelated commits, with the suspect present in both a red
and a green — a deterministic regression cannot produce that shape, and a ~35 ms
window losing a race can. This is the fourth data point for the diagnosis in the
description, and it moves the priority: main is red right now, and stays red on a
coin flip until this lands.
Note on this PR's two red shards — not from this branch
Test (shard 1/4) and Test (shard 2/4) are red here, and this PR cannot be the
cause: it changes e2e/smoke.spec.ts and .github/workflows/ci.yml, neither of which
a vitest shard runs. Both are dependency-range ratchets broken by the dependabot wave
that landed on main between 07:45Z and 08:03Z — lucide-react ^1.28.0 vs ^1.29.0
in packages/cli, vite ^8.2.0 vs ^8.2.1 in packages/create-plugin. Filed
separately as #4098.
Build & E2E on this branch — the gate this PR is about — is green, as are Type
Check, Lint, Control Byte Scan and Changeset Declaration.
Generated by Claude Code
Fixes #4086
The premise did not survive verification
Two of the issue's stated facts are disproven, and the third is real:
Build & E2Ered onmainat the smoke test9154d9e90(#4081) is the cause#4081 is exonerated by inclusion.
mainis green again at8497579db(run 31366842685,
Build & E2E= success, 07:43Z) with every line of9154d9e90still on it —toFilterNodehop,GRID_QUERY_INPUTS, pin test. A commit that deterministicallybroke the boot cannot be present on a green tip. The suspected import-cycle /
tree-shake mechanism is also ruled out mechanically: the
toFilterNodeimport wasadded to an import statement that already pulled
@object-ui/core, so the modulegraph did not change.
The console reaches paint. In the same red run,
should load the page without JavaScript errors,should render React content inside #root,should not show a blank pageandshould have correct page titleall passed. Only the shellassertion failed. An app that never boots cannot pass the first four.
What actually fails
e2e/smoke.spec.ts:78assertednavOR/Initializing|Loading|Connecting/i.Against the harness's own artifact — the CI build command byte for byte
(
VITE_BASE_PATH=/console/ pnpm --filter @object-ui/console exec vite build) servedby
vite preview, no backend — the app settles into neither. Measured at 30 mspolling granularity, three consecutive boots agreeing to within 10 ms:
The only state the old assertion could match was on screen for ~35 ms. It
passed by catching that window; it failed for the whole 30 s timeout — identically
on all three retries, because each retry re-runs the same race — whenever
Playwright's first poll landed after it. A coin flip on runner speed, which is
exactly the observed history: identical code red at 06:15Z, green at 07:40Z.
The app is not at fault. With
/api/v1/auth/get-sessionunable to resolve asession, the shell is never entitled to render a
navand/loginis the correctdestination. The test simply never listed the state the app is actually in.
The fix
The assertion now names all three recognised boot destinations — app shell,
boot splash, signed-out sign-in screen — each of them terminal-stable, so passing
no longer depends on winning a race. The set stays closed: a blank page, a
crashed render or an error boundary matches none of the three and still fails. The
sign-in screen is matched structurally (
[data-testid="login-config-loading"],#login-email,#login-phone) rather than by copy, so a locale change cannotsilently stop matching it.
Rider from the issue, also fixed: CI selects Playwright's
githubreporter, whichwrites annotations and no
playwright-report/directory — henceNo files were found with the provided path: playwright-report/and a red job thatdiscarded its own evidence. Screenshot, trace and
error-context.mdland intest-results/; both paths are uploaded now.Verification, both directions
Reverse verification had to be made deterministic, since the bug is a race — so
the settle step is forced (
waitForSelector('#login-email')) and then eachassertion is run against the settled page:
That is the CI failure reproduced verbatim, on demand. Note the honest shape here:
the old test could not be shown red by simply running it on this container — it
wins the race locally. What is falsifiable is the mechanism, and it is, in both
directions.
Full suite on this branch, mirroring the CI job (build, then
playwright test --project=chromium):#4081's pins intact —
pnpm exec vitest run --maxWorkers=2 packages/plugin-grid:Also green:
pnpm exec eslint e2e/smoke.spec.ts(exit 0),node scripts/check-control-bytes.mjs(3960 files),scripts/__tests__/merge-queue-reporting.test.ts(11 passed, the test that guardsthis workflow's gated jobs), and
ci.ymlre-parsed as YAML.No changeset
Only
e2e/and.github/workflows/change; no source of a released package does.node scripts/check-changeset-presence.mjsagrees:No
skip-changesetlabel is needed or possible — objectui#3724 records that neitherthat workflow nor that label was ever real in this repo; the presence script is the
gate.
Generated by Claude Code