This is a personal, single-maintainer project, but the process below is the same one a new contributor would follow, and it's what CI enforces.
- Read
docs/ROADMAP.mdfirst — it's the build spec and the source of the project's hard guardrails (never persist or transmit audio; the report must state its methodology and limitations honestly; local-only operation). - This repo inherits
/STANDARDS; see the README's Standards Conformance table for what applies and what's still a tracked gap (docs/GAP-LEDGER.md). - Never weaken
tests/test_no_audio.py,tests/test_no_egress.py, ortests/test_report_content.py— these are the project's merge-blocking safety guarantees. A PR that touches them needs an explicit callout in its description of why, not just a diff.
make dev creates .venv from the committed uv.lock and installs the dev dependency
group (pytest, ruff, mypy, bandit, pip-audit, hypothesis). Install
uv first; the lock is the
single dependency snapshot used by local, CI, and release verification. One tool is
not on PyPI and needs a separate install:
- gitleaks (secret scanning):
brew install gitleaks(macOS) or see https://github.com/gitleaks/gitleaks#installing. Required formake security/make verifyto pass locally — CI runs it either way viagitleaks-action, but the local gate hard-fails without it (no more silent skipping, seeMakefile). - Node.js 20+: needed for
make pwa-testand the pa11y/axe pass inmake a11y. - Docker: only needed for
docker build ./ the container smoke test; not required for the Python test suite. - veraPDF (optional, PDF/A-3a conformance validation): a Java tool, not a
PyPI package — https://verapdf.org/software/. Only used by the best-effort,
non-merge-blocking
make pdf-a11ytarget;tests/test_pdf_export.py's structural pytest gate is the enforced floor for the tagged-PDF export (EXP-06). Seedocs/adr/0004-weasyprint-for-tagged-pdf-a-export.md.
pip install -e '.[pdf]' adds weasyprint (and pypdf, used only by
tests/test_pdf_export.py to read the generated structure tree back out). This
extra needs a Python >=3.10 host interpreter — a further, deliberate, scoped
divergence from the project's >=3.9 core floor (ADR-0002), documented in
pyproject.toml and docs/adr/0004-weasyprint-for-tagged-pdf-a-export.md. It is
never installed by make dev / make verify, mirroring the live extra: nothing
on the core monitoring/report path depends on it. tests/test_pdf_export.py skips
itself cleanly (pytest.importorskip) when the extra isn't installed.
make dev # one-time setup
make verify # lint, type-check, coverage (>=85%), security, a11y, PWA tests, i18n gatemake verify is the same gate set CI enforces (see the README's Quickstart section and
docs/GAP-LEDGER.md#gap-cicd-1 for the one place CI and the Makefile still don't call
identical commands, and why).
- Open a PR against
main(direct pushes bypass every gate below — see.github/rulesets/main.jsonfor the intended enforcement, anddocs/adr/0001-single-maintainer-review-posture.mdfor why a solo maintainer still opens PRs instead of pushing straight tomain). - Fill in
.github/PULL_REQUEST_TEMPLATE.md, including the "regenerated dated artifacts if template/threshold changed" line if you touched the report template, the a11y walkthrough's subject matter, or anything indocs/audits/. make verifymust pass locally before you open the PR; CI re-runs the equivalent gates plus the full OS × Python compatibility matrix, container build + Trivy scan, and secret scanning.- Update
CHANGELOG.mdunder[Unreleased]. - If your change is an expensive-to-reverse decision (a new dependency, a schema
change, declaring something N/A, changing the Python floor), write an ADR under
docs/adr/— seedocs/adr/0000-record-architecture-decisions.md.
- Formatting/linting:
ruff format+ruff check(make fmt/make lint); the select set isE, W, F, I, UP, B, SIM, S, C90, RUF— seepyproject.toml. - Types:
mypy --strict(make type); the only opt-out ismonitor/capture_live.py(hardware-dependent, documented inpyproject.toml). - Tests:
pytestwith--strict-markers --strict-config(make test/make cov); coverage floor is 85% branch coverage.