Contributing

September 20, 2026 · View on GitHub

Comments and Docstrings

Code is the primary source of truth. Add prose only when it records information the code cannot express clearly.

  • Keep non-obvious rationale, invariants, concurrency or lifetime rules, security boundaries, platform constraints, compatibility contracts, and measured choices.
  • Do not narrate the next statement, restate a symbol's name or signature, add decorative section banners, leave commented-out code, or preserve implementation history that belongs in version control.
  • Public APIs need docstrings only when they have a meaningful contract beyond their typed signature. Keep them concise and use Google style when parameter, return, or exception details add information.
  • Tests should explain only non-obvious setup or why a behavior matters. Let test names and assertions describe ordinary cases.
  • Preserve required directives, licenses, shebangs, active TODOs, and user-facing or model-facing strings.
  • Update or remove nearby comments whenever behavior changes. Review comments for future staleness with the same care as code.

Before submitting a change, remove temporary notes and commented-out experiments, then verify that every remaining comment explains why rather than what.

Validation

Use uv run as described below, or activate the repository virtual environment before Python commands. Run tests through pytest so the shared fixtures isolate settings, manage Qt, and handle native-runtime teardown:

. .\venv\Scripts\Activate.ps1
python -m pytest tests/
python -m pytest tests/test_recorder.py tests/test_settings.py

CI checks Python correctness and unused code with Ruff 0.16.4:

python -m pip install ruff==0.16.4
python -m ruff check --select F .

Keep imports that perform required runtime initialization or verify optional libraries, with a specific noqa: F401 explanation. Avoid broad automatic fixes for typing or import order as part of behavior changes.

Dashboard changes also require npm test in webui before the locked build below. It covers transcript loading, action acknowledgements, and speech previews.

Development with uv

Install uv once. On Windows:

winget install --id astral-sh.uv -e

Open a new terminal after installation. From the repository root:

uv sync --locked
uv run python main.py
uv run python -m pytest tests/
uv run ruff check --select F .

No activation is needed. uv uses Python 3.12 (matching the release builders), creates .venv, and installs the locked app dependencies, pytest, Ruff, and build tools. It can download Python 3.12 if it is missing. An existing venv is retained; the ow/openwhisper launchers and installer scripts still use that environment. Use uv run python main.py when you want the uv development environment. On macOS, the existing source launcher also handles app-bundle identification for Accessibility permissions; use the documented pip/launcher workflow when testing that integration. Linux still needs the audio and Qt system libraries described in the README.

On Windows, install GPU Acceleration through Downloads → Components in the app. Source launches activate installed components automatically, so the normal uv run python main.py command supports GPU use without an extra flag. Other speech runtimes and downloaded models also use the app's component manager.

The gpu extra is an alternative way to put CUDA wheels in .venv, intended for Linux source installs (the downloadable GPU component is Windows-only) or for explicit testing of wheel-based GPU setups:

uv sync --locked --extra gpu
uv run --extra gpu python main.py

Keep --extra gpu on sync/run commands only when choosing that wheel-based setup. Point your editor at .venv/Scripts/python.exe on Windows or .venv/bin/python on macOS/Linux. JavaScript work in webui and the sidecars retains its existing Node/Bun workflow.

Updating dependencies

The existing requirements files remain the source of truth for app, GPU, build, and release dependencies. scripts/sync_uv_dependencies.py mirrors them into the marked block in pyproject.toml, including release constraints, so development does not silently upgrade packages used by the installers. Edit the appropriate requirements file first, then run:

python scripts/sync_uv_dependencies.py
uv lock
uv sync --locked
python scripts/sync_uv_dependencies.py --check
uv lock --check

Commit the requirements changes, generated pyproject.toml block, and uv.lock together. CI checks that the generated declarations match the requirements files and that the lockfile is current. Use python3 for the standalone maintenance script if that is your platform's Python command. It uses only the standard library and does not require activation. Release pins should only change as part of a deliberate dependency update. Use uv add only for experiments: permanent dependency changes must also go through the requirements files and generator. uv itself is optional for source users and is not required by the release builders.

Local speech backends and models

Local agents should read AGENTS.md when present (kept untracked by repository preference). Use the model reference for exact supported IDs, and the local speech guide for user-visible behavior. The optional runtimes target Windows x64, with NVIDIA Speech CPU also packaged for Apple Silicon macOS in the unreleased source integration. Do not describe other upstream platform support as support provided by this integration.

A backend or model change must keep these surfaces consistent:

