Skip to content

fix(daemon): forward the caller CLI's PATH per request for bare-name tool spawns - #1231

Closed
zackees wants to merge 2 commits into
mainfrom
fix/1219-request-path
Closed

fix(daemon): forward the caller CLI's PATH per request for bare-name tool spawns#1231
zackees wants to merge 2 commits into
mainfrom
fix/1219-request-path

Conversation

@zackees

@zackees zackees commented Aug 1, 2026

Copy link
Copy Markdown
Member

Closes #1219

What

The daemon resolved bare-name tool spawns (esptool, python/python3/py -3, objcopy) against whichever PATH it inherited at spawn — and a daemon lives up to 12 h, so byte-identical builds resolved different toolchains depending only on which shell started the daemon and when. This forwards the calling CLI's PATH per request and uses it for bare-name resolution at every spawn site the issue enumerated.

How

  • Per-request plumbing: caller_path: Option<String> on the CLI and daemon BuildRequest/DeployRequest (serde-defaulted for backward compatibility, skip-serialized when absent), captured via the same launcher_path logic already used at daemon spawn, and carried into BuildParams next to the existing pio_env per-request forwarder.
  • compute_env PATH-override fix (fbuild-core): the Windows branch merged overlays into a case-sensitive map seeded from the daemon env, so an overlay ("PATH", …) coexisted with an inherited "Path" — and since std's Command env map is case-insensitive last-insert-wins with BTreeMap yielding "PATH" before "Path", the daemon's value silently won. Overlay keys now evict case-insensitive matches first. Without this, the whole feature would be a no-op on Windows.
  • bare_name_path_overlay helper: applies the caller PATH only when the program is a bare name — provisioned absolute tool paths are untouched.
  • Spawn sites wired: esp32 esptool fallback (esp32_linker.rs), esp8266 esptool (esp8266_linker.rs), find_python() probes and the extra-scripts harness spawn (script_runtime.rs), boot-artifacts python/esptool (boot_artifacts.rs), and LinkerBase::objcopy_firmware (threaded through Ch32vLinker/Esp8266Linker; linkers with absolute toolchain paths pass None).
  • Drift visibility: the build handler logs one tracing::info! line (lengths only, not values) when the caller PATH differs from the daemon's.

RED → GREEN

RED captured before the fix (deterministic test seeding a "Path"-cased inherited key):

test subprocess::tests::path_overlay_wins_over_differently_cased_inherited_key ... FAILED
assertion `left == right` failed: exactly one PATH-ish key expected,
got [("PATH", "X-marker"), ("Path", "stale-daemon-path")]

GREEN after: that test plus path_overlay_replaces_inherited_path_case_insensitively and bare_name_path_overlay_applies_only_to_bare_names all pass.

Tests

  • soldr cargo test -p fbuild-core -p fbuild-build-engine -p fbuild-daemon -p fbuild-cli -p fbuild-build-esp -p fbuild-build-mcu — all green (25 test binaries, 0 failures), including new serde back-compat tests (request JSON without caller_path still deserializes).
  • soldr cargo clippy --workspace --all-targets -- -D warnings clean; cargo fmt applied.
  • Re-verified after rebasing onto 3e9dc5cc.

This closes the mechanism behind the #1217 CI outage class: a venv-installed tool is now visible to builds run from that venv, regardless of which shell spawned the daemon.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Improved build, deployment, and script tool discovery using the caller’s PATH.
    • Ensured bare tool names resolve through the caller’s environment while explicitly configured paths remain unchanged.
    • Improved Windows PATH handling to prevent conflicting case variations from overriding expected values.
    • Preserved compatibility with requests that lack caller environment information.
    • Improved firmware cache separation when tool resolution depends on the caller’s PATH.
  • Tests

    • Added coverage for PATH resolution, environment overlays, cache behavior, request compatibility, and Windows executable discovery.

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@zackees, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 50 minutes

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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 98d314bd-daf1-4cf1-89f1-117256359af9

📥 Commits

Reviewing files that changed from the base of the PR and between fa07577 and ba51e5d.

