test(studio): gate timeline viewport performance in Chromium - #2697
Conversation
|
Warning This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
This stack of pull requests is managed by Graphite. Learn more about stacking. |
2216018 to
fa190d8
Compare
259b0f1 to
bad5a54
Compare
fa190d8 to
d49c381
Compare
bad5a54 to
ac1bbdd
Compare
d49c381 to
2540026
Compare
ac1bbdd to
3e11275
Compare
2540026 to
a26435d
Compare
81e3a70 to
dcbe193
Compare
a26435d to
5d7f12d
Compare
dcbe193 to
f869766
Compare
b8882aa to
f411080
Compare
f869766 to
3467adf
Compare
e1fd1f2 to
9eaec45
Compare
4033f5f to
e9360d1
Compare
9eaec45 to
c3b04be
Compare
vanceingalls
left a comment
There was a problem hiding this comment.
Verdict: APPROVE — grade B — rubric CORRECT
Role in stack: D2 gate — wires D1 contract into Chromium-run perf test.
Root-cause fit: Script drives Studio preview via puppeteer-core, reads budgets/fixture/diagnostics off window.__studioTest.* (all provisioned by D1 useStudioTestHooks.ts), and emits machine-readable evidence. Contract fit is clean — no local re-declaration of thresholds, no fixture reimplementation. Body's "reproducible gate" framing is accurate: opt-in pnpm test:timeline-virtualization, not CI-enforced.
Claims verified (file:line at HEAD c3b04be4):
- Budgets come from D1 not local:
timeline-virtualization.mjs:172—budgets = await page.evaluate(() => window.__studioTest.timelineViewportBudgets). - Fixture reused, not reimplemented:
:157,168—window.__studioTest.loadTimelinePerformanceFixture({elementCount, profile}). - Warmup separated from measurement:
:180-183—if (index >= budgets.warmupRuns) runs.push(run). - Deterministic env: viewport
1440x900at:143-148; DPR 2 only underhigh-dpr; CPU throttle 4x only underlow-resource. - Precise heap via CDP:
:63-66Runtime.getHeapUsage+HeapProfiler.collectGarbagebefore both baseline (:161) and returned (:194). - Chrome major pin optional but honored:
:134-140. - Longtask via
PerformanceObserver('longtask'):106-116; asserted:187.
Adversarial findings:
- P3 — no CI wiring;
test:timeline-virtualizationis added topackages/studio/package.jsonbut not invoked by any workflow. "Gate" in the title reads aspirational; PR body honestly calls it a package script. - P3 — operator inconsistency at
:189:mountedTimelineDescendants < budgets.maxMountedTimelineDescendantsuses strict<, while sibling clip-root checks (:187-188) use<=. Likely intentional (spec says "less than N descendants") but worth a comment for future readers. - P3 — silent longtask fallback: if
supportedEntryTypesomitslongtaskthe observer is null andMath.max(0, ...[])returns 0, trivially passinglongTaskLimitMs. Chrome major pin mitigates but the pin is optional. - P3 — no
--disable-gpu/ compositor lockdown; frame-cadence numbers may pick up compositor variance, but D1 budgets appear sized with variance in mind (constrained tier + 4/5 quorum).
CI state: All required checks green. Mergeable=MERGEABLE.
Suggested next step: Land as D2; open a follow-up to schedule the script (nightly or weekly) once host baseline is characterized, and normalize the < vs <= operator + guard the longtask-unsupported branch to fail rather than pass.
— Review by Via
e9360d1 to
09b469a
Compare
c3b04be to
287a998
Compare
|
Addressed at
|
|
One blocker in the performance-gate contract:\n\n- packages/studio/tests/e2e/timeline-virtualization.mjs:69-80 starts a PerformanceObserver, awaits the scroll measurements, and then immediately disconnects it. PerformanceObserver delivery is asynchronous; disconnecting without draining takeRecords() can discard entries queued by the final measured render task. That means longestTaskMs can remain below budget even when the last scroll produced an over-budget long task. Because this PR exists to make that measurement a gate, this is a false-green path, not just test polish. Please drain the pending records before disconnecting (ideally in a finally block) and add a focused browser assertion that an injected >50 ms task is observed before the gate evaluates.\n\nI verified the current-head improvements from the prior review: unsupported Long Tasks now fails closed, the DOM ceilings are inclusive, Timeline/root cardinality is pinned, and GPU is disabled. The remaining hosted exact-head checks are green/skipped as expected; Graphite mergeability is still pending because this is downstack.\n\nLocal note: node --check passes. I could not reuse the existing checkout dependencies for Vitest because its installed Studio dependency graph is missing wawoff2; this does not change the source-level blocker above.\n\nGitHub will not let the shared author identity submit a formal request-changes review, so recording the verdict as a PR comment.\n\nVerdict: REQUEST CHANGES\nReasoning: The Long Tasks budget can silently miss queued measurements and allow the new Chromium gate to pass falsely.\n\n— Magi |
287a998 to
abb921f
Compare
|
Resolved at
Exact-tip Chrome evidence:
The full Studio suite passes (291 files, 3,193 tests), Studio typecheck passes, and the harness passes format, lint, and D3 and D4 were parent-restacked only. Their exact-delta patch IDs are unchanged from the approved revisions; the inherited D3 click-suppression rAF note remains deliberately outside this blocker fix. |
abb921f to
a870cf4
Compare
a186ca4 to
f158e6c
Compare
a870cf4 to
0011368
Compare
f158e6c to
c458578
Compare
0011368 to
4aaf015
Compare

What
Add a reproducible Chromium runner that enforces the D1 timeline viewport contract.
How
window.__studioTest.takeRecords()infinally, then disconnect the observer.The injected assertion is isolated from the timeline page, so it proves the observer contract without contaminating measured runs.
This PR provides the runnable gate. Permanent CI wiring for both the default and virtualized states is intentionally owned by #2884 later in this same Graphite stack.
Test plan
Review fixes are included at head
abb921f5f.