Fix flaky iOS BookSkips state waits on stalled simulators #24

Merged
backmeupplz merged 14 commits from fix/92-bookskips-flake into main 2026-10-13 07:07:40 +00:00
backmeupplz commented 2026-10-12 19:08:24 +00:00 (Migrated from github.com)

Fixes agentboard #92 (https://agentboard.icefish-betta.ts.net/#92).

Why it failed

On the #21 push run, iOS (offline-progress) failed BookSkips.testRemoteCallbackBodiesCrossFilesAndClamp in setUp's at(110). The step trace shows a stalled hosted simulator: relaunch automation setup took 19s (2s on the earlier BookSkips test), the first skip-state read took ~10s with internal retries, and the next took ~19s. XCTWaiter's 10s budget expired mid-read and threw that read away, but the failure message then re-read the label and showed the exact target state (track=1;actual=110.000;pos=110.000;paused=yes;ready=yes;speed=1.50). The same helper also failed testPlaybackAndSpeedSurviveBothCrossFileRoutes on #23's run (track=1;actual=138.250), where playback had already left the crossing window by the time the slow read returned.

Fix (test and DEBUG fixture only)

  • BookSkips.wait polls against a 30s deadline. A read that starts before the deadline is judged even if it finishes after it, it returns the read that satisfied it (no second slow read), and a failure reports the last state it evaluated. The paused conditions are unchanged: same track/file, actual AVQueuePlayer time and model position within 0.15s, paused/rate/speed/default rate, readiness, local file, and the remote handler status.
  • A longer deadline alone would have weakened the playing-skip checks, because a forward skip that wrongly landed at the next file's start (100s) could play into the old 124–145s window. The DEBUG fixture now latches the first skip made during playback from the actual AVQueuePlayer (start position and rate, then where playback settled: ready, at 1.5x, progressing without a jump for 1s). The test judges that landing against from ± 30s (5s below, plus above it only the playback in the time the skip took), so slow UI reads no longer matter and a landing at the target file's start fails. The forward playing case starts from 80s, which leaves ~13s of playback before it would leave file 0.
  • 1.5x playback leaves the crossing window ~13s after Play, and a stalled simulator can spend that on the Play read and skip tap alone. A skip that started outside the window (or was latched from the file-1 rebuild tick at 100) says nothing about crossing files, so it is retried from a fresh seed. The third attempt still judges and fails. A wrong landing from inside the window fails on the first attempt.
  • An At seed in the fixture clears a finished listen first, so a stall that let playback run to 150 no longer makes the next Play start over from 0.
  • at() attaches the read that passed instead of reading the label again (another 10-19s read per step on a stalled simulator). The backward playing window ends at 129, not 130: a skip sampled just under 130 can still skip from 130 into file 1, which can't be told apart from a skip clamped within the file, so it is retried rather than failing.

Verification

  • Local UITests/BookSkips (iPhone 17 simulator, -test-iterations 3): 9/9 passed at the final head.
  • Control with a forced 15s delay before the first skip tap and a 32s delay before Pause: both directions retried after starting outside the window (from=132.9 and from=102.6) and then passed. The book ran to 150, and the next route's seed(110) and Play still skipped from 110.4 to 80.0.
  • Negative control (temporary Player.seek mutation landing cross-file seeks at the target file's start): fails on attempt 1 with skip landed short: 110.464:1.50:0:0.000….
  • Merged main (#22, #23). Local UITests/BookSkips x3 at the merge head and at the final head: 9/9 each.
  • CI: all checks green at the final head, plus an offline-progress rerun; the earlier head a0bae2d passed offline-progress on two attempts.
Fixes agentboard #92 (https://agentboard.icefish-betta.ts.net/#92). ## Why it failed On the #21 push run, `iOS (offline-progress)` failed `BookSkips.testRemoteCallbackBodiesCrossFilesAndClamp` in `setUp`'s `at(110)`. The step trace shows a stalled hosted simulator: relaunch automation setup took 19s (2s on the earlier BookSkips test), the first `skip-state` read took ~10s with internal retries, and the next took ~19s. `XCTWaiter`'s 10s budget expired mid-read and threw that read away, but the failure message then re-read the label and showed the exact target state (`track=1;actual=110.000;pos=110.000;paused=yes;ready=yes;speed=1.50`). The same helper also failed `testPlaybackAndSpeedSurviveBothCrossFileRoutes` on #23's run (`track=1;actual=138.250`), where playback had already left the crossing window by the time the slow read returned. ## Fix (test and DEBUG fixture only) - `BookSkips.wait` polls against a 30s deadline. A read that starts before the deadline is judged even if it finishes after it, it returns the read that satisfied it (no second slow read), and a failure reports the last state it evaluated. The paused conditions are unchanged: same track/file, actual AVQueuePlayer time and model position within 0.15s, paused/rate/speed/default rate, readiness, local file, and the remote handler status. - A longer deadline alone would have weakened the playing-skip checks, because a forward skip that wrongly landed at the next file's start (100s) could play into the old 124–145s window. The DEBUG fixture now latches the first skip made during playback from the actual AVQueuePlayer (start position and rate, then where playback settled: ready, at 1.5x, progressing without a jump for 1s). The test judges that landing against `from ± 30s` (5s below, plus above it only the playback in the time the skip took), so slow UI reads no longer matter and a landing at the target file's start fails. The forward playing case starts from 80s, which leaves ~13s of playback before it would leave file 0. - 1.5x playback leaves the crossing window ~13s after Play, and a stalled simulator can spend that on the Play read and skip tap alone. A skip that started outside the window (or was latched from the file-1 rebuild tick at 100) says nothing about crossing files, so it is retried from a fresh seed. The third attempt still judges and fails. A wrong landing from inside the window fails on the first attempt. - An `At` seed in the fixture clears a finished listen first, so a stall that let playback run to 150 no longer makes the next Play start over from 0. - `at()` attaches the read that passed instead of reading the label again (another 10-19s read per step on a stalled simulator). The backward playing window ends at 129, not 130: a skip sampled just under 130 can still skip from 130 into file 1, which can't be told apart from a skip clamped within the file, so it is retried rather than failing. ## Verification - Local `UITests/BookSkips` (iPhone 17 simulator, `-test-iterations 3`): 9/9 passed at the final head. - Control with a forced 15s delay before the first skip tap and a 32s delay before Pause: both directions retried after starting outside the window (from=132.9 and from=102.6) and then passed. The book ran to 150, and the next route's seed(110) and Play still skipped from 110.4 to 80.0. - Negative control (temporary `Player.seek` mutation landing cross-file seeks at the target file's start): fails on attempt 1 with `skip landed short: 110.464:1.50:0:0.000…`. - Merged main (#22, #23). Local `UITests/BookSkips` x3 at the merge head and at the final head: 9/9 each. - CI: all checks green at the final head, plus an offline-progress rerun; the earlier head a0bae2d passed offline-progress on two attempts.
Sign in to join this conversation.
No description provided.