Bootstrap agent-ready docs and permissions (D-036)
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]>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
c1b710cb23
commit
0ece4b3a2a
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Bash(git status:*)", "Bash(git diff:*)", "Bash(git log:*)", "Bash(git show:*)",
|
||||
"Bash(git branch --list:*)", "Bash(git rev-parse:*)", "Bash(git add:*)", "Bash(git commit:*)",
|
||||
"Bash(bun install --frozen-lockfile:*)", "Bash(bun test:*)", "Bash(bun run build:*)",
|
||||
"Bash(bun run icons:*)", "Bash(bun dev:*)"
|
||||
],
|
||||
"ask": [
|
||||
"Bash(git push:*)", "Bash(git merge:*)", "Bash(git rebase:*)", "Bash(git switch:*)",
|
||||
"Bash(git restore:*)", "Bash(git commit --amend:*)", "Bash(rm:*)",
|
||||
"Bash(bun add:*)", "Bash(bun remove:*)", "Bash(bun update:*)", "Bash(bun upgrade:*)",
|
||||
"Bash(bun patch:*)", "Bash(bunx:*)", "Bash(bun x:*)",
|
||||
"Bash(docker:*)", "Bash(pip install:*)", "Bash(python3 -m pip install:*)"
|
||||
],
|
||||
"deny": [
|
||||
"Bash(git push --force:*)", "Bash(git push -f:*)", "Bash(git push --force-with-lease:*)",
|
||||
"Bash(git reset --hard:*)", "Bash(git clean:*)", "Bash(git add -f:*)", "Bash(git add --force:*)",
|
||||
"Bash(git commit --no-verify:*)", "Bash(git commit -n:*)",
|
||||
"Bash(rm -r:*)", "Bash(rm -R:*)", "Bash(rm -rf:*)", "Bash(rm -fr:*)", "Bash(rm -Rf:*)",
|
||||
"Bash(rm -fR:*)", "Bash(rm --recursive:*)", "Bash(sudo:*)",
|
||||
"Read(./.env)", "Read(./.env.*)", "Read(./data/**)"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
sandbox_mode = "workspace-write"
|
||||
approval_policy = "on-request"
|
||||
@@ -0,0 +1,23 @@
|
||||
# Rendered from docs/PERMISSIONS.md — change both in one commit.
|
||||
# No allow rules on purpose: in Codex `allow` means "run outside the sandbox".
|
||||
|
||||
# Deny
|
||||
prefix_rule(pattern = ["git", "push", ["--force", "-f", "--force-with-lease"]], decision = "forbidden",
|
||||
justification = "Force-push rewrites shared history.")
|
||||
prefix_rule(pattern = ["git", "reset", "--hard"], decision = "forbidden", justification = "Discards work.")
|
||||
prefix_rule(pattern = ["git", "clean"], decision = "forbidden", justification = "Deletes untracked files.")
|
||||
prefix_rule(pattern = ["git", "add", ["-f", "--force"]], decision = "forbidden",
|
||||
justification = "Stages ignored files, e.g. secrets.")
|
||||
prefix_rule(pattern = ["git", "commit", ["--no-verify", "-n"]], decision = "forbidden", justification = "Skips hooks.")
|
||||
prefix_rule(pattern = ["rm", ["-r", "-R", "-rf", "-fr", "-Rf", "-fR", "--recursive"]], decision = "forbidden",
|
||||
justification = "Recursive delete.")
|
||||
prefix_rule(pattern = ["sudo"], decision = "forbidden", justification = "No privilege escalation.")
|
||||
# Ask
|
||||
prefix_rule(pattern = ["git", ["push", "merge", "rebase", "switch", "restore"]], decision = "prompt")
|
||||
prefix_rule(pattern = ["git", "commit", "--amend"], decision = "prompt")
|
||||
prefix_rule(pattern = ["rm"], decision = "prompt")
|
||||
prefix_rule(pattern = ["bun", ["add", "remove", "update", "upgrade", "patch", "x"]], decision = "prompt")
|
||||
prefix_rule(pattern = ["bunx"], decision = "prompt")
|
||||
prefix_rule(pattern = ["docker"], decision = "prompt")
|
||||
prefix_rule(pattern = ["pip", "install"], decision = "prompt")
|
||||
prefix_rule(pattern = ["python3", "-m", "pip", "install"], decision = "prompt")
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"tools": {
|
||||
"allowed": [
|
||||
"run_shell_command(git status)", "run_shell_command(git diff)", "run_shell_command(git log)",
|
||||
"run_shell_command(git show)", "run_shell_command(git branch --list)",
|
||||
"run_shell_command(git rev-parse)", "run_shell_command(git add)", "run_shell_command(git commit)",
|
||||
"run_shell_command(bun install --frozen-lockfile)", "run_shell_command(bun test)",
|
||||
"run_shell_command(bun run build)", "run_shell_command(bun run icons)", "run_shell_command(bun dev)"
|
||||
],
|
||||
"exclude": [
|
||||
"run_shell_command(git push --force)", "run_shell_command(git push -f)",
|
||||
"run_shell_command(git push --force-with-lease)", "run_shell_command(git reset --hard)",
|
||||
"run_shell_command(git clean)", "run_shell_command(git add -f)", "run_shell_command(git add --force)",
|
||||
"run_shell_command(git commit --no-verify)", "run_shell_command(git commit -n)",
|
||||
"run_shell_command(rm -r)", "run_shell_command(rm -R)", "run_shell_command(rm -rf)",
|
||||
"run_shell_command(rm -fr)", "run_shell_command(rm -Rf)", "run_shell_command(rm -fR)",
|
||||
"run_shell_command(rm --recursive)", "run_shell_command(sudo)"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -38,3 +38,8 @@ __pycache__/
|
||||
|
||||
# Finder (MacOS) folder config
|
||||
.DS_Store
|
||||
|
||||
# agent-local files (D-036)
|
||||
CLAUDE.local.md
|
||||
.claude/settings.local.json
|
||||
.claude/worktrees/
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
# 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. Use `Bun.serve()` routes + HTML imports, `bun:sqlite`, built-in `WebSocket`, `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.ts` only — never on the main thread. Server inference = faster-whisper
|
||||
via `server-py/sidecar.py` — never Transformers.js on the server.
|
||||
- **`@huggingface/transformers` is patched and a devDependency on purpose.** It is pinned by
|
||||
`patches/@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 `load` to the worker.
|
||||
- **Server modules take dependencies by injection** (`createTranscribeHandlers(deps)` pattern) so tests
|
||||
run against `tests/fixtures/fake_sidecar.py`; validate `language`/`task` with
|
||||
`parseLanguageAndTask` before anything reaches the sidecar (D-032). Every stateful server module has a test file.
|
||||
- **Errors go through `classifyError` in `src/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 next `D-###` 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". No `TODO` comments 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.
|
||||
@@ -0,0 +1,9 @@
|
||||
# CLAUDE.md
|
||||
|
||||
**`AGENTS.md` is the single source of truth for this repository.** Read it now and follow it exactly.
|
||||
|
||||
@AGENTS.md
|
||||
|
||||
This file exists only so Claude Code finds the rules, and for Claude-specific configuration that
|
||||
AGENTS.md cannot express. It contains no project rules of its own — add those to `AGENTS.md`.
|
||||
If anything here ever conflicts with `AGENTS.md`, `AGENTS.md` wins.
|
||||
@@ -0,0 +1,32 @@
|
||||
# Design
|
||||
|
||||
Derived from the shipped `src/index.css` (single stylesheet, plain CSS, no framework). Describes what
|
||||
exists, not an aspiration. Update in the same commit as visual changes.
|
||||
|
||||
## Foundations
|
||||
|
||||
- **Theme:** dark only. `:root` sets `color: #e8e8e8; background-color: #16181d` (also the PWA
|
||||
`theme_color`). `color-scheme: light dark` is declared but there is no light palette.
|
||||
- **Tokens (`:root`):** `--panel #1f232b` · `--border #343a46` · `--accent #7aa2f7` · `--muted #9aa3b2`.
|
||||
Everything else is a hard-coded hex.
|
||||
- **Semantic colours in use (hard-coded):** success/ready text `#9ad6ae` (bg `#1d2b23`, border `#3d6b4f`);
|
||||
error text `#f2b8b5` (bg `#2a1c1e`, border `#8b3a3a`); live/recording `#e0605e`; raised surfaces
|
||||
`#262b34` / `#232a38`.
|
||||
- **Type:** `Inter, system-ui, …` stack — Inter is not loaded, so it renders in the system font unless
|
||||
installed locally. Body line-height 1.5; `h1` 2.2rem / 1.15. Any webfont must be self-hosted (COEP,
|
||||
see AGENTS.md).
|
||||
- **Layout:** single centred column, `.app { max-width: 720px; padding: 2.5rem 1.25rem 3rem; gap: 1.5rem }`,
|
||||
`body { min-width: 320px }`.
|
||||
- **Radius:** 8px for controls (most common); 10–14px for panels/cards; 999px for pills/badges.
|
||||
- **Motion:** `0.15s` colour/border transitions; `pulse` (live dot) and `blink` (cursor) animations,
|
||||
disabled under `prefers-reduced-motion`.
|
||||
|
||||
## Components
|
||||
|
||||
One BEM-ish class block per React component in `src/components/` (`.model-picker`, `.dropzone`,
|
||||
`.live-controls`, `.engine-toggle`, `.auth-panel`, `.update-toast`, …), with modifier classes like
|
||||
`-selected`, `-active`, `-disabled`. Interactive states use `:hover:enabled` and `:focus-visible`.
|
||||
`.visually-hidden` exists for screen-reader-only text.
|
||||
|
||||
TODO(maintainer): confirm whether the dark-only theme is intentional, and whether hard-coded semantic
|
||||
colours should become tokens.
|
||||
@@ -0,0 +1,7 @@
|
||||
# GEMINI.md
|
||||
|
||||
**`AGENTS.md` is the single source of truth for this repository.** Read `./AGENTS.md` before doing
|
||||
anything else and follow it exactly.
|
||||
|
||||
This file exists only so Gemini CLI finds the rules. It contains no project rules of its own — add
|
||||
those to `AGENTS.md`. If anything here ever conflicts with `AGENTS.md`, `AGENTS.md` wins.
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
# Product
|
||||
|
||||
Whisper Web — on-device speech-to-text. A browser-first transcription app powered by OpenAI Whisper,
|
||||
running through [Transformers.js](https://github.com/huggingface/transformers) (`@huggingface/transformers`).
|
||||
(Moved here from the original `docs/CLAUDE.md` §1.)
|
||||
|
||||
## Users and what they get
|
||||
|
||||
**Anonymous users (no login) can:**
|
||||
- Run Whisper models fully in the browser — no audio or transcript leaves the device.
|
||||
- Choose which Whisper model to use (tiny → large, English-only and multilingual, quantized variants).
|
||||
- Keep working when the network — or our own server — is offline (installable PWA + cached model weights).
|
||||
- Accelerate inference with WebNN or WebGPU (auto-selected in that order, D-017; never WebGPU on
|
||||
Firefox, D-026), falling back to WASM automatically.
|
||||
- Transcribe an uploaded audio/video file, or live from the microphone (D-016).
|
||||
- Export transcripts as `.txt` / `.srt` and manage cached model storage.
|
||||
|
||||
**Logged-in users (Passport.js, when `AUTH_ENABLED`) get all of the above, plus:**
|
||||
- A toggle to choose where inference runs: **their own device** (browser engine) or **the server**
|
||||
(server engine).
|
||||
- The server engine MUST use [faster-whisper](https://github.com/SYSTRAN/faster-whisper) (CTranslate2) —
|
||||
not Transformers.js (D-013). faster-whisper cannot run in a browser, so the browser engine stays on
|
||||
Transformers.js.
|
||||
|
||||
## Product principles (from `docs/Architecture.md` §5)
|
||||
|
||||
- **Privacy:** browser-engine audio never touches the network.
|
||||
- **Anonymous-first:** login only unlocks the server engine; nothing else is gated.
|
||||
- **Offline-first:** app shell + previously downloaded models work with no network.
|
||||
- **Honest status:** the UI must not claim a state it isn't in — e.g. "Downloading" vs "Loading model
|
||||
from cache" (D-033), no "Listening" before the model is ready (D-034).
|
||||
- **User-initiated downloads:** selecting a model never downloads it (D-020); large models on mobile
|
||||
get a warn/confirm gate (D-027–D-029).
|
||||
|
||||
## Out of scope (from `docs/WorkBreakdown.md`)
|
||||
|
||||
Arbitrary Hub model ids, OAuth providers, multi-user transcript storage, whisper.cpp server backend,
|
||||
i18n of the UI.
|
||||
|
||||
TODO(maintainer): confirm target audience and tone of voice for UI copy — not recorded anywhere.
|
||||
@@ -8,8 +8,11 @@ users can optionally run inference on the **server** instead, backed by
|
||||
[faster-whisper](https://github.com/SYSTRAN/faster-whisper) (CTranslate2)
|
||||
through a small Python sidecar (`server-py/`, Decisions.md D-013).
|
||||
|
||||
See `Architecture.md` for the design, `Decisions.md` for the decision log, and
|
||||
`CLAUDE.md` for the toolchain rules (Bun everywhere — no npm/node).
|
||||
See `docs/Architecture.md` for the design, `docs/Decisions.md` for the decision
|
||||
log, and `PRODUCT.md` for the product spec.
|
||||
|
||||
**AI coding agents:** read [`AGENTS.md`](AGENTS.md) — the single source of truth
|
||||
for commands, conventions and boundaries (Bun everywhere — no npm/node).
|
||||
|
||||
## Development
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Architecture
|
||||
|
||||
> Companion to `CLAUDE.md` (product spec & tooling rules) and `Decisions.md` (why things
|
||||
> are the way they are). This file describes **how the system is structured**. Update it
|
||||
> Companion to `../PRODUCT.md` (product spec), `../AGENTS.md` (agent & tooling rules) and
|
||||
> `Decisions.md` (why things are the way they are). This file describes **how the system is structured**. Update it
|
||||
> whenever the structure changes.
|
||||
|
||||
## 1. System overview
|
||||
|
||||
+9
-126
@@ -1,129 +1,12 @@
|
||||
# CLAUDE.md — Read this first, every session
|
||||
# docs/CLAUDE.md
|
||||
|
||||
This is the single source of truth for any agent working in this repo. If anything
|
||||
you're told conflicts with this file, this file wins.
|
||||
**`AGENTS.md` (repo root) is the single source of truth for agents in this repository.** Read
|
||||
`../AGENTS.md` and follow it exactly. This file contains no rules of its own.
|
||||
|
||||
## 1. What we are building
|
||||
This file used to hold the product spec and the Bun toolchain rules (it was the original `bun init`
|
||||
CLAUDE.md, extended). Its content now lives in:
|
||||
- Product spec ("what we are building") → `PRODUCT.md`
|
||||
- Bun toolchain and API rules → `AGENTS.md` § Rules
|
||||
- Bun API reference → `node_modules/bun-types/docs/`
|
||||
|
||||
A browser-first speech-to-text web app powered by OpenAI Whisper, running through
|
||||
[Transformers.js](https://github.com/huggingface/transformers) (`@huggingface/transformers`).
|
||||
|
||||
**Anonymous users (no login) can:**
|
||||
- Run Whisper models fully in the browser (no data leaves the device).
|
||||
- Choose which Whisper model to use (tiny → large, English-only and multilingual, quantized variants).
|
||||
- Keep working when the network — or our own server — is offline (installable PWA + cached model weights).
|
||||
- Accelerate inference with WebNN or WebGPU (auto-selected in that order, D-017), falling back to WASM automatically.
|
||||
- Transcribe from an uploaded audio/video file.
|
||||
|
||||
**Logged-in users (Passport.js) get all of the above, plus:**
|
||||
- A toggle to choose where inference runs: **their own device** (browser engine) or **the server** the app is deployed on (server engine).
|
||||
- The server engine MUST use [faster-whisper](https://github.com/SYSTRAN/faster-whisper) (CTranslate2) for inference — not Transformers.js (see Decisions.md D-013). faster-whisper cannot run in a browser (no WASM build of CTranslate2), so the browser engine stays on Transformers.js.
|
||||
|
||||
## 2 How to run things
|
||||
|
||||
|
||||
Default to using Bun instead of Node.js.
|
||||
|
||||
- Use `bun <file>` instead of `node <file>` or `ts-node <file>`
|
||||
- Use `bun test` instead of `jest` or `vitest`
|
||||
- Use `bun build <file.html|file.ts|file.css>` instead of `webpack` or `esbuild`
|
||||
- Use `bun install` instead of `npm install` or `yarn install` or `pnpm install`
|
||||
- Use `bun run <script>` instead of `npm run <script>` or `yarn run <script>` or `pnpm run <script>`
|
||||
- Use `bunx <package> <command>` instead of `npx <package> <command>`
|
||||
- Bun automatically loads .env, so don't use dotenv.
|
||||
|
||||
## APIs
|
||||
|
||||
- `Bun.serve()` supports WebSockets, HTTPS, and routes. Don't use `express`.
|
||||
- `bun:sqlite` for SQLite. Don't use `better-sqlite3`.
|
||||
- `Bun.redis` for Redis. Don't use `ioredis`.
|
||||
- `Bun.sql` for Postgres. Don't use `pg` or `postgres.js`.
|
||||
- `WebSocket` is built-in. Don't use `ws`.
|
||||
- Prefer `Bun.file` over `node:fs`'s readFile/writeFile
|
||||
- Bun.$`ls` instead of execa.
|
||||
|
||||
## Testing
|
||||
|
||||
Use `bun test` to run tests.
|
||||
|
||||
```ts#index.test.ts
|
||||
import { test, expect } from "bun:test";
|
||||
|
||||
test("hello world", () => {
|
||||
expect(1).toBe(1);
|
||||
});
|
||||
```
|
||||
|
||||
## Frontend
|
||||
|
||||
Use HTML imports with `Bun.serve()`. Don't use `vite`. HTML imports fully support React, CSS, Tailwind.
|
||||
|
||||
Server:
|
||||
|
||||
```ts#index.ts
|
||||
import index from "./index.html"
|
||||
|
||||
Bun.serve({
|
||||
routes: {
|
||||
"/": index,
|
||||
"/api/users/:id": {
|
||||
GET: (req) => {
|
||||
return new Response(JSON.stringify({ id: req.params.id }));
|
||||
},
|
||||
},
|
||||
},
|
||||
// optional websocket support
|
||||
websocket: {
|
||||
open: (ws) => {
|
||||
ws.send("Hello, world!");
|
||||
},
|
||||
message: (ws, message) => {
|
||||
ws.send(message);
|
||||
},
|
||||
close: (ws) => {
|
||||
// handle close
|
||||
}
|
||||
},
|
||||
development: {
|
||||
hmr: true,
|
||||
console: true,
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
HTML files can import .tsx, .jsx or .js files directly and Bun's bundler will transpile & bundle automatically. `<link>` tags can point to stylesheets and Bun's CSS bundler will bundle.
|
||||
|
||||
```html#index.html
|
||||
<html>
|
||||
<body>
|
||||
<h1>Hello, world!</h1>
|
||||
<script type="module" src="./frontend.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
With the following `frontend.tsx`:
|
||||
|
||||
```tsx#frontend.tsx
|
||||
import React from "react";
|
||||
import { createRoot } from "react-dom/client";
|
||||
|
||||
// import .css files directly and it works
|
||||
import './index.css';
|
||||
|
||||
const root = createRoot(document.body);
|
||||
|
||||
export default function Frontend() {
|
||||
return <h1>Hello, world!</h1>;
|
||||
}
|
||||
|
||||
root.render(<Frontend />);
|
||||
```
|
||||
|
||||
Then, run index.ts
|
||||
|
||||
```sh
|
||||
bun --hot ./index.ts
|
||||
```
|
||||
|
||||
For more information, read the Bun API docs in `node_modules/bun-types/docs/**.mdx`.
|
||||
See Decisions.md D-036.
|
||||
|
||||
@@ -1126,3 +1126,28 @@ change needs its own accuracy verification against
|
||||
`tests/live-accuracy.test.ts` and `tests/live-real-model.test.ts` (a
|
||||
repetition guard can change correct-transcript wording too) before landing,
|
||||
which is out of scope for this investigation.
|
||||
|
||||
## D-036 — Root `AGENTS.md` is the single source of truth for agents
|
||||
**Status:** Accepted (2026-10-29)
|
||||
`docs/CLAUDE.md` called itself the single source of truth, but Claude Code
|
||||
only auto-loads it when working inside `docs/`, other agents (Codex, Gemini,
|
||||
Cursor) never read it, and most of it was the generic `bun init` boilerplate
|
||||
(sample `Bun.serve`/HTML snippets) rather than repo-specific rules.
|
||||
**Decision:** a root `AGENTS.md` (read natively by Codex/Gemini, imported by
|
||||
the root `CLAUDE.md` via `@AGENTS.md`) holds the rules: verified commands, the
|
||||
conventions agents would otherwise break (Bun only, D-013 engine split, the
|
||||
transformers patch, COEP, D-020, D-030, D-032), boundaries, and docs duties.
|
||||
`CLAUDE.md`, `GEMINI.md`, and `docs/CLAUDE.md` are pointers only. The product
|
||||
spec from `docs/CLAUDE.md` §1 moved to `PRODUCT.md`; `DESIGN.md` records the
|
||||
shipped visual system (both read by the impeccable skill). Specs from the
|
||||
superpowers workflow live permanently in `docs/specs/`; plans live in
|
||||
`docs/plans/` only while work is in progress and are `git rm`'d before
|
||||
merging. Agent tool permissions are defined in `docs/PERMISSIONS.md` and
|
||||
rendered to `.claude/settings.json`, `.codex/`, `.gemini/settings.json`.
|
||||
**Kept as-is:** `docs/Architecture.md`, `docs/Decisions.md` (same filenames —
|
||||
renaming would break every existing reference) and `docs/WorkBreakdown.md`
|
||||
as the one backlog (its own rule: no separate backlog), rather than adding a
|
||||
`docs/TODO.md`.
|
||||
**Trade-off:** `AGENTS.md` is kept short (<150 lines) on purpose — rationale
|
||||
goes here, not there. Changes to it redirect every agent session, so review
|
||||
them like CI config.
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
# Agent permissions
|
||||
|
||||
Source of truth for what AI coding agents may run here. Tool files are renderings of this page:
|
||||
`.claude/settings.json`, `.codex/config.toml` + `.codex/rules/project.rules`, `.gemini/settings.json`.
|
||||
Change this page and every tool file in the same commit. (No Cursor/Copilot files: no sign the team uses them.)
|
||||
|
||||
## Verified commands
|
||||
|
||||
Run on 2026-10-29 (Bun 1.3.14). Only `worked` rows may appear in a tier, a tool file, or `AGENTS.md` Commands.
|
||||
|
||||
| Command | Result |
|
||||
|---|---|
|
||||
| `bun install --frozen-lockfile` | worked |
|
||||
| `bun test tests/export.test.ts` | worked — 28 pass |
|
||||
| `bun test tests/models.test.ts -t "dtype"` | worked — 5 pass, 14 filtered |
|
||||
| `bun test --path-ignore-patterns='tests/live-real-model.test.ts'` | worked — 239 pass, 1 skip, ~6 s |
|
||||
| `bun test` | ran — 242 pass, 1 skip, 1 fail in ~17 min. The fail is pre-existing: `tests/live-real-model.test.ts` › `en_alien` hits its 900 s timeout (reproduced in isolation). Allowed anyway: the command itself is the verified dev loop. |
|
||||
| `bun run build` | worked |
|
||||
| `bun run icons` | worked |
|
||||
| `bun dev` | worked — HTTP 200 on `/` |
|
||||
| `FW_INTEGRATION=1 bun test tests/transcribe.test.ts` | not run — faster-whisper not installed here |
|
||||
| `bun start` | not run — documented in README |
|
||||
| Typecheck / lint | not run — `typescript` not installed, no linter configured |
|
||||
| `docker build .` | not run — CI-only; in Ask tier |
|
||||
|
||||
TODO(maintainer): confirm every row above that is not worked.
|
||||
|
||||
## Tiers
|
||||
|
||||
| Tier | Commands | Why |
|
||||
|---|---|---|
|
||||
| Allow | `git status`, `git diff`, `git log`, `git show`, `git branch --list`, `git rev-parse`, `git add`, `git commit`; `bun install --frozen-lockfile`, `bun test`, `bun run build`, `bun run icons`, `bun dev` | Inspection and the verified dev loop; no prompt needed. |
|
||||
| Ask | `git push`, `git merge`, `git rebase`, `git switch`, `git restore`, `git commit --amend`, `rm`; `bun add`/`remove`/`update`/`upgrade`/`patch`, `bunx`, `bun x`; `docker`; `pip install`, `python3 -m pip install` | Leaves the machine, rewrites work, changes dependencies or the transformers patch, or touches deployment — a human decides. |
|
||||
| Deny | `git push --force`/`-f`/`--force-with-lease`, `git reset --hard`, `git clean`, `git add -f`/`--force`, `git commit --no-verify`/`-n`, `rm -r`/`-R`/`-rf`/`-fr`/`-Rf`/`-fR`/`--recursive`, `sudo`; reading `.env`, `.env.*`, `data/**` (local SQLite users + sessions) | Destroys work, skips checks, or exposes secrets/user data. |
|
||||
|
||||
## Known gaps
|
||||
- Allowlists save prompts; they are not a security boundary. Use the tool's sandbox for isolation.
|
||||
- Prefix rules are bypassable: `git -C dir push`, flags placed later (`git push origin main --force`),
|
||||
`+refspec` pushes. Claude Code's `:*` matches at a word boundary, so each flag cluster must be
|
||||
listed — which is why `git clean` is denied whole and `rm` always asks.
|
||||
- `bun install <pkg>` adds a dependency but cannot be put in Ask without also catching the allowed
|
||||
`bun install --frozen-lockfile` (Ask beats Allow in Claude Code). Treat it as Ask by convention.
|
||||
- `bun run build` deletes and rewrites `dist/` itself (`scripts/build.ts`), outside the `rm` rules.
|
||||
- Allowed test, build, and dev commands run code from the repo; an agent that can edit
|
||||
`package.json`, `scripts/`, or the tests can run anything through them. `bun test` also downloads
|
||||
model weights from huggingface.co (`tests/live-real-model.test.ts`).
|
||||
- Denying reads of `.env` does not stop a script that opens it itself (Bun auto-loads `.env`), and
|
||||
git can print staged or committed secrets. `git add -f` is denied so an agent cannot stage an
|
||||
ignored secret; keep real secrets out of the working tree.
|
||||
- Codex: loaded only once the project is trusted; `approval_policy = "on-request"` here can loosen a
|
||||
stricter user setting. No allow rules (Codex `allow` = run outside the sandbox). In
|
||||
`workspace-write`, `.git/` is read-only, so `git add`/`commit` ask for escalation; `bun install`
|
||||
needs network and asks too. `--force-with-lease=<ref>` and late flags only match the Ask rule.
|
||||
Rules checked with `codex execpolicy check`.
|
||||
- Gemini: ignored until the folder is trusted; `tools.exclude` is the only project-level deny; no
|
||||
Ask tier (anything not allowed already asks); cannot deny file reads.
|
||||
@@ -345,3 +345,35 @@ cached.
|
||||
Arbitrary Hub model ids, OAuth providers, multi-user transcript storage,
|
||||
whisper.cpp server backend, i18n of the UI.
|
||||
Promote to a milestone via a new entry in `Decisions.md` first.
|
||||
|
||||
---
|
||||
|
||||
## Noticed, not fixed
|
||||
Found while bootstrapping agent docs (D-036); docs-only pass, so no code changed.
|
||||
- [ ] `bun test tests/live-real-model.test.ts` fails: the `en_alien (en)` case
|
||||
times out at its 900 s limit (2026-10-29, Linux CPU/WASM, reproduced in
|
||||
isolation; `da_festuge` and `en_aiproblem` pass).
|
||||
- [ ] `isolate_wer2.ts` (repo root) is marked "TEMP (delete after use)" and
|
||||
writes to a stale `/tmp/claude-1000/...` scratchpad path.
|
||||
- [ ] `docs/Architecture.md` §3 and `scripts/build.ts` say `src/pwa/icons/`
|
||||
is "not checked in", but `icon-192.png`/`icon-512.png` are tracked.
|
||||
- [ ] No typecheck or lint: `tsconfig.json` is strict but `typescript` is not
|
||||
installed, so nothing checks types (`bun` strips them).
|
||||
- [ ] `.gitlab-ci.yml` only builds Docker images; `bun test` never runs in CI.
|
||||
- [ ] README "Graceful degradation" says server-engine weights download into
|
||||
the data volume; the env table, Dockerfile and D-031 say they are baked
|
||||
into the image at `/opt/faster-whisper/models`.
|
||||
- [ ] `src/index.css` declares `color-scheme: light dark` but only has a dark
|
||||
palette; `Inter` is listed first in the font stack but never loaded.
|
||||
|
||||
## Recurring
|
||||
- [ ] Maintain `AGENTS.md`:
|
||||
- **Add a rule only in response to a real failure.** When an agent repeatedly
|
||||
gets something wrong, add one line, ideally with a code example. Do not
|
||||
pre-empt hypothetical mistakes.
|
||||
- **Update in the same commit as the convention change.** A stale rules file
|
||||
actively misleads.
|
||||
- **Prune quarterly.** Delete rules whose convention changed or whose mistake
|
||||
no longer occurs. Keep it under 150 lines.
|
||||
- **Optional validation:** run the same task twice in clean sessions, with and
|
||||
without the file, and compare steps and token use.
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
# Plans
|
||||
|
||||
Implementation plans from the superpowers writing-plans workflow. **Transient:** a plan lives only while
|
||||
its work is in progress and is removed with `git rm` before merging. This README stays.
|
||||
|
||||
- Naming: `NNN-short-slug.md`, matching its spec number.
|
||||
- Links back to its spec. Bite-sized checkbox tasks with exact file paths, test commands, and
|
||||
commit checkpoints.
|
||||
- Written for an engineer with zero context for this codebase — spell out the obvious.
|
||||
- Tick checkboxes as you go so another agent can resume an interrupted session.
|
||||
- Front matter: `status: draft | approved | in-progress`. There is no `done` — a finished plan is deleted.
|
||||
@@ -0,0 +1,11 @@
|
||||
# Specs
|
||||
|
||||
Approved specifications from the superpowers brainstorming workflow. Nothing non-trivial gets
|
||||
implemented without one.
|
||||
|
||||
- Naming: `NNN-short-slug.md`, zero-padded, sequential.
|
||||
- A spec states **what** and **why**, plus acceptance criteria. Not **how**.
|
||||
- Front matter: `status: draft | approved | implemented | superseded`. No link to a plan — plans are
|
||||
removed before merge.
|
||||
- Never delete an implemented spec; it is the record of intent.
|
||||
- A spec that makes an architectural choice also gets a `D-###` entry in `docs/Decisions.md`.
|
||||
Reference in New Issue
Block a user