lab build fails on nested workspaces — cargo CWD should be the bin's workspace_root, not the outer repo_root #270

Closed
opened 2026-05-19 22:52:27 +00:00 by mik-tf · 1 comment
Owner

Symptom

lab build --release --platforms linux-musl-x86_64 --workspace on lhumina_code/hero_rpc fails for two of six discovered targets:

FAILED: hero_recipes_server:release:x86_64-unknown-linux-musl
error: package ID specification `hero_recipes_server` did not match any packages
FAILED: hero_recipes_admin:release:x86_64-unknown-linux-musl
error: package ID specification `hero_recipes_admin` did not match any packages

The other 4 (petstore_server, hero_lifecycle, hero_rpc_generator, petstore_client) build cleanly. This blocks hero_rpc's lab-publish.yaml CI (run #211 + #209) from going green — counts against #269 rollout.

Root cause

lab discovers hero_recipes_server correctly:

  1. find_package_manifests (cargo_discovery.rs:139) walks git_root and finds example/recipe_server/crates/hero_recipes_server/Cargo.toml.
  2. workspace_root_for (cargo_discovery.rs:169) walks upward, finds example/recipe_server/Cargo.toml (which has its own [workspace]), and groups the bin under that workspace root.
  3. cargo metadata against example/recipe_server/Cargo.toml returns the recipe packages.
  4. discover_binaries_in_repo (cargo_discovery.rs:80) emits them as DiscoveredBin.

Then execute (executor.rs:54) launches cargo:

cmd.args(&args)
    .current_dir(repo_root)        // ← always the git root, not the bin's workspace
    .env("CARGO_TARGET_DIR", target_dir);

repo_root is the outer hero_rpc workspace, which excludes example/recipe_server/:

# hero_rpc/Cargo.toml line 25
exclude = ["example/recipe_server"]

So cargo build -p hero_recipes_server from repo_root resolves the OUTER workspace and reports the package doesn't exist — even though lab already located its containing workspace via cargo metadata.

Fix shape

Two options:

(a) Set current_dir to the bin's workspace root

Most minimal: each DiscoveredBin already carries manifest_path. Compute its workspace root (or store it alongside in DiscoveredBin) and pass it as current_dir. The BuildTarget would need a workspace_root: PathBuf field plumbed through BuildTargetMeta (orchestrator.rs:644).

(b) Pass --manifest-path explicitly

Add --manifest-path <workspace_root>/Cargo.toml to the cargo args in build_command (executor.rs:106). Same plumbing requirement.

Both approaches require carrying the workspace root from discovery → BuildTarget → executor. (a) is more idiomatic; (b) leaves cargo's resolved CARGO_TARGET_DIR and the rest of the env alone.

Blast radius

This bug blocks all repos with nested workspaces under git_root. In the D-07 35-set:

  • hero_rpc: confirmed (this report)
  • Other candidates with sub-workspace examples need to be audited

Affected pattern: any repo that uses lab's --workspace mode and has an excluded sub-workspace containing [[bin]] targets.

Verification

Local reproduction:

cd lhumina_code/hero_rpc
lab build --release --platforms linux-musl-x86_64 --workspace
# tail: 4 built, 2 failed (hero_recipes_server, hero_recipes_admin)

After fix: same command should build all 6 targets.

See also

  • #269 catalogue category 9 ('Stale service.toml package references') — diagnoses hero_rpc as a per-repo bug. The diagnosis is wrong. hero_recipes_server and hero_recipes_admin crates EXIST and are valid; their parent workspace just isn't the outer hero_rpc workspace. Updating #269 to redirect this row here.
  • #268 — s127 origin issue (lab-publish rollout).
