Skip to content

refactor: add iOS runner lifecycle protocol#658

Merged
thymikee merged 6 commits into
mainfrom
codex/ios-runner-lifecycle-protocol
Jun 2, 2026
Merged

refactor: add iOS runner lifecycle protocol#658
thymikee merged 6 commits into
mainfrom
codex/ios-runner-lifecycle-protocol

Conversation

@thymikee
Copy link
Copy Markdown
Member

@thymikee thymikee commented Jun 2, 2026

Summary

Refactors the iOS runner command path to carry lifecycle ids through the existing protocol without changing daemon retry or preflight policy. Lifecycle-tracked runner commands now get a commandId, the Swift runner records accepted/started/completed/failed state in an in-memory journal, and a new read-only status command can query that lifecycle state by statusCommandId.

Closes #656

flowchart LR
  D["Daemon"] -->|"runner command + commandId"| R["iOS runner"]
  R -->|"accepted"| J["Lifecycle journal"]
  R -->|"started"| J
  R -->|"completed or failed"| J
  D -->|"status with statusCommandId"| R
  R -->|"state plus cached response or error"| D
Loading

Helpful cases versus the previous behavior:

  • Completed command, response lost: daemon can ask status and see completed with cached response JSON when the response is small enough to retain, instead of treating the result as an ambiguous timeout.
  • Structured runner failure, response lost: daemon can recover failed with error code/message/hint instead of collapsing everything into transport failure.
  • Accepted/started mutation, runner disconnected: daemon can distinguish accepted/started from unknown once follow-up transport/retry wiring can query status before resend.

This is intentionally a pure refactor for now: eager uptime, existing retries, and all command behavior remain in place so performance policy can be changed separately after the protocol is merged.

Review hardening included in this PR:

  • status probes are not journaled and do not get their own generated commandId, so polling cannot evict the command being queried and carries only statusCommandId.
  • status reads the locked journal directly instead of dispatching through the XCTest main-thread command path.
  • Journal entries retain only lifecycle metadata plus capped response JSON, not the full Response object; response JSON is omitted for snapshots and when encoded JSON exceeds 16 KiB.
  • Blank/whitespace TypeScript commandId values are normalized by assigning a generated runner id; caller-supplied ids are preserved.
  • Dead journal timestamp metadata was removed; pruning uses the journal order list.

Known follow-ups:

  • The daemon still does not query status before retrying after transport failure. The later reliability/performance change should add status-before-resend ordering and keep the original commandId in the retry-owning scope.
  • The runner currently handles connections on a serial queue, so accepted/started states are recorded but not practically observable during a long-running command until follow-up transport work lets status escape command serialization.

Touched-file scope: focused on the iOS runner protocol/client/session path and protocol fixtures/tests.

Validation

Passed:

  • pnpm format
  • pnpm typecheck
  • pnpm exec vitest run src/platforms/ios/__tests__/runner-client.test.ts
  • pnpm exec vitest run src/platforms/ios/__tests__/runner-client.test.ts src/platforms/ios/__tests__/runner-session.test.ts
  • pnpm exec vitest run src/platforms/ios/__tests__/runner-session.test.ts src/platforms/ios/__tests__/runner-client.test.ts src/platforms/ios/__tests__/runner-command-retry.test.ts src/platforms/ios/__tests__/runner-provider.test.ts
  • pnpm exec vitest run test/integration/provider-scenarios/ios-lifecycle.test.ts test/integration/provider-scenarios/ios-alert-settings.test.ts test/integration/provider-scenarios/ios-record-trace.test.ts test/integration/provider-scenarios/macos-recording.test.ts test/integration/provider-scenarios/tvos-remote.test.ts test/integration/provider-scenarios/transcript.test.ts
  • pnpm build:xcuitest

Known gap: broad pnpm check:unit was attempted but failed in unrelated environment/tooling suites, including local server listen EPERM, mocked tool timeouts, platform helper failures, and OCR/script typecheck failures.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 2, 2026

Size Report

Metric Base Current Diff
JS raw 1.1 MB 1.1 MB +224 B
JS gzip 360.7 kB 360.8 kB +77 B
npm tarball 462.0 kB 463.4 kB +1.3 kB
npm unpacked 1.5 MB 1.5 MB +6.2 kB

Startup median (7 runs, lower is better):

Scenario Base Current Diff
CLI --version 26.6 ms 26.7 ms +0.1 ms
CLI --help 42.2 ms 42.3 ms +0.1 ms

Top changed chunks:

Chunk Raw diff Gzip diff
dist/src/2415.js +239 B +83 B
dist/src/apps.js -15 B -6 B

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: eddeb43127

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

}

func execute(command: Command) throws -> Response {
commandJournal.accept(command: command)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Don't let status probes evict the command they query

Because every request is journaled before dispatch, status probes are stored and pruned like normal commands. When the journal is already at its 64-entry limit, a status request appends its own generated commandId and pruneIfNeeded() removes the oldest entry before commandJournal.status(statusCommandId) runs, so querying that oldest retained command returns notAccepted solely because of the probe. Avoid journaling status commands or defer pruning until after the lookup so lifecycle recovery does not consume its own history slot.

Useful? React with 👍 / 👎.

@thymikee thymikee merged commit 3f65124 into main Jun 2, 2026
18 checks passed
@thymikee thymikee deleted the codex/ios-runner-lifecycle-protocol branch June 2, 2026 01:40
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 2, 2026

PR Preview Action v1.8.1
Preview removed because the pull request was closed.
2026-06-02 01:40 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.

Explore TCP-like command lifecycle protocol for iOS runner

1 participant