Skip to content

perf(android): bound target readiness and confirm moved clears - #3815

Merged
thymikee merged 21 commits into
t3/investigate-android-speedfrom
t3/android-readiness-and-lifecycle
Oct 19, 2026
Merged

thymikee merged 21 commits into
t3/investigate-android-speedfrom
t3/android-readiness-and-lifecycle

Conversation

@thymikee

@thymikee thymikee commented Oct 19, 2026 •

Copy link
Copy Markdown
Member

Summary

Bound Android press readiness and avoid redundant stale-tree recovery; reuse the warm helper for app launch. Confirm moved empty fields only from complete, fresh captures with unique pre/post identity. Dead helper sessions retire centrally; uncertain mutations fail without a resend.

const caps = await client.devices.capabilities({ platform: 'android' });
// caps.targetReadiness?.press === 'bounded-geometry'

Moving-clear fixture: 1.637 s plus failure→0.323 s success. Unsafe adapter timer bypasses were rejected; its settling timer remains. Experiment and limits. Experimental-engine parity remains open.

49 files, parent-relative: production +363/−51; tests +575/−21; docs +1485/−1; 2,496 gross lines. Stacked on #3811. Adapter #1253 stays draft pending SDK release/dependency bump. Reference: e2e#1234.

Validation

Final c2fddc1ef9, parent 1977e685b2, main a661e43d83: full rebased gate 16,439 passed/one skipped (run-watm1q); final incremental gate 1,946 passed (run-FXwZHp); both exact/unchanged. All 20 upper commits remain patch-equivalent after rebase; independent review has no findings.

Isolated mutations detect missing moved-clear handoff, dead-session admission, and cancellation bypass. Live API 36: helper refusal→ADB fill; controlled screenshot failure→PNG; warm open zero host launches; exited-helper open exactly one. Test-app fill/clear/refill preserves its sibling; native alert acceptance uses one cancellable host tap. Sessions/daemons closed.

Fresh full benchmark: original eef1a3f76d 335.069 s; pre-daemon 0a43d61ff6 282.000 s; final c2fddc1ef9 273.989 s (18.2% reduction from original). All 42 passed, one authored skip, zero retries/recoveries. One local run per arm. Older APIs/physical devices and actual-head CI obligations remain.

@thymikee
thymikee added this pull request to stack #3816 October 19, 2026 12:15
@thymikee thymikee changed the title t3/android readiness and lifecycle perf(android): bound target readiness and avoid redundant recovery Oct 19, 2026
@github-actions

github-actions Bot commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor
PR Preview Action v1.8.1
Preview removed because the pull request was closed.
2026-10-19 17:53 UTC

@github-actions

github-actions Bot commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Size Report

Metric Base Current Diff
Installed (including dependencies) 5.45 MB 5.45 MB +3.4 kB
Package (unpacked) 5.45 MB 5.45 MB +3.4 kB
Package (download) 1.65 MB 1.65 MB +2.3 kB

Startup median (7 runs, lower is better):

Scenario Base Current Diff
CLI --version 29.1 ms 29.4 ms +0.3 ms
CLI --help 53.7 ms 56.2 ms +2.5 ms

@thymikee

Copy link
Copy Markdown
Member Author

