Skip to content

Q-D66-15 history — D21..D33 (2026-04-30..2026-05-01)

Status: verified — historical archive

Q-D66-15 Resolution log entries for D21 (V2 verdict), D22 (P-1a static disasm), D23 (P-1a deploy partial), D24 (W4 common funnel), D25-D29 (FORGE invalidation + 0/N baseline), D30 (AFM peer keep-alive failed), D31 (peer[1013] race + SUCCESS path observed), D32 (kernel-driven invalidation), D33 (devicectl injection feasibility). Archived from d66-research-questions.md on 2026-05-02 anchor refactor.

Q-D66-15 — RESOLVED, 2026-04-30 (D21): V2 — post-reply peer/capability check via side-channel peer[2019] invalidation

D21 lldb dynamic instrumentation closed the V1/V2/V3 trichotomy from D20 §verdict. Two trigger runs were captured: v1 attached lldb to CoreDeviceService (per the original brief assumption); v2 re-architected the probe after observing zero bp hits in v1 — the AUA async wrapper at CD+0x31750 runs in the client-side devicectl process address space, NOT in CoreDeviceService. This is the load- bearing observation that required the brief's CDS-attach approach to be replaced mid-probe.

V1 (pre-await sanity check) falsified. AUA_ENTRY at CD+0x31750 fires on devicectl Task 1 (queue com.apple.root.default-qos.cooperative) and resolves to symbol CoreDevice.RemoteDevice.acquireDeviceUsageAssertion(withReason:options:) async throws -> CoreDevice.DeviceUsageAssertion. The XPC reply success(...) carrying our inject-synthesised echo arrives at 11:36:13.294488 (v1 unified.log L74-78) and the function proceeds past entry — a pre-await abort would not allow either.

V3 (post-reply timeout adapter) falsified. The post-AUA THROW_RESUME backtrace contains exclusively _dispatch_call_block_and_release / _dispatch_lane_serial_drain / _dispatch_lane_invoke / _dispatch_root_queue_drain_deferred_wlh / _dispatch_workloop_worker_thread frames — zero dispatch_source_* or dispatch_after_* frames that V3 would require. The 11.266 ms gap from XPC-reply to errorCode-3 emission (v1 timing, lldb-untouched inferior) is also too short for any realistic timeout.

V2 (post-reply peer/capability check) confirmed. Three independent empirical citations:

  1. The throw fires from a named, dedicated serial dispatch queue com.apple.dt.coredevice.remotedevice.default.assertionq on devicectl thread #4 (NOT the original Task 1 cooperative thread), with frame chain swift_continuation_throwingResume → CD+0x31b502 (___30eb0+1538?) → CD+0x34cbd (___3bc80+61) → CD+0x316a2 (___30eb0+2034) → CD+0x17319 (___1e300+25) → _dispatch_call_block_and_release. The throw originates from inside ___lldb_unnamed_symbol_30eb0 + 2034 = CD+0x316a2, the SAME containing function whose +857 offset is the brief's failure-resume landmark CD+0x31209 — but ~1177 bytes deeper in a different basic block.
  2. Under v2 lldb-slowed timing, the failure manifests as XPCError(errorCode: 1001, "The connection was invalidated.", ... peer[2019].0x...) from a side-channel peer connection to CDS pid 2019. This is direct evidence of a SECOND XPC connection running parallel to the action-reply channel; when that side-channel closes, the assertionq observes the invalidated peer-state and throws.
  3. Both the AUA-context throw and an earlier non-AUA Mercury throw (ProvisioningProvidersListRequest path on default-qos) carry the identical Swift-error vtable 0x00007ffe42111e70 with errorCode 3 payload — uniform error structure consistent with V2's prediction that the wrapper synthesises a canonical CoreDeviceError(errorCode: 3) from the post-reply check rather than each path constructing a unique error.

Brief landmark refinement (D22 must use these instead of D20-era guesses):

  • CD+0x31750 — AUA wrapper entry — CONFIRMED (still authoritative).
  • CD+0x31a49 — claimed _swift_continuation_await call site — NOT on the executed path; resolves to ___lldb_unnamed_symbol_317a0 + 681 but never fires under primary trigger.
  • CD+0x31209 — claimed failure-resume xref — NOT the actual throw site; resolves to ___lldb_unnamed_symbol_30eb0 + 857 but never fires.
  • CD+0x316a2THE ACTUAL throw site___lldb_unnamed_symbol_30eb0 + 2034. D22's static disasm work begins here.

Apparatus integrity confirmed: 7/7 SHA256 matches across CoreDevice / CoreDeviceService / CoreDeviceUtilities / inject-dylib variants / devicectl, no DiagnosticReports/.ips files, lldb detached cleanly. Pre-attach manifest at /home/op/backups/iosmux/d21-pre-attach/manifest-source.txt (89 entries, 70 unique sha256, 470 files total under cat-a/+cat-b/+cat-c/).

Full evidence: notes/d21-aua-wrapper-probe.md §A-§R (gitignored, in-repo). 18 sections with raw register dumps, stack walks, memory dumps, unified-log timing, disassembly context. Cited verbatim per ADR-0006. Architectural distillation that survives compaction: docs/research/coredevice-internals/aua-side-channel-mechanism.md.

Implication for D22: P-1 (highest confidence) — hold side-channel XPC peer connection to CDS open beyond action reply emit. Either CDS-side hook on Mercury side-channel transport, or devicectl-side mock peer. P-2 (medium) — NOP throw call at CD+0x316a2 or substitute swift_continuation_resume with synthesised DeviceUsageAssertion (risk: Swift type metadata mismatch crash). P-3 (low) — redirect side-channel destination to inject-resident fake-listener; requires side-channel-establishment understanding (currently a §Q open). Six new gaps surfaced for D22+ scope, catalogued in notes/d21-*.md §Q.

Q-D66-15 — D22 (2026-04-30, static disasm): P-1a winning vector + corrections to D21 interpretation

D22 full static disasm of ___lldb_unnamed_symbol_30eb0, ___lldb_unnamed_symbol_1e300, ___lldb_unnamed_symbol_3bc80, ___lldb_unnamed_symbol_324a0 (all four functions D21 saw in the throw chain), plus xref scan of CoreDevice / Mercury / CoreDeviceUtilities binaries for side-channel transport symbols. Full evidence in notes/d22-side-channel-disasm.md §A-§N. Apparatus integrity 7/7 sha256 verified non-destructive.

