Skip to content

fix(deps): chacha20 0.10.1 is yanked, so deny refuses every branch (CLOUD-1074) - #716

Merged
wenzowski merged 1 commit into
mainfrom
claude/deny-chacha20-yank
Aug 27, 2026
Merged

fix(deps): chacha20 0.10.1 is yanked, so deny refuses every branch (CLOUD-1074)#716
wenzowski merged 1 commit into
mainfrom
claude/deny-chacha20-yank

Conversation

@wenzowski

@wenzowski wenzowski commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Closes CLOUD-1077

What is broken

mise run deny fails advisories on the committed lockfile, on every branch:

error[yanked]: detected yanked crate (try `cargo update -p chacha20`)
chacha20 0.10.1 registry+https://github.com/rust-lang/crates.io-index

chacha20 0.10.0 and 0.10.1 are both yanked; 0.10.2 is published and is not. It arrives transitively via rand, and 0.10.2 satisfies the existing requirement — so this is one relocked package, Cargo.lock only, no manifest change and no API surface.

After: advisories ok, bans ok, licenses ok, sources ok.

Why it blocks everyone, not one branch

ci.yml carries no paths: filter and [tasks.ci] is depends = ["hooks", "deny"], so deny runs in the ci job on every non-draft PR — and ci is in CI_REQUIRED_CHECKS. Reproduced on a detached worktree at untouched origin/main: identical failure. main itself is red on this gate right now.

How main went red while CI is green

This is the part worth keeping, and it is not a criticism of CI.

CI grades a SHA once, on its PR. main then advances only by fast-forward to that exact SHA, and there is deliberately no push-to-main trigger — ci.yml says re-running would be "pure waste" and AGENTS.md forbids it. That reasoning is airtight for a gate whose verdict is a property of the commit.

It is false for one whose verdict is a property of the world. cargo deny check advisories reads live yank data. chacha20 0.10.1 was not yanked when these SHAs were graded; it is now. Nothing re-asks — the only ci.yml runs on main are four, all from 2026-08-05, and of the 15 scheduled workflows in this repository, none runs cargo-deny.

So "main is green" is a statement about the past. CI's scheduling is correct; the gate's placement is what is wrong.

Third instance of one class

gate world-fact that moved outcome
lock-check mise tool releases already fixed — split into lock-complete (commit bytes) plus lock-currency.yml (a clock)
semver bisync 0.3.x yanked broke every branch; fixed by the gix 0.87 floor bump, now on main
deny chacha20 0.10.1 yanked this PR

.claude/rules/toolchain.md already carries the rule from the first one's post-mortem: "a property of the commit belongs in the gate, a property of the world belongs on a clock." deny's advisory half is on the wrong side of that line.

Splitting it is deliberately not in this PR. It changes what a required check means, and the Definition of Ready wants a deny gate's firing rate replayed over git rev-list origin/main before its severity is chosen. Deciding that inside a lockfile bump would be shipping a retuned required check with its severity picked instead of measured. It neighbours CLOUD-1070, whose exit-3 abstention shape is the same question one layer over.

Why this does NOT close CLOUD-1074 — the gates caught me

I first wrote this PR to close CLOUD-1074, reasoning from its §7 Done clause:

mise run semver completes and reports a verdict — either patch-compatible or a declared break — and mise run deny stays green over the changed dependency closure.

The gix 0.87 bump satisfied the first clause and is on main, so the second looked like this PR's to satisfy. Two gates refused that, and both were right.

mise run claim-check answered CLOUD-1074 not-todo (in In Progress) — assigned, with PR #714 already attached. A row that is claimed and carries a PR cannot be a second PR's closing key; that is claim-not-raced's whole concern, and closing it here would have stranded #714's claim on it.

I then filed CLOUD-1077 for this work but left the body closing CLOUD-1074, and batten-check reported exactly that on the branch:

mise-tasks/claim-race-check.sh claim-not-raced
Cargo.lock filed-over-own-diff

Both had one root cause — the wrong closing key — and both clear with this correction, since filed-over-own-diff exempts a row the PR closes.