At 8b29f46, the Android open path has one defect that should be fixed before merge. runAndroidAutomationHelper admits a session through getLiveAndroidSnapshotHelperSession, which is a bare sessions().get() (automation-helper.ts#L27). It skips the hasAndroidSnapshotHelperProcessEnded check that capture applies through isReusableAndroidSnapshotHelperSession (snapshot-helper-session-lifecycle.ts#L272-L277). If the helper process has exited but is still cached (helper crash, UiAutomation loss, adb restart dropping the forward), the new app action connects to a dead port. The connection is refused, the session is stopped, and tryAndroidHelperLaunch (app-lifecycle.ts#L261) rethrows COMMAND_FAILED. Nothing was dispatched, and there is no host fallback. So open <package> fails on a warm device whose helper died after the last capture, where it used host am start and succeeded before this PR. A retry would clear it, but this is the dead-helper-in-use case the redundant-recovery skip must not create. The rule: every command that piggybacks on a helper session admits the session only through the same reusability predicate the capture path uses. getLiveAndroidSnapshotHelperSession is the one owner every piggyback reader goes through, so please make it return undefined and retire the entry when the process has ended. Then input, screenshot, focus and app fall through to their host paths together. Please add a test where the cached session's process has ended and open reaches host am start exactly once.

Not blocking, and fine to take or leave: the Android readiness policy is decided at four sites keyed on separate platform strings (the gate at moving-target-gate.ts:74 and :146, ref-target-resolution.ts:228, and the inventory claim at inventory.ts:288) while the gate docblock still says it is backend-neutral; cacheInvalidated proves the read bypassed the node cache but not that the app committed the route, so the old stuck-route retry no longer covers app-side navigation latency such as a React Native JS-thread navigation (android.ts:93); the Android loop dispatches after 100 ms of quiet, before the window ends, so a target that starts a delayed animation can be certified where the shared rule would refuse it (moving-target-gate.ts:226); the inline package regex at app-lifecycle.ts:255 re-declares ANDROID_PACKAGE_NAME and could use classifyAndroidAppTarget(app) === 'package'; automation-helper-app.test.ts tests openAndroidApp and mocks runAndroidAutomationHelper wholesale, so the app session protocol and the advertised-command gate have no unit coverage and the cases belong beside the app-lifecycle-open tests; and the diff is about 1,700 lines over four concerns, so the helper app launch could ship as its own stacked PR.

Could one MovingTargetSchedule per platform (windowMs, quietMs) live beside RUNTIME_TREE_TARGET_STABILITY, with the gate picking its loop from it and the capabilities projection deriving targetReadiness from it? That would remove the inventory restatement, and putting the liveness check in getLiveAndroidSnapshotHelperSession would mean the app action needs no special case. The first change would be adding a declared per-platform schedule to the interaction-guarantees contract or the backend descriptor.

The live evidence (42 e2e cases and 10 test-app scenarios) is author-reported, on one API 36 emulator only. The helper advertises app for SDK 31 and up, and its Status: ok parsing is unverified on API 31-35. The dead-helper failure above comes from reading the code and was not reproduced live. I did not run any tests, and I have not shown whether the cacheInvalidated navigation-latency risk is real.

Both Smoke Tests jobs are still queued and nothing has failed yet. The diff overlaps the smoke routes, because the shared moving-target gate runs for every press and the Android helper and open path change for Android smoke. There are no conflicts. Before merge, make the piggyback helper commands admit only sessions whose process is alive, through getLiveAndroidSnapshotHelperSession, so a dead helper cannot fail an open, and let the smoke jobs finish green.

@thymikee thymikee changed the title perf(android): bound target readiness and avoid redundant recovery perf(android): bound target readiness and confirm moved clears Oct 19, 2026
@thymikee

Copy link
Copy Markdown
Member Author

Thanks for the update. At bfa27fb, one problem from the 8b29f46 review (#3815 (comment)) is still open, and it needs a fix before merge. I read the code and did not run tests or reproduce this live.

getLiveAndroidSnapshotHelperSession returns sessions().get(deviceKey) without the ended-process check that capture applies through isReusableAndroidSnapshotHelperSession (https://github.com/callstack/agent-device/blob/bfa27fb/packages/platform-android/src/snapshot-helper-session-lifecycle.ts#L150). runAndroidAutomationHelper admits the new app action through it, so a stored session whose process exited still passes. The connection is then refused, and tryAndroidHelperLaunch rethrows COMMAND_FAILED with no host fallback. So open <package> fails on a warm device whose helper died after the last capture, where the host am start path worked before this PR. The rule: every piggyback helper command (input, screenshot, focus, app) admits a session only through the same reusability predicate that capture uses. Please apply it once inside getLiveAndroidSnapshotHelperSession: return undefined and retire the entry when the process has ended. Please add a test where the stored session's process has ended and open reaches host am start exactly once.

Not blocking, take or leave: completeFreshAndroidFillCapture in fill-verification.ts restates the completeness predicate that session-snapshot-freshness.ts also writes inline, and the two copies already differ on systemSurfaceOnly, so one exported isCompleteFreshAndroidCapture(metadata) beside AndroidSnapshotBackendMetadata would serve both; no test goes through the fillAndroid helper branch, so dropping beforeCapture there would pass every new test; fill-verification-empty-target.test.ts should move into __tests__/fill-verification.test.ts to keep the one-to-one test layout; and could the moved-clear identity check ship as its own stacked PR, since this one already carries four concerns?

The live trials in the PR body are author-reported on one API 36 emulator, and I did not reproduce them. All checks were queued and none had failed when I looked, and the diff touches the Android smoke and integration routes, so those jobs need to finish green. There are no conflicts. Before merge, the helper session fix and its test must land.

@thymikee
thymikee force-pushed the t3/android-readiness-and-lifecycle branch from bfa27fb to d34d991 Compare October 19, 2026 15:50
@thymikee

thymikee commented Oct 19, 2026 •

Copy link
Copy Markdown
Member Author

Addressed the dead-helper finding through the shared session owner, inherited from #3811. Final upper head: c2fddc1ef9, rebased onto parent 1977e685b2 and main a661e43d83. Earlier named receipts below remain attributed to their original heads.

getLiveAndroidSnapshotHelperSession now retires an ended cached process before any piggyback command is admitted. All callers await it, including transient capture's release decision. App remains a mutation in the shared action-policy table, so uncertain launches cannot be resent through the host path.

Added real fake-socket/session coverage through openAndroidApp, without mocking the dispatcher: advertised app protocol/payload, unsupported-command host fallback, and ended-helper host fallback. The initial unpadded-package fixture on original bfa27fb3be: one failed, two passed, with the ended-session case failing ECONNREFUSED. The three original cases passed. Restoring only the unsafe session getter against those final cases produced one failed, two passed, again ECONNREFUSED. The cases also cover surrounding whitespace, which is normalized once for eligibility, payload, existence checks, and errors. Independent review caught that normalization omission and then confirmed its correction. The initial focused composition checks passed 49 tests.

Live rebuilt CLI/helper on Pixel 7/API 36:

  • Warm helper open: zero host am start calls.
  • After the owned helper host process had actually exited: successful reopen with exactly one host am start call; subsequent capture used android-helper, version 0.21.29-dev.
  • Real native fill refusal and controlled screenshot-response failure both recovered through their intended host paths, with fallback diagnostics.
  • Bundled test-app: fill, clear, refill, sibling preservation, and normal PNG capture passed.

The first controlled fixture lacked the DEFAULT launch category, so its normal host launch needed the existing component-resolution fallback (two shell attempts). I corrected that fixture category and repeated the full sequence; the exactly-one count above is from the final run. No production retry policy changed for this fixture issue.

For the optional points: I retained native helper package eligibility rather than using the broader shared classifier. Live _com.example.missing at 8879798220 failed with COMMAND_FAILED / Invalid application package; final c2fddc1ef9 retains the host path and returns APP_NOT_INSTALLED. A fourth fake-session case covers this shape; all four pass. The moving-target comment states platform-specific timing. I retained the existing readiness policy and PR layering. A fresh cache-invalidated tree does not prove that an app committed its route; a 100 ms quiet interval does not rule out later animation. Those remain experimental limits, with authored waits and uncertain-mutation safeguards retained. Consolidating platform schedules is a separate contract change; this review round does not broaden readiness guarantees.

Affected gate passed: 16,167 tests passed, one skipped; receipt run-85gC8b, exactCommit=true, checkoutChanged=false. GitHub coverage/native/device/provider checks remain separate obligations.

The rebase range was compared and independent review has no unresolved findings. All owned sessions/daemons closed. Older APIs/physical devices and actual-head CI obligations remain; experimental-engine parity stays open. There were no inline threads to resolve.

The final comment audit also found the moved-clear coverage suggestion in the later review. Addressed: the existing warm helper test now calls fillAndroid, clears a uniquely identified field, moves it away from a nonempty decoy, and retains its three-capture/one-session assertions. It replaces the weaker direct positive; dedicated negative verifier cases remain separate. Focused run at 0a43d61ff6: 52 passed. In isolation the moved-clear test passes; deleting only the beforeCapture argument makes it fail with verification against decoy D. The first isolated control exposed a swallowed install-version probe; the fixture now delegates that probe and no longer depends on earlier tests' cache state. Those setup failures are excluded from mutation proof.

The completeness predicate remains local because moved-empty corroboration deliberately rejects system-only captures in addition to the general freshness requirements. I retained the dedicated negative-test file and current stack rather than broaden this into a contracts/layout migration. All table results remain in the linked JSON; the duplicated prose table was removed. Final parent-relative gross size: 2,496 lines.

Final test/evidence-only head 0a43d61ff6: incremental affected gate passed, 52 tests passed; receipt run-P677Nr, exact commit and unchanged checkout. Independent follow-up reviews have no findings.

Current-main conflict follow-up: the parent preserves upstream cancellable input on the host path; its full exact gate passed 16,388 tests, one skipped (run-gWuy8f). Two isolated signal tests pass and both fail when only their bypass guards are removed. Native-alert acceptance with a live helper passed with one host tap, successful close and graceful daemon stop. Its first action also succeeded, but a missing CLI output artifact during concurrent validation prevented close; that run's close failure is retained separately, and the full check passed when repeated after the gate.

All 20 upper commits are patch-equivalent after rebase; independent review has no unresolved findings. Final upper validation: the full rebased gate at 8879798220 passed 16,439 tests, one skipped (run-watm1q); final c2fddc1ef9 passed its incremental affected gate with 1,946 tests (run-FXwZHp). Both receipts have exactCommit=true, checkoutChanged=false. The latest source replayed the requested fallback/open paths and test-app controls; all owned sessions and daemons were closed.

@thymikee
thymikee force-pushed the t3/android-readiness-and-lifecycle branch from 0a43d61 to c2fddc1 Compare October 19, 2026 16:22
@thymikee

Copy link
Copy Markdown
Member Author

Checked latest main a661e43d83, including daemon merges #3797, #3806 and #3818. Both explicit rebase commands were no-ops: parent 1977e685b2, upper c2fddc1ef9 already contain it. Published heads are unchanged and both are conflict-free. Their existing exact-head validation receipts still apply.

Fresh Android comparison: pre-daemon stack 0a43d61ff6 versus current c2fddc1ef9, sequential ABBA, two runs per source, same Pixel 7/API 36 and installed bundled Release test-app, isolated built SDKs/daemon states, zero retries. Native helper source is unchanged. All 236 timed operations, including 24 native clear oracle trials, passed. This compares all intervening main changes, not only daemon work.

Observed p50 in milliseconds:

Operation Before Current
Warm package open 162 170
Relaunch with URL 1054 940
Fill 286 274
Clear 276 275
Screenshot 86 86
Scroll until bottom target + settle 1988 1992
Scroll until top target + settle 1895 1966

No median regression reached 100 ms in these workloads. Every warm package open used zero host am start calls; helper reuse survived the runtime-store migration. No helper starts occurred during measured fill, screenshot or scroll requests. All four runs had zero helper fallbacks.

Cold follow-up: 20 fresh-daemon/app-start cycles, alternating source order, force-stopped app, identical source-built native APK installed before timing. Open median 2,220→2,054 ms; close 277→261 ms; daemon stop 187→187 ms. Both sources used two helper starts during cold foreground open; the extra start observed in one primary cold trial is also present on the older implementation.

Tails remain visible: one primary moving-clear trial took 791 ms, with additional reads in the existing input/verification path, and one cold open took 3.32 s. There was no fallback, resend or wrong edit. The fill verifier and native Java input source are unchanged. A dedicated alternating repeat added 16 moving-clear trials: before median 276.5 ms, range 257–327; current median 279 ms, range 262–290. All had one clear and zero decoy edits/clicks.

This is a small local regression check, not a statistical tail-latency guarantee or a new full 42-case speedup result. Scroll-until needed one or two passes depending on observed content position. The initial harness attempt failed its scroll target after generic Back; it is retained and excluded. Corrected runs use explicit keyboard dismissal and verify target visibility.

All owned sessions/daemons closed. Raw samples, traces, scripts and the excluded setup run are retained locally. No code/push was needed; these measurements do not replace actual-head CI obligations.

@thymikee

Copy link
Copy Markdown
Member Author

The PR is ready at c2fddc1. The earlier findings from bfa27fb (#3815 (comment)) are fixed, and I have no new problems with the code. Not blocking: the change turns the "fillAndroid runs the whole attempt on one warm daemon-session helper" test into the empty-clear case (https://github.com/callstack/agent-device/blob/c2fddc1/packages/platform-android/src/__tests__/text-input-fill.test.ts#L636), so no test now checks that a non-empty fill finishes on one warm session (one spawn, three captures); you can keep the original "chips" test and add the moved-clear case as a separate test, or leave it as is. I found no conflicts. CI is still pending: the 9 jobs that did not pass were cancelled by newer runs, not failed, so there is no failure to fix. The diff touches the Smoke, Integration and Coverage routes through the shared moving-target gate and the Android open, fill and helper paths, so those jobs need a fresh green run on c2fddc1 before merge. I ran no tests; I judged the "ended" and moved-clear tests by reading the fake session fixture and the pre-fix admission path. I reviewed only the five new commits (ffb7ed1..c2fddc1), because the range-diff shows the other 16 are patch-equivalent. The live API 36 trials (warm open with no host launch, open after the helper exited with one, moved-clear timing) come from your report on one emulator and I did not reproduce them. The helper "app" launch and its "Status: ok" parsing are still unchecked on API 31-35 and on physical devices.

@thymikee thymikee added the ready-for-human Valid work that needs human implementation, judgment, or maintainer merge label Oct 19, 2026
@thymikee

Copy link
Copy Markdown
Member Author

Fresh full Android benchmark on the final combined stack, using the original experiment’s unchanged app and test sources.

SDK Commit Passed / failed / authored skip Fixture recoveries Elapsed
Original baseline eef1a3f76d 42 / 0 / 1 0 335.069 s
Stack before daemon rebase 0a43d61ff6 42 / 0 / 1 0 282.000 s
Current combined stack c2fddc1ef9 42 / 0 / 1 0 273.989 s

Current saves 61.080 seconds (18.2% less elapsed time) versus the original baseline, and 8.011 seconds (2.8%) versus the pre-daemon stack. The daemon/main composition did not regress this full run.

All three reports contain exactly the same 43 selected identities / 42 runnable identities, matched against the reference case registry. Every runnable test passed in one attempt. Every attempt reports complete cleanup and no secondary errors. Runner retries were disabled; the fixture’s authored second-tap recovery at tests/fixtures.ts:92 was never called.

This measures #3811 + #3815 + the readiness opt-in from e2e #1253, including intervening main changes. It is not a standalone speedup attribution to either SDK PR or daemon work. The adapter is pinned at 5e1d8952f4b0fd0d6a12b084ed8151e71e7cd889; its default published-SDK dependency is unchanged. The benchmark explicitly links each independently built source SDK.

Method: Pixel_7_CI / emulator-5554, Android 16/API 36; one worker; zero retries; Device panel stream attached throughout. Release APK SHA-256 b163e985ebd8340bf10447cdf6826aea37e429ee8c4382f9179c863c8c0619b2. App/tests match reference 2593e45025edc52b2c381e809b41cb63d5878e2b. Original SDK uses the retained adapter timers because it lacks bounded readiness; both newer SDKs use E2E_MOBILE_DEVICE_READINESS=1. Each arm has isolated dependencies and daemon state. Native helper backend/version was proven before timing; builds and APK installation are excluded.

Largest original→current case reductions:

  • huge virtualized list reaches row 512: 6.476 s saved.
  • control inventory / a field autofocuses on mount and focus() moves the focus: 5.190 s saved.
  • product catalog adds exactly two of the right variant: 5.118 s saved.
  • sticky chrome: accept below the fold, then continue: 4.182 s saved.
  • control inventory / a double tap lands inside the 300 ms window: 3.667 s saved.

Current vs pre-daemon is mixed by case: bottom tabs took 1.023 s longer and error recovery 0.975 s longer; the total still improved by 8.011 s. This is one run per arm with host-load variation, not a statistical guarantee of the 2.8% difference or tail latency. The experimental direct engine, physical devices and older Android APIs were not rerun.

Exact matched runnable case IDs and attempt durations (seconds)
Test ID Original Pre-daemon Current
tests/controls.e2e.ts::control%20inventory::a%20label%20query%20resolves%20a%20labeled%20text%20field 3.576 3.561 3.553
tests/controls.e2e.ts::control%20inventory::a%20labeled%20field%20answers%20name%2C%20display%20value%2C%20value%20reads%2C%20fill%2C%20and%20clear 8.227 7.925 7.872
tests/controls.e2e.ts::control%20inventory::a%20placeholder%20query%20and%20attribute%20reads%20find%20the%20field 3.127 3.109 3.041
tests/controls.e2e.ts::control%20inventory::a%20secure%20field%20takes%20a%20fill%20and%20denies%20value%20reads 4.915 4.494 4.696
tests/controls.e2e.ts::control%20inventory::press%20sends%20a%20character%20and%20Enter%20through%20the%20soft%20keyboard 6.230 5.801 5.801
tests/controls.e2e.ts::control%20inventory::a%20field%20autofocuses%20on%20mount%20and%20focus%28%29%20moves%20the%20focus 9.343 4.290 4.153
tests/controls.e2e.ts::control%20inventory::toBeFocused%20follows%20the%20focus 8.296 5.126 4.763
tests/controls.e2e.ts::control%20inventory::a%20switch%20checks%2C%20unchecks%2C%20and%20reports%20its%20state 9.186 6.390 6.467
tests/controls.e2e.ts::control%20inventory::checkbox%20and%20radio%20roles%20answer%20getByRole%2C%20and%20a%20checkbox%20toggles%20by%20tap 6.265 4.323 4.233
tests/controls.e2e.ts::control%20inventory::checkbox%20and%20radio%20roles%20check%2C%20uncheck%2C%20and%20report%20their%20state 11.286 8.200 8.283
tests/controls.e2e.ts::control%20inventory::a%20tab%20role%20answers%20getByRole 2.981 2.960 3.286
tests/controls.e2e.ts::control%20inventory::a%20radio%20group%20reports%20the%20checked%20option 8.635 6.057 6.374
tests/controls.e2e.ts::control%20inventory::buttons%20report%20disabled%2C%20enabled%2C%20and%20a%20state%20that%20arrives%20later 7.748 5.007 5.164
tests/controls.e2e.ts::control%20inventory::a%20display%3A%20none%20element%20is%20absent%20rather%20than%20hidden%2C%20its%20visible%20twin%20attached 5.623 4.466 4.709
tests/controls.e2e.ts::control%20inventory::a%20header%20answers%20its%20accessible%20name 3.028 2.887 2.906
tests/controls.e2e.ts::control%20inventory::a%20tab%20row%20answers%20count%2C%20all%2C%20texts%2C%20filters%2C%20positions%2C%20and%20switching 4.819 4.727 4.714
tests/controls.e2e.ts::control%20inventory::a%20list%20answers%20counts%2C%20list-form%20text%2C%20reads%2C%20and%20boxes%20in%20order 4.720 3.628 3.524
tests/controls.e2e.ts::control%20inventory::a%20long%20press%20lands%20as%20one 6.708 5.488 5.520
tests/controls.e2e.ts::control%20inventory::a%20double%20tap%20lands%20inside%20the%20300%20ms%20window 7.377 4.101 3.710
tests/controls.e2e.ts::control%20inventory::an%20ambiguous%20locator%20fails%20at%20once 4.286 3.587 3.253
tests/controls.e2e.ts::control%20inventory::screenshot%2C%20restart%2C%20and%20clearState%20reopen%20the%20app%20on%20its%20home%20list 8.018 5.266 5.510
tests/controls.e2e.ts::refusals::verbs%20a%20device%20never%20declares%20are%20refused%20before%20the%20engine 0.002 0.001 0.002
tests/controls.e2e.ts::refusals::keys%20the%20soft%20keyboard%20cannot%20send%20are%20refused%20by%20the%20engine 0.128 1.541 1.480
tests/controls.e2e.ts::refusals::a%20missing%20locator%20fails%20as%20not%20found%20once%20its%20timeout%20passes 1.103 1.103 1.114
tests/flows-lists.e2e.ts::infinite%20scroll%20reaches%20the%20target%20item 14.632 14.200 14.599
tests/flows-lists.e2e.ts::product%20catalog%20adds%20exactly%20two%20of%20the%20right%20variant 9.454 4.464 4.336
tests/flows-lists.e2e.ts::gestures%3A%20long-press%2C%20then%20swipe%20left 7.660 7.234 7.465
tests/flows-lists.e2e.ts::gestures%3A%20double-tap%20completes%20the%20flow 9.002 8.649 8.797
tests/flows-lists.e2e.ts::sticky%20chrome%3A%20accept%20below%20the%20fold%2C%20then%20continue 11.036 6.733 6.854
tests/flows-lists.e2e.ts::async%20states%3A%20load%2C%20pull%20to%20refresh%2C%20claim%20through%20the%20toast 10.116 10.419 10.479
tests/flows-lists.e2e.ts::huge%20virtualized%20list%20reaches%20row%20512 55.260 59.336 48.784
tests/flows.e2e.ts::text%20inputs%20unlock%20the%20submit%20button 5.889 5.594 5.216
tests/flows.e2e.ts::filled%20text%20inputs%20report%20their%20values 5.133 4.747 4.738
tests/flows.e2e.ts::modal%20flow%20confirms%20through%20the%20native%20alert 7.716 4.635 4.395
tests/flows.e2e.ts::bottom%20tabs%20act%20inside%20the%20last%20tab 6.016 4.037 5.060
tests/flows.e2e.ts::error%20recovery%20retries%2C%20then%20confirms%20the%20delete 7.818 6.636 7.611
tests/flows.e2e.ts::choice%20controls%20place%20the%20exact%20order 8.047 4.060 4.471
tests/flows.e2e.ts::debounced%20search%20selects%20the%20target%20once%20results%20arrive 8.088 8.088 7.882
tests/login-form.e2e.ts::login%20form::rejects%20a%20malformed%20email 8.125 6.444 6.480
tests/login-form.e2e.ts::login%20form::rejects%20an%20unknown%20account 8.261 6.452 6.350
tests/login-form.e2e.ts::login%20form::signs%20in%20and%20logs%20out 12.082 11.484 11.570
tests/smoke.e2e.ts::home%20lists%20the%20scenarios%20and%20opens%20one 4.588 4.264 4.142

All owned sessions and daemons closed gracefully. Source SHAs and clean worktree state remained unchanged. Local artifacts retain full reports, source/dependency provenance, runner logs, setup snapshots and analysis scripts. Actual-head CI obligations remain separate.

@thymikee
thymikee merged commit 507ef23 into main Oct 19, 2026
23 of 32 checks passed
@thymikee
thymikee deleted the t3/android-readiness-and-lifecycle branch October 19, 2026 17:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-for-human Valid work that needs human implementation, judgment, or maintainer merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant