Repository navigation
Telemetry for 1.1.62: call-audio failures and reconnects, permission prompt, warmup, crash builds - #1781
Merged
Conversation
On 2026-09-23 a dictation started while AirPods were becoming the default input. The AirPods mic never came up inside the 6s start budget (format read timeout, bind failure, prewarm timeout) and dictation failed with "Selected mic unavailable". Nothing ever tried the Mac's own mic. - On a Bluetooth headset route (AirPods as mic and playback), dictation now binds the Mac's built-in mic first, so playback stays out of call mode and there's no slow HFP switch. It follows the headset instead when "Use Mac-selected microphone" is on (the same setting meetings read) or the MacBook lid is closed. - If the first mic hasn't started 2.5s into the wait loop, dictation switches to the other mic once and gives it at least 4s, instead of timing out on the stuck mic. The overlay says "Using your Mac's mic" / "Using your Bluetooth mic". The switch only lasts for that dictation. - The mic choice is an AUHAL bind only; dictation still never writes the Mac-wide default input. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018WR8eSZBbxLhZFaNENM5yU
Hardware log (AirPods Pro, music playing): every start picked the Mac mic, then the readiness check saw the input node's output bus stuck at 24k and reported routeNotSettled four times. After ~1.5s the recovery start suppressed the built-in fallback and recorded on AirPods, whose mic release later paused music on stop. - A local mic with Bluetooth playback no longer waits on a low-rate output bus. The raw tap uses the hardware input format and VPIO is deferred on that split route, so the bus rate doesn't matter. This is also the likely cause of the 2026-09-09 "built-in never became ready" report. - Remember the mic that last started on the same headset, so a mic the wait loop switched to starts directly next time. Recovery landings that followed the headset only because the fallback was suppressed are not remembered. - Route analytics now pass the live recording formats, so hfp_suspected reflects the running recording instead of always reading false. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018WR8eSZBbxLhZFaNENM5yU
Hardware log after relaunch (AirPods Pro, music playing): the first press landed 1s after launch. The cold built-in bind missed its 1.2s settle window. At 2.5s the wait loop switched to the AirPods mic, which put playback into call mode for the ~10s the dictation ran. The start took 5.4s and garbled the music. A Mac-mic first choice is now pinned, for recovery starts too. The wait loop switches only headset -> Mac mic. A slow Mac mic keeps getting readiness retries on the built-in, and if it never comes up, the user sees "Built-in mic unavailable" with Try Again rather than music in call mode. This also closes the known gap where a resume after borrowing the meeting mic could open the AirPods mic. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018WR8eSZBbxLhZFaNENM5yU
After a relaunch with AirPods as the Mac's input, the first dictation's cold AUHAL rebind to the built-in mic took 1.4s once and ran out the 7.7s start budget once (press 8.6s after launch, so this wasn't launch load). Warm starts took 81-84ms. - TranscriptedAppState runs the permission-gated input prewarm once at launch. It only binds the mic and reads formats; nothing records. This arms the engine's wake/route observers at launch, the same state the app is in after its first dictation today. - dictation_input_device_selection_failed now logs failure_kind and status_code. The error text is redacted from local logs, which hid why the cold bind failed. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018WR8eSZBbxLhZFaNENM5yU
- Core Audio tap: remember the last HAL step that refused and its OSStatus; report them as system_tap_step / system_tap_status on the meeting PostHog events and as Sentry tags on meeting_start_failed. The tap has no ScreenCaptureKit fallback, so this is the only off-device signal for why call audio did not start or come back. - dictation_started: add start_latency_bucket (request to recording). Both dictation start events: add first_since_launch, so cold starts after launch are visible. - Sentry crashes: tag build_revision and build_channel so a crash can be matched to the exact build. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SRK9LR6bS7HMphU19DMDfy
…memory Fixes three 1.1.62 deep-review findings on this PR: - S2 (lid closed): a slow AirPods start could switch to the MacBook mic, which is cut off in hardware while the lid is shut. - M12 (no built-in mic): on a Mac with no built-in mic, the overlay said "Using your Mac's mic" when it wasn't. - S3: remembering the switched-to mic pinned every later dictation to it, overriding "Use Mac-selected microphone" until the app quit. The wait loop's switch now asks the engine, which declines (returns nil) when the lid is closed or a pinned Mac-mic lookup finds no built-in mic. A declined switch doesn't extend the wait or change the overlay. The remembered-mic feature is removed. It existed to skip a failing first choice, and that failure was the stale-bus readiness gate fixed in 613a0df. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018WR8eSZBbxLhZFaNENM5yU
…sh' into claude/telemetry-1162-bofke5
…ng-xpyplf' into claude/telemetry-1162-bofke5
… into claude/telemetry-1162-bofke5 # Conflicts: # Sources/Meeting/MeetingSessionController.swift
…y4j8' into claude/telemetry-1162-bofke5
- Call-audio tap (#1762/#1771): per-recording reconnect counts by cause (wake, format change, silent after wake, stall), rebuild retries, sleeps, silent-after-wake give-ups and why system audio ended. Bucketed on meeting events and allowlisted as Sentry tags. - #1767: mic_format_rebuilds_bucket when AirPods flip format at start. - #1768: meeting_system_audio_prompt_answered (outcome, tcc_status), plus mic_only_by_choice and system_permission_check on meeting_recording_started. - #1773: launch_models_warmed once per launch; models_warm on meeting_recording_started. - #1774: headset_mic_switched on dictation events. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SRK9LR6bS7HMphU19DMDfy
Hardware (combined build a8036363, AirPods as default input and output, music playing): every app launch made a short audible blip in the AirPods. A fresh AVAudioEngine input node binds the macOS default input before any device can be pinned, and on AirPods that briefly flips playback toward call mode. This is the same mechanism as the meeting-mic sleep garble. The launch prebind now checks the default input off the main actor. When it's Bluetooth, the prebind is skipped and dictation_launch_prebind_skipped is logged. Those users pay the cold bind on their first dictation, which is where the touch happened before the prebind existed. Built-in and USB defaults keep the warm first press. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018WR8eSZBbxLhZFaNENM5yU
A press 1.96s after launch raced the launch prebind for the audio engine queue and fell into the slow start path (4.9s, ending on a stale 24k bus). The launch bind is bounded, so a press that lands while it runs now waits for it (up to 3s) and then starts on the warm engine instead of starting a second cold bind. Recovery attempts skip the join. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018WR8eSZBbxLhZFaNENM5yU
Hardware (build 94cc4fa1, AirPods as default input and output, music playing): with the launch bind skipped, the first dictation after launch took the cold path. The music garbled, the overlay showed a mic switch after 1-2s, and AirPods playback then cut out. That is much worse than the short launch bump, so the launch bind runs on Bluetooth defaults again. A press that lands during it still joins it (ac4d96a). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018WR8eSZBbxLhZFaNENM5yU
Hardware (build 94cc4fa1, AirPods as default input and output): both cold presses failed with binding_not_settled about 2.5s in. The AUHAL rebind from AirPods to the Mac mic outran the 1.2s settle window. The start then spent ~5s in retries while the AirPods garbled and cut out. The one successful cold bind took ~2s. The launch prebind now gets a 3s settle window when it pins the Mac mic away from a Bluetooth default input. A press keeps the 1.2s window its readiness refresh is sized for, and a press that lands during the launch bind waits up to 4.5s for it to finish. An unsettled bind has no OS status code, so selection_failed now logs settle_timeout_ms and settle_wait_ms instead. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018WR8eSZBbxLhZFaNENM5yU
When a cold bind pinning the Mac mic away from AirPods didn't settle in time, the wait path kept disturbing it. Every readiness refresh and prewarm retry reissued setDeviceID, which restarts a slow Bluetooth transition. After five refreshes, forced recovery swapped in a fresh AVAudioEngine, whose input node touched the AirPods mic again. On 2026-09-23 that is when the music garbled and then cut out. - ParakeetAUHALBindingIntent.hasPendingSwitch: a confirmed switch to the same mic on the same engine, issued within 4s, is polled instead of reissued. - forceInputReadinessRecovery keeps the engine while such a pin is unsettled (forced_recovery_skipped_bluetooth_default), for up to 4s after the first unsettled result. Blocked queues and timeouts still replace the engine. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018WR8eSZBbxLhZFaNENM5yU
Hardware (rev 995bbbc1, AirPods as default input): the launch prebind failed with "prewarm_snapshot timed out after 1500ms". The first input node read plus the Mac-mic pin on a pristine engine outran the ordinary engine-work timeout, which fires before the settle window even starts. A timeout leaves the engine queue busy and ends in retries or a fresh engine, and each fresh engine touches the AirPods mic again. The launch prebind now gets 3.5s for that first snapshot when it pins a mic away from a Bluetooth default input. Presses keep 1.5s. A press that lands during the launch bind waits up to 5s for it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018WR8eSZBbxLhZFaNENM5yU
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SRK9LR6bS7HMphU19DMDfy
Justin chose to leave the AirPods dictation change out of 1.1.62 (decision card, 2026-09-23 19:45Z). Reverses #1774's diff (main..d469c0e) on this branch only and drops its headset_mic_switched tracking from the dictation events, allowlists, test, and docs. #1767/#1771, #1768 and #1773 stay. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SRK9LR6bS7HMphU19DMDfy
AnalyticsEventPolicyTests checks the doc against analytics-events.psv. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SRK9LR6bS7HMphU19DMDfy
claude Bot
pushed a commit
that referenced
this pull request
Sep 23, 2026
…c-only-meeting-xpyplf Resolves the MeetingSessionController.swift conflict the way #1781's merge 6cc4dfe did: keep resolveSystemAudioAccessFromSystem from this PR and take catchUpModelsInBackgroundIfNeeded from #1773. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuTuFYbp6CM2NGiHz58ZUG
Contributor
Author
|
I don't think this PR causes it:
No fix for this stall exists yet, and the root cause isn't established. This session can't re-run jobs (the API returns 403), so a maintainer re-run of Generated by Claude Code |
r3dbars
approved these changes
Sep 23, 2026
claude Bot
pushed a commit
that referenced
this pull request
Sep 23, 2026
Keeps this branch's overflow reconnect (its last-resort fail() now carries #1781's buffer_overflow reason) and teaches #1781's reconnect counter about the overflow and noFirstBuffer triggers: overflow gets its own on-device count, a reconnect with no first buffer counts as a stall reconnect. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NZkqPnF73GpVNv9N5LYX9b
claude Bot
pushed a commit
that referenced
this pull request
Sep 23, 2026
Resolve the Core CLAUDE.md conflict (keep both notes) and point #1781's tap-failure diagnostics at the recording's own tap, so a mic-only meeting doesn't report the previous meeting's tap failure. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wd6LPpeoYvtp2aMj7wVQLU
This was referenced Sep 23, 2026
claude Bot
pushed a commit
that referenced
this pull request
Sep 23, 2026
Resolved the diagnostics snapshot and its shape test by keeping main's new fields and adding the pinned-mic backend fields beside them. The analytics psv files are a real 3-way merge (union merge would duplicate event lines). Pinned dictation interruptions other than wake now report at error level with mic_backend. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VM1D8NinbTDe7T1oZ3i4ZU
claude Bot
pushed a commit
that referenced
this pull request
Sep 24, 2026
…p ci] Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013GAxhzGyuaTxNTv8tpj9j2
7 of 8 tasks
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.
Requested by Justin · project thread
Before: when the new Core Audio tap couldn't start or reconnect, PostHog and Sentry only said "system_stream_unavailable". Nothing counted sleep/wake reconnects or AirPods format rebuilds. Nothing recorded how people answer the "can't hear the other side of the call" prompt, or whether models were ready at launch. Crash reports could only be matched by version number.
After: every meeting event says which tap step failed and with what code, how many times call audio reconnected and why, and why it ended if it did. Meeting starts say whether the models were already warm and whether mic-only was the user's choice. The permission prompt reports what people picked and what macOS says afterwards. Dictation reports how fast it started and whether it's the first dictation since launch. Launch reports once when the models are warm. Crashes carry
build_revisionandbuild_channel.Why
So we can tell after 1.1.62 ships whether the new capture, sleep, prompt and warmup pieces work for real users.
Product Impact
dictation/meetingsmeeting reliability/dictation reliabilityWhat changed
This branch is stacked. It merges the in-scope 1.1.62 heads (#1767 2529424 including #1771 86fa3df, #1768 1aec18d, #1773 b51ff88) so the tracking can hook into their code. Merge it last. #1774 was merged earlier and then backed out in 311a96e after Justin left it out of 1.1.62.
Tap failures (Use audio-only system capture and add Meetings plus menu #1747):
SystemAudioTapFailure→system_tap_step/system_tap_status(e.g.aggregate_creation/neg10877).Tap upkeep (Keep meeting system audio alive across repeated sleep/wake and output changes #1762/Keep AirPods clean after wake, handle Mac sleep quietly, and keep call audio through a rate change #1771):
SystemAudioTapDiagnosticscounts:system_end_reasonThese are bucketed on meeting events and sent as bucketed Sentry tags.
AirPods meeting start (Start meetings on AirPods without crashing or failing when they switch to call mode #1767):
mic_format_rebuilds_bucket.Permission prompt (Ask before a mic-only meeting; read system audio permission from macOS #1768):
meeting_system_audio_prompt_answered(outcome,tcc_status,tcc_status_after,trigger).meeting_recording_startedaddsmic_only_by_choiceandsystem_permission_check.Warmup (Warm models at launch; dictation and meetings never wait on them #1773): new
launch_models_warmed(once per launch).meeting_recording_startedaddsmodels_warm.Dictation:
dictation_startedaddsstart_latency_bucket. Both start events addfirst_since_launch.Crashes:
SentryPayloadSanitizer.crashRuntimeTagKeysaddsbuild_revisionandbuild_channel.Left out on purpose:
mic_route_not_readyclassifier value, because the message is shared with existing failures and classifier values must stay stable.How
serialized {}. Nothing touches the IOProc.start()resets the counts.neg10877because the shared category check rejects a leading dash.How I checked it
scripts/dev/agent-preflight.shpython3 scripts/dev/check-analytics-emitters.py,python3 scripts/ops/normalize-analytics-taxonomy.py --checkbash build.sh --no-open,bash run-tests.sh,swift test,bash run-integration-smoke.sh: no Swift toolchain in session, so CI is the compile checkRisk Review
SentryEventPolicyTestsandAnalyticsEventPolicyTests.🤖 Generated with Claude Code
https://claude.ai/code/session_01SRK9LR6bS7HMphU19DMDfy
Generated by Claude Code