Skip to content

feat(suggestions): skip live suggestions for backchannel interviewer turns - #101

Merged
alpha5611331 merged 2 commits into
mainfrom
feat/skip-backchannel-live-suggestions
Aug 14, 2026
Merged

feat(suggestions): skip live suggestions for backchannel interviewer turns#101
alpha5611331 merged 2 commits into
mainfrom
feat/skip-backchannel-live-suggestions

Conversation

@alpha5611331

Copy link
Copy Markdown
Member

Summary

  • Adds classifyInterviewerTurn() (src/main/utils/interviewer-turn.ts), an in-process, deterministic first stage that runs on the merged interviewer turn and returns Skip (pure backchannel - drop outright, no request, no card), Answer (a completed question or directive - generate immediately), or Uncertain (park on an INTERVIEWER_TURN_SETTLE_MS timer that any further interviewer final re-arms, so a question an ASR final split mid-sentence is judged whole).
  • Skip only fires when a backchannel lexicon consumes the entire turn from the front - deliberately asymmetric, since a missed question is silent and unrecoverable mid-interview while a wasted request just costs one ignored card.
  • transcript.service.ts now schedules the suggestion through this classifier instead of firing unconditionally on every interviewer final.
  • Answer / Uncertain ride the request as turn_verdict for the backend's own speculative gate (PowerInterviewAI/backend#49).
  • suggestion-live.service.ts no longer appends a Pending card the moment a request starts - it holds for LIVE_SUGGESTION_RENDER_DELAY_MS, so a turn the backend gate suppresses in that window produces no card at all instead of one that flashes and is retracted.

Fixes #100.

Test plan

  • pnpm lint - clean
  • pnpm exec tsc -p tsconfig.electron.json --noEmit / tsconfig.app.json - clean
  • pnpm test:main - all checks passed
  • New: test/interviewer-turn.test.mjs (Skip/Answer/Uncertain classification, both directions of the asymmetry, fragment-waits-for-continuation)
  • Manual: run an interview, confirm "mhm"/"okay"/"got it" produce no card and a question split across two ASR finals still gets one coherent answer

…turns

Every ASR final on the interviewer channel fired a full live-suggestion
request - profile, context, up to 60 transcripts - even for "mhm" or
"yeah, got it", and the only sign a turn didn't need an answer was the
backend's NO_SUGGESTION_NEEDED sentinel arriving after a card had already
been rendered and had to be retracted. An ASR final is also an acoustic
endpoint, not a finished thought, so a question split by a pause fired
twice and the second aborted the first mid-render.

classifyInterviewerTurn() runs in-process on the merged turn and returns
Skip (drop outright, no request, no card), Answer (a completed question or
directive - generate now), or Uncertain (park on an
INTERVIEWER_TURN_SETTLE_MS timer any further final re-arms, so a split
question is judged whole). Skip only fires when a backchannel lexicon
consumes the entire turn from the front; anything it can't fully consume
falls through, since a missed question is far more costly than one wasted
request.

Answer/Uncertain also ride the request as turn_verdict, for the backend's
own speculative gate (companion PR on backend). The client no longer
appends a Pending card on request start either - it holds for
LIVE_SUGGESTION_RENDER_DELAY_MS so a turn the backend gate suppresses in
that window produces no card at all instead of one that flashes and
disappears.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Comment thread src/main/services/transcript.service.ts
Comment thread CLAUDE.md Outdated
…ust its last block

A pause longer than TRANSCRIPT_INTER_TRANSCRIPT_GAP_MS keeps two Other
blocks separate even with no candidate turn between them. Classifying
only the last block read a real question as backchannel whenever it
was followed by a standalone acknowledgement, dropping it silently -
no request, no card, no error. Extracted as selectTrailingOtherTurn so
the concatenation is unit-testable without driving real wall-clock
gaps through ingest().

Also fixes a doc link in CLAUDE.md pointing turn_verdict readers at
the renderer types instead of main.

Addresses gitar-bot review findings on PR #101.
@gitar-bot

gitar-bot Bot commented Aug 14, 2026

Copy link
Copy Markdown
Code Review ✅ Approved 2 resolved / 2 findings

Adds deterministic classification to skip live suggestions for backchannel interviewer turns and delays pending card creation to prevent UI flashing. Addresses findings regarding block inspection scope and documentation links.

✅ 2 resolved
Edge Case: Skip verdict only inspects the last merged interviewer block

📄 src/main/services/transcript.service.ts:108-121
scheduleSuggestion classifies only cleaned.filter(Other).slice(-1)[0] (transcript.service.ts:111), not the full set of interviewer transcripts since the candidate last spoke. If an interviewer turn is split into two separate Other blocks (a pause > TRANSCRIPT_INTER_TRANSCRIPT_GAP_MS = 5s), and the final block is pure backchannel, the whole turn is Skipped — dropping any unanswered question in the earlier block entirely (no request, no card). Within 5s the blocks merge and re-classification is whole, so this is a low-likelihood edge, but it contradicts the 'runs on the merged turn' claim for multi-block turns and is exactly the silent-drop the asymmetry is meant to prevent. Consider classifying the concatenation of trailing Other blocks (or only returning Skip when every interviewer block since the last Self turn is backchannel).

Quality: CLAUDE.md links turn_verdict types to the wrong file

📄 CLAUDE.md:74 📄 src/main/types/llm.ts:72-83
CLAUDE.md line 74 states GenerateLiveSuggestionRequest / the turn_verdict wire values live in src/renderer/types/llm.ts ('types/llm.ts mirrors the wire values answer / uncertain'). Those types and the new RequestTurnVerdict enum are actually defined in src/main/types/llm.ts; the renderer file contains neither GenerateLiveSuggestionRequest nor RequestTurnVerdict. Update the doc link to point at src/main/types/llm.ts to avoid misleading readers.

Options

Auto-apply is off → Gitar will not commit updates to this branch.
Display: compact → Showing less information.

Comment with these commands to change the behavior for this request:

Auto-apply Compact
gitar auto-apply:on         
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

@alpha5611331
alpha5611331 merged commit affea27 into main Aug 14, 2026
2 checks passed
@alpha5611331
alpha5611331 deleted the feat/skip-backchannel-live-suggestions branch August 14, 2026 12:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Live suggestions fire on interviewer backchannel, not just real questions

1 participant