Skip to content

fix(embed): keep wrapper node first on PATH - #1188

Merged
jdx merged 2 commits into
mainfrom
agent/fix-wrapper-path-precedence
Jul 30, 2026
Merged

jdx merged 2 commits into
mainfrom
agent/fix-wrapper-path-precedence

Conversation

@jdx

@jdx jdx commented Jul 30, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

  • preserve whether an embedder runtime is a selector or a wrapper when resolving its PATH entry
  • put wrapper shims ahead of dependency and project .bin directories while retaining selector precedence
  • share the ordering logic across lifecycle scripts, run, and shell-mode exec/dlx
  • document and test both PATH-ordering modes

Why

A wrapper runtime could be bypassed when a dependency or project exposed a .bin/node, because every runtime bin directory was placed after local package bins. This silently defeated instrumentation, transpilation, or sandboxing supplied by the embedding host.

This addresses the gap reported in Discussion #1083.

Validation

  • cargo fmt --check
  • cargo clippy --all-targets -- -D warnings
  • cargo test

AI-assisted — Tool: Codex; model: openai/gpt-5; version: unavailable.


Note

Medium Risk
Changes how PATH is built for all script and shell spawns when a runtime is active; behavior shift is intentional for wrappers but could surprise projects that relied on a local node overriding the embedder shim.

Overview
Fixes wrapper embedder runtimes being silently bypassed when a dependency or project exposed a node (or other binary) under node_modules/.bin, which defeated instrumentation/sandbox shims.

PATH ordering is now explicit per runtime kind: EmbedderRuntime::wrapper sets bin_dir_precedes_project_bins so the shim directory is prepended before project-local bins; selectors and resolved version-manager runtimes keep the old rule—.bin first, runtime bin after. Shared helper order_path_entries / path_entries_with_project_bins apply the same rules in lifecycle run_script, aube run, shell-mode exec/dlx, and script settings via set_script_settings_with_path_order.

Docs in docs/embedding/rust.md describe selector vs wrapper PATH behavior; unit tests cover both orderings and scoped script settings propagation.

Reviewed by Cursor Bugbot for commit ad90f9e. Bugbot is set up for automated code reviews on this repo. Configure here.

Summary by CodeRabbit

  • New Features

    • Improved PATH handling for scripts and shell commands, with configurable ordering between the active Node runtime and project-local binaries.
    • Wrapper runtimes now prioritize their Node runtime binaries, while selector runtimes preserve project-local binary precedence.
    • Enhanced support for Node-related shims and node-gyp builds.
  • Documentation

    • Clarified runtime PATH ordering, Node shims, and real_node behavior in embedding documentation.

@coderabbitai

coderabbitai Bot commented Jul 30, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The change adds runtime-specific PATH precedence for wrapper and selector Node runtimes. Scoped script settings now carry this policy, shared PATH helpers centralize ordering, command paths use the helper, and embedding documentation and tests reflect the resulting behavior.

Changes

Runtime PATH precedence

Layer / File(s) Summary
Runtime precedence propagation and PATH helper
crates/aube/src/runtime.rs
Runtime contexts distinguish wrapper and selector precedence, and path_entries_with_project_bins orders runtime and project-local bin directories accordingly.
Scoped script settings and PATH ordering
crates/aube-scripts/src/lib.rs, crates/aube/src/commands/script_settings.rs
Install-scoped settings store the precedence flag, expose an atomic setter, and apply the selected ordering when scripts run.
Command PATH integration and embedding documentation
crates/aube/src/commands/{dlx,exec,run}.rs, docs/embedding/rust.md
Shell and script command paths use the centralized helper, while documentation describes selector and wrapper PATH behavior.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant RuntimeResolver
  participant RuntimeContext
  participant ScriptSettingsState
  participant CommandBuilder
  RuntimeResolver->>RuntimeContext: resolve bin_dir and precedence
  RuntimeContext->>ScriptSettingsState: configure script PATH policy
  ScriptSettingsState->>CommandBuilder: provide ordered project and runtime bins
  CommandBuilder->>CommandBuilder: append inherited PATH entries
Loading

Possibly related PRs

  • jdx/aube#1079: Both changes address embedder-provided runtime directories used in lifecycle-script PATH construction.
  • jdx/aube#1086: Both changes define selector-versus-wrapper runtime PATH ordering semantics.
  • jdx/aube#1141: Both changes modify command execution PATH and shim resolution behavior.

Poem

A rabbit hopped through PATH today,
With wrapper bins that led the way.
Selector bins came after .bin,
Scoped settings tucked the rule within.
“Hop, hop!” said Bunny—“scripts now agree!”

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: fixing embedded runtime PATH ordering so wrapper node stays first.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Comment thread crates/aube-scripts/src/lib.rs Resolved
@greptile-apps

greptile-apps Bot commented Jul 30, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