Critical corrections to D21 interpretation (V2 verdict stands; mechanism localised more precisely):

  1. ___30eb0 + 2034 is NOT a throw site. Full disasm (505 lines) shows ___30eb0 is a plain C-ABI Swift result-completion dispatcher for the callback-based variant acquireDeviceUsageAssertion(...invokingCompletionHandlerOn:completion:) at CD+0x304a0. NO _swift_continuation_* calls, NO xpc_* calls, NO dispatch_async, NO peer-state predicate inside. The +2034 instruction is the return PC after call qword ptr [rbp - 0xa0] at +2028 (the SUCCESS-path completion-callback invocation; ERROR path's analogous call is at +1908).

  2. D21 frame addresses contained transcription errors. lldb symbol naming convention: ___lldb_unnamed_symbol_NNNNN IS the hexadecimal file offset of the function's head. So:

  3. ___3bc80 + 61 = 0x3bc80 + 0x3d = CD+0x3bcbd (D21 said 0x34cbd)
  4. ___324a0 + 98 = 0x324a0 + 0x62 = CD+0x32502 (D21 said 0x31b502)

  5. Vtable 0x00007ffe42111e70 is taskHeapMetadata + 24, not a CoreDeviceError vtable. Resolved via Step 9 lldb attach to CDS pid 2019 — libswift_Concurrency.dylib.__DATA_DIRTY.__data + 1216. This is the Swift Concurrency runtime metadata for task-heap- allocated objects; D21's "uniform CoreDeviceError carrier" interpretation was incorrect.

  6. Real throw mechanism: the success-callback at +2028 invokes Swift code (likely a withCheckedThrowingContinuation adapter bridging the callback API to the async wrapper) which synchronously enters Mercury's XPCSideChannel-installed handler chain. When the cached peer connection has been invalidated, the handler observes XPCError(errorCode: 1001, "The connection was invalidated.") (Mercury's XPCError.connectionInvalid constant at Mercury+0x56e70) and converts it to CoreDeviceError(errorCode: 3) which is then thrown back through the awaiter chain.

Localised V2 mechanism — eviction at ActionConnectionCache: the cached peer-connection that the in-flight assertion holds is evicted by _$s10CoreDevice21ActionConnectionCacheC25removeCachedXPCConnection_02toB12IdentifiedByy7Mercury013RemoteXPCPeerD0C_10Foundation4UUIDVtF at CD+0xdbe0 between (a) the action reply and (b) the post-reply assertion-confirmation step. Hooking this one symbol to no-op prevents premature eviction. The cache key is (deviceUUID, feature), populated by getOrCreateRemoteXPCConnection* at CD+0xf700 (closure variant) and CD+0x108c0 (async variant). The endpoint→peer-connection conversion happens inside Mercury's RemoteXPCConnection.unsafePeer(from:) at Mercury+0x3eca0, not in CoreDevice itself.

Fix vector verdict: P-1a wins. ~30-50 LoC inject code extending inject/iosmux_inject.m (or new inject/iosmux_aua_keepalive.m), using the existing GAMBIT mach_vm_protect inline-patch pattern. Replacement bytes: C3 90 90 90 90 (single ret + nop padding — function returns void). Preserves ADR-0009 bridge mode. 4-6 hours including build, deploy, primary-trigger validation. Memory side- effect: leaks one Mercury peer-connection per AUA call (~MB-scale growth over hours, acceptable).

Fallback within P-1a (if NOP-evict insufficient): wrap _$s7Mercury14XPCSideChannelC15setEventHandler... at Mercury+0x1cae0 to filter XPCError.connectionInvalid (1001) from the user-supplied event handler.

P-2a empirically infeasible: there is no swift_continuation_throwingResume call inside ___30eb0 to NOP. The brief premise was wrong. NOP-ing the success-callback at +2028 would orphan the continuation and hang devicectl forever.

P-1b (devicectl-side mock peer) and P-1c (hybrid) rejected as overkill: 12-26h for new injection mechanism with no demonstrated need beyond P-1a. Reserved as escalations.

P-2b rejected: ADR-0009 §Decision violation (would lie when iPhone genuinely returned error) + high ABI-fragility risk (synthesising DeviceUsageAssertion Obj-C class with _lockedMutableState that crashes on null).

Implication for D23: implement P-1a primary approach. D23 brief authored next.

Q-D66-15 — D23 (2026-04-30, P-1a deploy): PARTIAL success — XPCError 1001 eliminated, residual gate revealed

D23 implemented and deployed P-1a NOP-evict on ActionConnectionCache.removeCachedXPCConnection at CD+0xdbe0. New TU inject/iosmux_aua_keepalive.m (113 LoC) + inject/Makefile update committed in f591aa1 and pushed.

Empirically validated on havoc:

  • Build sha256: 52df2cc643dbc6c24d3debd6eba8682ded9fd8cf5d1f9ee3c57962e0171b4803 (193664 B; was 188944 B on Phase 1 baseline)
  • Runtime hook log: [inject] AUA keepalive: removeCachedXPCConnection NOP installed at 0x102ebcbe0 rc=0
  • XPCError 1001 ELIMINATED from action-XPC connection eviction path (verified by grep -E "1001|connectionInvalid" /tmp/iosmux-d23-unified.log → 0 matches at this site)
  • Trigger improvements: exit code 0 (was non-zero); xcrun devicectl device info details now returns FULL device info (productType, hardwareModel, marketingName, hostnames, capabilities, tunnelState=connected)
  • Apparatus integrity: 4/4 system binaries (CoreDevice, CoreDeviceService, CoreDeviceUtilities, devicectl) sha256 UNCHANGED. iPhone connected. Zero CDS crashes.
  • dlsym duplicate-export discovery: CoreDevice export trie has TWO entries with mangled name removeCachedXPCConnection (at 0xdbe0 AND 0xe088). dlsym fails on ambiguity. v1 build (dlsym-only) returned rc=-1; v2 build (image-walk + hardcoded D22 0xdbe0 offset) returned rc=0. ADR-0006 satisfied — offset is empirically grounded in D22 §J.

Residual failure: Error: Failed to acquire assertion still in stdout AND CoreDeviceError(errorCode: 3) still in unified log, but on a SEPARATE downstream path that fires AFTER the action success() reply and analytics emit.

D23 agent halted on Stop Condition #6 (different call path than D22's V2-path). The brief's Step 12 fallback (filter XPCError.connectionInvalid (1001) via Mercury XPCSideChannel.setEventHandler wrap) was specifically designed against the cache-eviction-driven 1001; post-D23, 1001 from that path is gone. Filter would have nothing to filter — agent properly surfaced rather than executing theatre.

Implication for D24: dynamic re-probe to localise the new gate's instantiation site. Apparatus already in optimal post-D23 state.

Q-D66-15 — D24 (2026-04-30, post-D23 dynamic re-probe): W4 verdict — common funnel with side-channel peer as second input

D24 lldb dynamic re-probe via launch-devicectl-under-lldb (D21 methodology) with comprehensive bp set (17 base + regex matches). Apparatus identical to D23 deployed state at probe entry; identical at probe exit (verified). Notes deliverable notes/d24-residual-gate-probe.md §A-§P. Pre-registered hypotheses W1/W2/W3 all falsified; actual mechanism named W4 per ADR-0006 (no plausible-fit forcing).

W4 — assertionq dispatcher is the common funnel; side-channel peer is the second input:

  1. D23 NOP-evict still effective — bp4 (the D23-NOP'd removeCachedXPCConnection) NEVER fired during the trigger run.
  2. XPCError 1001 IS BACK in unified log line 47 (t=14:34:57.171 in capture) — but on a SECOND XPC connection: peer[2019] of a "side channel peer" connection that is distinct from both the action-XPC connection AND from ActionConnectionCache. Verbatim: [com.apple.dt.coredevice:useassertion] Recieved error from side channel peer: XPCError(errorCode: 1001, "The connection was invalidated.")
  3. Mercury's XPCSideChannel-installed event handler delivers this error to CD's [useassertion] subsystem. The subsystem posts a block to com.apple.dt.coredevice.remotedevice.default.assertionq (the SAME queue D21 saw).
  4. The assertionq dispatcher runs ___30eb0 AGAIN, but at a DIFFERENT internal call site:
    • D21 (cache-eviction input): ___30eb0+2034___324a0+98swift_continuation_throwingResume
    • D24 (side-channel input): ___30eb0+1910___324a0+56swift_continuation_throwingResumeWithError
    • Same wrapper functions (frames 2 + 4 + 5+ identical), DIFFERENT internal RetPC offsets — proves the dispatcher chain is a SHARED FUNNEL with multiple input paths.
  5. errorCode 3 user-visible at unified.log line 50 (t=14:34:57.285): Failed to acquire usage assertion ... CoreDeviceError(errorCode: 3, "Failed to acquire assertion"). The literal string Failed to acquire assertion lives in CoreDevice __cstring at file offset 0x369860 (notes/d24 §J.1).

W1/W2/W3 falsifications:

  • W1 (post-success validation on reply payload) — no validation logic; action XPC reply decodes as success() with no error key (notes/d24 §I.2). Construction happens AFTER reply-decode path completes.
  • W2 (separate cache or registry) — regex bps for UsageAssertionCache, checkAssertion, validateAssertion, registerAssertion matched ZERO symbols in CoreDevice. No second cache.
  • W3 (analytics-side gate) — analytics emit at unified.log lines 44-45 fires BETWEEN side-channel error (.171) and user-visible error (.285) — passive observer, not a gate.

D25 fix vector ranking (notes/d24 §N):

  • Q-1a (PICKED): hook the funnel at CD+0x31621 (= ___30eb0+1905, the call instruction whose return PC is the captured frame-3 RetPC CD+0x31626). NOP/wrap pattern, mirrors D23 P-1a. The dispatcher receives errors from BOTH cache-eviction AND side-channel inputs (D21+D24 evidence both routes through this dispatcher). Hooking the funnel addresses both inputs in one patch.
  • Q-1b (Mercury XPCSideChannel handler hook at Mercury+0x1cae0) — backup. Mercury binary not on disk (lives in dyld shared cache); broader scope (would affect non-AUA side-channel users).
  • Q-1c ([useassertion] subscriber hook via os_log string xref at CD file offset 0x371320) — backup. Caller offset not yet derived; D26+ static disasm could resolve.
  • Q-2 (CDS-side suppression) — last-resort. Requires CDS-side lldb attach + invasive lifecycle modification.

Implication for D25: Q-1a primary approach. Single hook expected to address both cache-eviction AND side-channel inputs to the assertionq dispatcher. If Q-1a deploys cleanly, D23 P-1a NOP-evict on removeCachedXPCConnection becomes redundant (funnel hook covers it) — revert/retain decision deferred to post-D25 acceptance test.

D24 also surfaced 7 D26+ open questions (notes/d24 §O): exact branch instruction at CD+0x324d3; errorCode 3 literal-load instruction site; string-xref caller for "Recieved error from side channel peer"; Mercury binary path on this build; CDS-side trace for side-channel teardown source; whether D23 NOP-evict becomes redundant if Q-1a deploys; how CD's [useassertion] subscribes to Mercury's XPCSideChannel events.

Q-D66-15 — D25+D26+D27+D28+D29 (2026-04-30, full chain): FORGE plan invalidated; 0/N AUA SUCCESS confirmed as natural baseline; cosmetic resolution accepted

Status: COSMETIC GAP ACCEPTED. FORGE DEFERRED INDEFINITELY pending daemon-side disasm.

Chain summary (research+writer cycle following D24's Q-1a recommendation):

  • D25 (research, notes/d25-funnel-hook-research.md) — picked FORGE strategy J-c: hook AUA Swift async wrapper ___317a0 head (file offset 0x317a0), fabricate CoreDevice.DeviceUsageAssertion instance via type-metadata accessor at 0xd3570, drive _swift_continuation_resume directly. Justified by §I empirical finding that awaiter has no fallback resume (___31750+___317a0 disasm shows only _swift_task_switch / _swift_task_alloc / _swift_continuation_init / _swift_continuation_resume — no dispatch_after, Task.sleep, withTaskCancellationHandler patterns), so suppressing the funnel error without driving completion would orphan the continuation. NOP/wrap strategies A/B/C all UNVIABLE without forging a DUA.

  • D26 (writer, halted at Step 3a OFFSET_UNRESOLVED) — c-developer agent attempted to derive DUA field offsets via empirical disasm before writing the C forge handler, per ADR-0006 + feedback_correct_solutions_only. Halted on Stop Conditions 3 + 4 (notes notes/d26-aua-forge-writer.md §B). Disasm of ___324a0+0x40..+0x97 and ___317a0+0x7b..+0xf5 (the two reference sites D25 §K specified) found NO swift_allocObject calls allocating a DUA; ___324a0 is an Error-rethrow / Result-coercion helper (its leaq 0x3b17ce(%rip) targets are mangled-name strings for __swift_instantiateConcreteTypeFromMangledNameV2 LOOKUP, not DUA field stores); ___3bc80 is also Error-coercion wrapper that tail-calls ___324a0. The actual DUA construction site is INSIDE ___304a0 legacy completion-handler callback OR XPC decoder for AUA reply payload — never mapped in D21-D27. Additionally, the brief's assumed _swift_continuation_resume(continuation, value, metadata) call signature was wrong: ___317a0's tail-jmp at +0xf5 sets %rsi=0, %rdx=0, %rcx=0xD000000000000030, %r8=tagged-string-ptr, %r9=async-helper — Swift 5.7+ async-frame call convention, not the simple (cont,val,meta) value-resume D25 assumed. D25's structural premise that ___324a0/___3bc80 are DUA constructors was empirically FALSIFIED.

  • D27 (research, notes/d27-gambit-completeness-research.md) — disambiguated discriminator 0x347e00 at ___317a0+0x72: it's a 6-byte chained-fixup stub for _$ss042_stdlib_isOSVersionAtLeastOrVariantVersiondE0yBi1_Bw_BwBwBwBwBwtF (libswiftCore — Swift's if #available(macOS X.Y.Z, *) runtime helper). Reads only compile-time-baked args (rdi=0xf, rcx=0x12, rest=0) + running macOS version. The same stub is invoked from 150+ call sites across CoreDevice (every if #available check). NOT inject-flippable. Verdict (b) — FORGE plan unchanged from D25, but with bonus FORGE-anchor refinements: legacy completion-handler entry at 0x304a0 (symmetric to ___31750); DUA type-metadata accessor at 0xd3570. Both branches of the if #available check route through the same Mercury+side-channel transport — there is no second transport for AUA.

  • D28 (research, notes/d28-true-branch-liveness.md) — runtime liveness probe: ___317a0 family (6 BPs at ___31750, ___317a0, +0x77, +0xf5, +0x599, ___304a0) NEVER fires across 5 launch-under-lldb invocations of devicectl. All BPs num_loc=1, resolved=1, hits=0; verify-byte at +0x77 confirms a8 01 74 7f (test al,1; je rel8 +0x7f). Run-1 logs show devicectl XPC-forwards the AUA action: [action] Forwarding action ... to ExecutionLocation.elsewhere(<SystemXPCPeerConnection ... CoreDeviceService, pid=4454 ...>). AUA work runs server-side in CoreDeviceService daemon, not client-side in devicectl. This contradicts D21 v2's interpretation but is reconcilable: D21 ran PRE-D23 keepalive when cache-eviction error path triggered client-side Error coercion that made ___31750 fire; post-D23 that path is short-circuited and ___317a0 family doesn't run client-side at all. D21's evidence was correct for its (pre-D23) apparatus state but became obsolete after D23 deployed.

  • D29 (research, notes/d29-aua-failure-pattern.md) — N=20 invocations to characterize D28's ⅘ SUCCESS rate (D28's intermittent observation). 0/20 SUCCESS, 100% deterministic FAILURE with Wilson 95% CI [83.9%, 100%]. All 20 produced bare success() reply (Codable Void Output, by GAMBIT design — gambit_build_void_box() for AUA per source line 1133) followed by Failed to acquire usage assertion ~10 ms later. Side-channel sc1001 fired in 4/20 only — does NOT correlate with outcome (all 20 are FAILURE regardless). CDS daemon pid stable (4454, no respawn between D28 and D29; etime monotonic; RSS drift 60 KB over 123 sec — no pressure event). Failure is the natural state, not intermittent.

  • Post-D29 verification cycles (read-only follow-up, 2026-04-30 18:00..19:50):

  • Source review of inject/iosmux_gambit.m confirmed GAMBIT design: AUA Output is gambit_build_void_box() (empty 0-key dict) by design (source line 467-470 + 1133); AFM payload is mangledTypeName: "CoreDevice.AssertionFulfilledMessage" + value: {identifier: <action's deviceIdentifier as xpc_uuid>, updatedSnapshot: <DSS connected>} via gambit_emit_afm_via_endpoint (line 926); send-barrier deferred cancel (line 1021-1023) ensures AFM lands before connection cancel. Nothing in source produces a populated success Output for AUA — D28's "⅘ success" cannot have come from GAMBIT producing different bytes.
  • Kickstart test (launchctl kickstart -k user/501/com.apple.CoreDevice.CoreDeviceService, daemon respawned 4454 → 13385 fresh process address space): N=5 cycle still 0/5 SUCCESS. Process-state-corruption hypothesis REJECTED.
  • VM-reboot test (full havoc VM reboot + apparatus rearm): N=10 cycle still 0/10 SUCCESS. Fresh kernel, fresh tunneld, fresh CDS, fresh inject load, manually-rearmed apparatus by dispatcher → same 100% failure rate. State-degradation hypothesis REJECTED.

Cumulative empirical baseline (post-VM-reboot): 0/10 SUCCESS at fresh apparatus state. D28's claimed ⅘ SUCCESS was an anomaly — most likely misclassification artifact (unified-log lookback window --last 30s or --last 1m picked up Successfully acquired usage assertion log lines from a prior session that pre-dated D28's run window, and the agent's classification logic counted them as current-run signal). Without baseline-line-count diff (which D29 introduced as inject-log-lines-before.txt), D28 had no way to tell apart pre-existing log entries from current-run output.

Architectural conclusion: Q-D66-15's main-path resolution stands per aua-side-channel-mechanism.md — D23 keepalive eliminates XPCError 1001 on the action-XPC connection's cache-eviction path (D24 W4 finding). The residual Failed to acquire assertion user-visible message remains COSMETIC per pair-button-and-cfnetwork.md §"Pair button gating" — does NOT block install / debug / app-launch pipelines that flow through the existing remote_service_create_connected_socket interpose chain (per ADR-0009 §Consequences). FORGE plan deferred indefinitely because the actual DUA construction site requires daemon-side disasm (not client-side, per D28) AND a daemon-side hook would need careful interaction with GAMBIT's existing reply synthesis (which already short-circuits the action-dispatch layer before the DUA-construction code path would run server-side).

Next-action recommendation: move to Phase D.6.6-impl Phase ⅔ work items per stage2.md. Q-D66-15 cosmetic gap accepted as documented project state. Re-open only if downstream Xcode IDE pipelines (install / debug attach / app-launch) empirically fail because of the cosmetic AUA failure — current evidence per ADR-0009 §Consequences predicts they do not.

Q-D66-15 — D30 (2026-04-30 evening, AFM peer keep-alive deploy): FAILURE — peer lifetime is not the gate; cosmetic-accept REVERSED 2026-05-01

Status: REOPENED. Empirical failure. D31 research probe pending.

User directive (2026-05-01): restore full UI functionality. The cosmetic-accept decision recorded in the prior D25..D29 Resolution log is REVERSED. Architectural goal is full UI functionality (Connected status, no error, real iOS version, real Capacity in Xcode Devices window).

D30 hypothesis (the test the cosmetic-accept never ran): explicit xpc_connection_cancel + xpc_release of the AFM peer connection in gambit_emit_afm_via_endpoint (the cancel-after-send pattern from commit 28d7d89) is what drives Apple's AUA Swift wrapper at CD+0x31750 to resolve _shadowUseAssertion = .invalidated(error)continuation.failure(CoreDeviceError(errorCode: 3)) thrown. Per aua-side-channel-mechanism.md admonition "Why GAMBIT's AFM endpoint emit alone does not satisfy V2".

Fix candidate (deployed): 3-edit patch on inject/iosmux_gambit.m. Globals g_afm_retained_peers (NSMutableArray) + g_afm_retained_lock (NSLock) at top of TU. Replace cancel-after-send block in gambit_emit_afm_via_endpoint with [g_afm_retained_peers addObject:(__bridge id)ep_conn] under lock — peer kept alive for the entire CDS process lifetime. Init lock + array at top of gambit_install_hook. Build sha 6c9b07bad7ac383c8a66d4660a1e04e41411608ff65286cce0eb808afde89f49, +16 bytes vs D23 v2 baseline. Backup of D23 v2 at /home/op/backups/iosmux/d30-pre-deploy/iosmux_inject.dylib.d23v2 (sha 52df2cc6...4803). CDS respawned via killall CoreDeviceService (PID 749 → 1940), both hook install paths (AUA keepalive NOP + GAMBIT trampoline) succeeded rc=0.

Result: FAILURE. Same CoreDeviceError(errorCode: 3, "Failed to acquire assertion") ~9.9 ms after success() reply. Latency identical to D23-v2 baseline.

Citations from havoc unified.log (2026-04-30 capture):

21:29:51.998775+0700  devicectl[2001]: ... Received reply from action
                       (type=AcquireDeviceUsageAssertionActionDeclaration,
                        invocation=33377F43-...): success()
21:29:52.008620+0700  devicectl[2001]: [com.apple.dt.coredevice:useassertion]
                       Failed to acquire usage assertion on device
                       E8A190DD-... due to error: CoreDeviceError(errorCode: 3,
                       errorUserInfo: ["NSLocalizedDescription":
                       "Failed to acquire assertion"])

GAMBIT path executed correctly (peer retained, no cancel scheduled):

[inject] GAMBIT: synthesised reply for aid=com.apple.coredevice.action.acquireusageassertion
[inject] GAMBIT: emitted AFM endpoint=0x7fc754107cb0 did=E8A190DD-...

Critical absence: zero XPCError 1001 lines anywhere in the captured 60s window. Zero peer[2019] references. Zero Recieved error from side channel peer strings. D30 DID prevent peer invalidation via the cancel-after-send path — the cancel was the cause of the 1001 surfacing, but the 1001 surfacing was NOT the cause of errorCode 3. Peer lifetime is not the gate.

What D30 ruled out: AFM peer cancel-after-send is what drives .invalidated(error). RULED OUT. With cancel removed and peer held for entire CDS process lifetime, AUA outcome is byte-identical (same errorCode 3, same NSLocalizedDescription, same ~10 ms latency).

What D30 reveals: the dispatcher chain has a third uncatalogued input source (input #3) that produces Failed to acquire assertion even when (a) action XPC reply is success(), (b) cache-eviction is NOP'd (D23 P-1a), and © AFM peer never invalidates (D30). The ___30eb0/___3bc80/___324a0 dispatcher chain may still be the final funnel, but its input here is not the side-channel invalidation D24 documented.

Empirically falsified inputs (DO NOT retest):

  • Byte-shape (D20): AFM payload mangledTypeName / shape / identifier
  • Cache-eviction (D23): ActionConnectionCache.removeCachedXPCConnection at CD+0xdbe0
  • Peer lifetime (D30): AFM peer connection cancel-after-send

Open hypothesis space (5 candidates for D31 research probe):

  1. AFM identifier value mismatch — GAMBIT sends action's CoreDevice.deviceIdentifier UUID as assertion identifier in AFM value.identifier field. Apple's AUA wrapper may match against IdentifiableAssertionDetails.identifier (separate assertion-instance UUID generated wrapper-side, not present in request envelope).
  2. Mercury.XPCSideChannel framing layer above raw xpc_connection — AFM endpoint we send on is raw libxpc (xpc_endpoint_create / xpc_connection_create_from_endpoint). Apple's AUA wrapper may expect Mercury-framed message on top.
  3. AFM timing race vs success-callback — GAMBIT emits AFM before returning synthetic success() reply. AUA wrapper may subscribe to AFM only AFTER success callback fires on assertionq → AFM dropped.
  4. Separate notification channel — AFM-via-endpoint may not be the right delivery vehicle. Wrapper may listen on darwin notification (notify_post/notify_register) or Mercury KVO property update or separate XPC service.
  5. DSS field validationupdatedSnapshot.monotonicIdentifier in AFM payload may be validated against snapshot cached at action-dispatch time; rejected if not strictly newer (mirrors Q-D66-14 RDSUE filter).

Apparatus integrity (D30 also non-destructive): iPhone (iosmux) connected (no DDI) unchanged. CoreDevice sha unchanged. Zero new diagnostic reports. Backup at /home/op/backups/iosmux/d30-pre-deploy/iosmux_inject.dylib.d23v2 intact and ready for revert.

Architectural conclusion: Q-D66-15 is REOPENED. The cosmetic gap framing of D29 — "does NOT block install/debug/app-launch pipelines" — was correct at the install-pipeline scope but does not satisfy the user-stated UI-functionality goal. UI shows "Disconnected" + "Failed to acquire assertion" because _shadowUseAssertion.fulfilled stays nil; making UI Connected requires AUA wrapper to resolve .fulfilled → input #3 must be localised and addressed.

Next-action: D31 research probe (general-purpose agent, RESEARCH not implementation per feedback_split_research_and_implementation). lldb dynamic attach to devicectl during AUA invocation in post-D23 + post-D30-revert apparatus. Goal: localise EXACT instruction site that synthesises residual errorCode 3. Breakpoints: swift_continuation_throwingResume* (full stack walk), "Failed to acquire" cstring xref sites at CD file offsets 0x371880/0x3718e0, CoreDeviceUtilities.CoreDeviceError.init(code:userInfo:) (D21 §K — 3 locations), CD+0x304a0 legacy completion-handler API (per D27: ___317a0 FALSE branch invokes; per D28: ___317a0 family runs server-side post-D23, but legacy callback path may run client-side). Apparatus must be reverted to D23 v2 BEFORE probe (D30 dylib reverted to baseline; D30 source edits reverted).

Distillation status: D30 raw notes (notes/d30-afm-peer-keepalive.md, ~32 KB, gitignored) deleted at distillation time per feedback_notes_are_temporary_buffer. Empirical findings + citations preserved in this entry + aua-side-channel-mechanism.md §"D30 update: AFM peer keep-alive — FAILED" + REOPEN admonition near top of that doc.

Q-D66-15 — D31 (2026-05-01, lldb dynamic probe v2): input #3 IS peer[1013] race; SUCCESS path empirically observed; D32 designs cause-side suppression

Status: REOPENED, race localised. D32 next.

D31 v2 dispatch (general-purpose research agent, lldb attach to client-side devicectl, three trigger runs against AUA invocation in post-D30-revert apparatus). Empirically localised the third input source named in the D30 admonition.

Construction site:

Symbol Module File offset Role
CoreDeviceError.init(code:userInfo:) CoreDeviceUtilities +0xbabf0 Final ctor — code:Int32 = 3 in rdi
Swift.Error<...CoreDevice._Error>.init(Int32, String) CoreDeviceUtilities +0xc10c6 Inner-call call-site
Swift.Error<...CoreDevice._Error>.init(τ_0_0, Optional<String>) CoreDeviceUtilities +0xc2780 Outer-wrap adds NSLocalizedDescription
xpcError.getter CoreDeviceUtilities +0xc2978 Bridge from XPCError to CoreDeviceError

Caller chain (input #3 anchor inside CoreDevice):

Symbol File offset entry PC at hit Role
___lldb_unnamed_symbol_30230 +0x30230 +0x30368 (inner) / +0x303cd (outer wrap) Input #3 synthesis caller
___lldb_unnamed_symbol_31700 +0x31700 +0x31734 Outer caller — invokes ___30230
Side-channel pump CDU +0x55510 / +0x55540 / +0x52510 / +0x52630 / +0x54060 / +0x56350 / +0x54ab0 Drain queue on _dispatch_lane_serial_drain

Throw funnel: same ___30eb0/___3bc80/___324a0 dispatcher as D24 W4 input #2 (current build offsets +0x776 → +0x3d → +0x38). Inputs #2 and #3 share final dispatcher; differ only in WHICH XPC peer's invalidation drives the 1001.

THE PEER:

<SystemXPCPeerConnection 0x... { <connection: 0x... {
    name = com.apple.xpc.anonymous.0x...peer[1013].0x...,
    listener = false, pid = 1013, euid = 501, egid = 20,
    asid = 100024 } }>

pid = 1013 is the CoreDeviceService daemon — i.e. the CDS↔devicectl primary side-channel connection. This is distinct from peer[2019] (the AFM peer that D30 retained). D30 left peer[1013] untouched, so its post-reply invalidation continued to fire and continued to be caught by the same [useassertion] observer that synthesises errorCode 3.

Race condition empirically confirmed (the architectural sea-change):

D31 captured TWO outcomes under identical apparatus state:

  • Run #2 (success): [useassertion] Successfully acquired usage assertion E8A190DD-... — the success-resume thread won the race. The 1001 STILL fires post-reply but arrives AFTER the success continuation resumed → throw becomes a no-op.
  • Run #3 (failure, iter 1 of 5-iteration retry): Failed to acquire usage assertion ... CoreDeviceError(errorCode: 3) — the side-channel observer drain pump won.

Two competing threads in devicectl post-reply:

  1. Thread A — success-resume path: swift_continuation_throwingResume (no error variant). Triggered by GAMBIT-synthesised success(...) reply unwinding through CoreDevice's reply-handling code.
  2. Thread B — side-channel observer drain pump: catches XPCError 1001 from peer[1013] invalidation, synthesises CoreDeviceError(code:3) via the construction chain above.

Whichever thread first calls into the AUA continuation wins. Run #2 proves Thread A CAN win deterministically if the post-reply teardown path is short-circuited or delayed long enough.

Image base addresses (current build, CDS PID 1013, post-D30-revert apparatus):

Image __TEXT base
CoreDeviceService.xpc 0x102b1d000
CoreDevice.framework 0x104c7b000
CoreDeviceUtilities.framework 0x103a99000
CoreDeviceInternal.framework 0x1032ad000
iosmux_inject.dylib 0x1032d4000

Process locality: client-side (devicectl). Both BP2 hits and the throw at BP1 fire inside the devicectl process. CDS daemon (PID 1013) reply is not the immediate trigger — the trigger is the post-reply peer invalidation propagated from CDS to devicectl via the side-channel.

Apparatus integrity (D31 also non-destructive): iosmux_inject.dylib sha unchanged (52df2cc6...4803); CoreDevice sha unchanged (bea205e2...0495); iPhone (iosmux) connected (no DDI); CDS PID stable at 1013 across all 3 lldb attaches; zero new diagnostic reports.

Strategy disposition for D32 (per dispatcher decision based on prior D23/D30 chained-NOP failure pattern):

  1. PICKED — suppress peer[1013] invalidation upstream of 1001 emission. Find what triggers peer[1013] invalidation CDS-side and block at AUA reply time. Closes race at cause, not symptom. User rationale: prior D23/D30 NOP attempts pulled chained downstream gates one-after-another; cause-side blocking is the only architecturally clean exit.
  2. REJECTED — NOP side-channel drain pump (CDU +0x55510 family). Risky blast radius — same pump may handle legitimate errors elsewhere.
  3. REJECTED — NOP CD+0x30230 synthesis. Most localised but not AUA-specific. CD+0x303cd path also reached for non-AUA errors. Likely breaks other CoreDevice surfaces.

Next-action: D32 — research probe (general-purpose, lldb dynamic attach to CoreDeviceService daemon PID 1013) to localise the EXACT instruction site that triggers peer[1013] invalidation post-AUA-reply. Goal: identify a CDS-side hook point that prevents peer[1013] teardown during the AUA reply window. RESEARCH only, no implementation.

Distillation status: D31 raw notes (notes/d31-aua-errorcode3-localisation.md, ~575 lines, gitignored) deleted at distillation time per feedback_notes_are_temporary_buffer. Empirical findings + citations preserved in this entry + aua-side-channel-mechanism.md §"D31 update".

Q-D66-15 — D32 (2026-05-01, lldb attach to live CDS): peer[1013] invalidation is KERNEL-driven; no CDS-side hook point exists; D31 cause-side strategy empirically falsified

Status: REOPENED, kernel-driven trigger localised. D33 strategy decision pending (three options ranked).

D32 dispatch (general-purpose research agent, lldb running-attach to live CDS PID 1013, 12 BPs covering xpc_connection_cancel, _xpc_connection_cancel, xpc_remote_connection_cancel, _xpc_connection_dispose, Mercury XPCSideChannel.{deinit,__deallocating_deinit}, Mercury RemoteXPCConnection.{deinit,__deallocating_deinit}, RemoteXPCConnection.unsafePeer(from:forServiceNamed:), XPCSideChannel.send(message:), CoreDevice.XPCSideChannel.sendCancelledMessage(), and the CoreDeviceUtilities.invoke(anyOf:usingContentsOf:) action sentinel).

Two distinct XPC connections observed during AUA window:

Connection Address (run 5) Identity Cancel mechanism
A 0x7f89c6e075e0 iosmux's AFM peer (xpc_connection_create_from_endpoint(input.endpoint) in GAMBIT) iosmux explicit cancel: __gambit_emit_afm_via_endpoint_block_invoke_2 → xpc_connection_cancel
B 0x7f89c6a09040 peer[1013] — CDS↔devicectl primary side-channel anonymous accepted peer Kernel-driven via MACH_NOTIFY_NO_SENDERS; ZERO application frames in chain

Connection A lifetime is irrelevant to errorCode 3 (D30 already proved). Connection B is the input #3 source from D31.

Connection B's invalidation chain (entirely in libxpc, no app code):

Frame  Function                                  Module / offset
#00    _xpc_connection_cancel                    libxpc + 0x4d2f7
#01    do_mach_notify_no_senders +0x3c           libxpc + 0x407c0
#02    _Xmach_notify_no_senders +0x21            libxpc + 0x40761
#03    notify_server +0x4e                       libxpc + 0x3ff32
#04    _xpc_connection_pass2mig +0x8e            libxpc + 0x3fe77
#05    _xpc_connection_mach_event +0x4d5         libxpc + 0x39755
#06+   _dispatch_client_callout4 / dispatch / pthread bottom

Secondary cleanup chain at t+126ms confirms peer identity via _xpc_connection_remove_peer_impl +0x3c → _xpc_connection_remove_peer — internal libxpc code that removes an anonymous accepted peer from a listener's peer table. Matches D31's name = com.apple.xpc.anonymous.0x...peer[1013] shape.

Negative results: BPs ⅔/⅘/6/8/9/12 (Mercury/RemoteXPC/XPCSideChannel application-level) NEVER fired during AUA window. Mercury user-facing API was NOT exercised CDS-side. Connection B is owned directly by an XPC listener (likely the CDS service's own listener), bypassing all Mercury/CD/CDU layers.

The architectural sea-change for D33:

MACH_NOTIFY_NO_SENDERS fires when the OTHER side (devicectl) drops its last mach send right on the connection's underlying mach port. The trigger lives in devicectl's process, not in CDS. When devicectl finishes processing the AUA reply and releases its handle to peer[1013], the kernel delivers the notification to CDS, libxpc tears down its peer table entry, devicectl-side observes its own connection handle's invalidation and emits XPCError 1001 → input #3 fires.

peer[1013] cannot be kept alive CDS-side without devicectl also keeping its end alive. devicectl's release is governed by its own process-internal logic — no CDS-side hook can reach it.

D31's preferred strategy "suppress upstream of cause" remains correct in principle, but the empirical localisation now shows that "upstream" lives in devicectl, not in CDS. The CDS-side path that D31 picked for D32 is empirically impossible.

Three remaining strategy options for D33:

  1. devicectl-side inject — new dylib loaded into devicectl via DYLD_INSERT_LIBRARIES (or LC_LOAD_DYLIB on a patched devicectl copy following the existing CDS pattern in iter-18 findings). Hook the function inside devicectl that releases peer[1013]'s handle; defer the release past AUA continuation resolution. Most stable architectural exit but expands inject footprint to a new process. devicectl may have hardened runtime / library validation — codesign accommodation needed. Pre-existing project disposition: previously rejected as "REJECTED — overkill, 12-20 hours, new injection mechanism" in aua-side-channel-mechanism.md Inject-side fix vectors §P-1b. D32 finding reopens that rejection.

  2. Surgical hook on side-channel observer drain pump in devicectl — narrower than D31's "NOP drain pump" rejection. Hook xpcError.getter (CDU+0xc2978) or one of the CDU+0x55510 family entry points; filter ONLY when error code is 1001 AND caller is in AUA context (stack walk discriminator). Bounded blast radius compared to blanket-NOPing the pump. Still requires devicectl-side inject.

  3. NOP CD+0x30230 synthesis in devicectl — least AUA-specific; CD+0x303cd path also reached for non-AUA errors. Still requires devicectl-side inject.

All three options require devicectl-side inject mechanism — no CDS-side option remains.

Apparatus integrity (D32 also non-destructive): iosmux_inject.dylib sha unchanged (52df2cc6...4803), CoreDevice sha unchanged (bea205e2...0495), iPhone (iosmux) connected (no DDI), CDS PID stable at 1013 across lldb attach/detach (running-attach worked first try), zero new diagnostic reports.

Distillation status: D32 raw notes (notes/d32-peer1013-invalidation-source.md, ~349 lines, gitignored) deleted at distillation time per feedback_notes_are_temporary_buffer. Empirical findings + libxpc invalidation chain + Connection A/B distinction + strategy disposition preserved in this entry + aua-side-channel-mechanism.md §"D32 update".

Status: REOPENED, D34 implementation brief design pending dispatcher decision.

D33 dispatch (general-purpose research agent) closed three concrete questions before D34 implementation. The dispatcher's pre-dispatch preference for Option 1 needs reconsideration based on D33's empirical reframing.

Q1 — DYLD_INSERT_LIBRARIES injection: FEASIBLE

devicectl path: /Library/Developer/PrivateFrameworks/CoreDevice.framework/Versions/A/Resources/bin/devicectl, sha256 4fede2dd4fdbe542547b535f70ad320f8c33093cd23ff5b226aaad7c327cbf6c, Mach-O universal [x86_64 + arm64e], signed by Apple com.apple.CoreDevice.devicectl TeamIdentifier 59GAB85EFG.

Codesign properties:

  • Library validation flag (0x2000) PRESENT.
  • Hardened runtime flag NOT present (runtime flag absent — this is the key permissive condition).
  • Three Apple-private entitlements (none restrict dyld inserts).
  • NO com.apple.security.cs.allow-dyld-environment-variables (not needed when hardened runtime is off).
  • NO com.apple.security.cs.disable-library-validation.

Empirical test: ad-hoc-signed no-op probe dylib loaded into devicectl via DYLD_INSERT_LIBRARIES=/tmp/iosmux-d33-probe.dylib. Probe constructor banner appeared in stderr ([d33] DYLD_INSERT_LIBRARIES probe loaded pid=3533); devicectl exited 0; no rejection messages.

Verdict: D34 does NOT need to design insert_dylib LC_LOAD_DYLIB patching of an Apple binary copy. Standard DYLD_INSERT_LIBRARIES injection works directly.

Q2 — Option 1 hook target REFRAMED — peer release is libxpc-internal, not app code

P3 lldb capture (xpc_release BP filtered for connections matching xpc_connection_get_pid == CDS-PID) localised the release call to:

#0  xpc_release+0x0
#1  libxpc.dylib`_xpc_connection_mach_event+0x418
#2  libdispatch.dylib`_dispatch_client_callout4+0x7
#3  libdispatch.dylib`_dispatch_mach_cancel_invoke+0x40
#4  libdispatch.dylib`_dispatch_mach_invoke+0x399
#5  libdispatch.dylib`_dispatch_root_queue_drain_deferred_wlh+0x113
#6  libdispatch.dylib`_dispatch_workloop_worker_thread+0x367

ZERO frames in CD/CDU/Mercury Swift app code. The release is a passive libxpc Mach-event reaction — the symmetric image of D32's CDS-side kernel-driven invalidation (CDS gets MACH_NOTIFY_NO_SENDERS from devicectl side; devicectl gets cancel-Mach-event from CDS side).

Two peer connections per AUA invocation (new finding):

  • 0x7faad11056d0 — first peer, AUA reply path
  • 0x7faad11043c0 — second peer, side-channel "result peer" named peer[1013] (the cancel recipient)

The actionable Option 1 hook target identified: Mercury.SystemXPCPeerConnection.__deallocating_deinit at Mercury+0x4ea10 (sister .deinit at +0x4e9d0). Mercury __TEXT base in P3 run: 0x100453000 (slide differs per launch).

BUT this is a Swift type's deinit chain that fires AFTER libxpc has already released the underlying mach-port-backed connection. Option 1 cannot achieve "prevent peer release" as the dispatcher originally hoped — the release is libxpc-internal. Option 1 can only modify post-release Swift cleanup, which is symptomatic, not causal.

Q3 — Option 2 hook target viability: CONFIRMED

CDU __TEXT base in current devicectl run: 0x1020a4000. All D31 §E offsets resolve to expected symbols:

File offset Resolved symbol Notes
CDU+0xbabf0 CoreDeviceUtilities.CoreDeviceError.init(code:userInfo:) NAMED Swift symbol — direct dlsym target
CDU+0xc2940 static Swift.Error<...>.xpcError.getter (entry) NAMED Swift symbol
CDU+0xc2978 static Swift.Error<...>.xpcError.getter +0x38 (same function)
CDU+0x55510, +0x54ab0 drain pump unnamed symbols Reachable via slide arithmetic
Mercury+0x57b40 Mercury.XPCError.xpcError.getter NAMED Swift symbol — wire-level XPCError layer
Mercury+0x5cd90 Mercury generic xpcError getter variant NAMED

dlsym/Apple-Swift-mangled-name lookup is sufficient for named targets. Same proven pattern as the existing CDS-side inject's hooks.

Bonus finding: CoreDevice.ActionConnectionCache.removeCachedXPCConnection at CD+0xdbe0 (the D23 NOP-evict target) is ALSO present in devicectl-side CoreDevice. Same class type with same method on both sides of the boundary; CDS-side instance is currently NOP'd, devicectl-side instance runs unmodified. Possible follow-up but out of scope for D33.

Strategy comparison for D34

Dimension Option 1 (Mercury deinit) Option 2 (CDU error path)
Symptom vs cause Symptom-side (Swift cleanup AFTER libxpc release) Symptom-side (error construction)
Hook point named Yes — Mercury.SystemXPCPeerConnection.__deallocating_deinit Yes — CoreDeviceError.init, xpcError.getter
Discrimination needed Yes — peer's xpc_pid + AUA context Yes — errorCode == 1001 + AUA-context-on-stack
Side-effects Memory-leak risk (preventing deinit chain leaves connection-bookkeeping graph in CD/Mercury caches) Lower — error construction is stateless; suppression doesn't keep dead state alive
Failure mode Hook misses → connection still released (deinit was already too late anyway) Hook misses → error surfaces normally
Implementation complexity Higher — Swift deinit + refcount semantics on Mercury internal type Lower — Swift function prologue rewrite

Recommendation for D34: Option 2

Agent recommendation: Option 2. Rationale:

  1. Both options are symptom-suppression — Option 1's "cause-side" framing was empirically falsified (release is libxpc-internal, not app code; Mercury deinit fires AFTER libxpc release). Given symmetric symptom-side scope, simpler symbol set wins.

  2. Named Swift symbols dlsym-resolvableCoreDeviceError.init(code:userInfo:) and xpcError.getter are NOT stripped. Same proven pattern as CDS-side gambit_install_hook from inject/iosmux_gambit.m.

  3. No refcount/deinit hazards — Option 1 prevents a Swift type's deinit chain; Mercury's internal connection-bookkeeping graph may rely on the deinit cascade, and preventing it could leak Mercury state. Option 2 hooks stateless error-construction functions — no state-machine entanglement.

  4. Filter discrimination is bounded — Option 2 filter: code == 1001 AND caller-stack contains AUA path symbols. Option 1 filter: peer's xpc_connection_get_pid == CDS-PID AND deinit context is AUA-related. Both feasible but Option 2's discriminator is on values (errorCode integer) not on object pointers (xpc_pid).

Apparatus integrity (D33 also non-destructive)

  • iosmux_inject.dylib sha unchanged (52df2cc6...4803).
  • CoreDevice sha unchanged (bea205e2...0495).
  • devicectl sha unchanged (4fede2dd...bf6c) — Apple binary untouched throughout the probe.
  • iPhone (iosmux) connected (no DDI).
  • CDS PID stable at 1013 across all probe steps.
  • Zero new diagnostic reports.
  • Test probe dylib at /tmp/iosmux-d33-probe.dylib on havoc — will not survive reboot.

Distillation status

D33 raw notes (notes/d33-devicectl-feasibility.md, ~562 lines, gitignored) deleted at distillation time per feedback_notes_are_temporary_buffer. Empirical findings + injection feasibility verdict + Option 1 reframing + Option 2 verification + strategy comparison preserved in this entry + aua-side-channel-mechanism.md §"D33 update".

See also