Skip to content

test(e2e): tell an open modal from a blank viewport in the boot-continuity coverage rule - #7305

Merged
yinlianghui merged 1 commit into
mainfrom
claude/issue-6578-boot-coverage-discriminator
Sep 2, 2026
Merged

test(e2e): tell an open modal from a blank viewport in the boot-continuity coverage rule#7305
yinlianghui merged 1 commit into
mainfrom
claude/issue-6578-boot-coverage-discriminator

Conversation

@yinlianghui

Copy link
Copy Markdown
Collaborator

Fixes #6578

The Console boot continuity coverage rule read an open modal dialog as a blank
viewport, on every sample for as long as the modal was open. This adds the
discriminator the card said was not established, after measuring three candidates
through the card's own four control arms.

Measurement first, per the 2026-08-27 triage ruling: nothing was written until a
candidate was shown to stay RED on the genuinely-blank arm and GREEN on the modal arm.

Instrument

Production console bundle (VITE_BASE_PATH=/console/ pnpm turbo run build --filter=@object-ui/console,
35 tasks, exit 0) served by vite preview on :4173. Signed-out /login boot with
this spec's own endpoint mocks. Arms are page.evaluate DOM mutations applied after
the boot completes, so the only variable per arm is the DOM shape. Sampling cadence
is unchanged (every mutation and every animation frame after React's first commit),
settle 1500 ms, n=3 per arm. Chromium 141.0.7390.37.

Pixel ledger alongside every row: CDP Page.startScreencast at everyNthFrame: 1,
each frame classified by objectui#6378's white rule (no colour channel below 242),
correlated with the DOM ledger on a shared performance.timeOrigin.

Candidate table (four arms, post-arm samples, n=3)

Uncovered-sample counts. RED on the blank arm and GREEN on the modal arms is what a
candidate has to show.

arm samples top rule (current) full-stack rule (a) host holds the point + rendered content (c) portal-aware hit test (b) white frames / post-arm frames min channel
untouched — must read covered 91-92 0 0 0 0 0 / 1-2 0
#root emptied + splash removed — must read uncovered 91 91 91 91 91 1 / 1 246
body-level overlay, app hit-testable underneath 90-92 90-92 0 0 0 0 / 3 0
overlay + body{pointer-events:none} (Radix modal shape) 92 92 92 0 0 0 / 1-3 0

Row 2 reproduces #6570's number exactly: min channel 246, the bare page background.
Rows 3-4 are a fully rendered, fully visible app that both DOM rules call empty.

All three candidates clear both arms on these four. The fifth shape below is what
separates them.

Portal survey — and the measurement that decided it

Shapes derived one-for-one from the real component sources in
packages/components/src/ui/. dropdown-menu.tsx and popover.tsx pass no modal
prop, so Radix's defaults apply (DropdownMenu modal, Popover not).

shape top rule full-stack (a) (c) white frames min channel
modal DropdownMenu — portal off-centre, no overlay, body{pointer-events:none} 87-91 87-91 0 87-91 0 / 1-4 0
non-modal Popover 0 0 0 0 0 / 2-3 0
Tooltip 0 0 0 0 0 / 1-3 0
Toast viewport / sonner, bottom-right 0 0 0 0 0 / 1-2 0

Two things this establishes that the card left open:

  1. The false positive is not confined to the dialog shape. A modal
    DropdownMenu produces it too, and there the centre element is the bare html
    rather than any portal — Radix parks pointer-events: none on the body for every
    modal layer, whether or not that layer renders a full-screen overlay.
    AlertDialog, Sheet and CommandDialog are the dialog shape by source
    (fixed inset-0 z-50 bg-black/80 overlay inside a portal), so they read as row 4.
  2. Popovers, tooltips and toasts are benign. None of them covers the viewport
    centre and none is modal, so the hit test still lands inside #root.

What ships, and why the other two were rejected

Shipped — (a) a named host still holds the sample point with something rendered in
it.
When the hit test says "uncovered", the rule asks the second question the
defect is actually about. objectui#6378's window is an empty #root, which answers
no and stays red. It is consulted only after the hit test has already failed, so it
can only reclassify a sample the old rule called uncovered — never the reverse — and
costs nothing on a covered sample.

Rejected — (c) portal-aware hit test. It reads 87-91 uncovered on the modal
DropdownMenu shape: with no overlay under the sample point there is no portal at the
point to recognise, so the false positive survives. It also keys the gate to one
component library's attribute spellings, which a Radix major could rename while the
gate stayed green.

Rejected for the gate, kept as the arbiter — (b) the pixel ledger. It separates
every arm correctly and is why this PR can claim the blank arm is genuinely blank
(min channel 246) and the modal arms are genuinely painted (min channel 0). It is a
bad gate for the reason this file already states about #6378: it needs a CDP
session, so it is chromium-only where this spec runs five projects, and it decides on
frames the compositor happens to swap. Measured here: on the blank arm it had one
post-arm frame to decide 1500 ms on, where the DOM rule had 91 samples.

Ablation

The probe is extracted from e2e/console-boot-indicator.spec.ts on disk and
transpiled, so mutating the file really changes what is measured.

Mutation const ok = hit || appStillHoldsPoint(cx, cy); to const ok = hit;,
confirmed on disk before the run: deleted-text count 1 to 0, injected-text count 0 to
1, blob 147c6cdd to 0c0a2971. The extracted probe that ran carries
const ok = hit; at line 106.

arm shipped rule (uncovered) discriminator removed (uncovered)
untouched 0
#root emptied + splash removed 91-92 (stays RED) 92-94
body-level overlay 0 89-91
overlay + body{pointer-events:none} 0 91-92
modal DropdownMenu 0 89-93

Restore proven by state, not by an exit code: on-disk blob back to 147c6cdd
(equal to the HEAD blob), git diff HEAD empty, git status --porcelain empty.
The mutation ran under trap ... EXIT INT TERM with absolute paths.

The spec's own run

Running 4 tests using 2 workers
  ok  2 [chromium] > console-boot-indicator.spec.ts:47:3 > ships the indicator in the HTML document, not in a JS chunk (276ms)
  ok  1 [chromium] > console-boot-indicator.spec.ts:86:3 > renders the indicator with the app bundle blocked (478ms)
  ok  3 [chromium] > console-boot-indicator.spec.ts:119:3 > paints content BEFORE React mounts, and hands over without a gap (2.2s)
  ok  4 [chromium] > console-boot-indicator.spec.ts:285:3 > never hands the viewport to an empty document between splash and destination (2.7s)

  4 passed (4.3s)

The signed-out boot this spec runs never opens a modal, so its own reading is
unchanged at 0 uncovered — the change is for the signed-in scenarios the campaign
wants to add next, which is where the false positive bites.

Gates, at the final commit

git rev-parse --short HEAD = 5e61d0e96, and every reading below is from that tree.

gate verdict
pnpm type-check:e2e exit 0
npx eslint e2e/console-boot-indicator.spec.ts exit 0, 0 errors, 0 warnings
pnpm check:control-bytes check-control-bytes: OK (scanned 6008 tracked text file(s); skipped 85 binary)
node scripts/check-changeset-presence.mjs No source or published contract of a released package changed in this range, so no changeset is owed.
pnpm exec vitest run scripts/__tests__/e2e-type-check.test.ts 1 file passed, 14 tests passed
Playwright, this file, chromium 4 passed

Declared deviations

Scope

One file, e2e/console-boot-indicator.spec.ts. No apps/console or
packages/app-shell source, no change to the splash, no timeout raised, no test
skipped or quarantined, no live-e2e / import-harness config touched, and no shared
helper added (an addInitScript callback is serialised, so it cannot import one).

🤖 Generated with Claude Code

https://claude.ai/code/session_01BGMDbrVa8JjZcCQ7DWYH1b


Generated by Claude Code

…nuity rule

The `Console boot continuity` coverage rule read an open modal dialog as an
empty viewport, on every sample for as long as the modal was open. A Radix
`DialogPortal` renders the overlay and the dialog as body-level siblings of
`#root`, so the centre hit test lands outside both named hosts; and
`DismissableLayer` parks `pointer-events: none` on the body while a modal layer
is open, which drops `#root` and its whole subtree out of hit testing, so
reading the full `elementsFromPoint` stack does not rescue it either.

When the hit test says "uncovered", the rule now asks the second question the
defect is actually about: does a named host still hold the sample point with
something rendered inside it? objectui#6378's window is an EMPTY `#root`, which
answers no and stays red.

Consulted only after the hit test has already failed, so it can only reclassify
a sample the old rule called uncovered, never the other way round.

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

finding(e2e): the boot-continuity coverage rule reads an open modal as a blank viewport

2 participants