Skip to content

feat(kernel): gate the managed Node.js runtime behind runtime-node - #5397

Merged
senamakel merged 10 commits into
tinyhumansai:mainfrom
senamakel:runtime-node-gate
Aug 9, 2026
Merged

feat(kernel): gate the managed Node.js runtime behind runtime-node#5397
senamakel merged 10 commits into
tinyhumansai:mainfrom
senamakel:runtime-node-gate

Conversation

@senamakel

Copy link
Copy Markdown
Member

Summary

  • Adds a default-ON runtime-node feature gating the managed Node.js runtime: runtime::node, the runtime::javascript language slot, node_exec / npm_exec, and the node_runtime harness-init step.
  • First gate in this program to remove a native toolchain build. Floor drops 312/285/6 → 310/283/5xz2 and its static liblzma C build go, and lzma-sys leaves the native list.
  • Facade + stub (forced by ShellTool), with every registration site leaf-gated so the off-state is absence, not a failing stub.
  • 12 files, one new stub module.

Problem

xz2 is declared unconditionally and pulls a static liblzma C build. It is used at exactly one call site — extract_tar_xz in runtime::node::extractor — for unpacking .tar.xz Node distributions.

The stated kernelization target is 222 crate names / 2 native builds (libsqlite3-sys, ring). Six natives stood in the way; nothing in the program had removed one yet.

Solution

Facade + stub, and the reason is not the obvious one. I scoped this wrong twice, so it is worth stating precisely:

  • It is not agent/harness_init. Its node_runtime_step() is an entry in a vec! of bootstrap steps — a registration site, leaf-gateable on its own.
  • The forcing constraint is ShellTool, which holds Option<Arc<NodeBootstrap>> as a struct field for managed-Node PATH injection. tools/impl/system/shell.rs is kernel — always compiled. Delete the module and the shell tool goes with it.

So runtime/node/stub.rs carries the type surface (NodeBootstrap, ResolvedNode, NodeSource, ExecuteToolOutcome, ops::{classify_tool_call, execute_tool}), and every registration site is leaf-gated instead: the javascript controllers in core/all.rs, node_exec/npm_exec in tools/ops.rs, and node_runtime_step. Registration sites want absence — a stub registering a failing controller would make javascript.* a known method that errors, the opposite of the intended unknown-method.

Stub signature drift is this pattern's main hazard, and it bit immediately. Both ops functions were guessed wrong on the first attempt: classify_tool_call returns Result<CommandClass, String> (not Option<String>), and execute_tool returns ExecuteToolOutcome (not Value). Signatures are now read off the real definitions, and the stub's doc comment records the near-miss so the next person copies the caution rather than the guess.

Off-state

  • try_cached / probe_installed return None ⇒ the shell never prepends a managed bin/ dir. Identical to today's node.enabled = false path.
  • tools/ops.rs does not construct a bootstrap at all when the feature is off, keeping that branch dead rather than a silent per-invocation no-op.
  • The flows adapter's classify_tool_call(...).unwrap_or(CommandClass::Network) degrades to the most restrictive classification — never a wider one.

Scope note: tar and zip are NOT shed

tar is shared with inference (install_piper) and runtime::python; zip with inference and the document tools. The feature list is ["dep:xz2"] and that is deliberate — please do not "fix" it by adding them.

Submission Checklist

Impact

Runtime/platform: none for the shipped app — default-ON and forwarded, verified by check-feature-forwarding.mjs. Slim builds lose managed Node: javascript.* becomes unknown-method, node_exec/npm_exec are absent from the tool list, and the harness-init step disappears.

Compatibility: runtime::node's public types keep their signatures in both builds (that is the stub's job), so callers compile unchanged.

Performance: removes a static liblzma C build from slim builds — the first native toolchain build this program has eliminated.

Security: none. The flows classification path degrades restrictively, not permissively.

