# v2.40.1 — hold the key we actually listen for

**Released 2026-09-25.** Upgrade:

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

## The first thing a new user was told to do did nothing

The APT and pipx installers opened with this, before anything else:

```
  Hold Space → speak → release → text appears anywhere
```

Holding Space does nothing. `[hotkey] key` defaults to `auto`, which resolves to
`platform.default_hotkey` — **`right_alt`** on Linux — and first-run seeding never
writes a hotkey, so `auto` is what a fresh install runs with. The modifier was chosen
deliberately, so that dictation never collides with normal typing the way the space bar
would.

This is the first thing anyone does after installing. Following the instruction produces
silence, with no error to explain it, and the reasonable conclusion is that YazSes does
not work.

The same sentence was in the `.deb`'s `Description:` field — what `apt show` and every
software centre display *before* you install — in the Linux section of the GitHub release
body, and in the "hold this key" tables on the Russian and Hindi home pages, whose Linux
rows still read `Space` after the English ones had been corrected.

The README and the docs home page were fixed in v2.27.0. That entry says the correction
covered "four places". It was a list written by hand, which is why it missed nine more.
The list is now a test: `tests/test_hotkey_naming_truth.py` reads each platform's
`default_hotkey` out of the source and scans every user-facing file, failing the build if
one tells a reader to hold a key no platform defaults to, or pairs an OS with another
OS's key.

Two of those places only reach a user at a tag, which is why this is a release rather
than a documentation commit: the `.deb` description is baked into the package at build
time, and the release body is written at publish time. Every `.deb` published before this
one still says Space.

## Wayland's default injector no longer drops your accents

On Wayland, `[injection] backend = "auto"` picks `ydotool`, and the text went straight to
`ydotool type`. That presses keycodes against the active XKB layout, so a character the
layout has no keycode for — `å`, `ä`, `ö`, French and German accents, any non-Latin
script — pressed nothing at all. Nothing errored: the command exited 0, the daemon
reported success, and the only symptom was the wrong words on screen ([#329](https://github.com/MSKazemi/yazses/issues/329)).

The layout-independent path already existed as an opt-in `unicode` backend ([#364](https://github.com/MSKazemi/yazses/issues/364)),
which resolves a character through `libxkbcommon` and presses it via a private
`/dev/uinput` keyboard. The ydotool backend now splits the text into ASCII and non-ASCII
runs, keeps sending the ASCII runs through `ydotool type` exactly as before, and routes
the rest through that injector — so dictating in your own language works on the default
`auto` path with no config change. It needs no new permission: `yazses setup`'s udev rule
already grants `/dev/uinput` to the same `input`-group membership `ydotool` itself
requires before `auto` will select it.

## Also in this release

Everything else here is documentation and packaging truth — pages that described a YazSes
different from the one in the tree:

- **The Fedora package was four releases behind, again.** `packaging/fedora/yazses.spec`
  said `Version: 2.36.0` while the project shipped 2.40.0, so COPR built and published
  2.36.0 as the current package. `dnf copr enable mskazemi/yazses` still installs 2.36.0
  until a rebuild. The spec is right now, and — the part that matters — the release
  script rewrites it and the packaging-version guard derives which files it checks from
  the tree instead of a hand-written list, so the next one cannot slip through.
- **Install instructions named channels that do not serve what they claimed.** Every
  channel claim was re-checked against each registry's own API. `yay -S yazses` cannot
  work — YazSes has never been published to the AUR — winget holds a build seven releases
  old rather than "a few days" behind, and COPR serves 2.36.0. The pages say so now.
- **The support page denied two installers that had shipped for 23 releases.** Intel Mac
  and Windows-on-ARM users were told their native installer "lands at the next tagged
  release"; the `.dmg` and the `.exe` have been attached to every release since v2.22.0.
- **Every contributor page promised a test suite that runs in seconds.** A full run takes
  minutes — 15713 tests measured at 4m53s on a recent laptop — not the "about 30 seconds"
  eleven pages quoted. They now describe the scale instead of a figure that varies by an
  order of magnitude with the machine.
- **The documented contributor setup did not produce the clean mypy run it promised.** A
  correct fresh `uv sync` reported 12 `import-not-found` errors nobody had introduced,
  because `PySide6` moved into the `desktop` extra without an entry in the `[tool.mypy]`
  overrides that silence every other optional backend.
- **The Store notes said the Face-Gesture Switch did not exist.** It has shipped since
  v2.40.0; the reason the MSIX does not declare `webcam` is the frozen bundle carrying no
  camera extra, which is what the page says now. The Store page also states plainly that
  YazSes is **not** listed yet, so nobody finishes it believing otherwise.
- **The contribute links pointed at finished work.** Newcomer-facing pages recruited
  against closed issues, two of them saying outright that the work was "open right now".
  They now point at the directories and pages that take the pull request. No issue was
  opened, closed or commented on; only the links moved.
- **The second paper is being written in the open**, under `design/publication/paper-v2/`
  — manuscript, claim ledger and the reconstruction of the August measurement campaign,
  every number resolving to a committed artifact under `paper/results/`.
- **The Microsoft Store artwork is drawn by a script** rather than by a snippet pasted
  into a README, so it stopped being the retired blue logo; and the image drift guards
  that bind each asset to its generator now measure a tolerance instead of comparing
  pixels exactly, which was never portable across architectures.
