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]: