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.