Skip to content

fix: harden apple runner recovery#1126

Merged
thymikee merged 3 commits into
mainfrom
codex-worker/issues1121-1122-runner-20260706
Jul 6, 2026
Merged

fix: harden apple runner recovery#1126
thymikee merged 3 commits into
mainfrom
codex-worker/issues1121-1122-runner-20260706

Conversation

@thymikee

@thymikee thymikee commented Jul 6, 2026

Copy link
Copy Markdown
Member

Summary

Fixes runner recycle accounting so a failed replacement boot does not spend the per-request recycle budget; the slot is committed only after a replacement runner boots successfully.

Hardens abandoned-work handling by checking RUNNER_BUSY/RUNNER_WEDGED before the Swift main-thread fast path, coalescing duplicate command coverage, mapping RUNNER_BUSY to a retriable COMMAND_FAILED, and preserving RUNNER_WEDGED as fatal invalidation. Closes #1121. Refs #1122: the off-main tree snapshot call still needs a larger async/orchestrator redesign because the current snapshot command body already runs synchronously on the main queue.

Adds a narrow text-entry fast path for already-focused bare type when the iOS software keyboard is visible; selector/ref/coordinate fill readiness and the first-character split are unchanged.

Validation

Focused runner Vitest suite passed: runner-command-retry, runner-client, runner-session, runner-recycle-ledger (167 tests).

Ran pnpm format, pnpm check:quick, pnpm build, and pnpm build:xcuitest.

Manual Settings simulator smoke on iPhone 17 Pro: fill @E18 alpenglowxyz completed in 6.28s; already-focused type qwertyuiop completed in 0.91s; session codex-text-entry was closed.

@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown

Size Report

Metric Base Current Diff
JS raw 1.5 MB 1.5 MB +358 B
JS gzip 491.8 kB 491.9 kB +111 B
npm tarball 597.4 kB 598.0 kB +680 B
npm unpacked 2.1 MB 2.1 MB +3.3 kB

Startup median (7 runs, lower is better):

Scenario Base Current Diff
CLI --version 25.6 ms 25.6 ms -0.0 ms
CLI --help 49.7 ms 49.3 ms -0.4 ms

Top changed chunks:

Chunk Raw diff Gzip diff
dist/src/runner-client.js +358 B +111 B

@thymikee

thymikee commented Jul 6, 2026

Copy link
Copy Markdown
Member Author

Coordinator review / CI follow-up:

  • Swift Runner Unit Compile fails on a compile typo in RunnerTests+CommandExecution.swift: response.error?.message?.contains(...) optional-chains through message, but message is a non-optional String once error exists.
  • Fallow Code Quality flags src/platforms/apple/core/runner/runner-session.ts:841 parseRunnerResponse at 19 cyclomatic / 21 cognitive complexity. Please split the error-code/message/details normalization into focused helpers without changing wire behavior.
  • Review finding: executeRunnerCommand appears to commit recycle budget whenever hasRunnerRequestTouchedSession(recycleKey) is true, even if the current runner was alive and tryBeginRunnerRecycle was never called. That can spend the one allowed recycle on ordinary session reuse, then incorrectly fail a later real replacement boot in the same request. Please commit only when a replacement boot was actually begun and add coverage for “second command reuses alive runner without consuming recycle budget.”

I revived the runner worker to fix these on this branch.

@thymikee

thymikee commented Jul 6, 2026

Copy link
Copy Markdown
Member Author

CI follow-up pushed in 6f831af.

Root causes:

  • Swift Runner Unit Compile failed because ErrorPayload.message is non-optional after response.error? unwrap, so the test used invalid optional chaining on message.
  • Fallow flagged parseRunnerResponse after RUNNER_BUSY/RUNNER_WEDGED handling pushed parsing and error construction over the complexity threshold.
  • Coordinator review also found executeRunnerCommand could commit recycle budget after ordinary alive-session reuse because commit was keyed only on hasRunnerRequestTouchedSession, not on whether a replacement boot was actually begun.

