Session 8 — Five Research Questions¶
Date: 2026-04-12
Triggered by: Stage 0 deploy revealing behavior change in devicectl list
devices (timeout → "No devices found.") plus a new 127.0.0.1:62078
fetch failure visible in CDS log.
This document captures the answers to five parallel research questions
that ran in session 8. The findings invalidated multiple existing
assumptions and produced plan-stage1-rebuild.md.
Q1 — Who issues the HTTP GET to 127.0.0.1:62078?¶
Answer: Our own inject.
inject/iosmux_xpc_proxy.m:557-602—iosmux_build_device_properties()callsNSData dataWithContentsOfURL:"http://127.0.0.1:62078/device-info"to assemble the XPC dict installed intoOS_remote_device._properties.inject/iosmux_md_proxy.m:30-44—fetch_device_properties()does the same thing for the MDRemoteServiceSupport proxy path.cmd/iosmux-relay/main.go:20— Go relay listens on127.0.0.1:62078and serves/device-infofrom RSD peer_info via tunneld.
Port 62078 is the iOS-side lockdown port; on macOS, nothing in Apple's stack listens there. The "Connection refused" we see means the Go relay is not running on the macOS VM at the moment CDS is being injected.
The downstream consequence chain:
fetch fails (ECONNREFUSED)
→ property dict has only LocationID=0
→ installed into OS_remote_device._properties
→ MDRemoteServiceDeviceLoadBaseProperties reads ProductType/UDID/etc
→ all missing
→ "Failed to load remote device properties"
→ "Failed to allocate RSD device" (AMD error -402653181)
→ developer mode resolution gives Unknown
→ wrapper init still returns a pointer but with half-broken state
Better fix than starting the relay: the 46-key RSD Handshake
Properties dict is already parsed and held in process memory as
g_rsd_handshake_properties by the time iosmux_build_device_properties
runs. Build the consumer dict from the in-memory copy. Removes the
relay dependency entirely and fixes a class of startup races.
Q2 — Why did devicectl behavior change without code change to Step 19?¶
Answer: Step 19 still blocks check-in. The change in surface behavior (timeout → "No devices found." in 2s) is devicectl's own client-side handling, not a real success.
Live trace in coredevice-xpc-protocol.md:283-316 shows the actual
message flow:
T+0.004 ProvisioningProvidersListRequest → "Ignoring" (not action)
T+0.026 DeviceManagerCheckInRequest → "Handling..." → "Published...Complete"
Both messages traverse Mercury's XPCMessageDispatcher and both hit
invoke(anyOf:usingContentsOf:) on entry as the ActionDeclaration
prefilter. Our mov al, 1; ret patch makes both look "handled", so
the typed dispatcher never runs and the reply is never built.
In session 8, the system log still shows NO Handling
DeviceManagerCheckInRequest and NO Published Complete. The
"No devices found" is devicectl giving up on its own (not a 15s XPC
timeout). The hook is unchanged so the symptom is unchanged at the CDS
level — only devicectl's rendering of the failure differs, possibly
because the inject's other Stage 0 changes affected how fast the peer
connection invalidates.
Decisive grep: presence of Handling DeviceManagerCheckInRequest
in CDS log = check-in past prefilter; absence = still blocked.
Currently absent.
Q3 — Step 19 (CDS+0xB896): why was it added, do we still need it?¶
Answer: Added in commit 6501219 to mask a SIGSEGV in PairAction's
NULL self handler. The real root cause of that SIGSEGV was the SDR+104
read overflow that we have since fixed in commit 59ca5cd (Stage 2
phase, before session 8). Step 19 is now masking a bug that no longer
exists, while blocking check-in.
Variant analysis:
- Current (mov al, 1; ret): Always claims handled. Blocks all
check-in. Bad.
- Passthrough (movabs r10, orig; jmp r10) — preferred:
Functionally equivalent to no hook. Restores natural Mercury flow.
Acts as a canary: if the original SIGSEGV returns, we learn the SDR
fix wasn't sufficient.
- Always false (xor al, al; ret): Worse than passthrough. Real
ActionDeclaration messages would also fall through to typed handlers
that don't recognize them.
- Conditional on envelope shape: Requires Mercury wire format
capture. Not viable in Stage 1.
Mercury dispatcher's "invoke on every message" claim is itself hypothesis (from a log string + a live trace), not from disasm. Worth verifying directly if S1.B doesn't restore check-in as predicted.
Q4 — developerModeStatus = Unknown: does it matter?¶
Answer: Probably not for the current target functionality.
Type definition in coredevice-representation.md:398:
enum DeveloperModeStatus {
case enabled(UInt64) // payload: timestamp/token
case disabled
case unsupported
case unknown(CoreDeviceError)
}
No runtime gate on Enabled was found in the research:
- Devices window does NOT gate on it (device already shows with Unknown)
- Pair button gates on pairingState, not developerMode
- acquireusageassertion has no documented dependency on it
- App install / debug gates on DDI mount, which is a separate Action
The developerModeEnabledIfRequired bit in our preparednessState=0xF
should already satisfy preparedness gates.
If we later need to set it explicitly, the setter symbol is:
$s19CoreDeviceProtocols0B4InfoV20developerModeStatusAA0bcD0OSgvs
(indirect via RDI, payload buffer construction needed for the
enabled(UInt64) case).
Deferred to a later stage. Not on the Stage 1 critical path.
Q5 — Why does devicectl list devices return empty despite SDR hook?¶
Answer: Our hook is on the wrong code path. devicectl list devices
does not call serviceDeviceRepresentations(forDeviceIdentifiedBy:).
The actual flow:
client → CDS: DeviceManagerCheckInRequest { identifier }
CDS → client: DeviceManagerCheckInCompleteEvent {
checkInRequestIdentifier,
initialDeviceSnapshots: [DeviceStateSnapshot],
serviceFullyInitialized
}
The device list comes from initialDeviceSnapshots. That array is
populated by ClientManager.publish(event:) which reads internal CDS
state derived from managedServiceDevices. Our handleDiscoveredSDR
publishes events but does NOT add anything to managedServiceDevices
(see iosmux_inject.m:982 comment).
serviceDeviceRepresentations(forDeviceIdentifiedBy:) is the
PairAction UUID-indexed lookup, completely separate from list-devices.
Our hook works for actions, but actions never get to run because:
(a) check-in never completes (Step 19 blocking, see Q2/Q3), and
(b) even if check-in completed, the device wouldn't be in the snapshot
list to be selectable by the user.
Right fix: make the device actually present in
managedServiceDevices, so the natural DeviceStateSnapshot
publication picks it up. We already call updateIdentifier(devId, sdr,
sdm) at iosmux_inject.m:1011 and the inject log confirms it ran —
but it apparently doesn't put the SDR into the managed set. Stage S1.C
investigates why and fixes the registration path properly.
Possible interception points (in increasing order of "shortcut"):
1. Drive the canonical browser-callback registration path
2. Call the right Swift function with the right ABI
3. Hook ClientManager.publish(event:) to inject snapshots
We choose (1) or (2) as the correct/grounded approach. (3) is a last resort.
Cross-cutting takeaway¶
Each of the three big findings (Q1, Q3, Q5) is a layer built on top of
a wrong assumption. Removing each layer produces a smaller, more
correct system rather than a larger, more complex one. This is the
unwinding work that plan-stage1-rebuild.md schedules.