CLOUD-1074 stays open and is cited rather than served. Different crate, different gate, different work.

Verification

  • mise exec -- cargo deny check advisories — exit 1 before, exit 0 after
  • mise run denyadvisories ok, bans ok, licenses ok, sources ok
  • one package relocked, chacha20 v0.10.1 -> v0.10.2; git diff --stat is Cargo.lock | 4 ++--

@linear-code

linear-code Bot commented Aug 27, 2026

Copy link
Copy Markdown
CLOUD-1074 `bisync` 0.3.0 and 0.3.1 are both yanked, so `cargo update` cannot resolve `gix-protocol`'s `^0.3.0` — `semver` exits 101 on every branch and CI is red repo-wide

mise run semver cannot complete. It is not a verdict about any branch's API delta — the comparison never runs.

What was measured

cargo-semver-checks builds a scratch crate that depends on crates/batten by path and runs cargo update in it, deliberately ignoring the committed lockfile. That resolution now fails:

error: failed to select a version for the requirement `bisync = "^0.3.0"`
  version 0.3.0 is yanked
  version 0.3.1 is yanked
location searched: crates.io index
required by package `gix-protocol v0.64.0`
    ... which satisfies dependency `gix-protocol = "^0.64.0"` (locked to 0.64.0) of package `gix v0.86.0`
    ... which satisfies dependency `gix = "^0.86"` (locked to 0.86.0) of package `batten`

Every published bisync 0.3.x is yanked, so the requirement has no satisfying version at all — this is not a "pick a newer patch" case.

Reproduced on the committed tree with mise exec -- cargo update --dry-run -p bisync, which fails identically. Cargo.lock pins bisync 0.3.0 (checksum 5020822f…), and a yank does not break an existing lockfile — which is why the ordinary build, test:cargo and batten check are all unaffected. Only the paths that re-resolve break.

Why it is repo-wide rather than one branch's

semver.sh correctly refuses: exit 101 is neither of cargo-semver-checks' two verdicts, so reporting it as a pass would be the false green that task exists to prevent. But the consequences are wide:

  • semver is in CI_REQUIRED_CHECKS, so CI is red on every branch whose diff touches crate source.
  • verify depends on semver (mise.toml), and land runs verify per lap, so nothing can land locally either.
  • A cached target/semver-checks scratch crate hides it until the version string changes. v0.0.120's release bumped it, so every branch rebasing past e908debf now builds a fresh scratch crate and hits this.

It first surfaced on claude/batten-hook-policy-enforcement-vnvhch, where it is demonstrably not the branch's: that diff touches no Cargo.toml, Cargo.lock or deny.toml, and the scratch crate's only input from us is crates/batten/Cargo.toml, byte-identical on main.

Refinement — Ready

  • Source of truth (§1). The crates.io index, read through cargo's own resolver — not the crates.io HTTP API, which the agent proxy answers 403 for. mise exec -- cargo update --dry-run -p bisync is the one-command reproduction and the one-command confirmation of a fix.
  • **Mechanism (§3). **gix 0.87.1 is published. The candidate fix is bumping the workspace pin gix = { version = "0.86", … } to "0.87" in the root Cargo.toml, probably with gix-diff = "0.66" alongside since its version tracks gix, then cargo update. Whether that is two lines or an API migration is the open question — gix is pre-1.0, so a minor bump may break call sites, and git.rs uses it heavily (the in-process patch identity, ref and object reads, status, git::landing). Establish which before estimating.
  • Not a fix (§3). Vendoring, patching, or a [patch.crates-io] entry pointing at a git rev. Those keep an un-resolvable requirement in the graph and would have to be unwound; the upstream release is the remedy.
  • **Done (§7). **mise run semver completes and reports a verdict — either patch-compatible or a declared break — and mise run deny stays green over the changed dependency closure. Not "semver was made to pass": the vacuous-run refusal and the neither-verdict refusal both stay exactly as they are, because both are what caught this.
  • Scope boundary (§7). Dependency currency is lock-currency.yml's on a schedule and Renovate's, by AGENTS.md's "a property of the commit belongs in the gate, a property of the world belongs on a clock". This row exists because a yank is not currency drift — it removes the only satisfying version, so the scheduled lane cannot route around it and a human bump is required.

