Skip to content

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

Merged
thymikee merged 4 commits into
mainfrom
perf/669-runner-sequence-tracer
Jun 11, 2026
Merged

perf(ios): add lifecycle-safe runner sequence command for hot press series#764
thymikee merged 4 commits into
mainfrom
perf/669-runner-sequence-tracer

Conversation

@thymikee

Copy link
Copy Markdown
Member

Closes #669

What

Adds a narrow, lifecycle-tracked sequence runner command that batches an explicit allowlist of coordinate steps (tap, longPress, drag) into one runner request, and routes the iOS hold/jitter press series through it.

  • Allowlist enforced on both ends: the daemon validates steps via buildRunnerSequenceCommand/validateRunnerSequenceSteps (new src/platforms/ios/runner-sequence.ts), and the Swift runner rejects non-allowlisted kinds/missing coords with INVALID_ARGS before executing anything. Non-fuseable inputs (count=1, Android, doubleTap, tapSeries-eligible, selector presses) keep their existing paths.
  • Lifecycle safety: one command ID, one journal entry. Step failure is data (ok:true + failedStepIndex + per-step results), so the journal records completed and retains the small payload — lost-response recovery returns observed results without replaying the gesture sequence. In-flight/failed states follow the existing no-replay semantics. Sequence is mutating (not in isReadOnlyRunnerCommand): single-send, conservative recovery.
  • Bounded: max 20 steps per request, chunked by step count and by estimated wall-clock (20s budget, safely under the runner's 30s main-thread watchdog — press --count 20 --hold-ms 1500 splits instead of tripping the watchdog mid-execution). Worst-case retained response stays far below the journal's 16KB cap (asserted in Swift tests). Ordering is preserved; execution stops at the first failed step; cross-chunk failures rebase failedStepIndex/completedSteps to global indices.
  • Semantics parity: sequence taps honor the synthesized HID path exactly like the individual tap command; drags route through keyboardAvoidingDragPoints like the individual drag.

Bug found during validation

runDirectPressSeries guarded the awaited interaction with ??=, so press --count N with hold/jitter silently performed only the first press (presses 2..N short-circuited once the first result was kept; introduced in #512, also affects Android and doubleTap series). Fixed in a separate commit with a regression test (the old test's mock returned undefined, which masked the short-circuit).

Validation (iPhone 17 Pro Max simulator, iOS 26.2, isolated daemon state dirs)

runner requests presses actually performed
main, press 220 600 --count 10 --jitter-px 2 1 (tap) 1 (the ??= bug)
corrected main behavior 10 + preflights 10
this PR 1 (sequence) 10 (completedSteps: 10, 10 per-step results)
  • press --count 25 → exactly 2 sequence requests (20+5 chunks), results aggregated across chunks.
  • press --count 20 --hold-ms 1500 (~36s of holds) → 2 budget-split requests, zero watchdog trips, zero session invalidations.
  • pnpm typecheck clean; core + iOS unit suites 406 passed (incl. completed/failed-at-index/in-flight/lost-response recovery, allowlist rejection, chunk rebasing, budget chunking); pnpm build:xcuitest:ios succeeds with new Swift journal-retention and sequence-assembly tests.

@github-actions

github-actions Bot commented Jun 11, 2026

Copy link
Copy Markdown

Size Report

Metric Base Current Diff
JS raw 1.2 MB 1.2 MB +3.8 kB
JS gzip 390.8 kB 392.0 kB +1.2 kB
npm tarball 504.6 kB 510.2 kB +5.6 kB
npm unpacked 1.7 MB 1.7 MB +22.9 kB

Startup median (7 runs, lower is better):

Scenario Base Current Diff
CLI --version 27.8 ms 26.0 ms -1.8 ms
CLI --help 42.7 ms 42.5 ms -0.3 ms

Top changed chunks:

Chunk Raw diff Gzip diff
dist/src/2415.js +3.8 kB +1.2 kB

@thymikee

Copy link
Copy Markdown
Member Author

CI is not review-ready yet: Fallow reports one unused export in src/core/dispatch-series.ts.

  • RUNNER_SEQUENCE_CHUNK_BUDGET_MS is exported but has no known consumers.

This should either become a local constant if only dispatch-series.ts needs it, or be consumed by the tests/helpers that intentionally need the shared budget. After the change, please rerun pnpm check:fallow --base origin/main and the focused sequence/series tests.

@thymikee

Copy link
Copy Markdown
Member Author

Non-blocking merge-order note with #763: once the healthy-mutation preflight skip lands, sequence should join PREFLIGHT_SKIP_ELIGIBLE_RUNNER_COMMANDS in runner-session.ts.

This command is deliberately a lifecycle-tracked mutating interaction and, after a successful sequence, it is exactly the kind of recent healthy runner response that should earn the next hot-command skip. If #763 merges first, please rebase this PR and add sequence; if this PR merges first, #763 should add it while resolving/rebasing. Otherwise the sequence path remains correct but gives up part of the perf win by always taking the conservative_command preflight path.

@thymikee thymikee force-pushed the perf/669-runner-sequence-tracer branch from 0a29352 to 1982104 Compare June 11, 2026 09:12
thymikee and others added 4 commits June 11, 2026 10:07
…eries

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
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.
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 thymikee force-pushed the perf/669-runner-sequence-tracer branch from 1982104 to c1d5817 Compare June 11, 2026 10:09
@thymikee thymikee merged commit e6e2baf into main Jun 11, 2026
19 checks passed
@thymikee thymikee deleted the perf/669-runner-sequence-tracer branch June 11, 2026 10:50
@github-actions

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

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): add lifecycle-safe runner sequence tracer for hot interaction loops

2 participants