Skip to content

gate: sharing CARGO_TARGET_DIR between worktrees links another worktree's source, yielding errors that match no commit #2013

Description

@macanderson

Problem

Pointing CARGO_TARGET_DIR at a shared directory from two different worktrees
of this repository produces compile errors that name symbols the source does
not contain
. A build in worktree A can link against worktree B's build of a
workspace crate, so rustc reports a real-looking error against B's source
while citing line numbers in A's files.

This is a trap for the workflow AGENTS.md already documents: parallel worktrees
under .claude/worktrees/, plus a make gate that is slow from cold, makes
"reuse the warm cache" an obvious-looking optimisation. It is not safe.

Observed

While unbreaking main (PR #2000), make gate was run in a worktree with
CARGO_TARGET_DIR=/path/to/main-checkout/target to reuse a warm cache. It
failed:

error[E0004]: non-exhaustive patterns: `&stella_protocol::AgentEvent::TurnParked { .. }`
and `&stella_protocol::AgentEvent::TurnWoken { .. }` not covered
  --> crates/stella-pipeline/src/replay.rs:648:11
note: `stella_protocol::AgentEvent` defined here
  --> crates/stella-protocol/src/event.rs:396:4

AgentEvent::TurnParked and AgentEvent::TurnWoken exist in neither that
worktree nor the main checkout:

$ rg -uu -c 'TurnParked' crates/stella-protocol/src/     # no matches, exit 1
$ rg -uu -n 'TurnParked' /path/to/main-checkout/crates/stella-protocol/src/event.rs
                                                          # no matches

Another parallel job (four concurrent cargo/rustc processes at the time) was
building its own worktree — one that does carry those variants, plausibly the
parked-waits work — into the same target directory.

Re-running with a private target dir (unset CARGO_TARGET_DIR) compiled every
workspace crate with no such error, which is what confirms the diagnosis.

Why it is worth guarding rather than just knowing

The failure is plausible. replay.rs does match exhaustively on
AgentEvent, and adding arms for two variants is exactly what a reader would do
next. Acting on it writes arms for variants that do not exist, which then fails
to compile for a second, unrelated reason. The only tell is that the cited
symbol cannot be found in the cited file — a check nobody performs by default.

Definition of done

Any of, in preference order:

  1. A sentence in AGENTS.md (near the make gate / worktree material) stating
    that CARGO_TARGET_DIR must not be shared between worktrees, and that a
    compile error naming an absent symbol means exactly this.
  2. A guard in the Makefile's gate entrypoint that refuses to run when
    CARGO_TARGET_DIR resolves outside the current worktree, with that message.
  3. Root-cause it properly: determine whether this is a cargo fingerprinting bug
    worth reporting upstream (two workspace members sharing name+version across
    source roots), and if so, file it and link back here.

Option 2 is the one that actually stops a future session losing an hour, since
the guidance in option 1 only helps someone who already reads it.

Constraints

  • scripts/impacted-crates.sh and the pre-push hook already reason about
    scoping; a target-dir guard belongs beside them, not inside a crate.
  • Do not "fix" this by making worktrees share a lockfile or a target dir on
    purpose. Isolation is the property that makes parallel jobs safe here.

Verify

Two worktrees of this repo, both with CARGO_TARGET_DIR set to the same path,
building concurrently, where one carries a workspace-crate enum the other lacks.
With the guard in place, the run must refuse to start rather than emit E0004.

Metadata

Metadata

Assignees

No one assigned

    Labels

    triageUntyped request — convert by adding bug / feature / epic

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions