Skip to content

fix(build): strip Windows verbatim \?\ prefix from registry common_dir#528

Merged
coseto6125 merged 2 commits into
mainfrom
worktree-fix+win-verbatim-path
Jun 1, 2026
Merged

fix(build): strip Windows verbatim \?\ prefix from registry common_dir#528
coseto6125 merged 2 commits into
mainfrom
worktree-fix+win-verbatim-path

Conversation

@coseto6125
Copy link
Copy Markdown
Owner

Problem

On Windows, ecp <cmd> --repo <name> fails with:

Error preparing index for <repo>: build_l2: 目錄名稱無效。(os error 267)

os error 267 is ERROR_DIRECTORY / ENOTDIR. The same command via cwd
(cd into the repo, no --repo) works fine — so it is not a CJK path, not a
corrupt index, and not a registry-corruption issue (doctor reports the
registry clean).

Root cause

std::fs::canonicalize returns a verbatim path on Windows
(\\?\C:\...). git_common_dir_string (build path) stored that verbatim
string into the registry's common_dir:

"common_dir": "\\\\?\\C:\\Revice_Code\\backstage_api_test_new\\.git"

Resolving --repo <name> derives worktree_root by taking the parent of
common_dir and hands it to ensure_fresh / build_l2. The verbatim prefix
flows into ignore::WalkBuilder and git archive, which treat the .git
file component as a directory and bail out with ERROR_DIRECTORY (267).
The cwd path never hit this because its worktree root comes from
current_dir — a plain path.

Fix

Two points, so the bug is both prevented and recoverable without a rebuild:

Layer Change Why
build/orchestrator.rs write common_dir via dunce::canonicalize root cause — new registry entries are plain
git_cache.rs worktree_root_from_common_dir runs dunce::simplified defensive — registries already holding a verbatim prefix resolve correctly without re-indexing

update_repo_meta only writes common_dir on first build, so existing repos
keep their old verbatim value — the read-side simplified is what unblocks
those without forcing a rebuild. Both dunce calls are no-ops on non-Windows
and on already-plain paths (simplified returns a borrowed sub-slice, zero
alloc).

Deliberately not touched: head_sha_hex's std::fs::canonicalize
(orchestrator.rs:368) only feeds an xxhash digest for cache identity — it
never reaches the build pipeline, and it must stay byte-identical to the
matching digest in git_cache.rs so existing non-git caches remain valid.

Tests

  • worktree_root_drops_dot_git_component, worktree_root_falls_back_when_no_parent — cross-platform.
  • worktree_root_strips_verbatim_disk_prefix, worktree_root_leaves_plain_path_untouched#[cfg(windows)].

cargo test -p egent-code-plexus green; repo_selector (18) and
build_orchestrator suites unaffected.

🤖 Generated with Claude Code

`std::fs::canonicalize` emits verbatim (`\\?\C:\...`) paths on Windows. The
build path stored that string as a repo's `common_dir`; resolving `--repo
<name>` then derived `worktree_root` from it and fed the verbatim path into
`ignore::WalkBuilder` / `git archive`, which treat the `.git` file component
as a directory and fail with ERROR_DIRECTORY (os error 267). The cwd path
(no `--repo`) was unaffected because its worktree root is already plain.

- orchestrator: write `common_dir` via `dunce::canonicalize` so new registry
  entries are plain from the start (root cause).
- git_cache: `worktree_root_from_common_dir` runs `dunce::simplified` so
  registries already polluted with a verbatim prefix resolve correctly without
  a rebuild (defensive; no-op on non-Windows and on already-plain paths).
- tests: cross-platform coverage plus Windows-gated cases for the verbatim
  disk prefix and idempotent plain-path passthrough.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coseto6125 coseto6125 enabled auto-merge (squash) June 1, 2026 07:36
@coseto6125 coseto6125 added the merge-queue Opt-in to Mergify merge queue label Jun 1, 2026
@coseto6125
Copy link
Copy Markdown
Owner Author

Unified Code Review — PR #528

Risk: MEDIUM (Windows-only; build pipeline + pre_tool_use hot path touched, but change is a zero-alloc borrowed-slice transform)
Affected Flows: ensure_freshbuild_l2; find / group impact / pre_tool_use query paths

Root cause analysis is correct and independently confirmed: std::fs::canonicalize emits a \\?\ verbatim path on Windows; stored into registry common_dir, it flows into ignore::WalkBuilder / git archive which treat the .git file component as a directory → ERROR_DIRECTORY (267). The two-point fix (write-side dunce::canonicalize, read-side dunce::simplified) is the right shape, and dunce::simplified correctly preserves non-strippable verbatim paths (\\?\UNC\, \\.\, reserved names) — verified against dunce 1.0.5 is_safe_to_strip_unc source + its test suite. Compiles clean, cross-platform tests green.

Found 1 issue:

  1. Fix is incomplete — three sibling paths still hit os error 267 (Score: 80) — Bug
    The PR fixes worktree_root_from_common_dir, but three call sites derive the worktree root by open-coding common_dir.parent() instead of going through that (now-fixed) helper, so a registry holding a verbatim common_dir still feeds a verbatim path into a walk / git subprocess on Windows:

    Same root cause, same fix: replace each open-coded .parent() with crate::git_cache::worktree_root_from_common_dir(common_dir). Recommend bundling into this PR since they share the fix and are otherwise a guaranteed repeat 267 the moment a Windows user runs summary / group.

Nit (non-blocking): PR body carries a Generated with Claude Code footer — this repo's CLAUDE.md forbids that trailer on PR bodies; drop it on the next push.

…ing helper

Three call sites derived the worktree root by open-coding common_dir.parent(),
bypassing worktree_root_from_common_dir (and thus its dunce::simplified call).
A registry holding a Windows verbatim \?\ common_dir therefore still fed a
verbatim path into a filesystem walk / git subprocess, repeating os error 267:

- summary.rs    -> ensure_index -> any_source_newer_than -> WalkBuilder (ecp summary)
- group/sync.rs -> walk_and_extract -> WalkDir            (ecp group sync)
- group/status.rs -> git -C repo_root                     (ecp group status)

Route all three through git_cache::worktree_root_from_common_dir so the
verbatim prefix is stripped on the read side, consistent with the main-line
find / group-impact / pre_tool_use query paths. No-op on non-Windows.
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 1, 2026

ecp impact cache (0 symbols) — internal, used by ecp dev pr-analyze

[]

@github-actions github-actions Bot added the ecp:risk-low ecp signal label Jun 1, 2026
@coseto6125 coseto6125 merged commit f849798 into main Jun 1, 2026
18 checks passed
@coseto6125 coseto6125 deleted the worktree-fix+win-verbatim-path branch June 1, 2026 08:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ecp:risk-low ecp signal merge-queue Opt-in to Mergify merge queue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant