You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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:
the installed bytes stop matching what the registry says they should
be, indefinitely and invisibly;
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 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.
Defect
Nothing detects that
mise.lockhas stopped agreeing with the registrythat produced it. #899 is the proof by instance: five tools sat pinned to
linux-gnuassets at versions whose registry entries maplinux: unknown-linux-muslwith no gnu override, and they sat theresilently until #854 happened to regenerate the lock for an unrelated
reason and moved ten entries nobody had touched.
lint:toolbelt-lockchecks every pinned tool has an entry, neverthat 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:
be, indefinitely and invisibly;
mise lockfor anyunrelated reason inherits the accumulated drift in their own diff and
must notice and strip it. That is not hypothetical — aqua resolves committed macos-arm64 to the x86_64 asset — a belt tool, so every arm64 machine in the org #854's lock diff
nearly shipped with eight unrelated entries, and was caught only
because the author happened to read the whole diff.
Decided build
Decided 2026-08-24, adopting the shape proposed on #899 and ruled
there.
A new
audit:*leg, not an extension ofaudit:lock-arch. The twoask different questions:
lock-archis per-entry (does this asset matchthe 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 lockthere, diff against the committedfile, red on any difference. It needs no new logic about registries,
libc, platforms or assets, because
mise lockalready IS theresolver — the check only asserts that the committed answer still
matches it. Network-bound, so
audit:*by the standing rule and neverci.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.04on both architectures. The org is taking onthat 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(differentquestion, 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.ymlfor consumers and one inaudit.ymlforthis repository, which does not call
repo-audit.yml— the arrangementaudit:lock-archalready uses, andlint:audit-scheduledwill refusethe task outright until both exist.
Done when
lint:audit-scheduledpasses
entry and watching it fail by name, not merely by passing on a clean
tree
(measured: a second
mise lockproduces no diff)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.tomland both audit workflows, so it takes its own PR andwants a quiet window rather than a race. Not blocked. Refs #899, #903,
#811, #854, #810.