Agent completed work jubii
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
# Decisions
|
||||
|
||||
Lightweight ADR log. One entry per decision that shapes the codebase. Statuses:
|
||||
**Accepted** (act on it), **Proposed** (default choice, revisit before building on it),
|
||||
**Superseded** (kept for history, points to replacement).
|
||||
|
||||
Add new entries at the bottom with the next `D-###` id. Never rewrite an Accepted
|
||||
entry to mean something else — supersede it.
|
||||
|
||||
---
|
||||
|
||||
## D-001 — Bun as the only runtime and toolchain
|
||||
**Status:** Accepted (mandated by CLAUDE.md)
|
||||
Bun replaces Node, vite/webpack (via `bun build` + HTML imports), jest/vitest
|
||||
(`bun test`), express (`Bun.serve`), better-sqlite3 (`bun:sqlite`), and dotenv.
|
||||
**Consequence:** no vite plugin ecosystem; PWA assets (service worker, manifest) are
|
||||
wired by hand instead of via a plugin.
|
||||
|
||||
## D-002 — Transformers.js (`@huggingface/transformers`) for all Whisper inference
|
||||
**Status:** Accepted (mandated by CLAUDE.md)
|
||||
Same library in the browser (WebGPU/WASM) and on the server (ONNX Runtime under Bun).
|
||||
**Why:** one model catalog, one pipeline API, no native whisper.cpp build step.
|
||||
**Trade-off:** server CPU inference is slower than whisper.cpp; acceptable — the server
|
||||
engine is a convenience for logged-in users, not the primary path.
|
||||
|
||||
## D-003 — Inference runs in a dedicated Web Worker
|
||||
**Status:** Accepted
|
||||
Model load + transcription block for seconds-to-minutes; running them on the main
|
||||
thread would freeze the UI. Communication via a small typed postMessage protocol
|
||||
(`src/worker/protocol.ts`) shared between UI and worker.
|
||||
|
||||
## D-004 — WebGPU with automatic WASM fallback
|
||||
**Status:** Accepted
|
||||
Detect via `navigator.gpu?.requestAdapter()` inside the worker; pass
|
||||
`device: "webgpu" | "wasm"` to the pipeline. The active backend is surfaced in the UI
|
||||
but users don't have to configure anything. Encoder-heavy Whisper benefits ~10-40× from
|
||||
WebGPU where available (Chromium); WASM keeps Firefox/Safari working.
|
||||
|
||||
## D-005 — Model weights cached via the browser Cache API (Transformers.js default)
|
||||
**Status:** Accepted
|
||||
Transformers.js already persists Hub downloads in the Cache API (`useBrowserCache`).
|
||||
We do **not** re-cache weights in the service worker — the SW precaches only the app
|
||||
shell. **Consequence:** offline = installed PWA + at least one previously loaded model;
|
||||
the UI must show per-model "downloaded / not downloaded" state (query
|
||||
`caches` for hub URLs) so users can prepare for offline use.
|
||||
|
||||
## D-006 — Quantized model catalog, curated not exhaustive
|
||||
**Status:** Proposed
|
||||
Expose a curated list (`src/lib/models.ts`): `whisper-tiny(.en)`, `base(.en)`,
|
||||
`small(.en)`, `medium(.en)`, `large-v3-turbo`, each with sensible dtype defaults
|
||||
(e.g. q4/q8 on WASM, fp16/q4 on WebGPU) and a size estimate shown before download.
|
||||
**Why not free-form model ids:** predictable UX, tested combinations, honest
|
||||
size/VRAM warnings. Revisit if users ask for arbitrary Hub ids.
|
||||
|
||||
## D-007 — PWA: hand-rolled service worker, app-shell precache
|
||||
**Status:** Accepted
|
||||
No Workbox/vite-plugin-pwa (conflicts with D-001 tooling). `bun build` emits hashed
|
||||
assets to `dist/`; a small build step writes the precache manifest into `sw.ts`.
|
||||
Strategy: precache app shell, cache-first for static assets, network-only for `/api/*`.
|
||||
|
||||
## D-008 — Passport.js via a connect-compat adapter over Bun.serve
|
||||
**Status:** Accepted (Passport mandated by CLAUDE.md; adapter is the how)
|
||||
Passport expects connect/express-style `(req, res, next)` and `req.login()`. We are
|
||||
not allowed to use express (D-001), so `src/server/auth.ts` provides a thin adapter:
|
||||
wrap Bun's `Request` in a minimal req/res shim, invoke the passport authenticate
|
||||
chain, translate the outcome back to a `Response`. Local (username/password)
|
||||
strategy first; other strategies plug into the same adapter later.
|
||||
**Alternative rejected:** dropping Passport for hand-rolled auth — CLAUDE.md names
|
||||
Passport explicitly.
|
||||
|
||||
## D-009 — Sessions + users in `bun:sqlite`
|
||||
**Status:** Accepted
|
||||
Single-file SQLite DB (path from env, default `./data/app.db`). Tables: `users`
|
||||
(id, username, password_hash — Argon2 via `Bun.password`), `sessions`
|
||||
(id, user_id, expires_at). Cookie: HttpOnly, SameSite=Lax, Secure in production.
|
||||
No Redis/Postgres until there's a scaling reason (would be `Bun.redis` / `Bun.sql`).
|
||||
|
||||
## D-010 — Engine abstraction: `BrowserEngine` and `ServerEngine` behind one interface
|
||||
**Status:** Accepted
|
||||
`src/lib/engine.ts` defines `Engine { load(model), transcribe(audio), onProgress }`.
|
||||
The UI never branches on engine type beyond the toggle. Keeps the server engine
|
||||
(milestone M5) from leaking into UI code built in M1–M4.
|
||||
|
||||
## D-011 — Server engine transport: streaming HTTP response first, WebSocket if needed
|
||||
**Status:** Proposed
|
||||
`POST /api/transcribe` returns a streamed (chunked JSON-lines) response with progress
|
||||
and partial transcripts. Simpler than WebSockets (no connection lifecycle, works with
|
||||
plain fetch + ReadableStream). Switch to `Bun.serve` websockets only if we need
|
||||
bidirectional control (e.g. cancel mid-run) that abort signals can't cover.
|
||||
|
||||
## D-012 — Audio decoding on the main thread via Web Audio API
|
||||
**Status:** Accepted
|
||||
`decodeAudioData` (handles most audio and audio-in-video containers) → mono downmix →
|
||||
resample to 16 kHz → transfer Float32Array to worker (zero-copy). No ffmpeg.wasm
|
||||
unless we hit real container-support gaps; note in `src/lib/audio.ts` if we do.
|
||||
Reference in New Issue
Block a user