📒 Files selected for processing (44)
  • crates/fbuild-build-arm/src/generic_arm/arm_linker.rs
  • crates/fbuild-build-arm/src/nrf52/nrf52_linker.rs
  • crates/fbuild-build-arm/src/renesas/renesas_linker.rs
  • crates/fbuild-build-arm/src/sam/sam_linker.rs
  • crates/fbuild-build-arm/src/silabs/silabs_linker.rs
  • crates/fbuild-build-arm/src/teensy/teensy_linker.rs
  • crates/fbuild-build-engine/src/build_fingerprint/mod.rs
  • crates/fbuild-build-engine/src/lib.rs
  • crates/fbuild-build-engine/src/linker.rs
  • crates/fbuild-build-engine/src/pipeline/context.rs
  • crates/fbuild-build-engine/src/script_runtime.rs
  • crates/fbuild-build-engine/src/script_runtime_tests.rs
  • crates/fbuild-build-esp/src/esp32/esp32_linker.rs
  • crates/fbuild-build-esp/src/esp32/orchestrator/boot_artifacts.rs
  • crates/fbuild-build-esp/src/esp32/orchestrator/build.rs
  • crates/fbuild-build-esp/src/esp8266/esp8266_linker.rs
  • crates/fbuild-build-esp/src/esp8266/orchestrator.rs
  • crates/fbuild-build-mcu/src/avr/avr_linker.rs
  • crates/fbuild-build-mcu/src/ch32v/ch32v_linker.rs
  • crates/fbuild-build-mcu/src/ch32v/orchestrator.rs
  • crates/fbuild-build/src/compile_many.rs
  • crates/fbuild-build/tests/avr_build.rs
  • crates/fbuild-build/tests/clangd_check_parity.rs
  • crates/fbuild-build/tests/eh_frame_strip_esp32.rs
  • crates/fbuild-build/tests/esp32_build.rs
  • crates/fbuild-build/tests/lite_scons_acceptance.rs
  • crates/fbuild-build/tests/nxplpc_build_flags.rs
  • crates/fbuild-build/tests/nxplpc_core_compile_commands.rs
  • crates/fbuild-build/tests/stm32_acceptance.rs
  • crates/fbuild-build/tests/teensy30_acceptance.rs
  • crates/fbuild-build/tests/teensy_build.rs
  • crates/fbuild-build/tests/teensylc_acceptance.rs
  • crates/fbuild-cli/src/cli/build.rs
  • crates/fbuild-cli/src/cli/clean.rs
  • crates/fbuild-cli/src/cli/deploy.rs
  • crates/fbuild-cli/src/daemon_client.rs
  • crates/fbuild-cli/src/daemon_client/types.rs
  • crates/fbuild-cli/src/mcp/tools.rs
  • crates/fbuild-core/src/subprocess.rs
  • crates/fbuild-daemon/src/handlers/emulator/select.rs
  • crates/fbuild-daemon/src/handlers/emulator/tests_process.rs
  • crates/fbuild-daemon/src/handlers/operations/build.rs
  • crates/fbuild-daemon/src/handlers/operations/deploy.rs
  • crates/fbuild-daemon/src/models.rs
📝 Walkthrough

Walkthrough

The change captures the CLI caller’s PATH, transports it through build and deploy requests, and uses it for bare-name resolution of tools and Python interpreters. Absolute toolchain paths remain unchanged.

Changes

Caller PATH propagation

Layer / File(s) Summary
Request PATH transport
crates/fbuild-cli/..., crates/fbuild-daemon/...
CLI and MCP requests capture PATH. Daemon models and handlers propagate it to BuildParams.
Subprocess PATH overlay
crates/fbuild-core/src/subprocess.rs
Windows overlays replace case-insensitive environment keys. Bare executable names can receive the caller PATH.
Engine tool and Python resolution
crates/fbuild-build-engine/...
Objcopy and extra-script Python execution use the optional caller PATH for bare names.
Platform tool integration
crates/fbuild-build-esp/..., crates/fbuild-build-mcu/..., crates/fbuild-build-arm/...
ESP32, ESP8266, and CH32V pass caller PATH to bare-name tools. Other linkers pass None for configured absolute tools.
Compatibility initialization
crates/fbuild-build/..., crates/fbuild-daemon/...
Internal build paths and tests initialize the new optional field and constructor parameters.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related issues

Possibly related PRs

  • FastLED/fbuild#1016 — Introduces APIs that this change extends with caller PATH propagation.

