Skip to content

test(interaction-harness): a paced touch play keeps its spacing after a stalled dispatch and reads the release the page read - #9725

Merged
cixzhang merged 5 commits into
meta:mainfrom
vjeux:harness/paced-touch-play
Nov 4, 2026
Merged

cixzhang merged 5 commits into
meta:mainfrom
vjeux:harness/paced-touch-play

Conversation

@vjeux

@vjeux vjeux commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

Maintainer impact

WebKit stamps an injected touch when it arrives, so the page reads a release velocity from arrival times. After a stalled dispatch, real-time pacing sent the rest of a flick back to back: a 900 px/s flick read as 4,000 px/s or more, and its coast ended before a busy block could judge it (4 of 20 runs of the Slider coast proof). This keeps a paced play's spacing, and names whose fault a stray release is. #9545 is the cast-step half.

Evidence

  • Bunched dispatch: over 20 runs per mode, arrival gaps of 2 ms or less went from 44 of 152 to 2 of 160 under pacing: 'spacing'.
  • A page's listeners: a touchmove or pointermove listener that holds the main thread fails a paced flick as TouchPageStallError; the pointermove case passed under touch-only timing. A wheel listener that stops each step went from a re-taken runner stall to WheelPageStallError (3 of 3).
  • Core's release: two moves at one stamp read 1,000 px/s (was Infinity) and a single move 0 (was 200 px/s). A page that read fewer than two moves, or a throw where the script rests, fails. Each case was red before. A Core unit test reads seven gestures with VelocityTracker and with the harness and finds them equal; a copy with another weight fails four of them.
  • Product behavior unchanged: only internal/interaction-harness and one Core test change.

Intended invariant

A paced play fails only when the page read another release than the script throws, and names whether the page or the runner caused it. A play without pacing: 'spacing' behaves as before.

Change

  • pacing: 'spacing': each event leaves its interval after the one before, or after a late dispatch returned, so a stall stretches one gap.
  • The page's release: in WebKit, armInputRun, shared with the wheel path, records each touch and pointer event and when the page finished with it, a stopped one included. The play reads each release as Core's velocity tracker does. It fails a page whose rest differs from the script's, or that read fewer than two moves. A stray release fails only under releaseTolerance.
  • Whose fault: TouchPageStallError when the page's listeners held the gap, else the runner's TouchPacingError.

This supersedes #8910's pacing half. #9694 moves the Slider's WebKit busy proof onto this and #9545.

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 b9ee608, on main bd69bed: typecheck, CI=true pnpm lint (no errors, and no warnings in changed files), Prettier, check:knowledge, changeset coverage (nothing ships), the node suite (16,297 passed), the harness's and Core's gesture unit tests (919, the Core cross-check among them), and the harness's browser self-tests in Chromium on a 2-thread pin (one physical core) at four workers (110 passed, 3 skipped) and in WebKit in the CI job's container profile (100 passed, 13 skipped). Every commit passes the harness's typecheck and unit tests on its own.

@vercel

vercel Bot commented Nov 3, 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 8:10am UTC

Request Review

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

astracat-bot Bot commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

AI review status for this pull request.

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

@github-actions

github-actions Bot commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

PR Analysis Report

🔎 Preview · b9ee608

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
Contributor

Choose a reason for hiding this comment

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

This looks ready to approve: the paced touch play, the release check and the shared input recorder stay inside the private interaction harness, Core's velocity tracker stays unexported, and the new unit and WebKit self-tests cover the pacing rule, both stall attributions and the stopped wheel step.

One optional follow-up:

  • The Core cross-check guards the copied release weight but not the stillness interval: every gesture lifts 8 to 16 ms or 88 ms after its last move, so a harness TOUCH_STILLNESS_MS anywhere from 16 to 87 ms passes all seven cases; a lift exactly UIKIT_VELOCITY.stillness.value after the last move and one 1 ms later, or a direct check of both constants against UIKIT_VELOCITY, would keep that copy in sync too.

[Automated review]

Comment on lines +84 to +91
'a rest before the lift': {
samples: [
[100, 50, 0],
[116, 50, 16],
[132, 50, 32],
],
lift: 120,
},

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.

Non-blocking: every other gesture lifts 8 to 16 ms after its last move, so with this one at 88 ms a harness TOUCH_STILLNESS_MS anywhere from 16 to 87 ms passes all seven cases, and a drift from UIKIT_VELOCITY.stillness would go unnoticed. A gesture that lifts exactly UIKIT_VELOCITY.stillness.value after its last move and one that lifts 1 ms later, or a direct expect(TOUCH_STILLNESS_MS).toBe(UIKIT_VELOCITY.stillness.value), would cover it.

