Repository navigation
ui-react: announce a pending approval in conversation mode and accept only a user-attributed spoken refusal #114
Description
Context
Part of #54. docs/decisions/voice-permission.md binds the permission-specific client behaviour: voice can refuse, never grant. The approved two-engine architecture (#317/#962), the adapters (#957 / #1008, #958 / #2063, #959 / #2070), the conversation client (#960 / #2142) and this issue's permission implementation (#2145, 9423920fcd696c57156a20424789e871066c21f4, with 39 real pinned offline browser scenarios and four failing/restored permission mutations) are delivered, as are the Gemini setup corrections #2356 and #2358 (#2549, 5b476a74). #110–#113 and #316/#317 supplied the completed policy and design prerequisites. The maintainer's reserved basic fallback listening pass is now complete on both engines and both output routes, as recorded in the completion receipt below. Broader qualification remains under #961; no exact-speech or spoken-attribution capability is promoted.
What ships, and what the listening pass therefore tests:
- No exact permission speech. GPT-Live reports
exactPermissionSpeech: "unsupported"and Gemini"unproven"; the server announces only when an engine reportssupported(packages/ui-server/src/ws/conversation.ts:863). The approved R1 fallback ships instead: one local earcon, the visual card and the strip line (quiet,packages/ui-react/src/voice/conversation-permissions.ts:54-68). - No attributable spoken refusal yet. Both adapters emit recognized input with
origin: "unknown"(packages/ui-server/src/voice/gemini-conversation.ts:163-166;packages/ui-server/src/voice/gpt-live-conversation.ts:161), and the refusal guard ignores any input whose origin is notuser(input,packages/ui-react/src/voice/conversation-permissions.ts:109-118). On the shipped engines, a spoken "brain, stop" therefore does not deny; refusal goes through the screen.
Binding rulings. 2026-10-05: interaction design approved (design comment §1–§8): R1 visual-only fallback without exact-copy speech, R2 the spoken ambiguity line, R3 the headphones notice with spoken refusal kept on. 2026-10-16: asynchronous tool calls genuinely continue during voice conversation, and the ordinary chat UI keeps showing progress and interactive elements throughout. 2026-10-17: protected provider access and numerical spend limits for live work are authorized under #961 (US$100 total; per-session US$5 and 10 minutes); count this pass against #961's cumulative reservation.
Design input
Consume the voice conversation proposal recorded by #316. It separates connection from microphone capture and distinguishes already-denied posture actions from genuinely pending approvals. Its interaction choices are proposals, not a speech-architecture ruling. Use its speech ownership, interruption, once-only announcement, input-routing and correlation rules for this permission-specific implementation; defining the wider conversation flow stays outside this issue.
The design requires refusal recognition during announcement playback without feeding the announcement's own words back into recognition. A scoped refusal takes priority over ordinary question/message binding and uses original recognized input before pronunciation overrides. Multiple live approvals in the selected session's current turn are all denied on an ambiguous refusal snapshot; later requests are not silently added. Keep the existing visual grant controls and request kind/rememberability behavior.
Scope
All spoken-copy requirements below are conditional on a qualified deterministic host-owned exact-copy path. Without it, the approved earcon/card/strip branch governs; a generated or retained script is not proof that the user heard it. Current remaining work is the reserved qualification of the delivered branch, not a new speech-path implementation.
- When a
tool_approval_requestframe arrives during a voice session, a qualified host-owned exact-copy speech path may speak the announcement described below. Without that capability, the approved local earcon, visual card and strip fallback applies; this issue does not require unproven engine-paraphrased speech. The conditional announcement is built from the host-approved matched-pattern
effect (currently carried asdescriptionfor command approvals): what
class of thing is about to happen, that the command is on screen, and the one
answer voice can give. Roughly ten seconds, flat in the payload size, and it
never reads the payload. A free-text description is not automatically a
vetted speakable effect; use the tool-name fallback without approved effect
provenance, and let ui-sdk: decide the live voice architecture and speech seam #317 assess the required transport. - A two-word refusal phrase, recognised only while a request is pending —
the voice analogue of D36's focus-scopeda/d
(docs/decisions/design-kit.md, "D36: single-key shortcuts are focus-scoped"). Outside that window it is ordinary
transcript text and reaches the composer like any other words. - A recognised refusal sends
tool_denialwith a message naming the phrase,
and is attributed to the voice channel (ui-server: an approval decision records the channel it was made on #113). - Ambiguity resolves as denial and the user hears that it was taken as a
refusal: a low-confidence transcript, a partial match, overlapping speakers,
or the phrase heard while more than one request is pending. - Under the enforced voice posture with no grant surface, ungrantable actions
are already denied: announce that refusal without inventing a pending request.
Ordinary chat with voice active can still have genuine pending approvals. - On no answer to a pending request, nothing further is said. When the turn
lapses on its budget, the server drains it as a denial; say once that the
action did not happen and must be asked for again when the user can look.
Reconcile the on-screen card; do not claim an expired request is still waiting. - The approval card is unchanged and remains the only surface that can grant.
Out of scope
- Any spoken grant, for any tool, at any payload size. There is no affirmative
phrase and none is added here — that is the decision, not an omission. - "Always allow" by voice. It is refused server-side and the client offers it
nowhere. - Defining turn-taking, endpointing, barge-in or wake words. Spike: voice phase 2's conversation model — turn-taking, barge-in and the screen #316 owns the
conversation design; this consumes its permission-specific interaction rules. - Selecting a speech architecture or provider (ui-sdk: decide the live voice architecture and speech seam #317, downstream of Spike: voice phase 2's conversation model — turn-taking, barge-in and the screen #316).
- Implementing the wider
AskUserCardconversation flow proposed by Spike: voice phase 2's conversation model — turn-taking, barge-in and the screen #316
under D38 ruling 1; this issue owns the permission announcement/refusal portion. - Speaker identification, and voice as an authentication factor — explicit
non-goals of Epic: voice phase 2 — a conversation rather than tap-to-dictate #54.
Scoped live listening evidence — 2026-10-27
The GPT-Live basic permission fallback listening checks passed on both headphone and speaker playback. For each route the maintainer heard exactly one short earcon, saw the genuine ordinary-turn approval card, and used screen Deny to obtain the host-confirmed result “The request was denied. No command ran.” Alert-only subchecks used a muted microphone.
Open-microphone refusal-boundary checks also passed on both routes. Speaking the refusal phrase elicited a conversational response from GPT-Live while the card remained pending; subsequent screen Deny resolved it. The headphone check confirmed microphone continuity. During the speaker reply the maintainer stayed quiet and observed no response to its own playback in that trial. This is the expected current unknown-attribution fallback, not a successful voice-channel denial or a general echo-isolation qualification.
The task backend was scripted, with no backend-model call or command execution. Experimental frontend source: 0f0fd07538c27dbca64d30d5b2a5de0467b0c90f. These results complete the scoped GPT-Live basic fallback listening portion. Duplicate/reconnect/race and broader qualification trials remain separate; Gemini's receive correction #3061 is merged and its bounded post-merge digital audio probe succeeded; the maintainer's Gemini physical listening checks are now next. Keep this issue open and do not promote provider capabilities or release qualification.
Acceptance criteria
-
Pending approval uses the approved capability branch: without a qualified deterministic exact-copy path, one local earcon plus the visual card/strip appears and no model-paraphrased permission speech is emitted. For a qualified exact-copy path, its generated announcement includes only the approved effect/tool label and no command payload. The current implementation uses the fallback; hand qualification must test what actually ships.
-
The refusal phrase resolves a pending request as
tool_denial, proven
against a fake ASR stream. -
The same phrase spoken while no request is pending reaches the composer
as text and resolves nothing. -
A low-confidence or partial recognition of the phrase resolves as
deny,
not as "ask again" — asserted. -
No spoken input, of any wording, can produce an
allow. A test enumerates
affirmative phrasings and asserts none of them resolves a request. -
Silence leaves the request pending and emits nothing further until the
turn’s budget expires. Expiry resolves it as denial; the spoken feedback
and on-screen state do not claim that the expired request is still waiting. -
Tests use a fake speech provider and a fake ASR stream; nothing needs a
key or the network. -
Heard by hand once by the maintainer, since a spoken interface that was
never listened to has not been designed. Said so in the PR. -
Each request is announced at most once across duplicate frames, interrupted playback and reconnect; resolved requests are not announced from a stale queue.
-
Refusal remains recognizable during announcement playback, and the generated announcement itself cannot trigger the detector.
-
An ambiguous refusal with multiple pending requests denies every request in the correlated current-turn snapshot, while other sessions and later arrivals remain untouched.
-
Visual-grant/refusal races and late callbacks report the host's actual result. Sending a denial alone is not treated as confirmation; disconnection shows uncertainty until reconciliation.
-
Host terminal/timeout reconciliation removes expired approval controls even when no tool result is delivered, retaining the resolved receipt and correct spoken outcome.
-
Approved design (2026-10-05, design comment §1–§8): an action already denied by the voice posture produces one outcome receipt and no card, refusal window or invitation to refuse.
-
With each §7 evidence item absent from the fake provider, its fallback ships: no exact-copy speech path means earcon plus visual card and strip line, never an engine paraphrase; missing confidence flags treat every phrase-like candidate as ambiguous; unproven echo isolation shows "Refusal by voice needs headphones with this service." on entry and keeps spoken refusal enabled.
-
An ambiguous, user-attributed refusal records the approved script "I couldn't hear that clearly, so I took it as a refusal. The action was denied." and the card shows
heard unclearly · taken as refusal. Spoken delivery remains subject to the exact-copy capability/fallback rule; unknown acoustic origin cannot authorize a denial. -
The maintainer's hand pass covers both speaker and headphone playback.
-
Existing permission cards and other chat interactions remain responsive during live audio and asynchronous host work; sending a voice refusal does not freeze the chat UI or replace host-confirmed outcomes.
Verification
Keyless proof for the implementation is delivered by #2145 (bun run test, bun run typecheck, bun run lint; nothing needs a key or the network).
Reserved maintainer listening pass, on a real device, with both configured services, on speaker and on headphones, under #961's authorization and spend accounting:
- Trigger a genuine pending ordinary-turn approval (a confirm-pattern command) in a voice session.
- Hear the delivered fallback: exactly one earcon per request, the card and strip shown, and no spoken grant prompt or model-paraphrased permission copy. Exact spoken copy is tested only on an engine that reports
exactPermissionSpeech: "supported"; neither does today. - Check that the chat and card controls stay usable and that asynchronous host work keeps progressing while audio plays.
- Say "brain, stop" while the request is pending. With
origin: "unknown", confirm it does not deny, and refuse on screen instead; confirm the Actions record shows the host-confirmed outcome. Record each engine's input-attribution capability as observed. Only where an engine is qualified to reportorigin: "user"is a voice-channeltool_denialexpected, and then the ambiguity line andheard unclearly · taken as refusalcard are checked too. - Note whether assistant output leaks into recognition, on speaker and on headphones.
- Publish only sanitized pass/fail/unknown evidence on this issue and ui: qualify both live conversation engines on real devices before activation #961: no credentials, no recordings with personal content, no deployment details.
A pass of the fallback qualifies the fallback; it does not qualify the unavailable speech or attribution capability, and the record says which one was tested.
Contract impact
The existing voice-channel tool_denial shape is the refusal transport. #317 must assess whether host acknowledgement, announcement receipts or terminal/reconnect reconciliation need additional contract changes; do not assume #113 is the only possible impact. Any resulting machine-surface change requires the integration-contract update and versioning rules.
2026-10-24: replaced the old hand-pass instruction (listen to the announcement, refuse it by voice, confirm a voice-channel denial) with the current listening protocol: test the delivered earcon/card/strip fallback, record attribution capability, and verify that unknown-origin speech cannot deny. Folded the delivered prerequisites and rulings into Context; removed the dated handoff paragraphs (preserved in a comment). Scope, safety criteria and the reserved listening requirement are unchanged.
Gemini interactive finding — 2026-10-28 UTC
After #3061's receive correction, the maintainer heard Gemini replies but ordinary follow-up speech after completed playback closed capture and required Resume. The adapter's input-finalization state was unsatisfied; #3064 owns the offline reproduction and bounded resolution. Do not attribute this to missing provider metadata alone: continued appended PCM can also clear the observed flag. This is separate from the delivered empty-envelope fix. No Gemini permission-listening completion is claimed; avoid repeated manual tests while the continuity finding is investigated.
The maintainer also reports that Gemini feels faster than GPT-Live. This is a subjective listening observation, not a measured latency comparison. The previously recorded scoped GPT-Live fallback results remain valid.
Gemini SPDIF permission-fallback listening result — 2026-10-28
The maintainer heard the alert chime and saw the approval card on SPDIF speakers with a directional microphone. Speaking the refusal phrase produced an audible conversational reply and a generated-speech entry; it did not resolve the approval. Screen Deny then produced the host-confirmed scripted result that the request was denied and no command ran. The listening report does not explicitly establish chime deduplication across retries/reconnect or general echo isolation.
Exact production source was f4fd0de35b97a2e004629fc813953a6163bba7b9. This fresh permission-only harness used a scripted ordinary-turn permission backend, no model backend call and no command execution. Its repeating waiting text and no-command receipt were explicit test output, not prescribed production copy. The live Gemini response was actual generated conversation speech. Voice opened at 20:10:45.428 UTC, recognition arrived at 20:11:06.304, and the connection settled at 20:11:24.566. The scripted status confirms one request, no pending card and denied-no-command.
Important qualification boundary: this permission-only fixture advertises contextual tasks unsupported. It deliberately exercised the retained manual-review path, which saves non-interim recognition into the composer; the maintainer observed the refusal phrase there and found it confusing. The source's automatic contextual path keeps recognition separate and returns before draft saving. Therefore this is not evidence that the newer automatic path inserts recognition into a typed draft, nor a second speech recognizer. #3050 retains the presentation finding. This pass does not qualify automatic model-driven permission admission/policy parity, unavailable exact permission speech or attributable spoken denial.
The scoped Gemini speaker fallback check passed. Gemini headphones remain pending, and broader matrix/race/echo checks remain separate. No capability was promoted. The prior ordinary-follow-up blocker is superseded for this targeted test by the merged fixes and both-provider physical concurrency receipts; it is no longer a reason to pause this listening pass.
Reserved fallback listening pass completed — 2026-10-28
The maintainer reports the fresh Gemini headphone check completed and behaved as instructed: alert/card, open-microphone refusal phrase without a voice permission decision, screen Deny and denied/no-command feedback. The server's bounded metadata confirms one Gemini opening at 20:30:41.565 UTC, ready at 20:30:42.041, recognized input at 20:30:54.984, generated output and normal final epoch teardown/settlement at 20:31:04.474. Physical audibility and the visible Deny result are maintainer observations; this scripted backend did not retain a separate durable denial receipt in the inspected message-block/activity tables, so none is claimed. No model backend or command execution occurred.
Combined scope: GPT-Live's earlier speaker/headphone fallback checks and Gemini's current SPDIF/headphone checks complete this issue's reserved basic listening pass. The shipped branch is one local earcon with the visual permission card/strip and screen authority. Neither engine gains exact permission speech or trustworthy user-origin attribution from these results. Spoken conversational acknowledgement is not a permission decision. This does not qualify general echo isolation, duplicate/reconnect/race matrix cells, automatic model-driven policy parity, or interruption recovery; those retain their own evidence requirements under #961 and the linked work.
The recognized phrase appearing in the composer was traced to this fixture's explicitly non-contextual legacy review profile and recorded under #3050. Repeating waiting text/no-command narration is scripted test output, not required product copy. Neither observation invalidates the scoped alert/card/screen-Deny listening result.
The fresh fixture has already reached its finite owner lifetime and exited. Cleanup confirms no retained owned mount, loop attachment, loop creation or backing file, no cleanup errors, and unchanged user-namespace quota. All voice/backend reservations are zero. No provider capability is promoted. The full live qualification issue #961 remains open.
Activity
schlessera commentedon Sep 25, 2026
Labelled
needs: designandneeds: decision2026-09-25: this issue is blocked by #316 (waiting on a design answer) and #317 (waiting on a maintainer ruling). The labels mirror those blockers. Remove them together withblockedwhen both close.schlessera commentedon Sep 28, 2026
Decision dependency review (2026-09-28): The September 25 comment confirms needs: decision was inherited from #317, not an independent choice here. The maintainer has since ruled that architecture must follow #316's live-conversation design. Remove the inherited label; #114 remains blocked by #316/#317 and needs: design. Do not reopen the already binding voice-refusal-only permission rules. It is not agent-ready.
schlessera commentedon Sep 30, 2026
Needs design — prompt refreshed 2026-09-30 after #657. Permission policy is already decided. #316 has recorded the conversation proposal in docs/plans/voice-conversation.md; #317 has selected both live engines and the common host-owned contract, while delivery remains a dependency. This issue still needs the reviewed permission interaction realized against that ruling. The prompt covers how that interaction reaches the existing approval surface without reopening spoken grants or assuming a separate TTS provider.
Paste to Claude:
Source pointers verified against remote
maine0875269b21b707482545be9e94901acb897893bon 2026-09-30; search the named anchors if lines move.2026-10-01: Refreshed denial, approval-request and StartTurnRequest citations in the existing prompt. The phase-1 driver citation still matches. Preserve #317 as the unresolved architecture blocker and the existing design and permission constraints. Changed pointers checked at main
35fb78d40542426b9bac4a1c3a2a32cf43803837; other dated evidence retains its original provenance.2026-10-01: Reverified the denial/approval frame and StartTurnRequest anchors after the form-tool merge at
b333db6fb6fba30e7a2a92186822e78b782c86d4. The #317 architecture dependency and needs: design remain.2026-10-01 triage at main
619ee2b2d48409fbd9d6a74ccc86bd73507362ac: Refreshed StartTurnRequest after the autonomous-turn additions. Denial/request frame anchors still match; #317 and the interaction design remain unresolved, with voice refusal and timeout/drain semantics unchanged.2026-10-01: Refreshed the denial and approval-request frame anchors in the existing prompt. #317 and the architecture-dependent design remain unresolved; permission and timeout rules are unchanged. Source inspected at main
3f0870d5338c9139f9bb53050847c23974662a00.schlessera commentedon Sep 30, 2026
#316's design proposal is recorded and merged in #657. This body now links it and incorporates its permission-specific interruption, original-input refusal routing, once-only announcement, current-turn multi-request snapshot, visual-grant race, host-confirmed feedback and stale-card/terminal reconciliation requirements. The effect phrase must have host-approved provenance; a free-text description alone is not sufficient.
Removed the completed #316 dependency. #317 still blocks implementation and must assess the speech architecture plus any additional contract needs.
blockedandneeds: designremain for the architecture-dependent implementation design; this handoff does not select a provider/seam or mark implementation agent-ready. The existing keyless runtime and maintainer real-device listening evidence remain required for this implementation issue.schlessera commentedon Oct 1, 2026
2026-10-01 triage — Refreshed denial, approval-request and StartTurnRequest citations in the existing prompt. The phase-1 driver citation still matches. Preserve #317 as the unresolved architecture blocker and the existing design and permission constraints. Source inspected at main
35fb78d40542426b9bac4a1c3a2a32cf43803837.schlessera commentedon Oct 1, 2026
2026-10-01 triage: repaired the existing design prompt's moved denial/approval frame and StartTurnRequest anchors at
b333db6fb6fba30e7a2a92186822e78b782c86d4. The recorded conversation proposal remains an input; #317 and the architecture-dependent design are still required. No permission, architecture or readiness ruling is changed.16 remaining items
schlessera commentedon Oct 16, 2026
Current draft PR #2145 head
fc033a77e28487d92a41b9274266219178fdc49btargets UI parent2ded64053f2ba6d952567d5defde82cf5b9886ffin native stack #2135. It contains the permission implementation and an additional recovery correction. The rebase preserves main-model/title-usage accounting alongside the scoped voice-posture outcome handler. All moved source citations are anchored; milestone remains 0.44.0.The earlier confirmation 38906390560 exposed a real regression: applying a voice receipt changed transcript tool status before ordinary result/recovery routing could recognize the pending restored card. This hid terminal closure and cross-page answer evidence. Receipt phases now lock decisions through
awaitsDecision, while ordinary host results retain ownership of transcript status. The existing restored-store tests fail on the intendedendedassertions before this change and pass after it. The visual-grant race still verifies the confirmed grant receipt, visible wording and disappearance of approval controls.Current candidate evidence:
check:pr --base feat/960-voice-ui-42413928completed all initial gates and 699 focused tests / 4,420 assertions. Its separate import-graph feedback was deliberately interrupted after those passes while waiting for workspace access; the entire preflight is not claimed green. The earlier browser setup timeout and stale build import failure remain setup failures, not behavioral mutation receipts; rebuilt inputs and the direct native test provide the passing evidence above.The fixes and tests are pushed. GPT's next dependency-order confirmation 38933150766 is still waiting for a hosted runner; upper native aggregate support remains #2141. Keep this PR draft until lower layers land and its resulting current candidate can receive complete hosted confirmation. The earlier failed hosted run is not a passing receipt, and the local checks do not replace its selected categories.
This remains
Refs #114. The maintainer-reserved listening check remains open under #961, alongside provider access and an explicit numerical spend limit before any paid measurement. No live provider or device qualification was performed.schlessera commentedon Oct 16, 2026
2026-10-16 dependency reconciliation: removed the canonical #958 blocker after verifying its completed adapter output and merged #2063 (
cd5e8bb04b0d0e24f1a6c4d4f73e17f1b3fe4908) on inspected main. The #958 closure records source-bound selected confirmation separately from queued actual-main proof. Remaining #959/#960 and permission/qualification obligations stay open; retain blocked and needs: human. Adapter delivery provides no live samples, numerical spend authorization or reserved listening completion.schlessera commentedon Oct 17, 2026
Merged #2145 as
9423920fcd696c57156a20424789e871066c21f4. Verified on remote main: parent85873db77bf09143c51818255d2d4733a8eb1f67, tree56ba27792b64c808b81df0412b4b49344bb7a96a, exactly equal to the fully passing tested tree. PR and advanced issue #114 milestones read back as 0.44.0; #114 remains open under Refs for the reserved listening requirement. All four voice implementation layers are now delivered. The post-merge automatic run is separate from the successful pre-merge confirmation; no later run result is inferred.schlessera commentedon Oct 22, 2026
Maintainer ruling 2026-10-22: moved out of 0.44.0, so this is now unscheduled. The code was delivered by #2145 (9423920). What remains is the maintainer's speaker and headphone listening pass with both services, under the spend authorization recorded in #961. The Gemini half of that pass waits on #2358, which is now in 0.44.0. This issue stays open with
needs: humanandBlocked by #2358. Schedule it again when #2358 has landed and the listening pass can be booked.schlessera commentedon Oct 22, 2026
2026-10-22 triage at main
aa8c8966b59f031f054f4876dbca84048b40ae4f: #2358 is completed through merged PR #2549, squash5b476a747769e6c8703252a1fb17a6d2ac39fb13, verified in the inspected main ancestry. The actual Gemini message boundary now bounds and synchronously decodes Uint8Array/ArrayBuffer JSON. This retires the binary-frame code-delivery dependency only; it supplies no live-provider handshake, audio, device, acoustic-attribution or listening result. Existing numerical authorization/reservation accounting and the reserved speaker/headphone listening requirements remain unchanged. No live call or capability activation was performed.schlessera commentedon Oct 22, 2026
2026-10-22 acceptance reconciliation at a310317: aligned the opening scope and announcement/ambiguity criteria with the already approved R1 earcon/card/strip fallback and the delivered original-user attribution guard. The issue must not demand unqualified model-paraphrased speech or treat unknown acoustic origin as a refusal. #2145 is merged and ancestral; this changes no runtime or approved policy. The unscheduled needs: human handoff remains the reserved speaker/headphone listening pass with both services under #961’s existing authorization/accounting.
schlessera commentedon Oct 24, 2026
2026-10-24: the body was consolidated and its Verification rewritten to the current listening protocol. The text removed from the body is preserved here verbatim:
Removed body text
Delivered prerequisite: #2358 / PR #2549 (
5b476a747769e6c8703252a1fb17a6d2ac39fb13).Delivered prerequisite: #957 (PR #1008,
533c0a7c).Context
docs/decisions/voice-permission.mdbinds the permission-specific client behavior: voice can refuse, never grant. The approved two-engine architecture, adapters and conversation controls are delivered, and #2145 delivered this permission implementation as9423920fcd696c57156a20424789e871066c21f4. The issue remains open for the explicitly reserved maintainer listening pass, rather than further implementation or a new interaction/architecture ruling.Current source at inspected main
b7f6360a86be68f7da8cefa93c316ecc0bf9bf42iscreateConversationPermissions(packages/ui-react/src/voice/conversation-permissions.ts:20-22): the exact-copy speech path is absent, so the approved local earcon/card/strip fallback is used (quiet, same file:54-68). The original-input refusal guard (input, same file:109-120) requires user attribution; unknown acoustic origin cannot authorize a denial. The earlier tap-to-dictate description and outstanding adapter/design dependencies below are historical inputs, not current implementation gaps.#110–#113 and #316/#317 supplied completed policy/design prerequisites. The scope and criteria below preserve what was approved and delivered; they do not claim live-provider/device qualification. The current remaining handoff and prior proof identities are recorded below.
Verification
bun run test bun run typecheck bun run lintPlus one hand pass on a real device: trigger a confirm-pattern command in a
voice session, listen to the announcement, refuse it, and confirm the Actions
record shows a voice-channel denial.
Body updated 2026-09-25: the blocker was epic #54, which cannot close while this sub-issue is open, so
blockedcould never clear. It now names the two issues filed from #54's open questions, #316 (conversation model) and #317 (output seam). D36 citation moved todesign-kit.md:1799.Updated 2026-09-28: removed the inherited needs: decision label. The maintainer deferred speech architecture to #316 in the ruling on #317; this issue has no separate architecture choice. Existing voice-permission rules remain binding. blocked and needs: design remain; restore a decision label only if the resulting design exposes a distinct unresolved choice here.
Updated 2026-09-30: added the self-contained design prompt; aligned architecture sequencing, already-denied versus pending cases, and timeout wording with the current voice-permission record. #316/#317 remain blockers.
Updated 2026-09-30 after #316: linked the recorded conversation proposal, incorporated its permission-specific input routing, playback, identity/race and stale-card requirements, and removed the completed design-production blocker. #317 remains the blocker.
needs: designremains for the architecture-dependent implementation design; this update does not approve an architecture or mark the implementation agent-ready.Current architecture input — 2026-10-04
#317 selected Gemini Live and OpenAI GPT-Live behind one host-owned adapter contract; its complete specification and merged #962 supply the durable design inputs on main. They describe a future interface; implementation remains in #957–#959. #957–#961 divide contract, adapters, conversation controls and proof/adoption work. The existing prompt is refreshed to consume this selection. Keep the actual contract/adapter dependencies #957–#959 for required runtime delivery and
needs: designfor the reviewed permission interaction. Refusal-only policy, effect provenance, host confirmation, echo-safe recognition, expiry and real-device listening requirements remain unchanged. No architecture choice is reopened and no speech capability is inferred from the current dictation driver.2026-10-04 prerequisite delivery: #317/#962 is merged and verified on remote main. Its architecture record is complete; runtime contract/adapters #957–#959 and this issue’s permission-specific interaction design/listening proof remain required. No dictation capability is promoted to conversation support.
2026-10-05 maintainer ruling: the interaction design is approved (R1 visual-only fallback without exact-copy speech, R2 spoken ambiguity line, R3 headphones notice with refusal kept on). Acceptance criteria extended from its §8.
needs: decisionremoved; stillblockedby #957, #958, #959.2026-10-06 triage at main
543231e1: the design and residual choices are approved (2026-10-05 decision comment), so the 2026-09-28 note aboutneeds: designabove is historical. #110, #111, #112, #113, #316 and #317 are all closed as completed; theuse-dictation.ts:26citation still matches. Addedagent-readyalongsideblockedunder the maintainer's 2026-10-06 ruling that a fully specified, dependency-blocked issue carries both; it supersedes the 2026-10-05 comment's "notagent-readyuntil" line. #957, #958 and #959 are still open, soblockedkeeps this out of selection. The maintainer's real-device hand pass is an end-stage check and does not block coding.2026-10-06 triage at main
825cd044: #957 merged as PR #1008 (533c0a7c), so it is retired from the active blocker lines. #958 and #959 are still open, soblockedandagent-readyboth stay.2026-10-16 maintainer clarification: asynchronous tool calls must genuinely continue during voice conversation, and the ordinary chat UI must keep showing progress and interactive elements throughout. Added the corresponding runtime acceptance criterion without changing selected engines, approved interaction design or permission authority.
Implementation candidate and reserved verification — 2026-10-16
PR #2145 implements the approved permission slice above client PR #2142 and adapter PRs #2063/#2070. It uses
Refs #114: the issue stays open for the reserved listening evidence. Independent proof passed: 39 real pinned offline browser scenarios, four failing/restored permission mutations, 87 host/SDK tests, full build, strict types and 638-test parent-local preflight. This includes continued async work/chat interactions, attributed original input, once-only native earcons, snapshot isolation, host confirmation, visual-grant races, expiry, reconnect receipts and 320px layout.The selected providers have no proven deterministic exact permission-speech path, so the approved earcon/card/strip fallback ships. Both adapters still report unknown acoustic origin; those fragments cannot authorize a refusal. No model-paraphrased permission copy, spoken grant, real echo isolation or device qualification is claimed. Native upper-layer CI identity is tracked in #2141; no gate is bypassed.
Required maintainer step: the implementation is integrated and #961’s access/spend scope is already authorized within the recorded caps. Hand-hear the permission flow on speaker and headphones with both configured services. Trigger a genuine pending ordinary-turn approval, listen to the earcon/visual fallback, verify that chat/card controls remain usable, and inspect host-confirmed outcomes. Record whether original-user attribution permits scoped refusal and whether assistant output leaks into recognition. Unknown attribution remains closed even if the phrase sounds clear. Publish only sanitized pass/fail/unknown evidence under #114/#961, without credentials, recordings containing personal content or deployment details. Broader corpus/race measurements belong to #961 and are automated; this hand-hearing step remains explicitly reserved.
The implementation is reviewable; this handoff authorizes no paid/live calls. Removing agent-ready and adding needs: human preserves the open acceptance/verification boundary.
2026-10-16 triage at main
4ba792473152c89eb74c286fc914e54ca1dae8fc: removed the fulfilled #958 Gemini adapter blocker after reading its closure and merged #2063 /cd5e8bb04b0d0e24f1a6c4d4f73e17f1b3fe4908. #959/#960 and the permission candidate remain unfinished; retain blocked and needs: human for the reserved qualification/listening handoff. Keyless adapter proof is not live provider/device qualification.Maintainer ruling — 2026-10-17: The known ActionCard intermediate-opacity and final-disappearance timing findings in #1946 remain under evidence collection and are non-blocking for other issues. Matching failures must retain their actual failed identities and explicit attribution; this is accepted animation risk, not a repair or a green CI claim. Other proof and merge requirements remain applicable.
The retained ActionCard 713.9 ms final-disappearance failure does not block this voice-permission work. Adapter/client integration, current candidate qualification, native CI identity support and the explicitly reserved real-device/listening requirements retain their independent scope. No live provider qualification or completion is inferred.
2026-10-17 prerequisite delivery: #959 merged as #2070 (
18a65206d4ed5f6fc4790728293ac2ce72858026) and #960 merged as #2142 (85873db77bf09143c51818255d2d4733a8eb1f67). Their active blocker lines are retired. Permission implementation #2145 is now ready on the merged UI base; selected hosted proof is running. Keep this issue open with needs: human for the explicitly reserved listening/qualification evidence; no live provider or device result is inferred.2026-10-17 implementation delivered: permission PR #2145 merged as
9423920fcd696c57156a20424789e871066c21f4, on UI squash85873db77bf09143c51818255d2d4733a8eb1f67. Its complete selected confirmation run 39038370902 passed all 14 product jobs and the aggregate, including 14,478 unit tests and all 39 real pinned conversation/permission browser cases. The actual squash tree56ba27792b64c808b81df0412b4b49344bb7a96aexactly matches the tested tree and is verified on remote main. Both adapter PRs #2063/#2070 and client PR #2142 are also merged; there are no remaining code-delivery dependencies in this stack.Keep this issue OPEN with needs: human. The maintainer must hand-hear the announcement/refusal flow on speaker and headphones with both selected services, under separately authorized access and numerical spend limits coordinated in #961. Use the approved earcon/card/strip fallback, verify continued chat interaction and actual host outcomes, and record sanitized pass/fail/unknown evidence for attribution and output leakage. Unknown acoustic origin still cannot authorize a denial. The merge does not satisfy this reserved listening criterion.
2026-10-17 qualification prerequisite evidence: the maintainer authorized protected provider access and the numerical qualification limits recorded in #961 (US$100 total; US$40 Gemini voice, US$40 OpenAI voice, US$20 backend; US$5 and 10 minutes maximum per session). Authorization no longer blocks this listening step. Unmodified current-main live probes reached GPT-Live ready once, but Gemini setup failed twice before audio because the function parameter schema is rejected; #2356 tracks that correction. No audio, backend work or permission listening pass was performed. No headphone route is currently available. Keep this issue open with needs: human; no safe attribution, exact speech or real-device result is inferred. The reserved maintainer listening pass and #961 automated qualification remain outstanding.
2026-10-21 triage: reconciled the opening context and listening handoff with the delivered permission implementation, current earcon/original-input guards and the existing October 17 spending authorization. #2356 is delivered; #2358 remains an open Gemini binary-frame qualification obstacle under #961 and blocks completion of the required both-service listening pass; independent speaker/headphone preparation and the other service’s authorized checks can proceed. Added the canonical dependency and blocked label. Keep needs: human for the reserved maintainer speaker/headphone listening evidence; no paid call, acoustic attribution proof or listening pass was performed.
2026-10-22 triage at main
aa8c8966b59f031f054f4876dbca84048b40ae4f: #2358 is completed through merged PR #2549, squash5b476a747769e6c8703252a1fb17a6d2ac39fb13, verified in the inspected main ancestry. The actual Gemini message boundary now bounds and synchronously decodes Uint8Array/ArrayBuffer JSON. This retires the binary-frame code-delivery dependency only; it supplies no live-provider handshake, audio, device, acoustic-attribution or listening result. Existing numerical authorization/reservation accounting and the reserved speaker/headphone listening requirements remain unchanged. No live call or capability activation was performed.2026-10-22 acceptance reconciliation at a310317: aligned the opening scope and announcement/ambiguity criteria with the already approved R1 earcon/card/strip fallback and the delivered original-user attribution guard. The issue must not demand unqualified model-paraphrased speech or treat unknown acoustic origin as a refusal. #2145 is merged and ancestral; this changes no runtime or approved policy. The unscheduled needs: human handoff remains the reserved speaker/headphone listening pass with both services under #961’s existing authorization/accounting.
[-]ui-react: speak the approval announcement and accept a scoped spoken refusal[/-][+]ui-react: announce a pending approval in conversation mode and accept only a user-attributed spoken refusal[/+]