# v2.27.0 — what YazSes said about itself was not what it did

**Released 2026-08-17.** Upgrade:

```sh
sudo snap refresh yazses     # snap
pipx upgrade yazses          # or: uv tool upgrade yazses
```

The previous release came from reading one machine's log and finding that YazSes had
said nothing while dictation failed four times running. This one comes from continuing
to check its claims against its behaviour — the documentation, the status output, and
the quality gates — and finding that a surprising number of them were wrong.

Nothing here changes how dictation works. Almost everything here changes what YazSes
tells you about how it works, which had drifted far enough to send people down paths
that could not succeed.

## `yazses status` now reports whether dictation is actually working

Decode latency has always been measured, summarised, and shown. Whether a burst
produced any text at all was measured and never summarised — and it is the more basic
number. A fast decode that types nothing is not usable at all.

`status` now carries a line like:

```
typed: 6 of 14 recent bursts (43%)
```

It is a bounded recent window rather than a lifetime average, because a lifetime
average hides exactly the degradation worth catching: the machine that prompted this
had gone from 21 typed of 30 to 6 of 14, and nothing reported it. Below five samples it
says nothing at all — a rate the data cannot support is worse than no rate, because it
will be believed.

One correction to that metric shipped with it. The first version classified a burst by
its `discard_reason`, treating "no reason recorded" as success — but a burst that
*raises* never sets one, so injection failures and vanished backends were being counted
as text successfully typed. A health metric that counts failures as successes is worse
than no metric, since it is consulted precisely when something is wrong.

## "comment this line" was typed into your file instead of commenting it

The command grammar matched `comment this line` but not several natural forms of the
same instruction — `comment out`, `comment the line`, `comment this selection`. An
unmatched command is not an error: it falls through to dictation, so the words
themselves were typed into the buffer. Asking your editor to comment a line inserted
the sentence asking for it.

## The README told Linux users to hold the wrong key

Two front-door errors, both of which cost a new user their first attempt:

- The **hotkey** shown for Linux was not the one that ships. Someone following the
  README held a key that does nothing and concluded YazSes did not work.
- **Intel Mac users were sent away from a build that exists.** The macOS section
  described the `.dmg` as Apple-silicon-only. Intel `.dmg` builds have shipped since
  v2.22.0.

## Eight pages offered features that cannot be enabled

YazSes distinguishes capabilities that are *wired* from ones that are *designed but not
yet wired* — `features enable` refuses the latter, and `yazses features` labels them
**planned**. The documentation did not carry the distinction. Eight pages listed unwired
capabilities in ordinary "enable it with this command" tables, alongside features that
work, with no indication the command would be refused.

The accessibility page was the worst of them, offering six such slugs to the readers
least able to route around a dead end.

A test now pins this: a line that spells out `yazses features enable <slug>` for an
unwired capability must also say `planned`. It follows the registry rather than a copied
list, so wiring a feature retires the requirement automatically.

Related corrections in the same pass: the remote how-to promised a `[remote]
default_host` default for an argument that cannot be omitted; `xdotool` was documented
as an injection backend and is not one; `--min-speakers` still advertised itself as a
lower bound after being made a no-op; and the README described the Tier 2 SLM router and
one other capability in the present tense, as things YazSes does.

## Four crashes were waiting behind a gate that said it was clean

`make types` describes itself as *"advisory — currently clean; don't add errors"*. It
was not clean, and because CI does not run it, nothing ever said so. Four of the seven
errors standing in it were real `AttributeError`s waiting on a code path:

- **Rewrite crashed if it arrived during startup.** The function guarded the LLM cleaner
  for exactly that reason, then dereferenced the injector two lines later without a
  guard.
- **A spoken question could crash the same way** — the recorder and STT engine are both
  absent until startup finishes.
- **The tray reaped subprocesses through a controller that may not exist.**
- **The brand mark** built its glyph colour in a way that lost the exactly-three-channels
  guarantee its RGBA paste depends on.

mypy is now genuinely at zero across 488 files, and the claim in the Makefile is true
again.

## Also fixed

- **The tray left a zombie process behind every time you opened Settings.** The window
  was launched and never waited on. It is now reaped on the tray's own poll tick, which
  is correct whether the window closed a second or an hour ago.
- **The man page's release date stopped resolving at 2.25.0**, because the changelog
  changed which dash character it used and the extracting regex knew only the hyphen.
- **The silent-audio troubleshooting page now covers "Empty transcription"** alongside
  "Silent audio" — two different failures that feel identical from the user's side.
- **The second clipboard path is documented, with its platform limit.** `[injection]
  fallback_to_clipboard` is Linux-only; on macOS and Windows the key is inert in both
  directions, so it never provided the safety net its name implies.

## Full changelog

See [CHANGELOG.md](https://github.com/MSKazemi/yazses/blob/main/CHANGELOG.md) for the
complete list.