## Symptom `lab build --release --platforms linux-musl-x86_64 --workspace` on `lhumina_code/hero_rpc` fails for two of six discovered targets: ``` FAILED: hero_recipes_server:release:x86_64-unknown-linux-musl error: package ID specification `hero_recipes_server` did not match any packages FAILED: hero_recipes_admin:release:x86_64-unknown-linux-musl error: package ID specification `hero_recipes_admin` did not match any packages ``` The other 4 (`petstore_server`, `hero_lifecycle`, `hero_rpc_generator`, `petstore_client`) build cleanly. This blocks hero_rpc's lab-publish.yaml CI (run #211 + #209) from going green — counts against [#269](https://forge.ourworld.tf/lhumina_code/hero_skills/issues/269) rollout. ## Root cause lab discovers `hero_recipes_server` correctly: 1. `find_package_manifests` ([cargo_discovery.rs:139](https://forge.ourworld.tf/lhumina_code/hero_skills/src/branch/development/crates/lab/src/builder/cargo_discovery.rs#L139)) walks `git_root` and finds `example/recipe_server/crates/hero_recipes_server/Cargo.toml`. 2. `workspace_root_for` ([cargo_discovery.rs:169](https://forge.ourworld.tf/lhumina_code/hero_skills/src/branch/development/crates/lab/src/builder/cargo_discovery.rs#L169)) walks upward, finds `example/recipe_server/Cargo.toml` (which has its own `[workspace]`), and groups the bin under that workspace root. 3. `cargo metadata` against `example/recipe_server/Cargo.toml` returns the recipe packages. 4. `discover_binaries_in_repo` ([cargo_discovery.rs:80](https://forge.ourworld.tf/lhumina_code/hero_skills/src/branch/development/crates/lab/src/builder/cargo_discovery.rs#L80)) emits them as `DiscoveredBin`. Then `execute` ([executor.rs:54](https://forge.ourworld.tf/lhumina_code/hero_skills/src/branch/development/crates/lab/src/builder/executor.rs#L54)) launches cargo: ```rust cmd.args(&args) .current_dir(repo_root) // ← always the git root, not the bin's workspace .env("CARGO_TARGET_DIR", target_dir); ``` `repo_root` is the outer hero_rpc workspace, which **excludes** `example/recipe_server/`: ```toml # hero_rpc/Cargo.toml line 25 exclude = ["example/recipe_server"] ``` So `cargo build -p hero_recipes_server` from `repo_root` resolves the OUTER workspace and reports the package doesn't exist — even though lab already located its containing workspace via cargo metadata. ## Fix shape Two options: ### (a) Set `current_dir` to the bin's workspace root Most minimal: each `DiscoveredBin` already carries `manifest_path`. Compute its workspace root (or store it alongside in `DiscoveredBin`) and pass it as `current_dir`. The `BuildTarget` would need a `workspace_root: PathBuf` field plumbed through `BuildTargetMeta` ([orchestrator.rs:644](https://forge.ourworld.tf/lhumina_code/hero_skills/src/branch/development/crates/lab/src/builder/orchestrator.rs#L644)). ### (b) Pass `--manifest-path` explicitly Add `--manifest-path <workspace_root>/Cargo.toml` to the cargo args in `build_command` ([executor.rs:106](https://forge.ourworld.tf/lhumina_code/hero_skills/src/branch/development/crates/lab/src/builder/executor.rs#L106)). Same plumbing requirement. Both approaches require carrying the workspace root from discovery → BuildTarget → executor. (a) is more idiomatic; (b) leaves cargo's resolved `CARGO_TARGET_DIR` and the rest of the env alone. ## Blast radius This bug blocks **all repos with nested workspaces under git_root**. In the D-07 35-set: - **hero_rpc**: confirmed (this report) - Other candidates with sub-workspace examples need to be audited Affected pattern: any repo that uses lab's `--workspace` mode and has an excluded sub-workspace containing `[[bin]]` targets. ## Verification Local reproduction: ```bash cd lhumina_code/hero_rpc lab build --release --platforms linux-musl-x86_64 --workspace # tail: 4 built, 2 failed (hero_recipes_server, hero_recipes_admin) ``` After fix: same command should build all 6 targets. ## See also - [#269](https://forge.ourworld.tf/lhumina_code/hero_skills/issues/269) catalogue category 9 ('Stale service.toml package references') — diagnoses hero_rpc as a per-repo bug. **The diagnosis is wrong.** hero_recipes_server and hero_recipes_admin crates EXIST and are valid; their parent workspace just isn't the outer hero_rpc workspace. Updating #269 to redirect this row here. - [#268](https://forge.ourworld.tf/lhumina_code/hero_skills/issues/268) — s127 origin issue (lab-publish rollout).
Author
Owner

