Skip to content

perf(ios): skip runner uptime preflight after recent healthy mutations#763

Merged
thymikee merged 1 commit into
mainfrom
perf/667-broaden-preflight-skips
Jun 11, 2026
Merged

perf(ios): skip runner uptime preflight after recent healthy mutations#763
thymikee merged 1 commit into
mainfrom
perf/667-broaden-preflight-skips

Conversation

@thymikee

Copy link
Copy Markdown
Member

Closes #667

What

Reintroduces the adaptive readiness-preflight skip from #662 — deliberately reverted in #702 — as a structurally narrower "healthy mutation recency" signal, now covering hot tap/tapSeries plus the coordinate-gesture families (longPress, drag, dragSeries, swipe).

Why it doesn't reintroduce the #702 failure modes

#702 removed the original skip because unhealthy runners were cached as recently-successful. Each failure mode is now structurally addressed:

  • Sparse AX-fallback responses cached as healthy (Bluesky): recency is recorded only from ok responses to allowlisted mutating interactions, after the runnerFatal check. Snapshots, read-only responses, and runnerFatal ok-payloads never refresh it.
  • AX-fatal responses must invalidate: unchanged — fatal paths clear recency explicitly and the record lives on the RunnerSession object, which dies with every invalidation/restart. The first command after any restart always preflights.
  • Stale runners after navigation taps: the root teardown trigger was removed by fix: stabilize iOS runner navigation taps #702's coordinate-first activation; the window is tightened to 5s (from perf: adapt ios runner uptime preflight #662's 10s); each skip must be re-earned by another healthy mutation; and a post-skip transport failure clears recency (no skip loops) and routes through journal status recovery — the skip marker key is disjoint from runnerReadinessPreflightFailed, so it can never enter restart-and-replay.
  • App activation uncertainty: the skip requires appBundleId to exactly match the recorded one; app switches preflight with reason app_activation_uncertain.

