You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
#1572 fixed gesture pan ignoring its duration on iOS, and the fix is real — but nothing in CI would catch it coming back.
Why the existing gate does not cover it
examples/test-app/replays/gesture-lab.ad already drives the exact command class that regressed:
gesture pan 110 443 48 0 500
Two problems:
It does not run on PRs.fixtureReplays is a full-tier scenario (test/integration/ios-simulator-e2e/scenarios.ts: { id: 'full:fixture-replays', runner: 'fixtureReplays', tier: 'full' }), and .github/workflows/ios.yml runs AGENT_DEVICE_IOS_E2E_TIER: smoke. Only replays-nightly.yml runs the full tier.
The runner reports gestureStartUptimeMs / gestureEndUptimeMs, but normalizeAppleRunnerResultForResponse (src/platforms/apple/core/runner/runner-result-response-normalization.ts) strips both from the response as diagnostic-only fields. The e2e harness runs the CLI with --json, so it cannot see them. #1572's evidence table had to be read out of --debug per-request ndjson.
Exposing those fields publicly just to test them would be the wrong trade — they are deliberately internal.
Suggested approach
Make the duration observable in the fixture instead of in the protocol. examples/test-app/src/screens/GestureLab.tsx already uses Gesture.Pan() and renders counters as plain text that replays assert with wait. Recording an observed-duration bucket from the handler's begin/end timestamps and rendering it the same way makes the assertion a one-line wait in gesture-lab.ad:
No new plumbing, no diagnostic-field exposure, and it exercises the public replay path end to end.
Worth deciding separately whether a single duration-checked pan should be promoted into the smoke tier, since a nightly-only guard means a regression can sit on main for a day.
Context
ADR 0013 now states that unit tests cannot prove timing or event delivery inside the private XCTest bridge, so live evidence is the only mechanism available — which is exactly why the live gate needs to actually assert timing.
Follow-up to #1562 / #1572.
#1572 fixed
gesture panignoring its duration on iOS, and the fix is real — but nothing in CI would catch it coming back.Why the existing gate does not cover it
examples/test-app/replays/gesture-lab.adalready drives the exact command class that regressed:Two problems:
fixtureReplaysis a full-tier scenario (test/integration/ios-simulator-e2e/scenarios.ts:{ id: 'full:fixture-replays', runner: 'fixtureReplays', tier: 'full' }), and.github/workflows/ios.ymlrunsAGENT_DEVICE_IOS_E2E_TIER: smoke. Onlyreplays-nightly.ymlruns the full tier.Why the obvious fix does not work
The runner reports
gestureStartUptimeMs/gestureEndUptimeMs, butnormalizeAppleRunnerResultForResponse(src/platforms/apple/core/runner/runner-result-response-normalization.ts) strips both from the response as diagnostic-only fields. The e2e harness runs the CLI with--json, so it cannot see them. #1572's evidence table had to be read out of--debugper-request ndjson.Exposing those fields publicly just to test them would be the wrong trade — they are deliberately internal.
Suggested approach
Make the duration observable in the fixture instead of in the protocol.
examples/test-app/src/screens/GestureLab.tsxalready usesGesture.Pan()and renders counters as plain text that replays assert withwait. Recording an observed-duration bucket from the handler's begin/end timestamps and rendering it the same way makes the assertion a one-linewaitingesture-lab.ad:No new plumbing, no diagnostic-field exposure, and it exercises the public replay path end to end.
Worth deciding separately whether a single duration-checked pan should be promoted into the smoke tier, since a nightly-only guard means a regression can sit on
mainfor a day.Context
ADR 0013 now states that unit tests cannot prove timing or event delivery inside the private XCTest bridge, so live evidence is the only mechanism available — which is exactly why the live gate needs to actually assert timing.