Skip to content

H-6763: Add the voice interview experience - #9380

Open
kostandinang wants to merge 31 commits into
kostandin/h-6763-voice-preview-reliabilityfrom
kostandin/h-6763-voice-interview-ux
Open

H-6763: Add the voice interview experience#9380
kostandinang wants to merge 31 commits into
kostandin/h-6763-voice-preview-reliabilityfrom
kostandin/h-6763-voice-interview-ux

Conversation

@kostandinang

@kostandinang kostandinang commented Aug 27, 2026

Copy link
Copy Markdown

🌟 What is the purpose of this PR?

This PR turns the H-6763 composer microphone into a persistent, half-duplex interview experience. Domain experts can hear canonical Brunch questions, answer by voice, review finalized transcripts, correct an answer, pause or interrupt playback, fall back to typing, and end the interview without losing the surrounding conversation.

It is stacked on #9378 and keeps provider-specific OpenAI transport, transcription, and synthesis in the Petrinaut website host while exposing a provider-neutral interview stage through Petrinaut. The remaining upstream ask-emission contract and credentialed end-to-end witness are called out below rather than hidden behind the UI.

🔗 Related links

🚫 Blocked by

  • #9378
  • An accepted Brunch/Flue contract that emits the terminal brunch_ask before background sweep/settlement. The controller consumes an emitted ask immediately, but current upstream ordering can still delay emission; the UI deliberately does not claim answer readiness before a canonical ask exists.

🔍 What does this change?

  • Adds a provider-neutral interview-stage API to Petrinaut and an OpenAI-backed implementation in @apps/petrinaut-website.
  • Keeps one controller and WebRTC session alive across full sidebar, compact sidebar, and detached bottom-bar presentations. Placement changes do not reconnect or reopen the microphone; recoverable errors reopen the full stage.
  • Separates interview-answer capacity from generic chat readiness, speaks emitted brunch_ask text before settlement completes, and records finalized-answer → question-visible/spoken/answer-ready latency.
  • Implements explicit half-duplex turn taking: playback completes before capture opens, Interrupt and speak cancels playback first, and Done speaking closes capture while provider VAD remains the single commit authority.
  • Adds a real RMS microphone meter whose audio context is resumed from the start action, plus reduced-motion and text equivalents.
  • Distinguishes provisional and committed transcripts, gates redo/edit controls on actual correction capacity, preserves rejected correction drafts, and clears prior-answer state when an interview ends.
  • Keeps the current question and keyboard composer visible, protects active interviews from Clear Chat, and presents authoritative covered/still-exploring topics without speculative projection or question counts.
  • Adds disclosure and consent, accessible responsive controls, Petrinaut user documentation, and the required Petrinaut changeset.

Pre-Merge Checklist 🚀

🚢 Has this modified a publishable library?

This PR:

  • modifies an npm-publishable library and I have added a changeset file(s)

📜 Does this require a change to the docs?

The changes in this PR:

  • require changes to docs which are made as part of this PR

🕸️ Does this require a change to the Turbo Graph?

The changes in this PR:

  • do not affect the execution graph

⚠️ Known issues

  • The accepted upstream ask-before-settlement contract described in Blocked by is not yet available, so upstream work can still delay when the next question is emitted.
  • The authoritative projection contract is not available. This PR therefore leaves Petrinaut's main canvas as the model surface instead of rendering a speculative preview.
  • No credentialed OpenAI + Brunch browser witness was available. Browser review covered start, responsive layout, microphone-error recovery, keyboard fallback, Clear Chat protection, and accessibility, but not real synthesis quality or end-to-end turn latency.

🐾 Next steps

  • Establish terminal-ask emission before background settlement and capture the requested answer-to-next-question latency witness.
  • Run a credentialed expert witness covering interruption, vocabulary rendering, microphone feedback, correction flows, and presentation transitions.
  • Connect the emerging model to Petrinaut's main canvas when the authoritative projection contract is available.

🛡 What tests cover this?

  • yarn workspace @apps/petrinaut-website test:unit — 18 files and 130 tests covering the realtime session, turn controller, speech playback, responsive interview controls, diagnostics, and mocked integration boundary.
  • yarn workspace @hashintel/petrinaut test:unit — 28 files and 220 tests covering the host API, conversation lifecycle, answer queue, Clear Chat protection, and panel integration.
  • Brunch transport and app unit suites cover interview-answer routing and terminal ask behavior.
  • Website and Petrinaut TypeScript/ESLint checks cover the affected packages; formatting and Markdown lint pass for the changed files and user guide.
  • Browser checks cover desktop and 390 px layouts, recovery behavior, and the affected sidebar's WCAG A/AA axe audit.

