Skip to content

Dependency Contract

The five requirements*.in files own every exact direct Python dependency. Each is paired with a same-stem requirements*.txt lock generated by pip-compile with hashes; locks must never be edited manually. Development, standalone, and documentation inputs each include requirements.in exactly once, so all runtime-derived environments resolve the same published runtime pins. The lint input remains independent.

pyproject.toml declares runtime dependencies as dynamic setuptools metadata read from requirements.in. It contains no second copy of those versions and no internal development, standalone, or documentation extras. Its build-system setuptools range expresses a backend capability; the exact setuptools used by project commands remains pinned in requirements-dev.in.

requirements-standalone.txt contains the runtime graph plus the exact PyInstaller toolchain. It is installed only into venv-standalone/; the ordinary source runtime does not carry a bundler.

requirements-docs.txt contains the runtime graph plus the exact MkDocs Material toolchain. It is installed only into venv-docs/ and owns both local site rendering and the Pages build.

.github/dependency-review-config.yml is the single license policy consumed by GitHub Dependency Review and the local make licenses gate. Globally allowed entries are reviewed project-wide SPDX identifiers, including MPL-2.0. A dependency whose reported license set also contains a GPL-family identifier must never widen that global list: it requires a package-only PyPI URL under allow-dependencies-licenses, an adjacent review rationale, and a matching package in the current locks. Missing, newly unrecognized, and stale exceptions fail locally. A package exception permits only its otherwise rejected GPL-family identifiers; every non-GPL identifier still has to belong to the project-wide allow-list.

The GitHub action matches license-exclusion package URLs by package identity, so the policy intentionally contains no duplicate version values. The inputs are the only source of direct versions; the generated locks are the only resolved versions used for installation. The local gate requires each excepted identity to exist in a lock and inspects the installed locked version. GitHub's exception remains package-wide; reviewers must therefore inspect a changed dependency-graph license finding for an excepted package. The local gate separately refuses to let that exception conceal a non-GPL identifier reported by installed package metadata.

The local gate inspects the installed package metadata for every runtime, development, lint, standalone, and documentation lock. GitHub additionally uses its dependency-graph license data, which may aggregate license files not declared by wheel metadata. The package exceptions cover that external aggregation without granting the same license to unrelated dependencies; they do not turn the GitHub action's package-level exception into a per-license one.

ssh-wrapper==0.1.0 is resolved from PyPI. Every runtime-derived lock—runtime, development, standalone, and documentation—binds its published artifact hashes, and no source directory is added to PYTHONPATH.

Use make lock after changing direct dependencies in requirements*.in and make refresh-dependencies for a deliberate whole-tree upgrade. Review every changed pin and hash. Dependabot treats each .in file as a native pip-compile manifest, updates the matching .txt, and groups the resulting Python changes into one pull request. pyproject.toml is excluded from its pip manifests so a plain requirements updater cannot take ownership of generated locks. The two local resolver images pin the pip==26.1.1 and pip-tools==7.5.3 pair, matching the hosted Dependabot resolver used when this contract was established. A future resolver migration must update both images together and reproduce all five locks.

A Dependabot pull request must install and reproduce all five locks before it can merge. make freeze-check reproduces the locks; make dependency-snapshot renders their exact dependency graph; make audit enforces the reviewed vulnerability exceptions in .github/dependency-audit-exceptions.json. Exceptions must identify one package, version, advisory, and review reason. The strict audit consumes the complete hash-locked development, documentation, and standalone graphs without invoking pip, including the published SSH library. Run both make licenses and make audit after a lock change; both are already blocking parts of make ci.