Windows: worktree-mode createSandbox fails when the host repo is itself a linked worktree (host .git pointer-file mount is never patched)

Author: slubby101Created Sep 11, 2026Updated Sep 11, 2026

Summary

On a Windows host, worktree-mode createSandbox() with the docker() provider fails when the host repo directory is itself a linked git worktree, i.e. its .git is a pointer file. resolveGitMounts() returns an identity mount for the host repo's .git file. patchGitMountsForWindows() never rewrites it, because it only matches against the newly created sandbox worktree's .git. The drive-letter identity mount (C:/…/.git:C:/…/.git) therefore reaches docker create and is rejected.

Distinct from #550 (createSandbox skipped the patch) and #793 (.git as a directory): both are fixed. Related to #868, which is the same identity-mount design seen from Linux.

Environment

  • Windows 11, Docker Desktop (WSL2 backend)
  • Observed on @ai-hero/sandcastle 0.10.0. I read the 0.12.0 code and the logic is unchanged (not re-run on 0.12.0).
  • docker() provider, worktree branch strategy, via createSandbox()

Repro

  1. git worktree add ../repo-feature feature from a normal clone, so ../repo-feature/.git is a file: gitdir: C:/Users/<user>/code/repo/.git/worktrees/repo-feature.
  2. From ../repo-feature, call createSandbox({ sandbox: docker({ imageName }) }) (worktree mode).
  3. docker create fails on the volume string for C:/Users/<user>/code/repo-feature/.git.

The same call from the main clone (.git is a directory) succeeds.

Root cause (0.12.0, dist/chunk-VOG34SRF.js)

  • resolveGitMounts(join(hostRepoDir, ".git")) (worktree path, ~L26729). For a pointer file it returns two identity mounts:
    • { hostPath: <hostRepoDir>/.git, sandboxPath: <same> }
    • { hostPath: <parent .git>, sandboxPath: <same> }
  • patchGitMountsForWindows(gitMounts, worktreeInfo.path, SANDBOX_REPO_DIR) (~L26740) reads worktreeInfo.path/.git. That is the sandbox worktree created under .sandcastle/worktrees/….
    • It correctly remaps the parent .git mount, because every linked worktree shares the main repo's .git.
    • It compares mounts against gitFileHostPath = <worktreeInfo.path>/.git. The host repo's own <hostRepoDir>/.git mount never matches, so it falls through to correctedMounts.push(m) unchanged. Since nothing matched, it also appends the override file mount.

The leftover <hostRepoDir>/.git identity mount is the one Docker rejects. The sandbox doesn't need it: the sandbox repo is worktreeInfo.path, not hostRepoDir.

Suggested fix

In worktree mode, resolve git mounts from the sandbox worktree, not the host repo: resolveGitMounts(join(worktreeInfo.path, ".git")). Or have patchGitMountsForWindows drop any remaining identity mount of <hostRepoDir>/.git when hostRepoDir !== worktreeInfo.path.

Workaround

Run from the main clone rather than from a linked worktree.