Sequence Diagram(s)

sequenceDiagram
  participant CLI
  participant Daemon
  participant BuildEngine
  participant ToolProcess
  CLI->>Daemon: send caller_path
  Daemon->>BuildEngine: create BuildParams
  BuildEngine->>ToolProcess: resolve bare tool with caller PATH
  ToolProcess-->>BuildEngine: execute tool
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes forwarding the caller CLI's PATH to the daemon for bare-name tool resolution.
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.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/1219-request-path

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.

coderabbitai[bot]
coderabbitai Bot previously requested changes Aug 1, 2026

@coderabbitai coderabbitai 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.

Actionable comments posted: 3

🧹 Nitpick comments (1)
crates/fbuild-daemon/src/models.rs (1)

688-710: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Test the request serialization boundary.

The build_request_caller_path_round_trips and deploy_request_caller_path_round_trips tests only deserialize JSON. They do not verify that the CLI request types emit caller_path or omit it when it is None.

Add assertions for both Some(...) and None using the client request types. Rename the tests if they remain deserialization-only.

As per path instructions, tests must define the supported specification and cover caller-PATH propagation and backward-compatible request handling.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/fbuild-daemon/src/models.rs` around lines 688 - 710, Extend the
request tests around BuildRequest and DeployRequest to serialize client request
instances and assert caller_path is emitted for Some(...) and omitted for None,
while retaining deserialization coverage for backward-compatible payloads.
Rename the existing round-trip tests if they only validate deserialization, and
cover caller-PATH propagation for both request types.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/fbuild-build-esp/src/esp32/esp32_linker.rs`:
- Around line 480-491: Update the ESP32 BIN cache flow around can_reuse_bin and
BinArtifactCache to account for caller_path or the effective bare-name esptool
executable selected by bare_name_path_overlay. Include a stable executable
identity or caller-PATH hash in the cache key, bump the cache version, and add a
regression test proving different PATH resolutions do not reuse the same
firmware.bin.

In `@crates/fbuild-core/src/subprocess.rs`:
- Around line 1029-1089: Add a subprocess-level test near bare_name_path_overlay
that creates a temporary executable in tempfile::TempDir, invokes it through
run_command by bare name with a caller PATH overlay, and asserts the
executable’s output. In the same test, invoke the executable via its absolute
path and verify the overlay is not applied, testing observable command behavior
rather than only bare_name_path_overlay’s return value.

In `@crates/fbuild-daemon/src/handlers/operations/deploy.rs`:
- Line 255: Propagate BuildParams.caller_path through deployer
selection/construction and the Deployer::deploy API into deploy-time subprocess
execution, including helper spawns that currently pass None for the environment
overlay. Ensure both normal deployment and the skip_build path apply
caller_path, and add coverage verifying the environment is propagated in each
case.

---

Nitpick comments:
In `@crates/fbuild-daemon/src/models.rs`:
- Around line 688-710: Extend the request tests around BuildRequest and
DeployRequest to serialize client request instances and assert caller_path is
emitted for Some(...) and omitted for None, while retaining deserialization
coverage for backward-compatible payloads. Rename the existing round-trip tests
if they only validate deserialization, and cover caller-PATH propagation for
both request types.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 59f28d34-56ea-468c-9006-2ea64fd41b39

📥 Commits

Reviewing files that changed from the base of the PR and between 3e9dc5c and b496983.

