diff --git a/desktop/playwright.config.ts b/desktop/playwright.config.ts index 58e90761c8..5dad5d64d2 100644 --- a/desktop/playwright.config.ts +++ b/desktop/playwright.config.ts @@ -107,7 +107,6 @@ export default defineConfig({ "**/workspace-rail.spec.ts", "**/community-rail.spec.ts", "**/boot-splash.spec.ts", - "**/startup-usable.perf.ts", "**/thread-reply-anchor-roleplay.spec.ts", "**/threadpane-ultrawide.spec.ts", "**/thread-focus-mode.spec.ts", diff --git a/desktop/src/app/App.tsx b/desktop/src/app/App.tsx index 1f9ce36d9a..a05b66f016 100644 --- a/desktop/src/app/App.tsx +++ b/desktop/src/app/App.tsx @@ -79,8 +79,17 @@ import { StartupWindowDragRegion } from "@/shared/ui/StartupWindowDragRegion"; const LOADING_TEXT = "Setting up your community..."; +// Minimum time the cold-boot splash stays on screen. A real boot resolves the +// community in well under 100ms, and the native window setup plus first paint +// can take longer than that — without a hold, the bee is unmounted before it is +// ever visible. The hold runs as an overlay above the already-mounted app, so +// time-to-interactive is unchanged; only the reveal waits. +const BOOT_SPLASH_MIN_VISIBLE_MS = 1_200; +const BOOT_SPLASH_FADE_MS = 200; const INITIAL_RENDER_READY_EVENT = "initial-render-ready"; +type BootSplashPhase = "holding" | "fading" | "done"; + function useInitialRenderReady() { useLayoutEffect(() => { if (!isTauri()) { @@ -91,6 +100,44 @@ function useInitialRenderReady() { }, []); } +// E2E runs skip the hold (it would slow every spec's boot and block pointer +// actionability); a spec can opt back in via __BUZZ_E2E__.bootSplashHoldMs. +function bootSplashHoldMs(): number { + const e2e = ( + window as Window & { + __BUZZ_E2E__?: { bootSplashHoldMs?: number }; + } + ).__BUZZ_E2E__; + if (e2e) { + return e2e.bootSplashHoldMs ?? 0; + } + return BOOT_SPLASH_MIN_VISIBLE_MS; +} + +function useBootSplashHold(): BootSplashPhase { + const [phase, setPhase] = useState(() => + bootSplashHoldMs() > 0 ? "holding" : "done", + ); + + useEffect(() => { + const holdMs = bootSplashHoldMs(); + if (holdMs <= 0) { + return; + } + const fadeTimer = window.setTimeout(() => setPhase("fading"), holdMs); + const doneTimer = window.setTimeout( + () => setPhase("done"), + holdMs + BOOT_SPLASH_FADE_MS, + ); + return () => { + window.clearTimeout(fadeTimer); + window.clearTimeout(doneTimer); + }; + }, []); + + return phase; +} + // Animated Buzz mark for the loading gates. The static BuzzMark renders in // normal flow and sizes the box — it's plain SVG (no JS/SMIL), so it paints on // the very first frame even before scripting starts, avoiding a blank flash on @@ -484,6 +531,8 @@ function CommunityApp({ transitionCommunity, ]); + const bootSplashPhase = useBootSplashHold(); + const transaction = communityOnboarding.transaction; useEffect(() => { if (transaction?.stage !== "connecting") { @@ -539,6 +588,12 @@ function CommunityApp({ // starter team" screen visibly restarts mid-handoff. const isEnteringCurtain = transaction?.stage === "entering"; + // The app mounts (and starts loading data) beneath the splash overlay; the + // overlay just keeps the bee on screen long enough to be seen, then fades. + // Community switches keep their quiet gate. + const showBootSplashOverlay = + bootSplashPhase !== "done" && !isCommunitySwitch && !isContinuingOnboarding; + let appContent: ReactNode = null; if (!transaction) { if (community.needsSetup) { @@ -570,9 +625,7 @@ function CommunityApp({ } } // Wait for this exact community config to be applied to the backend before - // rendering anything that connects to the relay. Reveal the app as soon as - // this real gate clears; a cosmetic splash must not block usable content. - // The appliedKey check avoids + // rendering anything that connects to the relay. The appliedKey check avoids // a one-render race where React sees the new active community while the // Tauri backend is still configured for the previous one. const communityApplied = @@ -599,6 +652,19 @@ function CommunityApp({ key={communityKey} isSharedIdentity={sharedIdentity} /> + {showBootSplashOverlay ? ( + + ) : null} ) : isCommunitySwitch || isContinuingOnboarding ? ( diff --git a/desktop/tests/e2e/boot-splash.spec.ts b/desktop/tests/e2e/boot-splash.spec.ts index 95f8d3659a..053ee84c72 100644 --- a/desktop/tests/e2e/boot-splash.spec.ts +++ b/desktop/tests/e2e/boot-splash.spec.ts @@ -1,166 +1,57 @@ -import { expect, test, type Page } from "@playwright/test"; +import { expect, test } from "@playwright/test"; import { installMockBridge } from "../helpers/bridge"; -async function holdStartupCommand(page: Page, commandToHold: string) { - // Intercept the existing native boundary, not the readiness hook itself. - await page.addInitScript((commandToHold) => { - type Invoke = ( - command: string, - args: unknown, - options: unknown, - ) => Promise; - const state = { pending: 0, release: (_error?: string) => {} }; - Object.assign(window, { __STARTUP_GATE__: state }); - let settle: (error?: string) => void = () => {}; - const gate = new Promise((resolve, reject) => { - settle = (error) => (error ? reject(new Error(error)) : resolve()); - }); - let holding = true; - state.release = (error) => { - holding = false; - settle(error); - }; - const internals = {}; - let inner: Invoke; - Object.defineProperty(internals, "invoke", { - configurable: true, - get: () => async (command: string, args: unknown, options: unknown) => { - if (command === commandToHold && holding) { - state.pending++; - await gate; - } - return inner(command, args, options); - }, - set: (invoke: Invoke) => { - inner = invoke; - }, - }); - Object.assign(window, { __TAURI_INTERNALS__: internals }); - }, commandToHold); -} +// Cold-boot splash hold: on a real boot the community resolves in well under +// 100ms — before the hidden Tauri window ever puts a frame on screen — so the +// loading gate keeps the flapping bee up as an overlay above the already +// mounted app for a minimum visible duration, then fades out. E2E runs skip +// the hold by default (it would slow every spec's boot and block pointer +// actionability); this spec opts back in via __BUZZ_E2E__.bootSplashHoldMs. -async function releaseStartup(page: Page, error?: string) { - await page.evaluate((error) => { - ( - window as Window & { - __STARTUP_GATE__: { release: (error?: string) => void }; - } - ).__STARTUP_GATE__.release(error); - }, error); -} - -for (const cached of [false, true]) { - test(`ready sidebar accepts input without a cosmetic hold (${cached ? "cached" : "uncached"})`, async ({ - page, - }) => { - await installMockBridge(page); - await page.addInitScript(() => { - // Old production waited 1200 + 200ms; normal E2E skipped that wait. - // Keep this override to make restoring the old implementation fail here. - Object.assign(window.__BUZZ_E2E__ ?? {}, { bootSplashHoldMs: 1200 }); - }); - await page.goto("/"); - if (cached) { - await expect(page.getByTestId("channel-general")).toBeVisible(); - await expect - .poll(() => - page.evaluate(() => - Object.keys(localStorage).some((key) => - key.startsWith("buzz-channels.v1:"), - ), - ), - ) - .toBe(true); - await page.reload(); - } - const row = page.getByTestId("channel-general"); - await expect(row).toBeVisible(); - // Synchronous hit-test: unlike click() auto-wait, this cannot hide a splash - // intercepting input for another second above an already-mounted sidebar. - expect( - await row.evaluate((element) => { - const rect = element.getBoundingClientRect(); - return element.contains( - document.elementFromPoint( - rect.x + rect.width / 2, - rect.y + rect.height / 2, - ), - ); - }), - ).toBe(true); - await row.click(); - await expect(page.getByTestId("chat-title")).toHaveText("general"); - await expect(page.getByTestId("message-row").first()).toBeVisible(); - await page.getByTestId("message-input").fill("Ready now"); - await expect(page.getByTestId("message-input")).toHaveText("Ready now"); - await expect(page.getByTestId("boot-splash-overlay")).toHaveCount(0); - }); -} - -for (const command of ["set_agent_avatar_communities", "apply_workspace"]) { - test(`real startup gate remains while ${command} is pending`, async ({ - page, - }) => { - await installMockBridge(page); - await holdStartupCommand(page, command); - await page.goto("/"); - const gate = page.getByTestId("app-loading-gate"); - await expect(gate).toBeVisible(); - await expect - .poll(() => - page.evaluate( - () => - (window as Window & { __STARTUP_GATE__: { pending: number } }) - .__STARTUP_GATE__.pending, - ), - ) - .toBeGreaterThan(0); - await expect(page.getByTestId("home-inbox-list")).toHaveCount(0); - await expect(page.getByTestId("channel-general")).toHaveCount(0); - expect( - await page.evaluate(() => - window.__BUZZ_E2E_COMMANDS__?.includes("get_channels"), - ), - ).toBe(false); - const wingState = await gate.locator(".bee-wing-left").evaluate((wing) => ({ - name: getComputedStyle(wing).animationName, - state: wing.getAnimations()[0]?.playState, - })); - expect(wingState).toEqual({ name: "bee-wing-left-flap", state: "running" }); - await releaseStartup(page); - await page.getByTestId("channel-general").click(); - await expect(page.getByTestId("chat-title")).toHaveText("general"); - await expect(gate).toHaveCount(0); - }); -} - -test("failed community apply stays closed and retry is usable", async ({ +test("boot splash overlay holds with a flapping bee, then dismisses", async ({ page, }) => { await installMockBridge(page); - await holdStartupCommand(page, "apply_workspace"); + // Registered after installMockBridge so it runs after the bridge's init + // script and can extend the config it assigns. + await page.addInitScript(() => { + const testWindow = window as Window & { + __BUZZ_E2E__?: { bootSplashHoldMs?: number }; + }; + testWindow.__BUZZ_E2E__ = { + ...(testWindow.__BUZZ_E2E__ ?? {}), + bootSplashHoldMs: 1_500, + }; + }); await page.goto("/"); - await expect - .poll(() => - page.evaluate( - () => - (window as Window & { __STARTUP_GATE__: { pending: number } }) - .__STARTUP_GATE__.pending, - ), - ) - .toBeGreaterThan(0); - await releaseStartup(page, "Startup apply failed"); - await expect(page.getByTestId("community-apply-error")).toContainText( - "Startup apply failed", - ); - await expect(page.getByTestId("channel-general")).toHaveCount(0); - expect( - await page.evaluate(() => - window.__BUZZ_E2E_COMMANDS__?.includes("get_channels"), - ), - ).toBe(false); - await page.getByTestId("community-apply-error-retry").click(); - await page.getByTestId("channel-general").click(); - await expect(page.getByTestId("chat-title")).toHaveText("general"); - await expect(page.getByTestId("community-apply-error")).toHaveCount(0); + + const overlay = page.getByTestId("boot-splash-overlay"); + await expect(overlay).toBeVisible(); + + // The bee is actually animating while the overlay holds — pure CSS, no SMIL. + const wingState = await overlay.locator(".bee-wing-left").evaluate((wing) => { + const animation = wing.getAnimations()[0]; + return { + name: getComputedStyle(wing).animationName, + state: animation?.playState, + }; + }); + expect(wingState).toEqual({ name: "bee-wing-left-flap", state: "running" }); + + // The app mounts and loads beneath the overlay — boot is not delayed. + await expect(page.getByTestId("home-inbox-list")).toBeVisible(); + + // After the hold elapses the overlay fades out and unmounts. + await expect(overlay).toHaveCount(0, { timeout: 6_000 }); + await expect(page.getByTestId("home-inbox-list")).toBeVisible(); +}); + +test("boot splash overlay is skipped when the hold is zero (e2e default)", async ({ + page, +}) => { + await installMockBridge(page); + await page.goto("/"); + + await expect(page.getByTestId("home-inbox-list")).toBeVisible(); + await expect(page.getByTestId("boot-splash-overlay")).toHaveCount(0); }); diff --git a/desktop/tests/e2e/onboarding.spec.ts b/desktop/tests/e2e/onboarding.spec.ts index c7503c4253..ecca4f123e 100644 --- a/desktop/tests/e2e/onboarding.spec.ts +++ b/desktop/tests/e2e/onboarding.spec.ts @@ -927,6 +927,15 @@ test("fresh-key harness completion continues directly into profile onboarding", }, { skipCommunitySeed: true, skipOnboardingSeed: true }, ); + await page.addInitScript(() => { + const testWindow = window as Window & { + __BUZZ_E2E__?: { bootSplashHoldMs?: number }; + }; + testWindow.__BUZZ_E2E__ = { + ...(testWindow.__BUZZ_E2E__ ?? {}), + bootSplashHoldMs: 2_000, + }; + }); await page.goto("/"); await page.getByRole("button", { name: "Create a new identity key" }).click(); diff --git a/desktop/tests/e2e/startup-usable.perf.ts b/desktop/tests/e2e/startup-usable.perf.ts deleted file mode 100644 index 3ee31ac97a..0000000000 --- a/desktop/tests/e2e/startup-usable.perf.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { expect, test } from "@playwright/test"; -import { installMockBridge } from "../helpers/bridge"; - -// Opt-in matched browser benchmark. Native startup and relay work are mocked. -test.skip(!process.env.BUZZ_STARTUP_BENCHMARK, "opt-in startup measurement"); -for (const cached of [false, true]) { - test(`startup usable (${cached ? "cached roster" : "no snapshot"})`, async ({ - page, - }, testInfo) => { - await installMockBridge(page); - await page.addInitScript(() => { - // Reproduce the OLD production policy, which its default E2E path skips. - // The candidate removes this policy altogether and ignores this field. - Object.assign(window.__BUZZ_E2E__ ?? {}, { bootSplashHoldMs: 1200 }); - const samples: Record = {}; - Object.assign(window, { __STARTUP_SAMPLE__: samples }); - const inspect = () => { - const row = document.querySelector('[data-testid="channel-general"]'); - if (row) { - samples.rowMounted ??= performance.now(); - const box = row.getBoundingClientRect(); - if ( - box.width && - box.height && - row.contains( - document.elementFromPoint( - box.x + box.width / 2, - box.y + box.height / 2, - ), - ) - ) { - samples.rowReceivesPointer ??= performance.now(); - } - } - if (!samples.rowReceivesPointer) requestAnimationFrame(inspect); - }; - requestAnimationFrame(inspect); - document.addEventListener( - "click", - (event) => { - if ( - (event.target as Element).closest('[data-testid="channel-general"]') - ) - samples.channelClicked = performance.now(); - }, - true, - ); - }); - await page.goto("/"); - if (cached) { - await expect(page.getByTestId("channel-general")).toBeVisible(); - await expect - .poll(() => - page.evaluate(() => - Object.keys(localStorage).some((key) => - key.startsWith("buzz-channels.v1:"), - ), - ), - ) - .toBe(true); - await page.reload(); - } - await page.getByTestId("channel-general").click(); - await expect(page.getByTestId("chat-title")).toHaveText("general"); - await expect(page.getByTestId("message-row").first()).toBeVisible(); - await page.getByTestId("message-input").fill("Startup input check"); - const result = await page.evaluate(() => ({ - ...(window as Window & { __STARTUP_SAMPLE__: Record }) - .__STARTUP_SAMPLE__, - contentAndInputReady: performance.now(), - snapshotPresence: ( - performance - .getEntriesByName("buzz:sidebar:snapshot-diagnostic") - .at(-1) as PerformanceMark - )?.detail?.presence, - })); - expect(result.snapshotPresence).toBe(cached ? "present" : "absent"); - console.log( - "STARTUP_SAMPLE", - JSON.stringify({ - scenario: cached ? "cached" : "no-snapshot", - ...result, - }), - ); - await testInfo.attach("startup-sample", { - body: JSON.stringify(result, null, 2), - contentType: "application/json", - }); - }); -} diff --git a/perf/STARTUP_REVEAL.md b/perf/STARTUP_REVEAL.md deleted file mode 100644 index 02b4b57832..0000000000 --- a/perf/STARTUP_REVEAL.md +++ /dev/null @@ -1,100 +0,0 @@ -# Startup: reveal usable content without a cosmetic hold - -## Retained change - -Remove the cold-boot overlay and its 1,200 ms hold + 200 ms fade from -`desktop/src/app/App.tsx`. Keep the actual community/identity/permission gate, -animated loading bee, onboarding curtain, community-switch gate, error recovery, -and native first-render/window-geometry reveal unchanged. Fast boots may not show -the bee at all: responsiveness takes precedence over a minimum logo viewing time. - -This does **not** make native initialization, relay reads, or the engine faster. -It removes a real input-blocking UI wait after the app has mounted. It applies the -foreground-first/no-unnecessary-waiting lesson without transplanting a transport. -No changes to membership, unread state, recency, subscriptions, or pagination. -The production delta is 69 removed lines and 3 added comment lines in one file. - -## Matched browser result — 2026-09-20 - -Baseline: `9e5a9518e7f3fb62cdf14dc735d1092ed5844a85`. -Candidate: that baseline plus the App.tsx change in this commit. -Candidate App.tsx SHA-256: -`27210160d45b9d61574bb50a8cffec062404d34cae0ba8ed851f72241676f158`. - -Five runs per scenario/version, one Chromium worker, same mock native/relay fixture, -Hermit toolchain, E2E build and machine. Each uncached sample uses a fresh browser -context. The cached sample reloads after the app persists its own sidebar snapshot; -the harness asserts snapshot diagnostics are `absent` or `present` respectively. -Old production's 1,200 ms policy is explicitly enabled: ordinary old E2E runs -silently bypassed it. Candidate has no hold policy or E2E exception. - -All times are **median milliseconds from browser document navigation**, not process -launch. Raw samples: [STARTUP_REVEAL_RESULTS_2026_09_20.json](STARTUP_REVEAL_RESULTS_2026_09_20.json). - -| Scenario / observation | Before | After | -| --- | ---: | ---: | -| No snapshot: sidebar row mounted | 332.1 | 313.8 | -| No snapshot: row accepts pointer hit-test | 1,655.6 | 376.3 | -| No snapshot: actual channel click received | 1,780.5 | 418.6 | -| No snapshot: messages visible and composer filled | 2,145.8 | 786.2 | -| Cached roster: sidebar row mounted | 118.0 | 116.6 | -| Cached roster: row accepts pointer hit-test | 1,486.8 | 156.3 | -| Cached roster: actual channel click received | 1,520.0 | 196.5 | -| Cached roster: messages visible and composer filled | 1,880.7 | 556.6 | - -The useful interaction completes about **1.32–1.36 seconds sooner** in this fixture. -The much smaller change in row-mount time separates loading from the input-blocking -cover. Click timestamps include Playwright actionability/retry overhead; the -frame-sampled `elementFromPoint` observation independently establishes obstruction. -The final endpoint includes a visible message and successful composer fill, not -merely a connection badge, overlay removal, or mounted-but-covered element. - -## Regression evidence - -- Two ready-sidebar tests (cached/uncached) **fail on unchanged baseline** at the - synchronous pointer hit-test, then pass on the candidate. They explicitly enable - the legacy production hold to prevent the old E2E bypass hiding its restoration. -- Five startup tests pass: usable cold/warm sidebar; actual permission-trust IPC - and workspace-apply IPC keep app/data reads gated while unresolved; failed apply - stays closed and its Retry returns to a usable channel. Loading wings still animate. -- All 10 sidebar snapshot tests pass (including stale/wrong scope and refresh). -- All 72 onboarding tests pass. -- All 25 community-rail tests pass on the final full-file run. **One earlier - keyboard-reorder test failed** in the combined 40-test run (39 passed). One - baseline control and five candidate focused repeats passed, then full-file - baseline and candidate runs both passed 25/25. Root cause of that intermittent - failure is not established; no unrelated reorder change was made. -- E2E TypeScript/build passed. Full desktop lint/typecheck/unit checks are supplied - by the standard pre-push hook; see the commit/push report for their result. - -## Reproduce - -In an isolated candidate checkout, activate Hermit, then: - -```sh -cd desktop -pnpm build:e2e -BUZZ_STARTUP_BENCHMARK=1 pnpm exec playwright test --project=smoke startup-usable.perf.ts --repeat-each=5 -pnpm exec playwright test --project=smoke boot-splash.spec.ts sidebar-snapshot.spec.ts community-rail.spec.ts -pnpm exec playwright test --project=integration onboarding.spec.ts -``` - -For the baseline, use a separate isolated checkout at the stated revision, copy -only `startup-usable.perf.ts` and its smoke registration into it, then rebuild and -run the same benchmark. Copy the new `boot-splash.spec.ts` there to reproduce the -two ready-sidebar failures. Never use a stale port-4173 server/build from the other -revision. The control used a separate source/dependency/build directory; no peer -worktree or live app storage was altered. - -## Evidence boundary / remaining work - -This is Chromium UI evidence with mocked native and relay commands. There is no -native WebKit/window-paint or live-relay before/after measurement here. Native -geometry restoration and `initial-render-ready` remain unmodified, but eliminating -the decorative reveal must still be visually checked in a packaged native app. -The old timer starts at CommunityApp mount: if real initialization already exceeds -its deadline, removing it saves little or nothing. These results are not evidence -that backend slowness, channel fan-out, or the entire old/new app gap is solved. - -Repository-wide `just ci`, release packaging, native visual validation, PR creation, -merge, and deployment are not certified by this slice. diff --git a/perf/STARTUP_REVEAL_RESULTS_2026_09_20.json b/perf/STARTUP_REVEAL_RESULTS_2026_09_20.json deleted file mode 100644 index 9c1a1aea47..0000000000 --- a/perf/STARTUP_REVEAL_RESULTS_2026_09_20.json +++ /dev/null @@ -1,201 +0,0 @@ -{ - "baseline": { - "samples": [ - { - "scenario": "no-snapshot", - "rowMounted": 362.5, - "rowReceivesPointer": 1678.5, - "channelClicked": 1817.6999998092651, - "contentAndInputReady": 2190.199999809265, - "snapshotPresence": "absent" - }, - { - "scenario": "cached", - "rowMounted": 171.2000002861023, - "rowReceivesPointer": 1533.5999999046326, - "channelClicked": 1570.7000002861023, - "contentAndInputReady": 1933.5999999046326, - "snapshotPresence": "present" - }, - { - "scenario": "no-snapshot", - "rowMounted": 332.09999990463257, - "rowReceivesPointer": 1657.8999996185303, - "channelClicked": 1780.5, - "contentAndInputReady": 2145.7999997138977, - "snapshotPresence": "absent" - }, - { - "scenario": "cached", - "rowMounted": 119, - "rowReceivesPointer": 1491, - "channelClicked": 1520, - "contentAndInputReady": 1880.7000002861023, - "snapshotPresence": "present" - }, - { - "scenario": "no-snapshot", - "rowMounted": 336.5, - "rowReceivesPointer": 1655.5999999046326, - "channelClicked": 1785.7999997138977, - "contentAndInputReady": 2153.2999997138977, - "snapshotPresence": "absent" - }, - { - "scenario": "cached", - "rowMounted": 118, - "rowReceivesPointer": 1486.8000001907349, - "channelClicked": 1524.5, - "contentAndInputReady": 1881.9000000953674, - "snapshotPresence": "present" - }, - { - "scenario": "no-snapshot", - "rowMounted": 325.90000009536743, - "rowReceivesPointer": 1655.5, - "channelClicked": 1775.8000001907349, - "contentAndInputReady": 2141, - "snapshotPresence": "absent" - }, - { - "scenario": "cached", - "rowMounted": 106.90000009536743, - "rowReceivesPointer": 1476.3000001907349, - "channelClicked": 1492.1000003814697, - "contentAndInputReady": 1859.7000002861023, - "snapshotPresence": "present" - }, - { - "scenario": "no-snapshot", - "rowMounted": 320.3999996185303, - "rowReceivesPointer": 1644.7999997138977, - "channelClicked": 1767.5999999046326, - "contentAndInputReady": 2138.0999999046326, - "snapshotPresence": "absent" - }, - { - "scenario": "cached", - "rowMounted": 110.2000002861023, - "rowReceivesPointer": 1484.8000001907349, - "channelClicked": 1499.9000000953674, - "contentAndInputReady": 1868.5999999046326, - "snapshotPresence": "present" - } - ], - "medians": { - "no-snapshot": { - "rowMounted": 332.1, - "rowReceivesPointer": 1655.6, - "channelClicked": 1780.5, - "contentAndInputReady": 2145.8 - }, - "cached": { - "rowMounted": 118, - "rowReceivesPointer": 1486.8, - "channelClicked": 1520, - "contentAndInputReady": 1880.7 - } - } - }, - "candidate": { - "samples": [ - { - "scenario": "no-snapshot", - "rowMounted": 313.7999997138977, - "rowReceivesPointer": 376.2999997138977, - "channelClicked": 415, - "contentAndInputReady": 786.1999998092651, - "snapshotPresence": "absent" - }, - { - "scenario": "cached", - "rowMounted": 117.09999990463257, - "rowReceivesPointer": 156.40000009536743, - "channelClicked": 196.5, - "contentAndInputReady": 556.2999997138977, - "snapshotPresence": "present" - }, - { - "scenario": "no-snapshot", - "rowMounted": 309.3999996185303, - "rowReceivesPointer": 373, - "channelClicked": 418.59999990463257, - "contentAndInputReady": 784.8999996185303, - "snapshotPresence": "absent" - }, - { - "scenario": "cached", - "rowMounted": 115.09999990463257, - "rowReceivesPointer": 154.5, - "channelClicked": 198.89999961853027, - "contentAndInputReady": 557.2999997138977, - "snapshotPresence": "present" - }, - { - "scenario": "no-snapshot", - "rowMounted": 338.2000002861023, - "rowReceivesPointer": 402.30000019073486, - "channelClicked": 448.59999990463257, - "contentAndInputReady": 818.9000000953674, - "snapshotPresence": "absent" - }, - { - "scenario": "cached", - "rowMounted": 116.2999997138977, - "rowReceivesPointer": 155.5, - "channelClicked": 195.59999990463257, - "contentAndInputReady": 553.6999998092651, - "snapshotPresence": "present" - }, - { - "scenario": "no-snapshot", - "rowMounted": 315.7000002861023, - "rowReceivesPointer": 377.90000009536743, - "channelClicked": 423.5, - "contentAndInputReady": 795.6000003814697, - "snapshotPresence": "absent" - }, - { - "scenario": "cached", - "rowMounted": 116.59999990463257, - "rowReceivesPointer": 156.2999997138977, - "channelClicked": 195.7999997138977, - "contentAndInputReady": 556.5999999046326, - "snapshotPresence": "present" - }, - { - "scenario": "no-snapshot", - "rowMounted": 311.59999990463257, - "rowReceivesPointer": 374.90000009536743, - "channelClicked": 413.09999990463257, - "contentAndInputReady": 782.9000000953674, - "snapshotPresence": "absent" - }, - { - "scenario": "cached", - "rowMounted": 117.5, - "rowReceivesPointer": 157.19999980926514, - "channelClicked": 203.59999990463257, - "contentAndInputReady": 563.1999998092651, - "snapshotPresence": "present" - } - ], - "medians": { - "no-snapshot": { - "rowMounted": 313.8, - "rowReceivesPointer": 376.3, - "channelClicked": 418.6, - "contentAndInputReady": 786.2 - }, - "cached": { - "rowMounted": 116.6, - "rowReceivesPointer": 156.3, - "channelClicked": 196.5, - "contentAndInputReady": 556.6 - } - } - }, - "baseCommit": "9e5a9518e7f3fb62cdf14dc735d1092ed5844a85", - "candidateAppSha256": "27210160d45b9d61574bb50a8cffec062404d34cae0ba8ed851f72241676f158", - "method": "Chromium browser; mocked native/relay boundaries; performance.now from document navigation; old production 1200ms hold explicitly enabled; five samples per scenario per revision; no performance thresholds used as regressions" -}