diff --git a/strix/interface/utils.py b/strix/interface/utils.py index b1e01c9da..99a0b0039 100644 --- a/strix/interface/utils.py +++ b/strix/interface/utils.py @@ -1720,13 +1720,20 @@ def _split_workspace_spec(spec: str) -> tuple[str, str | None]: A Windows drive letter's colon (``C:\\path\\file``) is the only colon in a bare path. ``rpartition`` still finds it, leaving a single-letter ``raw`` - with the rest of the path in what would be ``dest`` -- that split is - detected here and rejected, so the whole spec is returned as the path. + with the rest of the path in what would be ``dest``. That collides with a + real single-letter source that declares an absolute ``/workspace/...`` + destination (``a:/workspace/input.txt``), so the drive-letter case is only + taken when ``dest`` is not one of those: the explicit form keeps its + existing meaning either way. """ raw, sep, dest = spec.rpartition(":") if not sep or not dest.strip(): return spec, None - if re.fullmatch(r"[A-Za-z]", raw) and dest[:1] in ("\\", "/"): + if ( + re.fullmatch(r"[A-Za-z]", raw) + and dest[:1] in ("\\", "/") + and not dest.startswith("/workspace/") + ): return spec, None return raw, dest diff --git a/tests/test_workspace_files.py b/tests/test_workspace_files.py index 45335ed28..ce8136bc1 100644 --- a/tests/test_workspace_files.py +++ b/tests/test_workspace_files.py @@ -59,6 +59,23 @@ def test_a_windows_drive_letter_is_not_mistaken_for_a_dest_separator( assert _split_workspace_spec(spec) == expected +def test_a_single_letter_source_with_a_workspace_destination_is_not_a_drive_letter() -> None: + assert _split_workspace_spec("a:/workspace/input.txt") == ("a", "/workspace/input.txt") + + +def test_a_single_letter_file_can_declare_an_absolute_workspace_destination( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + (tmp_path / "a").write_text("x", encoding="utf-8") + monkeypatch.chdir(tmp_path) + + # A relative, single-letter spec: "a:/workspace/input.txt" is exactly the + # shape rpartition(":") could confuse for a Windows drive letter. + resolved = resolve_workspace_files(["a:/workspace/input.txt"]) + + assert resolved[0]["workspace_path"] == "/workspace/input.txt" + + @pytest.mark.skipif(sys.platform != "win32", reason="tmp_path is only drive-letter-shaped here") def test_a_bare_windows_path_resolves_end_to_end(tmp_path: Path) -> None: source = tmp_path / "wordlist.txt"