Skip to content

test(core): layout starts read back from the browser allow round-off - #10188

Merged
cixzhang merged 1 commit into
meta:mainfrom
vjeux:test/animated-layout-start-round-off
Nov 6, 2026
Merged

cixzhang merged 1 commit into
meta:mainfrom
vjeux:test/animated-layout-start-round-off

Conversation

@vjeux

@vjeux vjeux commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

Maintainer impact

One animated-layout case fails on CI by round-off: "a swap whose parts ride the glide starts them with the glide, on the start read for the change, however late in its frame the change is made". In #9699's pr-interaction (2/2), animatedLayout.interaction.browser.spec.ts:1013 reported "Expected: 63.79999999998836, Received: 63.79999999998835". The code writes the change's start into animation.startTime, and layoutStartFor returns the same number. But the case reads the start back from the browser, which keeps it in its own units:

  • Chromium converts it to seconds and back: (63.79999999998836 / 1000) * 1000 is 63.79999999998835, CI's value.
  • WebKit rounds it to the microsecond: it reads 63.8.

The block case at line 949 ("a change made after a block that follows an earlier layout motion…") has the same exposure: it compares a start read back with a performance.now() reading it can equal.

Intended invariant

What each case checks is unchanged: the glide and the copy start on the change's start, and a change after a block starts after the block. No product code changes, and no timeout or retry changes.

Change

  • Lines 1013 and 1014 compare to six digits: toBeCloseTo(run.startedAt, 6) allows a difference under 5e-7 ms.
  • Line 949 lets the start sit 5e-7 ms below the clock reading.

Both tolerances stay at round-off scale. A bound of a few units in the last place would fail WebKit's microsecond rounding.

Evidence

