Skip to content

fix(release,ci): hold sigstore at 4.4.0 per owner ruling, make it stick, and audit the lock (BACKLOG #332) - #801

Merged
wshallwshall merged 4 commits into
mainfrom
worktree-agent-a0b411c043c40076f
Sep 4, 2026
Merged

fix(release,ci): hold sigstore at 4.4.0 per owner ruling, make it stick, and audit the lock (BACKLOG #332)#801
wshallwshall merged 4 commits into
mainfrom
worktree-agent-a0b411c043c40076f

Conversation

@wshallwshall

@wshallwshall wshallwshall commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

Summary

Holds the release signing toolchain at sigstore==4.4.0 per owner ruling, adds a Dependabot
ignore so it stays there, and closes an audit gap that made both of those safe. Four commits, one
layer each.

The version is not reopened here. Owner ruling 2026-08-22, re-affirmed 2026-09-03.

# Commit Layer
1 e0bfb91e7 Record the shipped mechanism; raise the discrepancy (written before the ruling)
2 127587adf The pin: pyproject.toml, uv.lock, ci/locks/release-tools.lock
3 144abab09 Make it stick: dependabot.yml ignore + security.yml audit backstop
4 3c3f778ae Correct every record that argued for 4.5.0

Why 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 against
pre-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 (sigstore 3, 4.4.0 3, 4.5.0 2). One correction
to the numbers I was given:
332 is 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
ignore is scoped to leave that track open. The record does not say 4.4.0 is uniquely unpatchable.

The dependabot.yml:55 conflict: I took (a), and it is only sound because of a fix

dependabot.yml records 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's
version 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
ignore also suppresses the security track for its range. The file already accepts that for
annotated-types and ruff on one explicit ground: pip-audit still detects a withheld fix and reds a
required gate within ~24h. That backstop did not exist here.

Gap found and closed: a9354808e added ci/locks/release-tools.lock to the export set and the
byte-diff gate but not to security.yml's audit step, which ran pip-audit over requirements.lock,
ci-scanners.lock and ci-quality.lock only. From 2026-08-29 the release signing closure was the one
committed 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 --desc now runs beside the other
two, 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 a
required gate — wanted behaviour for the closure that signs the wheel, sdist, SBOM and VEX.

Records corrected

  • 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 46ea10a7. Marked in place, not
    deleted. A 2026-09-03 amendment records the sequence and the discharged re-evaluation clause.
  • The residual's reasoning contained a false counterfactual, and that is the transferable part: it
    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.
  • The :350 citation was wrong in four places in the backlog: close #1106 (already shipped) and file #1223 -- http_auth_user leaks verbatim #332 row. :350 is a blank line and was
    wrong 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 the
    argument.
  • dependabot.yml said SIX exported artifacts; it has been seven since a9354808e.

Still open

#332 stays OPEN, and step 6 is now the only thing in it. build and cyclonedx-bom remain inline
in the same privileged job. cyclonedx-bom is half of a byte-identical pair — release.yml:326 and
security.yml:367 — that test_sbom_install_is_byte_identical_in_release_and_security requires to
stay 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.lock into the DEP-1 set,
because test_dep1_lock_resync_lockstep.py is satisfied by set equality between the two workflows.

Checks

Ran locally:

  • pytest on test_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.py163 passed, 7 skipped.
  • DEP-1 re-export, control first: with the CI-pinned uv==0.12.0, the unchanged tree re-exported
    byte-identically (DIFFS=0), so the instrument matches CI's. After the change: uv lock --check
    passes 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 never
    reached.
  • ruff check / ruff format --check, backlog_status_check.py, ledger_check.py,
    scan_forbidden.py, control_char_check.py, and actionlint via pre-commit — all pass. The leak
    gate blocked one commit for an internal branch slug I had cited; removed, not allowlisted.
  • Not run, and not the gate: mypy and the full pytest suite. No Python source changed.
  • Hosted-runner only, unread by me: the pip-audit legs themselves, windows-service-smoke, and
    anything needing the Actions environment. The new pip-audit -r ci/locks/release-tools.lock line
    has never executed
    — read it on this PR's security.yml run before merging.

Testability caveat

release.yml runs only on a tag push or workflow_dispatch, so no PR CI leg executes the
signing path.
The guards assert the lock exists, pins sigstore exactly, is fully hashed and is
installed with --require-hashes; they cannot assert it installs. The first real run of this change
is a release.
Per ADR 0034's protocol: dry-run via workflow_dispatch and read the log before the
next 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

…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>
@wshallwshall wshallwshall added the reviewed A reviewer has read this. Removed automatically when new commits arrive. label Sep 3, 2026
wshallwshall and others added 3 commits September 3, 2026 17:42
… 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>
@wshallwshall wshallwshall changed the title docs(backlog,adr): record #332's shipped mechanism and raise the sigstore version discrepancy (BACKLOG #332) fix(release,ci): hold sigstore at 4.4.0 per owner ruling, make it stick, and audit the lock (BACKLOG #332) Sep 3, 2026
@github-actions github-actions Bot removed the reviewed A reviewer has read this. Removed automatically when new commits arrive. label Sep 4, 2026
@wshallwshall

Copy link
Copy Markdown
Collaborator Author

LANDER: applying reviewed under an explicit owner override. The owner has suspended the reviewer process and directed that this label be applied as needed to clear the gate.

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 git merge-tree against every PR sharing a non-BACKLOG file), and no blocking defect was recorded against it. That sweep did not run any tests, and CI has been down for hours, so nothing here has a current green.

-- Lander

@wshallwshall wshallwshall added the reviewed A reviewer has read this. Removed automatically when new commits arrive. label Sep 4, 2026
@wshallwshall
wshallwshall added this pull request to the merge queue Sep 4, 2026
@wshallwshall
wshallwshall removed this pull request from the merge queue due to a manual request Sep 4, 2026
@github-actions github-actions Bot added the ci-red A required check went red. Attribute it before retrying. label Sep 4, 2026
@wshallwshall
wshallwshall added this pull request to the merge queue Sep 4, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Sep 4, 2026
@wshallwshall
wshallwshall added this pull request to the merge queue Sep 4, 2026
Merged via the queue into main with commit 93288ff Sep 4, 2026
46 of 47 checks passed
@wshallwshall
wshallwshall deleted the worktree-agent-a0b411c043c40076f branch September 4, 2026 18:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci-red A required check went red. Attribute it before retrying. reviewed A reviewer has read this. Removed automatically when new commits arrive.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant