Running Tests in Git Worktrees¶
TL;DR¶
If that passes, run tests normally. There is nothing else to configure.
The one thing that breaks this¶
Never export a PYTHONPATH that points at a specific checkout.
An absolute PYTHONPATH exported for one checkout precedes the venv's site-packages on sys.path. Inside any other worktree, uv run — and therefore just check and just test — then imports tradai.* from that first checkout. Tests pass or fail on code you did not change, and nothing warns you:
$ cd .claude/worktrees/my-feature
$ uv run python -c "import tradai.common as m; print(m.__file__)"
/Users/you/tradai-uv/libs/tradai-common/src/tradai/common/__init__.py # ← the WRONG tree
The fix is to remove it, not to set it to something else:
Then relaunch your shell, editor, or agent so the child processes stop inheriting it.
just recipes are already immune — the justfile exports PYTHONPATH := '' at the top — but a bare uv run, pytest, editor test-runner, or agent invocation is not.
What is allowed¶
The invariant is no PYTHONPATH entry may resolve outside the current git toplevel, not "never use PYTHONPATH". These are fine:
${workspaceFolder} is not a safe way to write one. It resolves to the folder VS Code has open, not the worktree your terminal is standing in — so it becomes an absolute path into the wrong checkout the moment you cd. That is why terminal.integrated.env.* was removed from .vscode/settings.json. python.analysis.extraPaths still uses ${workspaceFolder} safely, because it never reaches a subprocess. - Container-local values baked into a Dockerfile. - Anything under the worktree you are currently in.
Why the venvs are fine on their own¶
Each worktree needs its own environment:
uv then writes per-venv _editable_impl_tradai_*.pth files scoped to that worktree, and imports resolve correctly with no environment variables at all. Confirm with:
A worktree missing that file has a half-built venv — uv venv ran but uv sync never finished. It will either fail on a missing third-party import or fall through to another checkout. Re-sync it, or delete .venv and let the next uv run rebuild it.
Historical note. Earlier versions of this document blamed
.pthaggregation — the claim that thetradainamespace package merges paths from every checkout with the main repo winning. That does not reproduce with currentuv, and thewt-testhelper it recommended setPYTHONPATH, which is what causes the problem it was meant to solve. Both have been removed. Audited 2026-08-23; seereports/worktree-branch-health-audit-2026-08-22.md§2.
Creating and retiring worktrees¶
just wt-new feat/my-thing # .claude/worktrees/, uv sync, gt track, wt-doctor
just wt-prune # what has landed and is safe to remove
just wt-prune apply # remove only what that dry run approved
wt-prune is deliberately conservative. It keeps anything dirty, anything holding ignored local state (.env, mlflow.db, Pulumi state — invisible to git status, deleted by git worktree remove), anything with commits absent from origin/dev, any Graphite stack parent, and any locked worktree. Read the KEEP reasons, not just the removable list.
Guardrails¶
| Guard | What it catches |
|---|---|
just wt-doctor | ambient PYTHONPATH, half-built venv, imports resolving outside this worktree |
tests/unit/test_worktree_import_isolation.py | the same invariant, under pytest and in CI |
justfile export PYTHONPATH := '' | protects every just recipe regardless of your shell |
tests/scripts/worktree_prune_test.sh | 27 fixture-repo assertions on the cleanup tool |
Running services from a worktree¶
just up, just up-ministack and friends work from any worktree simultaneously. Each checkout gets its own Compose project, so containers, networks and named volumes are namespaced, and the host ports are shifted into a per-worktree band:
The main checkout keeps project tradai and the original ports (8000, 5433, 4566, …), so nothing about the established workflow changes there — only additional worktrees move. Override either explicitly when you need a fixed value:
Two things to know:
just down -vonly destroys this checkout's volumes. Before #1350 the volumes were shared, so tearing down from one worktree deleted another's Postgres and MLflow data.- A bare
docker compose updoes not get this. The derivation lives in the justfile, and.envstill pinsCOMPOSE_PROJECT_NAME=tradai. Usejust, or export the variables yourself.