Fixed in hero_skills de34fe0.

Implementation took the first option from the issue body (per-bin workspace_root threaded through the builder), not the --manifest-path alternative.

Patch shape (+22/-5 across 3 files)

  • crates/lab/src/builder/cargo_discovery.rsDiscoveredBin gains pub workspace_root: PathBuf, populated in discover_binaries_in_repo from each outer cargo metadata's workspace_root (one value per Metadata since every package within shares the workspace).
  • crates/lab/src/builder/orchestrator.rsbuild_target_for drops its outer workspace_root parameter and writes bin.workspace_root into BuildTarget (the field already existed but was always overwritten with the single outer value).
  • crates/lab/src/builder/executor.rscmd.current_dir(repo_root)cmd.current_dir(&bt.workspace_root); repo_root param renamed _repo_root for caller compat.

Verification

Repo Before After
hero_rpc 4/6 built (hero_recipes_server + hero_recipes_admin failed with package ID specification did not match) 6/6 (2 nested-workspace bins now build)
hero_proc (regression check, single workspace) 3/3 3/3 (unchanged — bin.workspace_root resolves to outer repo_root for single-workspace repos)
cargo test -p lab 79/79 79/79 (forge_api_no_token failure is pre-existing env-pollution when FORGEJO_TOKEN is exported)

Follow-up

hero_rpc's next push to development (or a workflow_dispatch re-fire) will now publish 6 linux-musl-x86_64 binaries to releases/tag/latest instead of 4.

Fixed in hero_skills [`de34fe0`](https://forge.ourworld.tf/lhumina_code/hero_skills/commit/de34fe0). Implementation took the first option from the issue body (per-bin workspace_root threaded through the builder), not the --manifest-path alternative. ### Patch shape (+22/-5 across 3 files) - `crates/lab/src/builder/cargo_discovery.rs` — `DiscoveredBin` gains `pub workspace_root: PathBuf`, populated in `discover_binaries_in_repo` from each outer `cargo metadata`'s `workspace_root` (one value per `Metadata` since every package within shares the workspace). - `crates/lab/src/builder/orchestrator.rs` — `build_target_for` drops its outer `workspace_root` parameter and writes `bin.workspace_root` into `BuildTarget` (the field already existed but was always overwritten with the single outer value). - `crates/lab/src/builder/executor.rs` — `cmd.current_dir(repo_root)` → `cmd.current_dir(&bt.workspace_root)`; `repo_root` param renamed `_repo_root` for caller compat. ### Verification | Repo | Before | After | |---|---|---| | hero_rpc | 4/6 built (`hero_recipes_server` + `hero_recipes_admin` failed with `package ID specification did not match`) | 6/6 (2 nested-workspace bins now build) | | hero_proc (regression check, single workspace) | 3/3 | 3/3 (unchanged — `bin.workspace_root` resolves to outer repo_root for single-workspace repos) | | `cargo test -p lab` | 79/79 | 79/79 (`forge_api_no_token` failure is pre-existing env-pollution when `FORGEJO_TOKEN` is exported) | ### Follow-up hero_rpc's next push to `development` (or a `workflow_dispatch` re-fire) will now publish 6 `linux-musl-x86_64` binaries to `releases/tag/latest` instead of 4.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
lhumina_code/hero_skills#270
No description provided.