Skip to content

test(interaction-harness): a busy-window control clock and a marker search in a region - #9872

Merged
cixzhang merged 3 commits into
meta:mainfrom
vjeux:uikit/c28-harness-busy-control
Nov 4, 2026
Merged

cixzhang merged 3 commits into
meta:mainfrom
vjeux:uikit/c28-harness-busy-control

Conversation

@vjeux

@vjeux vjeux commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

Maintainer impact

A busy-main-thread proof blocks the page's main thread and reads the screencast for the motion that must keep going. On a loaded machine the compositor presents fewer frames, and the screencast stamps some of them late. A proof that times its frames by their stamps then fails with nothing wrong in the component.

Intended invariant

A busy-main-thread proof judges the component against the harness's compositor control, recorded in the same screencast (startBusyControl, busyWindowProof). A starved machine fails the control, not the component.

Change

  • controlClock reads the control animation's own position as its clock. The cycle's origin is the one most frames agree with, and each frame's stamp then picks its own out or back time. So a late or reordered frame moves only its own reading, and a gap moves none. A frame near a turn gets no reading.
  • Core's animator spec reads the same control, so it now uses this clock. Core's copy of the clock and its test go; their edge cases move to busy.clock.test.ts.
  • trackControl finds the control in its own strip at the page's top, and CONTROL_SIDE and CONTROL_LEG_MS name that strip and its pace.
  • trackMarker takes an optional region, so a proof decodes only the strip it reads, which keeps a loaded machine inside the test budget.

The harness, plus Core's animator spec, which switches to it. Its first new user is the Lab RefreshControl's interaction spec (#9865).

Evidence

  • Failure before the change: busy.clock.test.ts fails against main's busy.ts (controlClock is not a function). Its five cases also fail 5 of 5 against the first version of this clock, which counted turns in stamp order.
  • Success after the change: busy.clock.test.ts passes, including frames stamped up to 50 ms late and sorted by stamp, as the screencast hands them over. A gap longer than a leg reads right too. The harness's node tests pass.
  • Product/runtime behavior verified unchanged: outside the harness only a Core test changes; the harness's existing exports keep their signatures (trackMarker's region is optional).
  • The animator's busy proof passes 16 of 16 on either clock, on one physical core. The harness clock reads 43 frames against the proof's floor of 20.

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

Local gates at 7877214, on main: the harness's node tests (7 files / 132) and its type check. Strict ESLint reports 0 errors with the browser-spec import rule; Prettier and review:pre pass.

@vercel

vercel Bot commented Nov 4, 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 4, 2026 7:43am UTC

Request Review

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

astracat-bot Bot commented Nov 4, 2026 •

Copy link
Copy Markdown

AI review status for this pull request.

Review status Updated
🟢 Reviewed (for maintainers only) Nov 4, 2026, 7:55 AM UTC

@github-actions

github-actions Bot commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

PR Analysis Report

🔎 Preview · 7877214

Storybook · Sandbox · built from this PR's head

Package size: no published package changed; no new dependencies.

API changes: none in this pull request

Committed API report behind this build: cli 8 rows, theme-butter 1 row, theme-chocolate 1 row, theme-gothic 1 row, theme-matcha 1 row, theme-neutral 1 row, theme-stone 1 row, theme-y2k 1 row; run pnpm api:report.

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

Choose a reason for hiding this comment

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

Changes requested: the new control clock still depends on the screencast's stamps, through the order of the frames it receives.

Blocking:

  • controlClock counts a turn at every direction flip in input order, but cast.stop() returns frames sorted by stamp, so frames stamped more than one frame interval apart become false turns that shift every later reading by 800 ms without a throw (busy.ts:232); reject a reading that moves back or jumps a leg or more (or accept a flip only near an end) and add a test that sorts late-stamped frames with inPresentationOrder.
  • The description says frames "stamped late by up to a leg" read their animation time "whatever the screencast stamps say", but busy.clock.test.ts stamps frames at most 12 ms late and keeps them in true order, so either fix the clock as above or narrow the claim to frames stamped less than one frame interval apart.

Also worth a look: the busy.ts header and the README's busy-main-thread section do not mention controlClock or trackControl yet. CI has not run on this head because the pull request is a draft.

[Automated review]

@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.

Changes requested: the control clock now reads late, stamp-sorted frames at their own time, but it lands as a second copy of a clock core already has for the same control.

