Skip to content

nothing detects a lock that has stopped agreeing with its registry: mise lock is not asserted idempotent #904

Description

@CarlAllenn

Defect

Nothing detects that mise.lock has stopped agreeing with the registry
that produced it. #899 is the proof by instance: five tools sat pinned to
linux-gnu assets at versions whose registry entries map
linux: unknown-linux-musl with no gnu override, and they sat there
silently until #854 happened to regenerate the lock for an unrelated
reason and moved ten entries nobody had touched.

lint:toolbelt-lock checks every pinned tool has an entry, never
that the entry is what the registry resolves today. audit:lock-arch
(#811) checks one entry's asset against the architecture its key
declares, entry by entry. Neither asks the file-level question, so the
gap between "the lock parses and is complete" and "the lock is what mise
would write now" is unwatched.

Two harms, and the second is the one that bites:

Decided build

Decided 2026-08-24, adopting the shape proposed on #899 and ruled
there.

A new audit:* leg, not an extension of audit:lock-arch. The two
ask different questions: lock-arch is per-entry (does this asset match
the architecture its key declares?), this is whole-file (is this file
what mise would write today?). Folding one into the other obscures both.

The mechanism, deliberately almost trivial: copy the config and lock to a
scratch directory, run mise lock there, diff against the committed
file, red on any difference. It needs no new logic about registries,
libc, platforms or assets, because mise lock already IS the
resolver
— the check only asserts that the committed answer still
matches it. Network-bound, so audit:* by the standing rule and never
ci.

Guard for applicability and skip clean where there is no lock, per the
belt's universal-linter rule. Do the work on a copy: the check must not
leave the tree dirty, and must not be the thing that rewrites the lock.

The accepted cost, recorded as accepted rather than discovered later

This leg will red whenever upstream changes a registry mapping. That
is a recurring, actionable red, not a one-off — and the work it demands
is not just regenerating: it is the #899-style proof that the newly
resolved assets actually run, which for that issue meant executing every
binary inside ubuntu:24.04 on both architectures. The org is taking on
that obligation knowingly. A check nobody acts on decays into noise, and
this one asks for real work each time it fires.

Rejected, recorded: folding it into audit:lock-arch (different
question, see above); a lint:* form (resolution needs the registry,
which is network — it cannot be deterministic); and comparing entries
field-by-field against a re-derived registry answer (that reimplements
the resolver in order to check the resolver).

Canon consequence

Belt-delivered like every other task, so it reaches each repo at its next
pin bump with no repo change, and it walks whatever tree it runs in. It
wants a job in repo-audit.yml for consumers and one in audit.yml for
this repository, which does not call repo-audit.yml — the arrangement
audit:lock-arch already uses, and lint:audit-scheduled will refuse
the task outright until both exist.

Done when

  • the leg exists, is scheduled on both surfaces, and lint:audit-scheduled
    passes
  • it reds on a planted drift — demonstrated by mutating one committed
    entry and watching it fail by name, not merely by passing on a clean
    tree
  • it passes on the canon's own lock as of fix(belt): relock five tools onto their musl assets (#899) #903, which is idempotent
    (measured: a second mise lock produces no diff)
  • it leaves the working tree unchanged, asserted rather than assumed
  • the accepted cost above is restated wherever the task is documented, so
    the next person to meet a red knows the obligation is deliberate

Sequencing

Successor to the undecided half of #899; the entries half shipped as
#903. Independent of the release path; no lab run. Touches
mise/config.toml and both audit workflows, so it takes its own PR and
wants a quiet window rather than a race. Not blocked. Refs #899, #903,
#811, #854, #810.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions