Skip to content

fix(apple-runner): resend a read only on the runner's busy refusal - #2863

Merged
thymikee merged 1 commit into
mainfrom
fix/2862-runner-resend-rule
Sep 24, 2026
Merged

thymikee merged 1 commit into
mainfrom
fix/2862-runner-resend-rule

Conversation

@thymikee

@thymikee thymikee commented Sep 24, 2026 •

Copy link
Copy Markdown
Member

Summary

A read-only runner command is now resent inside one request only when the runner refused it with RUNNER_BUSY. Before, any COMMAND_FAILED with details.retriable: true was resent. That flag is for a caller's poll (wait); #1126 added the check for RUNNER_BUSY only. The rule is renamed flagged_retriable → runner_busy and keys on details.runnerErrorCode. The app_not_running row and its ordering note are deleted.

Producers of retriable: true that reach the classifier:

  • RUNNER_BUSY: still resent.
  • APP_NOT_RUNNING: not resent, as before.
  • runner_phase_budget_exhausted and apple_toolchain_probe_unavailable (thrown in ensureRunnerSession): no longer resent. Each resend opened a fresh startup budget, which defeated the fix(ios): budget cold toolchain probes for the first-exec signature stall #2423 cap. A probe timeout already gets one retry inside that budget.
  • External AppleRunnerProviders: no resend on the flag alone. A provider that forwards the runner's RUNNER_BUSY code is still resent.

All keep retriable, so the caller retries on its next request. CHANGELOG updated. 4 files. Closes #2862.

With #2804: its runner_busy_refusal row matches this runner_busy row. When it rebases, it adds drainResend: true here and drops its duplicate row. The new busy test does not assert the status probe that #2804 removes.

Validation

Tested at 7a24d34091. pnpm check:affected --run passed: 387 files, 2669 tests. The first run had one timeout in the unrelated runner-client.test.ts; it passed on the rerun.

All tests go through runAppleRunnerCommand. Mutations that each turn tests red:

  • Key the rule on retriable: APP_NOT_RUNNING, startup-phase and provider tests fail.
  • Restore main's classifier: the three retriable-producer tests fail.
  • Set retryable: false on runner_busy: the busy-resend and alert-provider tests fail.

No device run: no Swift or wire change.

@github-actions

github-actions Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

Size Report

Metric Base Current Diff
Installed (including dependencies) 4.80 MB 4.80 MB -280 B
Package (unpacked) 4.80 MB 4.80 MB -280 B
Package (download) 1.44 MB 1.44 MB -27 B

Startup median (7 runs, lower is better):

Scenario Base Current Diff
CLI --version 26.7 ms 26.2 ms -0.5 ms
CLI --help 79.1 ms 77.3 ms -1.8 ms

@thymikee

Copy link
Copy Markdown
Member Author

Adversarial review: clean. Applied its one note in 105702ed69: the comment now names the toolchain probe among the retriable producers. Merge-order note: #2804 declares its own hasRunnerBusyCode/runner_busy_refusal row. Whichever of the two merges second must fold them into one runner_busy row carrying retryable: true and drainResend: true; otherwise the duplicate declaration fails to compile.

@thymikee
thymikee force-pushed the fix/2862-runner-resend-rule branch from 791763d to 105702e Compare September 24, 2026 07:39
@thymikee

Copy link
Copy Markdown
Member Author

Reviewed at 105702e. The code looks correct: only a RUNNER_BUSY refusal now earns a read-only resend, and the spent startup budget, unreadable toolchain and external-provider cases are no longer resent. The new tests fail on the old classifier and pass here.

Not blocking: removing the app_not_running row from #2852 also removes its explicit connectRetry: false, and shouldRetryRunnerConnectError falls back to true when no row matches. I found no route where APP_NOT_RUNNING reaches that check today. Would you keep a narrowed row with only connectRetry: false, or add a classification test for that verdict?

Smoke Tests was still queued, so there is no CI result to attribute yet. No device run is needed for a classification-table change. #2804 adds its own runner-busy row in the same file, so whichever PR merges second should fold the two into one rule.

@thymikee thymikee added the ready-for-human Valid work that needs human implementation, judgment, or maintainer merge label Sep 24, 2026
The transport's same-request resend also matched a generic flagged_retriable
row keyed on details.retriable, which tells a caller's poll to try again.
Startup-phase failures (runner_phase_budget_exhausted,
apple_toolchain_probe_unavailable) and external provider failures carry that
flag, so a read-only command resent them up to three times, each with a fresh
startup budget. Drop that row and the app_not_running row that undid it for
APP_NOT_RUNNING: #2804's runner_busy_refusal row, keyed on the RUNNER_BUSY
runner code, is now the only resend rule for a structured runner refusal.

Closes #2862
@thymikee
thymikee force-pushed the fix/2862-runner-resend-rule branch from 105702e to 31eecff Compare September 24, 2026 08:52
@thymikee

Copy link
Copy Markdown
Member Author

Rebased onto main after #2804 merged, and squashed to 31eecfffcf. As predicted, the two busy rules are now one: #2804's runner_busy_refusal row (retryable, connectRetry, drainResend, keyed on the RUNNER_BUSY code) stays. This PR deletes the generic flagged_retriable row and the app_not_running row, and keeps main's isRetryableRunnerError doc. The classification and recovery-wiring tests pass 47/47, and pnpm check:affected --run passed.

@thymikee
thymikee merged commit 5c19230 into main Sep 24, 2026
18 checks passed
@thymikee
thymikee deleted the fix/2862-runner-resend-rule branch September 24, 2026 09:09
@github-actions

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

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.

Runner transport resend keys on the caller-facing retriable flag

1 participant