diff --git a/apps/desktop/src/app/chat/sidebar/index.tsx b/apps/desktop/src/app/chat/sidebar/index.tsx index d3dd287ba5..fcc84966c8 100644 --- a/apps/desktop/src/app/chat/sidebar/index.tsx +++ b/apps/desktop/src/app/chat/sidebar/index.tsx @@ -1008,7 +1008,15 @@ export function ChatSidebar({ // Layer the user's manual drag-order on top of the deterministic sort. Empty // (default) returns `sorted` untouched; projects the user hasn't ordered yet // keep their sorted position rather than jumping the hand-picked list. - return orderProjectsByIds(sorted, projectOrderIds) + // Deduplicate by id — safety net for backend merge edge cases + // (e.g. Windows path-casing differences across profiles). + const seen = new Set() + const deduped = sorted.filter(project => { + if (seen.has(project.id)) return false + seen.add(project.id) + return true + }) + return orderProjectsByIds(deduped, projectOrderIds) }, [ projectTree, dismissedAutoProjects, diff --git a/hermes_cli/web_routers/profiles.py b/hermes_cli/web_routers/profiles.py index aa1c3487a4..9672eb29ec 100644 --- a/hermes_cli/web_routers/profiles.py +++ b/hermes_cli/web_routers/profiles.py @@ -16,6 +16,7 @@ from hermes_cli.web_read_coalescing import coalesced_read import inspect import json import logging +import os import re import subprocess import sys @@ -687,7 +688,7 @@ def _merge_profile_tree( session["profile"] = profile session["is_default_profile"] = profile == "default" - key = project.get("path") or project["id"] + key = os.path.normcase(project.get("path") or project["id"]) existing = merged.get(key) if existing is None: merged[key] = project