Status snapshot: 2026-05-31. Branch
transcript-editor. Derived from a full verified audit of the repo (build health, transcript feature, transcription pipeline, rebrand, desktop, infra). Each item below citesfile:lineevidence.
OpenScript is a fork of OpenCut (browser video editor, Next.js + Rust/WASM) with a new transcript-driven editing layer — "edit video by editing the transcript." apps/web is the product; apps/website is a separate waitlist/marketing site; apps/desktop is a GPUI scaffold.
Working today: the OpenCut editor base is healthy (project create → editor mounts, WASM/WebGPU/preview/timeline render, no console errors), the transcript happy-path is wired end-to-end, and the transcript/transcription code is type-clean. The editor runs fully local with bun install && bun run dev:web (no Postgres/Redis needed).
Working method: TDD. For every fix, a failing test (or a failing typecheck) comes first, then the implementation that makes it green. bun test is the runner; next build is the type gate.
next build fails on the TypeScript gate (12 errors); the app only runs via next dev. 4 of the 5 root causes are also live runtime bugs.
- Keybindings type guards missing. persistence.ts:2,4 imports
isShortcutKey/isActionWithOptionalArgsthat don't exist. ImplementisShortcutKeyin keybinding.ts andisActionWithOptionalArgsin definitions.ts. Comprehensive tests already exist in persistence.test.ts — they are the red tests. - Sticker registry registered under
undefined. stickers/providers/index.ts:22 callsregister(provider.id, provider)positionally against an object-param API → built-in stickers (logos/flags/shapes) broken at runtime. Fix to object form + add a unit test. - Storage migration adapter calls positional (4 sites). runner.ts:41,98 and v1-to-v2.ts:124,133,160 construct
IndexedDBAdapter/ call.setpositionally → legacy-project migration opensindexedDB.open(undefined)and corrupts/fails. Fix to object form (tsc is the guard; IndexedDB isn't available underbun test). - Stale MediaTime tests. update-pipeline.test.ts:69 and resolve.test.ts:651 pass raw
numberwhere the brandedMediaTimeis required. Wrap withmediaTime({ ticks })/ZERO_MEDIA_TIME. - Verify: full typecheck clean,
bun testgreen,next buildexits 0.
Also noted (non-blocking): bun.lock is stale vs package.json (--frozen-lockfile fails); typescript/next version drift between root and apps/web; ESLint is red (131 problems — mostly style, but 15 react-hooks/set-state-in-effect are real smells).
Three confirmed correctness bugs in the new feature.
- Undo desync (transcript-panel.tsx:212): after delete + Cmd+Z, timeline media returns but words stay struck-through and un-selectable. Panel never subscribes to command history. Reconcile
deletedflags from the live timeline on undo/redo. -
push()vsexecute()(apply-deletion.ts:60): bypasses ripple + reactors → orphan empty tracks; gaps when ripple is on; coordinate drift after other ripple edits. Route througheditor.command.execute(). - No persistence: transcript lives in React state only (transcript-panel.tsx:39); lost on reload/tab-toggle while timeline cuts persist → divergence. Persist
TranscriptDocument(incl. deleted flags + model/lang) into project storage; rehydrate on mount.
- Inline text correction — words are delete-only today; you cannot fix a misrecognized word. The core Descript capability.
- Ripple-close on delete (apply-deletion.ts:34) — deletions currently leave silent gaps instead of removing time.
- Model + language pickers — locked to
whisper-small/auto (transcript-panel.tsx:75); 4 models + 9 languages defined but unused. Add a download-size hint.
- COOP/COEP headers scoped to the editor route (or default to
whisper-tiny) — without cross-origin isolation, wasm runs single-threaded on Safari/Firefox/iOS andwhisper-smallis painfully slow (next.config.ts has noheaders()). Scope carefully: global isolation breaks cross-origin images. - Gate Generate on
timelineHasAudio(audio.ts:124) instead of running Whisper on silence and erroring. - Graceful fallback when a model returns no word timings (preserve
result.text); add a Cancel button (service already supportscancel()).
- drizzle schema path wrong: drizzle.config.ts:16 →
./src/lib/db/schema.tsdoesn't exist; real path./src/db/schema.ts.db:generate/migrate/pushhard-error. - Migration out of sync: 0000_brainy_saracen.sql creates dead
waitlist, omits thefeedbacktable the code uses (feedback/queries.ts) → feedback popover 500s on a fresh migrated DB. Regenerate after fixing the path. - README: rewrite getting-started (editor is local-only; Postgres/Redis only for optional auth/sounds/feedback; add
.env.localcopy + migrate steps). Add a migrate step todocker-compose.yml.
-
/roadmap404 (mobile-gate.tsx:63) + dead routes in sitemap.ts. -
manifest.jsonstill "OpenCut"; LICENSE copyright;.githubdocs + dead Discord invite;"#"placeholder social links + placeholder email on /privacy & /terms; orphan OpenCut logo assets. - Decide on
apps/desktop: a static upstream "hello window" (renders "OpenCut", no editor, no link torust/crates). Either wire it (webview or native) or mark it a placeholder. Remove emptypackages/{env,ui,desktop-bridge}and the dead*:toolsscripts referencing the non-existent@openscript/tools.
- ORT-wasm worker bundling — ORT loads from jsDelivr CDN by default; the
new Worker(new URL(...))idiom is Turbopack-supported. dtype:"q4"encoder — all 4 models shipencoder_model_q4.onnx(verified via HF API); only a minor accuracy tradeoff.- Next 16.1.3 vs
@opennextjs/cloudflarepeer range — OpenNext's actual version gates pass 16.1.3; it's just an install warning.