Context
Every workflow in .github/workflows/** that installs Node uses
actions/setup-node's node-version-file: .nvmrc — confirmed across ci.yml, audit.yml,
upstream-contract.yml, all four publish-*.yml files, stuck-check-watchdog.yml,
test-timing-refresh.yml, ci-duration-report.yml, mcp-release-watch.yml, mcp-ui-version-sync.yml,
and more — except three, which hardcode a literal patch version instead:
.github/workflows/gittensor-impact.yml:39: node-version: 22.23.1
.github/workflows/ui-preview-deploy.yml:70: node-version: "22.23.1"
.github/workflows/visual-capture-fallback.yml:101: node-version: "22.23.1"
ui-preview-deploy.yml's hardcoded value carries a comment explaining why and claims the same reason
applies to the other two:
Hardcoded, NOT node-version-file: .nvmrc -- this job deliberately has no checkout step (see the
file header), so there is no .nvmrc on the runner to read. Keep this in sync with .nvmrc's pinned
version by hand; same exception already established for gittensor-impact.yml and
visual-capture-fallback.yml.
That reasoning is genuinely true for ui-preview-deploy.yml's own job (its Setup Node step has no
prior Checkout step in that job). It is not true for the other two: both gittensor-impact.yml
(publish job: Checkout → Setup Node) and visual-capture-fallback.yml (Checkout PR head →
Setup Node) run actions/checkout before Setup Node, so .nvmrc is present on the runner in both
cases — there is no structural reason for them to hardcode instead of reading it, unlike
ui-preview-deploy.yml.
Concretely, this means two of the three hardcodes are unnecessary manual-sync burdens with a real drift
risk: scripts/check-engines-nvmrc-sync.ts's own header comment states "CI itself was never at risk"
of a Node-version mismatch specifically because .github/actions/setup-workspace (used by most
workflows) pins node-version-file: .nvmrc — but that guarantee doesn't extend to these three
directly-hardcoded workflows, and nothing catches it if .nvmrc's major is bumped in the future and
these three are forgotten (they were: the comment already admits "keep in sync ... by hand").
Requirements
- In
.github/workflows/gittensor-impact.yml's publish job, replace the hardcoded
node-version: 22.23.1 on the Setup Node step with node-version-file: .nvmrc (the Checkout step
already runs first in that job, so .nvmrc is present).
- In
.github/workflows/visual-capture-fallback.yml, replace the hardcoded node-version: "22.23.1"
on the Setup Node step with node-version-file: .nvmrc (the Checkout PR head step already runs
first in that job).
- Do not change
.github/workflows/ui-preview-deploy.yml — its job genuinely has no checkout step,
so its hardcoded value is structurally necessary and must stay.
- Update
ui-preview-deploy.yml's own comment (the one quoted in Context above) so it no longer cites
gittensor-impact.yml/visual-capture-fallback.yml as sharing its "no checkout step" reasoning,
since after this fix they won't share that reasoning anymore — reword it to state plainly that this
job is the only one in the repo with this exception, and why.
Deliverables
Test Coverage Requirements
This is a .github/workflows/**-only change; neither .github/** nor scripts/** is in Codecov's
coverage.include (codecov.yml), so there is no codecov/patch obligation. Verification is:
npm run actionlint passes on both edited workflow files, and a visual diff confirming the only
functional change is the Setup Node with: block (no other step reordering).
Expected Outcome
Only one workflow in the repo (ui-preview-deploy.yml, which structurally cannot do otherwise) still
hardcodes a Node patch version; the other two follow every other workflow's .nvmrc-derived pattern and
can never silently drift from it again.
Links & Resources
.github/workflows/gittensor-impact.yml (line ~39, publish job)
.github/workflows/visual-capture-fallback.yml (line ~101)
.github/workflows/ui-preview-deploy.yml (line ~70, the comment to reword — this file itself is NOT
changed functionally)
scripts/check-engines-nvmrc-sync.ts (header comment establishing the "CI is never at risk" premise
this issue narrows the exception to actually match)
.nvmrc (the source of truth these workflows should follow)
Context
Every workflow in
.github/workflows/**that installs Node usesactions/setup-node'snode-version-file: .nvmrc— confirmed acrossci.yml,audit.yml,upstream-contract.yml, all fourpublish-*.ymlfiles,stuck-check-watchdog.yml,test-timing-refresh.yml,ci-duration-report.yml,mcp-release-watch.yml,mcp-ui-version-sync.yml,and more — except three, which hardcode a literal patch version instead:
ui-preview-deploy.yml's hardcoded value carries a comment explaining why and claims the same reasonapplies to the other two:
That reasoning is genuinely true for
ui-preview-deploy.yml's own job (itsSetup Nodestep has noprior
Checkoutstep in that job). It is not true for the other two: bothgittensor-impact.yml(
publishjob:Checkout→Setup Node) andvisual-capture-fallback.yml(Checkout PR head→Setup Node) runactions/checkoutbeforeSetup Node, so.nvmrcis present on the runner in bothcases — there is no structural reason for them to hardcode instead of reading it, unlike
ui-preview-deploy.yml.Concretely, this means two of the three hardcodes are unnecessary manual-sync burdens with a real drift
risk:
scripts/check-engines-nvmrc-sync.ts's own header comment states "CI itself was never at risk"of a Node-version mismatch specifically because
.github/actions/setup-workspace(used by mostworkflows) pins
node-version-file: .nvmrc— but that guarantee doesn't extend to these threedirectly-hardcoded workflows, and nothing catches it if
.nvmrc's major is bumped in the future andthese three are forgotten (they were: the comment already admits "keep in sync ... by hand").
Requirements
.github/workflows/gittensor-impact.yml'spublishjob, replace the hardcodednode-version: 22.23.1on theSetup Nodestep withnode-version-file: .nvmrc(theCheckoutstepalready runs first in that job, so
.nvmrcis present)..github/workflows/visual-capture-fallback.yml, replace the hardcodednode-version: "22.23.1"on the
Setup Nodestep withnode-version-file: .nvmrc(theCheckout PR headstep already runsfirst in that job).
.github/workflows/ui-preview-deploy.yml— its job genuinely has no checkout step,so its hardcoded value is structurally necessary and must stay.
ui-preview-deploy.yml's own comment (the one quoted in Context above) so it no longer citesgittensor-impact.yml/visual-capture-fallback.ymlas sharing its "no checkout step" reasoning,since after this fix they won't share that reasoning anymore — reword it to state plainly that this
job is the only one in the repo with this exception, and why.
Deliverables
.github/workflows/gittensor-impact.yml:node-version-file: .nvmrcinstead of a hardcodedversion.
.github/workflows/visual-capture-fallback.yml:node-version-file: .nvmrcinstead of ahardcoded version.
.github/workflows/ui-preview-deploy.yml's comment updated to no longer claim the other twoworkflows share its no-checkout justification.
Test Coverage Requirements
This is a
.github/workflows/**-only change; neither.github/**norscripts/**is in Codecov'scoverage.include(codecov.yml), so there is nocodecov/patchobligation. Verification is:npm run actionlintpasses on both edited workflow files, and a visual diff confirming the onlyfunctional change is the
Setup Nodewith:block (no other step reordering).Expected Outcome
Only one workflow in the repo (
ui-preview-deploy.yml, which structurally cannot do otherwise) stillhardcodes a Node patch version; the other two follow every other workflow's
.nvmrc-derived pattern andcan never silently drift from it again.
Links & Resources
.github/workflows/gittensor-impact.yml(line ~39,publishjob).github/workflows/visual-capture-fallback.yml(line ~101).github/workflows/ui-preview-deploy.yml(line ~70, the comment to reword — this file itself is NOTchanged functionally)
scripts/check-engines-nvmrc-sync.ts(header comment establishing the "CI is never at risk" premisethis issue narrows the exception to actually match)
.nvmrc(the source of truth these workflows should follow)