Skip to content

fix(pr-proof): resolve real Cargo executable outside version-manager shims - #1624

Open
kjgbot wants to merge 1 commit into
fix/1602-authoritative-live-workersfrom
fix/pr-proof-cargo-shim
Open

fix(pr-proof): resolve real Cargo executable outside version-manager shims#1624
kjgbot wants to merge 1 commit into
fix/1602-authoritative-live-workersfrom
fix/pr-proof-cargo-shim

Conversation

@kjgbot

@kjgbot kjgbot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Summary

Broadens resolveCargo() in tests/relayflows/cases/1602-parentless-worker-inventory/run.mjs so the PR-proof case runner can find the real toolchain cargo in a Cloud sandbox whose PATH holds only version-manager shims.

Also adds resolve-cargo.test.mjs (node --test) that exercises the failure and success paths.

REVISED HYPOTHESIS

The brief pointed at scripts/pr-proof/run-arm.mjs on origin/main. That file has no Cargo resolution logic — it is a language-agnostic case-runner harness that runs input.manifest.runner.command. The bug lives one layer down, in the case-specific runner introduced on this PR's own branch:

  • File: tests/relayflows/cases/1602-parentless-worker-inventory/run.mjs
  • Branch: fix/1602-authoritative-live-workers (this PR is targeted at that branch, not main, since the buggy code has not merged).

Confirmed by fetching the failed run log for relay#1611 GHA run 33174758939, which surfaces the exact error from that file's resolveCargo():

could not resolve a real Cargo executable outside a version-manager shim
Case runner failed with exit 1; expected-red behavior must be reported as a successful structured observation

The Cloud sandbox exits after ~2.5s at the Cargo lookup, before any Rust compile could run.

Before

