Context
Surfaced during the pre-PR audit of #130, the toolchain-pin refresh that moved the Go pin from 1.26.5 to 1.27.0. That change moved all four pins the currency report named; the audit found that the one pin no report can name went unexamined at the exact moment a maintainer was reviewing pins.
Problem
mise is the component that verifies every other tool: jdx/mise-action runs mise install --locked, checking each downloaded tool against the checksums recorded in mise.lock. Its own version is the one part of the toolchain the repository does not govern, in two independent ways.
In CI it is pinned by hand and covered by nothing. Dependabot's github-actions ecosystem updates the jdx/mise-action@v4 tag but never reads an action's version: input. mise outdated reads mise.toml, where mise does not appear — so the Toolchain currency workflow's own coverage guard, which compares mise ls --local against mise outdated --bump --local, has mise in neither its numerator nor its denominator. The guard is sound; mise is simply outside the set it can reason about.
Locally it is not pinned at all. mise.toml has no mise entry, so whichever mise a contributor happens to have installed is the one that writes mise.lock — the file CI then verifies against.
.claude/rules/toolchain-ci-parity.md records the first of these accurately and assigns it a manual review posture, which SECURITY.md repeats. The gap is that the posture has no trigger. The occasion on which anyone reviews toolchain pins is a currency-report-driven refresh, and that report is structurally incapable of naming mise. So "it moves only when a human moves it" degenerates in practice to "it does not move" — the verifier ages indefinitely while everything it verifies is kept current by the very process that cannot see it.
The second half is recorded nowhere, and is the sharper of the two: the writer of mise.lock and its verifier are different, mutually unconstrained versions of the same tool. A lockfile-format change between them is a CI failure with no local reproduction — or, worse, a silent difference in what "verified" means on each side.
Evidence
Suggested approaches
Options, not a prescription:
- Bring mise inside the report it currently sits outside — a
mise entry in mise.toml would put it in mise outdated --bump --local and in the coverage guard's count. Whether a mise-managed mise is the right way to run it in CI is the open part.
- Give the manual posture a trigger instead — make the mise pin an explicit item of any pin-refresh change, so the occasion that already exists carries it rather than a new mechanism.
- Constrain the writer as well as the verifier — pin a local mise, or assert at
mise lock time that the writing version matches the CI pin, so the lockfile is produced and checked by the same version.
Context
Surfaced during the pre-PR audit of #130, the toolchain-pin refresh that moved the Go pin from 1.26.5 to 1.27.0. That change moved all four pins the currency report named; the audit found that the one pin no report can name went unexamined at the exact moment a maintainer was reviewing pins.
Problem
mise is the component that verifies every other tool:
jdx/mise-actionrunsmise install --locked, checking each downloaded tool against the checksums recorded inmise.lock. Its own version is the one part of the toolchain the repository does not govern, in two independent ways.In CI it is pinned by hand and covered by nothing. Dependabot's
github-actionsecosystem updates thejdx/mise-action@v4tag but never reads an action'sversion:input.mise outdatedreadsmise.toml, where mise does not appear — so the Toolchain currency workflow's own coverage guard, which comparesmise ls --localagainstmise outdated --bump --local, has mise in neither its numerator nor its denominator. The guard is sound; mise is simply outside the set it can reason about.Locally it is not pinned at all.
mise.tomlhas nomiseentry, so whichever mise a contributor happens to have installed is the one that writesmise.lock— the file CI then verifies against..claude/rules/toolchain-ci-parity.mdrecords the first of these accurately and assigns it a manual review posture, whichSECURITY.mdrepeats. The gap is that the posture has no trigger. The occasion on which anyone reviews toolchain pins is a currency-report-driven refresh, and that report is structurally incapable of naming mise. So "it moves only when a human moves it" degenerates in practice to "it does not move" — the verifier ages indefinitely while everything it verifies is kept current by the very process that cannot see it.The second half is recorded nowhere, and is the sharper of the two: the writer of
mise.lockand its verifier are different, mutually unconstrained versions of the same tool. A lockfile-format change between them is a CI failure with no local reproduction — or, worse, a silent difference in what "verified" means on each side.Evidence
.github/workflows/ci.ymland.github/workflows/toolchain.ymlboth pinversion: 2026.8.3; the latest release at time of filing is2026.8.14.mise.lockis2026.7.7— older than the CI pin that verifies it.2026.8.11shipped "versioned lockfiles," so a format-affecting change already sits inside the current gap.mise.tomlcontains nomiseentry..claude/rules/toolchain-ci-parity.md§ "The mise version is one atomic value across both workflows": "Dependabot'sgithub-actionsecosystem updates thejdx/mise-action@v4tag but never reads the action'sversion:input;mise outdatedreadsmise.toml, where mise itself does not appear. Nothing reports it — it moves only when a human moves it."Suggested approaches
Options, not a prescription:
miseentry inmise.tomlwould put it inmise outdated --bump --localand in the coverage guard's count. Whether a mise-managed mise is the right way to run it in CI is the open part.mise locktime that the writing version matches the CI pin, so the lockfile is produced and checked by the same version.