Skip to content

fix(ci): pin the Kani version — an upstream release broke every merge - #426

Merged
avrabe merged 1 commit into
mainfrom
fix/pin-kani-version
Sep 16, 2026
Merged

avrabe merged 1 commit into
mainfrom
fix/pin-kani-version

Conversation

@avrabe

@avrabe avrabe commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

Closes #425. Unblocks #424 and every other open PR.

What happened

The required Kani gate went red on main, failing 45 engine legs at once,
and blocked every merge in the repo.

event time
kani-0.68.0 released upstream 2026-09-16T03:43:14Z
our first failing CI run 2026-09-16T03:55:44Z12 minutes later
last passing run 2026-09-15T19:49 (the v1.138.0 tag commit)

model-checking/kani-github-action@v1 has a kani-version input that
defaults to latest. We never set it. So every run installed whatever Kani
had most recently published, and a required gate fell over twelve minutes after
an upstream release.

It is not the proofs — established, not assumed

Three independent lines:

  1. Every failing leg completed in ~11 seconds. Model checking does not
    finish in 11 s.
  2. The only tree change between the last green run and the first red one was
    PR plan(v1.139): the two dark gates, the horizontal hold, and v1.138.0's release notes #406artifacts and release notes, no Rust.
  3. Running the same thing locally on 0.67.0:
crates/relay-lc $ cargo kani
 ** 0 of 75 failed
VERIFICATION:- SUCCESSFUL
Complete - 9 successfully verified harnesses, 0 failures, 9 total.

The fix

Pin kani-version: '0.67.0' — exactly what latest resolved to for the entire
period the gate was green (released 2026-01-16). This restores the known-good
state rather than choosing a new one. Moving to 0.68.0 is a deliberate change
with its own validation.

The lesson, which we have now paid for twice

An unpinned tool in a required gate is an unreviewed dependency on someone
else's release schedule. The same class bit us from the other direction days
ago: a stale, four-month-old local synth binary silently emitted wrong float
code with exit 0, and we nearly reported "floats don't lower" because of it.

Floating means "whatever, whenever" — in both directions.

Worth recording separately: this was visible for hours as "the Kani gate is red"
and read as a proof problem. The 11-second job duration was the tell, and
duration is not something anyone looks at when a verification gate goes red.

🤖 Generated with Claude Code

https://claude.ai/code/session_01HvusAXYbHLyv3uTzfBcMbG

…#425)

The required `Kani gate` went red on main and blocked every merge in the repo.
45 engine legs failing at once.

ROOT CAUSE, with the timeline:

  kani-0.68.0 released   2026-09-16T03:43:14Z
  first failing CI run   2026-09-16T03:55:44Z   <- 12 minutes later
  last passing CI run    2026-09-15T19:49       <- the v1.138.0 tag commit

`model-checking/kani-github-action@v1` has a `kani-version` input that defaults
to `latest`. We never set it, so every run installed whatever Kani had most
recently published. Upstream shipped 0.68.0 and our required gate fell over
twelve minutes later.

IT IS NOT THE PROOFS, established rather than assumed. Every failing leg
completed in about 11 s, which is far too fast for model checking, and the only
change to the tree between the last green run and the first red one was PR #406
— artifacts and release notes, no Rust. Running the same thing locally on 0.67.0
settles it:

  crates/relay-lc $ cargo kani
  ** 0 of 75 failed
  VERIFICATION:- SUCCESSFUL
  Complete - 9 successfully verified harnesses, 0 failures, 9 total.

0.67.0 is exactly what `latest` resolved to for the whole period the gate was
green (it was released 2026-01-16), so this pins the known-good state rather
than picking a new one. Moving to 0.68.0 is a deliberate change with its own
validation — not something an upstream release gets to do to us at 03:43 on a
required gate.

THE GENERAL LESSON, which this repo has now paid for twice. An unpinned tool in
a REQUIRED gate is an unreviewed dependency on someone else's release schedule.
The same class already bit us on the other side: a stale, four-month-old local
synth binary silently emitted wrong float code with exit 0. Floating means
"whatever, whenever", in both directions.

Also worth recording: this was visible for hours as "the Kani gate is red" and
read as a proof problem. The 11-second job duration was the tell, and duration
is not something anyone looks at when a verification gate goes red.

Closes #425.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HvusAXYbHLyv3uTzfBcMbG
@avrabe
avrabe enabled auto-merge (squash) September 16, 2026 13:05
@avrabe
avrabe merged commit 6f032e4 into main Sep 16, 2026
55 checks passed
@avrabe
avrabe deleted the fix/pin-kani-version branch September 16, 2026 13:07
avrabe added a commit that referenced this pull request Sep 16, 2026
…427) (#428)

A required status check that does not RUN does not pass by default. It never
reports, and the PR stays blocked forever.

I promoted `Gazebo SITL (real gz bridge)` to a required context earlier today
with its stated precondition met — five consecutive green runs on main, the wasm
leg executing 6250 ticks and holding 0.01 m, bit-identical to native. It
deadlocked every merge in the repo within minutes. PR #426 sat BLOCKED with ZERO
failing checks, because it touched only `.github/workflows/kani.yml` and the
path filter meant gazebo never triggered. Removing the context returned it to
CLEAN instantly.

The precondition I checked was "is this leg meaningful". The one that decides
whether a context can be required is "does it report on EVERY pr", and those are
different properties.

This removes the `pull_request` path filter so the job always runs. Cost: about
7 minutes of runner time per PR, against the only gate in the repo that flies
the actually-published wasm component through real physics and compares it to
native bit for bit. That is cheap.

NOT PROMOTING IT IN THIS COMMIT. The sequence is: land this, observe the job
reporting on a PR that touches none of the old filter paths, THEN add the
required context. Promoting on the strength of reasoning rather than evidence is
exactly what caused the deadlock, and doing it again in the same day would be
hard to explain.

Worth naming the shape, because this repo now has both halves of it:

  #417 — a job that is SKIPPED but reports `success` verifies nothing while
         looking green.
  #427 — a job that is SKIPPED and reports nothing blocks everything.

Same root: a gate whose workflow does not always run. The only shape safe to
require is one that always runs and always reports.

Refs #427.


Claude-Session: https://claude.ai/code/session_01HvusAXYbHLyv3uTzfBcMbG

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
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.

URGENT: the required Kani gate is RED on main — ~14 engines fail in ~11s each, so nothing can merge

1 participant