Summary
The Security Audit workflow's "Check committed audit requirements are current" step (.github/scripts/check_security_requirements.py) fails on PRs that make no dependency changes. Two independent design issues combine to turn any triggering PR red until someone re-commits the snapshot.
Recent real occurrence: PR #3769 (a code-only presets fix) failed this check — run 30368709737.
Root cause
1. The change-detection gate trips on non-dependency edits (incl. base/head SHA skew).
The gate diffs pyproject.toml between DEPENDENCY_DIFF_BASE (github.event.pull_request.base.sha) and DEPENDENCY_DIFF_HEAD (github.sha, the generated merge commit). In the #3769 run it reported Dependency audit inputs changed: pyproject.toml, yet the PR touches only presets/ and tests. The only pyproject.toml delta in that range was a version-string bump 0.14.3.dev0 -> 0.14.4.dev0 that lives solely in the merge commit (base.sha was still 0.14.3, PR head is 0.14.3). So main advancing between the recorded base.sha and the merge-ref generation misattributes an unrelated version bump to the PR — and a version string has zero effect on dependency resolution.
2. Once triggered, --upgrade guarantees drift.
The regeneration step runs uv pip compile ... --upgrade, which resolves the latest versions at CI runtime and compares byte-for-byte against the committed file. Any transitive dependency released since the snapshot was generated causes a mismatch. Reproduced against main: committed pins annotated-doc==0.0.4, a fresh --upgrade compile now resolves annotated-doc==0.0.5. This makes the check fail on any triggering PR, regardless of content, until the file is regenerated and re-committed.
Impact
- Contributors get red CI on PRs they didn't cause and can't fix (fork PRs can't easily regenerate + push).
- Maintainers must periodically re-commit the snapshot purely to chase upstream releases — churn unrelated to the PR under review.
Steps to reproduce
- Open any PR that doesn't change dependencies while a transitive dep has a newer release than the committed snapshot.
- Ensure the diff range includes a
pyproject.toml edit (e.g. a version bump on main) so the gate fires.
- The "Check committed audit requirements are current" step fails with "Regenerate .github/security-audit-requirements.txt...".
Proposed fixes (either/both)
- Narrow the gate: ignore version-only
pyproject.toml changes — key off [project.dependencies] / [project.optional-dependencies] rather than the whole file. This also sidesteps the base/merge SHA skew for version bumps.
- Drop
--upgrade from the sync check: validate that the committed pins are internally consistent with pyproject.toml (deterministic), and leave "chase latest releases" to the scheduled job, which already does live --upgrade resolution across the matrix. This decouples "is the committed file correct?" from "has a new version shipped since?".
Notes
- The scheduled
dependency-audit-scheduled job already provides live upgrade coverage, so removing --upgrade from the PR gate does not reduce vulnerability detection.
Summary
The
Security Auditworkflow's "Check committed audit requirements are current" step (.github/scripts/check_security_requirements.py) fails on PRs that make no dependency changes. Two independent design issues combine to turn any triggering PR red until someone re-commits the snapshot.Recent real occurrence: PR #3769 (a code-only presets fix) failed this check — run 30368709737.
Root cause
1. The change-detection gate trips on non-dependency edits (incl. base/head SHA skew).
The gate diffs
pyproject.tomlbetweenDEPENDENCY_DIFF_BASE(github.event.pull_request.base.sha) andDEPENDENCY_DIFF_HEAD(github.sha, the generated merge commit). In the #3769 run it reportedDependency audit inputs changed: pyproject.toml, yet the PR touches onlypresets/and tests. The onlypyproject.tomldelta in that range was a version-string bump0.14.3.dev0 -> 0.14.4.dev0that lives solely in the merge commit (base.sha was still0.14.3, PR head is0.14.3). So main advancing between the recordedbase.shaand the merge-ref generation misattributes an unrelated version bump to the PR — and a version string has zero effect on dependency resolution.2. Once triggered,
--upgradeguarantees drift.The regeneration step runs
uv pip compile ... --upgrade, which resolves the latest versions at CI runtime and compares byte-for-byte against the committed file. Any transitive dependency released since the snapshot was generated causes a mismatch. Reproduced againstmain: committed pinsannotated-doc==0.0.4, a fresh--upgradecompile now resolvesannotated-doc==0.0.5. This makes the check fail on any triggering PR, regardless of content, until the file is regenerated and re-committed.Impact
Steps to reproduce
pyproject.tomledit (e.g. a version bump on main) so the gate fires.Proposed fixes (either/both)
pyproject.tomlchanges — key off[project.dependencies]/[project.optional-dependencies]rather than the whole file. This also sidesteps the base/merge SHA skew for version bumps.--upgradefrom the sync check: validate that the committed pins are internally consistent withpyproject.toml(deterministic), and leave "chase latest releases" to the scheduled job, which already does live--upgraderesolution across the matrix. This decouples "is the committed file correct?" from "has a new version shipped since?".Notes
dependency-audit-scheduledjob already provides live upgrade coverage, so removing--upgradefrom the PR gate does not reduce vulnerability detection.