Skip to content

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

Open
kostandinang wants to merge 2 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 2 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?

Turn the H-6763 composer microphone into a persistent, half-duplex interview experience while preserving canonical Brunch speech, keyboard fallback, and the reliability work in the parent stack.

This draft is stacked on #9378.

🔗 Related links

🚫 Blocked by

  • An accepted Brunch/Flue contract that emits the terminal brunch_ask before background sweep/settlement. The transport and voice controller consume an emitted ask immediately, but current upstream ordering can still delay emission itself; the UI deliberately does not fake answer readiness before the canonical ask exists.

🔍 What does this change?

  • Adds a provider-neutral, persistent interview-stage surface to Petrinaut while keeping OpenAI connection, transcription, and synthesis behavior in @apps/petrinaut-website.
  • Uses one controller and WebRTC session across full sidebar, sidebar-mini, and detached-mini presentations. Sidebar close/reopen only changes placement; errors reopen the full stage.
  • Separates one-answer interview capacity from generic chat readiness, speaks emitted brunch_ask text before settlement completes, and measures finalized-answer → question-visible/spoken/answer-ready latency.
  • Adds real microphone RMS feedback only while capture is enabled, reduced-motion and text equivalents, explicit pause/done-speaking controls, deterministic playback cancellation before Interrupt and speak, redo, and typed correction.
  • Keeps the current question and keyboard composer visible, distinguishes provisional and committed transcripts, protects active conversations from Clear Chat, and presents authoritative completion/must-know coverage without question counts or speculative projection.
  • Adds the consent/start card, conversational status copy, responsive stage/bar behavior, accessible controls, Petrinaut user documentation, and the existing 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

  • No accepted upstream ask-before-settlement contract exists yet, as described in Blocked by.
  • The authoritative projection contract is not available, so this does not render a mock model preview; Petrinaut's main canvas remains the model surface.
  • A credentialed OpenAI + Brunch browser witness was not available. Browser review covered start, responsive layout, microphone-error recovery, keyboard fallback, Clear Chat protection, and accessibility using non-secret placeholder configuration, but not real synthesis quality or end-to-end turn latency.

🐾 Next steps

  • Establish and implement terminal-ask emission before background settlement, then capture the latency witness requested by the UX review.
  • Run the credentialed expert witness for interruption behavior, vocabulary rendering, listening feedback, and presentation transitions.
  • Add the emerging model to Petrinaut's main canvas when the authoritative projection contract is available.

🛡 What tests cover this?

  • corepack yarn workspace @apps/petrinaut-website test:unit — 120 tests
  • corepack yarn workspace @hashintel/petrinaut test:unit — 219 tests
  • Transport and Brunch app unit suites — 12 and 38 tests
  • Website and Petrinaut TypeScript and ESLint checks
  • Website, Petrinaut, transport, and Brunch app production builds
  • corepack yarn lint:format
  • corepack yarn workspace @local/petrinaut-arch-docs lint:arch-docs
  • Desktop and 390 px browser layout/recovery checks; affected-sidebar axe WCAG A/AA audit reported 0 violations

❓ 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 choose Start voice interview. Verify disclosure/consent appears before microphone permission and starting opens the full stage.
  3. Exercise minimize/expand, close/reopen the sidebar, pause/resume, done speaking, interrupt, redo, typed correction, and keyboard submission. Confirm the WebRTC session and microphone state do not change solely because presentation changes.
  4. While an interview is active, confirm Clear Chat is disabled. Trigger a recoverable microphone/network error and confirm the sidebar reopens to the full recovery stage.
  5. At a narrow viewport, confirm the stage remains above the composer and the detached mini presentation becomes a bottom bar.

📹 Demo

Not attached: the available browser environment did not have credentials for a truthful end-to-end voice recording.

@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 27, 2026 5:10pm
petrinaut Ready Ready Preview Aug 27, 2026 5:10pm
petrinaut-docs Ready Ready Preview Aug 27, 2026 5:10pm
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
hashdotdesign-tokens Ignored Ignored Preview Aug 27, 2026 5:10pm

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 commented Aug 27, 2026

Copy link
Copy Markdown
Author

Warning

This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
Learn more

This stack of pull requests is managed by Graphite. Learn more about stacking.

@cursor

cursor Bot commented Aug 27, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Touches AI assistant submission timing, WebRTC/microphone handling, and voice answer delivery while chat is streaming; mistakes could drop or duplicate interview answers or leave sessions in a bad state.

Overview
Introduces a persistent host-rendered interview stage in @hashintel/petrinaut (alongside existing composer controls), with renderInterviewStage, sidebar vs detached placement when the AI panel is closed, setActive to block Clear AI chat, keyboard focusComposer, and submitInterviewAnswer that can queue one answer while the normal chat stream is still busy.

The Brunch local-storage demo wires VoiceInterviewControl through this API instead of a small composer mic control. The voice layer is expanded into a full half-duplex interview UX: consent/start card, full stage and minimized bottom bar, pause / done speaking / interrupt, redo and typed corrections via submitInterviewAnswer, live mic level from WebRTC, explicit input_audio_buffer.commit, and coverage (“Covered” / “Still exploring”) from validated Brunch sweep completion output. VoiceTurnController drives brunch-ask playback before listening, respects canAcceptInterviewAnswer, and records turn latency events.

Docs and changesets describe the new interview behavior; tests cover stage remounting, answer buffering, coverage selection, and controller/session edge cases.

Reviewed by Cursor Bugbot for commit 190c5d7. Bugbot is set up for automated code reviews on this repo. Configure here.

ampagent and others added 2 commits August 27, 2026 18:57
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 4 potential issues.

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 190c5d7. Configure here.

JSON.stringify({ type: "input_audio_buffer.commit" }),
);
} catch {
this.#handleConnectionFailure("network", "transcription");

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Done speaking can kill the session

High Severity

Done speaking always sends input_audio_buffer.commit while provider-default server VAD is still committing automatically. A second commit on an empty buffer, or a commit with no speech, returns a realtime error that this session treats as a fatal connection failure, so a normal end-of-answer click can drop the interview into reconnect.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 190c5d7. Configure here.

this.#audioContext = audioContext;
this.#analyser = analyser;
this.#meterSamples = new Uint8Array(analyser.fftSize);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Microphone meter stays silent

Medium Severity

The input meter creates an AudioContext only after getUserMedia resolves, with no resume(). That runs outside the user-gesture window, so the context stays suspended in Chromium and Safari. getByteTimeDomainData then returns silence and the stage reports Quiet even while capture is actually on.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 190c5d7. Configure here.

)}
</div>
</>
)}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Corrections discarded before they can send

High Severity

Redo answer and Edit text appear whenever lastCommittedText is set, including during delivering and waiting. The controller ignores both unless phase is listening and no answer is in flight. Submitting a typed correction also clears the form first, so an early send silently drops the user's text.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 190c5d7. Configure here.

this.#update({
errorMessage: "",
currentQuestion: "",
microphoneLevel: 0,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Ended interview keeps prior answer

Medium Severity

end() resets phase and the current question but leaves lastCommittedText in place. Starting a new interview before the next brunch_ask arrives still shows Redo answer / Edit text for the previous session, and using them sends a correction of that old answer into the new conversation.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 190c5d7. Configure here.

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