ConcernSource of truth / files to update
Family identity, defaults, meeting/streaming capabilityservices/local_asr/catalog.py; backend labels/maps in config.py
Weights and revisionsservices/local_asr/models.json; include URL, exact bytes, SHA-256, and the intended inference format
Isolated runtime and dependency versionsservices/local_asr/*runtime.json; component version, archive pins, and measured extracted size
UI technical profiles and optional component descriptionsservices/model_catalog.py, services/component_catalog.py
Worker inference and lifecycletranscriber/optional_backend.py, services/local_asr/, controller engine lease and readiness
User and developer docsREADME, docs/models.md, docs/local-asr.md, docs/whisper-gpu.md, docs/packaging.md, AGENTS, CHANGELOG, THIRD_PARTY_NOTICES.md
GitHub-facing materialREADME, .github/ISSUE_TEMPLATE/bug_report.yml, repository About description, and the next release's notes

Do not install optional SDKs into the base app requirements. Test the exact pinned runtime through the normal component installer, with its embedded Python and local model paths. Preserve archive license metadata. If a dependency or runtime changes, update its version and measured install size so updates and disk checks remain accurate. Keep model/runtime downloads separate and respect the existing model-download policy and hard offline override.

After activating the repository venv, run the appropriate Python regressions for the changed adapter, model/component catalog, download flow, controller lifecycle, and selectors. Changes to meeting previews also require the dashboard build and node webui/tests/speech-preview.cjs. Before advertising support, exercise actual installed models on each advertised device, cancellation/reload, silence, long files, and streaming final flush where applicable. Use scripts/benchmark_local_asr.py and scripts/benchmark_local_asr_corpus.py for comparable raw-ASR results; record hardware, actual device, model/runtime revisions, normalization, and sample limitations. Do not present a short sample's decode time as full hotkey latency or a leaderboard accuracy claim.

For doc-only changes, check relative links, exact catalog IDs, platform claims, and release status; no model download or full regression run is needed. Historical release notes describe the binaries actually released. Announce the new families under Unreleased until compatible artifacts ship, then update the version-specific guidance and GitHub About description together with the release.

Dashboard bundle

The meeting dashboard's webui/dist is committed because the Python app serves it directly. After changing dashboard source or its lockfile, rebuild with the locked dependencies before committing:

cd webui
npm ci
npm run build

Include all changes under webui/dist, including new hashed assets and deleted old assets. npm run build alone can use stale packages already in node_modules, producing a different bundle from CI. CI installs the lockfile with npm ci and requires the rebuilt directory to match the committed files.

Source launchers

From a source checkout you can register ow and openwhisper so the app launches from any terminal — no need to cd into the repo or activate the venv first. Native packages already provide those commands.

Windows

From the repo root, run:

install.cmd

This adds scripts\ to your user PATH (via the registry, not setx — see note below). It is idempotent. Open a new terminal afterward for the change to take effect.

ow              # short alias
openwhisper     # full name

The launcher invokes venv\Scripts\pythonw.exe directly, so the app always uses the project's venv regardless of which environment your shell has activated. Code changes are picked up live — no reinstall needed after git pull.

uninstall.cmd

Removes the PATH entry only. Your venv, code, and the scripts/ folder are left untouched, so re-running install.cmd later will restore the commands.

If you cannot run the installer (for example, corporate execution-policy restrictions), add the path yourself in PowerShell:

$dir = "D:\path\to\OpenWhisper\scripts"   # <-- adjust to your clone location
$current = [Environment]::GetEnvironmentVariable("Path", "User")
if ($current -split ";" -notcontains $dir) {
    [Environment]::SetEnvironmentVariable("Path", "$current;$dir", "User")
}

setx PATH ... from a .cmd file silently truncates PATH at 1024 characters and can duplicate System PATH entries into User PATH. install.cmd shells out to PowerShell, which writes directly to HKCU\Environment\Path via [Environment]::SetEnvironmentVariable — no truncation, no leakage between User and System scopes.

If you would rather not modify PATH, drop a copy of scripts/openwhisper.cmd into %LOCALAPPDATA%\Microsoft\WindowsApps\ (already on Windows PATH for every user). That is a copy, so refresh it if the launcher logic changes.

macOS / Linux

From the repo root:

./install.sh

This adds the scripts/ folder to your PATH in your shell profile files (~/.bashrc, ~/.zprofile, and on macOS also ~/.bash_profile; fish users get ~/.config/fish/config.fish). It is idempotent. The installer prints the exact source … command for your shell — run that in your current terminal, or open a new one, then run ow or openwhisper.

The launcher invokes venv/bin/python directly. Code changes are picked up live. To remove the PATH entry, run ./uninstall.sh.