The dictionary
Whisper transcribes useEffect as "use effect", kubectl as "cube control", and nginx as "engine X". Those are not model bugs. They are accurate transcriptions of the sounds you made, and the fix belongs after the model rather than inside it.
The dictionary maps spoken forms to written ones.
Teaching it from a dictation that went wrong
Every entry in Recent has a Fix a word button. It shows what OpenVoice actually heard, then you click the words it got wrong, type what you meant, and save. It applies to the very next thing you dictate.
Seeing the raw transcript is most of the work. Knowing that kubectl arrived as "cube control" tells you exactly what to map; guessing it from the tidied-up output does not.
Why the spoken form is the misheard one
Enter the mistranscription you actually observed, not the correct pronunciation. "cube control" belongs in the dictionary precisely because it is wrong. Multi-word spoken forms work, and the longest match wins, so "use effect" beats a hypothetical entry for "use".
The space-free form is indexed too. Whisper sometimes returns an identifier already welded together but miscased, UseEffect for useEffect, and without that index the dictionary would see one unknown token and leave the wrong casing in place.
Groups
Each entry belongs to a group, and a profile enables the groups it wants. The shipped groups are code and shell. That is why cube control becomes kubectl in a terminal but not in an editor: the editor profile enables code only.
Why the shipped list is small
A large shipped dictionary produces confident wrong corrections in domains you do not work in, which is worse than no correction at all. The shipped list covers a few dozen terms; the real value comes from what you add.
The clearest example is a term deliberately left out. It is tempting to map "get" to git, since "get status" almost always means git status. But "get" is far too common a word to claim context-free, and claiming it turns kubectl get pods into kubectl git pods. That regression is pinned by a test.
Why not just tell the model?
This looked like the wrong design at first. Feeding the same terms to the decoder should be strictly better, since it still has acoustic evidence that post-processing has thrown away. The reasoning was sound and the conclusion was wrong. Measured on the Whisper engine OpenVoice used until v0.5.0 — same audio, same model, the hint being the only difference:
with hint: camelCaseUserProfile ==NewUserProfile open paren close paren
without hint: camel case user profile equals new user profile open paren close paren
A prompt full of camelCase identifiers teaches the model to write camelCase, so it silently welds together ordinary spoken words, including the very command words the formatter needs to see. The damage is worse than the problem it solves, because the dictionary can fix use effect afterwards whereas nothing downstream can recover words the model already joined.
A narrow version of the idea survived that result: proper nouns whose sound collides with an ordinary word, where nothing downstream can recover the difference — "Claude" against "cloud". That hint is currently unavailable. It relied on a Whisper input the present engine has no equivalent for, and restoring it needs a tokeniser file the model's release does not ship. Everything else the dictionary does is unaffected.
Adding one in the source
A new term is a few lines plus a test in dictionary.rs, and it is one of the easiest useful contributions to make. No Windows, no GPU and no microphone required:
cargo test -p ov-format