Repository navigation
fix(voice): leave a term the vocabulary spells two ways by case as heard - #607
Merged
Merged
Conversation
The session vocabulary kept one spelling per lowercase key, so with both UserService and userService in a session the normaliser re-cased a heard term into whichever spelling won the key, turning one real symbol into the other. The vocabulary now keeps every spelling the session itself uses, and the glossary stays the floor: a session spelling still replaces the generic glossary one, as before. The normaliser treats terms that differ only by case as one group: heard in that shared spelling the term keeps its heard case and counts as a known term, and its spoken words abstain instead of picking a case. Unambiguous terms are re-cased as before. Fixes #590
…sion spelling The glossary now yields without a weight contest only when the session spells a word two ways by case. A session spelling such as a repository `clarkcant` listed after another project, a provider `gemini` that is not first, or a tool `eslint` deep in the list again leaves the glossary's ClarkCant, Gemini and ESLint, as before.
Contributor
Author
|
Review attestation: ready to merge at A push to this PR makes this attestation stale; the new head needs its own review. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #590
Problem
The session vocabulary kept one spelling per lowercase key. With both
UserService(a class) anduserService(an instance) in a session, the transcript normaliser re-cased a heard term into whichever spelling won the key, turning one real symbol into the other.Change
buildRecognitionContext(packages/voice-adapters/src/coding-vocabulary.ts) keys terms by their exact spelling, so every spelling the session itself uses is kept. This is a behaviour change only when the session spells a word two or more ways by case.clarkcant, a providergemini, a tooleslint): unchanged frommain. The glossary entry and the session term contest by weight; the heavier spelling stays, a tie keeps the session's. SoClarkCant,GeminiandESLintsurvive when the session term is not first in its list, and a dependencyreactstill keepsreact.transcript-normalizer.ts) groups terms that differ only by case:userservice,UserService,userService), the span keeps its heard case and is reported as a known technical term: no change, no abstention, so no pointless retry;user service), it abstains with every spelling as a candidate instead of picking a case;RedactSecrets->redactSecretsin the same vocabulary).Behaviour changes compared with
mainOnly where the vocabulary holds two or more spellings of a word that differ by case:
Playwrightplus toolplaywright, "sửa playwrigt test" givesPlaywrightonmainand abstains with both spellings here. A one-letter slip is no stronger evidence of case than the exact words, so abstaining is consistent and safe.Everything else, including the glossary-against-single-session-spelling contest and the vocabulary sent to the recognizer in that case, is unchanged.
Evidence
Regression tests in
packages/voice-adapters/test/transcript-normalizer.spec.ts:UserServiceanduserService, each exact spelling once, and adds no glossary spelling beside a word the session spells two ways (PNPM+Pnpm);PNPMoutweighs the glossary'spnpm(and its aliases go with it); a repositoryclarkcantlisted after another project leaves the glossary'sClarkCantwith its aliases;repositories: ["other-app","clarkcant","web"]giveClarkCant;providers: ["google","gemini"]givesGemini model;eslintafter 30 other tools giveESLint lỗi;userservice,UserService,userServiceandUSERSERVICEare left as heard, with no change and no abstention, in Vietnamese and English sentences;user serviceabstains with both candidates;RedactSecretsis still re-cased beside the ambiguous pair.The previous test "dedupes case-insensitively and keeps the glossary's aliases" only checked the
CODING_GLOSSARYconstant; it now asserts the built context in both directions of the weight contest. The new weight-contest tests fail against the previous head (71e9a44) and pass here.Corpus bench (
corepack pnpm --filter @clarkcant/voice-adapters bench:transcription,vi-en-coding-corpus.json, 84 utterances / 105 terms), output identical onmainsources and this branch:Per kind (normalized) also unchanged: symbol 18/21, command 11/14, path 8/9, package 10/12, model 3/3, provider 6/7, acronym 9/9, glossary 19/24, version 2/2, branch 2/2, issue 0/2. Canonical references changed by the normaliser: 0. The corpus has no case-variant pairs and no multi-repository sources, so it exercises neither path; the unit tests above do.
The audio run (
--audio ... --recognizer gemini-transcribe-live|gemini-live) was not run: external gate, noGEMINI_API_KEYin this environment.Checks on head 4c00b7c: focused vitest
packages/voice-adapters+apps/runtime/test/voice*(14 files, 231 passed),pnpm typecheck,pnpm invariants(14/14), eslint on the changed TypeScript files.pnpm verifywas run on the previous head (71e9a44: 555 files passed, 1 skipped; 7363 tests passed, 37 skipped), not re-run locally on this head; CI covers it.Notes
transcript-normalizer.tsand ADR-003 (different hunks). With the weight contest restored, a session provider family such asgeminibehaves as onmain(it keepsGeminiwhen it is not first in its list; a lonegeminities and stays lowercase, as onmain), so fix(voice): bias the dedicated recognizer only with terms that have no real neighbour #591's case restoration still holds.ClarkCantandclarkcant, "clark cant web" abstains on "clark cant" (onmainit became the also-wrongClarkCant web) instead of becomingclarkcant-web.clarkcant-web) do not describe the normaliser; no docs change needed.