Repository navigation
feat(perf): time every Android command against the fixture app - #3902
Conversation
A JavaScript-only route that reproduces a heavy React Native home feed: 30 nested containers above a virtualized list of composite posts with image, link, quote, and quote-with-media embeds. Its native accessibility tree is deeper than 65 levels, so XCTest tree capture fails with kAXErrorIllegalArgument and recovers through private AX, as on Bluesky 1.136. Settings links to it with open-heavy-feed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
pnpm perf --platform ios now drives the test app through seven scenarios that time each catalog command the iOS simulator serves, including the heavy feed and a raw capture with the XCTest tree backend pinned. The suite records a reason for every command it does not time, and a unit test keeps that partition complete. Each sample keeps the client daemon round trip and a phase profile from its --debug request log: runner commands, host process spawns, snapshot sources, retries, stabilization, and fallbacks. A run writes summary.json, report.md, and the raw daemon and session logs. The iOS Settings tour is removed; the Android tour stays as it was. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…3886) pnpm perf:compare judges one run against earlier runs: by default the seven nights before it, or a chosen run, date, range, or local output. A step's baseline is the median of its nightly medians. A change is flagged only when it is larger than 20%, three times the night-to-night scaled MAD, twice the run's own cv/sqrt(n), and 50 ms; new failures and fixed steps are flagged too. perf-nightly runs the iOS suite at 02:40 UTC again (#1781 A3). Each run uploads perf-ios-summary (90 days) and perf-ios-raw (30 days), and writes the comparison to the job summary with warnings for regressions and new failures. Android stays report-only behind a dispatch input. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…interrupt - Rolling baselines take one scheduled run per UTC night (the newest usable one) and page through workflow runs until enough nights are found. Explicit --to/--from references still accept any trigger. - A run measures at least three rounds. A timing verdict needs three successful samples tonight and on each counted baseline night. - Failures get their own verdict. more-failures flags tonight failing more than every baseline night, in count and share; a faster median with worse failures is not an improvement. - The interrupt handler and the normal finish share one finalizer: save logs, stop session and daemon within a deadline, save again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Run iOS and Android from one shared suite: platform deltas are data on each step, and one harness owns rounds, sessions, and daemon restarts. The heavy-feed scenario times the Android helper's one-shot instrumentation capture by restarting the daemon with the helper session off. Request profiles name the helper transport that answered, adb spawns, and helper gestures. The nightly runs both platforms and compares each against its own nights; the Settings tour is removed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The API 35 and 36 emulator images ship no shell clipboard service, so clipboard is an Android exclusion. A daemon restart that reopens the session clears the stuck flag, and one that cannot reopen it skips the rest of the scenario as restore_failed. The one-shot step warms the restarted daemon's helper install check untimed. latest and date references resolve to the newest run with the platform's artifact. The tour-only --n alias is gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Size Report
Startup median (7 runs, lower is better):
|
pnpm perf --platform ios now drives the test app through seven scenarios that time each catalog command the iOS simulator serves, including the heavy feed and a raw capture with the XCTest tree backend pinned. The suite records a reason for every command it does not time, and a unit test keeps that partition complete. Each sample keeps the client daemon round trip and a phase profile from its --debug request log: runner commands, host process spawns, snapshot sources, retries, stabilization, and fallbacks. A run writes summary.json, report.md, and the raw daemon and session logs. The iOS Settings tour is removed; the Android tour stays as it was. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…3886) pnpm perf:compare judges one run against earlier runs: by default the seven nights before it, or a chosen run, date, range, or local output. A step's baseline is the median of its nightly medians. A change is flagged only when it is larger than 20%, three times the night-to-night scaled MAD, twice the run's own cv/sqrt(n), and 50 ms; new failures and fixed steps are flagged too. perf-nightly runs the iOS suite at 02:40 UTC again (#1781 A3). Each run uploads perf-ios-summary (90 days) and perf-ios-raw (30 days), and writes the comparison to the job summary with warnings for regressions and new failures. Android stays report-only behind a dispatch input. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…interrupt - Rolling baselines take one scheduled run per UTC night (the newest usable one) and page through workflow runs until enough nights are found. Explicit --to/--from references still accept any trigger. - A run measures at least three rounds. A timing verdict needs three successful samples tonight and on each counted baseline night. - Failures get their own verdict. more-failures flags tonight failing more than every baseline night, in count and share; a faster median with worse failures is not an improvement. - The interrupt handler and the normal finish share one finalizer: save logs, stop session and daemon within a deadline, save again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
558da5c to
fed2a4c
Compare
…t readers - The forced-tree step ran a raw capture. The runner honors a backend pin only on the regular plan, so the raw plan recovered through private AX and the expectation passed a degraded row. The step is now a regular pinned capture on the home screen, and its expectation requires the pinned backend with the runner's `requested-backend` code. - Perf metadata reads the revision, toolchain, and simulator through the readers of the iOS snapshot benchmark, moved to scripts/lib/bench-host.ts. - host-kit and the daemon client export the phase names that the profiler reads. - summary-fixture.ts becomes summary.fixtures.ts; the nightly header matches 3 rounds. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#3885 was rebased onto main after #3884 merged. This merge takes its new head with the Android layer's two commits replayed on it. Suite conflicts resolve to the renamed suite.ts and suite-harness.ts: the pinned-tree capture moves to home-reads (iOS only) and the Android one-shot step keeps its transport check. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… the pinned capture - platform-android exports the helper capture and gesture phase names through its mechanics entry; the profiler and its test import them. - The README's snapshot-path section and parity table name the iOS pinned capture on home-reads and the reason code it must carry. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…x the local compare example Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
The code at 4f18a1a looks ready, and I found nothing that needs to change. All 11 checks pass, but the perf nightly workflow this PR changes is not a PR check, so only the cited workflow_dispatch runs exercise the Android suite. I did not open those runs or their artifacts, so the 104-step and 0-failure results are the author's claim. The PR has merge conflicts with its base branch: the head carries pre-rebase copies of the base commits. Please rebase onto the current perf/ios-command-suite branch so the PR shows only its two commits, then re-run the checks. Base PR #3885 must land first. I reviewed the logical patch from 558da5c to the head, so any conflicts that appear after the rebase are not reviewed. Not blocking, and you can take or leave these: scripts/perf/profile.ts:112 counts a skipped persistent-session capture as a failure when |
|
New head: 181d526. #3884 merged, and the maintainer rebased #3885 onto main. This layer takes the new #3885 head (8958314) through a merge commit, so nothing was force-pushed. Merge (88d7d73)
Follow-ups
Device evidence at 70faef4The commit after it changes only a log string and the README.
The only failure was in the dropped warmup:
Validation
Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com |
Only scripts read it, so a root export has no production consumer and fails the fallow audit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * ps/ios-suite: fix(perf): keep the daemon request phase literal # Conflicts: # scripts/perf/profile.ts
|
Head is now 50838e4: a merge of #3885's 9e9ccc3, which keeps the
One observation from the earlier failed gate run, unrelated to this PR: Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com |
Brings in #3892, which fixes the iOS simulator logs stop hang. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * origin/main: perf(ios): read each resolved text input through one snapshot (#3882) fix(gate): take the host validation lock for heavy gates (#3900) fix(logs): keep an early iOS simulator logs stop from hanging the session (#3892) fix(apple): clean the runner derived path the runner uses (#3897) test(provider-scenarios): open deadline scenarios without the test IME (#3903) test(host-kit): stub os.homedir for the exec-trace late-close test (#3898)
#3892 fixed the iOS simulator logs stop hang, so the untimed wait before logs-stop is no longer needed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * ps/ios-suite: fix(perf): stop the log stream right after it starts perf(ios): read each resolved text input through one snapshot (#3882) fix(gate): take the host validation lock for heavy gates (#3900) fix(logs): keep an early iOS simulator logs stop from hanging the session (#3892) fix(apple): clean the runner derived path the runner uses (#3897) test(provider-scenarios): open deadline scenarios without the test IME (#3903) test(host-kit): stub os.homedir for the exec-trace late-close test (#3898)
|
Head is now 50502ec. It merges #3885's aed5aa7, which has main with #3892 and drops the Device check at 50502ec on my own API 35 emulator, with The same scenario on iOS at the parent head is clean too; see #3885.
Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com |
|
The conflict from my earlier review (#3902 (comment)) is fixed at 50502ec, and I found no code problems in the two new commits since 181d526. All 11 checks pass at this commit. I did not run a device. The Android emulator result comes from your comment, so that evidence is taken as reported. The PR still waits for its base #3885 (perf/ios-command-suite) to land first. One design question on |
|
I merged main after #3885 squash-merged (no force-push). main's |
|
I found no code problems at fdacf18. The Android timing run is the same logical patch as before, so the results you reported at 70faef4 and 50502ec still apply. All 16 PR checks pass and I know of no conflicts. perf-nightly.yml is not a PR check, so CI does not run the Android suite route. Only your workflow_dispatch and local runs cover it. I did not run a device, open the workflow_dispatch artifacts, or rerun vitest, lint or format. The design question in #3902 (comment) is still open. Can the answering transport be recorded where it is produced, instead of being inferred in |
* origin/main: perf(ios): confirm fill focus from the tapped input before the keyboard (#3906)
…re timer The snapshot helper's capture timers now name the transport they drove, and the session timer records whether the session answered. profile.ts reads those fields per event instead of inferring a silent session from event order, and no longer re-spells the producer names platform-android owns. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Head is now 1fddbe9. It answers the design question from #3902 (comment): yes, the helper now records the transport at its source, and Change
Why I kept the
|
|
This PR is ready at 1fddbe9. The earlier design question is answered: each snapshot-helper capture timer now records its transport and whether the session answered, and the emulator run you reported reaches the changed diagnostics on snapshot-raw, snapshot-i-scrolled and the forced one-shot step. Not blocking, take or leave: in |
|
Fail the heavy-feed snapshot-raw step with expectation_failed when the persistent helper session did not answer, so one-shot timings stay out of the session baseline. Drop the redundant second sessionCapture assignment in the helper capture flow. Follow-up to #3902. Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Summary
Android now runs the iOS command suite against the fixture app. One shared
suite.tsholds the steps, and platform deltas are data (only,on.android). One harness,suite-harness.ts, owns rounds, sessions, and daemon restarts. The Settings tour is removed. 30 files changed.hardware-button,push, andtelephony. iOS hasaction-button,prepare,clipboard, andhome-reads/snapshot-forced-tree.snapshot-raw-one-shotrestarts the daemon withAGENT_DEVICE_ANDROID_SNAPSHOT_HELPER_SESSION=0and must be served byinstrumentation(ADR 0002).transport, and the session timer also recordsanswered.profile.tsreads these fields and no longer infers the transport from event order.perf:comparegains--platform.Validation
persistent-session×3 and the one-shot step byinstrumentation×3. In every sample, the profile matches the response.heavy-feed,automationand iOShome-reads,heavy-feedran clean, apart from the 3 known iOS heavy-feed gaps.vitest scripts/perfpassed 45/45. The platform-androidsnapshot*tests passed 190/190.pnpm check:affected --run --head 1fddbe9d0fpassed (exactCommit: true, 37 passed, 22 GitHub-authoritative skips).🤖 Generated with Claude Code