@vjeux
vjeux marked this pull request as ready for review November 3, 2026 17:51
@cixzhang cixzhang added the ci:run A maintainer lets this outside contributor's pull request run CI (AST-030 FR17) label Nov 3, 2026
@cixzhang
cixzhang marked this pull request as draft November 3, 2026 18:13
@cixzhang
cixzhang marked this pull request as ready for review November 3, 2026 18:13
@cixzhang
cixzhang marked this pull request as draft November 4, 2026 04:25
@cixzhang
cixzhang marked this pull request as ready for review November 4, 2026 04:25
vjeux added 5 commits November 3, 2026 23:31
…ing after a stalled dispatch

WebKit's injected touches carry their real dispatch time, so the page reads a release velocity from when the moves arrive. Under a CPU quota a dispatch command stalls and real-time pacing then sends the moves behind it back to back, which reads as a far faster finger. `pacing: 'spacing'` keeps the script's spacing instead: each event leaves its interval after the one before it, or after that dispatch returned when it outlasted the interval, and a gap that reaches the release filter's 40 ms stillness interval fails the play as TouchPacingError once the lift is out. The default stays as it was.
…ad, fails a resting finger, and names a page that held a touch

- The pacing check reads what the release velocity reads: the lifting
  finger's last two move intervals, blended as core blends them, and the
  lift's gap against the stillness interval (strictly longer). An earlier
  stall leaves the release intact and no longer fails the play. A page that
  read a resting finger fails it. A release that strays from the script's
  fails it only under the proof's own `releaseTolerance`: WebKit delivers
  touches at its own cadence, so a flick scripted at 120 Hz reads anywhere
  from a third to more than twice its speed. The lift's
  `PlayedEvent.release` names the release the page read beside the
  script's.
- The page's own arrival stamps and handling times decide it, read by
  window listeners before and after the page's own. A gap the page's
  listeners held (they spent longer than the script's interval on the
  event before, and that handling accounts for the gap) fails as
  TouchPageStallError, the proof's own failure; any other as
  TouchPacingError, the runner's.
…t recorder

`armInputRun(page, types)` records, for each trusted event of the given
types, its stamp, when the page received it and when the page's own
listeners finished with it. It replaces the wheel driver's recorder and
the touch probe's copy of it. Each event keeps its own record, so events
that interleave cannot overwrite each other's marks. An event whose
propagation the page stops is marked by a task queued when it arrived,
and never later than the next event's arrival.
…eners too

A gesture host samples a finger from pointermove, and WebKit stamps a
touch event only once the page's pointer listeners are done. The probe now
records each step's pointer events with its touch event, and
`touchArrivalsFrom` gives the step the earliest stamp and the latest
handling of them. So a page whose pointer listener holds each move reads
as the page's rest (`TouchPageStallError`), where the touch events alone
showed no fault. A WebKit self-test spins a pointermove listener for 60 ms:
the touch-only probe let the play resolve; it now fails as the page's.
…elocity tracker reads it

`readRelease` now reads both the page's stamps and the script's times
through Core's filter: only moved samples count, a sample at the newest
moved sample's time replaces it, and an interval across no time reads 0.
With fewer than two moves after the touch-down, or after a rest past the
stillness interval, the release is 0. Both readings use the same
positions, rounded once, as WebKit delivers whole pixels. Two moves that
share a stamp no longer read Infinity, a single move reads 0, and a page
that read every move at one stamp fails with the new `moves` kind. A
late lift after a single move is no rest: one move releases at 0 for
the script and the page alike. Each reading keeps its own rest, so a page
that does not rest where the script does fails with the `throw` kind.
The filter is a copy of Core's, which the harness cannot call, so a
Core unit test reads the same samples with both and compares them.

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

This looks ready to approve: the paced touch play, its release check and the shared input recorder stay inside the private interaction harness, Core's velocity tracker stays unexported, and the new unit and WebKit self-tests cover the pacing rule, both stall attributions and the stopped wheel step.

One optional follow-up, still open from the earlier review:

  • The Core cross-check guards the copied release weight but not the stillness interval: a harness TOUCH_STILLNESS_MS anywhere from 16 to 87 ms passes all seven cases, so a gesture that lifts exactly UIKIT_VELOCITY.stillness.value after its last move and one 1 ms later, or a direct check of both constants against UIKIT_VELOCITY, would keep that copy in sync.

[Automated review]

@cixzhang
cixzhang merged commit b35fe83 into meta:main Nov 4, 2026
119 of 120 checks passed
@vjeux
vjeux deleted the harness/paced-touch-play branch November 4, 2026 10:40

This branch was successfully deployed

1 active deployment
Preview — b9ee608e 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