Skip to content

fix(voice): prefer a run-together compact over a shorter two-word match - #613

Merged
mrgoonie merged 1 commit into
mainfrom
fix/608-compact-over-two-word-match
Oct 7, 2026
Merged

mrgoonie merged 1 commit into
mainfrom
fix/608-compact-over-two-word-match

Conversation

@mrgoonie

@mrgoonie mrgoonie commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

Summary

matchAt in the transcript normaliser returned the longest spoken form before it looked at compacts (words that run together into a term). So "clark cant web" matched the two-word ClarkCant / clarkcant and left "web" behind, even though clarkcant-web matched all three words.

Now an exact match of either kind wins by length:

  • A compact that covers more words than the longest form at that position wins.
  • A form wins a tie and wins against shorter compacts, as before.
  • A near match is still tried only when no exact match starts at that position. It never outranks an exact form.

Compacts are still limited to three words, and command terms are still never matched as compacts.

Behaviour

Vocabulary buildRecognitionContext({ repositories: ["ClarkCant"], packages: ["clarkcant", "clarkcant-web"] }):

Heard main this branch
sửa clark cant web trước left as heard, abstains on "clark cant" sửa clarkcant-web trước
sửa clark cant trước abstains (ClarkCant / clarkcant) abstains (unchanged, #590)

Tests

New tests in packages/voice-adapters/test/transcript-normalizer.spec.ts, under "words that run together into a longer term than a form starting the same way":

  • With the issue's vocabulary, "sửa clark cant web trước" gives sửa clarkcant-web trước. Fails on main.
  • "sửa clark cant trước" still abstains with ClarkCant / clarkcant.
  • With a single spelling, the shorter term is still written when "web" does not follow. Fails on main for the "web" case.

I ran the new spec against main's transcript-normalizer.ts: 2 failed, 41 passed. On this branch all 43 pass.

Corpus bench

corepack pnpm --filter @clarkcant/voice-adapters bench:transcription gives identical results on main and on this branch:

Stage WER CER TTER Exact (strict) Exact (audio-tolerant) Changes Abstained Regressions
normalized 3.7% 0.8% 16.2% 77.4% 84.5% 64 1 0

The ADR-003 tables (EN and VI) already show these numbers, so they need no change.

Verification

  • vitest run packages/voice-adapters: 163 passed
  • pnpm typecheck: passed
  • pnpm invariants: all 14 checks passed
  • eslint on the changed files: clean
  • pnpm verify: passed (568 files, 7663 tests passed)

No official docs change: this is an internal correctness fix to the normaliser.

Fixes #608
Refs #590, #607

The matcher returned the longest spoken form before it looked at words that run together into a term, so 'clark cant web' matched the two-word ClarkCant and left 'web' behind instead of matching the package clarkcant-web. An exact match of either kind now wins by length, a form wins a tie, and a near match is still tried only when no exact match starts there.
@mrgoonie

mrgoonie commented Oct 7, 2026

Copy link
Copy Markdown
Contributor Author

Review attestation: ready to merge at 0925b0b5303db11958324227fb127f102004ee56, reviewed by agent:code-reviewer.

A push to this PR makes this attestation stale; the new head needs its own review.

@mrgoonie
mrgoonie enabled auto-merge (squash) October 7, 2026 21:36
@mrgoonie
mrgoonie merged commit f40c8cb into main Oct 7, 2026
22 checks passed
@mrgoonie
mrgoonie deleted the fix/608-compact-over-two-word-match branch October 7, 2026 21:41
mrgoonie added a commit that referenced this pull request Oct 8, 2026
… the sentence (#627)

* fix(voice): count the words a run-together term absorbed as code evidence for the sentence

When words ran together into a longer term, the shorter exact form they passed over no longer anchored the rest of an English sentence, so ClarkCant and React beside a run-together webapp were left as heard. The passed-over form and the exact matches inside the span now count as evidence, as they would on their own; they still never support the run-together span itself.

Refs #608, #613

* fix(voice): keep two run-together spans from vouching for each other

A word a run-together term absorbed now supports only spans that did not
absorb a word themselves. Such a span is rewritten only on a technical cue
or a term heard in its own spelling, so "the web app and the web app" and
"set up the web app for grandma" stay as heard, while "fix the clark cant
web app now" still gives ClarkCant and "fix the web app react now" still
restores React.

* fix(voice): keep a rewritten evidence term from leaning on a word it let a span absorb

A term that counts as evidence while it is itself rewritten, such as
"Follow-up" heard for the tool follow-up or "S3" for s3, supports a
run-together span that absorbs a word like "web". That absorbed word no
longer supports lowering the term in return, so "Follow-up on the web app"
keeps "Follow-up" as main does, instead of each change resting only on the
other.

* fix(voice): keep absorbed words from supporting a span that holds evidence itself

A span that holds a coding cue or an evidence term already supports every
run-together span outside it, so the words those spans absorbed are
rewritten away. They no longer support that span in return: "the code
review of the web app" keeps "code review" and "type script web app" keeps
"type script", as main does.
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.

fix(voice): prefer a run-together compact over a shorter two-word match ('clark cant web')

1 participant