# v2.25.1 — surfaces that stated things that were not true

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

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

Every fix here is the same shape: a command printed something confident and wrong.
None of them crashed, none failed a test, and every one was found by **running YazSes
on a real machine and reading what it said** — several of them within minutes of each
other, because once you start comparing what two commands say about the same thing the
disagreements are not subtle.

Three of these describe the *same underlying fact*, arrived at from three directions:
**speech models answer near-silence with a confident invented word rather than with
nothing.** Nothing in YazSes accounted for that.

## Your microphone gate may be below your room

`yazses doctor --mic` exists to warn when the room's resting noise level already
reaches `vad_threshold` — because then ordinary silence passes the gate, reaches the
model, and is transcribed. On a real machine it printed:

```
[OK] Mic level: ambient 0.0010 under vad_threshold 0.0005
```

0.0010 is not under 0.0005. The warning was conditioned on `not stats.is_silent`, and
`is_silent` is measured against a **fixed** floor (0.002) that has nothing to do with
your gate — so for any `vad_threshold` below that floor, the warning was suppressed
across exactly the band where the gate sits under the room, and the OK branch then
asserted "under" for a value that was over.

**The guard was disabled precisely when it was needed.** It now compares against your
gate and nothing else, and says what the consequence is: silence passes and is typed
as invented words. The fix is `yazses mic-level --set`.

## Silence transcribed to a confident word

Two seconds of digital silence through `yazses transcribe`:

```
Wrote out.txt
$ cat out.txt
You
```

A word, with a start and an end time, present in the JSON as an utterance. The
empty-transcript note added in v2.25.0 cannot see it — the transcript is not empty.

No property of the *output* separates an invented word from a real one, so the check is
on the **input**, where it is unambiguous: audio with no signal in it cannot contain
speech. A muted microphone, an input device held by another application, and capture
pointed at the wrong device all produce exactly this, and all three are ordinary.

Measured on the peak, not the mean — an hour of interview with sparse talking averages
to almost nothing, so a mean-based gate would call a real recording silent.

## `yazses verify` reported a count instead of the word

Run in a quiet room with nobody speaking:

```
[OK] Signal: level 0.0013 clears the gate (0.0005)
[OK] Transcription: produced 1 word(s)
✓ Dictation works end to end on this machine.
```

The one word was the same hallucination. A count cannot be checked against what you
said, so it cannot contradict anything. The line now reads `heard "You"` — the person
running this is the only one who knows what they said, so the evidence belongs in front
of them.

## `yazses report` understated the learning corpus 430x

Two surfaces, one corpus:

```
yazses report          corpus: 3.0 MB
yazses corpus status   size:   1294.9 MB
```

`report` sized `corpus.db` and nothing else; the encrypted audio clips beside it are
almost all of a real corpus. That number is what gets attached to bug reports, so a
maintainer reading *3 MB* rules the corpus out of a disk problem it is causing — and it
is what you check against `[learning] max_corpus_mb`, so someone 2.5× over the cap
reads it as comfortably under.

## The release gate certified a platform on one architecture of a pair

`release-complete.yml` counts two bundles per platform and explains why: "at least one
.dmg" is what let v2.20.0 and v2.21.0 print *All platforms published* while carrying no
Intel bundle. The wait was hardened at the time; **the check it delegates the verdict
to was not**, and that check also decides whether an incomplete release keeps the
"Latest" label. A release carrying only an arm64 `.dmg` passed it, was never demoted,
and went on pointing Intel users at a download that did not exist.

Run against the real releases after the fix — v2.20.0 and v2.21.0 now report exactly
the halves they shipped without, and v2.25.0 passes with both.

A second gate fix: the wait covered four of the five channels the tag push publishes and
omitted the container image, so the report named Docker missing while it was still
building. Both the wait and the verdict now go through one implementation, which is what
let them drift apart in the first place.

## Also

- **`yazses tune` looked like a hang.** Against a real corpus it printed one line and
  nothing more for over eight minutes — the re-transcription of 3280 clips, silently,
  on a run that would take about two hours. The count is a metadata query known before
  any audio is touched, so it now announces the total and counts up.
- **A missing file was reported as a format problem.** Asking the MCP server for a path
  that does not exist answered *"probably not an audio or video file, or it is
  truncated"* when the real error was `No such file or directory` — sending the reader
  to inspect the file's contents instead of its name. `load_audio` had promised
  `FileNotFoundError` in its docstring and never raised it.
- **Two status lines that read as faults.** `meeting list` described an undiarized
  8081-second meeting as `0 speaker(s)`, which reads as a failed recording rather than
  as speaker labelling never having been attempted; it now says `not diarized`. And
  `yazses status` printed `uptime: 48176.17s` — thirteen hours, in seconds, to two
  decimals never measured.
- **A privacy guard answered differently per Python.** Carried over from v2.25.0's
  cycle: `is_loopback_endpoint` trusted a stdlib predicate that changed behaviour in
  3.13, so an IPv4-mapped loopback endpoint was refused on 3.11 and 3.12.
