From 0d07fe63f9c2d4b1728601deb9627fffd4f21ceb Mon Sep 17 00:00:00 2001 From: Tranquil-Flow <66773372+Tranquil-Flow@users.noreply.github.com> Date: Sat, 27 Jun 2026 02:10:18 +0200 Subject: [PATCH] fix(projects): use _branch_lane_id for non-git folders to prevent duplicate lanes (#53329) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit _place_by_heuristic used the raw path as the lane key for non-git project folders, while the desktop overlay independently computed ::branch::main for the same session (since git_branch was null). The ID mismatch caused duplicate lanes — one from the backend with the folder name, one from the overlay labeled 'main'. Use _branch_lane_id(path, DEFAULT_BRANCH_LABEL) so the backend's lane key matches the overlay's expected ::branch::main scheme, eliminating the duplicate lane. --- tests/tui_gateway/test_project_tree.py | 32 ++++++++++++++++++++++++++ tui_gateway/project_tree.py | 2 +- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/tests/tui_gateway/test_project_tree.py b/tests/tui_gateway/test_project_tree.py index 0faeac7f1e..69fc2b0176 100644 --- a/tests/tui_gateway/test_project_tree.py +++ b/tests/tui_gateway/test_project_tree.py @@ -561,3 +561,35 @@ def test_colliding_repo_basenames_disambiguate_labels(): labels = sorted(p["label"] for p in tree["projects"]) assert labels == ["x/proj", "y/proj"] + + +def test_non_git_folder_uses_branch_lane_id(): + """#53329: _place_by_heuristic must use _branch_lane_id for non-git folders. + + Before the fix, non-git folders got a lane key equal to the raw path, + while the desktop overlay expected ::branch::main. This caused duplicate + lanes (one from backend, one from overlay). + """ + result = pt._place_by_heuristic("/home/user/my-project") + assert result is not None + assert result["lane_key"] == pt._branch_lane_id( + "/home/user/my-project", pt.DEFAULT_BRANCH_LABEL + ), ( + f"Expected lane_key to use _branch_lane_id scheme but got " + f"{result['lane_key']!r}" + ) + # The label should still be the folder basename + assert result["lane_label"] == "my-project" + # Must be marked as main lane + assert result["is_main"] is True + + +def test_non_git_folder_lane_matches_overlay_scheme(): + """#53329: verify the lane key format matches what the overlay expects.""" + result = pt._place_by_heuristic("/data/work/folder-x") + assert result is not None + # Overlay expects: ::branch::main + expected = "/data/work/folder-x::branch::main" + assert result["lane_key"] == expected, ( + f"Expected lane_key={expected!r} but got {result['lane_key']!r}" + ) diff --git a/tui_gateway/project_tree.py b/tui_gateway/project_tree.py index cd5c966a55..5a92b38c97 100644 --- a/tui_gateway/project_tree.py +++ b/tui_gateway/project_tree.py @@ -221,7 +221,7 @@ def _place_by_heuristic(path: str) -> Optional[dict]: repo_path = _with_base_name(path, m.group(1)) return _placement(repo_path, path, m.group(2), path, False, False) - return _placement(path, path, base, path, True, False) + return _placement(path, _branch_lane_id(path, DEFAULT_BRANCH_LABEL), base, path, True, False) def _place(cwd: str, branch: str, resolve: Optional[Resolve], persisted_root: str) -> Optional[dict]: