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.
This commit is contained in:
teknium1
2026-09-20 01:08:35 -07:00
committed by Teknium
parent 35accdbc30
commit 5195c13873
2 changed files with 23 additions and 2 deletions

View File

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

View File

@@ -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