# v2.23.0 — the accessibility product that ignored your accessibility settings

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

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

YazSes exists so that people who cannot comfortably type — or cannot use a pointer —
can still put words on a screen. This release is two places where it did not hold up,
found by reading its own code against its own documentation rather than by a bug report.

## The site respected your settings; the app did not

The voice-activity overlay draws rings that expand outward from near the pointer, sixty
times a second, for as long as you hold the key. Expanding motion in the periphery is
the specific pattern people with vestibular disorders and motion sensitivity ask
software to stop doing, and every desktop carries a switch where they have said so:
GNOME's **Reduce Animation**, macOS's **Reduce motion**, Windows's **Animation effects**.

YazSes read none of them. Meanwhile `docs/assets/extra.css` — the stylesheet for this
very documentation site — has honoured `prefers-reduced-motion` for months. The site
advertising the accessibility tool respected the preference. The tool did not.

`[overlay] reduced_motion` is now `auto` (the default), `on` or `off`. `auto` reads the
desktop once at startup, never on the render tick.

**Reduced motion removes motion, not information.** The overlay answers one question —
*am I being heard, and how loudly* — and someone asking for less animation has not asked
to stop being told. So the reduced form keeps the ring and drops the travel: one steady
circle while you speak, brightness in four discrete bands.

The bands matter. A brightness recomputed every frame from a live microphone level
shimmers, and a shimmer at 60 Hz is its own accessibility problem rather than a fix for
one. It also shows the ring during silence, which the animated form does not — tolerable
when rings are about to appear, and not when none ever will.

A desktop YazSes cannot read resolves to full motion, exactly as before, so nobody's
overlay changes on the strength of a failed probe. KDE, Xfce and bare window managers
have no such setting to read; they set `on` and are believed.

## A question only a pointer could answer

The mic-change guard is the one part of the pipeline that *asks* rather than tells. When
capture moves to another device, or a run of clips comes back silent, it pops a toast
offering **Re-calibrate**, **Pin this mic** and **Ignore**.

Those were buttons and only buttons. So the daemon interrupted you with a question about
your microphone that could only be answered with a mouse — and the person seeing it is
the one whose dictation has just stopped working, which is the worst possible moment to
be sent to the pointer.

With `[audio] voice_answer` on, hold your dictation key and say it instead:

| Say | Does |
|---|---|
| “re-calibrate” / “calibrate the mic” | the same as clicking **Re-calibrate** |
| “pin this mic” / “pin the microphone” | the same as clicking **Pin this mic** |
| “ignore” / “dismiss” / “never mind” | dismisses the toast |

Almost all the work here is in *not* firing. “ignore” and “pin” are ordinary English, so
the **whole utterance** must be the answer — “please ignore the second paragraph” is
prose and gets typed. And the words only count within 45 seconds of the toast; an
unbounded window would arm “ignore” as a control word for the rest of your session, and
once it closes the word types normally rather than being silently swallowed. No action
*and* no text is the worst outcome available.

It runs after the command-safety and check-digit gates and before staged dictation, so a
held `rm -rf` keeps first claim on the utterance and the staged buffer cannot swallow the
answer.

## Both are findable, which is half the point

An opt-in accessibility capability that appears nowhere but a documentation page is one
nobody switches on. Both are listed by `yazses features`:

```sh
yazses features enable mic-voice-answer
yazses features enable overlay-reduced-motion
yazses restart
```

Disabling `overlay-reduced-motion` restores `auto` rather than writing `off` — overriding
someone whose desktop asked for less motion is a different opinion, not an off switch.

Both ship **off by default**, and that is worth stating plainly rather than dressing up:
the measured baseline of *daemon-initiated states with no voice-only exit* stays at 2
until you turn the first one on.

## An example config must say what was observed

`examples/config.<app>.toml` files are copied verbatim by newcomers, so a wrong one costs
more than none at all. Three checks already guarded them — valid TOML, every key
resolving to a real field, an opening comment — and all three pass just as happily on a
profile nobody ran. Valid TOML, real keys and a header can all be produced without the
application ever being opened.

The fourteen profiles in the tree already record what was actually seen, several with the
dictated and arrived text side by side. Nothing enforced it. `scripts/check-app-profile.py`
and the test suite now do.

The check lives in the script rather than only in the suite, and the placement is the
point: a pull request from a first-time contributor arrives with every real workflow held
at *waiting for approval*, so the suite cannot see the file until a maintainer clears it.
The script runs on the contributor's own machine, while they can still fix it.

A marker word inside an unresolved `TODO` does not count — a starter file headed
*“TODO(you): replace this block with what you actually observed”* would otherwise satisfy
the check by quoting the instruction to fill it in.

## Also

- `paper/benchmark/`, the reproducibility code the benchmarks page points readers at, sits
  outside the lint gate and had drifted. Fixed — and the fix was the configuration, not
  the code: ruff had no way to know `_common` is the helper sitting *beside* those scripts,
  so its autofix wanted to reorder nine files' imports around a grouping their author chose.
