Goal
Make ./tests/full_validate.sh actually catch a stale uv.lock locally, so the local aggregate validator lives up to what docs/testing.md already claims about it.
Background
PR #413 added a standalone uv-lock CI job (.github/workflows/tests.yml) that runs uv lock --check, and updated docs/testing.md to document uv lock --check as the way to verify uv.lock still matches pyproject.toml. That same doc states that ./tests/full_validate.sh "composes the same named gates used by CI" and is "the one-command local aggregate of all source gates."
In practice, the lockfile-freshness check isn't one of those composed gates: tests/validate.sh's required_files list only checks that uv.lock exists as a file, not that it's in sync with pyproject.toml. The uv-lock CI job runs independently of full_validate.sh.
Scope
- Add a
uv lock --check call to tests/full_validate.sh (e.g. as part of the baseline gate, alongside the other repo-hygiene checks already there).
- Keep the existing standalone
uv-lock CI job as-is — this is about closing the local/CI parity gap, not restructuring CI.
- No changes to
pyproject.toml, uv.lock itself, or the dependency extras.
Acceptance criteria
Validation
./tests/full_validate.sh
./tests/full_validate.sh --gate baseline
- Manually stale a lockfile (bump a version in
pyproject.toml without regenerating uv.lock) and confirm the baseline gate now fails locally.
Non-goals
- Do not change what
uv lock --check validates or how the uv-lock CI job is triggered.
- Do not add uv as a new hard requirement for gates that don't already need it.
Origin
Follow-up from code review on #413 (comment: #413 (comment)).
Goal
Make
./tests/full_validate.shactually catch a staleuv.locklocally, so the local aggregate validator lives up to whatdocs/testing.mdalready claims about it.Background
PR #413 added a standalone
uv-lockCI job (.github/workflows/tests.yml) that runsuv lock --check, and updateddocs/testing.mdto documentuv lock --checkas the way to verifyuv.lockstill matchespyproject.toml. That same doc states that./tests/full_validate.sh"composes the same named gates used by CI" and is "the one-command local aggregate of all source gates."In practice, the lockfile-freshness check isn't one of those composed gates:
tests/validate.sh'srequired_fileslist only checks thatuv.lockexists as a file, not that it's in sync withpyproject.toml. Theuv-lockCI job runs independently offull_validate.sh.Scope
uv lock --checkcall totests/full_validate.sh(e.g. as part of the baseline gate, alongside the other repo-hygiene checks already there).uv-lockCI job as-is — this is about closing the local/CI parity gap, not restructuring CI.pyproject.toml,uv.lockitself, or the dependency extras.Acceptance criteria
./tests/full_validate.sh(or--gate baseline) locally fails whenuv.lockis stale relative topyproject.toml, without requiring a separate manualuv lock --checkinvocation.docs/testing.md's claim thatfull_validate.sh"composes the same named gates used by CI" is accurate for the lockfile check.uvbinary produces a clear error (consistent with howfull_validate.shalready treats bandit/pip-audit as required-not-skipped) rather than a silent skip.full_validate.shgates and the standaloneuv-lockCI job remain green.Validation
./tests/full_validate.sh./tests/full_validate.sh --gate baselinepyproject.tomlwithout regeneratinguv.lock) and confirm the baseline gate now fails locally.Non-goals
uv lock --checkvalidates or how theuv-lockCI job is triggered.Origin
Follow-up from code review on #413 (comment: #413 (comment)).