Skip to content

the node the image installs is a floating major (node_22.x at build time), so the image carries a node below the floors its consumers' lockfiles now declare — and the newest published image is one day short of v22.23.4 #8

Open

Description

@aitrawog

Problem

The docs-site image installs the docs repo's lockfile on a node that lockfile now declares unsupported. The chain: layer-docs-site requires @github.com/opencharly/layer-nodejs:v2026.272.0322, and that layer's own recorded measurement of its build image is node 22.22.2:

# layer-nodejs's charly.yml, in the comment that records the allowBuilds key-form
# measurement:
#   MEASURED in the real build image (Fedora 43, node 22.22.2) against pod-dsh's
#   dsh-web profile — opencharly/layer-nodejs#5

The docs repo's lockfile — as of opencharly/docs#187, merged 2026-10-26 — carries a floor of >=22.23.4:

$ python3 -c "import json; pk=json.load(open('package-lock.json'))['packages']; \
    print({n: (pk[n]['version'], pk[n].get('engines')) for n in ('node_modules/@astrojs/mdx','node_modules/@astrojs/prism')})}"
{'node_modules/@astrojs/mdx': ('8.1.1', {'node': '>=22.23.4'}),
 'node_modules/@astrojs/prism': ('4.1.1', {'node': '>=22.23.4'})}

So the moment the umbrella advances the docs pin, the check-docs bed installs that lockfile in this image, on node 22.22.2, and npm warns twice:

npm warn EBADENGINE Unsupported engine { package: '@astrojs/mdx@8.1.1', required: { node: '>=22.23.4' }, current: { node: 'v22.22.2' } }
npm warn EBADENGINE Unsupported engine { package: '@astrojs/prism@4.1.1', required: { node: '>=22.23.4' }, current: { node: 'v22.22.2' } }

The install still succeeds (npm warns; no engine-strict), so the bed is not red — it is noisy, and a floor this far below the requirement is a defect that surfaces later, at an arbitrary future patch.

Mechanism

The layer installs node from NodeSource's floating node_22.x repository at image build time:

- name: nodesource
  url: "https://deb.nodesource.com/node_22.x"

A floating major in an image is a snapshot, not a guarantee: the image carries whatever 22.x was current when it was built. The dates say what that is:

what when node it carries
layer-nodejs v2026.272.0322 (the tag layer-docs-site pins) committed 2026-07-28 22.22.2 (the layer's own measured image)
layer-nodejs v2026.292.2324 (the newest tag) committed 2026-10-19 22.23.3
v22.23.4 — the release the lockfile requires released 2026-10-20 —

Even the newest node layer is one day short of the floor. So a pin bump alone does not reach it: the newest published image predates v22.23.4 by a day.

Missed control

  • The node version in the image is never declared, only floated — so nothing can compare it with a floor, and the image's node drifts with the calendar.
  • The consumer's check is loose: layer-docs-site asserts only that node reports a parseable major (stdout: matches: "v[0-9]+\\."). "Some node 22" passes for "a node below the floor".
  • No install-cleanliness gate: the bed's npm ci warnings are not asserted against.

Blast radius

Every consumer of this layer that installs a lockfile with a higher node floor inherits it — the docs site today, and any future consumer of the node layer. The same floating-major mechanism produced the sibling defect in the docs repo itself (opencharly/docs#188): there the floating major was .node-version; here it is NodeSource's node_22.x.

Root fix (proposal)

Declare the node version the image installs, exactly, and assert it.

  1. A declared version constant in layer-nodejs (the node version the image installs), installed from that exact version rather than from the floating node_22.x repository — the same root fix as the sibling: a floor is only enforceable against a declared version.
  2. A check step that asserts node --version equals the declared version, so a drifted image is RED rather than quiet. The layer already has the shape of such a step; what it lacks is the equality assertion.
  3. Then, in producer-first order: a new layer-nodejs release → layer-docs-site bumps its pin (and its image is rebuilt on the new node) → the umbrella advances the layer-docs-site gitlink → the check-docs bed installs the docs lockfile on a node that satisfies its floor.

Note

This issue is filed from the docs scope (opencharly/docs#188's RCA) and is not claimed by this slug: the fix is a change in this repo, with its own build and its own release. It is open for a taker, and the umbrella's docs-pin leg waits on it only for the warning-free bed, not for the merge itself.

Agent: umbrella-prose-truth

Assisted-by: Claude Code minimax/minimax-m3.1-flash:cloud (analysed on a live system)

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

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions