Skip to content

feat(wsl): scope and path map for converting UNC Windows-drive projects (D1c-4a) - #33108

Merged
nwparker merged 8 commits into
mainfrom
nwparker/wsl-d1c-4a-unc-drive-scope
Oct 29, 2026
Merged

nwparker merged 8 commits into
mainfrom
nwparker/wsl-d1c-4a-unc-drive-scope

Conversation

@nwparker

@nwparker nwparker commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 2 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​220 0 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​220
Prod 6 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​233 $\color{#cf222e}{\Huge{\mathbf{−}}}$​4 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​229

ELI5

Some projects are saved under a WSL network path that actually points at a Windows folder: \\wsl.localhost\Ubuntu\mnt\c\src\app is the same folder as C:\src\app. The WSL move can't take these. They need to become plain Windows projects at C:\… instead. This PR adds the first piece: working out which projects qualify and what each saved path and workspace id becomes. Nothing changes yet.

What Changed

New file src/main/wsl/wsl-drive-unc-project-scope.ts:

  • wslUncDrivePathToWindows maps \\wsl.localhost\<distro>\mnt\c\x (either UNC spelling or slash, any case in mnt\<drive>) to C:\x. Anything else returns null.
  • collectWslDriveUncScope lists the local repos and folder workspaces of a distro that qualify. It refuses a repo with a reason when:
    • some of its saved paths (worktree folder, imported worktrees and so on) aren't on the drive;
    • it already runs on another host;
    • C:\x is already a project here, so converting would make two rows for one folder.
  • wslDriveUncWorktreeId gives a converted worktree its new id: same repo id, drive path, and any folder-workspace suffix kept.

New file src/main/wsl/wsl-unc-access-denied.ts: when adding a \\wsl.localhost\… path fails because Windows refused to read it (EPERM or EACCES), the add says "Windows denied access to this WSL path" instead of "Not a valid git repository", which sent users looking in the wrong place. Only on Windows with the WSL hosts gate open; otherwise the usual message, which the renderer uses to offer adding the folder as a non-git project.

The list of a repo's saved paths is the mover's own (wslMoveRepoPaths, now exported), so the two scope rules can't drift apart.

Why

Owner decision: Windows-drive projects that run under WSL become Windows projects with WSL terminals. For UNC-path projects the path itself changes, and a workspace id contains its path (<repoId>::<path>). So the next PRs must re-key the saved session, chats and terminals. Pinning down the scope and the mapping first lets those PRs share one definition.

Linked Issue

WSL re-write, step D1c (R36, R37). Stacked on #33097.

Visual Proof

N/A: no caller and no UI yet.

Testing

  • wsl-drive-unc-project-scope.test.ts: path map (spellings, case, drive root, other distros, in-distro paths), scope with every refusal reason, and worktree ids including the folder-workspace suffix.
  • The mover's scope tests pass with the exported helper.

Review

Self-reviewed.

Agent skill upstream boundary

  • Not applicable.

Notes

  • Flag-off: no change. describeWslUncAccessDenied returns null unless it is Windows with the gate open, without listing the folder, so a failed add still says "Not a valid git repository" and the renderer still offers adding it as a folder (pinned in wsl-unc-access-denied.test.ts). The new scope module has no production importer yet, and wslMoveRepoPaths is a rename plus an export.
    Pure functions; nothing runs them yet.

Status

@nwparker
nwparker force-pushed the nwparker/wsl-d1c-3-windows-git-ui branch from 820451d to 85eaf0a Compare October 25, 2026 02:00
@nwparker
nwparker requested a review from brennanb2025 as a October 25, 2026 02:00
@nwparker
nwparker force-pushed the nwparker/wsl-d1c-4a-unc-drive-scope branch from 71b61a9 to fae28ea Compare October 25, 2026 02:01
@nwparker
nwparker force-pushed the nwparker/wsl-d1c-3-windows-git-ui branch from 85eaf0a to 26e4f21 Compare October 25, 2026 02:30
@nwparker
nwparker force-pushed the nwparker/wsl-d1c-4a-unc-drive-scope branch from fae28ea to f5e6b51 Compare October 25, 2026 02:30
@nwparker
nwparker force-pushed the nwparker/wsl-d1c-3-windows-git-ui branch from 26e4f21 to 102d859 Compare October 25, 2026 04:34
@nwparker
nwparker force-pushed the nwparker/wsl-d1c-4a-unc-drive-scope branch from f5e6b51 to 725f1d4 Compare October 25, 2026 04:34
@nwparker
nwparker force-pushed the nwparker/wsl-d1c-3-windows-git-ui branch from 102d859 to 6920507 Compare October 25, 2026 06:40
@nwparker
nwparker force-pushed the nwparker/wsl-d1c-4a-unc-drive-scope branch from 725f1d4 to 9b2f879 Compare October 25, 2026 06:40
@nwparker
nwparker force-pushed the nwparker/wsl-d1c-3-windows-git-ui branch from 6920507 to 7105dc1 Compare October 25, 2026 08:30
@nwparker
nwparker force-pushed the nwparker/wsl-d1c-4a-unc-drive-scope branch from 9b2f879 to f82f8a8 Compare October 25, 2026 08:30
@nwparker
nwparker force-pushed the nwparker/wsl-d1c-3-windows-git-ui branch from 7105dc1 to acd9c2d Compare October 25, 2026 09:48
@nwparker
nwparker force-pushed the nwparker/wsl-d1c-4a-unc-drive-scope branch from f82f8a8 to 37f8dd7 Compare October 25, 2026 09:48
@nwparker
nwparker force-pushed the nwparker/wsl-d1c-3-windows-git-ui branch from acd9c2d to cc589b8 Compare October 25, 2026 10:21
@nwparker
nwparker force-pushed the nwparker/wsl-d1c-4a-unc-drive-scope branch from 37f8dd7 to 73d6c57 Compare October 25, 2026 10:21
@nwparker
nwparker force-pushed the nwparker/wsl-d1c-3-windows-git-ui branch from cc589b8 to 727b8be Compare October 25, 2026 11:10
@nwparker
nwparker force-pushed the nwparker/wsl-d1c-4a-unc-drive-scope branch from 73d6c57 to ed9113a Compare October 25, 2026 11:10
@nwparker
nwparker force-pushed the nwparker/wsl-d1c-3-windows-git-ui branch from 727b8be to 6b24e8c Compare October 25, 2026 11:59
@nwparker
nwparker force-pushed the nwparker/wsl-d1c-4a-unc-drive-scope branch from ed9113a to 67da7de Compare October 25, 2026 11:59
@nwparker
nwparker force-pushed the nwparker/wsl-d1c-3-windows-git-ui branch from 6b24e8c to 9bc5b72 Compare October 25, 2026 12:36
@nwparker
nwparker force-pushed the nwparker/wsl-d1c-4a-unc-drive-scope branch from 67da7de to 8b6579e Compare October 25, 2026 12:36
@nwparker
nwparker force-pushed the nwparker/wsl-d1c-3-windows-git-ui branch from 9bc5b72 to e247268 Compare October 25, 2026 13:03
@nwparker
nwparker force-pushed the nwparker/wsl-d1c-4a-unc-drive-scope branch from 8b6579e to c465a8e Compare October 25, 2026 13:03
@nwparker
nwparker force-pushed the nwparker/wsl-d1c-3-windows-git-ui branch from e247268 to 61534ac Compare October 25, 2026 13:16
@nwparker
nwparker force-pushed the nwparker/wsl-d1c-4a-unc-drive-scope branch from c465a8e to 8796cff Compare October 25, 2026 13:16
@OrcaWin
OrcaWin force-pushed the nwparker/wsl-d1c-3-windows-git-ui branch from 61534ac to 6170884 Compare October 27, 2026 23:53
@OrcaWin
OrcaWin force-pushed the nwparker/wsl-d1c-4a-unc-drive-scope branch from 8796cff to a96c9e4 Compare October 27, 2026 23:53
@OrcaWin
OrcaWin force-pushed the nwparker/wsl-d1c-3-windows-git-ui branch from c553e78 to f0f8f9e Compare October 28, 2026 02:20
@OrcaWin
OrcaWin force-pushed the nwparker/wsl-d1c-4a-unc-drive-scope branch from e43376e to f6a6990 Compare October 28, 2026 02:20
@OrcaWin
OrcaWin force-pushed the nwparker/wsl-d1c-3-windows-git-ui branch from f0f8f9e to b19ac70 Compare October 28, 2026 03:17
@OrcaWin
OrcaWin force-pushed the nwparker/wsl-d1c-4a-unc-drive-scope branch from f6a6990 to 7388160 Compare October 28, 2026 03:17
@OrcaWin
OrcaWin force-pushed the nwparker/wsl-d1c-3-windows-git-ui branch from b19ac70 to 77aa4b2 Compare October 28, 2026 10:47
@OrcaWin
OrcaWin force-pushed the nwparker/wsl-d1c-4a-unc-drive-scope branch from 7388160 to cf08184 Compare October 28, 2026 10:48
@OrcaWin
OrcaWin force-pushed the nwparker/wsl-d1c-3-windows-git-ui branch from 77aa4b2 to 462b073 Compare October 28, 2026 11:30
@OrcaWin
OrcaWin force-pushed the nwparker/wsl-d1c-4a-unc-drive-scope branch from cf08184 to 4912f85 Compare October 28, 2026 11:30
@OrcaWin
OrcaWin force-pushed the nwparker/wsl-d1c-3-windows-git-ui branch from 462b073 to 47c5a07 Compare October 28, 2026 12:05
@OrcaWin
OrcaWin force-pushed the nwparker/wsl-d1c-4a-unc-drive-scope branch from 4912f85 to 8c12faa Compare October 28, 2026 12:05
@OrcaWin
OrcaWin force-pushed the nwparker/wsl-d1c-3-windows-git-ui branch from 47c5a07 to a683039 Compare October 28, 2026 20:57
@OrcaWin
OrcaWin force-pushed the nwparker/wsl-d1c-4a-unc-drive-scope branch from 8c12faa to 5a931aa Compare October 28, 2026 20:57
@greptile-apps

greptile-apps Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

RetriggerView in GreptileConfidence Score: 3/5

[Low impact] The changes stay within WSL project selection and repo registration.

The PR should not merge until the two existing mount-reading findings are fixed; both can let conversion select the wrong folder.

Findings

  1. P1 Nested mounts select wrong folders ▶
  2. P1 Incomplete mounts select wrong folders ▶

Summary

Adds helpers that identify WSL UNC projects backed by Windows drives and map their paths and worktree IDs to Windows spelling.

  • Eligible WSL drive projects get mapped Windows paths for conversion.
  • Repo registration explains when Windows blocks access to a WSL path.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Saved repository or folder] --> B{UNC path names the distro's drive folder?}
  B -- No --> C[Leave outside scope]
  B -- Yes --> D{Local and all saved paths map?}
  D -- No --> E[Refuse with reason]
  D -- Yes --> F{Mounts confirm the drive folder?}
  F -- No --> E
  F -- Yes --> G{Windows path already claimed?}
  G -- Yes --> E
  G -- No --> H[Include project in conversion scope]
  H --> I[Map paths and preserve worktree ID suffix]
Loading

Reviews (3) · Last reviewed commit: "Merge branch 'main' into nwparker/wsl-d1..." · Reviewed by Greptile

Comment on lines +33 to +34
const mountInfo = result.timedOut || result.code !== 0 ? null : fence.readStdout(result.stdout)
return mountInfo === null ? null : parseWindowsDriveMountInfo(mountInfo)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Nested mounts select wrong folders

readWslDistroDriveMounts drops non-Windows mounts before the folder check chooses the innermost mount. If /home/me/app is bind-mounted at /mnt/c/shared, the parser skips that ext4 mount and leaves only /mnt/c → C:\. The check then accepts \\wsl.localhost\Ubuntu\mnt\c\shared as C:\shared, although they are different folders.

The conversion caller in PR #33135 trusts this result when rewriting saved paths. Folder projects have no Git check to stop the wrong conversion. Keep non-Windows mounts as entries with windowsRoot: null so they block the parent drive match, and test with parsed mountinfo rather than a hand-built mount list.

nwparker and others added 7 commits October 28, 2026 15:19
…tings

Project settings show a Git row for a WSL-runtime project on a Windows drive (WSL
hosts on). "Use Git for Windows" checks the project, shows any blocker in plain words,
optionally sets core.autocrlf in the repo, and saves the returned runtime through the
normal project update. Once converted the row says where Git and terminals run.
Switching the distro keeps Git for Windows instead of silently dropping it.

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
…ts (D1c-4a)

The mover refuses a project kept as a distro's UNC path into a Windows drive
(\\wsl.localhost\<d>\mnt\c\x). D1c converts it in place to C:\x. This adds the pure
part: which local repos and folder workspaces qualify (every stored path on the drive;
not remote; not already a C:\ project here), the UNC → drive path map, and the
worktree id each converted worktree gets (same repo id, drive path). Reuses the mover's
list of a repo's stored paths. Nothing calls it yet.

Co-Authored-By: Claude <noreply@anthropic.com>
The new message replaced "Not a valid git repository", which the renderer
matches to offer adding the folder as a non-git project. With the WSL hosts
gate closed the add fails with the usual text again.

(cherry picked from commit a5a70dad7743f679fd764c3e17a3e27b71c9f28b)
… the distro mounts from that Windows drive

- Two UNC spellings of one drive path (\\wsl$ vs \\wsl.localhost, \mnt\C vs \mnt\c) would
  both convert into two projects. Each accepted conversion now claims its drive path, so the
  next spelling is refused as a duplicate. Folder workspaces get the same duplicate check.
- Any /mnt/<letter> read as a Windows drive. The scope now takes the distro's Windows-drive
  mounts (drvfs/9p from its mountinfo, parsed as the WSL-host drive guard does) and converts a
  path only when its mount is that same whole drive. A folder workspace has one path, so that
  check is its mixed-location check too.

Co-Authored-By: Claude <noreply@anthropic.com>
… real /mnt/C directory never converts onto C:
@OrcaWin
OrcaWin force-pushed the nwparker/wsl-d1c-3-windows-git-ui branch from a683039 to ec56df4 Compare October 28, 2026 22:47
@OrcaWin
OrcaWin force-pushed the nwparker/wsl-d1c-4a-unc-drive-scope branch from 5a931aa to 4c5a342 Compare October 28, 2026 22:47
timeoutMs: READ_TIMEOUT_MS,
maxOutputBytes: 1_048_576
})
const mountInfo = result.timedOut || result.code !== 0 ? null : fence.readStdout(result.stdout)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Incomplete mounts select wrong folders