❓ How to test this?

  1. Configure the Petrinaut website with the OpenAI voice preview enabled, a valid OpenAI API key, and a Brunch chat endpoint.
  2. Open the AI sidebar and select Start voice interview. Confirm disclosure and consent appear before microphone access, then start the interview.
  3. Confirm the canonical question is visible and spoken before the microphone opens, and that the microphone meter responds only while capture is active.
  4. Exercise Done speaking, pause/resume, minimize/expand, sidebar close/reopen, Interrupt and speak, redo, typed correction, and keyboard fallback. Confirm presentation changes alone do not reconnect or change microphone state.
  5. While an interview is active, confirm Clear Chat is disabled. Trigger a recoverable microphone or network error and confirm the sidebar reopens to the full recovery stage.
  6. Confirm correction controls remain disabled while an answer is being written down, rejected correction text remains editable, and ending/restarting does not expose the prior answer.
  7. At a 390 px viewport, confirm the full stage stays above the composer and the detached compact presentation becomes a bottom bar.

📹 Demo

https://www.loom.com/share/4592af568cf144cd9f58c0b579da6492

@vercel

vercel Bot commented Aug 27, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
hash Ready Ready Preview Aug 28, 2026 7:29am
petrinaut Ready Ready Preview Aug 28, 2026 7:29am
petrinaut-docs Ready Ready Preview Aug 28, 2026 7:29am
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
hashdotdesign-tokens Ignored Ignored Preview Aug 28, 2026 7:29am

Request Review

@CLAassistant

CLAassistant commented Aug 27, 2026

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
1 out of 2 committers have signed the CLA.

✅ kostandinang
❌ ampagent
You have signed the CLA already but the status is still pending? Let us recheck it.

@github-actions github-actions Bot added area/infra Relates to version control, CI, CD or IaC (area) area/libs Relates to first-party libraries/crates/packages (area) type/eng > frontend Owned by the @frontend team area/apps labels Aug 27, 2026
@kostandinang
kostandinang force-pushed the kostandin/h-6763-voice-interview-ux branch from 8014db4 to f7f2f10 Compare August 27, 2026 14:59
Comment thread apps/petrinaut-website/src/voice-diagnostics.ts Outdated
@semgrep-code-hashintel

Copy link
Copy Markdown

Semgrep found 1 regex_dos finding:

Ensure that the regex used to compare with user supplied input is safe from regular expression denial of service.

kostandinang and others added 4 commits August 27, 2026 21:01
Co-authored-by: Cursor <cursoragent@cursor.com>
Treat OpenAI transcript deltas as display-only so capture continues until server VAD commits the answer. Update regression coverage and user-facing documentation for the corrected behavior.

Co-authored-by: Cursor <cursoragent@cursor.com>
Document natural-pause endpointing and distinguish it from pausing capture. Consolidate the partial-transcript regression coverage and remove the duplicate implementation note.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Comment thread apps/petrinaut-website/src/main/app/voice-interview/voice-interview-control.tsx Outdated
kostandinang and others added 10 commits August 27, 2026 23:23
Define the circular microphone, waveform, icon controls, transcript states, and recovery treatment for the simplified interview surface.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Failed and in-flight answer deliveries previously reached the transcript
strip as a green Sent status, because the view inferred success from the
presence of committed text. Record the delivery outcome on the snapshot
and render Sending and Not sent for the non-success cases.

Co-authored-by: Cursor <cursoragent@cursor.com>
Recoverable errors cover chat delivery, synthesis, timeouts, and aborted
requests, so the recovery heading now follows the error family instead of
always blaming the microphone. Put the keyboard fallback first in the full
stage actions, de-duplicate covered coverage labels, keep interview
coverage in the same low-emphasis details row as technical details, and
replace the presentation and focal-icon ternary chains with selectors.

Co-authored-by: Cursor <cursoragent@cursor.com>
The realtime session emitted an unquantized RMS value on every animation
frame, and the interview stage rescanned conversation messages for
coverage on every one of those renders. Quantize the level to the
resolution the waveform can show, skip unchanged samples, and memoize the
coverage scan on the messages it reads.

Co-authored-by: Cursor <cursoragent@cursor.com>
ADR-0009 and its summaries still described a generic composer seam only
and a microphone that closes whenever Brunch handles a turn, which the
interview stage and one-answer buffering contradict. Record the shipped
seams, note that the composer control remains supported, describe the new
transcript and recovery wording in the user guide, and move the shared
sweep output schema out of the local-storage demo folder.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 2 total unresolved issues (including 1 from previous review).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 1fc5e65. Configure here.

Comment thread apps/petrinaut-website/src/main/app/voice-interview/voice-interview-control.tsx Outdated
Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/apps area/infra Relates to version control, CI, CD or IaC (area) area/libs Relates to first-party libraries/crates/packages (area) type/eng > frontend Owned by the @frontend team

Development

Successfully merging this pull request may close these issues.

3 participants