diff --git a/agent/credential_pool_timestamps.py b/agent/credential_pool_timestamps.py index 6cb3cafdb3..340e4f8ebe 100644 --- a/agent/credential_pool_timestamps.py +++ b/agent/credential_pool_timestamps.py @@ -25,7 +25,7 @@ def _parse_absolute_timestamp(value: Any) -> Optional[float]: except ValueError: pass try: - return datetime.fromisoformat(raw.replace("Z", "+00:00")).timestamp() + return datetime.fromisoformat(raw).timestamp() except ValueError: return None return None diff --git a/evals/desktop_gateway_ticket_native/run.py b/evals/desktop_gateway_ticket_native/run.py index b457322bf7..c8566d8f2a 100644 --- a/evals/desktop_gateway_ticket_native/run.py +++ b/evals/desktop_gateway_ticket_native/run.py @@ -16,7 +16,7 @@ import psutil REPO = Path(__file__).resolve().parents[2] sys.path.insert(0, str(REPO)) -from hermes_cli.gateway_runtime_discovery import query_identify # noqa: E402 +from hermes_cli.gateway_runtime_discovery import query_identify logger = logging.getLogger(__name__) diff --git a/evals/desktop_gateway_ticket_native/run_multiplex.py b/evals/desktop_gateway_ticket_native/run_multiplex.py index 4d4179bab1..0299198694 100644 --- a/evals/desktop_gateway_ticket_native/run_multiplex.py +++ b/evals/desktop_gateway_ticket_native/run_multiplex.py @@ -16,8 +16,8 @@ import time REPO = Path(__file__).resolve().parents[2] sys.path.insert(0, str(REPO)) -from evals.desktop_gateway_ticket_native.run import stop_owned # noqa: E402 -from hermes_cli.gateway_runtime_discovery import query_identify # noqa: E402 +from evals.desktop_gateway_ticket_native.run import stop_owned +from hermes_cli.gateway_runtime_discovery import query_identify logger = logging.getLogger(__name__) diff --git a/evals/hosted_attachment_transfer/bench.py b/evals/hosted_attachment_transfer/bench.py index de28aef8af..28dfd9e48e 100644 --- a/evals/hosted_attachment_transfer/bench.py +++ b/evals/hosted_attachment_transfer/bench.py @@ -32,7 +32,7 @@ TMP = Path(tempfile.mkdtemp(prefix='hosted-attachment-bench-')) os.environ['HOME'] = str(TMP) os.environ['HERMES_HOME'] = str(TMP / '.hermes') -from pytest import MonkeyPatch # noqa: E402 +from pytest import MonkeyPatch class SourceMeter: diff --git a/evals/shared_gateway/search_readonly_ab.py b/evals/shared_gateway/search_readonly_ab.py index a9bf06f34b..ec6b2eace2 100644 --- a/evals/shared_gateway/search_readonly_ab.py +++ b/evals/shared_gateway/search_readonly_ab.py @@ -72,7 +72,7 @@ def main(): 'PYTHONDONTWRITEBYTECODE': '1'} proc = subprocess.run([sys.executable, str(Path(__file__).resolve()), '--child', str(repo), '--case', case, '--account', tmp], cwd=repo, env=env, - stdin=subprocess.DEVNULL, capture_output=True, text=True, timeout=45) + stdin=subprocess.DEVNULL, capture_output=True, text=True, timeout=45, check=False) if proc.returncode: raise RuntimeError(f'{label}/{case} failed: {proc.stderr}') row = json.loads(proc.stdout.strip().splitlines()[-1]) diff --git a/evals/shared_gateway/session_readers_cli_ab.py b/evals/shared_gateway/session_readers_cli_ab.py index 9fb6190491..d3e1c33487 100644 --- a/evals/shared_gateway/session_readers_cli_ab.py +++ b/evals/shared_gateway/session_readers_cli_ab.py @@ -40,7 +40,7 @@ for (label, tree), (name, command), populated in itertools.product(TREES.items() env.update(HOME=tmp, USERPROFILE=tmp, HERMES_HOME=str(home), PYTHONPATH=str(tree)) def run(args): return subprocess.run([PYTHON, *args], cwd=tree, env=env, stdin=subprocess.DEVNULL, - capture_output=True, text=True, timeout=45) + capture_output=True, text=True, timeout=45, check=False) if populated: seed = run(['-c', "from hermes_state import SessionDB; db=SessionDB(); db.create_session('reader-fixture',source='tui'); db.append_message('reader-fixture',role='user',content='fixture'); db.set_session_pinned('reader-fixture',True); db.close()"]) assert seed.returncode == 0, seed.stderr diff --git a/gateway/hosted_room_attachments.py b/gateway/hosted_room_attachments.py index 52000dc58f..debcad6a9a 100644 --- a/gateway/hosted_room_attachments.py +++ b/gateway/hosted_room_attachments.py @@ -1266,21 +1266,21 @@ class HostedRoomAttachmentStore: __all__ = [ + "CLASSIC_ATTACHMENT_TTL_SECONDS", + "DISBANDED_GRACE_SECONDS", + "MAX_ATTACHMENTS_PER_MESSAGE", + "MAX_ATTACHMENT_BYTES", + "MAX_MESSAGE_ATTACHMENT_BYTES", + "MAX_TASK_ATTACHMENTS", + "MAX_TASK_ATTACHMENT_BYTES", + "UNCOMMITTED_TTL_SECONDS", "AttachmentConflictError", "AttachmentData", "AttachmentError", "AttachmentIntegrityError", "AttachmentNotFoundError", "AttachmentQuotaError", - "CLASSIC_ATTACHMENT_TTL_SECONDS", - "DISBANDED_GRACE_SECONDS", "HostedRoomAttachmentStore", - "MAX_ATTACHMENT_BYTES", - "MAX_ATTACHMENTS_PER_MESSAGE", - "MAX_MESSAGE_ATTACHMENT_BYTES", - "MAX_TASK_ATTACHMENT_BYTES", - "MAX_TASK_ATTACHMENTS", - "UNCOMMITTED_TTL_SECONDS", "decode_content_base64", "default_attachment_root", "encode_content_base64", diff --git a/gateway/platforms/msgraph_webhook.py b/gateway/platforms/msgraph_webhook.py index 5e974634bb..ff0c7e2b9b 100644 --- a/gateway/platforms/msgraph_webhook.py +++ b/gateway/platforms/msgraph_webhook.py @@ -25,6 +25,7 @@ from gateway.platforms.base import ( BasePlatformAdapter, SendResult, is_network_accessible, ) from gateway.platforms.event import MessageEvent, MessageType +from datetime import UTC logger = logging.getLogger(__name__) @@ -341,7 +342,7 @@ class MSGraphWebhookAdapter(BasePlatformAdapter): source = event.source.to_dict() source['is_bot'] = event.source.is_bot envelope = {'source': source, 'route': route, 'provenance': provenance, - 'timestamp': datetime.fromtimestamp(0, timezone.utc).isoformat(), + 'timestamp': datetime.fromtimestamp(0, UTC).isoformat(), 'event': {'message_id': event.message_id}, 'automation': {'identity': event.message_id, 'owner': ref.session_id}} row = admit_session_input(authority.db, epoch=authority.epoch, diff --git a/gateway/relay/adapter.py b/gateway/relay/adapter.py index 5e857f066a..9b7a898571 100644 --- a/gateway/relay/adapter.py +++ b/gateway/relay/adapter.py @@ -35,6 +35,7 @@ from gateway.relay.egress import ( ) from gateway.relay.media import RelayMediaClient from gateway.relay.transport import RelayTransport +from datetime import UTC logger = logging.getLogger(__name__) @@ -895,7 +896,7 @@ class RelayAdapter(RelayDiscordInteractionMixin, BasePlatformAdapter): from gateway.platforms.webhook_ingress import admit_producer # The connector does not transmit a provider timestamp. Local arrival # time must not change the fingerprint of a retried provider identity. - event.timestamp = datetime.fromtimestamp(0, timezone.utc) + event.timestamp = datetime.fromtimestamp(0, UTC) # Wait only for the SQLite receipt, never inference or outbound ACKs # (those need this same WS reader). Exceptions deliberately suppress ACK. if await admit_producer(self, event) is None and getattr(event, '_consumer_reply', None): diff --git a/gateway/relay/adapter_discord_interaction.py b/gateway/relay/adapter_discord_interaction.py index 0dc2605121..1c83793831 100644 --- a/gateway/relay/adapter_discord_interaction.py +++ b/gateway/relay/adapter_discord_interaction.py @@ -27,7 +27,7 @@ class RelayDiscordInteractionMixin: session key matches the one the follow-up capability was bound under.""" try: payload = json.loads(bytes(getattr(forward, "body", b"")).decode("utf-8")) - except Exception: # noqa: BLE001 + except Exception: return None if not isinstance(payload, dict): return None diff --git a/gateway/run_inbound_hooks.py b/gateway/run_inbound_hooks.py index cb939b1813..bccc2b5cd9 100644 --- a/gateway/run_inbound_hooks.py +++ b/gateway/run_inbound_hooks.py @@ -24,8 +24,8 @@ class GatewayInboundHooksMixin: """Hook, quick-command and plugin-command dispatch for ``GatewayInboundMixin``.""" async def _hm_post_admission_consume( - self, event: "MessageEvent", source: SessionSource, session_key: str - ) -> Tuple[bool, Optional[str]]: + self, event: MessageEvent, source: SessionSource, session_key: str + ) -> tuple[bool, Optional[str]]: """``post_gateway_admission`` exactly once per inbound message: ``(consumed, reply)``. Never while an admitted row EXECUTES (first run or restart replay): the authority path @@ -38,8 +38,8 @@ class GatewayInboundHooksMixin: return await run_post_admission_hook(self, event, source, session_key) async def _hm_pre_gateway_dispatch_hook( - self, event: "MessageEvent", source: SessionSource - ) -> Optional["MessageEvent"]: + self, event: MessageEvent, source: SessionSource + ) -> Optional[MessageEvent]: """Run the ``pre_gateway_dispatch`` plugin hook; None = drop, else the (maybe rewritten) event. Results: ``{"action": "skip"}`` → drop; ``{"action": "rewrite", "text"}`` → replace ``event.text``; ``allow``/None → normal dispatch. Runs BEFORE auth so plugins can handle unauthorized senders.""" @@ -81,7 +81,7 @@ class GatewayInboundHooksMixin: return qc if isinstance(qc, dict) else {} @staticmethod - def _hm_expand_alias_quick_command(event: "MessageEvent", qcmd: dict) -> Optional[str]: + def _hm_expand_alias_quick_command(event: MessageEvent, qcmd: dict) -> Optional[str]: """Rewrite ``event.text`` to an alias quick command's target; returns the new command name.""" target = (qcmd.get("target") or "").strip() if not target: @@ -92,8 +92,8 @@ class GatewayInboundHooksMixin: return target_command.split()[0] if target_command else target_command async def _hm_command_hooks( - self, event: "MessageEvent", source: SessionSource, _quick_key: str, command: str, canonical: str - ) -> Tuple[bool, Optional[str], Optional[str]]: + self, event: MessageEvent, source: SessionSource, _quick_key: str, command: str, canonical: str + ) -> tuple[bool, Optional[str], Optional[str]]: """Fire ``pre_command`` (observer) and ``command:`` (interceptor) hooks → ``(handled, result, new_command)`` (``new_command`` set when a handler rewrote the command). The running-agent path deliberately does NOT fire these — a slow or hostile plugin must not @@ -159,8 +159,8 @@ class GatewayInboundHooksMixin: return t("gateway.quick_command.error", error=e) async def _hm_dispatch_quick_and_plugin_commands( - self, event: "MessageEvent", source: SessionSource, command: Optional[str] - ) -> Tuple[bool, Optional[str], Optional[str]]: + self, event: MessageEvent, source: SessionSource, command: Optional[str] + ) -> tuple[bool, Optional[str], Optional[str]]: """Drain gate, user-defined quick commands (exec/alias) and plugin slash commands → ``(handled, result, command)``; an alias quick command rewrites ``command``.""" if self._draining: diff --git a/gateway/run_notifications_update.py b/gateway/run_notifications_update.py index 6785413b86..17db7cda7d 100644 --- a/gateway/run_notifications_update.py +++ b/gateway/run_notifications_update.py @@ -89,7 +89,7 @@ class GatewayUpdateNotificationsMixin: logger.debug("Skipping update notification watcher: no running event loop") @classmethod - def _update_paths(cls) -> "GatewayUpdateNotificationsMixin._UpdatePaths": + def _update_paths(cls) -> GatewayUpdateNotificationsMixin._UpdatePaths: from gateway.run import _hermes_home return cls._UpdatePaths( pending=_hermes_home / ".update_pending.json", @@ -130,7 +130,7 @@ class GatewayUpdateNotificationsMixin: now = datetime.now(stamped.tzinfo) if stamped.tzinfo else datetime.now() return (now - stamped).total_seconds() - def _resolve_update_target(self, paths: "_UpdatePaths") -> Optional["_UpdateTarget"]: + def _resolve_update_target(self, paths: _UpdatePaths) -> Optional[_UpdateTarget]: """Resolve adapter/chat/session for update watcher messages from the pending marker.""" for path in (paths.claimed, paths.pending): if not path.exists(): @@ -162,7 +162,7 @@ class GatewayUpdateNotificationsMixin: reply_to_message_id=data.get("message_id"), adapter=adapter, ) - async def _watch_update_completion_only(self, paths: "_UpdatePaths", deadline: float, poll_interval: float) -> None: + async def _watch_update_completion_only(self, paths: _UpdatePaths, deadline: float, poll_interval: float) -> None: """Fallback when no adapter/chat can be resolved: wait for the exit code, then notify.""" logger.warning("Update watcher: cannot resolve adapter/chat_id, falling back to completion-only") # Poll until _send_update_notification delivers (it returns False while the platform reconnects). @@ -176,7 +176,7 @@ class GatewayUpdateNotificationsMixin: await self._send_update_notification() @staticmethod - def _update_exit_code(paths: "_UpdatePaths") -> int: + def _update_exit_code(paths: _UpdatePaths) -> int: return int(paths.exit_code.read_text(encoding="utf-8-sig").strip() or "1") @staticmethod @@ -190,7 +190,7 @@ class GatewayUpdateNotificationsMixin: return "", len(data) return data[offset:].decode("utf-8", errors="replace"), len(data) - async def _send_update_output(self, target: "_UpdateTarget", text: str) -> None: + async def _send_update_output(self, target: _UpdateTarget, text: str) -> None: """Send buffered update output as fenced chunks that fit message limits (Telegram: 4096).""" from tools.ansi_strip import strip_ansi clean = strip_ansi(text).strip() @@ -201,7 +201,7 @@ class GatewayUpdateNotificationsMixin: with _log_suppressed(logging.DEBUG, "Update stream send failed: %s"): await target.send(f"```\n{clean[i:i + max_chunk]}\n```") - async def _forward_update_prompt(self, target: "_UpdateTarget", prompt_text: str, default: str) -> None: + async def _forward_update_prompt(self, target: _UpdateTarget, prompt_text: str, default: str) -> None: """Forward an update prompt: platform-native buttons first (Discord, Telegram), else text.""" sent_buttons = False adapter = target.adapter @@ -220,7 +220,7 @@ class GatewayUpdateNotificationsMixin: self._session_state(target.session_key).persistent.update_prompt_pending = True logger.info("Forwarded update prompt to %s: %s", target.session_key, prompt_text[:80]) - def _clear_update_markers(self, paths: "_UpdatePaths", session_key: Optional[str]) -> None: + def _clear_update_markers(self, paths: _UpdatePaths, session_key: Optional[str]) -> None: paths.unlink_all() state = self._peek_session_state(session_key) if state is not None: diff --git a/gateway/run_runtime_init.py b/gateway/run_runtime_init.py index 16591b2f4c..3e75e19c4b 100644 --- a/gateway/run_runtime_init.py +++ b/gateway/run_runtime_init.py @@ -115,13 +115,13 @@ class GatewayRuntimeInitMixin: self._platform_lock_takeover_on_start = False # Capped LRU of live SessionSources for fallback routing (shutdown notices, synthetic events) when # the persisted origin is missing and _parse_session_key can't recover thread_id. - self._session_sources: "OrderedDict[str, SessionSource]" = OrderedDict() + self._session_sources: OrderedDict[str, SessionSource] = OrderedDict() self._session_sources_max = 512 # Lifecycle-scoped completion dedup: closes queue/watcher races inside one gateway without claiming # exactly-once across a crash; durable replay state stays owned by tools.async_delegation. self._completion_delivery_lock = threading.Lock() self._completion_deliveries_inflight: set[tuple[str, str, object]] = set() - self._completion_deliveries_delivered: "OrderedDict[tuple[str, str, object], None]" = OrderedDict() + self._completion_deliveries_delivered: OrderedDict[tuple[str, str, object], None] = OrderedDict() self._completion_delivery_retention = 2048 # Agent-triggered terminal completions from one conversation often land in the same scheduler # tick; hold them briefly so the agent gets one synthetic turn instead of one per process. @@ -137,7 +137,7 @@ class GatewayRuntimeInitMixin: from gateway.run import (Any, Dict, Optional, OrderedDict, Platform, threading) # AIAgent per session preserves prompt caching (fresh agent per message ~10x cost on Anthropic). # Value: (AIAgent, config_signature); LRU cap in _enforce_agent_cache_cap, TTL in expiry watcher. - self._agent_cache: "OrderedDict[str, tuple]" = OrderedDict() + self._agent_cache: OrderedDict[str, tuple] = OrderedDict() self._agent_cache_lock = threading.Lock() # Launch-time identity of the profile that owns ``self.adapters``; ``_authorization_adapter`` # compares against this rather than the per-turn ``_active_profile_name()``. A multiplex @@ -212,7 +212,7 @@ class GatewayRuntimeInitMixin: from gateway.pairing import PairingStore from gateway.hooks import ProfileHookRegistries self.pairing_store = PairingStore() - self.pairing_stores: Dict[str, "PairingStore"] = {} + self.pairing_stores: Dict[str, PairingStore] = {} # One HookRegistry per served profile home, resolved from the active scope at emit time. self.hooks = ProfileHookRegistries() # Per-chat voice reply mode: "off" | "voice_only" | "all" diff --git a/gateway/run_turn_hygiene.py b/gateway/run_turn_hygiene.py index 82c6f7991b..2688b6ca6f 100644 --- a/gateway/run_turn_hygiene.py +++ b/gateway/run_turn_hygiene.py @@ -32,8 +32,8 @@ from typing import Any, Callable, Dict, List, Optional, Tuple from utils import base_url_hostname if TYPE_CHECKING: # string annotations only; never imported at runtime (cycle) - from gateway.run import GatewayRunner # noqa: F401 - from gateway.run_turn_runner import TurnRunner # noqa: F401 + from gateway.run import GatewayRunner + from gateway.run_turn_runner import TurnRunner # Log-record parity with the origin module. logger = logging.getLogger("gateway.run") @@ -237,7 +237,7 @@ class GatewayTurnHygieneMixin: fence = attempt.commit_fence while True: if fence.is_cancelled: - raise asyncio.TimeoutError + raise TimeoutError # Charge the idle budget from the LAST PROGRESS event, else silence can approach 2x timeout. _hyg_waited = time.monotonic() - attempt.wait_started _slice = min( @@ -254,7 +254,7 @@ class GatewayTurnHygieneMixin: try: _compressed, _ = await asyncio.wait_for(asyncio.shield(attempt.future), timeout=_slice) return _compressed - except asyncio.TimeoutError: + except TimeoutError: if fence.is_cancelled: raise _hyg_waited = time.monotonic() - attempt.wait_started @@ -746,7 +746,7 @@ class GatewayTurnHygieneMixin: _compressed = await self._hmwa_hygiene_wait_for_summary(attempt, hs, session_entry) except HygieneTurnHoldExceeded: _compressed = await self._hmwa_hygiene_on_turn_hold(attempt, hs, session_entry, session_key, source) - except asyncio.TimeoutError: + except TimeoutError: _compressed = await self._hmwa_hygiene_on_timeout(attempt, hs, session_entry, session_key, source) except BaseException: self._hmwa_hygiene_on_unwind(attempt, hs, session_entry, session_key) diff --git a/gateway/run_turn_persistence.py b/gateway/run_turn_persistence.py index 8f05035f68..4ad15f6fec 100644 --- a/gateway/run_turn_persistence.py +++ b/gateway/run_turn_persistence.py @@ -34,8 +34,8 @@ from typing import Any, Callable, Dict, List, Optional, Tuple from utils import base_url_hostname if TYPE_CHECKING: # string annotations only; never imported at runtime (cycle) - from gateway.run import GatewayRunner # noqa: F401 - from gateway.run_turn_runner import TurnRunner # noqa: F401 + from gateway.run import GatewayRunner + from gateway.run_turn_runner import TurnRunner # Log-record parity with the origin module. logger = logging.getLogger("gateway.run") diff --git a/gateway/run_turn_progress.py b/gateway/run_turn_progress.py index b782a928b3..ef7aa5dcf1 100644 --- a/gateway/run_turn_progress.py +++ b/gateway/run_turn_progress.py @@ -24,8 +24,8 @@ from hermes_cli.config import cfg_get from utils import is_truthy_value if TYPE_CHECKING: # string annotations only; never imported at runtime (cycle) - from gateway.run import GatewayRunner # noqa: F401 - from gateway.run_turn_runner import TurnRunner # noqa: F401 + from gateway.run import GatewayRunner + from gateway.run_turn_runner import TurnRunner # Log-record parity with the origin module. logger = logging.getLogger("gateway.run") @@ -125,7 +125,7 @@ class GatewayTurnProgressMixin: # ── progress_callback (agent thread → progress queue) ─────────────────────────────────── - def progress_callback(self, event_type: str, tool_name: str = None, preview: str = None, args: dict = None, **kwargs): + def progress_callback(self, event_type: str, tool_name: str | None = None, preview: str | None = None, args: dict | None = None, **kwargs): """Callback invoked by agent on tool lifecycle events.""" ctx = self._ctx # Failed subagent → one clean user-facing notice, handled FIRST, before every progress-queue @@ -329,8 +329,8 @@ class GatewayTurnProgressMixin: class _TaskCardState: """Task-card rail state for ``_send_native_task_card_progress``.""" adapter: Any - tasks: Dict[str, Dict[str, str]] = dataclasses.field(default_factory=dict) - task_order: List[str] = dataclasses.field(default_factory=list) + tasks: dict[str, dict[str, str]] = dataclasses.field(default_factory=dict) + task_order: list[str] = dataclasses.field(default_factory=list) fallback_msg_id: Optional[str] = None native_failed: bool = False # TERMINAL for the turn, distinct from native_failed: no later publication @@ -347,7 +347,7 @@ class GatewayTurnProgressMixin: text = re.sub(r"\s+", " ", str(value or "")).strip() return text if len(text) <= limit else text[: limit - 3].rstrip() + "..." - def visible_tasks(self) -> List[Dict[str, str]]: + def visible_tasks(self) -> list[dict[str, str]]: return [self.tasks[task_id] for task_id in self.task_order[-8:]] def fallback_text(self) -> str: @@ -358,7 +358,7 @@ class GatewayTurnProgressMixin: for task in self.visible_tasks()] return t("gateway.progress.task_card_title") + "\n" + "\n".join(lines) - def _upsert(self, call_id: str, title: str) -> Dict[str, str]: + def _upsert(self, call_id: str, title: str) -> dict[str, str]: if call_id not in self.tasks: self.task_order.append(call_id) self.tasks[call_id] = {"id": call_id, "title": self._compact(title), "status": "in_progress"} @@ -548,7 +548,7 @@ class GatewayTurnProgressMixin: _PROGRESS_TEXT_LIMIT: int _edit_accepts_metadata: bool - def _progress_edit_state(self, adapter) -> "TurnRunner._ProgressEditState": + def _progress_edit_state(self, adapter) -> TurnRunner._ProgressEditState: ctx = self._ctx len_fn = adapter.message_len_fn if isinstance(adapter, BasePlatformAdapter) else len try: diff --git a/gateway/runtime_downgrade_exit.py b/gateway/runtime_downgrade_exit.py index c49c2b564b..b1460cd387 100644 --- a/gateway/runtime_downgrade_exit.py +++ b/gateway/runtime_downgrade_exit.py @@ -40,9 +40,9 @@ def downgrade_reason(skew, *, update_in_progress: bool, predates: Callable[[], b """The log line when this gateway must stop for a downgraded tree, else None.""" if not skew or update_in_progress or not predates(): return None - return ("This gateway loaded %s, but its checkout is now at %s, a release that predates the gateway " + return ("This gateway loaded {}, but its checkout is now at {}, a release that predates the gateway " "runtime. Downgrading is unsupported; stopping (no restart) so the older release can run its " - "own gateway." % tuple(skew)) + "own gateway.".format(*tuple(skew))) async def _stop_or_exit(runner, *, hard_exit: Callable[[int], None] = os._exit) -> None: diff --git a/gateway/session.py b/gateway/session.py index 83aca43b99..4f4f895c92 100644 --- a/gateway/session.py +++ b/gateway/session.py @@ -21,7 +21,7 @@ from gateway.session_recovery import SessionRecoveryMixin from gateway.session_lifecycle import SessionLifecycleMixin, _iso, _new_session_id, _now, _parse_iso from gateway.session_transcript import SessionTranscriptMixin # Inverse of _session_key_namespace, defined storage-side (hermes_state must not import gateway). -from hermes_state_keys import profile_from_session_key_namespace # noqa: F401 +from hermes_state_keys import profile_from_session_key_namespace logger = logging.getLogger(__name__) diff --git a/gateway/session_execution.py b/gateway/session_execution.py index e619d3ca6f..918f25b108 100644 --- a/gateway/session_execution.py +++ b/gateway/session_execution.py @@ -23,7 +23,7 @@ from hermes_cli.config import cfg_get from utils import is_truthy_value if TYPE_CHECKING: # string annotations only; never imported at runtime (cycle) - from gateway.run import GatewayRunner # noqa: F401 + from gateway.run import GatewayRunner # Log-record parity with the origin module. logger = logging.getLogger("gateway.run") diff --git a/gateway/session_shell.py b/gateway/session_shell.py index 99e324defe..ee57e787fd 100644 --- a/gateway/session_shell.py +++ b/gateway/session_shell.py @@ -64,7 +64,7 @@ def _run(command, cwd): try: result = subprocess.run(command, cwd=cwd, shell=True, env=env, capture_output=True, text=True, encoding='utf-8', errors='replace', timeout=TIMEOUT_SECONDS, - stdin=subprocess.DEVNULL, creationflags=windows_hide_flags()) + stdin=subprocess.DEVNULL, creationflags=windows_hide_flags(), check=False) except subprocess.TimeoutExpired: raise RuntimeStoreError('command_timeout') from None except OSError: diff --git a/hermes_cli/config_show.py b/hermes_cli/config_show.py index f9e709495c..6135e2d982 100644 --- a/hermes_cli/config_show.py +++ b/hermes_cli/config_show.py @@ -45,7 +45,7 @@ _SHOW_CONFIG_API_KEYS = ( ("FAL_KEY", "FAL")) -def _show_model_section(config: Dict[str, Any]) -> None: +def _show_model_section(config: dict[str, Any]) -> None: _section("Model") print(f" Model: {_cfg.redact_config_value(config.get('model', 'not set'))}") cfg_max_turns = config.get('agent', {}).get('max_turns', _cfg.DEFAULT_CONFIG['agent']['max_turns']) @@ -61,7 +61,7 @@ def _show_model_section(config: Dict[str, Any]) -> None: f"(run 'hermes doctor --fix' to remove)", Colors.YELLOW)) -def _show_display_section(config: Dict[str, Any]) -> None: +def _show_display_section(config: dict[str, Any]) -> None: _section("Display") display = config.get('display', {}) try: @@ -69,7 +69,7 @@ def _show_display_section(config: Dict[str, Any]) -> None: active_personality = active_personality_name(config) or 'none' except Exception: active_personality = display.get('personality') or 'none' - on_off = lambda flag: 'on' if flag else 'off' # noqa: E731 + on_off = lambda flag: 'on' if flag else 'off' print(f" Personality: {active_personality}") print(f" Reasoning: {on_off(display.get('show_reasoning', True))}") print( @@ -80,14 +80,14 @@ def _show_display_section(config: Dict[str, Any]) -> None: print(f" User preview: first {ump.get('first_lines', 2)} line(s), last {ump.get('last_lines', 2)} line(s)") -def _show_terminal_section(config: Dict[str, Any]) -> None: +def _show_terminal_section(config: dict[str, Any]) -> None: _section("Terminal") terminal = config.get('terminal', {}) print(f" Backend: {terminal.get('backend', 'local')}") print(f" Working dir: {terminal.get('cwd', '.')}") print(f" Timeout: {terminal.get('timeout', 60)}s") - configured = lambda *names: 'configured' if all(_cfg.get_env_value(n) for n in names) else '(not set)' # noqa: E731 + configured = lambda *names: 'configured' if all(_cfg.get_env_value(n) for n in names) else '(not set)' from hermes_cli.config_defaults import DEFAULT_SANDBOX_IMAGE as default_img, DEFAULT_VERCEL_IMAGE as _DEFAULT_VERCEL_IMAGE backend_lines = { 'docker': lambda: [f" Docker image: {terminal.get('docker_image', default_img)}"], @@ -109,7 +109,7 @@ def _show_terminal_section(config: Dict[str, Any]) -> None: print(line) -def _show_compression_section(config: Dict[str, Any]) -> None: +def _show_compression_section(config: dict[str, Any]) -> None: _section("Context Compression") compression = config.get('compression', {}) enabled = compression.get('enabled', True) @@ -133,7 +133,7 @@ def _show_compression_section(config: Dict[str, Any]) -> None: print(f" Provider: {comp_provider}") -def _show_aux_overrides(config: Dict[str, Any]) -> None: +def _show_aux_overrides(config: dict[str, Any]) -> None: aux_tasks = {"Vision": config.get('auxiliary', {}).get('vision', {})} overrides = { label: (t.get('provider', 'auto'), t.get('model', '')) diff --git a/hermes_cli/gateway_runtime_service.py b/hermes_cli/gateway_runtime_service.py index 47690468eb..6e6c41e672 100644 --- a/hermes_cli/gateway_runtime_service.py +++ b/hermes_cli/gateway_runtime_service.py @@ -68,7 +68,7 @@ class ExistingService: def _run(argv: list[str] | tuple[str, ...], deadline: float, *, encoding: str | None = "utf-8"): return subprocess.run(argv, stdin=subprocess.DEVNULL, capture_output=True, text=encoding is not None, encoding=encoding, errors="strict" if encoding else None, - creationflags=windows_hide_flags(), timeout=remaining(deadline)) + creationflags=windows_hide_flags(), timeout=remaining(deadline), check=False) def _verify_binding(verify, *args, **kwargs): diff --git a/hermes_cli/gateway_runtime_service_identity.py b/hermes_cli/gateway_runtime_service_identity.py index 4b5b7011dd..cd7334800f 100644 --- a/hermes_cli/gateway_runtime_service_identity.py +++ b/hermes_cli/gateway_runtime_service_identity.py @@ -172,13 +172,13 @@ def verify_launchd_loaded(output: str, home: Path, *, uid: int, username: str) - # Only the job's own blocks, never inherited/default environments or a # different job embedded in launchctl diagnostic text. def block(name): - found = re.findall(r"^\t" + name + r" = \{\n(.*?)^\t\}", output, re.M | re.S) + found = re.findall(r"^\t" + name + r" = \{\n(.*?)^\t\}", output, re.MULTILINE | re.DOTALL) if len(found) != 1: _unverified() return found[0].splitlines() for key, expected in (("uid", str(uid)), ("username", username)): - values = re.findall(r"^\t" + key + r" = (.+)$", output, re.M) + values = re.findall(r"^\t" + key + r" = (.+)$", output, re.MULTILINE) if values and values != [expected]: raise ValueError("service_account_mismatch") env = {} @@ -193,7 +193,7 @@ def verify_launchd_loaded(output: str, home: Path, *, uid: int, username: str) - if home.parent.name != "profiles" and not env.get("HERMES_SUPERVISED_CHILD"): _unverified() argv = [line.strip() for line in block("arguments")] - programs = re.findall(r"^\tprogram = (.+)$", output, re.M) + programs = re.findall(r"^\tprogram = (.+)$", output, re.MULTILINE) if len(programs) != 1 or not argv or programs[0] != argv[0]: _unverified() verify_gateway_argv(argv, home) diff --git a/hermes_cli/kanban_worker_client.py b/hermes_cli/kanban_worker_client.py index 741867b506..42e7fca60d 100644 --- a/hermes_cli/kanban_worker_client.py +++ b/hermes_cli/kanban_worker_client.py @@ -2,7 +2,7 @@ # Spawned as a bare ``sys.executable -m``: in a PM install that interpreter carries no dependencies # until hermes_bootstrap selects the committed environment, so it must be the first import. try: - import hermes_bootstrap # noqa: F401 + import hermes_bootstrap except ModuleNotFoundError as exc: if exc.name != "hermes_bootstrap": raise diff --git a/hermes_cli/runtime_provider.py b/hermes_cli/runtime_provider.py index 2c7fcc3395..73b17641a3 100644 --- a/hermes_cli/runtime_provider.py +++ b/hermes_cli/runtime_provider.py @@ -39,11 +39,11 @@ from utils import base_url_host_matches, base_url_hostname, base_url_path, env_i # A frozen session route's config (``frozen_runtime_config``); None = the live profile config. -_FROZEN_RUNTIME_CONFIG: ContextVar[Optional[Dict[str, Any]]] = ContextVar("_FROZEN_RUNTIME_CONFIG", default=None) +_FROZEN_RUNTIME_CONFIG: ContextVar[Optional[dict[str, Any]]] = ContextVar("_FROZEN_RUNTIME_CONFIG", default=None) @contextmanager -def frozen_runtime_config(config: Dict[str, Any]): +def frozen_runtime_config(config: dict[str, Any]): """Resolve against a session's frozen config instead of the live config.yaml. Every rung reads its endpoint through ``load_config``/``_get_model_config``, so a frozen route's diff --git a/hermes_cli/web_server_app.py b/hermes_cli/web_server_app.py index dc54da051c..2121894427 100644 --- a/hermes_cli/web_server_app.py +++ b/hermes_cli/web_server_app.py @@ -74,7 +74,7 @@ async def standalone_lifespan(app: "FastAPI"): # contended state.db migration, so keep it off the pre-yield path: Group # Chat must degrade on its own rather than block every Desktop feature. from tui_gateway import methods_groups as _hosted_groups - import tui_gateway.server # noqa: F401 + import tui_gateway.server try: tui_gateway.server.install_tui_message_injector() @@ -103,8 +103,8 @@ async def standalone_lifespan(app: "FastAPI"): # gateway running the scheduler. Server `hermes dashboard` is unaffected — # it relies on its own gateway. from hermes_cli.process_identity import is_desktop_owned_backend - cron_stop: "threading.Event | None" = None - cron_thread: "threading.Thread | None" = None + cron_stop: threading.Event | None = None + cron_thread: threading.Thread | None = None desktop_owned = is_desktop_owned_backend() if desktop_owned: # Reap an orphaned gateway from an abnormal previous exit (reparented to @@ -147,7 +147,7 @@ async def standalone_lifespan(app: "FastAPI"): from hermes_cli.local_runtime.bootstrap import ensure_local_runtime ensure_local_runtime(load_config()) - except Exception as exc: # noqa: BLE001 + except Exception as exc: logging.getLogger(__name__).warning("local runtime boot failed: %s", exc) threading.Thread(target=_boot_local_runtime, daemon=True, name="local-runtime-boot").start() diff --git a/hermes_state_compression.py b/hermes_state_compression.py index 065e03092a..1395d6d36e 100644 --- a/hermes_state_compression.py +++ b/hermes_state_compression.py @@ -278,9 +278,9 @@ class SessionCompressionMixin: def _publish_compression_child_on_conn( self, conn, *, parent_session_id: str, child_session_id: str, source: str, - messages: List[Dict[str, Any]], model: str = None, model_config: Dict[str, Any] = None, - system_prompt: str = None, cwd: str = None, profile_name: str = None, - compression_lock_holder: str = None, require_compression_lease: bool = True, + messages: list[dict[str, Any]], model: str | None = None, model_config: dict[str, Any] | None = None, + system_prompt: str | None = None, cwd: str | None = None, profile_name: str | None = None, + compression_lock_holder: str | None = None, require_compression_lease: bool = True, require_lease_refresh: bool = False, lease_ttl_seconds: float = 300.0, watermark: Optional[int] = None, watermark_ceiling: Optional[int] = None) -> None: """:meth:`publish_compression_child` on the caller's write transaction (never commits). The diff --git a/hermes_state_local_lineage.py b/hermes_state_local_lineage.py index 197d7edbdf..98655ebb82 100644 --- a/hermes_state_local_lineage.py +++ b/hermes_state_local_lineage.py @@ -8,6 +8,7 @@ import time from hermes_state_local import POLICY_PREFIX from hermes_state_runtime import RuntimeStoreError, _epoch, _json +import itertools def validate_local_lineage(conn, receipt): @@ -22,7 +23,7 @@ def validate_local_lineage(conn, receipt): if (not isinstance(lineage, list) or not lineage or len(set(lineage)) != len(lineage) or lineage[0] != root or lineage[-1] != receipt['entry']['session_id']): raise RuntimeStoreError('storage_unavailable') - for parent_id, child_id in zip(lineage, lineage[1:]): + for parent_id, child_id in itertools.pairwise(lineage): parent = conn.execute('SELECT end_reason FROM sessions WHERE id=?', (parent_id,)).fetchone() child = conn.execute('SELECT parent_session_id,model_config FROM sessions WHERE id=?', (child_id,)).fetchone() if parent is None or child is None or child['parent_session_id'] != parent_id: diff --git a/hermes_state_messages.py b/hermes_state_messages.py index b7798506ac..639dc3e19a 100644 --- a/hermes_state_messages.py +++ b/hermes_state_messages.py @@ -1023,12 +1023,12 @@ class SessionMessagesMixin: carried_messages=carried_messages, covered_ids=covered_ids, unresolved_held=unresolved_held), compacted_messages) def _archive_and_compact_on_conn( - self, conn, session_id: str, compacted_messages: List[Dict[str, Any]], *, - model_config_patch: Optional[Dict[str, Any]] = None, watermark: Optional[int] = None, + self, conn, session_id: str, compacted_messages: list[dict[str, Any]], *, + model_config_patch: Optional[dict[str, Any]] = None, watermark: Optional[int] = None, lock_holder: Optional[str] = None, tail_count: int = 0, - carried_messages: Optional[List[Dict[str, Any]]] = None, - covered_ids: Optional[List[int]] = None, - unresolved_held: Optional[List[Dict[str, Any]]] = None) -> int: + carried_messages: Optional[list[dict[str, Any]]] = None, + covered_ids: Optional[list[int]] = None, + unresolved_held: Optional[list[dict[str, Any]]] = None) -> int: """:meth:`archive_and_compact` on the caller's write transaction (never commits). The owner and the worker/canonical-mutation verbs share this one body so they cannot drift.""" from hermes_state import SessionCompressionInProgressError diff --git a/hermes_state_usage.py b/hermes_state_usage.py index a9d5911c01..ae91f8a22f 100644 --- a/hermes_state_usage.py +++ b/hermes_state_usage.py @@ -297,7 +297,7 @@ class SessionUsageMixin: self._execute_write(lambda conn: self._update_token_counts_in_transaction(conn, session_id, **values)) def _update_token_counts_in_transaction( - self, conn, session_id: str, input_tokens: int=0, output_tokens: int=0, model: str=None, cache_read_tokens: int=0, + self, conn, session_id: str, input_tokens: int=0, output_tokens: int=0, model: str | None=None, cache_read_tokens: int=0, cache_write_tokens: int=0, reasoning_tokens: int=0, estimated_cost_usd: Optional[float]=None, actual_cost_usd: Optional[float]=None, cost_status: Optional[str]=None, cost_source: Optional[str]=None, pricing_version: Optional[str]=None, billing_provider: Optional[str]=None, billing_base_url: Optional[str]=None, diff --git a/hermes_state_worker_compression.py b/hermes_state_worker_compression.py index 056347d327..b921f07ee1 100644 --- a/hermes_state_worker_compression.py +++ b/hermes_state_worker_compression.py @@ -209,7 +209,7 @@ def archive_on_connection(db, conn, session_id: str, compacted_messages: list[di tail_count=tail_count, carried_messages=carried_messages, covered_ids=covered_ids, unresolved_held=unresolved_held) -def publish_on_connection(db, conn, *, parent_session_id: str, child_session_id: str, source: str, messages: list[dict[str, Any]], model: str=None, model_config: dict[str, Any]=None, system_prompt: str=None, cwd: str=None, profile_name: str=None, compression_lock_holder: str=None, require_compression_lease: bool=True, require_lease_refresh: bool=False, lease_ttl_seconds: float=300.0, watermark: Optional[int]=None, watermark_ceiling: Optional[int]=None): +def publish_on_connection(db, conn, *, parent_session_id: str, child_session_id: str, source: str, messages: list[dict[str, Any]], model: str | None=None, model_config: dict[str, Any] | None=None, system_prompt: str | None=None, cwd: str | None=None, profile_name: str | None=None, compression_lock_holder: str | None=None, require_compression_lease: bool=True, require_lease_refresh: bool=False, lease_ttl_seconds: float=300.0, watermark: Optional[int]=None, watermark_ceiling: Optional[int]=None): # The owner's rotation body (lease, closure, child row, timestamps, tail clone, local target, # auto-archive lift) on this receipt's connection, so the two paths cannot drift. db._publish_compression_child_on_conn(conn, parent_session_id=parent_session_id, diff --git a/plugins/platforms/a2a/adapter.py b/plugins/platforms/a2a/adapter.py index b15d99663c..86ac76194a 100644 --- a/plugins/platforms/a2a/adapter.py +++ b/plugins/platforms/a2a/adapter.py @@ -275,7 +275,7 @@ class A2AAdapter(BasePlatformAdapter): # Request ownership outlives reply Futures and also covers synchronous profile forwards. self._active_tasks: set[str] = set() # Admission ids this process already forwarded; a resend of one is a retry, not a new turn. - self._forwarded_inputs: "OrderedDict[str, None]" = OrderedDict() + self._forwarded_inputs: OrderedDict[str, None] = OrderedDict() self._pending_lock = threading.Lock() @property diff --git a/plugins/platforms/discord/adapter.py b/plugins/platforms/discord/adapter.py index 2591415e44..f8c8cef033 100644 --- a/plugins/platforms/discord/adapter.py +++ b/plugins/platforms/discord/adapter.py @@ -1200,7 +1200,7 @@ class VoiceReceiver: ) -from plugins.platforms.discord.adapter_config_readers import ( # noqa: F401 - re-exported for callers/tests +from plugins.platforms.discord.adapter_config_readers import ( _DISCORD_PROMPT_TIMEOUT_DEFAULT, _DISCORD_PROMPT_TIMEOUT_MIN, _read_discord_prompt_timeout, _read_dm_role_auth_guild) diff --git a/scripts/probe_active_session_exclusivity.py b/scripts/probe_active_session_exclusivity.py index d419541430..30e268cbbc 100644 --- a/scripts/probe_active_session_exclusivity.py +++ b/scripts/probe_active_session_exclusivity.py @@ -160,7 +160,7 @@ def main() -> int: refused_b = subprocess.run( [str(PYTHON), "-u", "-m", "tui_gateway.entry"], cwd=str(REPO), env={**os.environ, "HERMES_HOME": str(home)}, stdin=subprocess.DEVNULL, - capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=180) + capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=180, check=False) check("B cannot start a second writer on A's home", refused_b.returncode == 1 and "gateway.ready" not in refused_b.stdout, (refused_b.stderr or "")[-200:]) diff --git a/tests/acp_adapter/test_gateway_acp_session_end.py b/tests/acp_adapter/test_gateway_acp_session_end.py index 37251be792..21eeca6420 100644 --- a/tests/acp_adapter/test_gateway_acp_session_end.py +++ b/tests/acp_adapter/test_gateway_acp_session_end.py @@ -51,7 +51,7 @@ async def test_last_viewer_or_shutdown_ends_only_idle_acp_sessions(tmp_path, mon acp, acp_row = create('acp', 'editor') gui, gui_row = create('gui', 'desktop') busy, busy_row = create('acp', 'busy-editor') - unseen, unseen_row = create('acp', 'unseen-editor') + _unseen, unseen_row = create('acp', 'unseen-editor') first, second = viewer(), viewer() for connection in (first, second): await resume(connection, acp) diff --git a/tests/acp_adapter/test_gateway_client_boundary.py b/tests/acp_adapter/test_gateway_client_boundary.py index 817b50e362..73c92c7fc8 100644 --- a/tests/acp_adapter/test_gateway_client_boundary.py +++ b/tests/acp_adapter/test_gateway_client_boundary.py @@ -5,7 +5,7 @@ from acp_adapter.server import HermesACPAgent def test_default_acp_does_not_construct_an_embedded_session_manager(monkeypatch): - import acp_adapter.server as server + from acp_adapter import server def forbidden(*args, **kwargs): raise AssertionError("ACP constructed an independent session owner") diff --git a/tests/acp_adapter/test_gateway_editor_mcp.py b/tests/acp_adapter/test_gateway_editor_mcp.py index de4d0697f9..34fa1091f6 100644 --- a/tests/acp_adapter/test_gateway_editor_mcp.py +++ b/tests/acp_adapter/test_gateway_editor_mcp.py @@ -149,7 +149,7 @@ async def test_native_cold_editor_rebind_refuses_changed_secret(daemon, specs, t os.kill(owned.pid, signal.SIGINT) await asyncio.to_thread(owned.wait, 20) with (tmp_path / 'cold.log').open('w') as log: - process = subprocess.Popen([sys.executable, '-m', 'gateway.run'], cwd=root, env=env, + process = subprocess.Popen([sys.executable, '-m', 'gateway.run'], cwd=root, env=env, # noqa: ASYNC220 -- Popen returns immediately; the test drives the child through its handle stdin=subprocess.DEVNULL, stdout=log, stderr=subprocess.STDOUT) try: deadline = time.monotonic() + 40 diff --git a/tests/acp_adapter/test_gateway_new_cold.py b/tests/acp_adapter/test_gateway_new_cold.py index 52c0ac096d..4df587ba1e 100644 --- a/tests/acp_adapter/test_gateway_new_cold.py +++ b/tests/acp_adapter/test_gateway_new_cold.py @@ -58,7 +58,7 @@ async def test_cold_acp_load_retains_policy_and_executes_after_daemon_restart(da config['platform_toolsets'] = {'acp': ['terminal']} (home / 'config.yaml').write_text(json.dumps(config)) with (tmp_path / 'restart.log').open('w') as log: - process = subprocess.Popen([sys.executable, '-m', 'gateway.run'], cwd=root, env=env, + process = subprocess.Popen([sys.executable, '-m', 'gateway.run'], cwd=root, env=env, # noqa: ASYNC220 -- Popen returns immediately; the test drives the child through its handle stdin=subprocess.DEVNULL, stdout=log, stderr=subprocess.STDOUT) try: deadline = time.monotonic() + 40 diff --git a/tests/acp_adapter/test_gateway_sessions.py b/tests/acp_adapter/test_gateway_sessions.py index d6543ba4fc..f9900e47e4 100644 --- a/tests/acp_adapter/test_gateway_sessions.py +++ b/tests/acp_adapter/test_gateway_sessions.py @@ -61,7 +61,7 @@ async def editor(daemon, tmp_path): process.stdin.close() try: await asyncio.wait_for(process.wait(), 5) - except asyncio.TimeoutError: + except TimeoutError: process.kill() await process.wait() await peer.reader diff --git a/tests/agent/test_runtime_session_store_proxy.py b/tests/agent/test_runtime_session_store_proxy.py index 8475db4cae..b9c20ca546 100644 --- a/tests/agent/test_runtime_session_store_proxy.py +++ b/tests/agent/test_runtime_session_store_proxy.py @@ -28,8 +28,8 @@ def _refusing_proxy(): def test_worker_rpc_reaches_loopback_owner_with_proxy_env(monkeypatch, tmp_path): from websockets.sync.server import serve from agent import runtime_session_store as store - import hermes_cli.gateway_client as gateway_client - import hermes_cli.gateway_runtime as gateway_runtime + from hermes_cli import gateway_client + from hermes_cli import gateway_runtime import hermes_cli.gateway_runtime_discovery as discovery def owner(ws): @@ -39,7 +39,7 @@ def test_worker_rpc_reaches_loopback_owner_with_proxy_env(monkeypatch, tmp_path) proxy, seen = _refusing_proxy() with serve(owner, '127.0.0.1', 0, subprotocols=['hermes-gateway-v1']) as server: threading.Thread(target=server.serve_forever, daemon=True).start() - origin = 'http://127.0.0.1:%d' % server.socket.getsockname()[1] + origin = f'http://127.0.0.1:{server.socket.getsockname()[1]}' endpoint = SimpleNamespace(api_origin=origin, control_home=None) monkeypatch.setattr(gateway_runtime, 'discover_gateway_endpoint', lambda home, timeout: SimpleNamespace(state='ready', endpoint=endpoint)) @@ -48,7 +48,7 @@ def test_worker_rpc_reaches_loopback_owner_with_proxy_env(monkeypatch, tmp_path) for name in ('NO_PROXY', 'no_proxy', 'HTTP_PROXY', 'http_proxy', 'HTTPS_PROXY', 'https_proxy', 'ALL_PROXY', 'all_proxy'): monkeypatch.delenv(name, raising=False) - monkeypatch.setenv('HTTPS_PROXY', 'http://127.0.0.1:%d' % proxy.getsockname()[1]) + monkeypatch.setenv('HTTPS_PROXY', f'http://127.0.0.1:{proxy.getsockname()[1]}') try: assert store.WorkerRPC(tmp_path)('worker.persist') == {'method': 'worker.persist'} finally: diff --git a/tests/agent/test_safe_worker_policy.py b/tests/agent/test_safe_worker_policy.py index 985b421fdc..1a5859682d 100644 --- a/tests/agent/test_safe_worker_policy.py +++ b/tests/agent/test_safe_worker_policy.py @@ -22,7 +22,7 @@ def run_worker(tmp_path, body, mode="safe"): result = subprocess.run( [sys.executable, str(script)], env=env, cwd=root, stdin=subprocess.DEVNULL, - capture_output=True, text=True, timeout=45, + capture_output=True, text=True, timeout=45, check=False ) assert result.returncode == 0, result.stdout + result.stderr return json.loads(result.stdout.strip().splitlines()[-1]) diff --git a/tests/e2e/core/dashboard/_helpers.py b/tests/e2e/core/dashboard/_helpers.py index daf8ba6a9d..b0b5bf2c3b 100644 --- a/tests/e2e/core/dashboard/_helpers.py +++ b/tests/e2e/core/dashboard/_helpers.py @@ -175,7 +175,7 @@ class Sandbox: with contextlib.suppress(Exception): subprocess.run([sys.executable, "-m", "hermes_cli.main", "gateway", "stop"], env=self.env({"HERMES_HOME": str(home)}), cwd=str(self.home), - capture_output=True, timeout=60) + capture_output=True, timeout=60, check=False) class SandboxLeak(AssertionError): diff --git a/tests/e2e/core/delivery/_cron_clock.py b/tests/e2e/core/delivery/_cron_clock.py index e5fe56bb73..50c827da5e 100644 --- a/tests/e2e/core/delivery/_cron_clock.py +++ b/tests/e2e/core/delivery/_cron_clock.py @@ -613,7 +613,7 @@ def drain_deliveries() -> int: """The live gateway's housekeeping drain of the durable delivery queue. No live adapters exist in the soak, so every send takes the standalone wire (``FakeSink``). Returns how many queued sends were performed.""" - import cron.scheduler as scheduler + from cron import scheduler total = 0 while True: @@ -623,7 +623,7 @@ def drain_deliveries() -> int: total += n -def delivery_status(home: Path) -> Dict[str, str]: +def delivery_status(home: Path) -> dict[str, str]: """execution id -> durable queue status (``pending``/``delivering``/``delivered``/``failed``/ ``unknown``/``suppressed``), including tombstoned rows.""" path = Path(home) / "cron" / "deliveries.db" @@ -639,7 +639,7 @@ def delivery_status(home: Path) -> Dict[str, str]: conn.close() -def effective_outcome(row: dict, deliveries: Dict[str, str]) -> Optional[str]: +def effective_outcome(row: dict, deliveries: dict[str, str]) -> Optional[str]: """The ledger's delivery outcome, resolved through the durable queue when the run only booked the handoff (``queued``): the queue's verdict is the delivery's truth.""" outcome = row["delivery_outcome"] diff --git a/tests/e2e/core/history/_helpers.py b/tests/e2e/core/history/_helpers.py index 422b30f524..ec3e32cd27 100644 --- a/tests/e2e/core/history/_helpers.py +++ b/tests/e2e/core/history/_helpers.py @@ -401,7 +401,7 @@ class TuiGateway: # the owner first so each "gw" hop is still a fresh process on the same durable store. subprocess.run([sys.executable, "-m", "hermes_cli.main", "gateway", "stop"], cwd=self.cwd, env={**self.env, "PWD": self.cwd}, stdin=subprocess.DEVNULL, - capture_output=True, text=True, encoding="utf-8", timeout=120) + capture_output=True, text=True, encoding="utf-8", timeout=120, check=False) self.proc = self.spawned.add(subprocess.Popen( [sys.executable, "-m", "tui_gateway.entry"], cwd=self.cwd, env={**self.env, "PWD": self.cwd}, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, diff --git a/tests/e2e/core/parity/_helpers.py b/tests/e2e/core/parity/_helpers.py index 04577b5455..fcfb5cbbfd 100644 --- a/tests/e2e/core/parity/_helpers.py +++ b/tests/e2e/core/parity/_helpers.py @@ -472,7 +472,7 @@ def stop_profile_gateway(ph: ParityHome, timeout: float = STOP_TIMEOUT) -> bool: """ proc = subprocess.run( hermes_argv("gateway", "stop"), cwd=ph.project, env=ph.env(), capture_output=True, text=True, - timeout=timeout, stdin=subprocess.DEVNULL, + timeout=timeout, stdin=subprocess.DEVNULL, check=False ) assert proc.returncode == 0, f"hermes gateway stop exited {proc.returncode}: {proc.stderr[-2000:]}" return "Stopped gateway" in proc.stdout diff --git a/tests/gateway/fixtures/automation_peer.py b/tests/gateway/fixtures/automation_peer.py index 9c504bde42..449a156b98 100644 --- a/tests/gateway/fixtures/automation_peer.py +++ b/tests/gateway/fixtures/automation_peer.py @@ -124,7 +124,7 @@ async def probe(root, base, mode): GATEWAY_RELAY_PLATFORMS='telegram', GATEWAY_RELAY_BOT_IDS='{"telegram":"fixture-bot"}', GATEWAY_RELAY_ID='fixture-gateway', GATEWAY_RELAY_SECRET='fixture-secret') with (home / 'daemon.log').open('w+') as log: - proc = subprocess.Popen([sys.executable, '-m', 'gateway.run'], cwd=root, env=env, + proc = subprocess.Popen([sys.executable, '-m', 'gateway.run'], cwd=root, env=env, # noqa: ASYNC220 -- Popen returns immediately; the test drives the child through its handle stdin=subprocess.DEVNULL, stdout=log, stderr=subprocess.STDOUT) try: ws = await asyncio.wait_for(ready.get(), 35) diff --git a/tests/gateway/fixtures/cli_launch_policy_probe.py b/tests/gateway/fixtures/cli_launch_policy_probe.py index 416d9f9abd..e55d9cdeee 100644 --- a/tests/gateway/fixtures/cli_launch_policy_probe.py +++ b/tests/gateway/fixtures/cli_launch_policy_probe.py @@ -106,7 +106,7 @@ def probe(tmp_path): base_url=origin + '/' + side + '/v1', reasoning='high' if side == 'left' else 'low', max_turns=1 if side == 'left' else 3, ignore_rules=side == 'left', toolsets='terminal') result = subprocess.run([sys.executable, '-c', 'import cli; cli.main(**' + repr(args) + ')'], - cwd=cwd, env=env, stdin=subprocess.DEVNULL, capture_output=True, text=True, timeout=70) + cwd=cwd, env=env, stdin=subprocess.DEVNULL, capture_output=True, text=True, timeout=70, check=False) # A budget run stops at --max-turns with the work unfinished: `chat -Q` exits 1 (documented # contract, same as the in-process CLI), never 0. assert result.returncode == (1 if budget else 0), result.stdout + result.stderr @@ -156,11 +156,11 @@ def probe(tmp_path): config.write_text(json.dumps(cfg)) for side in keys: assert 'LAUNCH_POLICY_OK' in cli(side, sessions[side]) - for side in keys: + for side, key in keys.items(): captured = [x for x in peer.requests if x['body']['model'] == 'gpt-5-' + side] assert len(captured) == 2, captured assert all(x['path'] == '/' + side + '/v1/chat/completions' for x in captured) - assert all(x['auth'] == 'Bearer ' + keys[side] for x in captured) + assert all(x['auth'] == 'Bearer ' + key for x in captured) bodies = [x['body'] for x in captured] assert all(b.get('reasoning_effort') == ('high' if side == 'left' else 'low') for b in bodies), bodies systems = [[m for m in b['messages'] if m['role'] in {'system', 'developer'}] for b in bodies] diff --git a/tests/gateway/fixtures/kanban_owner_probe.py b/tests/gateway/fixtures/kanban_owner_probe.py index 699a75c1b8..042908a72e 100644 --- a/tests/gateway/fixtures/kanban_owner_probe.py +++ b/tests/gateway/fixtures/kanban_owner_probe.py @@ -117,7 +117,7 @@ def main(): time.sleep(.05) raise AssertionError(('deadline', receipt)) try: - with daemon(root, home, env, barrier=False) as (proc, desc): + with daemon(root, home, env, barrier=False) as (_proc, desc): subprocess.Popen = spawn try: with closing(connect(board='owned')) as conn: diff --git a/tests/gateway/fixtures/legacy_migration_probe.py b/tests/gateway/fixtures/legacy_migration_probe.py index 25678c95e9..a7a3e21994 100644 --- a/tests/gateway/fixtures/legacy_migration_probe.py +++ b/tests/gateway/fixtures/legacy_migration_probe.py @@ -30,7 +30,7 @@ def probe(base, legacy_root, owner_root=None): def cli(checkout, *args): result = subprocess.run([sys.executable, '-m', 'hermes_cli.main', 'chat', *args], cwd=checkout, env={**env, 'PYTHONPATH': str(checkout)}, stdin=subprocess.DEVNULL, - capture_output=True, text=True, timeout=120) + capture_output=True, text=True, timeout=120, check=False) (home / ('cli-' + str(len(list(home.glob('cli-*')))) + '.log')).write_text(result.stdout + result.stderr) assert result.returncode == 0, result.stdout + result.stderr return result.stdout diff --git a/tests/gateway/fixtures/rotation_persistence_worker.py b/tests/gateway/fixtures/rotation_persistence_worker.py index 2af2086a14..98c0c1794d 100644 --- a/tests/gateway/fixtures/rotation_persistence_worker.py +++ b/tests/gateway/fixtures/rotation_persistence_worker.py @@ -45,7 +45,7 @@ def main(): store.update_system_prompt(scope['session_id'], 'BYTE_STABLE_PREFIX') agent._session_db_created = True before = agent.session_id - compressed, prompt = agent._compress_context(messages, 'BYTE_STABLE_PREFIX', force=True, approx_tokens=30000) + compressed, _prompt = agent._compress_context(messages, 'BYTE_STABLE_PREFIX', force=True, approx_tokens=30000) assert len(compressed) < len(messages), (len(compressed), len(messages), [ dict(operation=e['operation'], keys=[list(m) for m in e['payload'].get('messages', [])]) for e in store.journal['pending']]) diff --git a/tests/gateway/fixtures/stack_dump_daemon.py b/tests/gateway/fixtures/stack_dump_daemon.py index 905924d45d..5f465e39cd 100644 --- a/tests/gateway/fixtures/stack_dump_daemon.py +++ b/tests/gateway/fixtures/stack_dump_daemon.py @@ -17,7 +17,7 @@ import time path = Path(os.environ['HERMES_HOME']) / 'logs' / 'stacks.txt' path.parent.mkdir(parents=True, exist_ok=True) delay = float(os.environ.get('UGW_STACK_DUMP_AFTER', '60')) -_sink = open(path, "w", encoding="utf-8") # noqa: SIM115 - must outlive the dump +_sink = open(path, "w", encoding="utf-8") faulthandler.dump_traceback_later(delay, repeat=True, file=_sink) _real_run = asyncio.run @@ -39,7 +39,7 @@ def _describe(sink): f'draining={attrs.get("_draining")} descriptor={json.dumps(descriptor, default=str)}\n') sink.write(f'pipe={windows_pipe_name(Path(os.environ["HERMES_HOME"]))}\n') for obj in gc.get_objects(): - if type(obj) is GatewayControlServer: # noqa: E721 - never isinstance over arbitrary gc objects + if type(obj) is GatewayControlServer: reply = obj.handle_request_line(b'{"protocol":1,"id":1,"verb":"identify"}', 'local') sink.write(f'control_server home={obj._home} handlers={sorted(obj._handlers)} ' f'pipe_server={obj._pipe_server!r} error={getattr(obj._pipe_server, "_error", None)!r}\n' diff --git a/tests/gateway/test_adversarial_mutation_lifecycle.py b/tests/gateway/test_adversarial_mutation_lifecycle.py index 869a251de1..82baa76a94 100644 --- a/tests/gateway/test_adversarial_mutation_lifecycle.py +++ b/tests/gateway/test_adversarial_mutation_lifecycle.py @@ -71,7 +71,7 @@ async def test_cancelling_model_request_cannot_strand_next_admission(tmp_path, m @pytest.mark.asyncio async def test_profile_retirement_waits_for_mutation_thread(tmp_path, monkeypatch): from gateway.run_runtime import _retire_profile_authority - owner, connection, ref, task, release, completed = await pending_model(tmp_path, monkeypatch) + owner, connection, _ref, task, release, completed = await pending_model(tmp_path, monkeypatch) retirement = asyncio.create_task(_retire_profile_authority(owner)) try: # Retirement must not finish while the writer can still mutate this profile. diff --git a/tests/gateway/test_api_authority_execution.py b/tests/gateway/test_api_authority_execution.py index 6e7bf35f7e..770ef3367a 100644 --- a/tests/gateway/test_api_authority_execution.py +++ b/tests/gateway/test_api_authority_execution.py @@ -18,7 +18,7 @@ def _probe(tmp_path, *, advanced=False, runs=False): if runs: env['API_RUN'] = str(runs) result = subprocess.run([sys.executable, str(Path(__file__).parent / 'fixtures' / 'api_authority_peer.py')], - cwd=repo, env=env, stdin=subprocess.DEVNULL, capture_output=True, text=True, timeout=100) + cwd=repo, env=env, stdin=subprocess.DEVNULL, capture_output=True, text=True, timeout=100, check=False) assert result.returncode == 0, result.stdout + '\n' + result.stderr receipt = json.loads((state / 'receipt.json').read_text()) assert receipt['same_agent'], receipt diff --git a/tests/gateway/test_api_canonical_projection.py b/tests/gateway/test_api_canonical_projection.py index db142e3000..0c9ff3c913 100644 --- a/tests/gateway/test_api_canonical_projection.py +++ b/tests/gateway/test_api_canonical_projection.py @@ -86,7 +86,7 @@ async def test_initialization_failure_is_a_failed_receipt_not_completed(api, own async def test_run_sse_carries_controls_tool_payloads_and_authoritative_cancellation(api, owner): from tools import clarify_gateway admitted = admit_api_turn(api, session_id='sse', user_message='hello', conversation_history=[]) - _, ref, row = admitted + _, ref, _row = admitted launch, queue = _launch(api, admitted) tool_payloads = [] @@ -228,8 +228,8 @@ async def test_room_grant_answers_the_clarify_prompt_its_run_raised(api, owner, headers = {'Authorization': f'HermesRoom {grant}'} admitted = admit_api_turn(api, session_id='room-clarify', request_id='run_room', user_message='hello', conversation_history=[]) - _, ref, row = admitted - launch, queue = _launch(api, admitted, run_id='run_room') + _, ref, _row = admitted + launch, _queue = _launch(api, admitted, run_id='run_room') scope_request = SimpleNamespace(headers=headers, path='/v1/runs/run_room/clarify', method='POST') api._run_owners['run_room'] = api._run_idempotency_scope(scope_request) diff --git a/tests/gateway/test_api_retired_media.py b/tests/gateway/test_api_retired_media.py index 5c4c88db06..6bf85c5a65 100644 --- a/tests/gateway/test_api_retired_media.py +++ b/tests/gateway/test_api_retired_media.py @@ -27,7 +27,7 @@ def test_rejected_api_images_are_collected_and_history_owns_accepted_images(api, settle_session_input(owner.db, epoch=owner.epoch, admission_id=row['admission_id'], generation=started['generation'], outcome='completed') owner.db.create_session('branch', source='api_server', model_config={'_branched_from': 's'}) - owner.db.append_message('branch', 'user', '[Image attached at: %s]' % path) + owner.db.append_message('branch', 'user', f'[Image attached at: {path}]') owner.db.delete_session('s') assert path.read_bytes() == PNG, 'branch history still owns its image' owner.db.delete_session('branch') diff --git a/tests/gateway/test_api_runs_native_session_refusal.py b/tests/gateway/test_api_runs_native_session_refusal.py index 99c69e90b8..902c732af0 100644 --- a/tests/gateway/test_api_runs_native_session_refusal.py +++ b/tests/gateway/test_api_runs_native_session_refusal.py @@ -8,7 +8,7 @@ from pathlib import Path def test_runs_refuse_native_session_without_phantom_run(tmp_path): result = subprocess.run([sys.executable, __file__], cwd=Path(__file__).resolve().parents[2], - env=dict(os.environ, HERMES_HOME=str(tmp_path)), capture_output=True, text=True, timeout=90) + env=dict(os.environ, HERMES_HOME=str(tmp_path)), capture_output=True, text=True, timeout=90, check=False) assert result.returncode == 0, result.stdout + result.stderr print((tmp_path / 'runs-receipt.json').read_text(encoding='utf-8')) diff --git a/tests/gateway/test_api_unknown_resolution.py b/tests/gateway/test_api_unknown_resolution.py index b3dc1cbb11..685e39c564 100644 --- a/tests/gateway/test_api_unknown_resolution.py +++ b/tests/gateway/test_api_unknown_resolution.py @@ -251,7 +251,7 @@ async def test_http_resolve_unknown_refuses_stale_or_foreign_admission( async def test_fresh_adapter_recovers_non_idempotent_owner_but_refuses_other_credential( tmp_path, monkeypatch ): - client, adapters, store, owner, unknown, follower, executed, tasks = ( + client, adapters, store, _owner, unknown, _follower, _executed, tasks = ( await _restarted_api_runs(tmp_path, monkeypatch, idempotent=False) ) try: diff --git a/tests/gateway/test_authority_automation.py b/tests/gateway/test_authority_automation.py index 723331dc48..22f03fda1e 100644 --- a/tests/gateway/test_authority_automation.py +++ b/tests/gateway/test_authority_automation.py @@ -11,7 +11,7 @@ def _probe(tmp_path, mode): env['PYTHONPATH'] = str(root) result = subprocess.run([sys.executable, str(root / 'tests/gateway/fixtures/automation_peer.py'), str(tmp_path), mode], env=env, cwd=root, stdin=subprocess.DEVNULL, - capture_output=True, text=True, timeout=200) + capture_output=True, text=True, timeout=200, check=False) assert result.returncode == 0, result.stdout + result.stderr print(result.stdout) diff --git a/tests/gateway/test_authority_connection.py b/tests/gateway/test_authority_connection.py index e9b6e09556..3ccdf81a42 100644 --- a/tests/gateway/test_authority_connection.py +++ b/tests/gateway/test_authority_connection.py @@ -44,7 +44,7 @@ async def test_repeated_resume_and_close_preserve_only_live_memberships(tmp_path async def test_close_after_a_subscribed_session_was_deleted_releases_every_membership(tmp_path, deleted_first): """Deletion evicts the live session before the viewer disconnects; teardown must still release the other subscription and the transport instead of raising not_found.""" - import tui_gateway.ws # noqa: F401 — the fanout drain thread imports it lazily during teardown + import tui_gateway.ws db = SessionDB(db_path=tmp_path / 'state.db') try: for sid in ('doomed', 'kept'): diff --git a/tests/gateway/test_authority_events.py b/tests/gateway/test_authority_events.py index ab00f59d21..af8ad05ed0 100644 --- a/tests/gateway/test_authority_events.py +++ b/tests/gateway/test_authority_events.py @@ -18,7 +18,7 @@ def test_full_observer_cannot_block_real_authority_execution(tmp_path): PYTHONPATH=str(repo), PYTHONUNBUFFERED='1') result = subprocess.run( [sys.executable, str(Path(__file__).parent / 'fixtures' / 'authority_events_peer.py')], - cwd=repo, env=env, stdin=subprocess.DEVNULL, capture_output=True, text=True, timeout=100) + cwd=repo, env=env, stdin=subprocess.DEVNULL, capture_output=True, text=True, timeout=100, check=False) assert result.returncode == 0, result.stdout + '\n' + result.stderr print(result.stdout) diff --git a/tests/gateway/test_authority_resident_stop.py b/tests/gateway/test_authority_resident_stop.py index 1015542192..0b50e41344 100644 --- a/tests/gateway/test_authority_resident_stop.py +++ b/tests/gateway/test_authority_resident_stop.py @@ -13,7 +13,7 @@ async def test_stop_reaches_resident_agent_after_real_cache_eviction(tmp_path, m db, authority = _authority(tmp_path, monkeypatch) calls = [] agent = SimpleNamespace(interrupt=lambda: calls.append('stop')) - runner, state = _build_gateway(agent, []) + runner, _state = _build_gateway(agent, []) authority.runner = runner authority.sessions['s'].route = KEY with db: diff --git a/tests/gateway/test_authority_stream.py b/tests/gateway/test_authority_stream.py index fd715c4d12..e5dd7a7cce 100644 --- a/tests/gateway/test_authority_stream.py +++ b/tests/gateway/test_authority_stream.py @@ -17,7 +17,7 @@ def test_authority_stream_has_one_order_and_rejects_retired_callbacks(tmp_path, env = {k: os.environ[k] for k in ('PATH', 'SYSTEMROOT', 'LANG', 'TZ') if k in os.environ} env.update(HOME=str(home), USERPROFILE=str(home), HERMES_HOME=str(state), PYTHONPATH=str(repo), STREAM_PROBE_MODE=mode) result = subprocess.run([sys.executable, str(Path(__file__).parent / 'fixtures' / 'authority_stream_peer.py')], - cwd=repo, env=env, stdin=subprocess.DEVNULL, capture_output=True, text=True, timeout=100) + cwd=repo, env=env, stdin=subprocess.DEVNULL, capture_output=True, text=True, timeout=100, check=False) assert result.returncode == 0, result.stdout + '\n' + result.stderr receipt = json.loads((state / 'receipt.json').read_text()) print(json.dumps(receipt)) diff --git a/tests/gateway/test_authority_unknown_resolution.py b/tests/gateway/test_authority_unknown_resolution.py index d0f4bd178a..64dc8bb692 100644 --- a/tests/gateway/test_authority_unknown_resolution.py +++ b/tests/gateway/test_authority_unknown_resolution.py @@ -58,7 +58,7 @@ async def _restarted_owner_with_unknown_head(tmp_path, monkeypatch, executed): @pytest.mark.asyncio async def test_resolve_unknown_releases_the_paused_follower_exactly_once(tmp_path, monkeypatch): executed = [] - store, authority, ref, unknown, follower = await _restarted_owner_with_unknown_head(tmp_path, monkeypatch, executed) + store, authority, _ref, unknown, follower = await _restarted_owner_with_unknown_head(tmp_path, monkeypatch, executed) peer = Peer() viewer = AuthorityConnection(authority, peer, {'user_id': 'human'}) try: @@ -97,7 +97,7 @@ async def test_resolve_unknown_releases_the_paused_follower_exactly_once(tmp_pat @pytest.mark.asyncio async def test_resolve_unknown_is_fenced_by_generation_and_capability(tmp_path, monkeypatch): executed = [] - store, authority, ref, unknown, follower = await _restarted_owner_with_unknown_head(tmp_path, monkeypatch, executed) + store, authority, _ref, unknown, follower = await _restarted_owner_with_unknown_head(tmp_path, monkeypatch, executed) owner = AuthorityConnection(authority, Peer(), {'user_id': 'human'}) reader = AuthorityConnection(authority, Peer(), {'user_id': 'human', 'capabilities': ['session:read', 'session:submit']}) try: @@ -127,7 +127,7 @@ async def test_discard_and_its_transcript_boundary_commit_atomically_on_the_phys """R1: a failure writing the boundary must not leave the lost admission terminal (its exact Discard retry refused, the follower claimable onto an open user tail). The boundary lands on the rotated physical transcript, not the logical owner.""" - store, authority, ref, unknown, follower = await _restarted_owner_with_unknown_head(tmp_path, monkeypatch, []) + store, authority, _ref, unknown, follower = await _restarted_owner_with_unknown_head(tmp_path, monkeypatch, []) db = authority.db db.create_session('s-tip', source='telegram', parent_session_id='s') db.end_session('s', 'compression') @@ -165,7 +165,7 @@ async def test_discarded_lost_turn_is_closed_and_never_merged_into_the_follower_ from tests.acp_adapter.test_failed_turn_closure import _LoopbackProvider monkeypatch.setenv('HERMES_DISABLE_PLUGINS', '1') monkeypatch.setenv('NO_PROXY', '127.0.0.1,localhost') - store, authority, ref, unknown, follower = await _restarted_owner_with_unknown_head(tmp_path, monkeypatch, []) + store, authority, _ref, unknown, _follower = await _restarted_owner_with_unknown_head(tmp_path, monkeypatch, []) db = authority.db db.append_message('s', 'user', 'HEAD lost in restart') # the lost turn's turn-start flush viewer = AuthorityConnection(authority, Peer(), {'user_id': 'human'}) diff --git a/tests/gateway/test_automation_compression_media.py b/tests/gateway/test_automation_compression_media.py index 36766588af..ecad9855d0 100644 --- a/tests/gateway/test_automation_compression_media.py +++ b/tests/gateway/test_automation_compression_media.py @@ -47,7 +47,7 @@ async def test_completion_after_compression_admits_to_the_logical_root(tmp_path, event = dict(pending(key, 'after-compression'), parent_session_id=child) assert await runner._deliver_async_delegation_group([event]) is True rows = list_session_admissions(db, session_id=root, pending_only=False) - assert [r['request_id'] for r in rows][0] == 'human' and len(rows) == 2, rows + assert next(r['request_id'] for r in rows) == 'human' and len(rows) == 2, rows assert 'after-compression' in rows[-1]['payload']['text'] assert list_session_admissions(db, session_id=child, pending_only=False) == [] # A lost-ACK redelivery is the same admission, found under the root's ledger. diff --git a/tests/gateway/test_automation_notification_category.py b/tests/gateway/test_automation_notification_category.py index 3b81867895..bff17f4deb 100644 --- a/tests/gateway/test_automation_notification_category.py +++ b/tests/gateway/test_automation_notification_category.py @@ -42,7 +42,7 @@ async def test_bot_category_is_committed_restored_and_part_of_retry_identity(tmp from gateway.session_local import create_local_session from gateway.session_local_title import title_new_session from gateway.session_automation import restore_local_automation - runner, authority = await _authority(tmp_path, monkeypatch) + _runner, authority = await _authority(tmp_path, monkeypatch) actor = Principal('test-owner', 'default', frozenset({'session:create', 'session:submit', 'session:read'}), 'fixture') try: ref = create_local_session(authority, actor, dict(request_id='bot', source='gui', model='fixture', toolsets=[])) @@ -84,7 +84,7 @@ async def test_bot_category_is_committed_restored_and_part_of_retry_identity(tmp @pytest.mark.parametrize('category', [None, [], {}, 'other']) async def test_bot_invalid_category_refuses_before_admission(tmp_path, monkeypatch, category): from gateway.session_bot import deliver - runner, authority = await _authority(tmp_path, monkeypatch) + _runner, authority = await _authority(tmp_path, monkeypatch) actor = Principal('test-owner', 'default', frozenset({'session:submit'}), 'fixture') try: with pytest.raises(RuntimeStoreError, match='invalid_params'): diff --git a/tests/gateway/test_bot_authority.py b/tests/gateway/test_bot_authority.py index f91043759b..05b04638ab 100644 --- a/tests/gateway/test_bot_authority.py +++ b/tests/gateway/test_bot_authority.py @@ -11,6 +11,6 @@ def test_bot_delivery_busy_retry_keeps_exact_target(tmp_path): env['PYTHONPATH'] = str(root) proc = subprocess.run([sys.executable, str(root / 'tests/gateway/fixtures/bot_authority_peer.py'), str(tmp_path)], cwd=root, env=env, stdin=subprocess.DEVNULL, - capture_output=True, text=True, timeout=150) + capture_output=True, text=True, timeout=150, check=False) assert proc.returncode == 0, proc.stdout + proc.stderr print(proc.stdout) diff --git a/tests/gateway/test_boundary_evict_commits_memory.py b/tests/gateway/test_boundary_evict_commits_memory.py index eeba427174..9422573926 100644 --- a/tests/gateway/test_boundary_evict_commits_memory.py +++ b/tests/gateway/test_boundary_evict_commits_memory.py @@ -6,7 +6,7 @@ import pytest # session.create's resume path imports this lazily mid-request; importing it whole here keeps the # conftest server-state fixture from meeting a half-initialized module at teardown. -import tui_gateway.server # noqa: F401 +import tui_gateway.server from gateway.run import GatewayRunner @@ -84,7 +84,7 @@ async def test_reset_and_compress_mutations_use_the_boundary_evict(tmp_path, mon from gateway.session import SessionStore from gateway.session_authority import initialize_session_authority from gateway.session_controls import AuthorityConnection - import gateway.run as run + from gateway import run monkeypatch.setenv('HERMES_HOME', str(tmp_path)) monkeypatch.setattr(run, '_load_gateway_config', lambda: {'model': {'default': 'fixture'}, 'platform_toolsets': {'cli': []}}) diff --git a/tests/gateway/test_cold_platform_activation.py b/tests/gateway/test_cold_platform_activation.py index 463193b1ca..e83d3e8e8f 100644 --- a/tests/gateway/test_cold_platform_activation.py +++ b/tests/gateway/test_cold_platform_activation.py @@ -43,7 +43,7 @@ def _load(tmp_path, *, config_yaml="", env=None, dotenv=""): if k in {"PATH", "LANG", "TZ", "HERMES_RUNTIME_DIR", "TMPDIR", "SYSTEMROOT"}} child_env.update(HOME=str(tmp_path), HERMES_HOME=str(home), PYTHONPATH=str(REPO), **(env or {})) out = subprocess.run([sys.executable, "-c", _PROBE], cwd=str(REPO), env=child_env, - capture_output=True, text=True, timeout=120) + capture_output=True, text=True, timeout=120, check=False) assert out.returncode == 0, out.stderr[-2000:] return json.loads(out.stdout.strip().splitlines()[-1]) diff --git a/tests/gateway/test_command_discovery.py b/tests/gateway/test_command_discovery.py index 277feda9df..28b12bdada 100644 --- a/tests/gateway/test_command_discovery.py +++ b/tests/gateway/test_command_discovery.py @@ -37,7 +37,7 @@ async def _legacy_ws_snapshot(root, home, env, requests): legacy_env = {**env, 'HERMES_HOME': str(legacy_home), 'HERMES_DASHBOARD_SESSION_TOKEN': 'disposable-discovery-token'} with (home.parent / 'legacy-serve.log').open('w+') as log: - process = subprocess.Popen([sys.executable, '-m', 'hermes_cli.main', 'serve', + process = subprocess.Popen([sys.executable, '-m', 'hermes_cli.main', 'serve', # noqa: ASYNC220 -- Popen returns immediately; the test drives the child through its handle '--host', '127.0.0.1', '--port', '0', '--skip-build', '--isolated'], cwd=root, env=legacy_env, stdin=subprocess.DEVNULL, stdout=log, stderr=subprocess.STDOUT) @@ -159,7 +159,7 @@ sys.__stdout__.write(json.dumps(results) + '\\n') # Ordinary startup syncs bundled skills; compare both transports on that same snapshot. legacy = subprocess.run([sys.executable, '-c', legacy_code, json.dumps(requests)], cwd=root, env=env, stdin=subprocess.DEVNULL, - capture_output=True, text=True, timeout=45) + capture_output=True, text=True, timeout=45, check=False) assert legacy.returncode == 0, legacy.stderr expected = json.loads(legacy.stdout.splitlines()[-1]) pairs = dict(expected[0]['pairs']) diff --git a/tests/gateway/test_hosted_room_attachment_publication.py b/tests/gateway/test_hosted_room_attachment_publication.py index 84a53f7722..79d063894d 100644 --- a/tests/gateway/test_hosted_room_attachment_publication.py +++ b/tests/gateway/test_hosted_room_attachment_publication.py @@ -143,7 +143,7 @@ _INVALIDATE_APPEND = { @pytest.mark.parametrize("invalid", ["room", "event", "metadata", "private", "expired"]) def test_append_requires_exact_live_room_visible_commitment(tmp_path, invalid): - db, store, manifest, args = _staged(tmp_path, viewer=invalid != "private") + db, _store, _manifest, args = _staged(tmp_path, viewer=invalid != "private") _INVALIDATE_APPEND[invalid](db, args) with pytest.raises(hosted_rooms.EventCursorConflictError): hosted_rooms.append_event(db, **args) diff --git a/tests/gateway/test_kanban_recovery.py b/tests/gateway/test_kanban_recovery.py index 20614d94fe..53b71141bd 100644 --- a/tests/gateway/test_kanban_recovery.py +++ b/tests/gateway/test_kanban_recovery.py @@ -21,6 +21,6 @@ def test_owner_board_recovery(tmp_path, mode): PYTHONPATH=str(repo), KANBAN_RECOVERY_MODE=mode) result = subprocess.run([sys.executable, str(repo / 'tests/gateway/fixtures/kanban_recovery_probe.py')], cwd=repo, env=env, stdin=subprocess.DEVNULL, - capture_output=True, text=True, timeout=180) + capture_output=True, text=True, timeout=180, check=False) assert result.returncode == 0, result.stdout + result.stderr print(json.dumps(json.loads((tmp_path / 'state/receipt.json').read_text()))) diff --git a/tests/gateway/test_local_automation.py b/tests/gateway/test_local_automation.py index be298cb347..78c85162e8 100644 --- a/tests/gateway/test_local_automation.py +++ b/tests/gateway/test_local_automation.py @@ -13,7 +13,7 @@ def _probe(tmp_path, source): env['PYTHONPATH'] = str(root) result = subprocess.run([sys.executable, str(root / 'tests/gateway/fixtures/local_automation_peer.py'), str(tmp_path), source], cwd=root, env=env, stdin=subprocess.DEVNULL, - capture_output=True, text=True, timeout=150) + capture_output=True, text=True, timeout=150, check=False) assert result.returncode == 0, result.stdout + result.stderr print(result.stdout) diff --git a/tests/gateway/test_local_session.py b/tests/gateway/test_local_session.py index 8234de3f33..e53ce15749 100644 --- a/tests/gateway/test_local_session.py +++ b/tests/gateway/test_local_session.py @@ -18,7 +18,7 @@ def test_fresh_local_session_owns_execution_across_viewers(tmp_path, kind): env.update(HOME=str(home), USERPROFILE=str(home), HERMES_HOME=str(state), PYTHONPATH=str(repo)) result = subprocess.run( [sys.executable, str(Path(__file__).parent / 'fixtures' / 'local_session_peer.py'), kind], - cwd=repo, env=env, stdin=subprocess.DEVNULL, capture_output=True, text=True, timeout=100) + cwd=repo, env=env, stdin=subprocess.DEVNULL, capture_output=True, text=True, timeout=100, check=False) assert result.returncode == 0, result.stdout + '\n' + result.stderr receipt = json.loads((state / 'receipt.json').read_text()) assert receipt['human_response'] and receipt['same_agent'] and receipt['detached_pending'] diff --git a/tests/gateway/test_local_session_lineage.py b/tests/gateway/test_local_session_lineage.py index 62f7119c50..c79daa0d62 100644 --- a/tests/gateway/test_local_session_lineage.py +++ b/tests/gateway/test_local_session_lineage.py @@ -17,7 +17,7 @@ async def test_local_lineage_transitions_preserve_owner_or_roll_back(tmp_path, m from hermes_state_runtime import (RuntimeStoreError, admit_session_input, claim_session_input, get_session_admission, list_session_admissions) - monkeypatch.setattr(run, '_load_gateway_config', lambda: {}) + monkeypatch.setattr(run, '_load_gateway_config', dict) def runner(): store = SessionStore(tmp_path / 'sessions', GatewayConfig()) return SimpleNamespace(session_store=store, _session_db=store._db, diff --git a/tests/gateway/test_local_source_labels.py b/tests/gateway/test_local_source_labels.py index 13f23e2080..6c7795f73f 100644 --- a/tests/gateway/test_local_source_labels.py +++ b/tests/gateway/test_local_source_labels.py @@ -12,7 +12,7 @@ async def _authority(tmp_path, monkeypatch): from gateway.config import GatewayConfig from gateway.session import SessionStore from gateway.session_authority import initialize_session_authority - import gateway.run as run + from gateway import run monkeypatch.setattr(run, '_load_gateway_config', lambda: {'model': {'default': 'fixture'}, 'platform_toolsets': {'cli': []}}) monkeypatch.setattr(run, '_resolve_gateway_model', lambda config: 'fixture') diff --git a/tests/gateway/test_managed_turn_shutdown_drain.py b/tests/gateway/test_managed_turn_shutdown_drain.py index a65f56705e..ee4f7824f5 100644 --- a/tests/gateway/test_managed_turn_shutdown_drain.py +++ b/tests/gateway/test_managed_turn_shutdown_drain.py @@ -19,7 +19,7 @@ from tests.gateway.restart_test_helpers import make_restart_runner async def test_drain_counts_managed_turns_and_the_timeout_path_stops_them(tmp_path): from gateway.session_managed_worker import ManagedWorker seen = tmp_path / 'controls' - child = subprocess.Popen([sys.executable, '-c', 'import sys; open(sys.argv[1], "wb").write(sys.stdin.buffer.readline())', + child = subprocess.Popen([sys.executable, '-c', 'import sys; open(sys.argv[1], "wb").write(sys.stdin.buffer.readline())', # noqa: ASYNC220 -- Popen returns immediately; the test drives the child through its handle str(seen)], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL) worker = ManagedWorker(child) worker.writer.start() diff --git a/tests/gateway/test_managed_worker_background_output.py b/tests/gateway/test_managed_worker_background_output.py index 6df29abbb8..145f0edef4 100644 --- a/tests/gateway/test_managed_worker_background_output.py +++ b/tests/gateway/test_managed_worker_background_output.py @@ -40,7 +40,7 @@ def test_worker_spawned_process_output_and_exit_survive_the_spawner(tmp_path): print(json.dumps(session.id), flush=True) os._exit(0) ''') - out = subprocess.run([sys.executable, '-c', spawner], env=env, cwd=ROOT, capture_output=True, text=True, encoding='utf-8', timeout=60) + out = subprocess.run([sys.executable, '-c', spawner], env=env, cwd=ROOT, capture_output=True, text=True, encoding='utf-8', timeout=60, check=False) session_id = json.loads(out.stdout.strip().splitlines()[-1]) adopter = textwrap.dedent(f''' import json @@ -49,7 +49,7 @@ def test_worker_spawned_process_output_and_exit_survive_the_spawner(tmp_path): registry.recover_from_checkpoint() print(json.dumps(registry.wait({session_id!r}, timeout=30))) ''') - out = subprocess.run([sys.executable, '-c', adopter], env=env, cwd=ROOT, capture_output=True, text=True, encoding='utf-8', timeout=60) + out = subprocess.run([sys.executable, '-c', adopter], env=env, cwd=ROOT, capture_output=True, text=True, encoding='utf-8', timeout=60, check=False) result = json.loads(out.stdout.strip().splitlines()[-1]) assert result['status'] == 'exited', (result, out.stderr[-2000:]) assert result['exit_code'] == 7 and result['output'] == 'FIRST_42\nSECOND_42\n', result @@ -72,7 +72,7 @@ class Model(BaseHTTPRequestHandler): 'arguments': {'action': 'wait', 'session_id': proc, 'timeout': 30}}]} else: name, args = None, {} - message = ({'role': 'assistant', 'content': None, 'tool_calls': [{'id': 'c%d' % len(messages), 'index': 0, + message = ({'role': 'assistant', 'content': None, 'tool_calls': [{'id': f'c{len(messages)}', 'index': 0, 'type': 'function', 'function': {'name': name, 'arguments': json.dumps(args)}}]} if name else {'role': 'assistant', 'content': 'OK'}) choice = {'index': 0, 'delta': message, 'finish_reason': 'tool_calls' if name else 'stop'} diff --git a/tests/gateway/test_managed_worker_bypass.py b/tests/gateway/test_managed_worker_bypass.py index 426bc09a1a..912cde426c 100644 --- a/tests/gateway/test_managed_worker_bypass.py +++ b/tests/gateway/test_managed_worker_bypass.py @@ -90,7 +90,7 @@ print(json.dumps({{'max_turns': cfg['agent']['max_turns'], 'raw_model': raw['mod env = {**child_env(), **{k: os.environ[k] for k in ('TIRITH_ENABLED',) if k in os.environ}} env.update(HOME=str(tmp_path), USERPROFILE=str(tmp_path), HERMES_HOME=str(home), PYTHONPATH=str(root)) result = subprocess.run([sys.executable, str(script)], cwd=root, env=env, stdin=subprocess.DEVNULL, - capture_output=True, text=True, timeout=90) + capture_output=True, text=True, timeout=90, check=False) assert result.returncode == 0, result.stdout + result.stderr receipt = json.loads(result.stdout.strip().splitlines()[-1]) assert receipt['max_turns'] == 7 and receipt['helper'] == 7 diff --git a/tests/gateway/test_managed_worker_detached_work.py b/tests/gateway/test_managed_worker_detached_work.py index d772b49746..c31baac981 100644 --- a/tests/gateway/test_managed_worker_detached_work.py +++ b/tests/gateway/test_managed_worker_detached_work.py @@ -82,7 +82,7 @@ def test_managed_turn_runs_detachable_work_inline_and_leaves_no_orphan(tmp_path) OPENAI_API_KEY='loopback-only', OPENAI_BASE_URL=url) created = subprocess.run([sys.executable, '-c', 'import json; from cron.jobs import create_job; ' 'print(json.dumps(create_job("CRON_CHILD_GOAL", "every 1h", name="inline", deliver="local")["id"]))'], - env=env, cwd=root, capture_output=True, text=True, timeout=60) + env=env, cwd=root, capture_output=True, text=True, timeout=60, check=False) peer.job_id = json.loads(created.stdout.strip().splitlines()[-1]) def rows(sql): diff --git a/tests/gateway/test_managed_worker_drain.py b/tests/gateway/test_managed_worker_drain.py index 899e3c126e..ef411dc261 100644 --- a/tests/gateway/test_managed_worker_drain.py +++ b/tests/gateway/test_managed_worker_drain.py @@ -15,7 +15,7 @@ async def test_reserved_worker_persists_while_runtime_drains(tmp_path): from hermes_state import SessionDB from hermes_state_runtime import admit_session_input, begin_runtime_epoch, claim_session_input, RuntimeStoreError db = SessionDB(tmp_path / 'state.db') - process = subprocess.Popen([sys.executable, '-c', 'import sys; sys.stdin.read()'], stdin=subprocess.PIPE, + process = subprocess.Popen([sys.executable, '-c', 'import sys; sys.stdin.read()'], stdin=subprocess.PIPE, # noqa: ASYNC220 -- Popen returns immediately; the test drives the child through its handle stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) try: db.create_session('owned', 'cli') diff --git a/tests/gateway/test_managed_worker_fencing.py b/tests/gateway/test_managed_worker_fencing.py index 08105bc66c..7d22111049 100644 --- a/tests/gateway/test_managed_worker_fencing.py +++ b/tests/gateway/test_managed_worker_fencing.py @@ -60,7 +60,7 @@ async def test_late_bootstrap_handshake_cannot_rearm_a_recovered_admission(tmp_p lost = admit_session_input(db, epoch=epoch, principal_id='human', session_id='owned', request_id='lost', payload={}) row = claim_session_input(db, epoch=epoch, session_id='owned') follower = admit_session_input(db, epoch=epoch, principal_id='human', session_id='owned', request_id='next', payload={}) - child = subprocess.Popen([sys.executable, '-c', 'import sys; sys.stdin.read()'], + child = subprocess.Popen([sys.executable, '-c', 'import sys; sys.stdin.read()'], # noqa: ASYNC220 -- Popen returns immediately; the test drives the child through its handle stdin=subprocess.PIPE, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) try: scope = reserve_admission_worker(authority, admission_id=row['admission_id'], process=child, diff --git a/tests/gateway/test_managed_worker_frozen_config.py b/tests/gateway/test_managed_worker_frozen_config.py index 3c7c421621..d028694a39 100644 --- a/tests/gateway/test_managed_worker_frozen_config.py +++ b/tests/gateway/test_managed_worker_frozen_config.py @@ -154,7 +154,7 @@ def test_ordinary_worker_binding_freezes_readers_but_keeps_customizations(tmp_pa env = {**child_env(), **{k: os.environ[k] for k in ('TIRITH_ENABLED',) if k in os.environ}} env.update(HOME=str(tmp_path), USERPROFILE=str(tmp_path), HERMES_HOME=str(home), PYTHONPATH=str(root)) result = subprocess.run([sys.executable, str(script)], cwd=root, env=env, stdin=subprocess.DEVNULL, - capture_output=True, text=True, timeout=90) + capture_output=True, text=True, timeout=90, check=False) assert result.returncode == 0, result.stdout + result.stderr receipt = json.loads(result.stdout.strip().splitlines()[-1]) assert receipt == {'safe': False, 'turns': [7, 7, 7], 'mcp': 'FROZEN_A', 'plugin': True}, receipt diff --git a/tests/gateway/test_managed_worker_turn_flags.py b/tests/gateway/test_managed_worker_turn_flags.py index dc250fa726..764f76d655 100644 --- a/tests/gateway/test_managed_worker_turn_flags.py +++ b/tests/gateway/test_managed_worker_turn_flags.py @@ -23,7 +23,7 @@ class Model(BaseHTTPRequestHandler): body = json.loads(self.rfile.read(int(self.headers['Content-Length']))) last = (body.get('messages') or [{'role': 'system'}])[-1] if last['role'] == 'user': - message = {'role': 'assistant', 'content': None, 'tool_calls': [{'id': 'call-%d' % len(body['messages']), + message = {'role': 'assistant', 'content': None, 'tool_calls': [{'id': f"call-{len(body['messages'])}", 'type': 'function', 'function': {'name': 'terminal', 'arguments': json.dumps({'command': self.server.command, 'timeout': 10})}}]} else: diff --git a/tests/gateway/test_msgraph_authority.py b/tests/gateway/test_msgraph_authority.py index 92e032f62b..81beb22b4b 100644 --- a/tests/gateway/test_msgraph_authority.py +++ b/tests/gateway/test_msgraph_authority.py @@ -9,7 +9,7 @@ import sys def test_graph_boundary_commits_only_valid_notifications(tmp_path): result = subprocess.run([sys.executable, __file__], env=dict(os.environ, HERMES_HOME=str(tmp_path)), - capture_output=True, text=True, timeout=90) + capture_output=True, text=True, timeout=90, check=False) assert result.returncode == 0, result.stdout + result.stderr print((tmp_path / 'graph-receipt.json').read_text()) diff --git a/tests/gateway/test_multiplex_cold_profile_sidebar_mutation.py b/tests/gateway/test_multiplex_cold_profile_sidebar_mutation.py index 6a384cc04c..532fa8a3de 100644 --- a/tests/gateway/test_multiplex_cold_profile_sidebar_mutation.py +++ b/tests/gateway/test_multiplex_cold_profile_sidebar_mutation.py @@ -83,7 +83,7 @@ def desktop_ensure(env, root, profile): """The exact child the Desktop spawns: ``hermes --profile

