Skip to content

v2.36.0 — the failures that reported success

Released 2026-08-30. Upgrade:

sudo snap refresh yazses     # snap
pipx upgrade yazses          # or: uv tool upgrade yazses
scoop update yazses          # Windows

This is a large release, and it has one shape running through it. Almost nothing here crashed. A config file was discarded and the daemon started anyway. A secret was written to disk by the setting that exists to remove it. A guard was skipped by the one code path that executes what it guards. A CI job ran 48 tests out of 13,800 and went green. In every case the failing thing returned normally, which is why these lasted as long as they did.

The full list is in the CHANGELOG. What follows is what actually affects you.

If you have ever edited config.toml on Windows

A UTF-8 byte-order mark threw away every setting in the file, not one line.

tomllib rejects a leading BOM — TOML has no place for one — and a TOML document parses as a whole, so the failure is total. The daemon fell back to defaults for everything: model, hotkey, VAD threshold, injector. The error it reported was

could not be read (Invalid statement (at line 1, column 1))

about a line that looks perfectly correct on screen, because the offending bytes are invisible.

This is not an exotic input. Windows PowerShell 5.1 — the default shell on Windows — writes UTF-8 with a BOM from Set-Content and Out-File, and "UTF-8 with BOM" is still an offered encoding in Notepad. Every hand-edited TOML this project reads was affected: config.toml, the macros file, the style-rules file, and the copy yazses report puts in a diagnostic bundle — so the bundle you would send to ask for help said your config was unreadable, pointing every reader at the wrong problem.

The same mark cost the vocabulary file its first entry, which is the word you added first and care about most. Kubernetes renders identically to Kubernetes in yazses vocab list, is not matched by yazses vocab remove Kubernetes, and reaches Whisper as a token the model has never seen — so priming that one word silently stopped working.

Both now read utf-8-sig. Writing any setting also repairs a file that already had a BOM, rather than carrying it forward — otherwise the one command you would run to fix your config would keep re-emitting the thing that broke it. A real syntax error is still reported as one.

A one-letter typo in [redaction] mode shipped the secret it was set to hide

[redaction] mode accepted a misspelling and fell through to not redacting. The setting that exists to keep a value out of a file was, when misspelled, the setting that put it there — and nothing said so, because an unrecognised mode is not a parse error.

If you use redaction, check the spelling of that key once after upgrading.

A destructive command skipped its safety gate in the one mode that runs it

cmdsafety holds a dictated rm -rf, mkfs, dd of=, curl | sh or force-push until you confirm it out loud. It was wired into the dictation path and not into command mode — the one path that actually presses Return. Holding the command key says "this is a command", not "and I accept this one", and a misheard command is exactly as dangerous as an unintended one.

Both branches are now gated.

Dictation

  • A large vocabulary erased the one word the prompt exists to prime. faster-whisper keeps only the last 223 tokens of initial_prompt and drops the front in silence, so putting a term first meant deleting it first. Priming is now appended, not prepended.
  • The disfluency filter lost the first half of a sentence in which a filler phrase is doing real work. The self-correction guard looks at the single word before a trigger to decide whether a rollback is genuine, and a pronoun is not enough to tell the two apart: "they never mind the noise from the street" came out as "the noise from the street". Android lost the same sentences, because only one of the guard's two lists had been ported to Kotlin.
  • yazses mic-level calibrated to an empty room and called it a recommendation. One recording cannot tell speech from room tone, so a quiet room produced a confident threshold below that room's own noise — the state where ambient noise clears the gate and the decoder answers it with an invented word. It now records twice, places the threshold between the two, and refuses when they are closer than 3× rather than inventing a number.
  • The microphone-change watcher could not fire on the setup most Linux users have. It compared the default input's name between polls, and on ALSA/PipeWire that name is default — a route, not a microphone. It compared default with default forever, so unplugging a headset mid-session went unannounced. It now reads through the alias where the desktop lets it, and stays quiet rather than guessing where it cannot.

Meeting Mode

  • Minutes could leave out part of a meeting without saying so: the reduce step never ran on a meeting long enough to need it, a summariser window could fail by returning rather than raising and nothing counted it, and the INCOMPLETE disclosure was left for the summariser itself to keep.
  • A pyannote pipeline that returns no annotation failed mid-loop instead of degrading.

Install channels

  • For two years, every release told Ubuntu users to run sudo add-apt-repository ppa:mskazemi/yazses. Launchpad answers 404 for that archive and for the account it would belong to — the PPA was never created, and the workflow meant to publish it had been dead since v1.0.0, triggering on v0.* tags only. The instruction is gone, and a test now refuses to let the release notes advertise a channel that nothing in this repository publishes.
  • On FreeBSD, every spoken command crashed. Resolving a key name imported evdev, a Linux-only C extension, so every command, every backspace correction and the clipboard paste raised ModuleNotFoundError. The keycode table is now committed — safe precisely because input-event-codes.h is a kernel ABI. The FreeBSD CI job that should have caught it was running 48 tests out of 13,800.
  • Flathub was still building the release before last. The one line in the generated Flatpak manifest that has to move with every release — the yazses wheel pin — did not move when 2.35.0 was cut, so Flathub would have built 2.34.0 while the metadata beside it advertised the current version. A test now fails the build when the pin falls more than one release behind.
  • A re-run of the snap publish uploaded a duplicate revision every time; ~200 store rejection emails came from a job that uploaded without asking the store first.

Security

  • The one advisory in this tree that is actually reachable had no assessment. The other two had Dependabot alerts — a UI, an email, a warning on push. This one existed only as an id inside a Scorecard check detail with no summary, so attention had followed notification surface, which is uncorrelated with severity. It is now assessed in SECURITY.md with its bounds stated and guarded by tests.
  • A setuptools<81 pin inside one opt-in extra held every install below a patched release — including both shipped bundles, because uv.lock is a universal resolution and a pin in an extra is not confined to it.
  • The program that writes the Flatpak pins was itself unpinned. uv and req2flatpak generate the 45 URL-and-hash pins Flathub builds from, and were installed with pip install --upgrade. The hash is the one field a reviewer cannot check by reading.
  • The only executable binary in the tree was checked by nothing. gradle-wrapper.jar is 43 KB that ./gradlew executes, and a swapped wrapper survives review because a reviewer sees a filename in the diff stat and cannot read the contents. It is now validated against Gradle's published checksums on every commit.
  • Three workflows granted a write token at the top level, where every job added to the file afterwards would inherit it.

Added

  • The text pipeline is now fuzzed. Two atheris harnesses cover the two surfaces that take input nobody wrote: the config parser and the transcript post-processing chain.
  • yazses doctor reports the update watcher, which was both off by default and invisible.
  • The windowctl layout grammar, which shipped with no caller, is wired.

For maintainers

Several fixes here are about checks that were not checking. Twenty guards reported findings about a repository they never read; a monthly Dependabot job was red on a bump that cannot exist; a contract-only change reported the previous run's pass; and a weekly job written to prove the two heavy extras work had, since it was added, produced no run at all — its pin was undone by the next line, and the gate underneath demanded something no single environment could give it.

Upgrading

Your config, models and learning corpus are untouched. If you edit config.toml on Windows, this release is the one that makes that safe.