Preserve wrapper-first PATH ordering without adding a required field to the public script-settings structure.

  • Store the PATH-ordering mode in private scoped state while retaining selector-style behavior for the existing public setter.
  • Apply shared wrapper-versus-selector ordering across lifecycle scripts, run, and shell-mode exec/dlx.
  • Document and test both runtime-ordering modes.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains; the previously reported public struct-literal compatibility issue is resolved because the ordering flag now lives in private state rather than as a required ScriptSettings field.

Important Files Changed

Filename Overview
crates/aube-scripts/src/lib.rs Moves the ordering flag into private scoped state, preserving source compatibility for public ScriptSettings literals while applying the selected order during script execution.
crates/aube/src/runtime.rs Tracks whether an embedder runtime is a wrapper or selector and centralizes placement of its bin directory around project-local bins.
crates/aube/src/commands/script_settings.rs Passes the runtime-derived ordering mode into lifecycle-script settings without changing the public settings structure.
crates/aube/src/commands/run.rs Uses the shared runtime/project-bin ordering logic when constructing script PATH.
crates/aube/src/commands/exec.rs Uses the shared ordering helper for shell-mode local binary execution.
crates/aube/src/commands/dlx.rs Uses the shared ordering helper for shell-mode scratch-package execution.
docs/embedding/rust.md Documents selector-after-project and wrapper-before-project PATH behavior.

Reviews (3): Last reviewed commit: "fix(embed): preserve script settings com..." | Re-trigger Greptile

@jdx
jdx force-pushed the agent/fix-wrapper-path-precedence branch from 4664a79 to af95d76 Compare July 30, 2026 16:05
@github-actions

github-actions Bot commented Jul 30, 2026 •

Copy link
Copy Markdown

Instruction counts

benchmark trend instructions Δ wall (min) Δ
graph ▁▁█████▇▇▇███▇██████▇ 16,715,627 → 16,714,102 -0.01% 3.41 → 3.20ms -6.06%
install ▃▁▆▃▄▆▅▄▄▆▄▄▄▄▇▇▆█▇█▇ 103,184,881 → 103,174,367 -0.01% 26.03 → 26.34ms +1.20%
startup ▁▁▇▇▇█▇▆▇▆▇▇▇▇▆█▇▇▇▇▇ 7,667,668 → 7,666,885 -0.01% 2.47 → 2.36ms -4.51%
tree ▁▁▄▄▄▄▄▄▄▄▄▄▄▇▇██████ 17,801,806 → 17,802,172 +0.00% 4.25 → 3.30ms -22.44%

No instruction-count regression above 1%.

Only instruction counts gate. Wall clock is shown for context — on identical hardware it moves 4-20% run to run.

Measured by tak — instruction-counted CLI benchmarks, stored in this repository's git notes.

ad90f9e27d0c vs 58385562fe49 · measured on this runner, not pushed to the history.

@jdx
jdx force-pushed the agent/fix-wrapper-path-precedence branch from af95d76 to ad90f9e Compare July 30, 2026 16:49
@jdx
jdx merged commit 5011f57 into main Jul 30, 2026
20 checks passed
@jdx
jdx deleted the agent/fix-wrapper-path-precedence branch July 30, 2026 17:14
jdx added a commit that referenced this pull request Jul 30, 2026
## Summary

- add `EmbedderRuntime::without_path()` to preserve the inherited `PATH`
- continue using the configured runtime for direct aube spawns and
`NODE`
- make `path_dir(...)` and `without_path()` follow last-call-wins
builder semantics
- document and test wrapper and selector behavior

## Why

Embedding hosts may need to supply the Node program and npm exec path
without changing how bare `node` commands resolve. Previously, wrapper
runtimes always derived a PATH entry from `node_program.parent()`, so
that configuration was not expressible.

This is a stacked follow-up to #1188 and targets its branch so this PR
contains only the explicit PATH opt-out. It can be retargeted to `main`
after #1188 merges.

## Validation

- `cargo fmt --check`
- `cargo clippy --all-targets -- -D warnings`
- `cargo test`

*AI-assisted — Tool: Codex; model: openai/gpt-5; version: unavailable.*

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Additive embedder builder API with unchanged defaults; only affects
hosts that opt in via `without_path()`.
> 
> **Overview**
> Adds **`EmbedderRuntime::without_path()`** so embedding hosts can keep
the inherited **`PATH`** while still setting **`NODE`**,
**`npm_node_execpath`**, and using the configured program for direct
aube spawns. Wrapper runtimes no longer always prepend the shim’s parent
directory to **`PATH`**.
> 
> **`path_dir(...)`** and **`without_path()`** use **last-call-wins**
builder semantics ( **`path_dir`** clears the opt-out;
**`without_path`** wins if called last). **`selector`** with
**`without_path()`** still resolves **`node_program`** from the bin dir
but does not prepend it to **`PATH`**.
> 
> Docs and unit tests cover wrapper, selector, and builder ordering.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
a36c67a. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
@cursor cursor Bot mentioned this pull request Jul 30, 2026
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