Every commit below is evidence, not for landing.

  • Failure before / success after, on a probe that writes CI's start and reads it back:
    • ca6d85c271 (main plus the probe, exact compare) fails 3 of 3 in each engine, with the values above.
    • 7eac81c8fa (the probe with this change's compare) passes 3 of 3 in each engine.
  • The tolerances still catch a wrong start:
    • 5fdc599df4 starts every layout animation 0.1 ms late. The swap case fails 3 of 3 in each engine.
    • dac0908315 starts a change after a block on the timeline's held frame time, the regression the block case guards. It fails 3 of 3 in each engine, starting about 1000 ms early.
    • 76cb9be7c6 starts it 0.1 ms early. The swap case fails 3 of 3 in Chromium. The one-sided block case still passes 3 of 3 per engine: its change starts over 0.1 ms after the block.
  • Product/runtime behavior verified unchanged: a test-only change.
  • CI impact: none; no workflow changes.

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

Two animated-layout cases compared a start time read back from the
browser with a number the page holds. The browser keeps a start time in
its own units: Chromium converts it to seconds and back, and WebKit
rounds it to the microsecond. So the read back can differ by round-off.
CI read 63.79999999998835 for 63.79999999998836 in "a swap whose parts
ride the glide starts them with the glide, ...". That case now compares
to six digits. "a change made after a block that follows an earlier
layout motion ..." now lets its start sit 5e-7 ms below the clock
reading. A start 0.1 ms off still fails the swap case, and a start on
the pre-block frame time fails the block case.
@vercel

vercel Bot commented Nov 6, 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 6, 2026 12:29am UTC

Request Review

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

astracat-bot Bot commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor

AI review status for this pull request.

Review status Updated
🟢 Reviewed (for maintainers only) Nov 6, 2026, 12:36 AM UTC

@github-actions

github-actions Bot commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor

PR Analysis Report

🔎 Preview · afa60a0

Storybook · Sandbox · built from this PR's head

Package size: measured; main has no measurement of the merge base to compare yet.

API changes: cli +14 · ~5 · −0 (against the merge base's committed report)

Committed API report behind this build: cli 19 rows, core 23 rows; run pnpm api:report.

19 API changes (advisory)
Package Where Signature Usage
+ cli ./api · Types · type SearchResultEntry deprecated?: ComponentDeprecation | undefined import type {SearchResultEntry} from '@astryxdesign/cli/api'; const x: SearchResultEntry = {deprecated: …}
+ cli ./authoring · Types · interface ComponentDeprecation interface ComponentDeprecation import type {ComponentDeprecation} from '@astryxdesign/cli/authoring'
+ cli ./authoring · Types · interface ComponentIconSlotDoc interface ComponentIconSlotDoc import type {ComponentIconSlotDoc} from '@astryxdesign/cli/authoring'
+ cli ./authoring · Types · interface ComponentIconSlotStateDoc interface ComponentIconSlotStateDoc import type {ComponentIconSlotStateDoc} from '@astryxdesign/cli/authoring'
+ cli ./authoring · Types · interface MultiComponentDoc deprecated?: ComponentDeprecation import type {MultiComponentDoc} from '@astryxdesign/cli/authoring'; const x: MultiComponentDoc = {deprecated: …}
+ cli ./authoring · Types · interface SingleComponentDoc deprecated?: ComponentDeprecation import type {SingleComponentDoc} from '@astryxdesign/cli/authoring'; const x: SingleComponentDoc = {deprecated: …}
+ cli ./doc · Types · interface ComponentBaseDoc deprecated?: ComponentDeprecation import type {ComponentBaseDoc} from '@astryxdesign/cli/doc'; const x: ComponentBaseDoc = {deprecated: …}
+ cli ./doc · Types · interface SubComponentDoc deprecated?: ComponentDeprecation import type {SubComponentDoc} from '@astryxdesign/cli/doc'; const x: SubComponentDoc = {deprecated: …}
+ cli ./doc · Also exported from an earlier entry point · ComponentDeprecation ComponentDeprecation (./authoring) import {ComponentDeprecation} from '@astryxdesign/cli/doc'
+ cli ./doc · Also exported from an earlier entry point · ComponentIconSlotDoc ComponentIconSlotDoc (./authoring) import {ComponentIconSlotDoc} from '@astryxdesign/cli/doc'
+ cli ./doc · Also exported from an earlier entry point · ComponentIconSlotStateDoc ComponentIconSlotStateDoc (./authoring) import {ComponentIconSlotStateDoc} from '@astryxdesign/cli/doc'
+ cli ./template · Also exported from an earlier entry point · ComponentDeprecation ComponentDeprecation (./authoring) import {ComponentDeprecation} from '@astryxdesign/cli/template'
+ cli ./template · Also exported from an earlier entry point · ComponentIconSlotDoc ComponentIconSlotDoc (./authoring) import {ComponentIconSlotDoc} from '@astryxdesign/cli/template'
+ cli ./template · Also exported from an earlier entry point · ComponentIconSlotStateDoc ComponentIconSlotStateDoc (./authoring) import {ComponentIconSlotStateDoc} from '@astryxdesign/cli/template'
~ cli ./api · Functions · function search function search: (query: string, options?: { cwd?: string | undefined; type?: "template" | "component" | "hook" | "theme" | "doc" | undefined; limit?: number | undefined }) => Promise<SearchResponse> → function search: (query: string, options?: { cwd?: string | undefined; type?: "template" | "component" | "doc" | "theme" | "hook" | undefined; limit?: number | undefined }) => Promise<SearchResponse> import {search} from '@astryxdesign/cli/api'; search(…)
~ cli ./authoring · Types · interface MultiComponentDoc theming?: { container?: boolean; targets: ComponentThemingTarget[]; vars?: ComponentThemingVar[]; derived?: ComponentThemingDerivedVar[] } → theming?: { container?: boolean; targets: ComponentThemingTarget[]; vars?: ComponentThemingVar[]; derived?: ComponentThemingDerivedVar[]; componentIcons?: ComponentIconSlotDoc[] } import type {MultiComponentDoc} from '@astryxdesign/cli/authoring'; const x: MultiComponentDoc = {theming: …}
~ cli ./authoring · Types · interface SingleComponentDoc theming?: { container?: boolean; targets: ComponentThemingTarget[]; vars?: ComponentThemingVar[]; derived?: ComponentThemingDerivedVar[] } → theming?: { container?: boolean; targets: ComponentThemingTarget[]; vars?: ComponentThemingVar[]; derived?: ComponentThemingDerivedVar[]; componentIcons?: ComponentIconSlotDoc[] } import type {SingleComponentDoc} from '@astryxdesign/cli/authoring'; const x: SingleComponentDoc = {theming: …}
~ cli ./doc · Types · interface ComponentBaseDoc theming?: { container?: boolean; targets: ComponentThemingTarget[]; vars?: ComponentThemingVar[]; derived?: ComponentThemingDerivedVar[] } → theming?: { container?: boolean; targets: ComponentThemingTarget[]; vars?: ComponentThemingVar[]; derived?: ComponentThemingDerivedVar[]; componentIcons?: ComponentIconSlotDoc[] } import type {ComponentBaseDoc} from '@astryxdesign/cli/doc'; const x: ComponentBaseDoc = {theming: …}
~ cli ./doc · Types · interface SubComponentDoc theming?: { container?: boolean; targets: ComponentThemingTarget[]; vars?: ComponentThemingVar[]; derived?: ComponentThemingDerivedVar[] } → theming?: { container?: boolean; targets: ComponentThemingTarget[]; vars?: ComponentThemingVar[]; derived?: ComponentThemingDerivedVar[]; componentIcons?: ComponentIconSlotDoc[] } import type {SubComponentDoc} from '@astryxdesign/cli/doc'; const x: SubComponentDoc = {theming: …}

Not run on this PR: add ci:run-visual, ci:run-a11y or ci:run-rtl to run them.


Generated by PR Enrichment workflow | View full report

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Approved pending CI: CI has not run on this head yet. It runs once the pull request is marked ready for review and a maintainer adds the ci:run label.

The tolerances stay at round-off scale. Chromium reads 63.79999999998836 back as 63.79999999998835, as CI reported, and both cases pass at this head. A start moved by 1 µs still fails the swap case, and a start kept on the frame before the block still fails the block case.

[Automated review]

@vjeux
vjeux marked this pull request as ready for review November 6, 2026 00:48
@cixzhang cixzhang added the ci:run A maintainer lets this outside contributor's pull request run CI (AST-030 FR17) label Nov 6, 2026
@cixzhang
cixzhang marked this pull request as draft November 6, 2026 00:52
@cixzhang
cixzhang marked this pull request as ready for review November 6, 2026 00:52
@cixzhang
cixzhang merged commit e895770 into meta:main Nov 6, 2026
194 of 205 checks passed
@vjeux
vjeux deleted the test/animated-layout-start-round-off branch November 6, 2026 02:19
cixzhang added a commit to vjeux/astryx that referenced this pull request Nov 6, 2026

This branch was successfully deployed

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

Labels

ci:run A maintainer lets this outside contributor's pull request run CI (AST-030 FR17) 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.

2 participants