Blocking:

  • controlClock in internal/interaction-harness/src/busy.ts copies the origin-vote clock in packages/core/src/motion/__tests__/controlClock.ts, which animator.interaction.browser.spec.ts keeps calling, and the two already disagree (a 24 px versus 36 px edge band, and repeated frames), so move the animator spec onto the harness clock and delete core's copy and its test (moving its two edge cases into busy.clock.test.ts), or say in the description why the harness clock cannot serve it.

Also worth a look: the README now points to trackControl right after describing a control mounted within a dialog that moves with the sheet, but trackControl searches only the top 256 × 16 px, so that case should keep trackMarker(page, frames, {rgb: CONTROL_RGB}). The earlier points on stamp-order turns and the lateness claim are resolved. CI has not run on this head because the pull request is a draft.

[Automated review]

Comment thread internal/interaction-harness/README.md Resolved

@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.

Approved pending CI: CI has not run on this head yet, because the pull request is a draft from a fork; once it is marked ready, a maintainer adds the ci:run label so CI runs.

The earlier point is resolved: the animator spec now reads its control with the harness's controlClock, core's copy and its test are gone, and the README sends a control mounted within a dialog to trackMarker. The animator's busy-window proof passes in Chromium on this head.

  • Worth a look, not blocking: the 36 px edge band reads 43 frames per block where core's 24 px band read 47, so the proof's toBeGreaterThan(20) floor has less margin on runs that drop many frames; if thin runs show up in CI, scale that floor with the band.

[Automated review]

@vjeux
vjeux marked this pull request as ready for review November 4, 2026 06:41
vjeux added 3 commits November 3, 2026 23:29
…earch in a region

A busy-main-thread proof reads the screencast. On a loaded machine the
compositor presents fewer frames and the screencast stamps some late.
So a proof that times its frames by their stamps fails with nothing
wrong in the component. The control animation's own position now gives
the time instead: `controlClock` reads it across the control's turns,
and `trackControl` finds the control in its own strip. `trackMarker`
takes an optional region, so a proof decodes only the strip it reads.
…ames at their own time

`controlClock` counted a turn at each direction flip in stamp order. A
frame stamped more than a frame late lands after frames that it came
before, and the flip that it fakes shifted every later reading by a leg,
with no error.

The clock now takes the cycle origin that most frames agree with, each
within 20 ms of one of the two times that its position gives. Each
frame's stamp, measured from that origin, then picks its own time. A
late or reordered frame moves only its own reading, and a gap moves
none, so the whole-leg throw goes. A frame within 36 px of a turn, or one
that repeats the position before it, gets no reading. Core's animator
spec reads its control the same way (motion/__tests__/controlClock.ts).

The unit test adds frames stamped 20 to 50 ms late and put in
presentation order, a gap of more than a leg, and a repeated position.
The file header and the README name trackControl and controlClock.
… harness clock, and core's copy goes

The harness controlClock duplicated core's motion/__tests__/controlClock.ts,
and the two already disagreed: core's left out only 24 px at a turn, and
it read a repeated frame at its stale position. The animator's busy-window
proof now reads `controlClock(control)` from the harness, over the whole
screencast, and core's copy and its test go. Their two edge cases move
into busy.clock.test.ts: a turn between the first two usable frames, and
a first frame stamped 50 ms late.

The README now says that a control mounted `within` a dialog moves with
it, so trackMarker tracks it; trackControl searches only the top strip.
@vjeux
vjeux force-pushed the uikit/c28-harness-busy-control branch from 453dcde to 7877214 Compare November 4, 2026 07:35
@cixzhang cixzhang added the ci:run A maintainer lets this outside contributor's pull request run CI (AST-030 FR17) label Nov 4, 2026
@cixzhang
cixzhang marked this pull request as draft November 4, 2026 07:40
@cixzhang
cixzhang marked this pull request as ready for review November 4, 2026 07:40

@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.

Approved pending CI: docsite-test is still running on this head; test, build, lint, typecheck and the Chromium and WebKit interaction suites pass, the animator's busy-window proof among them.

The control clock reads late, reordered frames at their own time, Core's animator spec now reads its control with it, and Core's copy and its test are gone. No blocking findings.

[Automated review]

@cixzhang
cixzhang merged commit c239daf into meta:main Nov 4, 2026
107 of 112 checks passed
@vjeux
vjeux deleted the uikit/c28-harness-busy-control branch November 4, 2026 08:22

This branch was successfully deployed

1 active deployment
Preview — 78772149 Deployed Nov 4, 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