fix(ios): press Open again when the runner watchdog abandons the launch-URL accept

TH
2h ago
+675-19

Summary

A freshly started runner can fail the "Open in" accept with MAIN_THREAD_TIMEOUT (typed details.runnerErrorCode) while the prompt stays up. open then failed with launch_confirmation_unanswered. Now it presses Open again, but only when the request's remaining time covers a whole press and its read: 2 × (1 s preflight + 10 s watchdog).

  • The deadline gates only that re-press. The first press and every read keep main's bounds.
  • Deadline: the enforced daemon deadline (batch, replay), otherwise the admitting transport's client-envelope end less the 30 s result margin. Nested attempts inherit their parent's; under an unbounded test nothing is re-pressed.
  • Shared runner code: only the moved preflight constant and the typed timeout predicate.

Follow-ups: #4023 (runner-wide deadline), #4024 (bounded test-attempt deadline).

Fixes #3861

Validation

  • Regression tests written first; each failed on its prior head. A first press with 5 s left is refused on 101a8cef38 and sent now.
  • index-launch-confirmation.test.ts: real alert legs and runner client; the verify read passes two RUNNER_BUSY resends, then the second press lands.
  • Live, iPhone 17e (iOS 27.0), fresh schemes: one plain attempt accepted on the first press. Three attempts paused this simulator's SpringBoard 12 s after the accept: watchdog abandoned, RUNNER_BUSY resends, drain, re-press (remainingMs 36822/36533/33523), second press ok=1, launchConfirmation: "accepted", no alert left. Logs are in the review reply. iOS 17.5 not measured.
  • pnpm check:affected --run --head e3fe452aed: all runnable checks passed (exactCommit).

🤖 Generated with Claude Code