📒 Files selected for processing (43)
  • crates/fbuild-build-arm/src/generic_arm/arm_linker.rs
  • crates/fbuild-build-arm/src/nrf52/nrf52_linker.rs
  • crates/fbuild-build-arm/src/renesas/renesas_linker.rs
  • crates/fbuild-build-arm/src/sam/sam_linker.rs
  • crates/fbuild-build-arm/src/silabs/silabs_linker.rs
  • crates/fbuild-build-arm/src/teensy/teensy_linker.rs
  • crates/fbuild-build-engine/src/lib.rs
  • crates/fbuild-build-engine/src/linker.rs
  • crates/fbuild-build-engine/src/pipeline/context.rs
  • crates/fbuild-build-engine/src/script_runtime.rs
  • crates/fbuild-build-engine/src/script_runtime_tests.rs
  • crates/fbuild-build-esp/src/esp32/esp32_linker.rs
  • crates/fbuild-build-esp/src/esp32/orchestrator/boot_artifacts.rs
  • crates/fbuild-build-esp/src/esp32/orchestrator/build.rs
  • crates/fbuild-build-esp/src/esp8266/esp8266_linker.rs
  • crates/fbuild-build-esp/src/esp8266/orchestrator.rs
  • crates/fbuild-build-mcu/src/avr/avr_linker.rs
  • crates/fbuild-build-mcu/src/ch32v/ch32v_linker.rs
  • crates/fbuild-build-mcu/src/ch32v/orchestrator.rs
  • crates/fbuild-build/src/compile_many.rs
  • crates/fbuild-build/tests/avr_build.rs
  • crates/fbuild-build/tests/clangd_check_parity.rs
  • crates/fbuild-build/tests/eh_frame_strip_esp32.rs
  • crates/fbuild-build/tests/esp32_build.rs
  • crates/fbuild-build/tests/lite_scons_acceptance.rs
  • crates/fbuild-build/tests/nxplpc_build_flags.rs
  • crates/fbuild-build/tests/nxplpc_core_compile_commands.rs
  • crates/fbuild-build/tests/stm32_acceptance.rs
  • crates/fbuild-build/tests/teensy30_acceptance.rs
  • crates/fbuild-build/tests/teensy_build.rs
  • crates/fbuild-build/tests/teensylc_acceptance.rs
  • crates/fbuild-cli/src/cli/build.rs
  • crates/fbuild-cli/src/cli/clean.rs
  • crates/fbuild-cli/src/cli/deploy.rs
  • crates/fbuild-cli/src/daemon_client.rs
  • crates/fbuild-cli/src/daemon_client/types.rs
  • crates/fbuild-cli/src/mcp/tools.rs
  • crates/fbuild-core/src/subprocess.rs
  • crates/fbuild-daemon/src/handlers/emulator/select.rs
  • crates/fbuild-daemon/src/handlers/emulator/tests_process.rs
  • crates/fbuild-daemon/src/handlers/operations/build.rs
  • crates/fbuild-daemon/src/handlers/operations/deploy.rs
  • crates/fbuild-daemon/src/models.rs

Comment thread crates/fbuild-build-esp/src/esp32/esp32_linker.rs
Comment thread crates/fbuild-core/src/subprocess.rs
Comment thread crates/fbuild-daemon/src/handlers/operations/deploy.rs
zackees added a commit that referenced this pull request Aug 2, 2026
…): end-to-end bare-name overlay resolution

CodeRabbit follow-ups on #1231:

* `BinArtifactCache` gains a serde-defaulted `esptool_fingerprint`
  field: empty for a provisioned absolute-path esptool (resolution
  cannot drift, so pre-existing cache records stay valid and caching
  behaves exactly as before), a short SHA-256 of the caller's PATH for
  bare-name spawns. Two requests whose caller PATHs differ can resolve
  different esptool binaries, so they must never share a cached
  firmware.bin. The shared BUILD_FINGERPRINT_VERSION constant is left
  untouched — bumping it would invalidate every unrelated cache, and
  the serde default already yields the correct match semantics.
* New subprocess test proves `run_command` actually resolves a bare
  executable from an overlay PATH (staged uniquely named probe in a
  TempDir), that absolute-path spawns work without an overlay, and
  that the bare name fails without the overlay — i.e. resolution came
  from the overlay, not ambient PATH.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
zackees added a commit that referenced this pull request Aug 2, 2026
…): end-to-end bare-name overlay resolution

CodeRabbit follow-ups on #1231:

* `BinArtifactCache` gains a serde-defaulted `esptool_fingerprint`
  field: empty for a provisioned absolute-path esptool (resolution
  cannot drift, so pre-existing cache records stay valid and caching
  behaves exactly as before), a short SHA-256 of the caller's PATH for
  bare-name spawns. Two requests whose caller PATHs differ can resolve
  different esptool binaries, so they must never share a cached
  firmware.bin. The shared BUILD_FINGERPRINT_VERSION constant is left
  untouched — bumping it would invalidate every unrelated cache, and
  the serde default already yields the correct match semantics.
