Skip to content

test(e2e): assert a boot state the console actually settles in — Build & E2E main red (#4086) - #4095

Merged
yinlianghui merged 1 commit into
mainfrom
claude/issue-4086-main-smoke-red
Aug 10, 2026
Merged

test(e2e): assert a boot state the console actually settles in — Build & E2E main red (#4086)#4095
yinlianghui merged 1 commit into
mainfrom
claude/issue-4086-main-smoke-red

Conversation

@yinlianghui

Copy link
Copy Markdown
Collaborator

Fixes #4086

The premise did not survive verification

Two of the issue's stated facts are disproven, and the third is real:

Claim Verdict
Build & E2E red on main at the smoke test True
"the console never reaches paint" False — it paints, and settles on the sign-in screen
9154d9e90 (#4081) is the cause False — see below

#4081 is exonerated by inclusion. main is green again at 8497579db
(run 31366842685,
Build & E2E = success, 07:43Z) with every line of 9154d9e90 still on it —
toFilterNode hop, GRID_QUERY_INPUTS, pin test. A commit that deterministically
broke the boot cannot be present on a green tip. The suspected import-cycle /
tree-shake mechanism is also ruled out mechanically: the toFilterNode import was
added to an import statement that already pulled @object-ui/core, so the module
graph 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 page and should have correct page title all passed. Only the shell
assertion failed. An app that never boots cannot pass the first four.

What actually fails

e2e/smoke.spec.ts:78 asserted nav OR /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) served
by vite preview, no backend — the app settles into neither. Measured at 30 ms
polling granularity, three consecutive boots agreeing to within 10 ms:

RUN 1
  +   37ms nav=0 loadingText=false url=/console/ |
  +   79ms nav=0 loadingText=true  url=/       | ObjectOS Initializing application... Connecting to data source
  +  112ms nav=0 loadingText=false url=/login  | ObjectOS Sign in to your account ...
  + 5646ms nav=0 loadingText=false url=/login  | ... Email Password Forgot password? Sign In
RUN 2   +71ms splash / +104ms sign-in
RUN 3   +70ms splash / +102ms sign-in

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-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 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 cannot
silently stop matching it.

Rider from the issue, also fixed: CI selects Playwright's github reporter, which
writes annotations and no playwright-report/ directory — hence
No files were found with the provided path: playwright-report/ and a red job that
discarded its own evidence. Screenshot, trace and error-context.md land in
test-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 each
assertion is run against the settled page:

✘  1 OLD assertion (nav | loading text) after settle — EXPECTED RED  (13.3s)
✓  2 NEW assertion (+ sign-in screen) after settle — EXPECTED GREEN  (7.2s)

   Error: expect(locator).toBeVisible() failed
   Locator: locator('nav').first().or(getByText(/Initializing|Loading|Connecting/i).first())
   Expected: visible
   Error: element(s) not found

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):

✓  33 e2e/smoke.spec.ts:103 › should settle into a recognised boot state (app shell, loading screen or sign-in) (1.3s)
   23 skipped
   10 passed (19.0s)

#4081's pins intact — pnpm exec vitest run --maxWorkers=2 packages/plugin-grid:

Test Files  58 passed (58)
     Tests  533 passed (533)

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 guards
this workflow's gated jobs), and ci.yml re-parsed as YAML.

No changeset

Only e2e/ and .github/workflows/ change; no source of a released package does.
node scripts/check-changeset-presence.mjs agrees:

Compared the working tree with 8497579db (merge-base with origin/main): 2 file(s)
changed, 0 of them under the src/ of a package the release covers ...
✅  No source of a released package changed in this range, so no changeset is owed.

No skip-changeset label is needed or possible — objectui#3724 records that neither
that workflow nor that label was ever real in this repo; the presence script is the
gate.


Generated by Claude Code

…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>
@vercel

vercel Bot commented Aug 10, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectui Ignored Ignored Aug 10, 2026 8:00am

Request Review

Copy link
Copy Markdown
Collaborator Author

The race re-fired on main while this PR was in CI — fourth occurrence, fourth commit

Build & E2E on main @ 47737ecb3
(run 31368387881, job
93391729992, 08:04–08:06Z) failed identically, 20 minutes after the run that
had gone green:

  1 failed
    [chromium] › e2e/smoke.spec.ts:78:3 › Console App – Smoke › should show the app shell or loading screen
  23 skipped
  9 passed (1.8m)

Same locator, same element(s) not found, same 30 s timeout on all three retries, and
the same discarded evidence:

##[warning]No files were found with the provided path: playwright-report/. No artifacts will be uploaded.

47737ecb3 is chore(deps): bump next from 16.2.12 to 16.3.0 (#4094). It touches
nothing the console boots through, and certainly nothing in plugin-grid. Combined
with the green run at 8497579db, the observed sequence on main is now:

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

@yinlianghui
yinlianghui marked this pull request as ready for review August 10, 2026 08:15
@yinlianghui
yinlianghui added this pull request to the merge queue Aug 10, 2026
Merged via the queue into main with commit 361dfdc Aug 10, 2026
17 of 19 checks passed
@yinlianghui
yinlianghui deleted the claude/issue-4086-main-smoke-red branch August 10, 2026 08:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Build &amp; E2E is red on main: the console smoke test finds neither app shell nor loading screen

2 participants