What is deliberately not proposed

Suppressing or skipping semver to unblock landing. It is the one gate that would then be off at exactly the moment the API surface is changing, and this branch's own diff is feat!-marked in three commits.

CLOUD-1077 `chacha20` 0.10.1 is yanked, so `deny` refuses every branch — and `main` is red on a gate CI never re-asks

Why

mise run deny fails advisories on the committed lockfile, on every branch and on main itself:

error[yanked]: detected yanked crate (try `cargo update -p chacha20`)
chacha20 0.10.1 registry+https://github.com/rust-lang/crates.io-index

chacha20 0.10.0 and 0.10.1 are both yanked; 0.10.2 is published and is not. It arrives transitively via rand, and 0.10.2 satisfies the existing requirement — so the fix is one relocked package, Cargo.lock only, no manifest change and no API surface.

**It blocks every branch. **.github/workflows/ci.yml carries no paths: filter and [tasks.ci] is depends = ["hooks", "deny"], so deny runs in the ci job on every non-draft PR, and ci is in CI_REQUIRED_CHECKS. Reproduced on a detached worktree at untouched origin/main: identical failure, advisories FAILED.

How main went red while CI is green, which is the part worth keeping

CI grades a SHA once, on its PR. main then advances only by fast-forward to that exact SHA, and there is deliberately no push-to-main trigger — ci.yml says re-running would be "pure waste" and AGENTS.md forbids it. That is airtight for a gate whose verdict is a property of the COMMIT, and false for one whose verdict is a property of the WORLD.

cargo deny check advisories reads live yank data. chacha20 0.10.1 was not yanked when these SHAs were graded; it is now, and nothing re-asks. Measured: the only ci.yml runs on main are four, all from 2026-08-05, and of the 15 scheduled workflows in this repository none runs cargo-deny. So "main is green" is a statement about the past.

Not a duplicate of CLOUD-1074, and the board is what settled it

CLOUD-1074 is the bisync yank breaking semver, fixed by the gix 0.87 floor bump now on main. Its §7 Done clause reads "and mise run deny stays green over the changed dependency closure", so serving it with this fix was arguable from the text — and mise run claim-check refused: CLOUD-1074 not-todo (in In Progress), assigned, with a PR already attached. A row that is claimed and carries a PR cannot be a second PR's closing key, which is claim-not-raced's whole concern. Different crate, different gate, different work.

Refinement — Ready

Refinement gate: Definition of Ready & Done. This body carries only specializations.

  • Source of truth (§1). The committed Cargo.lock, judged by cargo-deny against the live advisory data. mise run deny is the one-command reproduction and the one-command confirmation of a fix.
  • **Computable predicate (§2). **mise run deny exits 0 with advisories ok, bans ok, licenses ok, sources ok. No new gate and no tuning: the refusal is correct and is what caught this.
  • **Effect (§3). **cargo update -p chacha20 — one relocked package. No manifest edit, so no MSRV movement and no API surface.
  • Generated artifacts (§4). None. Cargo.lock is committed, not derived.
  • Output & exit (§5). Unchanged.
  • **Commit / bump (§6). **fix → patch until 0.1.0.
  • Test obligation (§7). The gate itself is the test: exit 1 before, exit 0 after, both recorded. There is nothing to unit-test in a lockfile bump, and adding a case asserting a version string would pin the symptom rather than the property.
  • Blockers (§8). None.

Done

mise run deny is green on main, so verify and the ci check stop refusing every branch for a reason no branch caused.

The recurrence is a separate row and is deliberately not this one

This is the third instance of one class: lock-check (mise releases — already fixed, split into lock-complete over committed bytes plus lock-currency.yml on a clock), semver (CLOUD-1074), and this. .claude/rules/toolchain.md carries the rule from the first one's post-mortem: "a property of the commit belongs in the gate, a property of the world belongs on a clock." deny's advisory half is on the wrong side of it and wants the same split — advisory checking against a pinned DB revision in the commit gate, live advisory data on a schedule.

That change is not in this row's scope because it changes what a required check MEANS, and the DoR wants a deny gate's firing rate replayed over git rev-list origin/main before its severity is chosen. Shipping it inside a lockfile bump would pick the severity instead of measuring it.

Review in Linear

@coderabbitai

coderabbitai Bot commented Aug 27, 2026

Copy link
Copy Markdown

Important

Review skipped

Review was skipped due to path filters

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock

CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including **/dist/** will override the default block on the dist directory, by removing the pattern from both the lists.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 2b368fa6-fafa-4f61-b49d-0ee4e0764257

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Comment @coderabbitai help to get the list of available commands.

`mise run deny` fails `advisories` on the committed lockfile:

    error[yanked]: detected yanked crate (try `cargo update -p chacha20`)
    chacha20 0.10.1 registry+https://github.com/rust-lang/crates.io-index

`chacha20` 0.10.0 and 0.10.1 are both yanked; **0.10.2 is published and is not**.
It arrives transitively via `rand`, and 0.10.2 satisfies the existing
requirement, so this is one relocked package, `Cargo.lock` only, no manifest
change and no API surface. After it: `advisories ok, bans ok, licenses ok,
sources ok`.

WHY THIS BLOCKS EVERYONE. `ci.yml` carries no `paths:` filter and `[tasks.ci]` is
`depends = ["hooks", "deny"]`, so `deny` runs in the `ci` job on every non-draft
PR, and `ci` is in `CI_REQUIRED_CHECKS`. Reproduced on a detached worktree at
untouched `origin/main`: identical failure.

HOW `main` WENT RED WITH CI GREEN, because that is the part worth writing down
rather than the bump. CI grades a SHA once, on its PR; `main` then advances only
by fast-forward to that exact SHA, and there is deliberately no push-to-`main`
trigger — `ci.yml` says re-running would be "pure waste" and AGENTS.md forbids it.
That is airtight for a gate whose verdict is a property of the COMMIT. It is false
for one whose verdict is a property of the WORLD: `cargo deny check advisories`
reads live yank data, `chacha20 0.10.1` was not yanked when these SHAs were
graded, and nothing re-asks. The only `ci.yml` runs on `main` are four from
2026-08-05, so "main is green" is a statement about the past.

Third instance of that class: `lock-check` (mise releases, already split into
`lock-complete` plus `lock-currency.yml`), `semver` (CLOUD-1074, `bisync` yanked,
fixed by the `gix` floor bump), and this. `.claude/rules/toolchain.md` carries the
rule from the first one's post-mortem — "a property of the commit belongs in the
gate, a property of the world belongs on a clock" — and `deny`'s advisory half is
on the wrong side of it. Splitting it is filed rather than done here: it changes
what a required check means, and the DoR wants a deny gate's firing rate replayed
before its severity is chosen.

SERVES CLOUD-1077 AND NOT CLOUD-1074, corrected after `claim-not-raced` refused
the first spelling. CLOUD-1074's §7 Done clause reads "and `mise run deny` stays
green over the changed dependency closure", so serving it looked arguable — but
`claim-check` answered `not-todo (in In Progress)`, assigned, with PR #714 already
attached, and a row that is claimed and carries a PR cannot be a second PR's
closing key. That is exactly what `claim-not-raced` then reported on this branch.
Different crate, different gate, different work.

Refs: CLOUD-1077, CLOUD-1074
@wenzowski
wenzowski force-pushed the claude/deny-chacha20-yank branch from 4f96da6 to 441fac8 Compare August 27, 2026 20:05
@sonarqubecloud

Copy link
Copy Markdown

❌ The last analysis has failed.

See analysis details on SonarQube Cloud

@wenzowski
wenzowski marked this pull request as ready for review August 27, 2026 20:38
@wenzowski

Copy link
Copy Markdown
Contributor Author

/fast-forward

@wenzowski
wenzowski merged commit 441fac8 into main Aug 27, 2026
17 of 18 checks passed
@wenzowski
wenzowski deleted the claude/deny-chacha20-yank branch August 27, 2026 21:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant