Repository navigation
Conversation
scripts/rules/locked_installs.py scans workflow run blocks, shell scripts, Makefile recipes, Dockerfiles and Modal image definitions for commands and calls that install Python packages, and reports each one that does not install exactly what uv.lock pins (uv sync/uv run without --frozen or --locked, pip, uv pip, uvx, uv tool, pipx and the Modal pip_install family). A [[rule.exception]] can now name one site with `match`, and end on a `removal` condition, a review date with an `issue`, or be a `pinned-tool` install the scanner checks is pinned with ==. The proof fixtures hold one bad example of each install kind. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The scanner now also reports every pytest, coverage run, unittest, tox or nox command in workflow run blocks, shell scripts, Makefile recipes and Dockerfiles that does not run through uv run --frozen or --locked. The proof fixtures hold a test step on setup-python's interpreter, a script and a Make target that do the same. The scan-coverage test now also skips the registry's own rule fixtures. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The scanner now reports a pyproject.toml without a committed uv.lock beside it, and a project whose uv.lock no pull request job checks with uv lock --check (following the scripts and Make targets each job runs). The pull request quality-guards job now installs uv 0.12.1, the version the Cloud Run image and the release job use, and runs uv lock --check. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The scanner now requires a workflow with a schedule trigger whose job, through the scripts and Make targets it runs, relocks with upgrades (--upgrade, --upgrade-package, -U or -P) and opens or updates a pull request, and that the relock covers every committed uv.lock. It reads only the workflow configuration; it does not run the workflow. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Registers api.dependencies.lock-committed, install-from-lock, test-from-lock and scheduled-relock, each enforced by the new locked-installs quality guard with proof fixtures, and documents them in docs/engineering/skills/dependencies.md. Installs that bypass uv.lock are now registered exceptions, each naming its command, a reason, and a removal condition or the pinned-tool category: the pinned shellcheck and ruff installs, the two operational workflows that pip install for a bare python, the bundle updater's pip install, the collector check's uv run --with, and docker/Dockerfile. Three sites move to the lock where that installs the same versions: the seed workflow's uv run steps and migrate_v2_metadata_schema.sh run with --frozen after the job's uv sync --frozen, and the rules tool's tests run through uv run --frozen in the quality-guards job. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tests/unit/test_locked_test_environments.py now uses the guard's makefile_recipes and executed_lines instead of its own copies, and drops its two checks that test jobs install nothing with pip and run tests through uv run --frozen: the guard checks both for every file, not only test jobs. It keeps the checks the guard does not make: the Make targets CI calls, the Python version each test job selects, and the operational jobs' explicit install. Adds the changelog fragment. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
anth-volk
added this pull request to stack #3998
October 21, 2026 20:43
A composite action keeps its steps under runs: steps:, not jobs:, so the workflow parser found none of its commands: an install in one was counted as scanned but never checked. Composite actions now have their own kind, whose run: values and blocks are read directly, and the scan-coverage test includes them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The scheduled-relock check accepted an upgrade flag anywhere in the
job, so a plain uv lock next to pip install -U pip passed. The flag must
now be on the uv lock command, or in a shell array the command expands
("${arguments[@]}"), as relock_dependencies.sh builds its
--upgrade-package list.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
uv run --frozen --no-sync runs in the environment a uv sync --frozen created, which rule test-from-lock allows, but the test-command check treated every --no-sync run as outside the lock. A uv run test command now counts as locked when it has --frozen or --locked and no --with or --no-project. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The rules-registry field table now lists match, removal, issue and category for [[rule.exception]] and points to dependencies.md. The test-from-lock rule's scope includes Dockerfiles, as its statement says, and an exception without a match is reported as naming no site, which fits both rules that read it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #3996
Third pull request in a stack: #3995 (tests install from
uv.lock) → #3994 (daily relock workflow) → this one.What this adds
Four rules in
rules/registry.toml, allmechanicalandenforced, checked by a newlocked-installsquality guard (scripts/rules/locked_installs.py, standard library only) in the pull requestquality-guardsjob, each with bad and good proof fixtures underscripts/rules/tests/fixtures/locked_installs/:api.dependencies.lock-committedpyproject.tomlhas a committeduv.lockbeside it, and a pull request job runsuv lock --checkfor it. Thequality-guardsjob now installs uv 0.12.1 and runsuv lock --check.api.dependencies.install-from-lockrun:blocks, tracked shell scripts, Makefile recipes, Dockerfiles and Modal image definitions usesuv sync --frozen/uv run --frozen(or--locked), unless it is a registered exception.api.dependencies.test-from-lockpytest,coverage run,unittest,toxornoxcommand in those files runs throughuv run --frozen.api.dependencies.scheduled-relockscheduletrigger relocks everyuv.lockwith upgrades and runsgh pr create/gh pr edit(static check ofrelock-dependencies.ymland the scripts it runs).[[rule.exception]]can now name one site (match), and end on aremovalcondition, a review date (expires) with a trackingissue, or be apinned-tool(the guard checks every package in the command is pinned with==). An exception withoutmatch, without a removal condition or tracked review date, or that matches nothing, fails the guard.test_every_install_source_is_scannedchecks, fromgit ls-files, that every workflow, shell script, Makefile, Dockerfile and Modal image module is scanned.docs/engineering/skills/dependencies.mdexplains the four rules and how to add an exception.tests/unit/test_locked_test_environments.py(from #3995) now uses the guard's job tracing and keeps only the checks the guard does not make (Make targets, the Python version per test job, the operational jobs' explicit install).Install sites found
Makefile(install,test,test-env-vars,debug-test,debug,debug-fastapi,typecheck,changelog),pr.yml,push.yml,alembic-v1-check.yml,alembic-v2-check.yml,v2-integration-check.yml,build_changelog.sh,run_live_staging_tests.sh,test_alembic_v2_lifecycle.sh,update-policyengine-package.sh,request-simulation-model-versions.sh,gcp/cloud_run/Dockerfileseed-v2-database.ymluv run python scripts/...(6 steps),migrate_v2_metadata_schema.shuv run alembic(3)uv run --frozen, after the job'suv sync --frozen. With a consistent lock this installs the same versions.pr.ymlquality-guardspython -m unittest discover -s scripts/rules/testsuv run --frozen --extra dev, withUV_PYTHON: "3.12"on the jobpr.yml,push.ymlpip install "shellcheck-py==0.11.0.1"install_locked_ruff.shpip install "ruff==${version}"(version read fromuv.lock)migrate-v1-cloud-sql.yml,process-v1-household-mirror-event.ymlpip install -e ".[dev]"pythonagainst the production and staging databases. Removal: the job usesuv sync --frozen/uv run --frozenafter a staging run succeeds.update-policyengine-bundle.yamlpip install requests uvuv.lock, so it runs before a matching lock exists. Removal: install uv withastral-sh/setup-uvat a pinned version and run its Python throughuv run --frozen.verify_observability_collector.shuv run --no-project --with grpcio --with opentelemetry-protouv sync --frozenand the check runs throughuv run --frozen.docker/Dockerfilepip install "policyengine[models]==6.7.5" ipythonipythonand the bundle's dependencies are unpinned. Removal: the image installs fromuv.lock, or is removed. Thematchleaves out the version so bundle updates don't make it stale.No Modal image definitions exist in this repository;
policyengine_api/libs/simulation_api_modal.pyis a client, and the scan of all non-test Python modules found no image install calls.Red and green
python -m unittest scripts.rules.tests.test_locked_installs: import ofscripts.rules.locked_installsfailsunlocked_test_commands/test_command_violationsfailslock_violationsfailsno pull request job runs uv lock --check for .until thequality-guardsstep was addedrelock_violationsfailstest_repository_has_no_violationsfails:rules/registry.toml has no rule ...run_quality_guards.py:locked-installs: passedtests/unit/test_locked_test_environments.py16 passAfter the self-review fixes below,
test_locked_installsruns 16 tests, all passing.Checks run locally
ruff format --check .,ruff check .(locked ruff): pass.python scripts/run_quality_guards.py: all guards pass, includinglocked-installs.python -m scripts.rules guard --base-ref origin/auto-relock-dependencies-workflow: pass;python -m scripts.rules render: no changes.uv lock --check: pass.python -m unittest discover -s scripts/rules/tests -t .(in the locked environment): pass.pytest tests/unit/v2/test_metadata_deployment.py tests/unit/test_locked_test_environments.py tests/unit/test_alembic_workflows.py tests/unit/test_relock_dependencies_workflow.py: pass.# noqacount: 98 before and after. No new suppressions; the guard's import joins an existing suppressed import inrun_quality_guards.py.Environment variables
pr.ymlquality-guards:UV_PYTHONis set in the job'senv:.uv syncuses public PyPI (no[tool.uv.index]), so it needs no credentials. The guards readGITHUB_ACTIONSandGITHUB_BASE_REF, which the runner sets.seed-v2-database.yml(only--frozenadded):V2_MIGRATION_DATABASE_URLis a secret in thestagingandproductionGitHub environments, and reaches each step through itsenv:. The job selects the environment withenvironment: ${{ inputs.deployment_environment }}, andpush.ymlcalls the workflow withsecrets: inherit.V2_SUPABASE_PROJECT_REFandV2_SUPABASE_ENVIRONMENTare variables in both environments, andPRODUCTION_V2_SUPABASE_PROJECT_REFis a repository variable; all three reach the job through itsenv:.migrate_v2_metadata_schema.shreadsV2_MIGRATION_DATABASE_URLfrom its step'senv:.seed-v2-database.ymlruns only frompush.ymlon the default branch, or by manual dispatch, so pull request CI does not run it. Its--frozenchange has not run.Self-review fixes
A self-review of this branch found three defects, each fixed test first:
runs: steps:were never readtest_rule_proof_install_from_lockfails: the new composite action fixture'spip install requestsanduv sync --extra devare not reportedrun:reader; the test passes, and the coverage test includesaction.ymltest_upgrade_flag_must_belong_to_the_relockfails: a plainuv locknext topip install -U pippassesuv lockcommand or in an array it expands ("${upgrade_arguments[@]}"); the test passes, and the repository's relock workflow still passesuv run --frozen --no-sync pytestwas reported as outside the locktest_rule_proof_test_from_lockfails on the good fixture's--no-syncrunuv runtest command is locked when it has--frozen/--lockedand no--with/--no-project; the test passesAlso fixed: the rules-registry skill's field table now lists the new exception fields, and the test-from-lock rule's scope includes Dockerfiles.
Not covered
.python-versionselects (3.14), which is outside these rules.run:blocks, not third-party actions' own installs.🤖 Generated with Claude Code