Root AGENTS.md is now the single source of truth for coding agents; CLAUDE.md, GEMINI.md and docs/CLAUDE.md are pointers. The product spec moves from docs/CLAUDE.md to PRODUCT.md, DESIGN.md records the shipped visual system, and docs/PERMISSIONS.md defines agent permission tiers rendered to .claude/, .codex/ and .gemini/. Adds docs/specs/ and docs/plans/, logs issues noticed (not fixed) in WorkBreakdown.md. Co-Authored-By: Claude Opus 5.5 <[email protected]>
6.5 KiB
whisper_web
Browser-first Whisper speech-to-text PWA. Bun 1.3 (runtime, bundler, test runner), React 19, TypeScript,
bun:sqlite, Passport.js; optional Python faster-whisper sidecar (server-py/) for the server engine.
AGENTS.md is the single source of truth for agents here. CLAUDE.md, GEMINI.md, and docs/CLAUDE.md
only point here. Deeper context on demand: docs/Architecture.md, docs/Decisions.md (D-### ids
cited throughout the code), docs/WorkBreakdown.md (milestones + backlog), PRODUCT.md, DESIGN.md.
Commands
Install: bun install --frozen-lockfile
Dev: bun dev
Test one file: bun test tests/export.test.ts | One case: bun test tests/models.test.ts -t "dtype"
Test (fast, ~6 s): bun test --path-ignore-patterns='tests/live-real-model.test.ts'
Test all: bun test — ~17 min: tests/live-real-model.test.ts downloads and runs real Whisper
weights whenever huggingface.co is reachable. Run it only when touching the live pipeline or decoding.
Currently 1 known failure there (en_alien timeout — see WorkBreakdown "Noticed, not fixed").
Build: bun run build (rewrites dist/)
Opt-in real sidecar test (needs pip install -r server-py/requirements.txt): FW_INTEGRATION=1 bun test tests/transcribe.test.ts
Lint / typecheck: none configured (typescript is not installed). TODO(maintainer): confirm whether one is wanted.
Browser-only behaviour (offline PWA, mic, WebGPU) has no automated coverage and there is no browser in
the dev environment: follow tests/offline.md, tests/live.md, tests/server-engine.md and say
plainly when a manual check is still pending — never claim it was verified.
CI (.gitlab-ci.yml) only builds Docker images — nothing else runs your tests.
Rules
- Bun only. No node/npm/npx/yarn/pnpm, vite/webpack, jest/vitest, express,
ws, better-sqlite3, dotenv. UseBun.serve()routes + HTML imports,bun:sqlite, built-inWebSocket,Bun.file,Bun.$. Bun API docs:node_modules/bun-types/docs/. - Two engines, two libraries (D-013). Browser inference = Transformers.js, inside
src/worker/inference.worker.tsonly — never on the main thread. Server inference = faster-whisper viaserver-py/sidecar.py— never Transformers.js on the server. @huggingface/transformersis patched and a devDependency on purpose. It is pinned bypatches/@huggingface%[email protected](D-027/D-028) and kept out of runtime deps so the Docker image stays small (D-031). Upgrading or moving it means redoing the patch: ask first.- No cross-origin subresources. Every response carries COOP/COEP
require-corp(D-023,src/index.ts); a CDN script/style/font will be blocked. Self-host it (onnxruntime is served from/ort/). - Selecting a model never downloads it (D-020). Only a transcription start or the explicit Download
button posts
loadto the worker. - Server modules take dependencies by injection (
createTranscribeHandlers(deps)pattern) so tests run againsttests/fixtures/fake_sidecar.py; validatelanguage/taskwithparseLanguageAndTaskbefore anything reaches the sidecar (D-032). Every stateful server module has a test file. - Errors go through
classifyErrorinsrc/lib/errors.ts(pattern-matched, D-030) — don't add error class hierarchies. - Configuration is runtime env only (D-015) — no build-time flags baked into the image. New variables go in README's env table.
- Anonymous users must keep every feature except the server engine; browser-engine audio never leaves the device (Architecture §5 invariants).
Workflow
Required skills: superpowers, impeccable. Check at session start; install if missing.
Specs → docs/specs/NNN-slug.md (permanent). Plans → docs/plans/NNN-slug.md while in progress;
git rm them before merging — the spec is the record. (Overrides the superpowers default paths.)
Anything beyond a trivial fix: brainstorm → spec (sign-off) → plan (sign-off) → TDD → review. TDD means failing test first, watch it fail, minimal code, watch it pass, commit. Tick plan checkboxes as you go so an interrupted session is resumable. Trivial changes (typo, version bump, one-line config): say "trivial, skipping spec" and proceed.
UI work: use impeccable — craft for new, audit/polish on existing, harden before shipping.
Design context: PRODUCT.md, DESIGN.md.
Architecture
src/index.ts -> Bun.serve entry: routes, /api/*, isolation headers, dev worker bundling, prod dist/ serving
src/App.tsx, src/components/ -> React UI
src/lib/ -> pure client logic (audio, live segmenter, model catalog, engines, errors) — most unit tests target this
src/worker/ -> inference worker + typed postMessage protocol (shared with UI)
src/pwa/ -> hand-rolled service worker (D-007); src/server/ -> auth, db, sidecar supervisor, live WS
server-py/ -> faster-whisper sidecar (JSON-lines over stdio)
Full picture: docs/Architecture.md — read it before your first change in a session.
Boundaries
Never touch: dist/ (build output), src/pwa/icons/ (generated by bun run icons), data/ (local SQLite).
Ask first: new runtime dependencies · changing or regenerating the transformers patch · auth/session
code (src/server/auth.ts, src/server/db.ts) · Dockerfile / .gitlab-ci.yml · anything
destructive · work the approved plan does not cover.
If the task turns out much larger than the plan assumed: stop, report, re-plan.
Docs duties
Same commit as the code, not "later":
docs/Architecture.md— when a module, boundary, or data flow changes.docs/Decisions.md— append at the bottom with the nextD-###id; never rewrite an Accepted entry, supersede it. Cite the id in code comments where the decision bites.docs/WorkBreakdown.md— tick items as they land; anything noticed and deliberately not fixed goes under "Noticed, not fixed". NoTODOcomments in source.docs/PERMISSIONS.md— together with every tool permission file, in one commit.
Git
Commit subjects: plain imperative sentence describing the change (e.g. "Show "Loading model from
cache" instead of "Downloading" when weights are already cached"). Small, each leaving tests green.
TODO(maintainer): confirm branch naming and whether work lands directly on main.
Done means verified
Before saying done: relevant tests run in this session and seen passing · bun run build succeeds
if src/ or scripts/ changed · matches the approved spec · docs duties complete · git status clean
and the diff is only what you intended.