A wake threshold nobody can see or move is wrong in every room but one #90
Labels
No labels
needs-info
needs-triage
ready-for-agent
ready-for-human
v2
wayfinder:grilling
wayfinder:map
wayfinder:prototype
wayfinder:research
wayfinder:task
wayfinder:ticket
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
aiko/denpa#90
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
A wake word with a fixed threshold is wrong in every room but the one it was tuned in. Hard-floors and soft furnishings, a headset against a laptop array, a phone in a pocket — the same phrase scores differently in each, and the only person who can tell whether it is firing too much or too little is the one in the room.
So it needs the two controls the microphone already has for silero, in the same place and the same shape.
What it is
Add to
VADSettings(src/renderer/src/context/vad-context.tsx:33) andDEFAULT_VAD_SETTINGS(:144), persisted through the sameuseLocalStorage('vadSettings', ...)(:182) as the thresholds beside it:#91has run on a phone. When off, hands-free behaves exactly as it does today.#87's report recommends rather than to a number someone liked the look of.The plumbing already exists and should not be reinvented: settings reach Rust as fractions through
invoke('mic_start', ...)(:500), andupdateSettings(:467) cycles an open microphone so a change takes effect without a restart. The wake threshold rides the same route.There is a trap in that file worth reading first.
updateSettingsreachesstartMicthrough a ref and readssettingsRef, and the comment at:471explains why — a captured closure held render-0's settings and changed thresholds restarted the microphone with the old ones, permanently. A new field added carelessly gets the same bug back.The meter is the point
#88publishesdenpa://wake-score. Draw it, next to or in place of the existing level meter (denpa://mic-level,previousTriggeredProbability), while the setting is open.A sensitivity slider with no visible score is a guessing game: the operator moves it, says the phrase, and gets a binary answer with no gradient. With the score on screen the tuning takes thirty seconds — say it three times, see where it peaks, set the threshold under that. This is the difference between a control that gets used and one that gets left at default forever.
Where it lives
ASR settings(src/renderer/src/components/sidebar/setting/asr.tsx:15) with itsuse-asr-settingshook (src/renderer/src/hooks/sidebar/setting/use-asr-settings.ts:4) is the existing pattern for exactly this kind of pair —SwitchFieldandNumberField, already translated, already wired.Follow it. Do not build a new settings surface for two fields.
A caution about
redemptionFramesVADSettingsstill carriesredemptionFrames, and the comment atvad-context.tsx:40records that it has been inert since capture moved to Rust — kept so a stored preference does not vanish. It is a live example of what happens when a control outlives the thing it controlled.The wake settings must not join it. If a knob added here stops reaching Rust, it should be deleted, not documented.
Done means
npm run extract-translationslike everything else in that panel.Related
#87— where the default threshold comes from#88— the score being drawn#89— what the switch actually turns on#91— the reason it ships offThe switch this issue asks for should replace the existing always-listening control rather than sit beside it. Operator's call, 2026-08-17.
Why the two-boolean shape in the issue body is wrong
The issue specifies a wake-phrase on/off switch added next to what is already there. That leaves two booleans — always listening and wake phrase — and four combinations, of which only three mean anything:
The fourth is unreachable nonsense that both the UI and the code would have to keep defending against forever. Two booleans encoding three states is the bug; the fix is not to validate the combination but to stop being able to express it.
The shape instead
One three-way control, replacing the existing always-listening switch:
#33protects and it must stay reachable.#89's window.These map onto machinery that already exists, which is the point: 2 and 3 differ only by
#89'senabledflag, and 1 is the microphone being shut. No new concept enters the system; a control that could express an impossible state is replaced by one that cannot.Wake phrase becomes the sensible default once
#91has run, but it must not become the default before that, because nobody has measured what continuous inference costs on a phone.Still needed alongside it
The sensitivity number and the live score readout from the issue body stand as written. Sensitivity is only meaningful in mode 2, so it should be visibly inert or hidden in the other two rather than sitting there implying it does something — the exact trap
redemptionFramesfell into and which the issue body already warns about.Default sensitivity is
#87's measured operating point: 0.70, which is where it recorded 98.75% detection at 0.19 false positives per hour over 10.7 hours of unrelated speech. Not a number anyone liked the look of.Migration
The stored preference is a boolean today. A
truebecomes always listening and afalsebecomes push to talk, so nobody's microphone changes behaviour on upgrade. Whatever reads the old key has to keep understanding it, or a storedtruesilently reads as mode 0 and the microphone stops opening for someone who never touched a setting.