Project Structure
August 6, 2026 · View on GitHub
Every folder has a single job. Every file at the root earns its place.
Layout
VoiceStudio/
│
├── README.md ⟵ user-facing overview
├── CHANGELOG.md ⟵ release history
├── LICENSE
│
├── pyproject.toml ⟵ Python project manifest
├── uv.lock ⟵ Python lockfile
├── package.json ⟵ monorepo manifest (Bun workspaces + Turborepo)
├── bun.lock ⟵ JS lockfile
├── turbo.json ⟵ turborepo pipeline
│
├── .dockerignore ⟵ Docker build context filter
├── backend.spec ⟵ pyinstaller spec (stays at root by pyinstaller convention)
├── alembic.ini ⟵ DB migration config (stays at root by alembic convention)
│
├── .env ⟵ user config; gitignored, .env.example is the template
├── .gitignore
│
├── backend/ ⟵ FastAPI server
│ ├── main.py
│ ├── api/routers/ HTTP endpoints (thin)
│ ├── core/ config, db, task queue, metrics
│ ├── services/ business logic
│ └── schemas/ pydantic request/response shapes
│
├── frontend/ ⟵ React 19 + Vite + Tauri desktop
│ ├── src/
│ │ ├── pages/ one file per top-level view
│ │ ├── components/ reusable UI
│ │ ├── api/ typed API clients
│ │ ├── store/ Zustand slices
│ │ ├── hooks/ custom React hooks
│ │ └── utils/
│ ├── src-tauri/ Rust desktop shell
│ └── public/
│
├── omnivoice/ ⟵ the underlying TTS model package
│ ├── models/
│ ├── cli/ CLI entry points (omnivoice-infer, etc.)
│ ├── data/ data utilities used by the model
│ ├── eval/ evaluation scripts
│ ├── scripts/ one-off utilities that ship with the package
│ ├── training/
│ └── utils/
│
├── tests/ ⟵ all tests live here, no exceptions
│ ├── conftest.py
│ ├── test_api.py
│ ├── test_dub_*.py
│ ├── test_job_queue.py
│ ├── test_segmentation.py
│ └── frontend/ Node-based frontend tests
│
├── scripts/ ⟵ dev / build / release shell + python scripts
│ ├── install.sh universal installer
│ ├── run.sh universal launcher
│ ├── smoke-test.sh end-to-end validation
│ └── desktop-prod.sh production desktop build
│
├── deploy/ ⟵ Docker deployment configs
│ ├── Dockerfile single-stage CUDA image
│ └── docker-compose.yml one-click local deployment
│
├── docs/ ⟵ developer docs, screenshots, branding
│ ├── ROADMAP.md where this project is going
│ ├── STRUCTURE.md you are here
│ ├── mcp.json MCP config template
│ ├── preview.png README hero image
│ ├── logo.png, logo.svg branding assets
│ ├── screenshot-*.png feature screenshots
│ ├── languages.md
│ ├── training.md
│ ├── data_preparation.md
│ ├── evaluation.md
│ └── voice-design.md
│
├── examples/ ⟵ runnable demos + sample inputs
│
├── omnivoice_data/ ⟵ Docker bind-mount target (gitignored)
│ DB + HF cache live here when running via compose
│
└── .git/
Rules of the root
-
Nothing at the root is a runtime artifact. Outputs, temp files, local DBs, crash logs — all go to
~/Library/Application Support/OmniVoice/(or the OS equivalent), never into the repo. The one exception isomnivoice_data/, which exists as a bind-mount anchor for Docker. -
No ad-hoc scripts at the root. One-off debug scripts live in
scripts/. Tests live intests/. Benchmarks live inscripts/benchmarks/(when we create them). -
Each subdirectory owns one concern. If you can't describe what goes in a directory in one sentence, it's wrong.
-
Every package has a manifest.
backend/,frontend/,omnivoice/each have their own deps declared viapyproject.toml/package.json— they are independently testable.
What lives where
| Kind of thing | Goes in |
|---|---|
| User-facing product code | backend/, frontend/ |
| The TTS model (independent of the studio) | omnivoice/ |
| Everything executable but not user-facing | scripts/ |
| Tests | tests/ |
| Developer + user docs (Markdown) | docs/ |
| Architecture decision records (ADRs) | docs/adr/ |
| Runnable demos and sample data | examples/ |
| Runtime data (never committed) | ~/Library/Application Support/OmniVoice/ on Mac |
What doesn't live at the root anymore
Removed in the cleanup pass:
| File | Why it was there | Where it went |
|---|---|---|
test_crash.py, test_server.py, test_whisper.py, test_mock.py, test_pyannote.py | One-off debug scripts from an April 14 crash investigation. Imported symbols that no longer exist after the router refactor. | Deleted (already gitignored, referenced dead code). |
benchmark.py | Another stale debug script; imported backend.main._get_db which no longer exists. | Deleted. |
output.wav, test.wav | Runtime artifacts. | Deleted / moved out. |
crash_log.txt | Runtime log. Now written to $DATA_DIR/crash_log.txt. | Deleted. |
omnivoice.zip (148 MB) | Offline reference archive of the project itself. | Moved out of the repo to ../omnivoice.zip.bak. |
data/ | Only contained .DS_Store. | Deleted. |
legacy_gradio/ | The pre-React Gradio UI. Kept for historical reference. | Archived to research/legacy_gradio/, then removed in the 2026-07-12 cleanup (git history). |
Scattered .DS_Store files | macOS Finder droppings. | Deleted from every non-ignored directory. |
Removed in the 2026-07-12 cleanup pass (all preserved in git history):
| Dir | Why it was there | Where it went |
|---|---|---|
.planning/ (74 files) | GSD-era planning archive: phases, quick plans, issue clusters. The GSD workflow was retired 2026-07-08. | Deleted; the four load-bearing decision docs moved to docs/adr/. |
specs/ | spec-kit specs for features 001–007 — all shipped. | Deleted. |
design/ | ASCII mockups of the pre-React target UX, superseded by the shipped app. | Deleted. |
research/ | Archived legacy Gradio UI + April-2026 competitor notes. | Deleted. |
.agents/ | Rules for a third-party agent tool no longer in use. | Deleted. |
Scaling path (proposed, not yet executed)
The current flat layout works fine for the current size. If the project grows to include additional apps (a mobile companion, a plugin SDK, multiple backends), migrate to a Turborepo-style monorepo:
VoiceStudio/
├── apps/
│ ├── api/ ← was backend/
│ ├── web/ ← was frontend/
│ └── desktop/ ← could extract src-tauri/ here later
├── packages/
│ ├── omnivoice-model/ ← was omnivoice/
│ └── tts-adapters/ ← new; the pluggable TTS interface from ROADMAP phase 3
├── config/
│ ├── docker/
│ └── pyinstaller/
├── tests/
└── docs/
Do not execute this migration without a dedicated PR. It breaks:
pyproject.toml[tool.hatch.build.targets.{sdist,wheel}]pathspackage.jsonworkspaces and scriptsturbo.json,Dockerfile,docker-compose.ymlpathsbackend.spec(['backend/main.py'],pathex=['.'])frontend/src-tauri/tauri.*.conf.jsonsidecar paths- every import that reads
from backend.main import …(tests, scripts)
Migrate when adding the second apps/* or the second packages/*. Not before.
Conventions
- Filenames: snake_case for Python, kebab-case or PascalCase for JS/TS components, lowercase for Markdown.
- Tests mirror source paths.
backend/services/dub_pipeline.py→tests/services/test_dub_pipeline.py. - One-off scripts go into
scripts/with a descriptive name, nottest_*.pyat the root. - New top-level directories require a PR that updates this file.