Changes:

  • Fixed the Swift assertion to call message.contains after the optional error unwrap.
  • Split parseRunnerResponse into small helpers for JSON parsing, runner error-code mapping, failure construction, and data extraction; wire behavior is unchanged.
  • Tracked recycleBootBegun in executeRunnerCommand and only commit recycle budget when tryBeginRunnerRecycle actually ran and ensureRunnerSession succeeded.
  • Added a regression test proving alive session reuse in the same request does not spend recycle budget, while a later dead runner can still perform the one allowed recycle.

Validation:

  • Focused runner Vitest passed: runner-command-retry, runner-client, runner-session, runner-recycle-ledger; 168 tests.
  • pnpm build:xcuitest passed.
  • pnpm check:fallow --base origin/main passed after rerunning with sandbox escalation for pnpm signature verification.
  • pnpm check:quick passed.
  • pnpm format passed after rerunning with sandbox escalation for pnpm signature verification.

Residual risk: no additional #1122 scope was expanded here; the off-main tree snapshot redesign remains the same larger follow-up noted in the PR body.

@thymikee

thymikee commented Jul 6, 2026

Copy link
Copy Markdown
Member Author

Review pass after CI-fix commit 6f831af0f68dbc51ed5fe9467c93a511faf99fa1: no actionable blockers found.

Checked against #1121 and the partial #1122 scope. The recycle ledger now only commits a recycle after a replacement runner boot actually begins and succeeds, so alive session reuse no longer spends the request's one recovery slot. RUNNER_BUSY is mapped to a retriable command failure, RUNNER_WEDGED stays fatal and invalidates the runner session, and the Swift busy/wedge gate now runs before the main-thread fast path. The added tests would fail on the prior implementation paths.

Validation evidence is good for maintainer judgment: CI is green, the worker reran focused runner Vitest (168 tests), pnpm build:xcuitest, pnpm check:fallow --base origin/main, pnpm check:quick, and pnpm format. Residual risk is limited to the larger off-main tree snapshot redesign still tracked by #1122, which this PR intentionally does not complete.

@thymikee thymikee added the ready-for-human Valid work that needs human implementation, judgment, or maintainer merge label Jul 6, 2026
@thymikee

thymikee commented Jul 6, 2026

Copy link
Copy Markdown
Member Author

Addressed the cleanup findings from the latest review in 9fc58302d.

Changes:

  • Removed the now-dead tryConsumeRunnerRecycle export so future callers cannot accidentally reintroduce unconditional recycle spending.
  • Updated runner-recycle-ledger.test.ts to exercise tryBeginRunnerRecycle / commitRunnerRecycle directly, including the failed-boot-does-not-consume-budget case.
  • Removed the redundant runnerErrorCode === 'RUNNER_BUSY' retry check and kept details.retriable === true as the single retry signal.

Validation:

  • pnpm exec vitest run src/platforms/apple/core/__tests__/runner-recycle-ledger.test.ts src/platforms/apple/core/__tests__/runner-client.test.ts src/platforms/apple/core/__tests__/runner-command-retry.test.ts
  • pnpm check:quick
  • pnpm format
  • pnpm check:fallow --base origin/main

No behavior change intended beyond removing the orphaned helper and duplicate predicate branch.

@thymikee thymikee removed the ready-for-human Valid work that needs human implementation, judgment, or maintainer merge label Jul 6, 2026
@thymikee thymikee merged commit ea69dc3 into main Jul 6, 2026
21 checks passed
@thymikee thymikee deleted the codex-worker/issues1121-1122-runner-20260706 branch July 6, 2026 09:40
@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
PR Preview Action v1.8.1
Preview removed because the pull request was closed.
2026-07-06 09:41 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.

fix(runner): consume recycle budget only after a successful runner boot

1 participant