# Contributor handoff — openai/whisper

> Canonical page: https://codevetter.com/unpack/whisper/agent-handoff

# Contributor handoff — openai/whisper

Scanned commit 86098128c0b4f24f0e2aa2994de830614b474227 (2026-08-31). Generated by codevetter codevetter 1.12.0 on 2026-09-22.

Small, flat, readable codebase (~3.7k LOC of Python). Before editing, read whisper/transcribe.py (orchestrator+CLI), whisper/decoding.py (engine), and whisper/model.py (nn defs). Style is enforced by pre-commit (black/isort/flake8) in CI. Tests need ffmpeg and network for model downloads; the fastest local check is `pytest -k 'not test_transcribe'`. Known traps: a return-instead-of-raise bug in _get_audio_features, an undeclared `regex` dependency, Whisper binding free functions as class attributes, and the deliberately convoluted seek loop in transcribe().
- Read-first files: transcribe.py (623 lines, orchestration + cli()), decoding.py (826 lines, DecodingTask/DecodingOptions/decoders/filters), model.py (352 lines, Whisper/AudioEncoder/TextDecoder), __init__.py (load_model + _MODELS), tokenizer.py (vocab + specials). — [whisper/transcribe.py](https://github.com/openai/whisper/blob/86098128c0b4f24f0e2aa2994de830614b474227/whisper/transcribe.py) [whisper/decoding.py](https://github.com/openai/whisper/blob/86098128c0b4f24f0e2aa2994de830614b474227/whisper/decoding.py) [whisper/model.py](https://github.com/openai/whisper/blob/86098128c0b4f24f0e2aa2994de830614b474227/whisper/model.py) [whisper/__init__.py](https://github.com/openai/whisper/blob/86098128c0b4f24f0e2aa2994de830614b474227/whisper/__init__.py) [whisper/tokenizer.py](https://github.com/openai/whisper/blob/86098128c0b4f24f0e2aa2994de830614b474227/whisper/tokenizer.py)
- Formatting is enforced: black + isort (profile=black, line_length=88, trailing comma, multi_line_output=3) + flake8 (--max-line-length 88, ignore E203,E501,W503,W504) via .pre-commit-config.yaml, run as the gating CI job. — [.pre-commit-config.yaml](https://github.com/openai/whisper/blob/86098128c0b4f24f0e2aa2994de830614b474227/.pre-commit-config.yaml) [pyproject.toml#L48-54](https://github.com/openai/whisper/blob/86098128c0b4f24f0e2aa2994de830614b474227/pyproject.toml#L48-L54) [.github/workflows/test.yml#L11-38](https://github.com/openai/whisper/blob/86098128c0b4f24f0e2aa2994de830614b474227/.github/workflows/test.yml#L11-L38)
- Test invocation: `pip install .[dev]` (pulls pytest+scipy) then `pytest`; ffmpeg must be on PATH; test_transcribe downloads checkpoints (~GBs) so `pytest -k 'not test_transcribe'` mirrors most of CI's fast path; CUDA tests are behind the requires_cuda marker. — [.github/workflows/test.yml#L73-79](https://github.com/openai/whisper/blob/86098128c0b4f24f0e2aa2994de830614b474227/.github/workflows/test.yml#L73-L79) [tests/test_transcribe.py](https://github.com/openai/whisper/blob/86098128c0b4f24f0e2aa2994de830614b474227/tests/test_transcribe.py) [tests/conftest.py](https://github.com/openai/whisper/blob/86098128c0b4f24f0e2aa2994de830614b474227/tests/conftest.py)
- Releases: bump whisper/version.py __version__ (date-based, e.g. "20250625") and push a commit titled 'Release <version>' to main — python-publish.yml regex-matches the message, tags v<version>, builds sdist, and uploads to PyPI with TWINE secrets. — [whisper/version.py](https://github.com/openai/whisper/blob/86098128c0b4f24f0e2aa2994de830614b474227/whisper/version.py) [.github/workflows/python-publish.yml#L12-37](https://github.com/openai/whisper/blob/86098128c0b4f24f0e2aa2994de830614b474227/.github/workflows/python-publish.yml#L12-L37)
- Trap: Whisper binds transcribe/decode/detect_language as class attributes pointing at module-level functions — renaming those functions or changing their signatures breaks the model.method() API used in README examples. — [whisper/model.py#L350-352](https://github.com/openai/whisper/blob/86098128c0b4f24f0e2aa2994de830614b474227/whisper/model.py#L350-L352) [README.md](https://github.com/openai/whisper/blob/86098128c0b4f24f0e2aa2994de830614b474227/README.md)
- Trap: DecodingTask._verify_options rejects incompatible combos (beam_size+best_of, best_of at T=0, patience without beam_size, length_penalty outside [0,1]) — new CLI flags must respect these invariants. — [whisper/decoding.py#L572-585](https://github.com/openai/whisper/blob/86098128c0b4f24f0e2aa2994de830614b474227/whisper/decoding.py#L572-L585)
- Trap: tokenizer.decode() silently drops all tokens >= timestamp_begin; use decode_with_timestamps() (renders <|1.08|>) when debugging timestamp/token issues. — [whisper/tokenizer.py#L164-173](https://github.com/openai/whisper/blob/86098128c0b4f24f0e2aa2994de830614b474227/whisper/tokenizer.py#L164-L173)
- Trap: fp16 is default-on but unsupported on CPU — transcribe() downgrades to fp32 with a warning, and DecodingOptions.fp16 controls the mel cast in _get_audio_features; keep both consistent when changing dtype logic. — [whisper/transcribe.py#L127-136](https://github.com/openai/whisper/blob/86098128c0b4f24f0e2aa2994de830614b474227/whisper/transcribe.py#L127-L136) [whisper/decoding.py#L644-656](https://github.com/openai/whisper/blob/86098128c0b4f24f0e2aa2994de830614b474227/whisper/decoding.py#L644-L656)
- Trap: binary assets (assets/*.tiktoken, assets/mel_filters.npz, normalizers/english.json) load via os.path.dirname(__file__) — restructuring the package requires updating MANIFEST.in and these loaders together. — [whisper/tokenizer.py#L332](https://github.com/openai/whisper/blob/86098128c0b4f24f0e2aa2994de830614b474227/whisper/tokenizer.py#L332) [whisper/audio.py#L105](https://github.com/openai/whisper/blob/86098128c0b4f24f0e2aa2994de830614b474227/whisper/audio.py#L105) [whisper/normalizers/english.py#L460-461](https://github.com/openai/whisper/blob/86098128c0b4f24f0e2aa2994de830614b474227/whisper/normalizers/english.py#L460-L461) [MANIFEST.in](https://github.com/openai/whisper/blob/86098128c0b4f24f0e2aa2994de830614b474227/MANIFEST.in)
- Safe-edit boundaries: utils.py writers and normalizers/ are low-risk leaf modules; audio.py constants (SAMPLE_RATE=16000, N_FRAMES=3000, hop=160) are load-bearing for timestamp math everywhere; the transcribe() seek loop and timing.py heuristics are the blast-radius hotspots. (inferred) — [whisper/utils.py](https://github.com/openai/whisper/blob/86098128c0b4f24f0e2aa2994de830614b474227/whisper/utils.py) [whisper/audio.py#L12-22](https://github.com/openai/whisper/blob/86098128c0b4f24f0e2aa2994de830614b474227/whisper/audio.py#L12-L22) [whisper/transcribe.py#L263-471](https://github.com/openai/whisper/blob/86098128c0b4f24f0e2aa2994de830614b474227/whisper/transcribe.py#L263-L471) [whisper/timing.py](https://github.com/openai/whisper/blob/86098128c0b4f24f0e2aa2994de830614b474227/whisper/timing.py)

Every claim links to its cited files at the pinned commit. Citations identify source evidence; they do not establish runtime correctness. Independent CodeVetter scan — not reviewed by upstream maintainers.

## Public product links

- [CodeVetter](https://codevetter.com/)
- [Download](https://codevetter.com/download)
- [Documentation](https://codevetter.com/docs/)
- [Source](https://github.com/Codevetter/codevetter)