Decision ladder reasons are exposed in diagnostics: used (startup / conservative_command / no_recent_healthy_mutation / app_activation_uncertain / healthy_mutation_stale + age), skipped (recent_healthy_mutation + age), recovered (skip context restored in status-recovery diagnostics and lost-response hints, as in #662).

ADR 0005 and docs/ios-runner-protocol-optimizations.md are amended in the same PR. Today's scroll verb is covered via drag; the fused scroll command from #760 should join the allowlist once that lands (noted in code).

Validation (iPhone 17 Pro Max simulator, iOS 26.2)

Per-request diagnostics (--debug), Settings app:

request runner requests preflight decision
press #1 (cold session) 2 ran — startup
press #2–4 (hot loop) 1 skipped — recent_healthy_mutation (age ≈ 210ms)
scroll ×2 3 (main: 4) interactionFrame preflights (conservative_command), drag skips
press after 6s idle 2 ran — healthy_mutation_stale

One fewer runner request per hot command, zero ios_runner_session_invalidated events, all interactions succeeded.

  • pnpm typecheck clean; full iOS unit suite 287 passed (incl. new per-family skip tests, recency hygiene tests, and reintroduced perf: adapt ios runner uptime preflight #662-era status-recovery/lost-response-guidance tests); ios-lifecycle provider integration 48 passed.

Reintroduces the #662 adaptive readiness-preflight skip with guardrails
for the #702 failure modes. Recency is recorded only from healthy
(non-runnerFatal) responses to an explicit mutating-interaction allowlist
(tap, tapSeries, longPress, drag, dragSeries, swipe), scoped to the same
appBundleId, capped at a 5s freshness window, and lives on the session
object so it dies with every invalidation. Startup, no-recent-success,
stale, app-switch, and non-allowlisted commands still preflight. A
transport failure after a skip clears recency, carries the skip context
through status recovery, and never routes into restart-and-replay.

Closes #667
@github-actions

Copy link
Copy Markdown

Size Report

Metric Base Current Diff
JS raw 1.2 MB 1.2 MB +1.7 kB
JS gzip 390.4 kB 390.7 kB +376 B
npm tarball 501.7 kB 502.0 kB +327 B
npm unpacked 1.7 MB 1.7 MB +1.6 kB

Startup median (7 runs, lower is better):

Scenario Base Current Diff
CLI --version 24.4 ms 24.2 ms -0.2 ms
CLI --help 38.1 ms 37.6 ms -0.5 ms

Top changed chunks:

Chunk Raw diff Gzip diff
dist/src/2415.js +2.0 kB +525 B
dist/src/9542.js -422 B -144 B
dist/src/cli.js +379 B +85 B
dist/src/8173.js -175 B -70 B

@thymikee

Copy link
Copy Markdown
Member Author

Non-blocking merge-order note: this allowlist is correct against current main, but it will need scroll once #760's fused runner command lands. If #760 merges first, please rebase this PR and add scroll to PREFLIGHT_SKIP_ELIGIBLE_RUNNER_COMMANDS; if this PR merges first, #760 should add it while resolving/rebasing. Otherwise the new hot scroll path stays correct but misses this preflight-skip optimization.

@thymikee thymikee merged commit 462db52 into main Jun 11, 2026
19 checks passed
@thymikee thymikee deleted the perf/667-broaden-preflight-skips branch June 11, 2026 09:46
@github-actions

Copy link
Copy Markdown
PR Preview Action v1.8.1
Preview removed because the pull request was closed.
2026-06-11 09:46 UTC

thymikee pushed a commit that referenced this pull request Jun 11, 2026
#763 landed the healthy-mutation preflight skip with a note that the
fused scroll command should join the allowlist once it exists. Add
'scroll' to PREFLIGHT_SKIP_ELIGIBLE_RUNNER_COMMANDS, drop the
now-resolved code note, extend the per-family skip tests, and update
the allowlist enumeration in ADR 0005 and the protocol-optimizations
doc.

https://claude.ai/code/session_01VokBZWESTDgcnbYwS4DkJo
thymikee added a commit that referenced this pull request Jun 11, 2026
…and (#760)

* perf(ios): fuse scroll frame resolution and drag into one runner command

Non-tvOS scroll now sends a single mutating 'scroll' runner command. The
Swift runner resolves the interaction frame and executes the same
non-synthesized drag path, eliminating the separate read-only
interactionFrame request per scroll. The command is lifecycle-journaled
with retained response JSON so lost-response recovery returns the result
without replaying the gesture.

Closes #668

* perf(ios): make fused scroll eligible for readiness preflight skip

#763 landed the healthy-mutation preflight skip with a note that the
fused scroll command should join the allowlist once it exists. Add
'scroll' to PREFLIGHT_SKIP_ELIGIBLE_RUNNER_COMMANDS, drop the
now-resolved code note, extend the per-family skip tests, and update
the allowlist enumeration in ADR 0005 and the protocol-optimizations
doc.

https://claude.ai/code/session_01VokBZWESTDgcnbYwS4DkJo

* test: complete scroll plan parity vector mirror

Address review on the cross-language parity vectors:
- mirror the Swift pixels-plan vector (down, 120px @ 300x600) in the
  vitest suite so every vector exists in both languages
- add amount > 1 clamp and tiny-frame (2x2) vectors to both suites;
  the tiny frame engages every max(1, ...) floor and the .5 rounding
  cases where JS half-up and Swift half-away-from-zero must agree

https://claude.ai/code/session_01VokBZWESTDgcnbYwS4DkJo

---------

Co-authored-by: Claude <noreply@anthropic.com>
thymikee pushed a commit that referenced this pull request Jun 11, 2026
Rebased onto main with #763 (healthy-mutation preflight skip) and #760
(fused scroll). Per the merge-order note, add 'sequence' to
PREFLIGHT_SKIP_ELIGIBLE_RUNNER_COMMANDS so a successful sequence earns
the next hot-command skip instead of always taking the
conservative_command path. Extend the per-family skip tests and the
allowlist enumeration in ADR 0005 and the protocol-optimizations doc.

https://claude.ai/code/session_01VokBZWESTDgcnbYwS4DkJo
thymikee added a commit that referenced this pull request Jun 11, 2026
…eries (#764)

* perf(ios): add lifecycle-safe runner sequence command for hot press series

Adds a narrow 'sequence' runner command that batches an explicit
allowlist of coordinate steps (tap, longPress, drag) into one
lifecycle-tracked request with stop-on-first-failure and small bounded
per-step results. iOS press series with hold/jitter now issue one
sequence request per ~20-step chunk (also budgeted to stay under the
runner's 30s main-thread watchdog) instead of one request per press.
Sequence responses are journaled and retained, so lost-response recovery
returns observed results without replaying the gesture sequence.

Closes #669

* fix: perform every press in direct press series

runDirectPressSeries guarded the awaited interaction itself with ??=,
so presses 2..N were silently skipped once the first result was kept
(affects Android series and doubleTap series; introduced in #512).
The kept-first-result shape is preserved.

* chore: unexport internal sequence chunk budget constant

* perf(ios): make sequence eligible for readiness preflight skip

Rebased onto main with #763 (healthy-mutation preflight skip) and #760
(fused scroll). Per the merge-order note, add 'sequence' to
PREFLIGHT_SKIP_ELIGIBLE_RUNNER_COMMANDS so a successful sequence earns
the next hot-command skip instead of always taking the
conservative_command path. Extend the per-family skip tests and the
allowlist enumeration in ADR 0005 and the protocol-optimizations doc.

https://claude.ai/code/session_01VokBZWESTDgcnbYwS4DkJo

---------

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

perf(ios): broaden adaptive runner uptime preflight beyond hot taps

1 participant