Skip to content

feat: versioned Node runtimes — node22/npm22/npx22, generalized cb expose (RM-11) - #30

Merged
AviBackToBlack merged 4 commits into
mainfrom
roadmap/RM-11-node-runtimes
Aug 19, 2026
Merged

feat: versioned Node runtimes — node22/npm22/npx22, generalized cb expose (RM-11)#30
AviBackToBlack merged 4 commits into
mainfrom
roadmap/RM-11-node-runtimes

Conversation

@AviBackToBlack

@AviBackToBlack AviBackToBlack commented Aug 19, 2026

Copy link
Copy Markdown
Owner

Why

A real MCP integration (@whdrnr2583/token-meter via npx) surfaced a gap: one npm package's
native addon (better-sqlite3@^11.5.0) has no compatible prebuild under node:24-slim (no
compiler toolchain in the slim image), while the same package works fine under node:22-slim.
ContainerBin only shipped one Node runtime, so there was no way to select a different major per
tool without hand-editing the registry and re-deriving all the isolation semantics yourself.

Filed as issue #28 / RM-11 in the roadmap (issue #2).

What changed

The registry (internal/registry) already had everything needed for this to be a
configuration-only change:

  • internal/pathmap's volume-ID functions already namespace every stateful volume by
    state_group ("cb-" + group + "-" + name[+hash]), so two profiles with different
    state_group values get fully isolated state — project node_modules, npm cache, npm
    global prefix — even when they reuse identical logical volume names.
  • lockfile.ConfiguredImages already collects distinct t.Image strings generically, so a
    second Node image gets its own lock entry for free.
  • AppendMissingDefaultTools already diffs against the built-in defaults and appends missing
    sections non-destructively, so existing installs pick up the new profiles automatically on
    the next cb setup/cb install.

So this PR is:

  1. Two new built-in profiles: node22/npm22/npx22 on node:22-slim,
    state_group = "node22". The Node 24 profiles (node/npm/npx) are byte-for-byte
    unchanged and remain the default.
  2. cb expose generalized: the one hardcoded != "npm" literal check is now a registry
    tool-name lookup, so cb expose npm22 <binary> becomes possible with no new CLI flag
    the existing positional argument already carries the meaning once the hardcode is removed.
    The generated [tools.NAME] section now correctly inherits the source tool's image,
    state group, shared volumes and env settings — so a binary exposed from npm22 keeps its
    Node 22 identity instead of silently falling back to the default Node 24 state group.
  3. Docs (README.md, docs/architecture.md, docs/security-model.md) updated in the same PR.

Deliberately only two Node lines (24 default, 22 compatibility/LTS alternative) — no
Node 20/21/23, no cb add-runtime command, no implicit/automatic runtime fallback. Runtime
selection stays explicit and inspectable, matching the issue's own non-goals.

internal/pathmap, internal/lockfile resolution, and internal/dockerrun are untouched —
verified during scoping that isolation and locking already work generically; this PR is
registry/CLI configuration only.

Pipeline

Implemented by SWE-1.7 Max, verified (blind, pasted-context) by GLM-5.2 High. Round 1 verify
found one real gap: the non-npm-shaped-source-tool guard clause in cb expose
(e.g. cb expose terraform) was the one daemon-free code path left untested, and the new
%q-formatted error could in principle have rendered as tool "" if Tool.Name weren't
populated from the parser. Fixed directly by the orchestrator with a test
(TestExposeRejectsNonNpmShapedTool) — Tool.Name is in fact populated correctly, this only
needed test coverage, not a code change.

