Skip to content

Read Developer Mode and DDI state from the device instead of inferring it from tool output #2683

Description

@thymikee

Why

Two device-readiness facts are guessed at from other people's output, and one failure classifier reads a file it cannot scope.

Developer Mode. verifyDeveloperModeForIosRunner (packages/platform-apple/src/runner/runner-session.ts:724-736) shells out to host DevToolsSecurity -status and throws COMMAND_FAILED with no reason. That is the macOS developer-tools security setting — not the device's Settings → Privacy & Security → Developer Mode toggle. What we do have is prose: resolveIosDevicectlHint (core/devicectl.ts:299-318, tested in core/__tests__/devicectl.test.ts:73-93) maps developer disk image / developer mode is disabled to the Settings hint, but only on devicectl paths, with no reason code, and it treats a disk-image failure as a symptom of Developer Mode being off (:308-312). Before splitting those into two reasons, prove they are distinguishable at all.

Evidence that can be stale. enrichRunnerFailureFromLog reads a 64 KB tail of the append-only session runner.log (runner/runner-failure-diagnostics.ts:5,46-53; append at runner-io.ts:28) while classifying a runtime failure (runner/runner-session.ts:996). A crash written by an earlier command can therefore be attributed to a later, unrelated failure — which is exactly the shape an external consumer built its own defense against, and its defense is only as good as our log format, which we do not version.

Task

  1. Read the device, not the tool output. resolveTunnel already runs devicectl device info details --json-output and parses it (core/physical-device-coredevice.ts:160-195, parseIosDeviceDetailsPayload :205+), but reads only tunnel fields. First step is a captured payload: confirm whether it carries a developer-mode status and a DDI availability field. If it does not, stop and record that here — do not invent a probe or keep inferring from another tool's stderr.
  2. Publish the fact once, keyed on a typed reason, shared by the runner startup path and the devicectl hint path instead of each keeping its own copy. runner-session.ts is 1,181 lines, so the probe does not land there (AGENTS.md: split before adding behavior).
  3. Bind log evidence to the attempt. Capture the log offset before the command runs and classify only bytes written since. A reason derived from runner.log must be unable to cite a previous command's crash; the existing AXRuntime-crash and target-crash classifications inherit this.
  4. Where a fact cannot be established, say so through the existing unavailability shape ({ available: false, reason, hint }, e.g. runtime.ts:103-107) rather than falling back to inference.

Acceptance criteria

  • A committed payload capture answers whether developer mode and DDI state are readable from devicectl device info details; the issue or the code records the answer, and the two reasons exist only if they are distinguishable.
  • Device evidence with commit SHA for Developer Mode actually off, when task 1 finds a readable field. If it does not, this issue stops at the recorded negative result and names which failure path the classifier keeps inferring from — a green build of the rest does not count as shipping the fact.
  • A test proves bytes written before the current attempt cannot produce a runner-failure reason, and the fix lands where the defect is (the runtime tail read), not in the build classifier.
  • verifyDeveloperModeForIosRunner's host check gets its own distinct reason and is not silently relabeled as a device-side check. It has no reason today, so this depends on the sibling issue creating the reason vocabulary.
  • A rendered-output fixture shows the new reason reaching the CLI JSON for both shapes, so a consumer never has to read runner.log to tell them apart.
  • Versioned help and user docs updated; pnpm check:affected --run green.

Non-goals

Mounting or unmounting a developer disk image, pairing, Device Manager orchestration, and any recovery that acts on the device. Classification only. The build/signing reasons are the sibling issue.

Related: ADR 0010, ADR 0005, #2662. Blocked by: #2680, which owns the reason vocabulary this issue adds two members to.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions