Skip to content

CI never passes --locked, so the committed Cargo.lock constrains nothing #841

Description

@hyperpolymath

The gap

#838's acceptance criterion 4 is in two halves:

Cargo.lock is committed and CI builds use --locked, so a future
incompatible bump fails at the lockfile rather than resolving differently in
CI than locally.

PR #840 satisfies the first half — the lockfile is committed and updated. The
second half is not satisfied and was not satisfiable in that PR.

Measured, 2026-09-22, on origin/main

No cargo invocation in any workflow passes --locked. Zero hits:

$ command grep -rn 'cargo \(check\|clippy\|test\|build\|fmt\|doc\)' .github/workflows/*.yml \
    | command grep -c -- '--locked'
0

The unlocked invocations that matter are:

File:line Command
.github/workflows/ci.yml:52 cargo check --workspace --all-targets
.github/workflows/ci.yml:55 cargo clippy --workspace --all-targets -- -D warnings
.github/workflows/ci.yml:95 cargo test --workspace --all-targets
.github/workflows/ci.yml:98 cargo test --workspace --doc
.github/workflows/rust.yml:35 cargo check --workspace
.github/workflows/rust.yml:47 cargo test --workspace
.github/workflows/rust.yml:72 cargo clippy --workspace -- -D warnings
.github/workflows/rust.yml:92,98,119 cargo check/clippy/test --all-targets
.github/workflows/quality.yml:122 cargo doc --workspace --no-deps --document-private-items

Why it matters

Without --locked, cargo is free to re-resolve and silently write a
different Cargo.lock in CI than the one committed. Two consequences:

  1. A dependency change that should fail loudly at the lockfile instead
    resolves to something else in CI, so CI and local disagree about what was
    actually built — and the disagreement is invisible.
  2. The committed lockfile stops being evidence of anything. It is checked in,
    but nothing verifies CI honoured it.

This is the same shape as the recurring guard/consumer mismatch: the lockfile
is the demand, but no CI step consumes it as a constraint.

Acceptance criteria

  1. Every cargo invocation in ci.yml, rust.yml and quality.yml that builds,
    checks, lints, tests or documents workspace code passes --locked.
    (cargo fmt takes no such flag and is out of scope.)
  2. A mutant proves the guard has teeth: on a branch, hand-edit Cargo.lock so
    it disagrees with Cargo.toml (e.g. bump a version string in the lock only)
    and confirm CI fails with cargo's
    the lock file ... needs to be updated but --locked was passed error —
    not a compile error, and not a silent re-resolve. Reverting restores
    green.
  3. The failure is legible: the job that fails names the lockfile in its output,
    so the next person reads "lockfile out of date" rather than a downstream
    symptom.

Blocked on

This needs edits to .github/workflows/*, which requires the workflow OAuth
scope. The current session token carries only
admin:public_key, gist, read:org, repo, so it cannot push workflow changes.
Unblocked by the owner re-authenticating with workflow in the scope set.

Split out of #838 rather than left silently unticked. Related: #840.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions