docs: record the audio clock, and that CI is green
Build & publish Arch package / arch-package (push) Successful in 2m2s
CI / check (push) Successful in 2m18s
Search index maintenance / maintain-index (push) Successful in 6s
CI / e2e (push) Successful in 4m33s

The e2e job passes on both engines for the first time, so the three
files that describe it as red are wrong. Also records the two things
that made it findable: the CI container is reproducible under Docker,
and the app's own audio stack had to be the thing measured.
This commit is contained in:
2026-08-12 14:03:09 -04:00
parent 0a25bca128
commit d681a7223e
4 changed files with 92 additions and 25 deletions
+54
View File
@@ -1529,3 +1529,57 @@ Seven more things worth keeping:
removed. Nothing is paid for it — the slot's light-DOM children are
in the DOM either way; a conditional `<slot>` only stops projecting
them.
## A dependency with a rate has to be checked at its rate
Same pass, after the plan's three landings: the `e2e` job's red history
turned out to be one measurement being wrong, and the fix was worth the
generalisation.
`ci.yml` had made ALSA's `null` plugin the container's default device,
with a comment saying it "advances its pointer on a timer, so playback
is consumed at real-time rate". It does not. Measured in the CI image
under Docker, through beep and oto with the same `speaker.Init`
arguments `player.InitSpeaker` uses:
| default device | 3000 ms of audio consumed in |
|---|---|
| `type null` | **2.96 ms** |
| PulseAudio null sink | **3762 ms** |
A thousand times too fast. Every track finished instantly, so the
position reset to zero while the UI kept interpolating, and three specs
failed on a clock that never moved — 1719 s adrift, which reads
exactly like the `H-3` bug Phase 2 fixed. `check` and `e2e` are both
green now, 54 specs on Chromium and 54 on WebKit.
Five things worth keeping:
- **A dependency with a *rate* needs a check at its rate.** Installing
a sound device and asserting it exists is not the same as asserting
it plays. The job now plays three seconds and fails if they take
under two, in a step called "The sink plays at real time" — so the
next regression names itself instead of surfacing three steps later
as an app bug.
- **A failure that succeeds quietly is the expensive kind.**
`InitSpeaker` returns nil in ~3 ms against both devices. Nothing
logged, nothing errored; the only symptom was arithmetic in three
specs. Two sessions read that as a flake and one as a possible
WebKit regression.
- **The CI container is reproducible locally, and nobody had tried.**
`docker run --rm ubuntu:24.04` reproduced the whole thing in four
minutes and let the fix be verified — including under the private
session bus and Xvfb `dev-headless.sh` runs the app in — before it
was pushed. Every previous attempt to reason about this job reasoned
from the *commits* instead, because the log looked unreachable
(which it was not either).
- **Test the stack you ship, not one that resembles it.** `aplay`
showed the same 1000× gap and would have been enough to *diagnose*.
It would not have shown that oto opens the pulse-backed device at
all, which is the thing that had to be true for the fix to work; a
fifteen-line Go program using the app's own `speaker.Init` did.
- **The comment was the bug's hiding place.** "Measured: InitSpeaker
succeeds in ~36 ms and all six playback specs pass" was true when
written and had been carried forward through every subsequent read of
that file, including two this session. A measurement in a comment
needs the same expiry as one in a plan.
@@ -1924,12 +1924,26 @@ route from the albums grid to `track-details`.
And two things this pass found rather than inherited:
- **Name the dialogs.** One helper, eight call sites.
- **CI's e2e job cannot go green until the container's audio clock
does.** Three specs assert on a position that does not advance
there. Either the container gets a sink that really consumes, or
those three specs learn to skip when it does not — but silently
loosening a tolerance would delete the only assertions this repo has
that the player tells the truth.
**CI is green, and the audio clock was the last thing between it and
green.** Fixed the same pass: `ci.yml` used ALSA's `null` plugin on the
belief that it paces, and it does not — measured in the CI image
through beep and oto with `player.InitSpeaker`'s own arguments,
**3000 ms of audio consumed in 2.96 ms**, against **3762 ms** through a
PulseAudio null sink. Every track finished instantly, so the position
reset to zero and three specs failed on a clock that never moved. It
looked like a flake because `InitSpeaker` succeeds either way, in ~3 ms
either way. `check` and `e2e` now both pass, **54 specs on Chromium and
54 on WebKit** — the first fully green run this plan has had.
Two things about how that was found are worth carrying forward. The fix
was **verified in `ubuntu:24.04` under Docker before it was pushed**,
including under the private session bus and Xvfb `dev-headless.sh` uses
— the CI container is reproducible locally, which nothing had tried.
And the sink is now **checked like the dependency-with-a-rate that it
is**: a step plays three seconds and fails if they take under two,
because otherwise the failure surfaces three steps later as "the
elapsed clock is 19 s adrift" and reads as an app bug.
## Phase 6 — Explore starts the conversation