gateway ensure --json``.""" result = subprocess.run([sys.executable, '-m', 'hermes_cli.main', '--profile', profile, 'gateway', 'ensure', '--json', '--timeout', '30'], - cwd=root, env=env, stdin=subprocess.DEVNULL, capture_output=True, text=True, timeout=60) + cwd=root, env=env, stdin=subprocess.DEVNULL, capture_output=True, text=True, timeout=60, check=False) payload = json.loads(result.stdout) assert result.returncode == 0 and payload['state'] == 'ready', (result.returncode, payload, result.stderr[-500:]) return payload['endpoint'] diff --git a/tests/gateway/test_multiplex_hot_serve_live.py b/tests/gateway/test_multiplex_hot_serve_live.py index edbc5f7994..6e22b6b396 100644 --- a/tests/gateway/test_multiplex_hot_serve_live.py +++ b/tests/gateway/test_multiplex_hot_serve_live.py @@ -155,7 +155,7 @@ def test_profile_created_under_live_multiplexer_is_served_and_owned(mux): assert served_homes(desc) == {root.resolve(), mux['boot'].resolve()} # ``hermes profile create`` under the multiplexer's root: the CLI signals the control socket. created = subprocess.run([sys.executable, '-m', 'hermes_cli.main', 'profile', 'create', 'fresh'], - cwd=ROOT, env=env, capture_output=True, text=True, timeout=120) + cwd=ROOT, env=env, capture_output=True, text=True, timeout=120, check=False) assert created.returncode == 0, (created.stdout, created.stderr) fresh = root / 'profiles' / 'fresh' assert fresh.is_dir() @@ -173,7 +173,7 @@ def test_profile_created_under_live_multiplexer_is_served_and_owned(mux): assert db.execute('SELECT 1 FROM sessions WHERE id=?', (sid,)).fetchone() # ``hermes -p fresh`` resolves to the multiplexer instead of spawning a daemon of its own. ensured = subprocess.run([sys.executable, '-m', 'hermes_cli.main', '-p', 'fresh', 'gateway', 'ensure', - '--timeout', '15'], cwd=ROOT, env=env, capture_output=True, text=True, timeout=60) + '--timeout', '15'], cwd=ROOT, env=env, capture_output=True, text=True, timeout=60, check=False) payload = json.loads(ensured.stdout.strip().splitlines()[-1]) assert payload['state'] == 'ready' and payload['endpoint']['instance_id'] == desc['instance_id'], (payload, ensured.stderr) assert not (fresh / 'gateway.pid').exists() @@ -214,7 +214,7 @@ def test_named_profile_ensure_during_multiplexer_downtime_starts_the_multiplexer assert recorded_served(root) == ['default', 'boot'] try: ensured = subprocess.run([sys.executable, '-m', 'hermes_cli.main', '-p', 'boot', 'gateway', 'ensure', - '--json', '--timeout', '90'], cwd=ROOT, env=env, capture_output=True, text=True, timeout=120) + '--json', '--timeout', '90'], cwd=ROOT, env=env, capture_output=True, text=True, timeout=120, check=False) payload = json.loads(ensured.stdout.strip().splitlines()[-1]) assert payload['state'] == 'ready', (payload, ensured.stderr) # The answering owner is the multiplexer at the root, serving boot as a secondary. @@ -253,7 +253,7 @@ def test_broken_secondary_state_db_parks_only_that_profile(mux): started = time.monotonic() ensured = subprocess.run([sys.executable, '-m', 'hermes_cli.main', '-p', 'broken', 'gateway', 'ensure', '--json', '--timeout', '30'], cwd=ROOT, env=mux['env'], capture_output=True, - text=True, timeout=60) + text=True, timeout=60, check=False) payload = json.loads(ensured.stdout.strip().splitlines()[-1]) assert (payload['state'], payload['reason_code']) == ('inaccessible', 'profile_parked'), (payload, ensured.stderr) assert 'unusable' in (payload.get('detail') or ''), payload diff --git a/tests/gateway/test_native_ingress_media.py b/tests/gateway/test_native_ingress_media.py index dbb97d6830..484d694049 100644 --- a/tests/gateway/test_native_ingress_media.py +++ b/tests/gateway/test_native_ingress_media.py @@ -42,7 +42,7 @@ def test_accepted_media_survives_mutation_cleanup_and_owner_kill(tmp_path): for calls in (1, 0): command[-1] = 'recover' result = subprocess.run(command, cwd=repo, env=env, stdin=subprocess.DEVNULL, - capture_output=True, text=True, timeout=60) + capture_output=True, text=True, timeout=60, check=False) assert result.returncode == 0, result.stdout + result.stderr receipt = json.loads((state / 'recovered.json').read_text()) assert receipt['model_calls'] == calls, receipt @@ -52,6 +52,6 @@ def test_accepted_media_survives_mutation_cleanup_and_owner_kill(tmp_path): def test_native_context_never_upgrades_metadata_or_transport_trust(tmp_path): command, env, repo, state = _peer(tmp_path, 'guards') result = subprocess.run(command, cwd=repo, env=env, stdin=subprocess.DEVNULL, - capture_output=True, text=True, timeout=45) + capture_output=True, text=True, timeout=45, check=False) assert result.returncode == 0, result.stdout + result.stderr print((state / 'guards.json').read_text()) diff --git a/tests/gateway/test_native_ingress_trust.py b/tests/gateway/test_native_ingress_trust.py index 28464db2c7..538ee89364 100644 --- a/tests/gateway/test_native_ingress_trust.py +++ b/tests/gateway/test_native_ingress_trust.py @@ -14,7 +14,7 @@ def _probe(tmp_path, mode): env.update(HOME=str(home), USERPROFILE=str(home), HERMES_HOME=str(state), PYTHONPATH=str(repo)) result = subprocess.run([sys.executable, str(Path(__file__).parent / 'fixtures' / 'native_ingress_trust_peer.py'), mode], cwd=repo, env=env, - stdin=subprocess.DEVNULL, capture_output=True, text=True, timeout=40) + stdin=subprocess.DEVNULL, capture_output=True, text=True, timeout=40, check=False) assert result.returncode == 0, result.stdout + result.stderr print(result.stdout) diff --git a/tests/gateway/test_native_pause_notice.py b/tests/gateway/test_native_pause_notice.py index d06bb39d49..5c00ea7b8a 100644 --- a/tests/gateway/test_native_pause_notice.py +++ b/tests/gateway/test_native_pause_notice.py @@ -37,7 +37,7 @@ async def test_paused_fifo_releases_the_messaging_waiter_with_one_notice_per_epi from tests.gateway.test_authority_unknown_resolution import Peer, _restarted_owner_with_unknown_head executed = [] - store, authority, ref, unknown, follower = await _restarted_owner_with_unknown_head(tmp_path, monkeypatch, executed) + store, authority, ref, unknown, _follower = await _restarted_owner_with_unknown_head(tmp_path, monkeypatch, executed) try: actor = Principal('human', 'p', frozenset({'session:submit'}), 'fixture') @@ -129,14 +129,14 @@ def test_crash_mid_turn_then_platform_message_gets_one_pause_notice_and_reset_un wait_for(lambda: rows().get(text, {}).get('status') == status, diagnostic) try: - with daemon(root, home, env, barrier=False) as (proc, desc): + with daemon(root, home, env, barrier=False) as (proc, _desc): send('WARM_HISTORY', 'terminal') send('BLOCK_STARTED', 'started') assert model.blocked.wait(20), diagnostic() proc.kill() assert proc.wait(timeout=10) == -9 model.release.set() - with daemon(root, home, env, barrier=False) as (proc, desc): + with daemon(root, home, env, barrier=False) as (proc, _desc): wait_for(lambda: rows()['BLOCK_STARTED']['status'] == 'unknown', diagnostic) paused_session = rows()['BLOCK_STARTED']['session'] # First message onto the paused FIFO: admitted (durable), the user is told once, diff --git a/tests/gateway/test_native_role_reauthorization.py b/tests/gateway/test_native_role_reauthorization.py index b69ba263af..bb29d166ef 100644 --- a/tests/gateway/test_native_role_reauthorization.py +++ b/tests/gateway/test_native_role_reauthorization.py @@ -25,7 +25,7 @@ def _probe(tmp_path, mode): env.update(HOME=str(home), USERPROFILE=str(home), HERMES_HOME=str(state), PYTHONPATH=str(repo)) result = subprocess.run([sys.executable, str(Path(__file__).parent / 'fixtures' / 'native_role_peer.py'), mode], cwd=repo, env=env, - stdin=subprocess.DEVNULL, capture_output=True, text=True, timeout=60) + stdin=subprocess.DEVNULL, capture_output=True, text=True, timeout=60, check=False) assert result.returncode == 0, result.stdout + result.stderr print(result.stdout) diff --git a/tests/gateway/test_native_telegram_startup_recovery.py b/tests/gateway/test_native_telegram_startup_recovery.py index b41c796661..b542742390 100644 --- a/tests/gateway/test_native_telegram_startup_recovery.py +++ b/tests/gateway/test_native_telegram_startup_recovery.py @@ -14,6 +14,7 @@ import importlib.machinery import pytest from tests.gateway.fixtures.local_recovery_probe import Model, child_env, daemon +import itertools class BotAPI(BaseHTTPRequestHandler): @@ -165,7 +166,7 @@ def test_telegram_fifo_unknown_and_current_authorization_survive_sigkill(tmp_pat observed = [next((marker for marker in markers if marker in text), text) for text in texts] assert observed == markers, observed assert len(set(b['pid'] for b in boots)) == len(boots) - assert all(b['epoch'] > a['epoch'] for a, b in zip(boots, boots[1:])) + assert all(b['epoch'] > a['epoch'] for a, b in itertools.pairwise(boots)) assert 'getUpdates' in bot.calls and bot.sent print(json.dumps({'boots': boots, 'model_inputs': observed, 'final_rows': rows()})) finally: diff --git a/tests/gateway/test_operator_cross_surface.py b/tests/gateway/test_operator_cross_surface.py index 8eb5b80417..1bd841e4a5 100644 --- a/tests/gateway/test_operator_cross_surface.py +++ b/tests/gateway/test_operator_cross_surface.py @@ -21,7 +21,7 @@ def test_operator_cross_surface_real_auth_fifo_and_attribution(tmp_path, creator PYTHONPATH=str(root), PYTHONUNBUFFERED='1') result = subprocess.run( [sys.executable, str(root / 'tests/gateway/fixtures/operator_cross_surface_peer.py'), creator], - cwd=root, env=env, capture_output=True, text=True, timeout=150) + cwd=root, env=env, capture_output=True, text=True, timeout=150, check=False) assert result.returncode == 0, result.stdout + result.stderr receipt = json.loads((home / 'operator-receipt.json').read_text()) assert receipt['creator'] == creator diff --git a/tests/gateway/test_relay_authority.py b/tests/gateway/test_relay_authority.py index 04d415fb6f..4983bdabbb 100644 --- a/tests/gateway/test_relay_authority.py +++ b/tests/gateway/test_relay_authority.py @@ -15,7 +15,7 @@ import time def test_authenticated_relay_uses_canonical_admission(tmp_path): env = dict(os.environ, HOME=str(tmp_path), HERMES_HOME=str(tmp_path), PYTHONUNBUFFERED='1') result = subprocess.run([sys.executable, __file__, 'peer'], env=env, - capture_output=True, text=True, timeout=100) + capture_output=True, text=True, timeout=100, check=False) assert result.returncode == 0, result.stdout + result.stderr receipt = json.loads((tmp_path / 'relay-receipt.json').read_text()) assert receipt['same_agent'] and receipt['negative_controls'] diff --git a/tests/gateway/test_retired_media_scan_bound.py b/tests/gateway/test_retired_media_scan_bound.py index 04fdc351e0..da9f49896c 100644 --- a/tests/gateway/test_retired_media_scan_bound.py +++ b/tests/gateway/test_retired_media_scan_bound.py @@ -41,7 +41,7 @@ def test_candidates_share_one_messages_pass(owner, tmp_path, monkeypatch): path.write_bytes(b'x') references.append({'path': str(path), 'sha256': sha, 'size': 1}) for reference in references[:3]: - db.append_message('keep', 'user', '[Image attached at: %s]' % reference['path']) + db.append_message('keep', 'user', '[Image attached at: {}]'.format(reference['path'])) db.append_message('keep', 'user', 'unrelated history') db._execute_write(lambda conn: conn.executemany('INSERT INTO state_meta(key,value) VALUES(?,?)', [(PREFIX + str(index), json.dumps(reference)) for index, reference in enumerate(references)])) diff --git a/tests/gateway/test_runtime_ownership.py b/tests/gateway/test_runtime_ownership.py index 8fbdad6a9b..395bcd5330 100644 --- a/tests/gateway/test_runtime_ownership.py +++ b/tests/gateway/test_runtime_ownership.py @@ -31,7 +31,7 @@ print('CLAIMED', result) witness = tmp_path / 'witness' try: result = subprocess.run([sys.executable, '-c', code], env={**os.environ, 'WITNESS': str(witness)}, - stdin=subprocess.DEVNULL, capture_output=True, text=True, timeout=30) + stdin=subprocess.DEVNULL, capture_output=True, text=True, timeout=30, check=False) assert result.returncode == 0, result.stderr assert not witness.exists(), result.stdout + result.stderr assert 'CLAIMED False' in result.stdout diff --git a/tests/gateway/test_runtime_worker_shutdown.py b/tests/gateway/test_runtime_worker_shutdown.py index 0f7c6beddb..39a1d97415 100644 --- a/tests/gateway/test_runtime_worker_shutdown.py +++ b/tests/gateway/test_runtime_worker_shutdown.py @@ -20,7 +20,7 @@ async def test_managed_claims_participate_in_zero_and_nonzero_drain(tmp_path): 's': SimpleNamespace(control=lambda frame: stopped.append(frame), closed=threading.Event())}) runner = SimpleNamespace(session_authority=authority, _running_agents={}, _running_agent_count=lambda: 0, _active_cron_job_count=lambda: 0, _active_api_run_count=lambda: 0, - _active_deferred_agent_worker_count=lambda: 0, _snapshot_running_agents=lambda: {}, + _active_deferred_agent_worker_count=lambda: 0, _snapshot_running_agents=dict, _update_runtime_status=lambda *a: None) runner._drain_work_counts = GatewayShutdownMixin._drain_work_counts.__get__(runner) authority.runner = runner diff --git a/tests/gateway/test_runtime_writer_drain.py b/tests/gateway/test_runtime_writer_drain.py index 0304edf628..b862e56360 100644 --- a/tests/gateway/test_runtime_writer_drain.py +++ b/tests/gateway/test_runtime_writer_drain.py @@ -12,7 +12,7 @@ import pytest @pytest.mark.platforms("linux") @pytest.mark.parametrize('partial_start', [False, True]) def test_live_runtime_writers_prevent_final_lock_release(tmp_path, monkeypatch, partial_start): - import gateway.run as run + from gateway import run from gateway.run_bootstrap import _start_gateway_start_cron_and_housekeeping from gateway.status import acquire_gateway_runtime_lock, release_gateway_runtime_lock from cron import scheduler_provider @@ -58,7 +58,7 @@ if claimed: def contender(): result = subprocess.run([sys.executable, '-c', probe], env=os.environ.copy(), - stdin=subprocess.DEVNULL, capture_output=True, text=True, timeout=15) + stdin=subprocess.DEVNULL, capture_output=True, text=True, timeout=15, check=False) assert result.returncode == 0, result.stderr return result.stdout.strip() diff --git a/tests/gateway/test_session_a2a.py b/tests/gateway/test_session_a2a.py index 576947c106..c2b7685efa 100644 --- a/tests/gateway/test_session_a2a.py +++ b/tests/gateway/test_session_a2a.py @@ -12,7 +12,7 @@ async def test_forwarded_identity_survives_reconnect_without_policy_override(tmp from gateway.config import GatewayConfig from gateway.session import SessionStore from gateway.session_authority import initialize_session_authority - import gateway.run as run + from gateway import run from gateway.session_policy import build_policy from hermes_state_runtime import RuntimeStoreError @@ -67,7 +67,7 @@ async def test_forwarding_does_not_merge_lossy_context_or_peer_identity(tmp_path from gateway.config import GatewayConfig from gateway.session import SessionStore from gateway.session_authority import initialize_session_authority - import gateway.run as run + from gateway import run monkeypatch.setenv('HERMES_HOME', str(tmp_path)) monkeypatch.setattr(run, '_load_gateway_config', lambda: {'model': {'default': 'fixture'}, 'platform_toolsets': {'cli': []}}) @@ -123,7 +123,7 @@ async def test_forwarded_turns_are_authored_by_the_peer_for_memory_attribution(t from gateway.session_ingress import row_turn_author from agent.turn_author import a2a_key from hermes_state_runtime import list_session_admissions - import gateway.run as run + from gateway import run monkeypatch.setenv('HERMES_HOME', str(tmp_path)) monkeypatch.setattr(run, '_load_gateway_config', lambda: {'model': {'default': 'fixture'}, 'platform_toolsets': {'cli': []}}) @@ -166,8 +166,8 @@ async def test_forward_wakes_on_the_completion_frame_instead_of_a_fixed_100ms_po import asyncio import time from websockets.asyncio.server import serve - import hermes_cli.gateway_client as gateway_client - import hermes_cli.gateway_runtime as gateway_runtime + from hermes_cli import gateway_client + from hermes_cli import gateway_runtime from gateway.session_a2a import forward_to_owner calls = [] diff --git a/tests/gateway/test_session_activity_ordering.py b/tests/gateway/test_session_activity_ordering.py index e7475262d9..ab0ffbfe27 100644 --- a/tests/gateway/test_session_activity_ordering.py +++ b/tests/gateway/test_session_activity_ordering.py @@ -1,5 +1,5 @@ """Recovered UTC routes coexist with legacy local-time routes.""" -from datetime import datetime, timedelta, timezone +from datetime import datetime, timedelta, timezone, UTC from gateway.config import GatewayConfig, Platform from gateway.session import SessionSource, SessionStore @@ -9,7 +9,7 @@ def test_list_sessions_orders_and_filters_mixed_clock_routes(tmp_path): store = SessionStore(sessions_dir=tmp_path / 'sessions', config=GatewayConfig()) recent = store.get_or_create_session(SessionSource(platform=Platform.TELEGRAM, chat_id='recent')) old = store.get_or_create_session(SessionSource(platform=Platform.TELEGRAM, chat_id='old')) - recent.updated_at = datetime.now(timezone.utc) + recent.updated_at = datetime.now(UTC) old.updated_at = datetime.now() - timedelta(hours=2) assert store.list_sessions() == [recent, old] diff --git a/tests/gateway/test_session_ancillary_reads.py b/tests/gateway/test_session_ancillary_reads.py index 04f01642d1..2933043ac2 100644 --- a/tests/gateway/test_session_ancillary_reads.py +++ b/tests/gateway/test_session_ancillary_reads.py @@ -16,7 +16,7 @@ def probe(tmp_path, mode): env.update(HOME=str(user), USERPROFILE=str(user), HERMES_HOME=str(home), PYTHONPATH=str(root), PYTHONUNBUFFERED='1') result = subprocess.run([sys.executable, str(root / 'tests/gateway/fixtures/ancillary_reads_peer.py'), mode], - cwd=root, env=env, capture_output=True, text=True, timeout=90) + cwd=root, env=env, capture_output=True, text=True, timeout=90, check=False) assert result.returncode == 0, result.stdout + result.stderr receipt = json.loads((home / 'receipt.json').read_text()) assert receipt['mode'] == mode and receipt['authenticated_ws'] diff --git a/tests/gateway/test_session_authority.py b/tests/gateway/test_session_authority.py index ee566562f3..d55e77bf48 100644 --- a/tests/gateway/test_session_authority.py +++ b/tests/gateway/test_session_authority.py @@ -23,7 +23,7 @@ def test_messaging_then_authenticated_ws_reuses_live_agent(tmp_path): PYTHONPATH=str(repo), PYTHONUNBUFFERED="1") result = subprocess.run( [sys.executable, str(Path(__file__).parent / "fixtures" / "shared_authority_peer.py")], - cwd=repo, env=env, stdin=subprocess.DEVNULL, capture_output=True, text=True, timeout=100, + cwd=repo, env=env, stdin=subprocess.DEVNULL, capture_output=True, text=True, timeout=100, check=False ) assert result.returncode == 0, result.stdout + "\n" + result.stderr receipt = json.loads((state / "receipt.json").read_text()) @@ -63,7 +63,7 @@ def test_native_queue_survives_owner_kill_without_replaying_unknown(tmp_path): env['AUTHORITY_PROBE_MODE'] = 'recover' for expected_calls in (1, 0): result = subprocess.run(command, cwd=repo, env=env, stdin=subprocess.DEVNULL, - capture_output=True, text=True, timeout=70) + capture_output=True, text=True, timeout=70, check=False) assert result.returncode == 0, result.stdout + '\n' + result.stderr receipt = json.loads((state / 'recovery-receipt.json').read_text()) assert receipt['model_calls'] == expected_calls, receipt diff --git a/tests/gateway/test_session_bypass_policy.py b/tests/gateway/test_session_bypass_policy.py index 991969fb57..355002d74c 100644 --- a/tests/gateway/test_session_bypass_policy.py +++ b/tests/gateway/test_session_bypass_policy.py @@ -37,7 +37,7 @@ def test_bypass_session_freezes_code_defaults_without_reading_profile(tmp_path, from types import SimpleNamespace from gateway.session_local import _bypass_policy from hermes_cli.config_defaults import DEFAULT_CONFIG - import gateway.session_policy, hermes_cli.tools_config, toolsets, tools.terminal_scope # noqa: F401,E401 + import gateway.session_policy, hermes_cli.tools_config, toolsets, tools.terminal_scope from hermes_cli.plugins import discover_plugins home = tmp_path / 'home' diff --git a/tests/gateway/test_session_kanban.py b/tests/gateway/test_session_kanban.py index c89b1da81c..bce67f614b 100644 --- a/tests/gateway/test_session_kanban.py +++ b/tests/gateway/test_session_kanban.py @@ -32,7 +32,7 @@ def test_claim_freezes_task_policy_and_rejects_forgery(tmp_path, monkeypatch): params = dict(board='owned', task_id=task.id, run_id=task.current_run_id, claim_lock=task.claim_lock) from gateway.session_kanban import build_kanban_policy config = {'model': {'default': 'loop-model', 'provider': 'custom'}, 'platform_toolsets': {'cli': ['terminal', 'file']}} - policy, secrets = build_kanban_policy(connection, params, config) + policy, _secrets = build_kanban_policy(connection, params, config) context = json.loads(policy.kanban_json) assert policy.source == 'kanban' and policy.cwd == str(workspace) assert policy.model == task.model_override and policy.provider == task.provider_override @@ -62,7 +62,7 @@ def test_real_dispatcher_lifecycle(tmp_path, mode): env = child_env() env.update(HOME=str(tmp_path / 'home'), USERPROFILE=str(tmp_path / 'home'), HERMES_HOME=str(tmp_path / 'state'), PYTHONPATH=str(repo), KANBAN_PROBE_MODE=mode) result = subprocess.run([sys.executable, str(Path(__file__).parent / 'fixtures' / 'kanban_owner_probe.py')], - env=env, cwd=repo, stdin=subprocess.DEVNULL, capture_output=True, text=True, timeout=150) + env=env, cwd=repo, stdin=subprocess.DEVNULL, capture_output=True, text=True, timeout=150, check=False) assert result.returncode == 0, result.stdout + result.stderr receipt = json.loads((tmp_path / 'state' / 'receipt.json').read_text()) print(json.dumps(receipt)) diff --git a/tests/gateway/test_session_listener_lazy_dashboard.py b/tests/gateway/test_session_listener_lazy_dashboard.py index 1c7a2d0f30..115fc4ab12 100644 --- a/tests/gateway/test_session_listener_lazy_dashboard.py +++ b/tests/gateway/test_session_listener_lazy_dashboard.py @@ -82,7 +82,7 @@ def test_session_listener_serves_native_ws_before_importing_the_dashboard(tmp_pa env = {k: v for k, v in os.environ.items() if k in {"PATH", "LANG", "TZ", "HERMES_RUNTIME_DIR", "TMPDIR"}} env.update(HOME=str(tmp_path), HERMES_HOME=str(home), PYTHONPATH=str(REPO)) proc = subprocess.run([sys.executable, "-c", _PROBE, str(home)], cwd=str(REPO), env=env, - capture_output=True, text=True, timeout=120) + capture_output=True, text=True, timeout=120, check=False) lines = [ln for ln in (proc.stdout + proc.stderr).splitlines() if ln.startswith("RESULT ")] assert proc.returncode == 0 and lines, proc.stderr[-3000:] out = json.loads(lines[-1][len("RESULT "):]) diff --git a/tests/gateway/test_session_mutation_model.py b/tests/gateway/test_session_mutation_model.py index ba41338952..2f88bc6ae6 100644 --- a/tests/gateway/test_session_mutation_model.py +++ b/tests/gateway/test_session_mutation_model.py @@ -95,7 +95,7 @@ def test_model_receipt_changes_next_wire_and_branch_keeps_independent_history(tm return params, result try: with daemon(root, home, env, barrier=False) as (_, desc): - params, result = asyncio.run(run(desc)) + params, _result = asyncio.run(run(desc)) async def retry(desc): async with websocket(home, desc) as ws: info = await rpc(ws, 'session.info', session_id=params['session_id']) diff --git a/tests/gateway/test_session_policy.py b/tests/gateway/test_session_policy.py index 28ea44f189..f3f3cbb24a 100644 --- a/tests/gateway/test_session_policy.py +++ b/tests/gateway/test_session_policy.py @@ -116,7 +116,7 @@ def test_launch_policy_reaches_real_turn_runner(tmp_path): env = child_env() env.update(HOME=str(home), USERPROFILE=str(home), HERMES_HOME=str(state), PYTHONPATH=str(repo)) result = subprocess.run([sys.executable, str(Path(__file__).parent / 'fixtures' / 'session_policy_peer.py')], - cwd=repo, env=env, stdin=subprocess.DEVNULL, capture_output=True, text=True, encoding='utf-8', errors='replace', timeout=130) + cwd=repo, env=env, stdin=subprocess.DEVNULL, capture_output=True, text=True, encoding='utf-8', errors='replace', timeout=130, check=False) assert result.returncode == 0, result.stdout + '\n' + result.stderr receipt = json.loads((state / 'policy-receipt.json').read_text()) assert receipt['cwd_effects'] and receipt['same_agents'] and receipt['no_spill'] @@ -199,7 +199,7 @@ def test_ordinary_daemon_cli_launch_policy(tmp_path): 'import json,sys; from pathlib import Path; ' 'from tests.gateway.fixtures.cli_launch_policy_probe import probe; ' 'print(json.dumps(probe(Path(sys.argv[1]))))', str(tmp_path)], cwd=root, - stdin=subprocess.DEVNULL, capture_output=True, text=True, encoding='utf-8', errors='replace', timeout=180) + stdin=subprocess.DEVNULL, capture_output=True, text=True, encoding='utf-8', errors='replace', timeout=180, check=False) assert result.returncode == 0, result.stdout + result.stderr print(json.dumps(json.loads(result.stdout.splitlines()[-1]))) diff --git a/tests/gateway/test_session_process_scope.py b/tests/gateway/test_session_process_scope.py index 65114196d9..ab731f6f25 100644 --- a/tests/gateway/test_session_process_scope.py +++ b/tests/gateway/test_session_process_scope.py @@ -20,7 +20,7 @@ def test_stop_is_session_scoped_and_global_verbs_keep_the_owner_refusal(tmp_path env.update(HOME=str(user), USERPROFILE=str(user), HERMES_HOME=str(home), PYTHONPATH=str(root), PYTHONUNBUFFERED='1') result = subprocess.run([sys.executable, str(root / 'tests/gateway/fixtures/process_scope_peer.py')], - cwd=root, env=env, capture_output=True, text=True, timeout=90) + cwd=root, env=env, capture_output=True, text=True, timeout=90, check=False) assert result.returncode == 0, result.stdout + result.stderr receipt = json.loads((home / 'receipt.json').read_text()) assert receipt['stop'] == {'killed': 2}, receipt diff --git a/tests/gateway/test_session_shell.py b/tests/gateway/test_session_shell.py index bd29d5e727..af84249b33 100644 --- a/tests/gateway/test_session_shell.py +++ b/tests/gateway/test_session_shell.py @@ -23,7 +23,7 @@ def test_shell_exec_runs_in_the_session_cwd_behind_the_safety_gates(tmp_path): env.update(HOME=str(user), USERPROFILE=str(user), HERMES_HOME=str(home), PYTHONPATH=str(root), PYTHONUNBUFFERED='1') result = subprocess.run([sys.executable, str(root / 'tests/gateway/fixtures/shell_exec_peer.py')], - cwd=root, env=env, capture_output=True, text=True, timeout=90) + cwd=root, env=env, capture_output=True, text=True, timeout=90, check=False) assert result.returncode == 0, result.stdout + result.stderr receipt = json.loads((home / 'receipt.json').read_text()) assert receipt['owner_cwd'] != receipt['session_cwd'] diff --git a/tests/gateway/test_shared_clarify.py b/tests/gateway/test_shared_clarify.py index bb34e02167..942e327fbf 100644 --- a/tests/gateway/test_shared_clarify.py +++ b/tests/gateway/test_shared_clarify.py @@ -14,7 +14,7 @@ def test_real_shared_clarify_survives_detach(tmp_path): env.update(HOME=str(home), USERPROFILE=str(home), HERMES_HOME=str(state), PYTHONPATH=str(repo)) result = subprocess.run( [sys.executable, str(Path(__file__).parent / 'fixtures' / 'authority_clarify_peer.py')], - cwd=repo, env=env, stdin=subprocess.DEVNULL, capture_output=True, text=True, timeout=60) + cwd=repo, env=env, stdin=subprocess.DEVNULL, capture_output=True, text=True, timeout=60, check=False) assert result.returncode == 0, result.stdout + result.stderr assert (state / 'clarify-passed').read_text() == 'reply reached real model; stale reply rejected' diff --git a/tests/gateway/test_shared_session_controls.py b/tests/gateway/test_shared_session_controls.py index c2689bb7b0..c39d465688 100644 --- a/tests/gateway/test_shared_session_controls.py +++ b/tests/gateway/test_shared_session_controls.py @@ -14,7 +14,7 @@ def test_shared_approval_outlives_viewer_and_bypasses_fifo(tmp_path): env = {k: os.environ[k] for k in ('PATH', 'SYSTEMROOT', 'LANG', 'TZ', 'TIRITH_ENABLED') if k in os.environ} env.update(HOME=str(home), USERPROFILE=str(home), HERMES_HOME=str(state), PYTHONPATH=str(repo)) result = subprocess.run([sys.executable, str(Path(__file__).parent / 'fixtures' / 'authority_controls_peer.py')], - cwd=repo, env=env, stdin=subprocess.DEVNULL, capture_output=True, text=True, timeout=100) + cwd=repo, env=env, stdin=subprocess.DEVNULL, capture_output=True, text=True, timeout=100, check=False) assert result.returncode == 0, result.stdout + '\n' + result.stderr receipt = json.loads((state / 'receipt.json').read_text()) print(json.dumps(receipt)) diff --git a/tests/gateway/test_shutdown_runtime_ownership.py b/tests/gateway/test_shutdown_runtime_ownership.py index fbe666605e..e91ac8420d 100644 --- a/tests/gateway/test_shutdown_runtime_ownership.py +++ b/tests/gateway/test_shutdown_runtime_ownership.py @@ -11,7 +11,7 @@ import pytest @pytest.mark.platforms("linux") @pytest.mark.parametrize('timed_out', [False, True]) def test_exit_state_keeps_runtime_reserved_until_final_cleanup(tmp_path, monkeypatch, timed_out): - import gateway.run as run + from gateway import run from gateway.run_shutdown import GatewayShutdownMixin from gateway.status import acquire_gateway_runtime_lock, release_gateway_runtime_lock, write_pid_file, remove_pid_file @@ -37,7 +37,7 @@ if claimed: def contender(): result = subprocess.run( [sys.executable, '-c', probe], cwd=os.getcwd(), env=os.environ.copy(), - stdin=subprocess.DEVNULL, capture_output=True, text=True, timeout=15, + stdin=subprocess.DEVNULL, capture_output=True, text=True, timeout=15, check=False ) assert result.returncode == 0, result.stderr return result.stdout.strip() diff --git a/tests/gateway/test_surface_admission.py b/tests/gateway/test_surface_admission.py index a2fd986514..5b959a10cf 100644 --- a/tests/gateway/test_surface_admission.py +++ b/tests/gateway/test_surface_admission.py @@ -155,7 +155,7 @@ def test_real_daemon_accepts_hud_and_voice_live_submits_and_notes_the_model(tmp_ return replies try: - with daemon(root, home, env, barrier=False) as (proc, desc): + with daemon(root, home, env, barrier=False) as (_proc, desc): replies = asyncio.run(drive(desc)) finally: model.release.set() @@ -221,7 +221,7 @@ def test_managed_worker_turn_carries_the_committed_surface_to_the_model(tmp_path await asyncio.to_thread(_wait_for, lambda: _admissions(home).get(name) == 'terminal') try: - with daemon(root, home, env, barrier=False) as (proc, desc): + with daemon(root, home, env, barrier=False) as (_proc, desc): asyncio.run(drive(desc)) finally: model.release.set() diff --git a/tests/gateway/test_terminal_commit_crash.py b/tests/gateway/test_terminal_commit_crash.py index b2049167cf..e46882e529 100644 --- a/tests/gateway/test_terminal_commit_crash.py +++ b/tests/gateway/test_terminal_commit_crash.py @@ -85,10 +85,10 @@ def test_terminal_commit_before_publication_survives_kill(tmp_path): assert snapshot()[1] == saved try: - with daemon(root, home, env, barrier=True, fixture='terminal_commit_daemon.py') as (proc, desc): + with daemon(root, home, env, barrier=True, fixture='terminal_commit_daemon.py') as (proc, _desc): pids.append(proc.pid) saved, follower = asyncio.run(first(proc)) - with daemon(root, home, env, barrier=False) as (proc, desc): + with daemon(root, home, env, barrier=False) as (proc, _desc): pids.append(proc.pid) asyncio.run(restart(saved, follower)) texts = [next(m.get('content', '') for m in reversed(r['messages']) if m['role'] == 'user') for r in peer.requests] diff --git a/tests/gateway/test_unified_gateway_native_live.py b/tests/gateway/test_unified_gateway_native_live.py index 6e9ac68b93..3f51f17297 100644 --- a/tests/gateway/test_unified_gateway_native_live.py +++ b/tests/gateway/test_unified_gateway_native_live.py @@ -12,7 +12,7 @@ No POSIX-only primitive is used (no AF_UNIX at import, no SIGKILL, no pty). """ import asyncio from contextlib import closing, contextmanager -from datetime import datetime, timezone +from datetime import datetime, timezone, UTC from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer import json import os @@ -146,7 +146,7 @@ def planned_stop(home, proc, desc): the Popen handle: a Windows uv venv ``python.exe`` is a trampoline whose pid differs.""" pid = desc['pid'] marker = {'target_pid': pid, 'target_start_time': process_start_time(pid), - 'stopper_pid': os.getpid(), 'written_at': datetime.now(timezone.utc).isoformat()} + 'stopper_pid': os.getpid(), 'written_at': datetime.now(UTC).isoformat()} (home / '.gateway-planned-stop.json').write_text(json.dumps(marker), encoding='utf-8') try: return proc.wait(timeout=90) diff --git a/tests/gateway/test_used_delete_restart.py b/tests/gateway/test_used_delete_restart.py index 222d41ce90..45541d9925 100644 --- a/tests/gateway/test_used_delete_restart.py +++ b/tests/gateway/test_used_delete_restart.py @@ -106,13 +106,13 @@ def test_used_api_and_native_delete_restart_exact_retry(tmp_path): assert db.execute("SELECT 1 FROM session_admissions WHERE request_id='busy-once'").fetchone() try: - with daemon(root, home, env, barrier=True) as (proc, desc): + with daemon(root, home, env, barrier=True) as (_proc, desc): first = asyncio.run(api_request()) native_sid = asyncio.run(native_turn(desc)) asyncio.run(busy_delete_refused(desc)) delete(desc, 'used-api'); delete(desc, native_sid) before = len(peer.requests) - with daemon(root, home, env, barrier=False) as (proc, desc): + with daemon(root, home, env, barrier=False) as (_proc, desc): with httpx.Client(base_url=desc['api_origin'], trust_env=False) as client: for sid, (params, receipt) in receipts.items(): path = '/api/sessions/' + sid diff --git a/tests/gateway/test_webhook_authority.py b/tests/gateway/test_webhook_authority.py index a135b1dee2..a13bf31e55 100644 --- a/tests/gateway/test_webhook_authority.py +++ b/tests/gateway/test_webhook_authority.py @@ -11,7 +11,7 @@ import sys def test_signed_webhook_storage_failure_is_retryable(tmp_path): result = subprocess.run([sys.executable, __file__], - env=dict(os.environ, HERMES_HOME=str(tmp_path)), capture_output=True, text=True, timeout=90) + env=dict(os.environ, HERMES_HOME=str(tmp_path)), capture_output=True, text=True, timeout=90, check=False) assert result.returncode == 0, result.stdout + result.stderr print((tmp_path / 'webhook-receipt.json').read_text()) diff --git a/tests/gateway/test_worker_admission_reservation.py b/tests/gateway/test_worker_admission_reservation.py index 847b70d61c..49b64b7736 100644 --- a/tests/gateway/test_worker_admission_reservation.py +++ b/tests/gateway/test_worker_admission_reservation.py @@ -57,7 +57,7 @@ async def test_reserved_process_uses_existing_authenticated_adoption_and_persist from gateway.session_worker import worker_request from gateway.session_worker_reservation import reserve_admission_worker db = SessionDB(tmp_path / 'state.db') - process = subprocess.Popen([sys.executable, '-c', 'import sys; sys.stdin.read()'], stdin=subprocess.PIPE, + process = subprocess.Popen([sys.executable, '-c', 'import sys; sys.stdin.read()'], stdin=subprocess.PIPE, # noqa: ASYNC220 -- Popen returns immediately; the test drives the child through its handle stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) try: db.create_session('owned', 'cli') @@ -113,7 +113,7 @@ async def test_input_queued_behind_a_live_worker_is_released_then_runs_when_it_f ran.append(row['admission_id']) return 'answered' monkeypatch.setattr(session_finite, 'execute_finite_admission', execute) - process = subprocess.Popen([sys.executable, '-c', 'import sys; sys.stdin.read()'], stdin=subprocess.PIPE, + process = subprocess.Popen([sys.executable, '-c', 'import sys; sys.stdin.read()'], stdin=subprocess.PIPE, # noqa: ASYNC220 -- Popen returns immediately; the test drives the child through its handle stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) actor = Principal('human', 'p', frozenset({'session:submit', 'session:control', 'worker:adopt'}), 't') connection = SimpleNamespace(authority=authority, actor=actor) diff --git a/tests/gateway/test_worker_agent_execution.py b/tests/gateway/test_worker_agent_execution.py index 5532eddf6f..ce971664f3 100644 --- a/tests/gateway/test_worker_agent_execution.py +++ b/tests/gateway/test_worker_agent_execution.py @@ -72,7 +72,7 @@ def test_real_agent_worker_persists_context_usage_and_releases_lease(tmp_path, p result = subprocess.run([sys.executable, str(root / 'tests/gateway/fixtures/agent_persistence_worker.py')], cwd=root, env=env, input=json.dumps({'home': str(home), 'url': url, 'session_id': sid, 'receipt': str(receipt), 'probe_constructors': probe_constructors}) + '\n', - text=True, capture_output=True, timeout=60) + text=True, capture_output=True, timeout=60, check=False) assert result.returncode == 0, result.stdout + result.stderr proof = json.loads(receipt.read_text()) assert not proof['failed'], proof diff --git a/tests/gateway/test_worker_compression_execution.py b/tests/gateway/test_worker_compression_execution.py index a45c652f74..5338d2172b 100644 --- a/tests/gateway/test_worker_compression_execution.py +++ b/tests/gateway/test_worker_compression_execution.py @@ -47,7 +47,7 @@ def test_ordinary_daemon_worker_compresses_without_canonical_sqlite_opens(tmp_pa receipt = tmp_path / 'receipt.json' result = subprocess.run([sys.executable, str(root / 'tests/gateway/fixtures/compression_persistence_worker.py')], cwd=root, env=env, input=json.dumps(dict(home=str(home), url=url, session_id=sid, - foreign=foreign, receipt=str(receipt))) + '\n', text=True, capture_output=True, timeout=120) + foreign=foreign, receipt=str(receipt))) + '\n', text=True, capture_output=True, timeout=120, check=False) assert result.returncode == 0, result.stdout + result.stderr proof = json.loads(receipt.read_text()) assert proof['opens'] == proof['fds'] == [] diff --git a/tests/gateway/test_worker_ledger_tool_execution.py b/tests/gateway/test_worker_ledger_tool_execution.py index e6feaab37c..ab42efc24b 100644 --- a/tests/gateway/test_worker_ledger_tool_execution.py +++ b/tests/gateway/test_worker_ledger_tool_execution.py @@ -80,7 +80,7 @@ def test_real_tool_enabled_worker_never_opens_canonical_database(tmp_path): worker = subprocess.run([sys.executable, str(root / 'tests/gateway/fixtures/ledger_tool_worker.py')], cwd=root, env=env, input=json.dumps({'home': str(home), 'url': url, 'session_id': sid, 'receipt': str(receipt)}) + '\n', - text=True, capture_output=True, timeout=90) + text=True, capture_output=True, timeout=90, check=False) assert worker.returncode == 0, worker.stdout + worker.stderr proof = json.loads(receipt.read_text()) assert not proof['failed'] and proof['result'] == 'WORKER_TOOL_LEDGER_OK', proof diff --git a/tests/gateway/test_worker_rotation_execution.py b/tests/gateway/test_worker_rotation_execution.py index 6f27a10451..9fe174d9b4 100644 --- a/tests/gateway/test_worker_rotation_execution.py +++ b/tests/gateway/test_worker_rotation_execution.py @@ -47,7 +47,7 @@ def test_full_agent_worker_rotation_continues_inference_without_canonical_opens( receipt = tmp_path / 'receipt.json' result = subprocess.run([sys.executable, str(root / 'tests/gateway/fixtures/rotation_persistence_worker.py')], cwd=root, env=env, input=json.dumps(dict(home=str(home), url=url, session_id=sid, - foreign=foreign, receipt=str(receipt))) + '\n', text=True, capture_output=True, timeout=120) + foreign=foreign, receipt=str(receipt))) + '\n', text=True, capture_output=True, timeout=120, check=False) assert result.returncode == 0, result.stdout + result.stderr proof = json.loads(receipt.read_text()) assert proof['opens'] == proof['fds'] == [], json.dumps(proof['opens'], indent=2) diff --git a/tests/gateway/test_yolo_command.py b/tests/gateway/test_yolo_command.py index 4481e33ea0..3b95acc763 100644 --- a/tests/gateway/test_yolo_command.py +++ b/tests/gateway/test_yolo_command.py @@ -63,7 +63,7 @@ async def test_yolo_command_toggles_only_current_session(monkeypatch): def test_launch_yolo_revocation_survives_the_next_turn(monkeypatch): """`hermes chat --yolo` seeds the bypass once; a later `/yolo` off is not re-enabled per turn.""" from types import SimpleNamespace - import gateway.session_policy as session_policy + from gateway import session_policy from gateway.run_turn_runner import TurnRunner from gateway.turn_context import TurnContext diff --git a/tests/gateway/test_yolo_launch_tristate.py b/tests/gateway/test_yolo_launch_tristate.py index 223562e2ac..71774d176f 100644 --- a/tests/gateway/test_yolo_launch_tristate.py +++ b/tests/gateway/test_yolo_launch_tristate.py @@ -36,7 +36,7 @@ def test_untoggled_entry_keeps_launch_yolo_after_restart_but_explicit_off_stays_ def restarted(key): result = subprocess.run([sys.executable, '-c', _RESTARTED_OWNER, str(tmp_path), key], - capture_output=True, text=True, timeout=30) + capture_output=True, text=True, timeout=30, check=False) assert result.returncode == 0, result.stderr return result.stdout.strip() assert restarted(untouched.session_key) == 'ENABLED True', 'launch --yolo lost for an entry nobody revoked' diff --git a/tests/gateway/test_yolo_restart_revocation.py b/tests/gateway/test_yolo_restart_revocation.py index 89a740b544..cd1d084298 100644 --- a/tests/gateway/test_yolo_restart_revocation.py +++ b/tests/gateway/test_yolo_restart_revocation.py @@ -29,7 +29,7 @@ _session_yolo(SimpleNamespace(runner=SimpleNamespace(session_store=store)), assert not is_session_yolo_enabled(entry.session_key), 'revoked launch was re-enabled' ''' result = subprocess.run([sys.executable, '-c', probe, str(tmp_path), entry.session_key], - capture_output=True, text=True, timeout=30) + capture_output=True, text=True, timeout=30, check=False) assert result.returncode == 0, result.stdout + result.stderr diff --git a/tests/hermes_cli/test_corelane_frozen_worker_skill_env.py b/tests/hermes_cli/test_corelane_frozen_worker_skill_env.py index 6aa12711e3..e78e714a85 100644 --- a/tests/hermes_cli/test_corelane_frozen_worker_skill_env.py +++ b/tests/hermes_cli/test_corelane_frozen_worker_skill_env.py @@ -41,7 +41,7 @@ def _run(tmp_path, mode): env = child_env() env.update(HOME=str(home), USERPROFILE=str(home), HERMES_HOME=str(home), PROBE_MODE=mode, HERMES_DISABLE_LAZY_INSTALLS="1") proc = subprocess.run([sys.executable, str(script)], cwd=ROOT, env=env, stdin=subprocess.DEVNULL, - capture_output=True, text=True, timeout=90) + capture_output=True, text=True, timeout=90, check=False) assert proc.returncode == 0, proc.stdout + proc.stderr return json.loads(proc.stdout.strip().splitlines()[-1]) diff --git a/tests/hermes_cli/test_gateway_chat_commands_live.py b/tests/hermes_cli/test_gateway_chat_commands_live.py index 6cadbb9fa9..5467c26e87 100644 --- a/tests/hermes_cli/test_gateway_chat_commands_live.py +++ b/tests/hermes_cli/test_gateway_chat_commands_live.py @@ -41,7 +41,7 @@ def test_classic_view_title_undo_retry_new_usage_tools_and_reads(tmp_path): def tmux(*args): return subprocess.run(["tmux", "-L", tmux_name, *args], env=env, stdin=subprocess.DEVNULL, - capture_output=True, text=True, timeout=10) + capture_output=True, text=True, timeout=10, check=False) def screen(): return tmux("capture-pane", "-p", "-J", "-S", "-3000", "-t", "chat").stdout diff --git a/tests/hermes_cli/test_gateway_chat_flags_live.py b/tests/hermes_cli/test_gateway_chat_flags_live.py index b2280a2b24..3eeaed41ee 100644 --- a/tests/hermes_cli/test_gateway_chat_flags_live.py +++ b/tests/hermes_cli/test_gateway_chat_flags_live.py @@ -40,7 +40,7 @@ def test_continue_latest_footer_and_tool_listing(tmp_path): def hermes(*args, cwd, module="hermes_cli.main"): result = subprocess.run([sys.executable, "-m", module, *args], cwd=cwd, env=env, - stdin=subprocess.DEVNULL, capture_output=True, text=True, timeout=120) + stdin=subprocess.DEVNULL, capture_output=True, text=True, timeout=120, check=False) sid = next((line.split("Session: ", 1)[1].strip() for line in result.stderr.splitlines() if line.startswith("Session: ")), None) return result, sid diff --git a/tests/hermes_cli/test_gateway_chat_native.py b/tests/hermes_cli/test_gateway_chat_native.py index 2dfece5682..6948209b02 100644 --- a/tests/hermes_cli/test_gateway_chat_native.py +++ b/tests/hermes_cli/test_gateway_chat_native.py @@ -63,7 +63,7 @@ finally: def tmux(*args): return subprocess.run(["tmux", "-L", tmux_name, *args], env=env, - stdin=subprocess.DEVNULL, capture_output=True, text=True, timeout=10) + stdin=subprocess.DEVNULL, capture_output=True, text=True, timeout=10, check=False) def screen(): result = tmux("capture-pane", "-p", "-S", "-1000", "-t", "chat") diff --git a/tests/hermes_cli/test_gateway_chat_title_live.py b/tests/hermes_cli/test_gateway_chat_title_live.py index ed68a1ec32..ed5d34acdf 100644 --- a/tests/hermes_cli/test_gateway_chat_title_live.py +++ b/tests/hermes_cli/test_gateway_chat_title_live.py @@ -46,7 +46,7 @@ finally: def chat(*args): result = subprocess.run([sys.executable, str(wrapper), "--cli", "chat", *args, "-Q"], env=env, - cwd=tmp_path, stdin=subprocess.DEVNULL, capture_output=True, text=True, timeout=90) + cwd=tmp_path, stdin=subprocess.DEVNULL, capture_output=True, text=True, timeout=90, check=False) assert json.loads(witness.read_text(encoding="utf-8")) == [], witness.read_text(encoding="utf-8") sid = next((line.split("Session: ", 1)[1].strip() for line in result.stderr.splitlines() if "Session: " in line), None) diff --git a/tests/hermes_cli/test_gateway_first_run_live.py b/tests/hermes_cli/test_gateway_first_run_live.py index 57ed7ae4aa..b20d7e6ca0 100644 --- a/tests/hermes_cli/test_gateway_first_run_live.py +++ b/tests/hermes_cli/test_gateway_first_run_live.py @@ -19,7 +19,7 @@ def test_headless_first_run_exits_with_setup_guidance_before_gateway(tmp_path, a PYTHONPATH=str(root), PYTHONUNBUFFERED="1") result = subprocess.run( [sys.executable, "-m", "hermes_cli.main", *argv], cwd=tmp_path, env=env, - stdin=subprocess.DEVNULL, capture_output=True, text=True, timeout=40, + stdin=subprocess.DEVNULL, capture_output=True, text=True, timeout=40, check=False ) assert result.returncode == 1, (result.stdout, result.stderr) assert "hermes config set model.provider custom" in result.stdout diff --git a/tests/hermes_cli/test_gateway_multiplex_mode.py b/tests/hermes_cli/test_gateway_multiplex_mode.py index 3f6c70d00a..43cb58b7a9 100644 --- a/tests/hermes_cli/test_gateway_multiplex_mode.py +++ b/tests/hermes_cli/test_gateway_multiplex_mode.py @@ -228,7 +228,7 @@ def test_pre_lock_decision_never_publishes_a_rival_gateway_owner(tmp_path): """) def rival() -> list: - out = subprocess.run([sys.executable, "-c", probe], env=env, text=True, capture_output=True, timeout=60) + out = subprocess.run([sys.executable, "-c", probe], env=env, text=True, capture_output=True, timeout=60, check=False) assert out.returncode == 0, out.stderr return json.loads(out.stdout) try: diff --git a/tests/hermes_cli/test_gateway_runtime.py b/tests/hermes_cli/test_gateway_runtime.py index 740089e9cc..57a2fa2e1c 100644 --- a/tests/hermes_cli/test_gateway_runtime.py +++ b/tests/hermes_cli/test_gateway_runtime.py @@ -94,7 +94,7 @@ def test_fifo_control_pointer_is_rejected_without_a_writer(tmp_path): result = subprocess.run( [sys.executable, '-c', code, str(home)], cwd=Path(__file__).resolve().parents[2], stdin=subprocess.DEVNULL, - capture_output=True, text=True, timeout=3, + capture_output=True, text=True, timeout=3, check=False ) except subprocess.TimeoutExpired: pytest.fail('discovery blocked on a FIFO instead of rejecting its file type') diff --git a/tests/hermes_cli/test_gateway_runtime_ensure.py b/tests/hermes_cli/test_gateway_runtime_ensure.py index 4a890f3709..9c1160d1ac 100644 --- a/tests/hermes_cli/test_gateway_runtime_ensure.py +++ b/tests/hermes_cli/test_gateway_runtime_ensure.py @@ -144,17 +144,17 @@ def test_public_ensure_json_deadline_and_invalid_invocation(tmp_path): encoding="utf-8") env = {**os.environ, "HERMES_HOME": str(home)} result = subprocess.run([sys.executable, "-m", "hermes_cli.main", "gateway", "ensure", "--json"], - env=env, stdin=subprocess.DEVNULL, capture_output=True, text=True, timeout=15) + env=env, stdin=subprocess.DEVNULL, capture_output=True, text=True, timeout=15, check=False) assert result.returncode == 6, result.stderr value = json.loads(result.stdout) assert value["state"] == "draining" and value["reason_code"] == "update_paused" assert "private-token" not in result.stdout result = subprocess.run([sys.executable, "-m", "hermes_cli.main", "gateway", "ensure", "--json", "--timeout", "nan"], - env=env, stdin=subprocess.DEVNULL, capture_output=True, text=True, timeout=15) + env=env, stdin=subprocess.DEVNULL, capture_output=True, text=True, timeout=15, check=False) assert result.returncode == 2 assert json.loads(result.stdout)["reason_code"] == "invalid_invocation" result = subprocess.run([sys.executable, "-m", "hermes_cli.main", "gateway", "ensure", "--json", "--unknown", "private-value"], - env=env, stdin=subprocess.DEVNULL, capture_output=True, text=True, timeout=15) + env=env, stdin=subprocess.DEVNULL, capture_output=True, text=True, timeout=15, check=False) assert result.returncode == 2 assert json.loads(result.stdout)["reason_code"] == "invalid_invocation" assert "private-value" not in result.stdout @@ -209,7 +209,7 @@ def test_unmanaged_child_uses_explicit_home_and_survives_launcher_exit(tmp_path, "print(child.pid, flush=True)\n", encoding="utf-8") env = {**os.environ, "PYTHONPATH": str(Path.cwd()), "HERMES_HOME": str(tmp_path / "wrong")} result = subprocess.run([sys.executable, str(launcher)], env=env, stdin=subprocess.DEVNULL, - capture_output=True, text=True, timeout=10) + capture_output=True, text=True, timeout=10, check=False) try: assert result.returncode == 0, result.stderr deadline = time.monotonic() + 5 diff --git a/tests/hermes_cli/test_gateway_runtime_service.py b/tests/hermes_cli/test_gateway_runtime_service.py index 69031de85c..b84620b0a8 100644 --- a/tests/hermes_cli/test_gateway_runtime_service.py +++ b/tests/hermes_cli/test_gateway_runtime_service.py @@ -242,10 +242,10 @@ def test_native_task_query_uses_installed_xml_and_vendor_launcher(wrong, tmp_pat script.write_text(_build_gateway_vbs_script(sys.executable, str(home), str(home), ''), encoding='utf-8', newline='') original = script.read_bytes() # whoami is read-only native account evidence; no task-manager mutation. - identity = subprocess.run(['whoami.exe', '/USER', '/FO', 'CSV', '/NH'], stdin=subprocess.DEVNULL, capture_output=True, timeout=5) + identity = subprocess.run(['whoami.exe', '/USER', '/FO', 'CSV', '/NH'], stdin=subprocess.DEVNULL, capture_output=True, timeout=5, check=False) from hermes_cli.gateway_windows import _schtasks_encoding import csv - account, sid = next(csv.reader(identity.stdout.decode(_schtasks_encoding()).splitlines())) + _account, sid = next(csv.reader(identity.stdout.decode(_schtasks_encoding()).splitlines())) suffix = service.service_suffix(home) name = 'Hermes_Gateway' + ('_' + suffix if suffix else '') xml = _build_scheduled_task_xml(name, script, 'S-1-5-18' if wrong else sid) diff --git a/tests/hermes_cli/test_gateway_service_choice.py b/tests/hermes_cli/test_gateway_service_choice.py index adf11ea781..2af6dc7b90 100644 --- a/tests/hermes_cli/test_gateway_service_choice.py +++ b/tests/hermes_cli/test_gateway_service_choice.py @@ -91,7 +91,7 @@ def test_import_and_ordinary_setup_never_authorize_install(supervisor, choice, i @pytest.mark.platforms("linux") @pytest.mark.parametrize("answer, fail", [(False, False), (True, False), (True, True)]) def test_explicit_setup_consent_is_durable_only_after_success(supervisor, monkeypatch, answer, fail): - profile, calls = supervisor + _profile, calls = supervisor config_api.save_config({"gateway": {"service_install_choice": None}, "custom": "keep"}) prompts = [] monkeypatch.setattr(gateway, "prompt_yes_no", lambda *args: prompts.append(args) or answer) @@ -120,7 +120,7 @@ def test_explicit_setup_consent_is_durable_only_after_success(supervisor, monkey @pytest.mark.parametrize("start_now", [False, True]) @pytest.mark.parametrize("consent", [False, True]) def test_wizard_service_choice_controls_installation(supervisor, monkeypatch, start_now, consent): - profile, calls = supervisor + _profile, _calls = supervisor config_api.save_config({"gateway": {"service_install_choice": None}}) stream = io.StringIO() stream.isatty = lambda: True @@ -168,7 +168,7 @@ def test_explicit_install_records_only_completed_install(supervisor, monkeypatch @pytest.mark.platforms("linux") def test_wizard_start_does_not_spawn_over_reserved_owner(supervisor, monkeypatch): from gateway.runtime_ownership import ProfileOwnership - profile, calls = supervisor + profile, _calls = supervisor profile.chmod(0o700) config_api.save_config({"gateway": {"service_install_choice": "decline"}}) stream = io.StringIO() diff --git a/tests/hermes_cli/test_kanban_gateway_restart_handoff.py b/tests/hermes_cli/test_kanban_gateway_restart_handoff.py index 1b2a703775..3c17fb3730 100644 --- a/tests/hermes_cli/test_kanban_gateway_restart_handoff.py +++ b/tests/hermes_cli/test_kanban_gateway_restart_handoff.py @@ -262,6 +262,6 @@ def test_real_user_systemd_scope_preserves_worker_context( env=process_registry.systemd_user_bus_env()) state = subprocess.run(["systemctl", "--user", "show", unit, "--property=ActiveState", "--value"], stdin=subprocess.DEVNULL, capture_output=True, text=True, timeout=10, - env=process_registry.systemd_user_bus_env()) + env=process_registry.systemd_user_bus_env(), check=False) assert state.stdout.strip() in ('inactive', 'failed'), state.stdout + state.stderr print(json.dumps({'worker': payload, 'scope_after_cleanup': state.stdout.strip()})) diff --git a/tests/hermes_cli/test_kanban_per_profile_cap.py b/tests/hermes_cli/test_kanban_per_profile_cap.py index 14e63f2781..8899304716 100644 --- a/tests/hermes_cli/test_kanban_per_profile_cap.py +++ b/tests/hermes_cli/test_kanban_per_profile_cap.py @@ -30,7 +30,7 @@ def isolated_kanban_home_with_profiles(monkeypatch): from hermes_cli import kanban_db_dispatch # The module purge above discards the conftest's memory-sample neutralizer; # re-apply it so the host's live memory pressure cannot cap this tick. - monkeypatch.setattr(kanban_db_dispatch, "_system_memory_sample", lambda: {}) + monkeypatch.setattr(kanban_db_dispatch, "_system_memory_sample", dict) yield kanban_db diff --git a/tests/hermes_cli/test_kanban_worker_owner_wait.py b/tests/hermes_cli/test_kanban_worker_owner_wait.py index b0db0092f7..c8973e6b67 100644 --- a/tests/hermes_cli/test_kanban_worker_owner_wait.py +++ b/tests/hermes_cli/test_kanban_worker_owner_wait.py @@ -39,7 +39,7 @@ async def test_worker_waits_out_a_draining_owner_and_submits_once(monkeypatch, t submitted = [] monkeypatch.setattr(gateway_client, "connect_gateway", _owner(["draining", "starting"], submitted)) monkeypatch.setattr(kanban_worker_client.asyncio, "sleep", _no_sleep) - import gateway.session_kanban as session_kanban + from gateway import session_kanban monkeypatch.setattr(session_kanban, "worker_result", lambda *_: {"exit_code": 0, "last_output": "card closed"}) monkeypatch.delenv("HERMES_TUI_GATEWAY_URL", raising=False) diff --git a/tests/hermes_cli/test_launch_readonly.py b/tests/hermes_cli/test_launch_readonly.py index caa11cd95a..d24c9708ad 100644 --- a/tests/hermes_cli/test_launch_readonly.py +++ b/tests/hermes_cli/test_launch_readonly.py @@ -45,7 +45,7 @@ after = path.read_bytes() if path.exists() else None assert after == before, 'launch reader created or mutated state.db' '''.replace('POPULATED', repr(populated)) result = subprocess.run([sys.executable, "-c", code], cwd=repo, env=env, - stdin=subprocess.DEVNULL, capture_output=True, text=True, timeout=30) + stdin=subprocess.DEVNULL, capture_output=True, text=True, timeout=30, check=False) assert result.returncode == 0, result.stdout + result.stderr assert 'Error generating insights:' not in result.stdout if populated: diff --git a/tests/hermes_cli/test_readers_concurrent_owner.py b/tests/hermes_cli/test_readers_concurrent_owner.py index 628ba8b8d5..43a41dc4b4 100644 --- a/tests/hermes_cli/test_readers_concurrent_owner.py +++ b/tests/hermes_cli/test_readers_concurrent_owner.py @@ -44,5 +44,5 @@ with SessionDB(db_path=path) as owner: assert owner.get_session_title('committed-fixture') == 'Committed title' ''' result = subprocess.run([sys.executable, '-c', code], cwd=repo, env=env, stdin=subprocess.DEVNULL, - capture_output=True, text=True, timeout=60) + capture_output=True, text=True, timeout=60, check=False) assert result.returncode == 0, result.stdout + result.stderr diff --git a/tests/hermes_cli/test_runtime_maintenance_exclusion.py b/tests/hermes_cli/test_runtime_maintenance_exclusion.py index 6a4e1addd3..12528c877d 100644 --- a/tests/hermes_cli/test_runtime_maintenance_exclusion.py +++ b/tests/hermes_cli/test_runtime_maintenance_exclusion.py @@ -74,7 +74,7 @@ def test_restore_refuses_live_authority_without_changing_data(tmp_path): return snapshot try: - with daemon(root, home, env, barrier=False) as (proc, desc): + with daemon(root, home, env, barrier=False) as (_proc, desc): snapshot = asyncio.run(exercise(desc)) old_epoch = desc['authority_epoch'] peer.release.set() @@ -138,7 +138,7 @@ def test_import_and_startup_exclude_each_other_before_publication(tmp_path, monk env.update(HOME=str(tmp_path / 'user'), HERMES_HOME=str(home), PYTHONPATH=str(root)) contender = subprocess.run([sys.executable, '-c', "import logging, runpy; logging.basicConfig(level=logging.INFO); runpy.run_module('gateway.run', run_name='__main__')"], cwd=root, env=env, - stdin=subprocess.DEVNULL, capture_output=True, text=True, timeout=25) + stdin=subprocess.DEVNULL, capture_output=True, text=True, timeout=25, check=False) assert 'already owns profile' in contender.stdout + contender.stderr, contender assert not (home / 'state.db').exists() finally: diff --git a/tests/hermes_cli/test_runtime_provider_openai_runtime.py b/tests/hermes_cli/test_runtime_provider_openai_runtime.py index d818a8349d..606fd72ae1 100644 --- a/tests/hermes_cli/test_runtime_provider_openai_runtime.py +++ b/tests/hermes_cli/test_runtime_provider_openai_runtime.py @@ -75,7 +75,7 @@ def test_openai_alias_resolves_identically_on_runtime_and_aux_client_paths(monke monkeypatch.setattr(aux, "_get_auxiliary_task_config", lambda task: block if task == "background_review" else {}) monkeypatch.setattr(rp, "_get_model_config", lambda: {"provider": "custom:mylocal", "default": "local-main"}) - aux_provider, aux_model, aux_base, aux_key, _ = aux._resolve_task_provider_model("background_review") + aux_provider, _aux_model, aux_base, aux_key, _ = aux._resolve_task_provider_model("background_review") runtime = rp.resolve_runtime_provider(requested=block["provider"], target_model=block["model"], explicit_api_key=block["api_key"], explicit_base_url=block["base_url"]) diff --git a/tests/hermes_cli/test_safe_mode.py b/tests/hermes_cli/test_safe_mode.py index ae7be6e47d..51c8f48203 100644 --- a/tests/hermes_cli/test_safe_mode.py +++ b/tests/hermes_cli/test_safe_mode.py @@ -204,7 +204,7 @@ def test_canonical_safe_launch_is_frozen_policy_executed_in_isolated_worker(tmp_ assert any('/plugins/' in p for p in owner_opens) and any(p.endswith('config.yaml') for p in owner_opens) # Canonical safe launch through the real CLI on a real PTY. - safe_sid, output = safe_cli('one', '--safe-mode') + safe_sid, _output = safe_cli('one', '--safe-mode') assert query('SELECT status FROM session_admissions WHERE target_session_id=?', (safe_sid,)) == [('terminal',)] assert query('SELECT status FROM worker_executions WHERE session_id=?', (safe_sid,)) == [('terminal',)] policy = json.loads(query('SELECT value FROM state_meta WHERE key=?', ('gateway.local_policy.v1:' + safe_sid,))[0][0])['policy'] @@ -241,7 +241,7 @@ def test_canonical_safe_launch_is_frozen_policy_executed_in_isolated_worker(tmp_ # Config-only keeps plugin discovery (the gate is safe_mode), yet reads no profile YAML. (home / 'config.yaml').write_text(json.dumps(config), encoding='utf-8') - config_sid, _ = safe_cli('three', '--ignore-user-config') + _config_sid, _ = safe_cli('three', '--ignore-user-config') config_worker = [p for p in worker_pids(owner.pid) if p not in workers][-1] config_opens = [r['path'] for r in records() if r['kind'] == 'open' and r['pid'] == config_worker] assert any('/plugins/' in p for p in config_opens), 'config-only must still discover plugins' diff --git a/tests/hermes_cli/test_sessions_observation.py b/tests/hermes_cli/test_sessions_observation.py index 7bc20dc867..d093e60460 100644 --- a/tests/hermes_cli/test_sessions_observation.py +++ b/tests/hermes_cli/test_sessions_observation.py @@ -46,5 +46,5 @@ for action in ('list', 'stats', 'pinned', 'export'): assert after == before, action + ' created or mutated state.db' '''.replace('POPULATED', repr(populated)) result = subprocess.run([sys.executable, '-c', code], cwd=repo, env=env, stdin=subprocess.DEVNULL, - capture_output=True, text=True, timeout=30) + capture_output=True, text=True, timeout=30, check=False) assert result.returncode == 0, result.stdout + result.stderr diff --git a/tests/hermes_cli/test_update_restore_authority.py b/tests/hermes_cli/test_update_restore_authority.py index 2b5af4acf1..a2972b5b74 100644 --- a/tests/hermes_cli/test_update_restore_authority.py +++ b/tests/hermes_cli/test_update_restore_authority.py @@ -65,7 +65,7 @@ def test_restore_refuses_live_authority_without_changing_data(tmp_path): return snapshot try: - with daemon(root, home, env, barrier=False) as (proc, desc): + with daemon(root, home, env, barrier=False) as (_proc, desc): snapshot = asyncio.run(exercise(desc)) old_epoch = desc['authority_epoch'] peer.release.set() @@ -168,7 +168,7 @@ def test_update_restore_excludes_startup_until_publication(tmp_path, monkeypatch contender = subprocess.run([sys.executable, '-c', "import logging, runpy; logging.basicConfig(level=logging.INFO); runpy.run_module('gateway.run', run_name='__main__')"], cwd=root, env=env, stdin=subprocess.DEVNULL, capture_output=True, - text=True, encoding='utf-8', timeout=25) + text=True, encoding='utf-8', timeout=25, check=False) assert 'already owns profile' in contender.stdout + contender.stderr, contender assert (destination.read_bytes(), source.read_bytes()) == before finally: diff --git a/tests/test_live_system_guard_self_test.py b/tests/test_live_system_guard_self_test.py index 1c235df6b2..490c9b78cd 100644 --- a/tests/test_live_system_guard_self_test.py +++ b/tests/test_live_system_guard_self_test.py @@ -160,11 +160,11 @@ def test_host_gateway_start_blocked_but_container_exec_allowed(): """A host-side ``hermes gateway start`` spawns a real runtime; the same words addressed to a container via ``docker exec`` stay inside it and are the Docker harness's business.""" with pytest.raises(RuntimeError, match="REAL hermes gateway runtime"): - subprocess.run(["hermes", "-p", "p", "gateway", "start"]) + subprocess.run(["hermes", "-p", "p", "gateway", "start"], check=False) argv = ["docker", "exec", "-u", "hermes", "hermes-test-x", "sh", "-c", "hermes -p p gateway start"] # Past the guard the call reaches the real subprocess.run; a missing binary is fine here. with pytest.raises(FileNotFoundError): - subprocess.run(argv, env={"PATH": "/nonexistent"}) + subprocess.run(argv, env={"PATH": "/nonexistent"}, check=False) def test_subprocess_run_string_shell_true_blocked(): diff --git a/tests/test_state_config_contention.py b/tests/test_state_config_contention.py index f92c36aced..2bf2ec45c6 100644 --- a/tests/test_state_config_contention.py +++ b/tests/test_state_config_contention.py @@ -90,7 +90,7 @@ def test_partial_config_import_cannot_invert_config_and_module_locks(tmp_path): }, capture_output=True, text=True, - timeout=15, + timeout=15, check=False ) assert completed.returncode == 0, completed.stdout + completed.stderr diff --git a/tests/tools/test_bot_mode_dm.py b/tests/tools/test_bot_mode_dm.py index 44ea0e9fb1..fd00907da0 100644 --- a/tests/tools/test_bot_mode_dm.py +++ b/tests/tools/test_bot_mode_dm.py @@ -762,7 +762,7 @@ def _admitted_live_dm(tmp_path, monkeypatch, delivery_id): def test_live_dm_wait_reports_reply_after_initial_wait_budget(tmp_path, monkeypatch, capsys): """A retained admission still delivers its reply after the first wait window (main a94b9758313).""" - live, authority, record = _admitted_live_dm(tmp_path, monkeypatch, "a" * 32) + _live, authority, record = _admitted_live_dm(tmp_path, monkeypatch, "a" * 32) settling = threading.Timer(0.05, lambda: authority.settle(record["delivery_id"], status="settled", reply="PONG")) settling.start() try: diff --git a/tests/tools/test_process_registry_import_fence.py b/tests/tools/test_process_registry_import_fence.py index 0e0aa7f83e..2120137069 100644 --- a/tests/tools/test_process_registry_import_fence.py +++ b/tests/tools/test_process_registry_import_fence.py @@ -30,7 +30,7 @@ def test_importing_process_registry_does_not_connect_to_state_db(tmp_path): env = {k: v for k, v in os.environ.items() if not k.startswith("HERMES_")} env.update(HERMES_HOME=str(home), HOME=str(tmp_path), PYTHONPATH=os.getcwd()) out = subprocess.run([sys.executable, "-c", _IMPORT_PROBE], env=env, capture_output=True, text=True, - timeout=120, stdin=subprocess.DEVNULL) + timeout=120, stdin=subprocess.DEVNULL, check=False) assert out.returncode == 0, out.stderr assert json.loads(out.stdout.strip().splitlines()[-1]) == [] assert not (home / "state.db").exists() diff --git a/tests/tools/test_safe_worker_mcp.py b/tests/tools/test_safe_worker_mcp.py index db97a2a508..aefaef02f3 100644 --- a/tests/tools/test_safe_worker_mcp.py +++ b/tests/tools/test_safe_worker_mcp.py @@ -58,7 +58,7 @@ def test_safe_worker_never_spawns_mcp_servers(tmp_path, mode): "discovered": discovered, "again": again}})) '''), encoding="utf-8") result = subprocess.run([sys.executable, str(script)], cwd=root, env=env, stdin=subprocess.DEVNULL, - capture_output=True, text=True, timeout=120) + capture_output=True, text=True, timeout=120, check=False) assert result.returncode == 0, result.stdout + result.stderr out = json.loads(result.stdout.strip().splitlines()[-1]) if mode == "safe": diff --git a/tests/tools/test_safe_worker_tools.py b/tests/tools/test_safe_worker_tools.py index 28e3f8b2e2..9bd43c98d3 100644 --- a/tests/tools/test_safe_worker_tools.py +++ b/tests/tools/test_safe_worker_tools.py @@ -20,7 +20,7 @@ def run_worker(tmp_path, body, mode="safe"): script = home / "probe.py" script.write_text(f"import sys; sys.path.insert(0, {str(root)!r})\n" + textwrap.dedent(body), encoding="utf-8") result = subprocess.run([sys.executable, str(script)], cwd=root, env=env, - stdin=subprocess.DEVNULL, capture_output=True, text=True, timeout=90) + stdin=subprocess.DEVNULL, capture_output=True, text=True, timeout=90, check=False) assert result.returncode == 0, result.stdout + result.stderr return json.loads(result.stdout.strip().splitlines()[-1]) diff --git a/tests/tools/test_session_search_readonly.py b/tests/tools/test_session_search_readonly.py index c9f9db02c8..c10d8707e2 100644 --- a/tests/tools/test_session_search_readonly.py +++ b/tests/tools/test_session_search_readonly.py @@ -9,7 +9,7 @@ import sqlite3 def test_search_does_not_create_current_storage_for_empty_or_foreign_reads(tmp_path, monkeypatch): from hermes_constants import reset_hermes_home_override, set_hermes_home_override from hermes_state import SessionDB - import tools.session_search_tool # noqa: F401 + import tools.session_search_tool from tools.registry import registry monkeypatch.setattr(Path, "home", lambda: tmp_path) @@ -45,7 +45,7 @@ def test_search_does_not_create_current_storage_for_empty_or_foreign_reads(tmp_p def test_search_reads_live_storage_without_a_write_commit(tmp_path, monkeypatch): from hermes_constants import reset_hermes_home_override, set_hermes_home_override from hermes_state import SessionDB - import tools.session_search_tool # noqa: F401 + import tools.session_search_tool from tools.registry import registry home = tmp_path / "profile" diff --git a/tests/tui_gateway/test_gateway_bootstrap_control_home.py b/tests/tui_gateway/test_gateway_bootstrap_control_home.py index 171d193b81..e330fac7be 100644 --- a/tests/tui_gateway/test_gateway_bootstrap_control_home.py +++ b/tests/tui_gateway/test_gateway_bootstrap_control_home.py @@ -7,7 +7,7 @@ import json from pathlib import Path from types import SimpleNamespace -import hermes_cli.gateway_runtime as gateway_runtime +from hermes_cli import gateway_runtime import hermes_constants diff --git a/tools/bot_live_delivery.py b/tools/bot_live_delivery.py index aa6dc26b58..ad57c89fb1 100644 --- a/tools/bot_live_delivery.py +++ b/tools/bot_live_delivery.py @@ -264,7 +264,7 @@ def claim_pending_delivery(profile_home, owner): Keep this refusal until the legacy notification poller's call site is removed. A previously claimed record can still publish its terminal receipt below. """ - return None + return def complete_delivery( diff --git a/tui_gateway/contracts/__init__.py b/tui_gateway/contracts/__init__.py index e766eb3c27..aa9ce0f060 100644 --- a/tui_gateway/contracts/__init__.py +++ b/tui_gateway/contracts/__init__.py @@ -20,7 +20,7 @@ from . import ( tools_commands, tools_mcp_plugins, ) -from . import canonical # noqa: F401,E402 (last: authority-only verbs reuse the topic models above) +from . import canonical from .base import JsonValue, Params, Payload, Result, WireEnum from .registry import CANONICAL_METHODS, EVENTS, METHODS, SERVER_REQUESTS diff --git a/tui_gateway/session_registry.py b/tui_gateway/session_registry.py index 547f337ba9..538d5b6691 100644 --- a/tui_gateway/session_registry.py +++ b/tui_gateway/session_registry.py @@ -72,7 +72,7 @@ def _wait_agent_for_prompt(session: dict, rid: str, sid: str) -> dict | None: return _err(rid, 5032, err) if (err := session.get("agent_error")) else None -def _bind_build_profile_scopes(profile_home: "str | None") -> "_TurnScopes | None": +def _bind_build_profile_scopes(profile_home: str | None) -> _TurnScopes | None: """Bind a session profile's HERMES_HOME / secret / terminal scopes for an agent build. ``None`` is the launch profile: its own launch-env secret scope (live env while single-profile, frozen once multiplexing is active — a hosted-room turn for a default member otherwise died at build with @@ -90,7 +90,7 @@ def _bind_build_profile_scopes(profile_home: "str | None") -> "_TurnScopes | Non return scopes -def _release_build_profile_scopes(scopes: "_TurnScopes | None") -> None: +def _release_build_profile_scopes(scopes: _TurnScopes | None) -> None: with contextlib.suppress(Exception): _release_profile_runtime_scope_tokens(scopes) diff --git a/tui_gateway/ws.py b/tui_gateway/ws.py index ed0843011a..45f30c574f 100644 --- a/tui_gateway/ws.py +++ b/tui_gateway/ws.py @@ -294,7 +294,7 @@ def _disable_nagle(ws: Any) -> None: _log.debug("ws TCP_NODELAY skip: %s", exc) -def _authority_connection(ws: Any, transport: "WSTransport", auth_identity: dict | None, operator: bool): +def _authority_connection(ws: Any, transport: WSTransport, auth_identity: dict | None, operator: bool): """The session-authority connection for this socket (route scope, else app state), or None when this backend has no authority and requests go to the legacy ``server.dispatch``.""" authority = (getattr(ws, 'scope', None) or {}).get('hermes.session_authority') or getattr( @@ -305,7 +305,7 @@ def _authority_connection(ws: Any, transport: "WSTransport", auth_identity: dict return AuthorityConnection(authority, transport, auth_identity or {}, operator=operator) -async def _dispatch_request(authority_connection: Any, req: Any, req_method: Any, transport: "WSTransport") -> Any: +async def _dispatch_request(authority_connection: Any, req: Any, req_method: Any, transport: WSTransport) -> Any: """One request through the authority when attached, else the legacy dispatcher.""" if authority_connection is None: return await asyncio.to_thread(server.dispatch, req, transport) diff --git a/ui-tui/scripts/native_canonical_probe.py b/ui-tui/scripts/native_canonical_probe.py index 0b9e221416..e1ec2218aa 100644 --- a/ui-tui/scripts/native_canonical_probe.py +++ b/ui-tui/scripts/native_canonical_probe.py @@ -134,7 +134,7 @@ def run(): def grant(): result = subprocess.run([sys.executable, str(ROOT / 'ui-tui/scripts/gateway_bootstrap.py')], cwd=ROOT, env=env, stdin=subprocess.DEVNULL, - capture_output=True, text=True, timeout=10) + capture_output=True, text=True, timeout=10, check=False) assert result.returncode == 0, result.stderr return json.loads(result.stdout)