* New subprocess test proves `run_command` actually resolves a bare
  executable from an overlay PATH (staged uniquely named probe in a
  TempDir), that absolute-path spawns work without an overlay, and
  that the bare name fails without the overlay — i.e. resolution came
  from the overlay, not ambient PATH.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@zackees
zackees force-pushed the fix/1219-request-path branch from fe39f44 to fa07577 Compare August 2, 2026 00:50
@zackees

zackees commented Aug 2, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review — both actionable findings are fixed in fe39f44 (BIN-cache esptool fingerprint + end-to-end overlay resolution test) and the deploy-side threading is tracked in #1234; branch rebased onto current main.

@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- commit metadata and changed files ---'
git show --no-ext-diff --format=fuller --stat fe39f44c
git show --no-ext-diff --format= --name-only fe39f44c

echo '--- relevant changed code ---'
git show --no-ext-diff --format= --unified=80 fe39f44c -- \
  crates/fbuild-build-esp/src/esp32/esp32_linker.rs \
  crates/fbuild-build-esp/src/esp32/esp32_linker_tests.rs \
  crates/fbuild-core/src/subprocess.rs \
  crates/fbuild-core/src/subprocess_tests.rs || true

echo '--- esptool cache key/fingerprint references ---'
rg -n -C 4 -i 'esptool|fingerprint|cache.*bin|bin.*cache' crates/fbuild-build-esp crates/fbuild-core

echo '--- caller PATH overlay tests and call sites ---'
rg -n -C 4 'bare_name_path_overlay|caller_path|PATH overlay|overlay' \
  crates/fbuild-build-esp crates/fbuild-core crates/fbuild-daemon

Length of output: 1387


🏁 Script executed:

#!/bin/bash
set -euo pipefail

gh issue view 1234 \
  --repo FastLED/fbuild \
  --json number,title,state,body,url,author,assignees \
  --jq '{number, title, state, url, author: .author.login, assignees: [.assignees[].login], body}'

Length of output: 2668


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- current revision and PR base candidates ---'
git status --short --branch
git rev-parse --short HEAD
git branch --all --no-color
git remote -v

echo '--- recent current-branch commits ---'
git log --oneline -n 30

echo '--- changed files relative to likely base refs ---'
for base in origin/main main origin/master master; do
  if git rev-parse --verify --quiet "$base" >/dev/null; then
    echo "### $base"
    merge_base="$(git merge-base HEAD "$base")"
    git diff --name-status "$merge_base"..HEAD
  fi
done

echo '--- candidate source and test files ---'
fd -t f -E target -E .git | rg -i '(esp32.*linker|linker.*esp32|esptool|subprocess).*\.rs$'

Length of output: 8435


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- exact follow-up commit patch ---'
git show --no-ext-diff --format=fuller --unified=100 fa07577 -- \
  crates/fbuild-build-engine/src/build_fingerprint/mod.rs \
  crates/fbuild-build-esp/src/esp32/esp32_linker.rs \
  crates/fbuild-core/src/subprocess.rs

echo '--- structure of relevant files ---'
ast-grep outline crates/fbuild-build-engine/src/build_fingerprint/mod.rs --items all
ast-grep outline crates/fbuild-build-esp/src/esp32/esp32_linker.rs --items all
ast-grep outline crates/fbuild-core/src/subprocess.rs --items all

echo '--- focused symbols and assertions ---'
rg -n -C 8 -i \
  'caller_path|fingerprint|bin.*reuse|reuse.*bin|esptool|bare_name_path_overlay|overlay.*resolv|resolv.*overlay' \
  crates/fbuild-build-engine/src/build_fingerprint/mod.rs \
  crates/fbuild-build-esp/src/esp32/esp32_linker.rs \
  crates/fbuild-core/src/subprocess.rs

Length of output: 125283


@zackees The rebase changed fe39f44c to fa07577. I verified both fixes.

  • The ESP32 BIN cache now fingerprints caller PATH for bare-name esptool. The tests reject reuse across different caller PATH values. They preserve reuse for provisioned absolute esptool paths.
  • The subprocess test verifies actual bare-name resolution through the PATH overlay. It also verifies absolute-path behavior and failure without the overlay.
  • Issue #1234 is open and fully tracks the deploy-side gap, including deploy-time spawns and the skip_build path.

