From f3edd0e5383810231bbdc15fab8cb88a1560c662 Mon Sep 17 00:00:00 2001 From: iso2kx <8766057+iso2kx@users.noreply.github.com> Date: Fri, 31 Jul 2026 11:46:47 +0800 Subject: [PATCH] fix(desktop): use the canonical gateway matcher for the preflight exemption _is_pausable_gateway() hand-rolled a second gateway parser and regressed a valid form: in `--profile gateway gateway run` the profile VALUE shadowed the subcommand token, so the scan reported that gateway as a fatal preflight holder. Delegate to gateway.status.looks_like_gateway_command_line() - profile-selector aware, shlex-tokenizing, run-only - so the preflight exemption, the pause discovery, and the updater's guard fallback share one parser. Non-run gateway subcommands, serve backends, and REPLs still block; the bare-`gateway` form now classifies as a running gateway, mirroring the canonical matcher's contract. --- hermes_cli/_scan_venv_blockers.py | 19 ++++++++++++------- tests/hermes_cli/test_scan_venv_blockers.py | 8 ++++++-- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/hermes_cli/_scan_venv_blockers.py b/hermes_cli/_scan_venv_blockers.py index 74940fae9a320..9673ca1ac192e 100644 --- a/hermes_cli/_scan_venv_blockers.py +++ b/hermes_cli/_scan_venv_blockers.py @@ -109,16 +109,21 @@ def _is_pausable_gateway(cmdline: str) -> bool: venv (an operator's REPL, a stray script, a ``serve`` backend that survived the desktop's own teardown) has no pause machinery downstream and must keep blocking the handoff. + + Delegates to ``gateway.status.looks_like_gateway_command_line`` — the + canonical ``gateway run`` matcher (profile-selector aware, shlex + tokenization, ``run``-only) — so this exemption, the pause discovery, + and the updater's guard fallback all share one parser. A hand-rolled + token scan here regressed ``--profile gateway gateway run``: the profile + *value* shadowed the subcommand token. An import failure counts as + not-pausable — the scan then reports the process as a blocker, which is + exactly the pre-exemption behavior. """ - lowered = cmdline.lower() - if "hermes_cli.main" not in lowered: - return False try: - tokens = [t for t in lowered.split() if t] - idx = tokens.index("gateway") - except ValueError: + from gateway.status import looks_like_gateway_command_line # noqa: PLC0415 + except Exception: return False - return len(tokens) > idx + 1 and tokens[idx + 1] == "run" + return looks_like_gateway_command_line(cmdline) def main() -> None: diff --git a/tests/hermes_cli/test_scan_venv_blockers.py b/tests/hermes_cli/test_scan_venv_blockers.py index 72f109c58d569..08142502d68c9 100644 --- a/tests/hermes_cli/test_scan_venv_blockers.py +++ b/tests/hermes_cli/test_scan_venv_blockers.py @@ -118,6 +118,12 @@ def test_redact_short_flags_not_redacted() -> None: " -m hermes_cli.main gateway run --replace", # profile-scoped gateway "python.exe -m hermes_cli.main --profile work gateway run", + # a profile literally NAMED "gateway" — the profile value must not + # shadow the subcommand token (the hand-rolled matcher regressed this) + "python.exe -m hermes_cli.main --profile gateway gateway run", + "python.exe -m hermes_cli.main -p gateway gateway run", + # bare `gateway` defaults to `run` (mirrors the canonical matcher) + "python.exe -m hermes_cli.main gateway", # case variations survive "PYTHON.EXE -m hermes_cli.main GATEWAY RUN", ], @@ -138,8 +144,6 @@ def test_is_pausable_gateway_accepts_gateway_run_chains(cmdline: str) -> None: # operator REPL / stray script "python.exe", "python.exe myscript.py gateway run", # not a hermes_cli.main invocation - # 'gateway' as a trailing word with nothing after it - "python.exe -m hermes_cli.main gateway", "", ], )