engineering / testing

Three layers, none optional. Every release passed all of them.

The numbers below are the gate this exact deploy went through — not a badge from some earlier good day.

unit
browser unit
integration
qa-to-docs journeys
live QA
post-deploy
The principle

Each layer catches what the one below cannot see

A service with no backend still has plenty to break: audio parsing, a cross-origin engine import, an LLM API, a CDN's caching rules. One test layer cannot cover that spread — three small ones can.

1 · Unit (node --test)

Pure-logic tests of the app modules: audio content-sniffing, config, the chat tool registry's validation, the declared workflow machine, the debug store. Fast enough to run on every change.

1b · Browser unit (QUnit in Chromium)

The same modules re-tested under REAL browser semantics — genuine File/Blob objects, genuine localStorage, genuine custom-element upgrade for the site nav. The page ships with the site, so you can watch the suite pass against this very deploy, in your own browser: run the browser unit tests now.

2 · Integration (Playwright)

Headless Chromium boots the REAL app: the engine imports cross-origin, the full action contract publishes, a real voice-note fixture ingests, the debug and chat panes work. A second suite scripts OpenRouter and drives the whole chat tool loop — including the model genuinely redrawing the infographic.

3 · Live QA (post-deploy)

After the deploy, plain fetches against the live site: the version it claims, every cache-busted asset, the samples and prompts, the engine origin + CORS, and every GitHub/self link on the record pages. The pipeline checks its own work.

+ · QA-to-docs journeys

The key user journeys — key setup, the one pass, the chat — replayed with a scripted, deterministic model and screenshotted at each story moment. An image-diff gate compares every shot to its committed baseline: pixel noise is ignored, a real UI change either fails the run (a caught visual regression) or refreshes the baseline with the feature — and those screenshots ARE the user docs' images. One run, three outputs.

The receipts that matter most

Bugs these layers actually caught

The hallucinated transcript (issue 025)

Some .ogg voice notes reached the model undecodable — and came back as fluent, confident transcripts of audio nobody recorded. A unit-tested content-sniffer (magic bytes, never the file extension) now guarantees the model receives real audio, and a six-variant regression matrix keeps it that way.

The stale JavaScript deploy (issue 026)

Fresh HTML paired with ten-minute-old cached modules broke a new feature silently. Now CI stamps every asset URL per release and the live-QA layer fetches each one — this class of failure cannot ship quietly again.

The misleading "Failed to fetch" (issue 032)

A disabled OpenRouter key surfaced as a network error (the rejection carries no CORS headers, so the browser hides it). Found live via the app's own debug pane; fixed with a key-status re-check; pinned by an integration test.

The re-run that failed (issue 029)

Transcribing the same file twice failed with "not an audio file" — the engine silently dedupes identical uploads. The end-to-end test caught it; the fix reuses the deduped queue item; the case is now part of the gate.

Receipts