feat(kernel): gate the managed Node.js runtime behind runtime-node - #5397
Conversation
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>
There was a problem hiding this comment.
senamakel has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in: 1 minute Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the 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 configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (15)
Comment |
There was a problem hiding this comment.
💡 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".
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>
There was a problem hiding this comment.
💡 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".
|
|
||
| # 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 |
There was a problem hiding this comment.
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 👍 / 👎.
| # 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 |
There was a problem hiding this comment.
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 👍 / 👎.
- 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).
There was a problem hiding this comment.
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 | |||
There was a problem hiding this comment.
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.
| @@ -0,0 +1 @@ | |||
| Subproject commit 5f619690188c6dbad58ceb170e9a86d61971c334 | |||
There was a problem hiding this comment.
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.
Summary
runtime-nodefeature gating the managed Node.js runtime:runtime::node, theruntime::javascriptlanguage slot,node_exec/npm_exec, and thenode_runtimeharness-init step.xz2and its static liblzma C build go, andlzma-sysleaves the native list.ShellTool), with every registration site leaf-gated so the off-state is absence, not a failing stub.Problem
xz2is declared unconditionally and pulls a static liblzma C build. It is used at exactly one call site —extract_tar_xzinruntime::node::extractor— for unpacking.tar.xzNode 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:
agent/harness_init. Itsnode_runtime_step()is an entry in avec!of bootstrap steps — a registration site, leaf-gateable on its own.ShellTool, which holdsOption<Arc<NodeBootstrap>>as a struct field for managed-NodePATHinjection.tools/impl/system/shell.rsis kernel — always compiled. Delete the module and the shell tool goes with it.So
runtime/node/stub.rscarries the type surface (NodeBootstrap,ResolvedNode,NodeSource,ExecuteToolOutcome,ops::{classify_tool_call, execute_tool}), and every registration site is leaf-gated instead: thejavascriptcontrollers incore/all.rs,node_exec/npm_execintools/ops.rs, andnode_runtime_step. Registration sites want absence — a stub registering a failing controller would makejavascript.*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
opsfunctions were guessed wrong on the first attempt:classify_tool_callreturnsResult<CommandClass, String>(notOption<String>), andexecute_toolreturnsExecuteToolOutcome(notValue). 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_installedreturnNone⇒ the shell never prepends a managedbin/dir. Identical to today'snode.enabled = falsepath.tools/ops.rsdoes not construct a bootstrap at all when the feature is off, keeping that branch dead rather than a silent per-invocation no-op.classify_tool_call(...).unwrap_or(CommandClass::Network)degrades to the most restrictive classification — never a wider one.Scope note:
tarandzipare NOT shedtaris shared withinference(install_piper) andruntime::python;zipwithinferenceand the document tools. The feature list is["dep:xz2"]and that is deliberate — please do not "fix" it by adding them.Submission Checklist
javascript_controllers_registered_when_feature_on/_absent_when_feature_off(the OFF half is the one that proves the gate removes anything), plusdefault_tools_omits_node_tools_when_runtime_node_offas the tool-absence failure path. Also retrofits two existing tests that hard-assertednode_execregistration —all_tools_default_registry_contains_expected_baseline_surfacenow extends its baseline conditionally (same shape as thechannelsconditional beside it) andall_tools_registers_node_exec_when_node_enabledis#[cfg]-gated. Both were caught by running the gates-off suite, not by inspection.cfgattributes, the stub, and test retrofits; the stub's behaviour is asserted by the OFF-half tests above.N/A: no feature rows added, removed, or renamed.## Related—N/A: no matrix feature IDs affected.N/A: this removes a dependency, adds none.N/A: no release-cut surface touched.runtime-nodeis default-ON and forwarded to the desktop shell, so the shipped app is unchanged.Closes #NNN—N/A: no dedicated tracking issue. Dependency-shed work under the kernelization program (Feature gates for core subsystems — tracking (lightweight harness builds) #4795 → refactor(kernel): collapse 124 flat domains into 31 gate-aligned families #5328 → feat(kernel): realign DomainGroup with the family directories #5332 → fix(kernel): guard the three unenforced DomainGroup consumers #5364/feat(kernel): gate the macOS Contacts cohort behindcontacts#5365).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_execare 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
libgit2-sys(blocked on the cross-repomemory-gitwork —vendor/tinycortexmust carve its inertmemory::difftypes out from behindgit-difffirst),libz-sys,aws-lc-sys(needs a rustls-provider decision).all_tools_executes_*_family_against_fake_backendremain flaky under parallel execution (shared fake-backend port); they pass with--test-threads=1. Pre-existing, untouched here.tests/*.rsnamebuild_core_http_router, which is behindhttp-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
Commit & Branch
runtime-node-gatea0d5d3f91Validation Run
pnpm --filter openhuman-app format:check—cargo fmt --checkclean. Prettier halfN/A: no frontend files changed.pnpm typecheck—N/A: no TypeScript changed.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.cargo check --all-targetsclean; gates-off--libclean;cargo clippy -p openhuman -- -D warnings(CI's exact command) clean in both configurations.cargo check --manifest-path app/src-tauri/Cargo.tomlclean.scripts/assert-shed.sh tokenjuice-treesitter xz2 lzma-sys→ both shed.check-kernel-floor.shpasses at the lowered 310/283/5.--features "tokenjuice-treesitter,skills,flows"withruntime-nodeOFF compiles — both families call intoruntime::nodebehind their own gates, so this is the combination most likely to break.Validation Blocked
command:cargo clippy -p openhuman --all-targets -- -D warningserror:803 lints in test codeimpact:Not a regression and not CI-gating. Cleanorigin/mainfails the same command with 451. CI runscargo 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
Parity Contract
cfgpredicates.core::counts move 684 → 685 and 564 → 565 solely from the new both-ways tests.check-feature-forwarding.mjs, and the lowered kernel-floor ratchet.Duplicate / Superseded PR Handling