resolveCargo() on fix/1602-authoritative-live-workers:

  1. Walks PATH for cargo; accepts if the realpath's basename is still cargo. Rejects rustup's symlink-proxy (basename becomes rustup) but silently accepts hard-linked / scripted shims.
  2. Extracts a rustup home only when a PATH entry ends in /.local/share/mise/shims or /.cargo/bin, then walks <home>/.rustup/toolchains/*/bin/cargo.
  3. Falls back to /usr/local/cargo/bin/cargo and /opt/rust/bin/cargo.
  4. Throws a generic message with no context if all fail.

The Daytona sandbox on this run had neither RUSTUP_HOME nor a PATH entry with the expected suffixes, so step 2 was a no-op and step 3 missed the actual install location — leaving no way to reach any real cargo even when rustup which cargo would have printed one.

After

resolveCargo() on this branch:

  1. Explicit shim detection (isShimPath) rejects any candidate under a /shims/ directory or .volta/bin/, in addition to the existing basename check.
  2. Asks the installed version manager for the toolchain-selected cargo via rustup which cargo, then mise which cargo, then asdf which cargo. This is the authoritative resolution when only shims sit on PATH — it works even if RUSTUP_HOME is unset.
  3. Toolchain-walk homes now also include HOME, CARGO_HOME, RUSTUP_HOME, and any PATH entry ending in /.asdf/shims.
  4. System-install fallbacks extended with /root/.cargo/bin/cargo and /home/daytona/.cargo/bin/cargo.
  5. On failure, the thrown error lists every path considered and why each was rejected, so the next debug pass has ground truth (e.g. attempts: /usr/local/bin/cargo -> /usr/local/bin/rustup (rejected: proxy, not named cargo); rustup: not found on PATH; ...).

The resolver takes an options bag with injectable env, pathEntries, isExecutable, realpath, readdir, runOnce, and extraSystemPaths so tests can drive it hermetically. main() is guarded by the invoked-as-CLI check so importing the module does not execute the probe pipeline.

Test

node --test tests/relayflows/cases/1602-parentless-worker-inventory/resolve-cargo.test.mjs

Five cases, all passing on this branch:

  • isShimPath recognizes rustup / mise / asdf /shims/ and .volta/bin/, and does not misclassify real toolchain / mise-installs / asdf-installs cargos.
  • resolveCargo skips a rustup symlink-proxy on PATH and walks the toolchain directory to find the real cargo.
  • resolveCargo prefers the path printed by rustup which cargo over a shim already on PATH.
  • resolveCargo throws a diagnostic listing when nothing resolves (locks the improved error contract).
  • resolveCargo returns a direct non-shim cargo on PATH when present and does not shell out.

The listing / options-bag surface does not exist on the pre-fix code, so the test file itself is the regression fence — reverting run.mjs breaks the import.

Context

Test plan

  • Reviewer runs node --test tests/relayflows/cases/1602-parentless-worker-inventory/resolve-cargo.test.mjs locally and confirms 5/5 pass.
  • Reviewer re-triggers the RelayFlow PR-proof gate on fix(broker): unify fleet live worker inventory #1611 after this lands on fix/1602-authoritative-live-workers and confirms prove-base gets past the cargo-lookup step (either succeeds or fails downstream with a real toolchain error, not the shim message).

…shims

The 1602-parentless-worker-inventory case runner's resolveCargo() rejects
rustup's symlink-proxy (basename after realpath becomes "rustup") but had
no way to reach the real toolchain cargo in a Cloud sandbox whose PATH
contains only shims. When RUSTUP_HOME is unset and no PATH entry ends in
/.cargo/bin or /.local/share/mise/shims, the toolchain walk gets no home
to work from and the resolver throws.

Broaden the resolver:

- Detect known shim directories (rustup/mise/asdf /shims/, .volta/bin/)
  explicitly and reject them alongside the basename check.
- Ask the version manager for the toolchain-selected cargo path via
  `rustup which cargo`, `mise which cargo`, `asdf which cargo`. This is
  the authoritative resolution when only shims sit on PATH.
- Infer likely rustup homes from HOME, CARGO_HOME, RUSTUP_HOME, and
  asdf shim locations in addition to the existing mise/.cargo hints.
- Extend the hardcoded system-install fallbacks with /root/.cargo/bin/
  and /home/daytona/.cargo/bin/ so a rustup install without any PATH
  hint is still found.
- On failure, include the ordered list of attempted paths so the next
  debug pass has ground truth instead of just a rejection message.

The resolver now takes an options bag with injectable dependencies so
unit tests can drive it with a mocked filesystem and a stubbed version
manager. Main() is guarded by the invoked-as-CLI check so importing the
module for tests does not execute the probe pipeline.

Add resolve-cargo.test.mjs with `node --test` coverage for:
- rustup symlink proxy skipped, real toolchain cargo picked up
- `rustup which cargo` output preferred over a shim on PATH
- shim-only PATH throws with the diagnostic listing
- direct non-shim cargo on PATH short-circuits the fallbacks
- isShimPath heuristics for rustup, mise, asdf, and volta layouts
@coderabbitai

coderabbitai Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 4e95b06e-fe1b-48fd-9893-50634d6d83c3

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

@cubic-dev-ai cubic-dev-ai 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.

2 issues found across 2 files

You’re at about 94% of the monthly reviewed-line limit. You may want to disable incremental reviews to conserve quota. Reviews will continue until that limit is exceeded. If you need help avoiding interruptions, please contact contact@cubic.dev.

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="tests/relayflows/cases/1602-parentless-worker-inventory/resolve-cargo.test.mjs">

<violation number="1" location="tests/relayflows/cases/1602-parentless-worker-inventory/resolve-cargo.test.mjs:99">
P2: The new resolver in run.mjs has several distinct branches that this test file leaves uncovered. Step 2 only mocks `rustup which cargo`; the identical `mise which cargo` and `asdf which cargo` lookups are never exercised. Step 3's home inference from CARGO_HOME, RUSTUP_HOME, and the `/.cargo/bin`/`/.asdf/shims` PATH-suffix is untested (only HOME is exercised), and step 4's extraSystemPaths fallback is not covered at all — every call passes `extraSystemPaths: []`. Since the whole point of this PR is robust resolution across shim managers, add at least one test asserting the extraSystemPaths fallback is hit, and one covering a non-HOME toolchain-home inference, so a regression in those branches cannot slip through.</violation>
</file>

<file name="tests/relayflows/cases/1602-parentless-worker-inventory/run.mjs">

<violation number="1" location="tests/relayflows/cases/1602-parentless-worker-inventory/run.mjs:66">
P2: resolveCargo step 2 probes toolchains by spawning `rustup/mise/asdf which cargo` in defaultRunOnce without a timeout. A hung shim (lock or network wait) leaves the promise unresolved, so resolveCargo never returns, the probe never starts, and the whole case is killed only by the outer cloud timeout. Bound the probe with an AbortSignal timeout so a stuck shim fails fast and falls through to the toolchain walk or diagnostic; also bound the accumulated stdout/stderr (run() already uses boundedAppend for this).</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

@@ -0,0 +1,153 @@
// Unit tests for the Cargo resolver used by the 1602 PR-proof case runner.

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.

P2: The new resolver in run.mjs has several distinct branches that this test file leaves uncovered. Step 2 only mocks rustup which cargo; the identical mise which cargo and asdf which cargo lookups are never exercised. Step 3's home inference from CARGO_HOME, RUSTUP_HOME, and the /.cargo/bin//.asdf/shims PATH-suffix is untested (only HOME is exercised), and step 4's extraSystemPaths fallback is not covered at all — every call passes extraSystemPaths: []. Since the whole point of this PR is robust resolution across shim managers, add at least one test asserting the extraSystemPaths fallback is hit, and one covering a non-HOME toolchain-home inference, so a regression in those branches cannot slip through.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At tests/relayflows/cases/1602-parentless-worker-inventory/resolve-cargo.test.mjs, line 99:

<comment>The new resolver in run.mjs has several distinct branches that this test file leaves uncovered. Step 2 only mocks `rustup which cargo`; the identical `mise which cargo` and `asdf which cargo` lookups are never exercised. Step 3's home inference from CARGO_HOME, RUSTUP_HOME, and the `/.cargo/bin`/`/.asdf/shims` PATH-suffix is untested (only HOME is exercised), and step 4's extraSystemPaths fallback is not covered at all — every call passes `extraSystemPaths: []`. Since the whole point of this PR is robust resolution across shim managers, add at least one test asserting the extraSystemPaths fallback is hit, and one covering a non-HOME toolchain-home inference, so a regression in those branches cannot slip through.</comment>

<file context>
@@ -0,0 +1,153 @@
+      runOnce,
+      extraSystemPaths: [],
+    });
+    assert.equal(resolved, realCargo);
+  } finally {
+    await rm(root, { recursive: true, force: true });
</file context>


async function defaultRunOnce(command, args) {
return new Promise((resolve) => {
const child = spawn(command, args, {

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.

P2: resolveCargo step 2 probes toolchains by spawning rustup/mise/asdf which cargo in defaultRunOnce without a timeout. A hung shim (lock or network wait) leaves the promise unresolved, so resolveCargo never returns, the probe never starts, and the whole case is killed only by the outer cloud timeout. Bound the probe with an AbortSignal timeout so a stuck shim fails fast and falls through to the toolchain walk or diagnostic; also bound the accumulated stdout/stderr (run() already uses boundedAppend for this).

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At tests/relayflows/cases/1602-parentless-worker-inventory/run.mjs, line 66:

<comment>resolveCargo step 2 probes toolchains by spawning `rustup/mise/asdf which cargo` in defaultRunOnce without a timeout. A hung shim (lock or network wait) leaves the promise unresolved, so resolveCargo never returns, the probe never starts, and the whole case is killed only by the outer cloud timeout. Bound the probe with an AbortSignal timeout so a stuck shim fails fast and falls through to the toolchain walk or diagnostic; also bound the accumulated stdout/stderr (run() already uses boundedAppend for this).</comment>

<file context>
@@ -46,39 +47,158 @@ async function isExecutable(filePath) {
+
+async function defaultRunOnce(command, args) {
+  return new Promise((resolve) => {
+    const child = spawn(command, args, {
+      stdio: ['ignore', 'pipe', 'pipe'],
+      env: process.env,
</file context>

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