Skip to content

test(core): the busy-window spring proof times each frame from the control - #9543

Closed
vjeux wants to merge 1 commit into
meta:mainfrom
vjeux:uikit/f1-busy-median-control-clock
Closed

vjeux wants to merge 1 commit into
meta:mainfrom
vjeux:uikit/f1-busy-median-control-clock

Conversation

@vjeux

@vjeux vjeux commented Nov 2, 2026

Copy link
Copy Markdown
Contributor

Maintainer impact

The animator's browser proof "a spring keeps advancing every frame through a 1 s main-thread block, on its curve" fails on a loaded runner in Chromium. Its last check, that the spring sits on its curve from its start, took the median of each frame's screencast stamp less the spring's start and its time on the curve, and required it under 20 ms. A loaded runner stamps frames late, and that median moves by the stamps' latency. A sweep of main on a 2-core pin with 4 workers read 21.97 ms in 1 of 5 runs, while the spring's drift against the harness's control held at 3.9 ms (bound 12) and the faster-spring check held.

Intended invariant

In Chromium, the proof still checks that the spring is on its analytic curve from the start it reports, to within 20 ms at the median. The drift against the control, the faster-spring check and the busy-window verdict are unchanged.

Change

Each frame's time now comes from the control instead of the frame's stamp. The control moves 240 px out and back every 800 ms, linearly, on the compositor, and the test reads its start from its animation. On each frame, the control's position gives its time within a cycle, and the frame's stamp only picks which cycle, so a stamp may run late by up to 400 ms without changing the result. The spring's median offset from its curve at those times keeps the 20 ms bound. The stamped median is still recorded beside it. Only Chromium stamps frames on the page's clock, so the check stays Chromium-only. controlClock.ts exports its cycle length for this.

Evidence

  • Failure before the change: with every frame's stamp moved 30 ms late (the seam below), the check fails 3 of 3 on main (median 31.2–32.5 ms). On the 2-core pin with 4 workers, main's check fails 11 of 20 (medians 22.8–34.9 ms) while the drift against the control holds (2.7–5.6 ms).
  • Success after the change: with the same seam, the check passes 3 of 3: the median against the control reads −1.1 to −1.6 ms, and the stamped median, still recorded, reads 30.6–47.5 ms. On the 2-core pin with 4 workers it passes 20 of 20 (−0.7 to −1.7 ms) where the stamped median read 2.5–34.4 ms, and at 1 worker 10 of 10 (−1.1 to −1.6 ms; stamped 1.1–18.4 ms).
  • The check still catches a spring off its curve: with the animated spring's response scaled by 0.9, the median against the control reads −51 to −61 ms (drift 60–95 ms); scaled by 1.1, it reads +47 to +49 ms (drift 73–75 ms). Both fail, at the drift check first and at this check too.
  • Product/runtime behavior verified unchanged: test-only; no product file changes.
  • CI impact: none (no workflow or CI script changes).

Status at this head: every gate has finished. Build, type check, strict lint (ASTRYX_STRICT_LINT=1 pnpm lint), the comment-provenance baseline check, check:changesets and check:repo pass, and changeset coverage reports that nothing here ships. test:ui passes (17,968). The node suites fail only the two cache.test.mjs lock cases that fail on main. The whole animator spec passes ×3 in Chromium on the 2-core pin at 1 worker (54) and at 4 workers (54), ×10 under the CI WebKit job's command (140), and at 3 CPUs with 1 and 16 workers (14 each). Outside these profiles, unpinned on a busier box, the spec's case "a retarget made before the page can read a call's start begins where the screen shows the element" failed in Chromium both on main and with this change, its frames reading x = 0: a late start on main that a separate fix covers, and a case this change does not touch.

Scope

  • No intended public API, product behavior, visual direction, or policy change.
  • Product changes discovered during the work were removed or split.
  • Public text and artifacts contain no internal Meta context.

Testing

pnpm test:interaction packages/core/src/motion/__tests__/animator.interaction.browser.spec.ts -g "a spring keeps advancing every frame" --project chromium

The stamp-delay seam used for the reproduction (local only, not committed), in blockedRun:

const frames = (await cast.stop()).map(f => ({
  ...f,
  t: f.t + Number(process.env.STAMP_DELAY ?? 0),
}));

The off-curve spring (local only): the animated spring's response scaled by 0.9 or 1.1 while the proof judges it against the unscaled curve.

…ntrol

In Chromium, the animator's busy-window proof also checks that the spring
sits on its curve from its start: the median of each frame's stamp, less
the spring's start and its time on the curve, had to be under 20 ms. A
loaded runner stamps screencast frames late, and that median moves by the
stamps' latency: it read 21.97 ms on a 4-worker pin while the spring's
drift against the control held at 3.9 ms (bound 12).

Each frame's time now comes from the control instead of its stamp: the
control's start, read from its animation, plus its time since then, which
its position gives within the cycle the stamp falls in. The spring's median
offset from its curve at those times keeps the 20 ms bound. The stamped
median is still recorded.
@vercel

vercel Bot commented Nov 2, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated
astryx Ready Ready Preview Nov 2, 2026 12:57pm UTC

Request Review

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Meta Open Source bot. label Nov 2, 2026
@astracat-bot

astracat-bot Bot commented Nov 2, 2026 •

Copy link
Copy Markdown

AI review status for this pull request.

Review status Updated
🟢 Reviewed (for maintainers only) Nov 2, 2026, 1:04 PM UTC

@github-actions

github-actions Bot commented Nov 2, 2026

Copy link
Copy Markdown
Contributor

PR Analysis Report

Current run: The current CI run concluded skipped. Current analysis is unavailable.


Generated by PR Enrichment workflow | View current CI run

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to approve: the Chromium check now takes frame times from the control animation, so a stamp delay under 400 ms no longer moves the result, and the check still fails a spring whose reported start is off. One optional note sits inline on the comment that keeps the check Chromium-only.

[Automated review]

Comment on lines +256 to +257
// not this one. Only Chromium stamps frames on the page's clock, so only
// there does a stamp tell the cycle.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional, non-blocking: this reason may be stronger than needed. frames.ts rebases WebKit's monotonic stamps onto the wall clock "so both engines share the page's timeline", and the cycle choice here only needs a stamp within 400 ms of the page clock. In local WebKit runs of this test, fromStart read about -4 to -5 ms. If that holds on the WebKit CI job, the comment could say that the old 20 ms stamped check needed Chromium, not the cycle choice. A follow-up could also extend the bound to WebKit.

@vjeux

vjeux commented Nov 4, 2026

Copy link
Copy Markdown
Contributor Author

Superseded by #9885 and #9872.

@vjeux vjeux closed this Nov 4, 2026

This branch was successfully deployed

1 active deployment
Preview — 3361fa58 Deployed Nov 2, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Meta Open Source bot.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant