From f9908e2ed641b0d071fed13f5008cad5c8da9392 Mon Sep 17 00:00:00 2001 From: Lime-oss-hash Date: Sat, 29 Aug 2026 19:30:54 +1200 Subject: [PATCH] fix(bot-mode): avoid inherited stdin on Windows Query-file DM transports do not consume stdin. Use DEVNULL for both the initial attempt and policy-gated retry so Git Bash cannot pass an invalid pseudo-handle to Windows subprocess creation. --- tests/tools/test_bot_mode_dm.py | 30 ++++++++++++++++++++++++++++++ tools/bot_mode_dm.py | 2 ++ 2 files changed, 32 insertions(+) diff --git a/tests/tools/test_bot_mode_dm.py b/tests/tools/test_bot_mode_dm.py index 61d53dc189..cc196c2982 100644 --- a/tests/tools/test_bot_mode_dm.py +++ b/tests/tools/test_bot_mode_dm.py @@ -390,6 +390,36 @@ def test_delivery_runner_preserves_child_failure_and_unlinks(tmp_path): assert not dm_file.exists() +def test_query_file_delivery_closes_stdin_for_initial_attempt_and_retry( + tmp_path, monkeypatch +): + dm_file = tmp_path / "message.txt" + dm_file.write_text("secret", encoding="utf-8") + calls = [] + responses = [ + subprocess.CompletedProcess([], 1, stdout="", stderr="HTTP 429 rate limit"), + subprocess.CompletedProcess([], 0, stdout="", stderr=""), + ] + + def fake_run(argv, **kwargs): + calls.append((argv, kwargs)) + return responses.pop(0) + + monkeypatch.setattr(subprocess, "run", fake_run) + + returncode = bot_mode_dm._run_delivery( + ["hermes", "-p", "researcher"], str(dm_file), stdin_file=False + ) + + assert returncode == 0 + assert len(calls) == 2 + assert [kwargs["stdin"] for _argv, kwargs in calls] == [ + subprocess.DEVNULL, + subprocess.DEVNULL, + ] + assert not dm_file.exists() + + @pytest.mark.parametrize("args", [[], ["--run-delivery"], ["--run-delivery", "bad", "x"]]) def test_delivery_main_rejects_invalid_cli(args): assert bot_mode_dm._delivery_main(args) == 2 diff --git a/tools/bot_mode_dm.py b/tools/bot_mode_dm.py index 0cce0cfee6..46879344c5 100644 --- a/tools/bot_mode_dm.py +++ b/tools/bot_mode_dm.py @@ -572,6 +572,7 @@ def _run_delivery(argv: list[str], dm_file: str, *, stdin_file: bool) -> int: proc = subprocess.run( [*argv, "--query-file", dm_file], check=False, + stdin=subprocess.DEVNULL, capture_output=True, text=True, ) @@ -587,6 +588,7 @@ def _run_delivery(argv: list[str], dm_file: str, *, stdin_file: bool) -> int: proc = subprocess.run( [*argv, "--query-file", dm_file], check=False, + stdin=subprocess.DEVNULL, capture_output=True, text=True, )