Uh oh!
There was an error while loading. Please reload this page.
test(e2e): tell an open modal from a blank viewport in the boot-continuity coverage rule - #7305
Merged
Merged
Conversation
…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
yinlianghui
marked this pull request as ready for review
September 2, 2026 05:36
Uh oh!
There was an error while loading. Please reload this page.
yinlianghui
deleted the
claude/issue-6578-boot-coverage-discriminator
branch
September 2, 2026 05:51
This was referenced Sep 2, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes#6578
The
Console boot continuitycoverage rule read an open modal dialog as a blankviewport, 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 previewon :4173. Signed-out/loginboot withthis spec's own endpoint mocks. Arms are
page.evaluateDOM mutations applied afterthe 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.startScreencastateveryNthFrame: 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.
#rootemptied + splash removed — must read uncoveredbody{pointer-events:none}(Radix modal shape)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.tsxandpopover.tsxpass nomodalprop, so Radix's defaults apply (DropdownMenu modal, Popover not).
body{pointer-events:none}Two things this establishes that the card left open:
DropdownMenuproduces it too, and there the centre element is the barehtmlrather than any portal — Radix parks
pointer-events: noneon the body for everymodal layer, whether or not that layer renders a full-screen overlay.
AlertDialog,SheetandCommandDialogare the dialog shape by source(
fixed inset-0 z-50 bg-black/80overlay inside a portal), so they read as row 4.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 answersno 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.tson disk andtranspiled, so mutating the file really changes what is measured.
Mutation
const ok = hit || appStillHoldsPoint(cx, cy);toconst ok = hit;,confirmed on disk before the run: deleted-text count 1 to 0, injected-text count 0 to
1, blob
147c6cddto0c0a2971. The extracted probe that ran carriesconst ok = hit;at line 106.#rootemptied + splash removedbody{pointer-events:none}Restore proven by state, not by an exit code: on-disk blob back to
147c6cdd(equal to the
HEADblob),git diff HEADempty,git status --porcelainempty.The mutation ran under
trap ... EXIT INT TERMwith absolute paths.The spec's own run
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.pnpm type-check:e2enpx eslint e2e/console-boot-indicator.spec.tspnpm check:control-bytescheck-control-bytes: OK (scanned 6008 tracked text file(s); skipped 85 binary)node scripts/check-changeset-presence.mjsNo 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.tsDeclared deviations
@playwright/testwants chromiumbuild 1234; this container has 1194 pre-installed and the download CDN is blocked,
so the run used
executablePath: '/opt/pw-browsers/chromium'through a throwawayconfig outside the repo.
playwright installwas never run. Same remedy finding(console): /organizations leaves the viewport centre uncovered for ~453ms after the org gate has handed off #6570'sreproduction used. Nothing in the repo's own
playwright.config.tschanged, so CIruns the file with its own pinned browser.
listrather than the config's defaulthtml, to avoid thereport server. Scope of the run is the whole file, unnarrowed.
;, so the wrapper'sVERDICT line covers only the last part; each part's own exit code was captured
separately and is what the table above reports.
Scope
One file,
e2e/console-boot-indicator.spec.ts. Noapps/consoleorpackages/app-shellsource, no change to the splash, no timeout raised, no testskipped or quarantined, no live-e2e / import-harness config touched, and no shared
helper added (an
addInitScriptcallback is serialised, so it cannot import one).🤖 Generated with Claude Code
https://claude.ai/code/session_01BGMDbrVa8JjZcCQ7DWYH1b
Generated by Claude Code