From 98f446e4683fdae23e2d50f0a09bad71f7c67068 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 18 Jul 2026 06:51:45 +0000 Subject: [PATCH 1/3] Initial plan From e0f0186e86b9cb4dce7c8ed6d11fd1c959e6e8d5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 18 Jul 2026 06:56:46 +0000 Subject: [PATCH 2/3] Harden Planfile probe decoding on Windows queue mode --- src/koru/queue/ticket.py | 17 ++++++++++++----- tests/test_planfile_queue.py | 33 ++++++++++++++++++++++++++++++++- 2 files changed, 44 insertions(+), 6 deletions(-) diff --git a/src/koru/queue/ticket.py b/src/koru/queue/ticket.py index ea8382f6..fd3a0c0b 100644 --- a/src/koru/queue/ticket.py +++ b/src/koru/queue/ticket.py @@ -14,6 +14,7 @@ from pathlib import Path from typing import Any +from koru.queue.runners import _decode_subprocess_output from koru.queue.types import CommandResult @@ -203,7 +204,7 @@ def _python_has_planfile_cli(python: str) -> bool: ), ], capture_output=True, - text=True, + text=False, timeout=5, check=False, ) @@ -243,12 +244,15 @@ def _planfile_supports_structured_queue_json(executable: str) -> bool: [executable, "--version"], check=False, capture_output=True, - text=True, + text=False, timeout=2, ) except (OSError, subprocess.SubprocessError): return True - version = _parse_version_tuple(f"{result.stdout}\n{result.stderr}") + version = _parse_version_tuple( + f"{_decode_subprocess_output(result.stdout)}\n" + f"{_decode_subprocess_output(result.stderr)}" + ) if version is None: return True return version >= _MIN_STRUCTURED_QUEUE_PLANFILE_VERSION @@ -326,7 +330,7 @@ def _configured_planfile_cmd_usable(configured: str) -> bool: proc = subprocess.run( [*parts, "--version"], capture_output=True, - text=True, + text=False, timeout=5, check=False, ) @@ -334,7 +338,10 @@ def _configured_planfile_cmd_usable(configured: str) -> bool: return True if proc.returncode == 0: return True - text = f"{proc.stdout}\n{proc.stderr}".lower() + text = ( + f"{_decode_subprocess_output(proc.stdout)}\n" + f"{_decode_subprocess_output(proc.stderr)}" + ).lower() return not any(marker in text for marker in _PLANFILE_MODULE_MISSING_MARKERS) diff --git a/tests/test_planfile_queue.py b/tests/test_planfile_queue.py index 3adff09d..9d6a3379 100644 --- a/tests/test_planfile_queue.py +++ b/tests/test_planfile_queue.py @@ -10,7 +10,12 @@ from koru.cqrs.event_store import JsonlEventStore from koru.queue import run_next_planfile_task -from koru.queue.ticket import parse_next_ticket, planfile_command +from koru.queue.ticket import ( + _configured_planfile_cmd_usable, + _planfile_supports_structured_queue_json, + parse_next_ticket, + planfile_command, +) def _ok(stdout: str = "") -> SimpleNamespace: @@ -23,6 +28,32 @@ def _ticket_args(command: list[str]) -> list[str]: class TestPlanfileCommand(unittest.TestCase): + def test_structured_queue_probe_handles_non_utf8_bytes(self) -> None: + _planfile_supports_structured_queue_json.cache_clear() + with patch( + "koru.queue.ticket.subprocess.run", + return_value=SimpleNamespace( + returncode=0, + stdout=b"planfile wersja \xf3 0.1.101", + stderr=b"", + ), + ) as run: + self.assertTrue(_planfile_supports_structured_queue_json("/tmp/planfile")) + self.assertFalse(run.call_args.kwargs["text"]) + + def test_configured_command_probe_handles_non_utf8_module_missing_marker(self) -> None: + _configured_planfile_cmd_usable.cache_clear() + with patch( + "koru.queue.ticket.subprocess.run", + return_value=SimpleNamespace( + returncode=1, + stdout=b"", + stderr=b"\xf3 no module named 'planfile.cli'", + ), + ) as run: + self.assertFalse(_configured_planfile_cmd_usable("/tmp/venv/bin/python -m planfile.cli")) + self.assertFalse(run.call_args.kwargs["text"]) + def test_prefers_local_planfile_before_importable_module_from_active_env(self) -> None: with tempfile.TemporaryDirectory() as tmp_dir: root = Path(tmp_dir) From 29aeee66d47d1081f5f9dbed6845f3d4bad102c3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 18 Jul 2026 06:58:54 +0000 Subject: [PATCH 3/3] Strengthen non-UTF8 queue probe tests --- tests/test_planfile_queue.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/test_planfile_queue.py b/tests/test_planfile_queue.py index 9d6a3379..5f16bb70 100644 --- a/tests/test_planfile_queue.py +++ b/tests/test_planfile_queue.py @@ -40,6 +40,16 @@ def test_structured_queue_probe_handles_non_utf8_bytes(self) -> None: ) as run: self.assertTrue(_planfile_supports_structured_queue_json("/tmp/planfile")) self.assertFalse(run.call_args.kwargs["text"]) + _planfile_supports_structured_queue_json.cache_clear() + with patch( + "koru.queue.ticket.subprocess.run", + return_value=SimpleNamespace( + returncode=0, + stdout=b"planfile wersja \xf3 0.1.99", + stderr=b"", + ), + ): + self.assertFalse(_planfile_supports_structured_queue_json("/tmp/planfile-old")) def test_configured_command_probe_handles_non_utf8_module_missing_marker(self) -> None: _configured_planfile_cmd_usable.cache_clear() @@ -53,6 +63,16 @@ def test_configured_command_probe_handles_non_utf8_module_missing_marker(self) - ) as run: self.assertFalse(_configured_planfile_cmd_usable("/tmp/venv/bin/python -m planfile.cli")) self.assertFalse(run.call_args.kwargs["text"]) + _configured_planfile_cmd_usable.cache_clear() + with patch( + "koru.queue.ticket.subprocess.run", + return_value=SimpleNamespace( + returncode=0, + stdout=b"\xf3 planfile 0.1.101", + stderr=b"", + ), + ): + self.assertTrue(_configured_planfile_cmd_usable("/tmp/venv/bin/python -m planfile.cli")) def test_prefers_local_planfile_before_importable_module_from_active_env(self) -> None: with tempfile.TemporaryDirectory() as tmp_dir: