refactor(ios-runner): one response decoder, a session state enum, and a verdict on the curl-through-simctl transport
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 5/5
- Estimated time
- Over a week
- Newbie friendliness
- 45/100
- Issue type
- Refactor
- Clarity
- Clearly specified
- Activity status
- Active
- Tech stack
- typescript
- Domain
- mobile-dev, testing-qa
Research direction
Start with packages/platform-apple/src/runner/runner-session.ts, runner-command-recovery.ts, and runner-adoption.ts, then run the named recovery and adoption suites to trace the three response-decoding paths. Work through the cuts in order; done means the stated grep checks, affected/layering/export checks, simulator and macOS lanes, and the required session-transition tests pass without changing timeout values.
Written by the indexing model from the issue text.
Description
Remaining scope in the deletion-first initiative
#2803 now includes task 3 (the curl-through-simctl verdict) in its measurement workstream. The decoder/state-enum work has already landed; do not repeat it from the historical descriptions below.
Before deleting postCommandViaSimulator, establish that the control route is reachable and reproduce or explicitly account for the original failure condition. A week with no fallback observations is insufficient if the relevant simulator-set/toolchain/network condition was never exercised. Record baseline SHA, route selection, successful primary/fallback controls, timeout/cancellation outcomes, and a retain/delete verdict. Preserve #2963's scoped simulator-set behavior; coordinate overlapping edits with that PR. If retained, name the actual condition and evidence. If deleted, report all removed production paths/encoders and the net production-line delta, with tests separate. Temporary instrumentation is removed or justified as an owning diagnostic.
The public dispatch-outcome discussion remains outside this cleanup scope. Historical task descriptions follow.
Why
The largest maintenance surface on the Apple platform is not geometry, it is the daemon-side cluster that babysits the XCTest runner process: packages/platform-apple/src/runner/ is 38 production files, about 10k lines. A survey on main at 91652a8fc5 found the following (file:line refer to that head):
- Three response decoders for one wire.
parseRunnerResponsePayloadinrunner-session.ts:973(the canonical one),parseLifecycleResponsePayloadinrunner-command-recovery.ts:353(thestatusrecovery probe), and an inlineJSON.parseinrunner-adoption.ts:140(theuptimeprobe). - Four command-encode paths.
runner-transport.ts:48(sendRunnerCommandOnce, host TCP / usbmux),runner-usbmux.ts:52(raw HTTP framing over the usbmux socket),runner-startup-transport.ts:371(tryRunnerEndpoints, the startup connect probe) andrunner-startup-transport.ts:418(postCommandViaSimulator:simctl spawn <udid> /usr/bin/curl …, a shell-out inside the Simulator, still reachable from lines 119 and 400). - Six notions of "alive".
isRunnerProcessAlive/isRunnerProcessTreeAlive/runnerSessionsStillAliveinrunner-disposal.ts:274/269/177;hasLiveIosRunnerSessioninrunner-client.ts:192readinggetRunnerSessionSnapshotinrunner-session.ts:442;probeRunnerAnswersUptimeinrunner-adoption.ts:127(wire-level); andcanSkipRunnerReadinessPreflightAfterHealthyMutationinrunner-command-traits.ts:56, a separate "healthy enough to skip the preflight" axis. - No runner state.
RunnerSession(runner-session-types.ts) has nostatefield, only booleans (ready, computedalive,lastHealthyMutation); the readiness-preflight decision inrunner-session.ts:87–101and the cache decision inrunner-cache.ts:450–462are string-literal unions that stand in for one. - 29 timeout/budget constants across 12 files (
runner-startup-transport.ts:36–40,runner-disposal.ts:26–33,runner-session.ts:78–81,runner-lease.ts:23–25,runner-device-set.ts:18–20,runner-sequence.ts:24–35, and single constants in six more files).
None of this is a bug. It is where the next incident will take longest to diagnose, and it is the code a contributor has to read to touch anything about runner startup.
Task
Three bounded cuts, each its own PR, in this order. Do not attempt a rewrite.
- One response decoder. Make
parseRunnerResponse(runner-session.ts) the only place a runner response body is decoded; have the recoverystatusprobe and the adoptionuptimeprobe call it (or a narrower function it exports) and delete the two private decoders. Tests:runner-command-recoveryandrunner-adoptionsuites keep their current expectations; add one test per probe proving a malformed body is rejected the same way the main path rejects it. - A session state enum. Add
state: 'starting' | 'ready' | 'draining' | 'stopped'(adjust names to what the code actually distinguishes; derive from the existing booleans and the readiness-preflight reasons, do not invent states) toRunnerSession, with one transition function. Replace the six "alive" checks with two: process liveness (OS fact,host.ts) and session state.hasLiveIosRunnerSessionbecomes a state read. The readiness-preflight decision keeps its reason codes (they are diagnostics), but the decision readsstatepluslastHealthyMutation. AnySessionState-like field added to a persisted record follows the R7/R10 rule inCONTEXT.md(owner entry plus schema bump). - Retire
postCommandViaSimulatorif it is dead. Establish first whether the curl-through-simctl path ever answers where the host TCP and usbmux paths do not, after usbmux became primary (#1403). Instrument with a diagnostic (ios_runner_startup_transportphase, which transport answered) and read it over the iOS simulator lanes and the nightly (.github/workflows/xctest-nightly.yml) for a week, or find the commit that introduced it and the failure it worked around. If no run needs it, delete it together with its encode path; if one does, document the condition next to the function and close this sub-task.
Acceptance criteria
- After (1):
grep -rn 'JSON.parse' packages/platform-apple/src/runner/*.ts(production files) shows exactly one site;pnpm check:affected --rungreen. - After (2):
RunnerSessionhas astatefield;grep -rn 'alive' packages/platform-apple/src/runner/*.tsshows only the process-liveness primitive and reads ofstate; the daemondevice statusoutput for a running iOS session is unchanged (compare JSON againstmain); the iOS simulator integration lane and the macOS host lane green; one recorded startup, one idle-stop and one recycle each transition through the enum, asserted inrunner-sessiontests. - After (3): either the function and its encode path are gone and the startup transport tests are updated, or a comment above it names the concrete condition it serves with the run that proved it.
- No timeout value changes in any of the three PRs (the constants are a smell, not a target; changing them changes startup timing, which #2324/#2325 calibrated).
- The layering check (
pnpm check:layering) andpnpm check:production-exportsstay green; no new exports frompackages/platform-apple/src/runner/index.ts.
Non-goals
Merging or renaming files for their own sake. Changing lease, cache or xctestrun-artifact logic (runner-lease.ts, runner-cache*.ts, runner-artifact*.ts): those are a different cluster with their own invariants (#2598). Reducing the number of timeout constants.
Related: #1403 (usbmux primary), #2324 / #2325 (startup budget), #2598 (process lock), ADR 0005 (runner interaction lifecycle), ADR 0019 (request-bound platform runtime).
- Dominant language
- TypeScript
- Stars
- 4.8k
- Forks
- 315
- Avg merge
- 11h 17m
- Merged PRs (30d)
- 536
Getting set up
- No Dockerfile or Docker Compose file
- No pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from callstack/agent-device
-
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
callstack/agent-device#1869 ·
Maintainers usually reply within 1 day
-
needs-triage refactor
Difficulty 5/5 Over a week Newbie friendliness 25/100
callstack/agent-device#3116 · 4 comments ·
Maintainers usually reply within 1 day
-
Difficulty 5/5 Over a week Newbie friendliness 20/100
callstack/agent-device#3106 ·
Maintainers usually reply within 1 day
-
Difficulty 3/5 1-2 days Newbie friendliness 68/100
callstack/agent-device#3105 ·
Maintainers usually reply within 1 day
-
Difficulty 3/5 1-2 days Newbie friendliness 72/100
callstack/agent-device#3104 ·
Maintainers usually reply within 1 day
All issues in callstack/agent-device
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
Maintainers usually reply within 1 day
-
external-issue to-triage
Difficulty 1/5 Under an hour Newbie friendliness 90/100
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 85/100
diegosouzapw/OmniRoute#15401 ·
Maintainers usually reply within 2 days
-
Difficulty 2/5 1-3 hours Newbie friendliness 86/100
code-yeongyu/oh-my-openagent#9454 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
smart-village-solutions/sva-studio#1654 ·
Maintainers usually reply within 1 day