If a distro's mount table exceeds the 1 MiB limit, runProcess keeps its beginning and sets outputTruncated. This code ignores that flag, and readStdout returns the captured text even when the closing marker is missing.

A parent drive mount can remain while a later nested mount is missing. Conversion then accepts a Windows folder that is not the UNC folder and rewrites saved paths to the wrong location. Return null when result.outputTruncated is true.

Suggested change
const mountInfo = result.timedOut || result.code !== 0 ? null : fence.readStdout(result.stdout)
const mountInfo =
result.timedOut || result.code !== 0 || result.outputTruncated
? null
: fence.readStdout(result.stdout)

@nwparker
nwparker changed the base branch from nwparker/wsl-d1c-3-windows-git-ui to main October 29, 2026 00:45
@coderabbitai

coderabbitai Bot commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 19 seconds.

Check out review usage here.

View limit details

Limit details: You’ve used all 10 included reviews currently available.

Learn how review limits work.

Review configuration:

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: c4f4f6b3-88af-45f3-bab3-421b8c3b350e
Commits

Reviewing files that changed from the base of the PR and between e6a2708 and be42e48.

Files selected for processing: 8
  • src/main/ipc/repos/local-repo-registration.ts
  • src/main/persistence/moving-wsl-projects/wsl-move-scope.ts
  • src/main/runtime/runtime-repository-registration-controller.ts
  • src/main/wsl/wsl-drive-unc-mounts.ts
  • src/main/wsl/wsl-drive-unc-project-scope.test.ts
  • src/main/wsl/wsl-drive-unc-project-scope.ts
  • src/main/wsl/wsl-unc-access-denied.test.ts
  • src/main/wsl/wsl-unc-access-denied.ts
Finishing Touches: 💡 1
🛠️ Fix failing CI checks: 💡
  • Commit to this branch
  • Create a new PR
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@nwparker
nwparker merged commit 3a51382 into main Oct 29, 2026
49 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants