Skip to content

fix(projects): match git root when Windows drive letter case differs - #111

Open
NicoKiaru wants to merge 1 commit into
JayantDevkar:mainfrom
NicoKiaru:fix/windows-drive-letter-grouping
Open

NicoKiaru wants to merge 1 commit into
JayantDevkar:mainfrom
NicoKiaru:fix/windows-drive-letter-grouping

Conversation

@NicoKiaru

@NicoKiaru NicoKiaru commented Oct 12, 2026 •

Copy link
Copy Markdown

Problem

On Windows, the Projects page shows git-repository groups with "0 projects", and the sessions of those projects can't be reached from there.

Claude Code can record a project's path with a lowercase drive letter (f:/code/journal), while git rev-parse reports the git root with an uppercase one (F:/code/journal). For example, from /projects:

path: f:/code/UNIGE/journal   git_root_path: F:/code/UNIGE/journal   session_count: 51

groupProjects() in frontend/src/lib/utils/grouped-projects.ts compares the two with an exact string match after normalizing backslashes, so the root project is never found. With no root and no nested projects, the group ends up with totalProjects === 0. It still goes into gitRoots, which renders as an empty "0 projects" card.

Fix

  • normalizePath() now uppercases a leading Windows drive letter, alongside the existing backslash → slash normalization. Drive letters are case-insensitive on Windows, so this is safe. The rest of the path is left as is.
  • Added a regression test: a project at f:/code/journal with a git root at F:/code/journal now lands in singleGitProjects.

Testing

  • npx vitest run src/tests/grouped-projects.test.ts: 18 tests pass.
  • Found on Windows 11 with real data: the /projects API response above shows the mismatch for every repo where the session path has a lowercase drive letter.

🤖 Generated with Claude Code

On Windows, Claude Code can record a project path as `f:/repo` while
`git rev-parse` reports the root as `F:/repo`. The exact string compare
in groupProjects() then found no root project, producing empty
"0 projects" groups on the Projects page and hiding their sessions.

Normalize the drive letter to uppercase alongside the existing
backslash normalization, and add a regression test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@NicoKiaru

Copy link
Copy Markdown
Author

I checked and with this fix I can see all my sessions for each projects as expected. Thanks for the cool project!

(this message is written by an organic brain)

@JayantDevkar

Copy link
Copy Markdown
Owner

Thanks for tracking this down, the f: vs F: explanation matches what I'd expect from Claude Code recording the cwd as typed.

Two things before merging:

  1. getRelativePath a few lines down has the same exact-match comparison, so nested projects under one of these roots will now show absolute paths. If you move the normalizer into $lib/utils.ts and use it in both places, both symptoms go away together. Inline notes point at the lines.
  2. The comment above normalizePath grew to four lines. We keep code comments to one line in this repo; something like // Normalize separators and uppercase the drive letter so f:/repo matches F:/repo is enough, the rest is already in your PR description.

Longer term the API should probably emit path and git_root_path with consistent casing so the frontend doesn't have to know about drive letters at all, but that's a separate change and not on you.

CI hasn't run on this branch yet (first-contributor approval), I'll kick it off.

@JayantDevkar JayantDevkar left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Inline context for reviewers, one note per change area.

// git outputs forward slashes — normalize both to forward slashes before comparing).
// Also uppercase the Windows drive letter: Claude Code may record `f:/repo` while
// git reports `F:/repo`, which would otherwise leave the group with 0 projects.
const normalizePath = (p: string) =>

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Could this live in $lib/utils.ts as an exported normalizePath and be used by getRelativePath below as well? That function still does a plain startsWith on the un-normalized root, so a nested f:/code/journal/pkg under the new F:/code/journal key renders as an absolute path instead of ./pkg. There are five other .replace(/\\/g, '/') sites across this file and utils.ts, and decodeProjectPath already uppercases the drive on its own.

const projects = [
makeProject({
encoded_name: 'f--code-journal',
path: 'f:/code/journal',

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Worth adding a nested project to this fixture (path: 'f:/code/journal/pkg', is_nested_project: true) and asserting getRelativePath(...) returns ./pkg. That's the case the current code still misses.

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