fix(helper): pump run loop for CLLocationManager auth before scan (the real root cause)#45
Merged
Merged
Conversation
Real root cause of the macOS 26 install hang. The v1.0.3 fix added disclaim-responsibility + CLLocationManager.startUpdatingLocation() + Thread.sleep(0.3) to runScanAndDumpJSON. The disclaim hop and the manager init are correct; the Thread.sleep was wrong. Thread.sleep does NOT pump the run loop. CLLocationManager's delegate-callback handshake with locationd never actually completes inside a short-lived CLI subprocess. CoreWLAN's macOS 26 redaction gate checks whether the process is a *registered* location consumer — not just authorised — so scans came back redacted (ssid/bssid null) even when the bundle's TCC grant was in place. The bundle GUI sees authorizationStatus as granted because that's a synchronous read of cached state; the redaction gate is the stricter check. This kept the user stuck at "需要以下权限:- 定位服务" through v1.0.3 / v1.0.4 / v1.0.5 even after clicking Allow on every prompt. Fix: new LocationAuthProbe : CLLocationManagerDelegate that flips a flag when `locationManagerDidChangeAuthorization` resolves status to non-.notDetermined. The scan subcommand pumps `RunLoop.current.run(mode:.default, before:…)` in 50ms slices, exiting as soon as the callback fires (typically <100ms when the grant is already in TCC.db) or after a 2s timeout. Only then does scanForNetworks run. Same pattern as the existing runBluetoothStatusProbe. Verified locally end-to-end: - helper/build.sh rebuilds → new cdhash 6e74d56e... - user grants Location on the new cdhash via `open helper/...` - 3 cold-start subprocess scans (helper GUI killed between runs to defeat warm-cache effects) → 100% unredacted rows each run Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The real root cause of the macOS 26 install hang you've been seeing through v1.0.3 → v1.0.5.
The earlier fix in v1.0.3 (
disclaim + CLLocationManager.startUpdatingLocation + Thread.sleep(0.3)) was on the right track for points 1 + 3 but got point 2 wrong:Thread.sleepdoes not pump the run loop. CLLocationManager's delegate-callback handshake with locationd never actually completes inside a short-lived CLI subprocess. CoreWLAN's macOS 26 redaction gate checks whether the calling process is a registered location consumer — not just authorised — so scans came back redacted (ssid/bssidnull) even when the bundle's TCC grant was in place.The helper GUI window saying "Location: granted" was reading
authorizationStatussynchronously (cached state). The CoreWLAN redaction gate is the stricter check, and it failed silently — leading to the polling hang.Fix
New
LocationAuthProbe : CLLocationManagerDelegatethat flips a flag whenlocationManagerDidChangeAuthorizationresolves status to non-.notDetermined. The scan subcommand pumpsRunLoop.current.run(mode: .default, before: …)in 50ms slices, exiting as soon as the callback fires (typically <100ms when the grant is already in TCC.db) or after a 2s timeout. Only then doesscanForNetworksrun.Mirrors the existing
runBluetoothStatusProbepattern — which is exactly whybluetooth-statushas been working correctly through all these versions while scan has been broken.Verification (local, before opening this PR)
3 cold-start subprocess runs:
100% unredacted, no warm GUI session needed. This is the first time the fix has been verified to work in the exact same conditions install.sh produces.
uv run pytest— 499 passed🤖 Generated with Claude Code