Contributing to vakforge
Ground rules
- We ship recipes we have actually run. A PR adding a model that "should work" is not mergeable; a PR adding a model with a committed
report.jsonon the demo dataset is. - Core stays dependency-light. If your change makes
import vakforge.schemapull in torch, it will be rejected. - Language- or market-specific logic goes in a locale pack (
docs/LOCALE_PACKS.md). A PR that addsif lang == "..."to core will be asked to move it. - Upstream APIs are verified against source, and the version is pinned. Say where you verified it in the PR description.
- Data safety code paths (redaction, consent) cannot be bypassed silently. Any new flag that weakens them must log a warning.
Setup
git clone https://github.com/vakforge-ai/vakforge && cd vakforge
uv sync --group dev
uvx pre-commit install # runs ruff and file checks on every commit
uv run pytest # must pass on CPU with no downloads
uv run ruff check . && uv run ruff format --check .
Recipe work. The extras are declared but still empty: each one gets its pins when that recipe is actually built, so these commands work today and install nothing extra:
uv sync --group dev --extra lfm25 # or --extra moshi / --extra qwen / --extra cascade
uv run pytest -m model # opt-in, downloads models
uv run pytest -m gpu # opt-in, needs CUDA
Docs site (site/docs/ is generated and committed, so rebuild before you push):
uv run --group docs python scripts/build_docs.py
What CI checks
lint, test-linux (3.11–3.14, every version requires-python admits), test-windows,
docs (the generated site must match docs/), package (build, twine check, install the
wheel into a clean environment, run the CLI from it), coverage (branch coverage, 90% floor)
and dependency-audit (pip-audit over the locked runtime set). A final required job
passes only if every one of those did, and it is the single check main requires, so the
ruleset does not change when the Python versions do.
Every action is pinned to a commit SHA: a moving tag like v7 can be repointed at any commit
by whoever owns the action. Dependabot updates the SHAs; keep the trailing # v7 comment so the
release is still readable.
The rest of the toolchain is pinned the same way. UV_VERSION at the top of ci.yml and
release.yml sets the uv every job uses; keep the two in step. twine, pip-audit and
pytest-cov come from the locked ci dependency group, never uvx, so a job runs the same
version of each tool until someone changes uv.lock. The build backend is pinned exactly in
[build-system].
Branches and commits
- Every change goes on a branch from
main(feat/<area>-<short>,fix/…,ci/…,docs/…) and lands through a pull request, merged only when CI is green. That includes typos and one-line fixes: CI is what catches a stalesite/docs/or a lockfile out of step, and it has to pass before a change lands, not after. - Small, unrelated fixes can share one branch, one commit each, rather than a pull request apiece.
- Nothing is pushed to
maindirectly, andmainis never force-pushed. - Commits are small and logical: one concern per commit, several commits per branch. A branch that adds a CLI command, its tests and its docs is three commits, not one.
- Conventional commit messages:
feat(prepare): roman-hindi language tagging. - Large recipes land as a sequence of pull requests: adapter → train wrapper → eval → serve.
- Every pull request description follows
.github/pull_request_template.md: What, Why, How to check and Checklist, in that order, each one filled in. Thepr-descriptioncheck fails a description with a missing, reordered or empty section; editing the description re-runs it. Descriptions written by bots, such as Dependabot's, are not checked.
Releases
- Bump
versioninpyproject.tomland__version__invakforge/__init__.py, runuv lock, commit aschore(release): X.Y.Z. - Push a matching tag:
git tag -a vX.Y.Z -m "vX.Y.Z" && git push origin vX.Y.Z. - The
releaseworkflow checks the tag against the version, runs tests, builds, publishes to PyPI through trusted publishing (no token is involved), and then creates the GitHub Release with generated notes and the same wheel and sdist attached.
PyPI never lets a published version change, so check the README renders (images must use public URLs) before tagging.
Security
Report vulnerabilities privately as described in SECURITY.md, never in a public issue.
Pull request checklist
- Tests added/updated; CPU suite green
-
ruffclean - Docs updated (
docs/*.md,README.mdrecipe table if applicable) -
docs/ROADMAP.mdcheckbox ticked - For upstream integrations: version pinned, verification noted,
UPSTREAM_NOTES.mdupdated - For recipes:
LICENSE_NOTES.mdpresent; per-locale support declared in launch packs - For locale packs: golden tests for normalizer,
detect_lang, and every PII pattern;privacy_notessourced - No audio binaries, checkpoints, or real customer data in the diff
Reporting model or data issues
Open an issue with: recipe, config hash, manifest hash, versions.txt, and the relevant slice of report.json. Do not attach real customer audio to issues.
Code style
Python 3.11+, type hints, docstrings on public functions, pydantic for anything that crosses a boundary, rich for user-facing output, no print debugging left behind.
Licence
By contributing you agree your code is released under Apache-2.0. Model weights and datasets keep their own licences.