No further actionable findings from this follow-up review.

You are interacting with an AI system.

@zackees
zackees dismissed coderabbitai[bot]’s stale review August 2, 2026 02:02

Both actionable findings were fixed in fe39f44 and confirmed addressed by CodeRabbit in-thread; deploy-side threading tracked in #1234. CI fully green (95/95).

zackees and others added 2 commits August 1, 2026 19:04
…tool spawns

The daemon resolved bare-name tool spawns (esptool, python, objcopy)
against whichever PATH it inherited at spawn time — up to hours stale
when the user's venv or toolchain PATH changed since. Fixes:

* Build/Deploy requests now carry an optional `caller_path` snapshot of
  the calling CLI's PATH (serde-defaulted for backward compatibility;
  older CLIs simply send nothing). The CLI populates it from the same
  case-insensitive PATH lookup used at daemon spawn (`launcher_path`).
* `compute_env` (fbuild-core subprocess) now removes case-variant keys
  before applying an overlay on Windows: an overlay "PATH" previously
  coexisted with the inherited "Path" and lost the last-insert-wins
  race inside the child's env map, silently discarding the override.
* New `bare_name_path_overlay` helper builds a PATH overlay only for
  bare-name programs; absolute/relative tool paths are unaffected.
* The five known bare-name spawn sites now apply the overlay:
  esp32 esptool elf2image, esp8266 esptool elf2image, `find_python`
  probes (and the extra_scripts harness spawn), esp32 boot-artifact
  esptool/python spawns, and `LinkerBase::objcopy_firmware`.
* The daemon build handler logs one line when the caller's PATH
  differs from its own (lengths only, no PATH values dumped).

Closes #1219

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…): end-to-end bare-name overlay resolution

CodeRabbit follow-ups on #1231:

* `BinArtifactCache` gains a serde-defaulted `esptool_fingerprint`
  field: empty for a provisioned absolute-path esptool (resolution
  cannot drift, so pre-existing cache records stay valid and caching
  behaves exactly as before), a short SHA-256 of the caller's PATH for
  bare-name spawns. Two requests whose caller PATHs differ can resolve
  different esptool binaries, so they must never share a cached
  firmware.bin. The shared BUILD_FINGERPRINT_VERSION constant is left
  untouched — bumping it would invalidate every unrelated cache, and
  the serde default already yields the correct match semantics.
* New subprocess test proves `run_command` actually resolves a bare
  executable from an overlay PATH (staged uniquely named probe in a
  TempDir), that absolute-path spawns work without an overlay, and
  that the bare name fails without the overlay — i.e. resolution came
  from the overlay, not ambient PATH.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@zackees
zackees force-pushed the fix/1219-request-path branch from c0cbae9 to ba51e5d Compare August 2, 2026 02:04
@zackees

zackees commented Aug 2, 2026

Copy link
Copy Markdown
Member Author

Superseded by #1236, which merged the same per-request caller-PATH design (including the equivalent case-insensitive PATH overlay fix in compute_env) while this PR was in CI. One piece of this PR is NOT in #1236 and is being re-landed separately: the BIN-artifact-cache esptool fingerprint (CodeRabbit's confirmed cache-soundness finding here — on current main, can_reuse_bin can hand a firmware.bin produced by a differently-resolved esptool to a request with a different caller PATH). Follow-up issue incoming.

@zackees zackees closed this Aug 2, 2026
@zackees
zackees deleted the fix/1219-request-path branch August 2, 2026 02:41
@zackees

zackees commented Aug 2, 2026

Copy link
Copy Markdown
Member Author

Superseded: #1219 was closed by PR #1236 (merged as e85fb71), which landed the per-request caller-PATH threading for the build path with the same request->BuildParams->spawn-site design. This branch now hard-conflicts with main across the same files. The deploy-side scope lives in #1234, which will be implemented against main's merged shapes and will adopt this PR's bare_name_path_overlay idea (absolute-path no-op helper) where it fits. Closing to avoid two divergent implementations of the same issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Triage

Development

Successfully merging this pull request may close these issues.

daemon binds tool resolution to spawn-time PATH for up to 12h — bare-name esptool/python/objcopy spawns resolve nondeterministically

1 participant