Require headphones before a session starts - #117
Merged
Conversation
`ch_0` is a loopback of the render endpoint, so on speakers the microphone hears the interviewer too and the question lands as a recent `Self` final. `skipDueToRecentSelf` then suppresses the live suggestion for the question that was just asked, silently. The notice is shown before every session until the user silences it, and it names that failure rather than recommending headphones for "best results". It sits ahead of the macOS permission gate and ahead of `doStart`: on speakers the echo is in the audio before the first question. Nothing in the renderer can detect the output route, so the dialog asks rather than guesses. The preference is written when the user goes through, not when they tick the box, so a tick followed by Cancel does not silence a warning they never acted on. Closes #116 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The dialog is mounted for the life of the control panel, so a tick that was followed by Cancel survived and was waiting, already checked, the next time it opened - silencing the warning on a click the user did not knowingly make. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
alpha5611331
force-pushed
the
feat/headphone-notice
branch
from
August 28, 2026 02:34
88ee7b4 to
4baaa5e
Compare
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.
Closes #116. The cheap half of #111 - useful now, and still useful once the gate lands.
Why this is a correctness fix, not a recommendation
ch_0is a loopback of the system's render endpoint, so on speakers the microphone hears the interviewer a fraction of a second after the loopback does. The echo lands as a recentSelffinal, andskipDueToRecentSelfintranscript.service.tsthen suppresses the live suggestion for the question that was just asked. There is no error, no card, nothing in the panel. The duplicated transcript lines are the visible half; this is the half that costs the interview.The dialog says that, rather than recommending headphones for "best results". A user who reads the second version and ignores it has made a reasonable decision on the information given.
What it does
Shown on Start, before the macOS permission gate and before
doStart- on speakers the echo is already in the audio by the time the first question is asked.startAfterNoticeholds everything from the permission gate onwards so the dialog can hand the start back without duplicating those checks.Shown on every session until the user ticks "Do not show this again", which persists as
headphoneNoticeAcknowledged. Opt-out rather than opt-in: whether the call is on speakers is a property of the machine and the meeting, not a setting, and it can change between sessions on the same install. The preference is written when the user goes through, not when they tick the box, so a tick followed by Cancel does not silence a warning they never acted on.What it deliberately does not do
It does not try to detect headphones.
enumerateDevices()reports what exists, not what the sound is coming out of, and a label match on "headset" is wrong in both directions - it clears a user whose headphones are plugged in but not selected, and nags one whose USB interface is named after a mixer. The user's answer is the only signal available, so it is asked for.Checks
pnpm lint, bothtscconfigs,pnpm buildandpnpm test:mainpass locally.test/config-store.test.mjsgains a check that a store written before this key existed reads as "not acknowledged" rather than silenced - the migration backfill direction that matters here.Note on merge order
Touches
handleStartClickincontrol-panel/index.tsx, as does #115. Whichever lands second needs a one-hunk rebase there: the confirmation from #115 goes before this notice, so the order is check config -> ask to save the previous interview -> headphone notice -> permission gate -> start.🤖 Generated with Claude Code