# v2.26.0 — when dictation stops, you find out

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

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

This release comes from reading one real machine's daemon log. Every dictation attempt
on it for an hour had produced nothing — the key held, words spoken, no text — and
YazSes had said nothing at all, four times, because each of the three things that
should have noticed was looking somewhere else.

## A microphone that hears you but yields nothing

The mic-change guard counts **silent** discards: audio below `vad_threshold`. A run of
them switches capture back to the last device that worked and notifies *"Heard nothing
3× in a row — your mic may have changed."*

An **empty transcription** — audio that clears the gate, reaches the model, and decodes
to nothing — logged one line and returned. No streak, no notification, no auto-heal, no
earcon. So a microphone capturing audible but unintelligible sound (too quiet, wrong
device, badly attenuated) discarded for ever with `silent_streak` stuck at `0`, while
the guard built for that exact symptom saw a perfectly healthy microphone.

Measured on the machine in question: four consecutive empty transcriptions at capture
levels **0.0022–0.0069**, against **0.0199** for its own last successful dictation.

From your side the two failures are one event — hold the key, speak, nothing appears —
and the notification's advice is right for both. The empty path now runs the same
handler, and plays the error earcon for the reason the silent branch already gives:
nothing will be typed, and without a screen that is indistinguishable from a slow
decode. A *single* empty transcription still says nothing; the threshold of three
consecutive, reset by any success, is unchanged, so holding the key without speaking
costs you nothing.

## `default` is a route, not a microphone

The other half of that guard watches for the OS default input changing. It detects a
change by **comparing the device's name over time**.

On ALSA and PipeWire, `default` is a virtual entry that forwards to whichever device is
current — PortAudio lists it at its own index, alongside `sof-hda-dsp: - (hw:0,0)`.
Whatever microphone it points at, the name it reports is `default`. So the watcher
compares `default` with `default` for ever, and on the most common Linux audio stack the
proactive half of the guard **cannot fire**: the microphone behind the alias changes and
nothing on screen changes with it.

Reading through the alias needs a PipeWire or PulseAudio client library, which is not a
dependency worth taking on for one diagnostic. What YazSes can do is stop presenting the
alias as an answer — and name what is actually behind it:

```
OS default:    default
               → Raptor Lake-P/U/H cAVS Digital Microphone  (volume 65%)
               ⚠ that is a routing alias, not a microphone — the device behind it
                 can change without this name changing. Pin a real one to be sure:
                 yazses audio use <name>
```

That line is the diagnosis, and until now it could only be obtained by leaving YazSes
and reading `wpctl status` by hand. It appears in both `yazses audio status` and
`yazses doctor` — the surface the documentation points at first — and is suppressed when
the default is already a real device, because resolving a name to itself is noise.

Resolved through `wpctl`, the way this project already reaches `notify-send`, `xdotool`
and `wl-copy`: used when present, absent without complaint, never required, never
raising into the caller. It reads only the `Sources:` block, because `Sinks:` has the
same row shape and its own starred default and reporting a speaker as your microphone
would be worse than silence — and it says nothing at all when no source is marked
default, since which one is current is then genuinely unknown.

## `gitvoice` truncated a branch name, then acted on what was left

```
"delete branch feature slash login"       ->  git branch -D feature
"delete branch my slash deep slash name"  ->  git branch -D my
"create branch feature slash login"       ->  git checkout -b feature
```

`feature/login` spoken is *"feature slash login"*, and `feature/…` is the most common
branch convention there is. The ref patterns capture `[\w./-]+`, which stops at the
space, and `re.search` is unanchored — so everything after the first segment was
discarded and a **destructive command was emitted against a different ref than the one
named**, with nothing on screen showing that anything had been dropped.

A spoken `slash` now makes the `/` it sounds like. And every ref pattern is anchored at
**both** ends, which is this project's own rule for spoken grammars — *"a suffix match
swallows 'click undo' instead of typing it"*. A phrase the grammar does not model is now
refused, with the list of what it does understand, rather than half-understood and run.

Capitalisation you speak is still preserved, because git refs are case-sensitive:
*"delete branch Feature slash Login"* gives `Feature/Login`.

## Also

- **Coverage for three things that were correct.** The WCAG contrast used for secondary
  text in the settings window was verified against 3000 palettes rather than six; the
  four check-digit schemes were pinned to their published error-detection profiles
  (Verhoeff detects every adjacent transposition — corrupt one cell of its permutation
  table and 17 of 18 tests still pass, which is why the property is now tested rather
  than assumed); and word↔turn alignment was pinned to the property a long meeting
  depends on, that merging loses no word.
- **A ledger of 63 config keys** the loader accepts, validates and documents while no
  code reads them — `[audio] channels` is documented with default `1` while the recorder
  passes a literal `channels=1`. It fails when a 64th appears, or when one is wired and
  its entry goes stale.
