Skip to content

fix(voice): keep the last word after release - #78

Merged
ANT1OFF merged 1 commit into
mainfrom
fix/voice-keep-last-word
Nov 2, 2026
Merged

ANT1OFF merged 1 commit into
mainfrom
fix/voice-keep-last-word

Conversation

@ANT1OFF

@ANT1OFF ANT1OFF commented Nov 2, 2026 •

Copy link
Copy Markdown
Owner

What

Letting go of dictation no longer clips the last word.

  • On a stop (release or toggle), capture runs on for 200 ms, Hex's POST_RELEASE_TAIL. The status changes to "Finishing…" at once.
  • The worklet then flushes its partly filled 100 ms frame, which used to be lost. The page sends stop only after the worklet confirms, with a 100 ms guard in case the audio thread has stopped answering.
  • Frames keep flowing during stopping, and the level bars stay at 0.
  • Escape, and a stop that overtakes the start (a tap that never got to listen), get no tail.

The speech process already accepts frames of 1 to 16000 samples, so nothing changes on its side.

Results

Real recogniser, synthetic voice. I ran 8 sentences from macOS say through the installed Nemotron int8 560 ms model and the real createSpeechService, at 3 frame phases, so 24 runs per row. "Before" is full 100 ms frames up to the release. "After" is the release + 200 ms plus the short frame. Audio was framed by a scratch harness, not captured through the app; the script is not committed.

Last word kept:

Release vs end of speech Before After
−200 ms 0/24 24/24
−100 ms 10/24 24/24
−50 ms 16/24 24/24
0 ms 22/24 24/24
+100 ms 24/24 24/24
+200 ms 24/24 24/24

Whole sentence exact: 22/24 at every offset after, 0–22/24 before. The two misses after are one sentence at two of its three phases: "Run the" at the start heard as "runda" or "rhonda". The same error appears at +200 ms before this change, so it is a recogniser error at the first words and not clipping at the end.

Tests

  • capture.test.ts replays a synthetic WAV (tone "words") through the real worklet class, startCapture and session over fake Web Audio, released at −100 to +200 ms. Every sample up to the end of speech arrives.
  • Mutation check: with the tail set to 0, the −100 and −50 ms cases fail. With the flush removed, the short-frame case fails.
  • session.test.ts covers Escape during the tail, a stop during the start, a double stop, an error during the tail, and a cancel while the flush is out.

Real worklet in Chromium (fake mic device, mock speech process). A one-off spec, not committed, logged the last frames: 1600, 1536, stop, then 704, 960 and 256 on later runs, so the flush works on a real audio thread. Stop click to idle took 258–337 ms.

Commands: vitest run --project web apps/web/src/dictation (31 passed), e2e dictation.spec.ts (12 passed), typecheck, fmt:check, check:scaffold, oxlint src/dictation.

Known limits

  • Not tested with a real microphone or a human voice. The figures above are from say audio.
  • Slower finish: text lands about 200 ms later than before, which is the cost of the tail.
  • Send during the tail: the thread composer doesn't stop dictation on send (the drafts do), so pressing Enter straight after letting go can send before the last words commit, and they land in the emptied draft. That already happened; the window is now 200 ms longer.
  • Press during the tail: a new press while stopping does nothing, as before, and that window is also 200 ms longer.

No visible UI change, so no screenshots.

A stop cut capture at once and dropped the worklet's unsent part-frame, so
a release just before the end of speech lost the last word. A stop now keeps
capturing for 200 ms, flushes the part-frame, and only then tells the speech
process to stop. Escape and a stop during the start get no tail.
@ANT1OFF
ANT1OFF merged commit cc40a47 into main Nov 2, 2026
22 checks passed
@ANT1OFF
ANT1OFF deleted the fix/voice-keep-last-word branch November 2, 2026 04:40
ANT1OFF added a commit that referenced this pull request Nov 2, 2026
Desktop release metadata proposal only. Merging this PR does not publish
or tag Gram; follow docs/RELEASING.md to release.
---


## [1.4.0](v1.3.1...v1.4.0)
(2026-11-02)


### Features

* **agents:** identify Gram's Claude processes as the gram entrypoint
([#91](#91))
([f7dd703](f7dd703))
* **desktop:** run on Linux
([#82](#82))
([1d37deb](1d37deb))
* **desktop:** turn voice input off on Linux with one check
([#79](#79))
([470ff8b](470ff8b))
* **orchestration:** a person's PR comment wakes the worker that owns it
([#86](#86))
([19bdc1c](19bdc1c))
* **orchestration:** a wrap-up turn when a worker stops without
reporting ([#88](#88))
([36a07fb](36a07fb))
* **orchestration:** workers say what they ruled out and check before
repeating ([#90](#90))
([8bdce01](8bdce01))
* **release:** publish and update Linux AppImage and .deb
([#95](#95))
([c33c464](c33c464))
* **voice:** an accurate mode with a Parakeet pass on each pause
([#83](#83))
([2b04a52](2b04a52))
* **voice:** hold one key to talk, with a tone at each end
([#87](#87))
([1a9b3ef](1a9b3ef))
* **voice:** start capture at the press, and an opt-in ready microphone
([#81](#81))
([505d240](505d240))
* **web:** a Done section that keeps the last 7 days
([#65](#65))
([8c7bd49](8c7bd49))
* **web:** Done lists every finished chat
([#67](#67))
([c91d9ac](c91d9ac))
* **web:** one attention count, quieter rows, a sidebar and board that
agree ([#92](#92))
([f193da4](f193da4))
* **web:** orchestrator → workers in the sidebar, a board that matches
([#80](#80))
([686e737](686e737))


### Bug Fixes

* **desktop:** harden the Linux key vault and autostart entry
([#85](#85))
([4b35eac](4b35eac))
* **e2e:** hold the t-flaky turn while the smoke test reads its opening
line ([#93](#93))
([2edc4ac](2edc4ac))
* **e2e:** release the dictation tap as soon as a word is heard
([#94](#94))
([7781194](7781194))
* prepare only approved local-check sources
([#32](#32))
([af3fab7](af3fab7))
* repair main after [#32](#32)
([#68](#68))
([ce352e6](ce352e6))
* **voice:** keep the last word after release
([#78](#78))
([cc40a47](cc40a47))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

---------

Co-authored-by: gram-release-please[bot] <351321620+gram-release-please[bot]@users.noreply.github.com>
Co-authored-by: Johan <32440065+ANT1OFF@users.noreply.github.com>
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.

1 participant