Related

  • Closes:
  • Follow-up PR(s)/TODOs:
    • Remaining natives toward 2: libgit2-sys (blocked on the cross-repo memory-git work — vendor/tinycortex must carve its inert memory::diff types out from behind git-diff first), libz-sys, aws-lc-sys (needs a rustls-provider decision).
    • all_tools_executes_*_family_against_fake_backend remain flaky under parallel execution (shared fake-backend port); they pass with --test-threads=1. Pre-existing, untouched here.
    • Integration tests still do not compile gates-off (CI: run the full gates-off test suite (blocked on task_local stack overflow) #5021) — tests/*.rs name build_core_http_router, which is behind http-server. This is why the gates-off verification below is --lib-scoped, matching CI.

AI Authored PR Metadata (required for Codex/Linear PRs)

Linear Issue

  • Key: N/A
  • URL: N/A

Commit & Branch

  • Branch: runtime-node-gate
  • Commit SHA: a0d5d3f91

Validation Run

  • pnpm --filter openhuman-app format:checkcargo fmt --check clean. Prettier half N/A: no frontend files changed.
  • pnpm typecheckN/A: no TypeScript changed.
  • Focused tests: core:: 685 ON / 565 OFF; openhuman::tools:: 867 ON / 767 OFF (--test-threads=1). Both-ways tests confirmed to run the correct half in each direction.
  • Rust fmt/check: cargo check --all-targets clean; gates-off --lib clean; cargo clippy -p openhuman -- -D warnings (CI's exact command) clean in both configurations.
  • Tauri fmt/check: cargo check --manifest-path app/src-tauri/Cargo.toml clean.
  • Shed proof: scripts/assert-shed.sh tokenjuice-treesitter xz2 lzma-sys → both shed. check-kernel-floor.sh passes at the lowered 310/283/5.
  • Combination check: --features "tokenjuice-treesitter,skills,flows" with runtime-node OFF compiles — both families call into runtime::node behind their own gates, so this is the combination most likely to break.

Validation Blocked

  • command: cargo clippy -p openhuman --all-targets -- -D warnings
  • error: 803 lints in test code
  • impact: Not a regression and not CI-gating. Clean origin/main fails the same command with 451. CI runs cargo clippy -p openhuman -- -D warnings (no --all-targets), which is clean here. Flagged rather than silently dropped; the pre-existing test-code lint debt is worth its own cleanup.

Behavior Changes

  • Intended behavior change: none in any shipped configuration. Slim builds lose the managed Node runtime.
  • User-visible effect: none — default-ON and forwarded to the desktop shell.

Parity Contract

  • Legacy behavior preserved: with the feature on, every module, controller, tool and harness step is exactly as before — the gate only adds cfg predicates. core:: counts move 684 → 685 and 564 → 565 solely from the new both-ways tests.
  • Guard/fallback/dispatch parity checks: both-ways controller tests, tool-absence test, check-feature-forwarding.mjs, and the lowered kernel-floor ratchet.

Duplicate / Superseded PR Handling

  • Duplicate PR(s): none
  • Canonical PR: this one
  • Resolution: N/A

First gate in this program to remove a NATIVE toolchain build. The floor drops
312/285/6 -> 310/283/5: `xz2` and its static liblzma C build are exclusive to
`runtime::node::extractor`'s `.tar.xz` path, and `lzma-sys` leaves the native
list entirely. Remaining natives: aws-lc-sys, libgit2-sys, libsqlite3-sys,
libz-sys, ring — the 222-names/2-native target keeps the last two.
`kernel-floor.limits` is lowered accordingly, which is the point of the ratchet.

FACADE + STUB, not a leaf gate, and the reason is worth recording because I got
it wrong twice while scoping. It is NOT `agent/harness_init`: its
`node_runtime_step()` is an entry in a `vec!` of bootstrap steps, i.e. a
registration site, which is leaf-gateable on its own. The forcing constraint is
`ShellTool`, which holds `Option<Arc<NodeBootstrap>>` as a struct FIELD for
managed-Node PATH injection — and `tools/impl/system/shell.rs` is kernel, always
compiled. Delete the module and the shell tool goes with it.

So `runtime/node/stub.rs` carries the type surface (`NodeBootstrap`,
`ResolvedNode`, `NodeSource`, `ExecuteToolOutcome`, `ops::{classify_tool_call,
execute_tool}`) while every registration site is leaf-gated: the `javascript`
controllers in `core/all.rs`, `node_exec`/`npm_exec` in `tools/ops.rs`, and
`node_runtime_step`. Registration sites want absence — a stub that registered a
failing controller would make `javascript.*` a KNOWN method that errors, the
opposite of the intended unknown-method.

Stub signature drift is the pattern's main hazard and it bit immediately: both
`ops` functions were guessed wrong on the first attempt (`classify_tool_call`
returns `Result<CommandClass, String>`, not `Option<String>`; `execute_tool`
returns `ExecuteToolOutcome`, not `Value`). Signatures are now taken from the
real definitions, and the stub's doc comment says so.

Off-state: `try_cached`/`probe_installed` return `None`, so the shell simply
never prepends a managed bin dir — identical to today's `node.enabled = false`
path — and `tools/ops.rs` does not construct a bootstrap at all, keeping that
branch dead rather than a silent per-invocation no-op. The flows adapter's
`classify_tool_call(...).unwrap_or(CommandClass::Network)` degrades to the most
restrictive classification, never a wider one.

`tar` and `zip` are NOT shed — `tar` is shared with `inference` (install_piper)
and `runtime::python`, `zip` with `inference` and the document tools. Do not
"fix" the feature list by adding them.

Retrofits two existing tests that hard-asserted `node_exec` registration:
`all_tools_default_registry_contains_expected_baseline_surface` now extends its
baseline conditionally (same shape as the `channels` conditional beside it), and
`all_tools_registers_node_exec_when_node_enabled` is `#[cfg]`-gated. Adds
both-ways tests: `javascript_controllers_{registered_when_feature_on,
absent_when_feature_off}` and `default_tools_omits_node_tools_when_runtime_node_off`.

Verified: default and gates-off lib builds clean; `--all-targets` clean;
Tauri shell clean; `cargo clippy -p openhuman -- -D warnings` (CI's command)
clean in both configurations; fmt clean. core:: 685 ON / 565 OFF; tools 867 ON /
767 OFF (serial). Combination check `--features "tokenjuice-treesitter,skills,
flows"` with runtime-node OFF compiles — both families call into
`runtime::node` behind their own gates. Shed proven with
`scripts/assert-shed.sh tokenjuice-treesitter xz2 lzma-sys`.

Co-authored-by: Medulla <medulla@tinyhumans.ai>
@senamakel
senamakel requested a review from a team August 4, 2026 13:47

@greptile-apps greptile-apps Bot 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.

senamakel has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 1 minute

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 709bf77b-0ab1-4865-a635-1b41e464c820

📥 Commits

Reviewing files that changed from the base of the PR and between afdf3aa and 4d7a817.

📒 Files selected for processing (15)
  • .github/workflows/ci-lite.yml
  • AGENTS.md
  • Cargo.toml
  • app/src-tauri/Cargo.toml
  • app/src-tauri/vendor/tauri-cef
  • app/src-tauri/vendor/tauri-plugin-notification
  • scripts/kernel-floor.limits
  • src/core/all.rs
  • src/core/all_tests.rs
  • src/openhuman/agent/harness_init/registry.rs
  • src/openhuman/runtime/javascript/mod.rs
  • src/openhuman/runtime/node/mod.rs
  • src/openhuman/runtime/node/stub.rs
  • src/openhuman/tools/ops.rs
  • src/openhuman/tools/ops_tests.rs

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a0d5d3f91d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/openhuman/runtime/node/stub.rs Outdated
Comment thread src/openhuman/agent/harness_init/registry.rs
@senamakel senamakel self-assigned this Aug 8, 2026
senamakel and others added 8 commits August 8, 2026 16:02
Checkpoint of work in progress, touching src/openhuman/runtime/node/mod.rs.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Checkpoint of work in progress, touching src/openhuman/runtime/node/stub.rs.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Checkpoint of work in progress, touching src/openhuman/agent/harness_init/registry.rs.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Checkpoint of work in progress, touching .github/workflows/ci-lite.yml.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Checkpoint of work in progress, touching AGENTS.md.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Checkpoint of work in progress, touching Cargo.toml.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Checkpoint of work in progress, touching src/openhuman/agent/harness_init/registry.rs.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3853fdda0e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Cargo.toml

# Managed Node.js runtime: `runtime::node` (download / verify / extract / install
# a pinned toolchain), the `runtime::javascript` language slot over it,
# `runtime::pool::node`, the `node_exec` / `npm_exec` agent tools, and the

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Gate the Node pool behind runtime-node

In a --no-default-features --features flows kernel build, src/openhuman/runtime/pool/mod.rs still declares pub mod node unconditionally, so embedders retain the public run_inline Node execution API and the embedded pool_worker.js even though this feature's declared scope includes runtime::pool::node. Gate that module and its Node-only tests behind runtime-node so the off build actually removes the promised compile-time surface.

AGENTS.md reference: AGENTS.md:L338-L338

Useful? React with 👍 / 👎.

Comment thread .github/workflows/ci-lite.yml Outdated
# This asserts the calibration still holds. If it fails, every
# projection built on the simulator is suspect until it is fixed.
run: python3 scripts/dep-sim.py --cut-nothing --expect-names 285
run: python3 scripts/dep-sim.py --cut-nothing --expect-names 283

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Add runtime-node to the gate-contract scanner

In the inspected rust-feature-gate-smoke job, the scanner at lines 516–517 hardcodes the recognized feature names but omits runtime-node, while the gates-off test command at line 448 does not run agent::harness_init. This PR already adds a runtime-node-conditional expectation in agent/harness_init/registry.rs, so that test is never executed with the feature off and the allowlist guard cannot detect future tests with the same gap; extend the regex, EXPECTED list, and scoped test filter for this gate.

AGENTS.md reference: AGENTS.md:L391-L391

Useful? React with 👍 / 👎.

coderabbitai[bot]
coderabbitai Bot previously approved these changes Aug 8, 2026
- Cargo.toml: keep upstream default list (tokenjuice-treesitter and
  prediction-markets removed upstream) plus runtime-node.
- AGENTS.md: upstream contacts row + our runtime-node row.
- all_tests.rs / ops_tests.rs: keep upstream memory-subsystem tests and
  re-apply the runtime-node gate assertions on top.
- kernel-floor.limits / ci-lite.yml: remeasured on the merged tree —
  flows 305/282/4 (runtime-node sheds xz2 + lzma-sys from 307/284/5).
@senamakel
senamakel merged commit 8774fe4 into tinyhumansai:main Aug 9, 2026
14 of 21 checks passed

@tinysweeper tinysweeper Bot 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.

Requesting changes: 2 lane(s) blocking, worst finding is high.

Fix or reply to the findings below and push. The next review clears this automatically once they are gone — you should not need to dismiss anything by hand.

$0.1824 · 102,559 in / 5,429 out · 10,752 cached (10%) · minimax/minimax-m3, moonshotai/kimi-k3
critique: $0.0396 · 18,609 in / 480 out · 512 cached (3%) · moonshotai/kimi-k3, minimax/minimax-m3
security: $0.1266 · 42,233 in / 1,377 out · 7,680 cached (18%) · moonshotai/kimi-k3
tests: $0.0030 · 9,722 in / 210 out · 896 cached (9%) · minimax/minimax-m3
commits: $0.0025 · 3,507 in / 1,326 out · 768 cached (22%) · minimax/minimax-m3
description: $0.0045 · 11,749 in / 1,002 out · 768 cached (7%) · minimax/minimax-m3

@@ -0,0 +1 @@
Subproject commit 455b47debb9d1f5d0f1527266e48af1670ad4bc5

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium Drop the unrelated tauri-cef submodule pin

This change adds a new app/src-tauri/vendor/tauri-cef submodule pin with no corresponding .gitmodules / Cargo / code change anywhere in the diff, and it is unrelated to the runtime-node gate. Merging it silently introduces a pinned third-party submodule into the tree; if it was committed accidentally (e.g. an in-progress vendor checkout), it should be dropped from this PR.

critique · uncertain ·

@@ -0,0 +1 @@
Subproject commit 5f619690188c6dbad58ceb170e9a86d61971c334

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium Drop the unrelated tauri-plugin-notification submodule pin

Same problem as the tauri-cef pin: this adds a new app/src-tauri/vendor/tauri-plugin-notification submodule reference with no .gitmodules or other supporting change, unrelated to the runtime-node feature gate. If it is not an intentional part of this change, it should be removed from the PR.

critique · uncertain ·

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.

1 participant