fix(release,ci): hold sigstore at 4.4.0 per owner ruling, make it stick, and audit the lock (BACKLOG #332) - #801
Merged
Conversation
…e version discrepancy (BACKLOG #332) Docs only. No build change. The #332 row said "not started". The mechanism shipped in a935480 (2026-08-29). Verified at 46ea10a on a clean tree, reading the working tree rather than piping git show: group at pyproject.toml:294, tracked lock (193 hashes), export + diff gate at security.yml:107-108, resync at dependabot-lock-resync.yml:140,152,156, --require-hashes install at release.yml:391, guard re-pointed at tests/test_ci_venv_pinning.py:204 and :994. Steps 2, 3 and 5 are done. Raised, NOT resolved: the row records an owner ruling of 2026-08-22 pinning sigstore==4.4.0, "NOT 4.5.0"; the shipped code pins 4.5.0 (pyproject.toml:295) and release.yml:385-390 argues the cooldown objection is spent. Both citations are live and they disagree. It needs an owner ruling, so both are recorded with the timeline and left alone. Also recorded: step 4's contamination risk did not materialise (measured with the CI-pinned uv==0.12.0, control-first); step 6 not started; and one new residual -- nothing pins release-tools.lock INTO the DEP-1 set, since the lockstep test is satisfied by set equality. ADR 0034's sigstore residual said the lock omits sigstore. That is now false, so the row is marked superseded rather than deleted. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… ruling (BACKLOG #332) Owner ruling 2026-08-22, re-affirmed 2026-09-03 with the full history in hand: pin sigstore==4.4.0, NOT 4.5.0. a935480 (2026-08-29) landed the hashed-lock mechanism but authored the pin as 4.5.0, reasoning that dependabot.yml's 5-day cooldown had expired. That argument was published on PR 531 at 2026-08-26T23:46:18Z and RETRACTED BY ITS OWN AUTHOR at 2026-08-27T02:45:36Z, two days before it shipped. The retraction's own words: "I checked whether the ADR's premise had expired. I never checked whether the question had since been answered." The cooldown never reached the ruling in either direction. It expired 2026-08-02; the first ruling came twenty days later, and the 2026-09-03 re-affirmation had the expiry in the packet. Neither version has a patch line, so patch availability does not discriminate. Measured against PyPI 2026-09-03: 4.x is 4.0.0, 4.1.0, 4.2.0, 4.3.0, 4.4.0, 4.5.0 and nothing else. The 3.x series did ship patches, so a future 4.4.1 is possible. Re-export measured with the CI-pinned uv==0.12.0, control first (the unchanged tree re-exported byte-identically, so the instrument matches CI's): uv.lock moves one line, release-tools.lock three, and all six pre-existing DEP-1 artifacts stay byte-identical. No contamination. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…t backs it (BACKLOG #332) Two changes that only work together. 1. dependabot.yml gains `ignore: sigstore >=4.5.0`, so a routine weekly PR cannot re-propose the version the owner twice declined. Without it the ruling survives only until the next bot PR is merged, which is how it was undone the first time. 2. security.yml's audit step gains `pip-audit -r ci/locks/release-tools.lock`. This resolves the stated conflict at dependabot.yml's "NOT IGNORED, deliberately" policy rather than stepping over it. That policy keeps the `==` pins moving so an unattended pin cannot rot into "pinned, stale, unpatched". Its reason is about versions NOBODY HAS DECIDED, so it does not reach a version under an owner ruling -- there the weekly PR does not refresh neglect, it re-litigates a decision. The carve-out is recorded at the policy, with the condition for lifting it. The STALENESS half of the policy still binds, so it is honoured rather than waived. An `ignore` also suppresses the security track for the named range, which the file already accepts for annotated-types and ruff on the grounds that pip-audit still detects a withheld fix and reds a required gate. That backstop did not exist here: a935480 added release-tools.lock to the export and byte-diff set but not to the audit set, leaving the release signing closure the only committed lock nothing audited. Change 2 closes that. Each side's comment names the other, so removing one without the other is caught by reading either. Scoped to >=4.5.0, not to the package. sigstore 4.x has shipped zero patch releases but 3.x did, so a 4.4.1 is possible; this range leaves the 4.4.x patch track open. Also corrected: the exported-artifact count in the same file said SIX; it has been seven since a935480. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ore 4.5.0 (BACKLOG #332) The pin moved back to 4.4.0 in the two commits before this one. This commit fixes the records that still argue the other way, so a reader does not find the case for 4.5.0 sitting unmarked beside the 4.4.0 pin. release.yml's "ON THE VERSION" comment restated the cooldown argument. It now records the ruling, names the retraction, and says plainly not to re-derive the argument. ADR 0034 had two false assertions, both that sigstore is absent from every lock: the section 3 register row at :206 and the residuals row at :358, read at 46ea10a. Both are marked in place rather than deleted, per that table's own rule. A 2026-09-03 amendment records the sequence, the discharged re-evaluation clause, and the pip-audit gap this work closed. The backlog row cited :350 for that residual in four places. :350 is a blank line and was wrong at every commit; the citations now name the row and give the line with the commit they were read at. The #332 row records the ruling, the retraction timeline, the packet measurement showing the ruling never rested on the cooldown, the PyPI measurement showing neither version has a patch line, and the audit gap. It stays OPEN: step 6 is now the only thing left in it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Collaborator
Author
|
LANDER: applying SO READ THIS LABEL AS A BYPASS TOKEN, NOT A REVIEW. It records that the gate was cleared by instruction. It does not assert that anyone read this diff, and no later reader should infer one from it. What I can say: this PR appeared in a read-only pre-verification sweep of all 59 non-draft open PRs (per-PR defect read, plus pairwise -- Lander |
github-merge-queue
Bot
removed this pull request from the merge queue due to failed status checks
Sep 4, 2026
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.
Summary
Holds the release signing toolchain at
sigstore==4.4.0per owner ruling, adds a Dependabotignoreso it stays there, and closes an audit gap that made both of those safe. Four commits, onelayer each.
The version is not reopened here. Owner ruling 2026-08-22, re-affirmed 2026-09-03.
e0bfb91e7127587adfpyproject.toml,uv.lock,ci/locks/release-tools.lock144abab09dependabot.ymlignore +security.ymlaudit backstop3c3f778aeWhy the pin moved back
a9354808e(2026-08-29) landed the hashed-lock mechanism correctly but authored the pin as 4.5.0,reasoning that the 5-day cooldown had expired. That argument was published on PR 531 at
2026-08-26T23:46:18Z and retracted by its own author at 2026-08-27T02:45:36Z — two days before it
shipped. Its words: "I checked whether the ADR's premise had expired. I never checked whether the
question had since been answered." So
release.yml's comment was not merely written againstpre-ruling
main; it restated a withdrawn position.The cooldown never reached the ruling in either direction, which is what makes the ruling robust.
It expired 2026-08-02; the first ruling came twenty days later. Verified in the owner packet at commit
f3d3d1be6: zero occurrences of cooldown, 2026-08-02, 5-day, ages out or publish date,against controls that fired in the same window (
sigstore3,4.4.03,4.5.02). One correctionto the numbers I was given:
332is 0 hits in that 30-line section, not 6.Neither version has a patch line, so patch availability does not discriminate. Measured against
PyPI 2026-09-03 with a control: 4.x is 4.0.0, 4.1.0, 4.2.0, 4.3.0, 4.4.0, 4.5.0 and nothing else. The
3.x series did ship patches (3.5.1, 3.6.7) — my control — so a future 4.4.1 is possible, and the
ignoreis scoped to leave that track open. The record does not say 4.4.0 is uniquely unpatchable.The
dependabot.yml:55conflict: I took (a), and it is only sound because of a fixdependabot.ymlrecords that the==pins in[dependency-groups]are deliberately not ignored,so the hash-pinned toolchain keeps moving rather than rotting into "pinned, stale, unpatched."
I added the ignore and amended the policy to record the carve-out — option (a). The reasoning:
that policy is about versions nobody has decided. It keeps an unattended pin fresh.
sigstore'sversion is an owner ruling, so the failure mode inverts — a weekly PR does not refresh neglect, it
re-proposes a declined version, and merging it silently reverses a decision. That is not
hypothetical: it is what happened in
a9354808e.But the staleness half of that policy does still bind, and honouring it required a fix. An
ignorealso suppresses the security track for its range. The file already accepts that forannotated-typesandruffon one explicit ground: pip-audit still detects a withheld fix and reds arequired gate within ~24h. That backstop did not exist here.
Gap found and closed:
a9354808eaddedci/locks/release-tools.lockto the export set and thebyte-diff gate but not to
security.yml's audit step, which ran pip-audit overrequirements.lock,ci-scanners.lockandci-quality.lockonly. From 2026-08-29 the release signing closure was the onecommitted lock nothing audited — hash-pinned, therefore sticky, therefore exactly the posture ADR 0034
calls worse than floating.
pip-audit -r ci/locks/release-tools.lock --descnow runs beside the othertwo, and each side's comment names the other, so removing one without the other is caught by reading
either. If that audit line ever goes, the ignore entry must go with it.
Scoped
>=4.5.0, not to the package. Consequence stated: a CVE in sigstore's closure now reds arequired gate — wanted behaviour for the closure that signs the wheel, sdist, SBOM and VEX.
Records corrected
register row at
:206and the residuals row at:358, read at46ea10a7. Marked in place, notdeleted. A 2026-09-03 amendment records the sequence and the discharged re-evaluation clause.
assumed routing sigstore through the lock would force 4.5.0, so it treated "hash-lock it" and
"keep 4.4.0" as exclusive. A group carries its own specifier. They were never in tension, and that
coupling held a real gap open for five weeks.
:350citation was wrong in four places in the backlog: close #1106 (already shipped) and file #1223 -- http_auth_user leaks verbatim #332 row.:350is a blank line and waswrong at every commit. Now anchored on the row name, with the line and the commit it was read at.
release.yml's "ON THE VERSION" comment now records the ruling and says not to re-derive theargument.
dependabot.ymlsaid SIX exported artifacts; it has been seven sincea9354808e.Still open
#332 stays OPEN, and step 6 is now the only thing in it.
buildandcyclonedx-bomremain inlinein the same privileged job.
cyclonedx-bomis half of a byte-identical pair —release.yml:326andsecurity.yml:367— thattest_sbom_install_is_byte_identical_in_release_and_securityrequires tostay identical, so both halves must move in one commit or that test reds.
A smaller residual is recorded in the row: nothing pins
release-tools.lockinto the DEP-1 set,because
test_dep1_lock_resync_lockstep.pyis satisfied by set equality between the two workflows.Checks
Ran locally:
pytestontest_ci_venv_pinning.py,test_dep1_lock_resync_lockstep.py,test_new_dependency_check.py,test_release_pipeline.py,test_dependabot_automerge_guardrails.py,test_security_posture.py,test_install_instruction_provenance.py— 163 passed, 7 skipped.uv==0.12.0, the unchanged tree re-exportedbyte-identically (DIFFS=0), so the instrument matches CI's. After the change:
uv lock --checkpasses and all seven exports match the committed tree. All six pre-existing artifacts stayed
byte-identical — no contamination, so the
semgrep-style excluded-by-decision call was neverreached.
ruff check/ruff format --check,backlog_status_check.py,ledger_check.py,scan_forbidden.py,control_char_check.py, andactionlintvia pre-commit — all pass. The leakgate blocked one commit for an internal branch slug I had cited; removed, not allowlisted.
pip-auditlegs themselves,windows-service-smoke, andanything needing the Actions environment. The new
pip-audit -r ci/locks/release-tools.locklinehas never executed — read it on this PR's
security.ymlrun before merging.Testability caveat
release.ymlruns only on a tag push orworkflow_dispatch, so no PR CI leg executes thesigning path. The guards assert the lock exists, pins
sigstoreexactly, is fully hashed and isinstalled with
--require-hashes; they cannot assert it installs. The first real run of this changeis a release. Per ADR 0034's protocol: dry-run via
workflow_dispatchand read the log before thenext tag. That has not happened.
Auto-merge not armed. #332 claimed via
scripts/coord/claim.ps1; release it when this lands.🤖 Generated with Claude Code