Independently re-validated (not just the implementer's report) inside golang:1.24:

gofmt -l . ; go vet ./... && go test -race ./...

gofmt silent, vet clean, all packages pass including the three new/extended test files.

Validation

MSYS_NO_PATHCONV=1 docker run --rm -v "D:\Work\GIT\container-bin:/src" -w /src -e GOFLAGS=-buildvcs=false golang:1.24 sh -c "gofmt -l . ; go vet ./... && go test -race ./..."

🤖 Generated with a multi-agent pipeline (SWE-1.7 Max implementer, GLM-5.2 High blind verifier, orchestrated by Claude Code)


Open in Devin Review

AviBackToBlack and others added 2 commits August 19, 2026 21:43
… cb expose (RM-11)

- Add built-in `node22`, `npm22`, `npx22` profiles using `node:22-slim` and
  the `node22` state group, keeping the same logical volume names as the
  existing `node24` family so state isolation is provided by `state_group`.
- Generalize `cb expose` to accept any npm-shaped stateful profile already
  in the registry (e.g. `npm` or `npm22`); exposed tools inherit the source
  profile's image, state group and env/volume configuration.
- Update tests: registry count (16), node22 profile assertions, volume-name
  identity, `AppendMissingDefaultTools` pre-RM-11 upgrade, `Expose` guard
  clauses, and lockfile distinct `node:22-slim` / `node:24-slim` entries.
- Update README, docs/security-model.md and docs/architecture.md.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…ing)

GLM-5.2's round-1 verify flagged that cb expose terraform (a registry tool
that exists but has no npm-global shared volume) was the one daemon-free
guard clause left untested, and that the new %q-formatted error could in
principle render as tool "" if Tool.Name weren't populated. Pins both.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 4 potential issues.

Open in Devin Review

Comment thread internal/registry/registry.go
Comment thread internal/cli/cli.go Outdated
Comment thread internal/cli/cli.go
Comment thread internal/registry/registry.go

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds Node 22 as an isolated compatibility runtime and generalizes npm binary exposure.

Changes:

  • Adds node22, npm22, and npx22 profiles.
  • Preserves source runtime settings for exposed npm binaries.
  • Updates tests and runtime documentation.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
README.md Documents Node 22 and generalized exposure.
main.go Updates CLI help text.
internal/registry/state_group_test.go Tests runtime state isolation.
internal/registry/registry.go Defines Node 22 profiles.
internal/registry/registry_test.go Tests profile configuration.
internal/registry/file_test.go Tests registry upgrades.
internal/lockfile/lockfile_test.go Tests distinct image locking.
internal/cli/cli.go Generalizes npm exposure.
internal/cli/cli_test.go Tests exposure guard paths.
docs/security-model.md Updates exposure guidance.
docs/architecture.md Documents separate lock entries.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread internal/cli/cli.go
Comment thread internal/cli/cli.go
Comment thread internal/cli/cli.go
Comment thread internal/cli/cli.go Outdated
…-11)

- discoverNPMGlobalBins now resolves the discovery image through
  lockfile.RuntimeImageForTool, matching every other execution path.
- Expose rejects non-stateful source tools before any Docker run.
- Shared-volume matching is by container destination (/cb/npm-global)
  rather than hardcoded logical name.
- Existing-tool skip message now includes the existing tool's state_group.
- Extracted renderExposedToolSection and added TestRenderExposedToolSection
  to prove exposed sections inherit the source tool's Node identity.
- README notes that node22/npm22/npx22 are added on upgrade but must be
  locked before use.
- Added a deferred-scoping comment for node22 self-test steps in diag.go.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 new potential issues.

Open in Devin Review

Comment thread internal/cli/cli.go
Comment thread internal/cli/cli.go

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 12 changed files in this pull request and generated no new comments.

Suppressed comments (1)

README.md:204

  • The README says to run cb lock/cb update --all “before using” the new node22 profiles, but a few lines later it documents that “no lockfile → … UNLOCKED mode”. To avoid implying node22 cannot be used without a lockfile, make this sentence conditional on already having a lockfile (i.e., upgrading a previously-locked install).
fully isolating project `node_modules`, the npm cache and the npm global prefix; upgrading an existing installation adds these profiles automatically, but they are not yet locked, so run `cb lock` or `cb update --all` before using them.

… (RM-11 Devin finding)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 new potential issue.

Open in Devin Review

Comment thread internal/cli/cli.go
@AviBackToBlack
AviBackToBlack merged commit 6989cdf into main Aug 19, 2026
8 checks passed
@AviBackToBlack
AviBackToBlack deleted the roadmap/RM-11-node-runtimes branch August 19, 2026 21:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants