From 5195c138738cfd186489af1b9993484ddfa15586 Mon Sep 17 00:00:00 2001 From: teknium1 <127238744+teknium1@users.noreply.github.com> Date: Sun, 20 Sep 2026 01:08:35 -0700 Subject: [PATCH] fix: verify-on-stop recognises python.exe / py launcher interpreters (review follow-up) _is_interpreter_token matched Path(token).name against the bare-name regex, so a Windows venv path `...\Scripts\python.exe` (and the `py` launcher) recorded no ad-hoc evidence and the nudge loop the PR closes stayed open on that platform. Strip a case-insensitive .exe/.bat/.cmd suffix, take the basename across backslashes, and accept `py`. Parametrized invariant test, red before. --- agent/verification_evidence.py | 8 ++++++-- tests/agent/test_verification_evidence.py | 17 +++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/agent/verification_evidence.py b/agent/verification_evidence.py index ee1c6ab3c9..f514772a5e 100644 --- a/agent/verification_evidence.py +++ b/agent/verification_evidence.py @@ -26,7 +26,9 @@ _MAX_TOTAL_UNREFERENCED_EVENTS = 10_000 _AD_HOC_SCRIPT_NAME_PREFIXES = ("hermes-verify-", "hermes-ad-hoc-") _VERIFY_SCHEMA_VERSION = 1 -_INTERPRETERS = {"python", "python3", "node", "bash", "sh", "ruby", "perl"} +_INTERPRETERS = {"python", "python3", "py", "node", "bash", "sh", "ruby", "perl"} +# Windows spells the same interpreters `python.exe` / `py.exe` (a venv's absolute Scripts path). +_WINDOWS_EXE_SUFFIX_RE = re.compile(r"\.(?:exe|bat|cmd)$", re.IGNORECASE) # The same interpreter is reached as `python3`, `python3.12`, `/usr/bin/python3.12`, or # `env python3`. Matching only the bare token recorded no evidence for the invocation shapes # the verify-on-stop nudge itself hands the agent, so a passing run left the workspace @@ -316,7 +318,9 @@ def _is_interpreter_token(token: str) -> bool: only the bare token left the ad-hoc branch blind to the invocation shapes the nudge hands the agent, so a passing run recorded no evidence and the workspace stayed ``unverified``. """ - return bool(_INTERPRETER_NAME_RE.match(Path(token).name)) + # Basename by hand: on POSIX ``Path`` treats a Windows backslash path as one component. + name = token.replace("\\", "/").rsplit("/", 1)[-1] + return bool(_INTERPRETER_NAME_RE.match(_WINDOWS_EXE_SUFFIX_RE.sub("", name))) def _ad_hoc_script_args(tokens: list[str], root: str | Path | None) -> Optional[list[str]]: diff --git a/tests/agent/test_verification_evidence.py b/tests/agent/test_verification_evidence.py index c62ad493bc..6a7124d891 100644 --- a/tests/agent/test_verification_evidence.py +++ b/tests/agent/test_verification_evidence.py @@ -374,6 +374,23 @@ def test_recording_expires_old_edit_only_state(tmp_path, monkeypatch): assert status["changed_paths"] == [] +@pytest.mark.parametrize( + "interpreter", + [r"C:\Users\me\venv\Scripts\python.exe", "python.EXE", "py -3"], +) +def test_windows_exe_interpreter_records_ad_hoc_evidence(tmp_path, monkeypatch, interpreter): + """A venv's absolute ``Scripts\\python.exe`` (or the ``py`` launcher) is the interpreter shape + Windows hands the agent; ``.exe`` must not hide it from the ad-hoc branch (review follow-up).""" + from agent.verification_evidence import _find_ad_hoc_match + + monkeypatch.setattr( + "agent.verification_evidence._is_temp_script_path", + lambda token, root: "hermes-verify-" in token and token.endswith(".py"), + ) + win_script = r"C:\Users\me\AppData\Local\Temp\hermes-verify-x.py" + assert _find_ad_hoc_match(f"{interpreter} {win_script}", tmp_path) == [] + + def test_windows_backslash_ad_hoc_script_path_is_matched(tmp_path, monkeypatch): """Ad-hoc verification scripts with Windows backslash paths must be matched by ``_find_ad_hoc_match`` trying ``posix=False`` in addition to