R``) leak into the status line and
can freeze input when the reply is slow (#13870 on SSH/slow PTYs). The same race hits local POSIX TTYs
- under heavy subagent / status-line load — see ``tests/cli/test_cpr_local_leak.py``.
+ under heavy subagent / status-line load — see ``tests/hermes_cli/test_cpr_local_leak.py``.
"""
return os.environ.get("PROMPT_TOOLKIT_NO_CPR", "") == "1" or sys.platform != "win32"
diff --git a/hermes_cli/cli_info_mixin.py b/hermes_cli/cli_info_mixin.py
index 0d7fbbd6ca..7ff260fdfe 100644
--- a/hermes_cli/cli_info_mixin.py
+++ b/hermes_cli/cli_info_mixin.py
@@ -459,7 +459,7 @@ class CLIInfoMixin:
Dispatched from the input loop BEFORE slash routing and before anything is queued for the
agent, so a bang command never becomes a turn: nothing touches ``conversation_history``,
zero tokens, role alternation / prompt caching untouched by construction
- (tests/cli/test_bang_shell_mode.py). Returns False when the text is not a bang command or
+ (tests/hermes_cli/test_bang_shell_mode.py). Returns False when the text is not a bang command or
bang mode is disabled for this context (gateway/cron), so the caller routes normally.
"""
from cli import _rich_text_from_ansi
diff --git a/hermes_cli/default_soul.py b/hermes_cli/default_soul.py
index 3c99d404dd..3a19405021 100644
--- a/hermes_cli/default_soul.py
+++ b/hermes_cli/default_soul.py
@@ -49,7 +49,7 @@ _LEGACY_TEMPLATE_SOULS = (
"investigations."
),
# ASCII-dashed variant seeded by scripts/install.ps1 (must stay pure ASCII, see
- # tests/test_install_ps1_ascii_only.py); upgrading converges Windows installs on the em-dash text.
+ # tests/scripts/install/test_install_ps1_ascii_only.py); upgrading converges Windows installs on the em-dash text.
DEFAULT_SOUL_MD.replace("\u2014", "--"),
)
diff --git a/hermes_state_portability.py b/hermes_state_portability.py
index 2a960ec038..7132f0b22a 100644
--- a/hermes_state_portability.py
+++ b/hermes_state_portability.py
@@ -534,7 +534,7 @@ class SessionPortabilityMixin:
/ ``last_activity_description`` / ``last_activity_provenance``) because they are part of the durable
row, but import deliberately RESETS them to NULL. This asymmetry is intentional and covered by
regression
- (tests/gateway/test_watchdog_review_76354.py::test_s4_export_includes_activity_import_resets_it).
+ (tests/gateway/test_watchdog_review.py::test_s4_export_includes_activity_import_resets_it).
"""
if not isinstance(sessions, list):
raise ValueError("sessions must be a list")
diff --git a/scripts/ci/classify_changes.py b/scripts/ci/classify_changes.py
index 558ccc12d5..6bf01febee 100644
--- a/scripts/ci/classify_changes.py
+++ b/scripts/ci/classify_changes.py
@@ -131,7 +131,7 @@ _INSTALLER_FILES = {"scripts/install.ps1", "scripts/install.cmd"}
# Windows desktop-update hand-off (scripts/desktop-update/windows.ps1 + the
# Electron side that launches it) and the pytest files that spawn it.
_DESKTOP_UPDATER_PATHS = ("scripts/desktop-update/",)
-_DESKTOP_UPDATER_TEST_PREFIX = "tests/test_desktop_update_"
+_DESKTOP_UPDATER_TEST_PREFIX = "tests/scripts/desktop_update/"
_DESKTOP_UPDATER_FILES = {
"apps/desktop/electron/updater-process.ts",
"apps/desktop/electron/managed-ssh-update.ts",
diff --git a/scripts/desktop-update/posix.sh b/scripts/desktop-update/posix.sh
index 063d41bbab..7f73d69beb 100755
--- a/scripts/desktop-update/posix.sh
+++ b/scripts/desktop-update/posix.sh
@@ -629,7 +629,7 @@ tcc_pick_update_invoke() { # sets UPDATE_INVOKE; safety net past a failed heal
# ── self-tests: no update, touch nothing ────────────────────────────────────
if [ "$SELF_TEST_TCC_HEAL" -eq 1 ]; then
# Runs the REAL heal + invoke selection against --install-root and reports;
- # tests/test_desktop_update_tcc_heal.py drives the state matrix through it.
+ # tests/scripts/desktop_update/test_desktop_update_tcc_heal.py drives the state matrix through it.
trap - EXIT
tcc_anchor_heal "$INSTALL_ROOT/venv/bin" || true
tcc_pick_update_invoke "$INSTALL_ROOT/venv/bin"
diff --git a/scripts/run_tests.sh b/scripts/run_tests.sh
index 4445d6d542..77eedc6d1c 100755
--- a/scripts/run_tests.sh
+++ b/scripts/run_tests.sh
@@ -17,7 +17,7 @@
# scripts/run_tests.sh # full suite
# scripts/run_tests.sh -j 4 # cap parallelism
# scripts/run_tests.sh tests/agent/ # discover only here
-# scripts/run_tests.sh tests/agent/ tests/acp/ # multiple roots
+# scripts/run_tests.sh tests/agent/ tests/acp_adapter/ # multiple roots
# scripts/run_tests.sh tests/foo.py # single file
# scripts/run_tests.sh tests/foo.py -q # path + bare pytest flag
# scripts/run_tests.sh tests/foo.py -v --tb=long # bare flags "just work"
diff --git a/scripts/run_tests_parallel.py b/scripts/run_tests_parallel.py
index eb923cc9b4..5e20caf3cb 100755
--- a/scripts/run_tests_parallel.py
+++ b/scripts/run_tests_parallel.py
@@ -508,8 +508,8 @@ def _parse_pytest_summary(output: str) -> dict[str, int]:
def _format_file(file: Path, repo_root: Path) -> str:
"""Render a test-file path for display: strip the repo-root prefix
- when possible so output reads ``tests/acp/test_auth.py`` instead of
- ``/home/runner/work/hermes-agent/hermes-agent/tests/acp/test_auth.py``.
+ when possible so output reads ``tests/acp_adapter/test_auth.py`` instead of
+ ``/home/runner/work/hermes-agent/hermes-agent/tests/acp_adapter/test_auth.py``.
Falls back to the absolute path for anything outside the repo root.
"""
diff --git a/tests/acp/__init__.py b/tests/acp_adapter/__init__.py
similarity index 100%
rename from tests/acp/__init__.py
rename to tests/acp_adapter/__init__.py
diff --git a/tests/acp/conftest.py b/tests/acp_adapter/conftest.py
similarity index 96%
rename from tests/acp/conftest.py
rename to tests/acp_adapter/conftest.py
index 4d5e1a37e9..620f9d3b6e 100644
--- a/tests/acp/conftest.py
+++ b/tests/acp_adapter/conftest.py
@@ -1,4 +1,4 @@
-"""Shared fixtures for tests/acp.
+"""Shared fixtures for tests/acp_adapter.
Keeps the ACP server tests offline: ``HermesACPAgent._build_model_state``
calls ``hermes_cli.inventory.build_models_payload``, which (without this
diff --git a/tests/acp/test_approval_isolation.py b/tests/acp_adapter/test_approval_isolation.py
similarity index 98%
rename from tests/acp/test_approval_isolation.py
rename to tests/acp_adapter/test_approval_isolation.py
index b45e96789a..bd3658ca7d 100644
--- a/tests/acp/test_approval_isolation.py
+++ b/tests/acp_adapter/test_approval_isolation.py
@@ -23,7 +23,7 @@ from tools import approval_context
def _isolate_approval_state(monkeypatch):
"""Keep these security regression tests hermetic.
- Earlier tests (e.g. tests/acp/test_permissions.py) lazily load the
+ Earlier tests (e.g. tests/acp_adapter/test_permissions.py) lazily load the
developer's real ``~/.hermes/config.yaml`` command allowlist into
``tools.approval._permanent_approved``. If that allowlist contains a
pattern like "recursive delete", ``rm -rf …`` is auto-approved before
diff --git a/tests/acp/test_auth.py b/tests/acp_adapter/test_auth.py
similarity index 100%
rename from tests/acp/test_auth.py
rename to tests/acp_adapter/test_auth.py
diff --git a/tests/acp/test_edit_approval.py b/tests/acp_adapter/test_edit_approval.py
similarity index 100%
rename from tests/acp/test_edit_approval.py
rename to tests/acp_adapter/test_edit_approval.py
diff --git a/tests/acp/test_empty_session_persistence.py b/tests/acp_adapter/test_empty_session_persistence.py
similarity index 100%
rename from tests/acp/test_empty_session_persistence.py
rename to tests/acp_adapter/test_empty_session_persistence.py
diff --git a/tests/acp/test_entry.py b/tests/acp_adapter/test_entry.py
similarity index 100%
rename from tests/acp/test_entry.py
rename to tests/acp_adapter/test_entry.py
diff --git a/tests/acp/test_events.py b/tests/acp_adapter/test_events.py
similarity index 100%
rename from tests/acp/test_events.py
rename to tests/acp_adapter/test_events.py
diff --git a/tests/acp/test_mcp_e2e.py b/tests/acp_adapter/test_mcp_e2e.py
similarity index 100%
rename from tests/acp/test_mcp_e2e.py
rename to tests/acp_adapter/test_mcp_e2e.py
diff --git a/tests/acp/test_named_provider_catalogs.py b/tests/acp_adapter/test_named_provider_catalogs.py
similarity index 100%
rename from tests/acp/test_named_provider_catalogs.py
rename to tests/acp_adapter/test_named_provider_catalogs.py
diff --git a/tests/acp/test_permissions.py b/tests/acp_adapter/test_permissions.py
similarity index 100%
rename from tests/acp/test_permissions.py
rename to tests/acp_adapter/test_permissions.py
diff --git a/tests/acp/test_ping_suppression.py b/tests/acp_adapter/test_ping_suppression.py
similarity index 100%
rename from tests/acp/test_ping_suppression.py
rename to tests/acp_adapter/test_ping_suppression.py
diff --git a/tests/acp/test_server.py b/tests/acp_adapter/test_server.py
similarity index 100%
rename from tests/acp/test_server.py
rename to tests/acp_adapter/test_server.py
diff --git a/tests/acp/test_session.py b/tests/acp_adapter/test_session.py
similarity index 100%
rename from tests/acp/test_session.py
rename to tests/acp_adapter/test_session.py
diff --git a/tests/acp/test_session_db_private_access.py b/tests/acp_adapter/test_session_db_private_access.py
similarity index 100%
rename from tests/acp/test_session_db_private_access.py
rename to tests/acp_adapter/test_session_db_private_access.py
diff --git a/tests/acp/test_session_provenance.py b/tests/acp_adapter/test_session_provenance.py
similarity index 100%
rename from tests/acp/test_session_provenance.py
rename to tests/acp_adapter/test_session_provenance.py
diff --git a/tests/acp/test_tools.py b/tests/acp_adapter/test_tools.py
similarity index 100%
rename from tests/acp/test_tools.py
rename to tests/acp_adapter/test_tools.py
diff --git a/tests/run_agent/conftest.py b/tests/agent/conftest.py
similarity index 96%
rename from tests/run_agent/conftest.py
rename to tests/agent/conftest.py
index f786b8467f..5dde64145c 100644
--- a/tests/run_agent/conftest.py
+++ b/tests/agent/conftest.py
@@ -1,4 +1,4 @@
-"""Fast-path fixtures shared across tests/run_agent/.
+"""Fast-path fixtures shared across tests/agent/.
Many tests in this directory exercise the retry/backoff paths in the
agent loop. Production code uses ``jittered_backoff(base_delay=5.0)``
diff --git a/tests/run_agent/repro_48013_image_shrink_brick.py b/tests/agent/repro_image_shrink_brick.py
similarity index 98%
rename from tests/run_agent/repro_48013_image_shrink_brick.py
rename to tests/agent/repro_image_shrink_brick.py
index ee099f48dc..5c2e17ca19 100644
--- a/tests/run_agent/repro_48013_image_shrink_brick.py
+++ b/tests/agent/repro_image_shrink_brick.py
@@ -16,11 +16,11 @@ brought under the cap and that the helper reports success.
Run directly to see a human-readable report:
- python tests/run_agent/repro_48013_image_shrink_brick.py
+ python tests/agent/repro_image_shrink_brick.py
Or as a pytest smoke test (skipped automatically when Pillow is absent):
- scripts/run_tests.sh tests/run_agent/repro_48013_image_shrink_brick.py
+ scripts/run_tests.sh tests/agent/repro_image_shrink_brick.py
"""
from __future__ import annotations
diff --git a/tests/run_agent/test_31273_402_not_retried.py b/tests/agent/test_402_not_retried.py
similarity index 100%
rename from tests/run_agent/test_31273_402_not_retried.py
rename to tests/agent/test_402_not_retried.py
diff --git a/tests/run_agent/test_413_compression.py b/tests/agent/test_413_compression.py
similarity index 100%
rename from tests/run_agent/test_413_compression.py
rename to tests/agent/test_413_compression.py
diff --git a/tests/run_agent/test_860_dedup.py b/tests/agent/test_860_dedup.py
similarity index 100%
rename from tests/run_agent/test_860_dedup.py
rename to tests/agent/test_860_dedup.py
diff --git a/tests/test_account_usage.py b/tests/agent/test_account_usage_fetch.py
similarity index 100%
rename from tests/test_account_usage.py
rename to tests/agent/test_account_usage_fetch.py
diff --git a/tests/run_agent/test_agent_guardrails.py b/tests/agent/test_agent_guardrails.py
similarity index 100%
rename from tests/run_agent/test_agent_guardrails.py
rename to tests/agent/test_agent_guardrails.py
diff --git a/tests/run_agent/test_anthropic_mid_tool_call_drop.py b/tests/agent/test_anthropic_mid_tool_call_drop.py
similarity index 100%
rename from tests/run_agent/test_anthropic_mid_tool_call_drop.py
rename to tests/agent/test_anthropic_mid_tool_call_drop.py
diff --git a/tests/run_agent/test_anthropic_prompt_cache_policy.py b/tests/agent/test_anthropic_prompt_cache_policy.py
similarity index 100%
rename from tests/run_agent/test_anthropic_prompt_cache_policy.py
rename to tests/agent/test_anthropic_prompt_cache_policy.py
diff --git a/tests/run_agent/test_anthropic_response_header_capture.py b/tests/agent/test_anthropic_response_header_capture.py
similarity index 100%
rename from tests/run_agent/test_anthropic_response_header_capture.py
rename to tests/agent/test_anthropic_response_header_capture.py
diff --git a/tests/run_agent/test_28161_anthropic_stream_pool_cleanup.py b/tests/agent/test_anthropic_stream_pool_cleanup.py
similarity index 100%
rename from tests/run_agent/test_28161_anthropic_stream_pool_cleanup.py
rename to tests/agent/test_anthropic_stream_pool_cleanup.py
diff --git a/tests/run_agent/test_anthropic_third_party_oauth_guard.py b/tests/agent/test_anthropic_third_party_oauth_guard.py
similarity index 100%
rename from tests/run_agent/test_anthropic_third_party_oauth_guard.py
rename to tests/agent/test_anthropic_third_party_oauth_guard.py
diff --git a/tests/run_agent/test_anthropic_truncation_continuation.py b/tests/agent/test_anthropic_truncation_continuation.py
similarity index 100%
rename from tests/run_agent/test_anthropic_truncation_continuation.py
rename to tests/agent/test_anthropic_truncation_continuation.py
diff --git a/tests/run_agent/test_api_max_retries_config.py b/tests/agent/test_api_max_retries_config.py
similarity index 100%
rename from tests/run_agent/test_api_max_retries_config.py
rename to tests/agent/test_api_max_retries_config.py
diff --git a/tests/run_agent/test_async_httpx_del_neuter.py b/tests/agent/test_async_httpx_del_neuter.py
similarity index 100%
rename from tests/run_agent/test_async_httpx_del_neuter.py
rename to tests/agent/test_async_httpx_del_neuter.py
diff --git a/tests/run_agent/test_auth_provider_failover.py b/tests/agent/test_auth_provider_failover.py
similarity index 100%
rename from tests/run_agent/test_auth_provider_failover.py
rename to tests/agent/test_auth_provider_failover.py
diff --git a/tests/run_agent/test_authorization_gate.py b/tests/agent/test_authorization_gate.py
similarity index 100%
rename from tests/run_agent/test_authorization_gate.py
rename to tests/agent/test_authorization_gate.py
diff --git a/tests/agent/test_auxiliary_client_base_url_host_validation_52608.py b/tests/agent/test_auxiliary_client_base_url_host_validation.py
similarity index 100%
rename from tests/agent/test_auxiliary_client_base_url_host_validation_52608.py
rename to tests/agent/test_auxiliary_client_base_url_host_validation.py
diff --git a/tests/agent/test_auxiliary_user_default_headers.py b/tests/agent/test_auxiliary_user_default_headers.py
index 2e9fafdb16..aff5b2ecc4 100644
--- a/tests/agent/test_auxiliary_user_default_headers.py
+++ b/tests/agent/test_auxiliary_user_default_headers.py
@@ -1,6 +1,6 @@
"""Tests for user-configured ``model.default_headers`` in the auxiliary client.
-Companion to ``tests/run_agent/test_provider_attribution_headers.py`` (which
+Companion to ``tests/agent/test_provider_attribution_headers.py`` (which
covers the main agent client). The main agent turn and the auxiliary client
(title generation, context compression, vision routing) build separate OpenAI
clients, so a ``custom`` endpoint behind a gateway/WAF that rejects the OpenAI
diff --git a/tests/run_agent/test_background_review.py b/tests/agent/test_background_review.py
similarity index 100%
rename from tests/run_agent/test_background_review.py
rename to tests/agent/test_background_review.py
diff --git a/tests/run_agent/test_background_review_cache_parity.py b/tests/agent/test_background_review_cache_parity.py
similarity index 100%
rename from tests/run_agent/test_background_review_cache_parity.py
rename to tests/agent/test_background_review_cache_parity.py
diff --git a/tests/run_agent/test_background_review_cost_controls.py b/tests/agent/test_background_review_cost_controls.py
similarity index 100%
rename from tests/run_agent/test_background_review_cost_controls.py
rename to tests/agent/test_background_review_cost_controls.py
diff --git a/tests/run_agent/test_background_review_input_budget.py b/tests/agent/test_background_review_input_budget.py
similarity index 100%
rename from tests/run_agent/test_background_review_input_budget.py
rename to tests/agent/test_background_review_input_budget.py
diff --git a/tests/test_background_review_list_shapes.py b/tests/agent/test_background_review_list_shapes.py
similarity index 99%
rename from tests/test_background_review_list_shapes.py
rename to tests/agent/test_background_review_list_shapes.py
index cc22d2825a..1589774b53 100644
--- a/tests/test_background_review_list_shapes.py
+++ b/tests/agent/test_background_review_list_shapes.py
@@ -43,7 +43,7 @@ import sys
import types
-REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+REPO_ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
def _isolate_hermes_home():
diff --git a/tests/run_agent/test_background_review_summary.py b/tests/agent/test_background_review_summary.py
similarity index 100%
rename from tests/run_agent/test_background_review_summary.py
rename to tests/agent/test_background_review_summary.py
diff --git a/tests/run_agent/test_background_review_toolset_restriction.py b/tests/agent/test_background_review_toolset_restriction.py
similarity index 100%
rename from tests/run_agent/test_background_review_toolset_restriction.py
rename to tests/agent/test_background_review_toolset_restriction.py
diff --git a/tests/test_bitwarden_secrets.py b/tests/agent/test_bitwarden_secrets.py
similarity index 99%
rename from tests/test_bitwarden_secrets.py
rename to tests/agent/test_bitwarden_secrets.py
index 420605416c..f304d64740 100644
--- a/tests/test_bitwarden_secrets.py
+++ b/tests/agent/test_bitwarden_secrets.py
@@ -24,7 +24,7 @@ import pytest
# Make the worktree importable without depending on the installed wheel.
-ROOT = Path(__file__).resolve().parents[1]
+ROOT = Path(__file__).resolve().parents[2]
if str(ROOT) not in sys.path:
sys.path.insert(0, str(ROOT))
diff --git a/tests/run_agent/test_callable_api_key.py b/tests/agent/test_callable_api_key.py
similarity index 100%
rename from tests/run_agent/test_callable_api_key.py
rename to tests/agent/test_callable_api_key.py
diff --git a/tests/agent/test_cascading_interrupt_6600.py b/tests/agent/test_cascading_interrupt.py
similarity index 100%
rename from tests/agent/test_cascading_interrupt_6600.py
rename to tests/agent/test_cascading_interrupt.py
diff --git a/tests/run_agent/test_codex_app_server_compaction.py b/tests/agent/test_codex_app_server_compaction.py
similarity index 100%
rename from tests/run_agent/test_codex_app_server_compaction.py
rename to tests/agent/test_codex_app_server_compaction.py
diff --git a/tests/run_agent/test_codex_app_server_integration.py b/tests/agent/test_codex_app_server_integration.py
similarity index 100%
rename from tests/run_agent/test_codex_app_server_integration.py
rename to tests/agent/test_codex_app_server_integration.py
diff --git a/tests/run_agent/test_codex_app_server_lifecycle.py b/tests/agent/test_codex_app_server_lifecycle.py
similarity index 100%
rename from tests/run_agent/test_codex_app_server_lifecycle.py
rename to tests/agent/test_codex_app_server_lifecycle.py
diff --git a/tests/run_agent/test_codex_multimodal_tool_result.py b/tests/agent/test_codex_multimodal_tool_result.py
similarity index 100%
rename from tests/run_agent/test_codex_multimodal_tool_result.py
rename to tests/agent/test_codex_multimodal_tool_result.py
diff --git a/tests/run_agent/test_codex_no_tools_nonetype.py b/tests/agent/test_codex_no_tools_nonetype.py
similarity index 100%
rename from tests/run_agent/test_codex_no_tools_nonetype.py
rename to tests/agent/test_codex_no_tools_nonetype.py
diff --git a/tests/run_agent/test_codex_sdk_transform_bypass.py b/tests/agent/test_codex_sdk_transform_bypass.py
similarity index 100%
rename from tests/run_agent/test_codex_sdk_transform_bypass.py
rename to tests/agent/test_codex_sdk_transform_bypass.py
diff --git a/tests/run_agent/test_codex_silent_hang_hint.py b/tests/agent/test_codex_silent_hang_hint.py
similarity index 100%
rename from tests/run_agent/test_codex_silent_hang_hint.py
rename to tests/agent/test_codex_silent_hang_hint.py
diff --git a/tests/run_agent/test_codex_xai_oauth_recovery.py b/tests/agent/test_codex_xai_oauth_recovery.py
similarity index 100%
rename from tests/run_agent/test_codex_xai_oauth_recovery.py
rename to tests/agent/test_codex_xai_oauth_recovery.py
diff --git a/tests/test_command_secret_source.py b/tests/agent/test_command_secret_source.py
similarity index 99%
rename from tests/test_command_secret_source.py
rename to tests/agent/test_command_secret_source.py
index 2cfd9e5c47..6e05a884f1 100644
--- a/tests/test_command_secret_source.py
+++ b/tests/agent/test_command_secret_source.py
@@ -27,7 +27,7 @@ from pathlib import Path
import pytest
-ROOT = Path(__file__).resolve().parents[1]
+ROOT = Path(__file__).resolve().parents[2]
if str(ROOT) not in sys.path:
sys.path.insert(0, str(ROOT))
diff --git a/tests/run_agent/test_commit_memory_session_context_engine.py b/tests/agent/test_commit_memory_session_context_engine.py
similarity index 100%
rename from tests/run_agent/test_commit_memory_session_context_engine.py
rename to tests/agent/test_commit_memory_session_context_engine.py
diff --git a/tests/test_compaction_prompt_rebuild.py b/tests/agent/test_compaction_prompt_rebuild.py
similarity index 100%
rename from tests/test_compaction_prompt_rebuild.py
rename to tests/agent/test_compaction_prompt_rebuild.py
diff --git a/tests/run_agent/test_compress_context_fallback_shim.py b/tests/agent/test_compress_context_fallback_shim.py
similarity index 100%
rename from tests/run_agent/test_compress_context_fallback_shim.py
rename to tests/agent/test_compress_context_fallback_shim.py
diff --git a/tests/run_agent/test_compress_focus_plugin_fallback.py b/tests/agent/test_compress_focus_plugin_fallback.py
similarity index 100%
rename from tests/run_agent/test_compress_focus_plugin_fallback.py
rename to tests/agent/test_compress_focus_plugin_fallback.py
diff --git a/tests/run_agent/test_compression_abort_state_reset.py b/tests/agent/test_compression_abort_state_reset.py
similarity index 100%
rename from tests/run_agent/test_compression_abort_state_reset.py
rename to tests/agent/test_compression_abort_state_reset.py
diff --git a/tests/run_agent/test_compression_boundary.py b/tests/agent/test_compression_boundary.py
similarity index 100%
rename from tests/run_agent/test_compression_boundary.py
rename to tests/agent/test_compression_boundary.py
diff --git a/tests/run_agent/test_compression_boundary_hook.py b/tests/agent/test_compression_boundary_hook.py
similarity index 100%
rename from tests/run_agent/test_compression_boundary_hook.py
rename to tests/agent/test_compression_boundary_hook.py
diff --git a/tests/run_agent/test_compression_budget_rearm.py b/tests/agent/test_compression_budget_rearm.py
similarity index 100%
rename from tests/run_agent/test_compression_budget_rearm.py
rename to tests/agent/test_compression_budget_rearm.py
diff --git a/tests/run_agent/test_compression_budget_refund.py b/tests/agent/test_compression_budget_refund.py
similarity index 100%
rename from tests/run_agent/test_compression_budget_refund.py
rename to tests/agent/test_compression_budget_refund.py
diff --git a/tests/run_agent/test_compression_closed_adoption.py b/tests/agent/test_compression_closed_adoption.py
similarity index 100%
rename from tests/run_agent/test_compression_closed_adoption.py
rename to tests/agent/test_compression_closed_adoption.py
diff --git a/tests/agent/test_compression_count_warning_36908.py b/tests/agent/test_compression_count_warning.py
similarity index 100%
rename from tests/agent/test_compression_count_warning_36908.py
rename to tests/agent/test_compression_count_warning.py
diff --git a/tests/run_agent/test_compression_feasibility.py b/tests/agent/test_compression_feasibility.py
similarity index 100%
rename from tests/run_agent/test_compression_feasibility.py
rename to tests/agent/test_compression_feasibility.py
diff --git a/tests/run_agent/test_compression_lock_defer.py b/tests/agent/test_compression_lock_defer.py
similarity index 99%
rename from tests/run_agent/test_compression_lock_defer.py
rename to tests/agent/test_compression_lock_defer.py
index 1eb5e69553..4e8459ad69 100644
--- a/tests/run_agent/test_compression_lock_defer.py
+++ b/tests/agent/test_compression_lock_defer.py
@@ -35,7 +35,7 @@ LOCK_HOLDER = "pid=4242:tid=1:agent=deadbeef:nonce=abcd1234"
# ---------------------------------------------------------------------------
-# Helpers (mirrors tests/run_agent/test_413_compression.py)
+# Helpers (mirrors tests/agent/test_413_compression.py)
# ---------------------------------------------------------------------------
diff --git a/tests/run_agent/test_compression_persistence.py b/tests/agent/test_compression_persistence.py
similarity index 100%
rename from tests/run_agent/test_compression_persistence.py
rename to tests/agent/test_compression_persistence.py
diff --git a/tests/agent/test_compression_review_76354.py b/tests/agent/test_compression_review.py
similarity index 100%
rename from tests/agent/test_compression_review_76354.py
rename to tests/agent/test_compression_review.py
diff --git a/tests/agent/test_compression_stall_fallback_78981.py b/tests/agent/test_compression_stall_fallback.py
similarity index 100%
rename from tests/agent/test_compression_stall_fallback_78981.py
rename to tests/agent/test_compression_stall_fallback.py
diff --git a/tests/agent/test_compression_stall_interrupt_96775.py b/tests/agent/test_compression_stall_interrupt.py
similarity index 100%
rename from tests/agent/test_compression_stall_interrupt_96775.py
rename to tests/agent/test_compression_stall_interrupt.py
diff --git a/tests/run_agent/test_compression_timeout_terminal.py b/tests/agent/test_compression_timeout_terminal.py
similarity index 100%
rename from tests/run_agent/test_compression_timeout_terminal.py
rename to tests/agent/test_compression_timeout_terminal.py
diff --git a/tests/run_agent/test_compression_trigger_excludes_reasoning.py b/tests/agent/test_compression_trigger_excludes_reasoning.py
similarity index 100%
rename from tests/run_agent/test_compression_trigger_excludes_reasoning.py
rename to tests/agent/test_compression_trigger_excludes_reasoning.py
diff --git a/tests/agent/test_compression_worker_isolation_76354.py b/tests/agent/test_compression_worker_isolation.py
similarity index 100%
rename from tests/agent/test_compression_worker_isolation_76354.py
rename to tests/agent/test_compression_worker_isolation.py
diff --git a/tests/run_agent/test_compressor_fallback_update.py b/tests/agent/test_compressor_fallback_update.py
similarity index 100%
rename from tests/run_agent/test_compressor_fallback_update.py
rename to tests/agent/test_compressor_fallback_update.py
diff --git a/tests/run_agent/test_concurrent_interrupt.py b/tests/agent/test_concurrent_interrupt.py
similarity index 100%
rename from tests/run_agent/test_concurrent_interrupt.py
rename to tests/agent/test_concurrent_interrupt.py
diff --git a/tests/run_agent/test_18028_content_policy_blocked.py b/tests/agent/test_content_policy_blocked.py
similarity index 100%
rename from tests/run_agent/test_18028_content_policy_blocked.py
rename to tests/agent/test_content_policy_blocked.py
diff --git a/tests/run_agent/test_1630_context_overflow_loop.py b/tests/agent/test_context_overflow_loop.py
similarity index 100%
rename from tests/run_agent/test_1630_context_overflow_loop.py
rename to tests/agent/test_context_overflow_loop.py
diff --git a/tests/run_agent/test_context_token_tracking.py b/tests/agent/test_context_token_tracking.py
similarity index 100%
rename from tests/run_agent/test_context_token_tracking.py
rename to tests/agent/test_context_token_tracking.py
diff --git a/tests/run_agent/test_continuation_ceiling_wedge.py b/tests/agent/test_continuation_ceiling_wedge.py
similarity index 100%
rename from tests/run_agent/test_continuation_ceiling_wedge.py
rename to tests/agent/test_continuation_ceiling_wedge.py
diff --git a/tests/run_agent/test_continuation_repetition_guard.py b/tests/agent/test_continuation_repetition_guard.py
similarity index 100%
rename from tests/run_agent/test_continuation_repetition_guard.py
rename to tests/agent/test_continuation_repetition_guard.py
diff --git a/tests/run_agent/test_conversation_fallback_state.py b/tests/agent/test_conversation_fallback_state.py
similarity index 100%
rename from tests/run_agent/test_conversation_fallback_state.py
rename to tests/agent/test_conversation_fallback_state.py
diff --git a/tests/test_copilot_initiator.py b/tests/agent/test_copilot_initiator.py
similarity index 100%
rename from tests/test_copilot_initiator.py
rename to tests/agent/test_copilot_initiator.py
diff --git a/tests/run_agent/test_copilot_native_vision_headers.py b/tests/agent/test_copilot_native_vision_headers.py
similarity index 100%
rename from tests/run_agent/test_copilot_native_vision_headers.py
rename to tests/agent/test_copilot_native_vision_headers.py
diff --git a/tests/run_agent/test_corruption_recovery_guidance.py b/tests/agent/test_corruption_recovery_guidance.py
similarity index 100%
rename from tests/run_agent/test_corruption_recovery_guidance.py
rename to tests/agent/test_corruption_recovery_guidance.py
diff --git a/tests/run_agent/test_create_openai_client_disables_sdk_retries.py b/tests/agent/test_create_openai_client_disables_sdk_retries.py
similarity index 100%
rename from tests/run_agent/test_create_openai_client_disables_sdk_retries.py
rename to tests/agent/test_create_openai_client_disables_sdk_retries.py
diff --git a/tests/run_agent/test_create_openai_client_kwargs_isolation.py b/tests/agent/test_create_openai_client_kwargs_isolation.py
similarity index 100%
rename from tests/run_agent/test_create_openai_client_kwargs_isolation.py
rename to tests/agent/test_create_openai_client_kwargs_isolation.py
diff --git a/tests/run_agent/test_create_openai_client_proxy_env.py b/tests/agent/test_create_openai_client_proxy_env.py
similarity index 100%
rename from tests/run_agent/test_create_openai_client_proxy_env.py
rename to tests/agent/test_create_openai_client_proxy_env.py
diff --git a/tests/run_agent/test_create_openai_client_reuse.py b/tests/agent/test_create_openai_client_reuse.py
similarity index 100%
rename from tests/run_agent/test_create_openai_client_reuse.py
rename to tests/agent/test_create_openai_client_reuse.py
diff --git a/tests/run_agent/test_create_openai_client_ssl_verify.py b/tests/agent/test_create_openai_client_ssl_verify.py
similarity index 100%
rename from tests/run_agent/test_create_openai_client_ssl_verify.py
rename to tests/agent/test_create_openai_client_ssl_verify.py
diff --git a/tests/run_agent/test_63425_credential_pool_auto_detect.py b/tests/agent/test_credential_pool_auto_detect.py
similarity index 100%
rename from tests/run_agent/test_63425_credential_pool_auto_detect.py
rename to tests/agent/test_credential_pool_auto_detect.py
diff --git a/tests/run_agent/test_credential_pool_interrupt.py b/tests/agent/test_credential_pool_interrupt.py
similarity index 100%
rename from tests/run_agent/test_credential_pool_interrupt.py
rename to tests/agent/test_credential_pool_interrupt.py
diff --git a/tests/run_agent/test_credential_rotation_route_settings.py b/tests/agent/test_credential_rotation_route_settings.py
similarity index 100%
rename from tests/run_agent/test_credential_rotation_route_settings.py
rename to tests/agent/test_credential_rotation_route_settings.py
diff --git a/tests/run_agent/test_credits_notices_toggle.py b/tests/agent/test_credits_notices_toggle.py
similarity index 100%
rename from tests/run_agent/test_credits_notices_toggle.py
rename to tests/agent/test_credits_notices_toggle.py
diff --git a/tests/agent/test_cron_inflight_prompt_reappend_100818.py b/tests/agent/test_cron_inflight_prompt_reappend.py
similarity index 100%
rename from tests/agent/test_cron_inflight_prompt_reappend_100818.py
rename to tests/agent/test_cron_inflight_prompt_reappend.py
diff --git a/tests/agent/test_cron_inline_api_call_62151.py b/tests/agent/test_cron_inline_api_call.py
similarity index 100%
rename from tests/agent/test_cron_inline_api_call_62151.py
rename to tests/agent/test_cron_inline_api_call.py
diff --git a/tests/run_agent/test_cross_process_turn_lease.py b/tests/agent/test_cross_process_turn_lease.py
similarity index 100%
rename from tests/run_agent/test_cross_process_turn_lease.py
rename to tests/agent/test_cross_process_turn_lease.py
diff --git a/tests/test_ctx_halving_fix.py b/tests/agent/test_ctx_halving_fix.py
similarity index 100%
rename from tests/test_ctx_halving_fix.py
rename to tests/agent/test_ctx_halving_fix.py
diff --git a/tests/run_agent/test_custom_provider_extra_headers_client.py b/tests/agent/test_custom_provider_extra_headers_client.py
similarity index 100%
rename from tests/run_agent/test_custom_provider_extra_headers_client.py
rename to tests/agent/test_custom_provider_extra_headers_client.py
diff --git a/tests/run_agent/test_deepseek_reasoning_content_echo.py b/tests/agent/test_deepseek_reasoning_content_echo.py
similarity index 100%
rename from tests/run_agent/test_deepseek_reasoning_content_echo.py
rename to tests/agent/test_deepseek_reasoning_content_echo.py
diff --git a/tests/run_agent/test_deepseek_v4_thinking_live.py b/tests/agent/test_deepseek_v4_thinking_live.py
similarity index 98%
rename from tests/run_agent/test_deepseek_v4_thinking_live.py
rename to tests/agent/test_deepseek_v4_thinking_live.py
index b938c27413..9092333a23 100644
--- a/tests/run_agent/test_deepseek_v4_thinking_live.py
+++ b/tests/agent/test_deepseek_v4_thinking_live.py
@@ -1,7 +1,7 @@
"""Live DeepSeek V4 thinking-mode tool-call replay smoke test.
Opt-in only:
- HERMES_LIVE_TESTS=1 pytest tests/run_agent/test_deepseek_v4_thinking_live.py -q
+ HERMES_LIVE_TESTS=1 pytest tests/agent/test_deepseek_v4_thinking_live.py -q
Requires DEEPSEEK_API_KEY in the process environment. The key is captured at
module import time because tests/conftest.py intentionally removes credential
diff --git a/tests/run_agent/test_dict_tool_call_args.py b/tests/agent/test_dict_tool_call_args.py
similarity index 100%
rename from tests/run_agent/test_dict_tool_call_args.py
rename to tests/agent/test_dict_tool_call_args.py
diff --git a/tests/run_agent/test_direct_contexts_stream_inline.py b/tests/agent/test_direct_contexts_stream_inline.py
similarity index 100%
rename from tests/run_agent/test_direct_contexts_stream_inline.py
rename to tests/agent/test_direct_contexts_stream_inline.py
diff --git a/tests/run_agent/test_dropped_tool_call_recovery.py b/tests/agent/test_dropped_tool_call_recovery.py
similarity index 100%
rename from tests/run_agent/test_dropped_tool_call_recovery.py
rename to tests/agent/test_dropped_tool_call_recovery.py
diff --git a/tests/run_agent/test_empty_response_recovery_persistence.py b/tests/agent/test_empty_response_recovery_persistence.py
similarity index 100%
rename from tests/run_agent/test_empty_response_recovery_persistence.py
rename to tests/agent/test_empty_response_recovery_persistence.py
diff --git a/tests/run_agent/test_empty_terminal_reasoning_surface.py b/tests/agent/test_empty_terminal_reasoning_surface.py
similarity index 100%
rename from tests/run_agent/test_empty_terminal_reasoning_surface.py
rename to tests/agent/test_empty_terminal_reasoning_surface.py
diff --git a/tests/run_agent/test_entitlement_fail_closed.py b/tests/agent/test_entitlement_fail_closed.py
similarity index 100%
rename from tests/run_agent/test_entitlement_fail_closed.py
rename to tests/agent/test_entitlement_fail_closed.py
diff --git a/tests/run_agent/test_env_credential_turn_refresh.py b/tests/agent/test_env_credential_turn_refresh.py
similarity index 100%
rename from tests/run_agent/test_env_credential_turn_refresh.py
rename to tests/agent/test_env_credential_turn_refresh.py
diff --git a/tests/test_env_loader_applied_homes.py b/tests/agent/test_env_loader_applied_homes.py
similarity index 100%
rename from tests/test_env_loader_applied_homes.py
rename to tests/agent/test_env_loader_applied_homes.py
diff --git a/tests/test_env_loader_secret_sources.py b/tests/agent/test_env_loader_secret_sources.py
similarity index 99%
rename from tests/test_env_loader_secret_sources.py
rename to tests/agent/test_env_loader_secret_sources.py
index ff4805064a..3ae55b94c1 100644
--- a/tests/test_env_loader_secret_sources.py
+++ b/tests/agent/test_env_loader_secret_sources.py
@@ -14,7 +14,7 @@ from pathlib import Path
import pytest
-ROOT = Path(__file__).resolve().parents[1]
+ROOT = Path(__file__).resolve().parents[2]
if str(ROOT) not in sys.path:
sys.path.insert(0, str(ROOT))
diff --git a/tests/agent/test_estimator_parity_84371.py b/tests/agent/test_estimator_parity.py
similarity index 100%
rename from tests/agent/test_estimator_parity_84371.py
rename to tests/agent/test_estimator_parity.py
diff --git a/tests/run_agent/test_exit_cleanup_interrupt.py b/tests/agent/test_exit_cleanup_interrupt.py
similarity index 100%
rename from tests/run_agent/test_exit_cleanup_interrupt.py
rename to tests/agent/test_exit_cleanup_interrupt.py
diff --git a/tests/run_agent/test_export_current_turn_boundary.py b/tests/agent/test_export_current_turn_boundary.py
similarity index 100%
rename from tests/run_agent/test_export_current_turn_boundary.py
rename to tests/agent/test_export_current_turn_boundary.py
diff --git a/tests/run_agent/test_32646_fallback_429_after_timeout.py b/tests/agent/test_fallback_429_after_timeout.py
similarity index 100%
rename from tests/run_agent/test_32646_fallback_429_after_timeout.py
rename to tests/agent/test_fallback_429_after_timeout.py
diff --git a/tests/run_agent/test_fallback_api_mode_preservation.py b/tests/agent/test_fallback_api_mode_preservation.py
similarity index 100%
rename from tests/run_agent/test_fallback_api_mode_preservation.py
rename to tests/agent/test_fallback_api_mode_preservation.py
diff --git a/tests/run_agent/test_fallback_credential_isolation.py b/tests/agent/test_fallback_credential_isolation.py
similarity index 100%
rename from tests/run_agent/test_fallback_credential_isolation.py
rename to tests/agent/test_fallback_credential_isolation.py
diff --git a/tests/run_agent/test_24996_fallback_exhaustion_cooldown.py b/tests/agent/test_fallback_exhaustion_cooldown.py
similarity index 100%
rename from tests/run_agent/test_24996_fallback_exhaustion_cooldown.py
rename to tests/agent/test_fallback_exhaustion_cooldown.py
diff --git a/tests/run_agent/test_fallback_reasoning_override.py b/tests/agent/test_fallback_reasoning_override.py
similarity index 100%
rename from tests/run_agent/test_fallback_reasoning_override.py
rename to tests/agent/test_fallback_reasoning_override.py
diff --git a/tests/run_agent/test_file_mutation_verifier.py b/tests/agent/test_file_mutation_verifier.py
similarity index 100%
rename from tests/run_agent/test_file_mutation_verifier.py
rename to tests/agent/test_file_mutation_verifier.py
diff --git a/tests/run_agent/test_fireworks_live.py b/tests/agent/test_fireworks_live.py
similarity index 97%
rename from tests/run_agent/test_fireworks_live.py
rename to tests/agent/test_fireworks_live.py
index ad7864c852..970ef45662 100644
--- a/tests/run_agent/test_fireworks_live.py
+++ b/tests/agent/test_fireworks_live.py
@@ -2,7 +2,7 @@
Opt-in only:
HERMES_LIVE_TESTS=1 FIREWORKS_API_KEY=fw_... \\
- pytest tests/run_agent/test_fireworks_live.py -q
+ pytest tests/agent/test_fireworks_live.py -q
Unlike a bare OpenAI() client pointed at the endpoint, this drives Hermes'
own provider resolution — ``resolve_provider_client('fireworks')`` — so it
diff --git a/tests/run_agent/test_first_chunk_at_hook.py b/tests/agent/test_first_chunk_at_hook.py
similarity index 100%
rename from tests/run_agent/test_first_chunk_at_hook.py
rename to tests/agent/test_first_chunk_at_hook.py
diff --git a/tests/run_agent/test_flush_diverts_on_corrupt_state_db.py b/tests/agent/test_flush_diverts_on_corrupt_state_db.py
similarity index 100%
rename from tests/run_agent/test_flush_diverts_on_corrupt_state_db.py
rename to tests/agent/test_flush_diverts_on_corrupt_state_db.py
diff --git a/tests/run_agent/test_gemini_native_reservation.py b/tests/agent/test_gemini_native_reservation.py
similarity index 100%
rename from tests/run_agent/test_gemini_native_reservation.py
rename to tests/agent/test_gemini_native_reservation.py
diff --git a/tests/test_hermetic_side_effect_guards.py b/tests/agent/test_hermetic_side_effect_guards.py
similarity index 100%
rename from tests/test_hermetic_side_effect_guards.py
rename to tests/agent/test_hermetic_side_effect_guards.py
diff --git a/tests/run_agent/test_identity_flush.py b/tests/agent/test_identity_flush.py
similarity index 100%
rename from tests/run_agent/test_identity_flush.py
rename to tests/agent/test_identity_flush.py
diff --git a/tests/run_agent/test_69078_image_corrupt_recovery.py b/tests/agent/test_image_corrupt_recovery.py
similarity index 99%
rename from tests/run_agent/test_69078_image_corrupt_recovery.py
rename to tests/agent/test_image_corrupt_recovery.py
index 51b834fa1a..400b0aab0b 100644
--- a/tests/run_agent/test_69078_image_corrupt_recovery.py
+++ b/tests/agent/test_image_corrupt_recovery.py
@@ -214,7 +214,7 @@ def _mock_response(content: str):
def _make_agent():
"""Build a minimal AIAgent, mirroring the idiom in
- tests/run_agent/test_32646_fallback_429_after_timeout.py."""
+ tests/agent/test_fallback_429_after_timeout.py."""
from run_agent import AIAgent
with (
diff --git a/tests/run_agent/test_image_generate_parallel.py b/tests/agent/test_image_generate_parallel.py
similarity index 100%
rename from tests/run_agent/test_image_generate_parallel.py
rename to tests/agent/test_image_generate_parallel.py
diff --git a/tests/run_agent/test_image_rejection_fallback.py b/tests/agent/test_image_rejection_fallback.py
similarity index 100%
rename from tests/run_agent/test_image_rejection_fallback.py
rename to tests/agent/test_image_rejection_fallback.py
diff --git a/tests/run_agent/test_image_shrink_recovery.py b/tests/agent/test_image_shrink_recovery.py
similarity index 100%
rename from tests/run_agent/test_image_shrink_recovery.py
rename to tests/agent/test_image_shrink_recovery.py
diff --git a/tests/run_agent/test_in_place_commit_rollback_99477.py b/tests/agent/test_in_place_commit_rollback.py
similarity index 100%
rename from tests/run_agent/test_in_place_commit_rollback_99477.py
rename to tests/agent/test_in_place_commit_rollback.py
diff --git a/tests/run_agent/test_in_place_compaction.py b/tests/agent/test_in_place_compaction.py
similarity index 100%
rename from tests/run_agent/test_in_place_compaction.py
rename to tests/agent/test_in_place_compaction.py
diff --git a/tests/run_agent/test_in_place_persist_marker_98450.py b/tests/agent/test_in_place_persist_marker.py
similarity index 100%
rename from tests/run_agent/test_in_place_persist_marker_98450.py
rename to tests/agent/test_in_place_persist_marker.py
diff --git a/tests/run_agent/test_infinite_compaction_loop.py b/tests/agent/test_infinite_compaction_loop.py
similarity index 100%
rename from tests/run_agent/test_infinite_compaction_loop.py
rename to tests/agent/test_infinite_compaction_loop.py
diff --git a/tests/run_agent/test_init_fallback_on_exhausted_pool.py b/tests/agent/test_init_fallback_on_exhausted_pool.py
similarity index 100%
rename from tests/run_agent/test_init_fallback_on_exhausted_pool.py
rename to tests/agent/test_init_fallback_on_exhausted_pool.py
diff --git a/tests/run_agent/test_interactive_interrupt.py b/tests/agent/test_interactive_interrupt.py
similarity index 100%
rename from tests/run_agent/test_interactive_interrupt.py
rename to tests/agent/test_interactive_interrupt.py
diff --git a/tests/run_agent/test_interpreter_shutdown_turn_exit.py b/tests/agent/test_interpreter_shutdown_turn_exit.py
similarity index 100%
rename from tests/run_agent/test_interpreter_shutdown_turn_exit.py
rename to tests/agent/test_interpreter_shutdown_turn_exit.py
diff --git a/tests/run_agent/test_interrupt_propagation.py b/tests/agent/test_interrupt_propagation.py
similarity index 100%
rename from tests/run_agent/test_interrupt_propagation.py
rename to tests/agent/test_interrupt_propagation.py
diff --git a/tests/run_agent/test_invalid_context_length_warning.py b/tests/agent/test_invalid_context_length_warning.py
similarity index 100%
rename from tests/run_agent/test_invalid_context_length_warning.py
rename to tests/agent/test_invalid_context_length_warning.py
diff --git a/tests/test_iron_proxy.py b/tests/agent/test_iron_proxy.py
similarity index 99%
rename from tests/test_iron_proxy.py
rename to tests/agent/test_iron_proxy.py
index cdd563041b..995c771f62 100644
--- a/tests/test_iron_proxy.py
+++ b/tests/agent/test_iron_proxy.py
@@ -7,7 +7,7 @@ extraction + checksum verification fully mocked), the subprocess lifecycle
docker backend's egress arg builder.
Live network and the real ``iron-proxy`` binary are NEVER touched. See
-``tests/test_iron_proxy_e2e.py`` (gated behind a marker) for the real-binary
+``tests/agent/test_iron_proxy_e2e.py`` (gated behind a marker) for the real-binary
smoke test.
"""
diff --git a/tests/test_iron_proxy_e2e.py b/tests/agent/test_iron_proxy_e2e.py
similarity index 100%
rename from tests/test_iron_proxy_e2e.py
rename to tests/agent/test_iron_proxy_e2e.py
diff --git a/tests/run_agent/test_iteration_budget_race.py b/tests/agent/test_iteration_budget_race.py
similarity index 100%
rename from tests/run_agent/test_iteration_budget_race.py
rename to tests/agent/test_iteration_budget_race.py
diff --git a/tests/run_agent/test_jsondecodeerror_retryable.py b/tests/agent/test_jsondecodeerror_retryable.py
similarity index 100%
rename from tests/run_agent/test_jsondecodeerror_retryable.py
rename to tests/agent/test_jsondecodeerror_retryable.py
diff --git a/tests/run_agent/test_last_reasoning_per_turn.py b/tests/agent/test_last_reasoning_per_turn.py
similarity index 100%
rename from tests/run_agent/test_last_reasoning_per_turn.py
rename to tests/agent/test_last_reasoning_per_turn.py
diff --git a/tests/run_agent/test_length_continuation_thinking_exhaustion.py b/tests/agent/test_length_continuation_thinking_exhaustion.py
similarity index 100%
rename from tests/run_agent/test_length_continuation_thinking_exhaustion.py
rename to tests/agent/test_length_continuation_thinking_exhaustion.py
diff --git a/tests/run_agent/test_lmstudio_load_mode.py b/tests/agent/test_lmstudio_load_mode.py
similarity index 100%
rename from tests/run_agent/test_lmstudio_load_mode.py
rename to tests/agent/test_lmstudio_load_mode.py
diff --git a/tests/agent/test_load_time_durability_stamp_92231.py b/tests/agent/test_load_time_durability_stamp.py
similarity index 100%
rename from tests/agent/test_load_time_durability_stamp_92231.py
rename to tests/agent/test_load_time_durability_stamp.py
diff --git a/tests/run_agent/test_long_context_tier_429.py b/tests/agent/test_long_context_tier_429.py
similarity index 100%
rename from tests/run_agent/test_long_context_tier_429.py
rename to tests/agent/test_long_context_tier_429.py
diff --git a/tests/run_agent/test_malformed_tool_arguments.py b/tests/agent/test_malformed_tool_arguments.py
similarity index 100%
rename from tests/run_agent/test_malformed_tool_arguments.py
rename to tests/agent/test_malformed_tool_arguments.py
diff --git a/tests/run_agent/test_materialize_data_url_cleanup.py b/tests/agent/test_materialize_data_url_cleanup.py
similarity index 100%
rename from tests/run_agent/test_materialize_data_url_cleanup.py
rename to tests/agent/test_materialize_data_url_cleanup.py
diff --git a/tests/run_agent/test_memory_nudge_counter_hydration.py b/tests/agent/test_memory_nudge_counter_hydration.py
similarity index 100%
rename from tests/run_agent/test_memory_nudge_counter_hydration.py
rename to tests/agent/test_memory_nudge_counter_hydration.py
diff --git a/tests/run_agent/test_memory_provider_init.py b/tests/agent/test_memory_provider_init.py
similarity index 100%
rename from tests/run_agent/test_memory_provider_init.py
rename to tests/agent/test_memory_provider_init.py
diff --git a/tests/run_agent/test_memory_sync_interrupted.py b/tests/agent/test_memory_sync_interrupted.py
similarity index 98%
rename from tests/run_agent/test_memory_sync_interrupted.py
rename to tests/agent/test_memory_sync_interrupted.py
index e879676d1f..8cf07a8063 100644
--- a/tests/run_agent/test_memory_sync_interrupted.py
+++ b/tests/agent/test_memory_sync_interrupted.py
@@ -25,7 +25,7 @@ import pytest
def _bare_agent():
"""Build an ``AIAgent`` with only the attributes
``_sync_external_memory_for_turn`` touches — matches the bare-agent
- pattern used across ``tests/run_agent/test_interrupt_propagation.py``.
+ pattern used across ``tests/agent/test_interrupt_propagation.py``.
"""
from run_agent import AIAgent
diff --git a/tests/test_message_reactions.py b/tests/agent/test_message_reactions.py
similarity index 100%
rename from tests/test_message_reactions.py
rename to tests/agent/test_message_reactions.py
diff --git a/tests/run_agent/test_message_sequence_repair.py b/tests/agent/test_message_sequence_repair.py
similarity index 100%
rename from tests/run_agent/test_message_sequence_repair.py
rename to tests/agent/test_message_sequence_repair.py
diff --git a/tests/agent/test_meta_agent_init.py b/tests/agent/test_meta_agent_init.py
index 2d56453d10..c916d51385 100644
--- a/tests/agent/test_meta_agent_init.py
+++ b/tests/agent/test_meta_agent_init.py
@@ -116,7 +116,7 @@ def test_agent_init_anthropic_url_implies_provider_and_api_mode():
def test_agent_init_anthropic_url_preserves_credential_pool():
"""Anthropic-scoped credential pool must survive provider auto-detection.
- Mirrors tests/run_agent/test_63425_credential_pool_auto_detect.py at the
+ Mirrors tests/agent/test_credential_pool_auto_detect.py at the
public AIAgent surface (provider=None URL detection).
"""
from types import SimpleNamespace
diff --git a/tests/agent/test_moa_cold_start_cache_66793.py b/tests/agent/test_moa_cold_start_cache.py
similarity index 100%
rename from tests/agent/test_moa_cold_start_cache_66793.py
rename to tests/agent/test_moa_cold_start_cache.py
diff --git a/tests/run_agent/test_moa_fanout_cadence.py b/tests/agent/test_moa_fanout_cadence.py
similarity index 100%
rename from tests/run_agent/test_moa_fanout_cadence.py
rename to tests/agent/test_moa_fanout_cadence.py
diff --git a/tests/run_agent/test_moa_loop_mode.py b/tests/agent/test_moa_loop_mode.py
similarity index 100%
rename from tests/run_agent/test_moa_loop_mode.py
rename to tests/agent/test_moa_loop_mode.py
diff --git a/tests/test_moa_prepared_request_leak_78382.py b/tests/agent/test_moa_prepared_request_leak.py
similarity index 100%
rename from tests/test_moa_prepared_request_leak_78382.py
rename to tests/agent/test_moa_prepared_request_leak.py
diff --git a/tests/run_agent/test_moa_privacy_filter.py b/tests/agent/test_moa_privacy_filter.py
similarity index 100%
rename from tests/run_agent/test_moa_privacy_filter.py
rename to tests/agent/test_moa_privacy_filter.py
diff --git a/tests/run_agent/test_moa_streaming.py b/tests/agent/test_moa_streaming.py
similarity index 100%
rename from tests/run_agent/test_moa_streaming.py
rename to tests/agent/test_moa_streaming.py
diff --git a/tests/run_agent/test_model_streaming_config.py b/tests/agent/test_model_streaming_config.py
similarity index 100%
rename from tests/run_agent/test_model_streaming_config.py
rename to tests/agent/test_model_streaming_config.py
diff --git a/tests/run_agent/test_66267_multimodal_interim.py b/tests/agent/test_multimodal_interim.py
similarity index 100%
rename from tests/run_agent/test_66267_multimodal_interim.py
rename to tests/agent/test_multimodal_interim.py
diff --git a/tests/run_agent/test_multimodal_tool_content_recovery.py b/tests/agent/test_multimodal_tool_content_recovery.py
similarity index 100%
rename from tests/run_agent/test_multimodal_tool_content_recovery.py
rename to tests/agent/test_multimodal_tool_content_recovery.py
diff --git a/tests/run_agent/test_native_compaction.py b/tests/agent/test_native_compaction.py
similarity index 100%
rename from tests/run_agent/test_native_compaction.py
rename to tests/agent/test_native_compaction.py
diff --git a/tests/run_agent/test_native_compaction_nontext_retention.py b/tests/agent/test_native_compaction_nontext_retention.py
similarity index 100%
rename from tests/run_agent/test_native_compaction_nontext_retention.py
rename to tests/agent/test_native_compaction_nontext_retention.py
diff --git a/tests/run_agent/test_native_compaction_summary_retention.py b/tests/agent/test_native_compaction_summary_retention.py
similarity index 100%
rename from tests/run_agent/test_native_compaction_summary_retention.py
rename to tests/agent/test_native_compaction_summary_retention.py
diff --git a/tests/run_agent/test_native_compaction_switch_capabilities.py b/tests/agent/test_native_compaction_switch_capabilities.py
similarity index 100%
rename from tests/run_agent/test_native_compaction_switch_capabilities.py
rename to tests/agent/test_native_compaction_switch_capabilities.py
diff --git a/tests/run_agent/test_nonretryable_error_html_summary.py b/tests/agent/test_nonretryable_error_html_summary.py
similarity index 100%
rename from tests/run_agent/test_nonretryable_error_html_summary.py
rename to tests/agent/test_nonretryable_error_html_summary.py
diff --git a/tests/run_agent/test_notice_spine.py b/tests/agent/test_notice_spine.py
similarity index 98%
rename from tests/run_agent/test_notice_spine.py
rename to tests/agent/test_notice_spine.py
index b928e6e9ca..2587cc7331 100644
--- a/tests/run_agent/test_notice_spine.py
+++ b/tests/agent/test_notice_spine.py
@@ -5,7 +5,7 @@ Covers:
object.__new__ — same pattern as test_steer.py and test_file_mutation_verifier.py).
B. Constructor / init_agent signature threading.
C. TUI _agent_cbs notice binding — mirrors the status_callback tests already
- in tests/test_tui_gateway_server.py.
+ in tests/tui_gateway/test_tui_gateway_server.py.
"""
from __future__ import annotations
diff --git a/tests/run_agent/test_nous_429_fallback_reentry.py b/tests/agent/test_nous_429_fallback_reentry.py
similarity index 100%
rename from tests/run_agent/test_nous_429_fallback_reentry.py
rename to tests/agent/test_nous_429_fallback_reentry.py
diff --git a/tests/run_agent/test_nous_fallback_unavailable.py b/tests/agent/test_nous_fallback_unavailable.py
similarity index 100%
rename from tests/run_agent/test_nous_fallback_unavailable.py
rename to tests/agent/test_nous_fallback_unavailable.py
diff --git a/tests/test_ollama_num_ctx.py b/tests/agent/test_ollama_num_ctx.py
similarity index 100%
rename from tests/test_ollama_num_ctx.py
rename to tests/agent/test_ollama_num_ctx.py
diff --git a/tests/test_onepassword_secrets.py b/tests/agent/test_onepassword_secrets.py
similarity index 99%
rename from tests/test_onepassword_secrets.py
rename to tests/agent/test_onepassword_secrets.py
index 8f2da661a4..cafcf60ad3 100644
--- a/tests/test_onepassword_secrets.py
+++ b/tests/agent/test_onepassword_secrets.py
@@ -19,7 +19,7 @@ import pytest
# Make the worktree importable without depending on the installed wheel.
-ROOT = Path(__file__).resolve().parents[1]
+ROOT = Path(__file__).resolve().parents[2]
if str(ROOT) not in sys.path:
sys.path.insert(0, str(ROOT))
diff --git a/tests/run_agent/test_openai_client_lifecycle.py b/tests/agent/test_openai_client_lifecycle.py
similarity index 100%
rename from tests/run_agent/test_openai_client_lifecycle.py
rename to tests/agent/test_openai_client_lifecycle.py
diff --git a/tests/run_agent/test_opencode_free_client_headers.py b/tests/agent/test_opencode_free_client_headers.py
similarity index 100%
rename from tests/run_agent/test_opencode_free_client_headers.py
rename to tests/agent/test_opencode_free_client_headers.py
diff --git a/tests/run_agent/test_92450_outer_error_retry_bound.py b/tests/agent/test_outer_error_retry_bound.py
similarity index 100%
rename from tests/run_agent/test_92450_outer_error_retry_bound.py
rename to tests/agent/test_outer_error_retry_bound.py
diff --git a/tests/test_output_cap_parsing.py b/tests/agent/test_output_cap_parsing.py
similarity index 100%
rename from tests/test_output_cap_parsing.py
rename to tests/agent/test_output_cap_parsing.py
diff --git a/tests/run_agent/test_overflow_overhead_aware_tokens.py b/tests/agent/test_overflow_overhead_aware_tokens.py
similarity index 100%
rename from tests/run_agent/test_overflow_overhead_aware_tokens.py
rename to tests/agent/test_overflow_overhead_aware_tokens.py
diff --git a/tests/run_agent/test_overflow_partial_terminal_106260.py b/tests/agent/test_overflow_partial_terminal.py
similarity index 100%
rename from tests/run_agent/test_overflow_partial_terminal_106260.py
rename to tests/agent/test_overflow_partial_terminal.py
diff --git a/tests/run_agent/test_partial_stream_finish_reason.py b/tests/agent/test_partial_stream_finish_reason.py
similarity index 100%
rename from tests/run_agent/test_partial_stream_finish_reason.py
rename to tests/agent/test_partial_stream_finish_reason.py
diff --git a/tests/run_agent/test_per_model_compression_threshold.py b/tests/agent/test_per_model_compression_threshold.py
similarity index 100%
rename from tests/run_agent/test_per_model_compression_threshold.py
rename to tests/agent/test_per_model_compression_threshold.py
diff --git a/tests/run_agent/test_per_model_threshold_init_ordering.py b/tests/agent/test_per_model_threshold_init_ordering.py
similarity index 100%
rename from tests/run_agent/test_per_model_threshold_init_ordering.py
rename to tests/agent/test_per_model_threshold_init_ordering.py
diff --git a/tests/run_agent/test_percentage_clamp.py b/tests/agent/test_percentage_clamp.py
similarity index 100%
rename from tests/run_agent/test_percentage_clamp.py
rename to tests/agent/test_percentage_clamp.py
diff --git a/tests/run_agent/test_plugin_context_engine_init.py b/tests/agent/test_plugin_context_engine_init.py
similarity index 100%
rename from tests/run_agent/test_plugin_context_engine_init.py
rename to tests/agent/test_plugin_context_engine_init.py
diff --git a/tests/run_agent/test_plugin_stream_hooks.py b/tests/agent/test_plugin_stream_hooks.py
similarity index 100%
rename from tests/run_agent/test_plugin_stream_hooks.py
rename to tests/agent/test_plugin_stream_hooks.py
diff --git a/tests/run_agent/test_post_tool_compression_attempt_cap.py b/tests/agent/test_post_tool_compression_attempt_cap.py
similarity index 100%
rename from tests/run_agent/test_post_tool_compression_attempt_cap.py
rename to tests/agent/test_post_tool_compression_attempt_cap.py
diff --git a/tests/run_agent/test_pre_compress_memory_context.py b/tests/agent/test_pre_compress_memory_context_handoff.py
similarity index 100%
rename from tests/run_agent/test_pre_compress_memory_context.py
rename to tests/agent/test_pre_compress_memory_context_handoff.py
diff --git a/tests/run_agent/test_preflight_compression_cap_e2e.py b/tests/agent/test_preflight_compression_cap_e2e.py
similarity index 100%
rename from tests/run_agent/test_preflight_compression_cap_e2e.py
rename to tests/agent/test_preflight_compression_cap_e2e.py
diff --git a/tests/agent/test_preflight_lock_defer.py b/tests/agent/test_preflight_lock_defer.py
index 4a0e9e6b11..006ba81145 100644
--- a/tests/agent/test_preflight_lock_defer.py
+++ b/tests/agent/test_preflight_lock_defer.py
@@ -1,6 +1,6 @@
"""Preflight lock-defer must not arm the insufficient-progress blocker.
-Companion to ``tests/run_agent/test_compression_lock_defer.py`` — pins the
+Companion to ``tests/agent/test_compression_lock_defer.py`` — pins the
``build_turn_context`` preflight loop's handling of a lock-contended
compression no-op (#69870 lock-skip signal, consumer salvaged from #49874):
diff --git a/tests/run_agent/test_preflight_timeout_typed_result.py b/tests/agent/test_preflight_timeout_typed_result.py
similarity index 100%
rename from tests/run_agent/test_preflight_timeout_typed_result.py
rename to tests/agent/test_preflight_timeout_typed_result.py
diff --git a/tests/run_agent/test_primary_runtime_restore.py b/tests/agent/test_primary_runtime_restore.py
similarity index 100%
rename from tests/run_agent/test_primary_runtime_restore.py
rename to tests/agent/test_primary_runtime_restore.py
diff --git a/tests/test_private_credential_writers.py b/tests/agent/test_private_credential_writers.py
similarity index 100%
rename from tests/test_private_credential_writers.py
rename to tests/agent/test_private_credential_writers.py
diff --git a/tests/run_agent/test_proactive_prune_loop_wiring.py b/tests/agent/test_proactive_prune_loop_wiring.py
similarity index 100%
rename from tests/run_agent/test_proactive_prune_loop_wiring.py
rename to tests/agent/test_proactive_prune_loop_wiring.py
diff --git a/tests/agent/test_protected_tail_pressure_61932.py b/tests/agent/test_protected_tail_pressure.py
similarity index 100%
rename from tests/agent/test_protected_tail_pressure_61932.py
rename to tests/agent/test_protected_tail_pressure.py
diff --git a/tests/run_agent/test_provider_attribution_headers.py b/tests/agent/test_provider_attribution_headers.py
similarity index 100%
rename from tests/run_agent/test_provider_attribution_headers.py
rename to tests/agent/test_provider_attribution_headers.py
diff --git a/tests/run_agent/test_provider_fallback.py b/tests/agent/test_provider_fallback.py
similarity index 100%
rename from tests/run_agent/test_provider_fallback.py
rename to tests/agent/test_provider_fallback.py
diff --git a/tests/run_agent/test_provider_parity.py b/tests/agent/test_provider_parity.py
similarity index 100%
rename from tests/run_agent/test_provider_parity.py
rename to tests/agent/test_provider_parity.py
diff --git a/tests/run_agent/test_reasoning_echo_resolver_e2e.py b/tests/agent/test_reasoning_echo_resolver_e2e.py
similarity index 100%
rename from tests/run_agent/test_reasoning_echo_resolver_e2e.py
rename to tests/agent/test_reasoning_echo_resolver_e2e.py
diff --git a/tests/test_redaction_registry.py b/tests/agent/test_redaction_registry.py
similarity index 100%
rename from tests/test_redaction_registry.py
rename to tests/agent/test_redaction_registry.py
diff --git a/tests/run_agent/test_repair_reanchors_current_turn_user_idx.py b/tests/agent/test_repair_reanchors_current_turn_user_idx.py
similarity index 100%
rename from tests/run_agent/test_repair_reanchors_current_turn_user_idx.py
rename to tests/agent/test_repair_reanchors_current_turn_user_idx.py
diff --git a/tests/run_agent/test_repair_tool_call_arguments.py b/tests/agent/test_repair_tool_call_arguments.py
similarity index 100%
rename from tests/run_agent/test_repair_tool_call_arguments.py
rename to tests/agent/test_repair_tool_call_arguments.py
diff --git a/tests/run_agent/test_repair_tool_call_name.py b/tests/agent/test_repair_tool_call_name.py
similarity index 100%
rename from tests/run_agent/test_repair_tool_call_name.py
rename to tests/agent/test_repair_tool_call_name.py
diff --git a/tests/run_agent/test_request_client_reuse_abort_races.py b/tests/agent/test_request_client_reuse_abort_races.py
similarity index 100%
rename from tests/run_agent/test_request_client_reuse_abort_races.py
rename to tests/agent/test_request_client_reuse_abort_races.py
diff --git a/tests/run_agent/test_reset_aware_primary_restore.py b/tests/agent/test_reset_aware_primary_restore.py
similarity index 100%
rename from tests/run_agent/test_reset_aware_primary_restore.py
rename to tests/agent/test_reset_aware_primary_restore.py
diff --git a/tests/run_agent/test_retry_status_buffer.py b/tests/agent/test_retry_status_buffer.py
similarity index 100%
rename from tests/run_agent/test_retry_status_buffer.py
rename to tests/agent/test_retry_status_buffer.py
diff --git a/tests/test_retry_utils.py b/tests/agent/test_retry_utils.py
similarity index 100%
rename from tests/test_retry_utils.py
rename to tests/agent/test_retry_utils.py
diff --git a/tests/run_agent/test_review_prompt_class_first.py b/tests/agent/test_review_prompt_class_first.py
similarity index 100%
rename from tests/run_agent/test_review_prompt_class_first.py
rename to tests/agent/test_review_prompt_class_first.py
diff --git a/tests/agent/test_rotation_flush_persisted_boundary_68196.py b/tests/agent/test_rotation_flush_persisted_boundary.py
similarity index 100%
rename from tests/agent/test_rotation_flush_persisted_boundary_68196.py
rename to tests/agent/test_rotation_flush_persisted_boundary.py
diff --git a/tests/run_agent/test_run_agent.py b/tests/agent/test_run_agent.py
similarity index 100%
rename from tests/run_agent/test_run_agent.py
rename to tests/agent/test_run_agent.py
diff --git a/tests/run_agent/test_run_agent_codex_responses.py b/tests/agent/test_run_agent_codex_responses.py
similarity index 100%
rename from tests/run_agent/test_run_agent_codex_responses.py
rename to tests/agent/test_run_agent_codex_responses.py
diff --git a/tests/run_agent/test_run_agent_multimodal_prologue.py b/tests/agent/test_run_agent_multimodal_prologue.py
similarity index 100%
rename from tests/run_agent/test_run_agent_multimodal_prologue.py
rename to tests/agent/test_run_agent_multimodal_prologue.py
diff --git a/tests/run_agent/test_sanitiser_escalation.py b/tests/agent/test_sanitiser_escalation.py
similarity index 100%
rename from tests/run_agent/test_sanitiser_escalation.py
rename to tests/agent/test_sanitiser_escalation.py
diff --git a/tests/run_agent/test_sequential_chats_live.py b/tests/agent/test_sequential_chats_live.py
similarity index 98%
rename from tests/run_agent/test_sequential_chats_live.py
rename to tests/agent/test_sequential_chats_live.py
index f6b9937bda..9a32c15cd9 100644
--- a/tests/run_agent/test_sequential_chats_live.py
+++ b/tests/agent/test_sequential_chats_live.py
@@ -11,7 +11,7 @@ reproduction against a real provider so we have a true end-to-end smoke test
for any future keepalive / transport plumbing.
Opt-in — not part of default CI:
- HERMES_LIVE_TESTS=1 pytest tests/run_agent/test_sequential_chats_live.py -v
+ HERMES_LIVE_TESTS=1 pytest tests/agent/test_sequential_chats_live.py -v
Requires ``OPENROUTER_API_KEY`` to be set (or sourced via ~/.hermes/.env).
"""
diff --git a/tests/run_agent/test_sequential_tool_timeout.py b/tests/agent/test_sequential_tool_timeout.py
similarity index 100%
rename from tests/run_agent/test_sequential_tool_timeout.py
rename to tests/agent/test_sequential_tool_timeout.py
diff --git a/tests/run_agent/test_session_activity_persist.py b/tests/agent/test_session_activity_persist.py
similarity index 100%
rename from tests/run_agent/test_session_activity_persist.py
rename to tests/agent/test_session_activity_persist.py
diff --git a/tests/run_agent/test_session_id_env.py b/tests/agent/test_session_id_env.py
similarity index 100%
rename from tests/run_agent/test_session_id_env.py
rename to tests/agent/test_session_id_env.py
diff --git a/tests/run_agent/test_session_meta_filtering.py b/tests/agent/test_session_meta_filtering.py
similarity index 100%
rename from tests/run_agent/test_session_meta_filtering.py
rename to tests/agent/test_session_meta_filtering.py
diff --git a/tests/run_agent/test_session_reset_fix.py b/tests/agent/test_session_reset_fix.py
similarity index 100%
rename from tests/run_agent/test_session_reset_fix.py
rename to tests/agent/test_session_reset_fix.py
diff --git a/tests/agent/test_session_rotation_flush_cold_resume_68454.py b/tests/agent/test_session_rotation_flush_cold_resume.py
similarity index 100%
rename from tests/agent/test_session_rotation_flush_cold_resume_68454.py
rename to tests/agent/test_session_rotation_flush_cold_resume.py
diff --git a/tests/run_agent/test_session_source.py b/tests/agent/test_session_source.py
similarity index 100%
rename from tests/run_agent/test_session_source.py
rename to tests/agent/test_session_source.py
diff --git a/tests/run_agent/test_70773_shared_client_fd_corruption.py b/tests/agent/test_shared_client_fd_corruption.py
similarity index 100%
rename from tests/run_agent/test_70773_shared_client_fd_corruption.py
rename to tests/agent/test_shared_client_fd_corruption.py
diff --git a/tests/run_agent/test_skill_applied_notifications.py b/tests/agent/test_skill_applied_notifications.py
similarity index 100%
rename from tests/run_agent/test_skill_applied_notifications.py
rename to tests/agent/test_skill_applied_notifications.py
diff --git a/tests/agent/test_skip_memory_store_65429.py b/tests/agent/test_skip_memory_store.py
similarity index 100%
rename from tests/agent/test_skip_memory_store_65429.py
rename to tests/agent/test_skip_memory_store.py
diff --git a/tests/test_sql_injection.py b/tests/agent/test_sql_injection.py
similarity index 100%
rename from tests/test_sql_injection.py
rename to tests/agent/test_sql_injection.py
diff --git a/tests/run_agent/test_start_order_gate.py b/tests/agent/test_start_order_gate.py
similarity index 100%
rename from tests/run_agent/test_start_order_gate.py
rename to tests/agent/test_start_order_gate.py
diff --git a/tests/run_agent/test_steer.py b/tests/agent/test_steer.py
similarity index 100%
rename from tests/run_agent/test_steer.py
rename to tests/agent/test_steer.py
diff --git a/tests/run_agent/test_stream_drop_logging.py b/tests/agent/test_stream_drop_logging.py
similarity index 100%
rename from tests/run_agent/test_stream_drop_logging.py
rename to tests/agent/test_stream_drop_logging.py
diff --git a/tests/run_agent/test_stream_interrupt_retry.py b/tests/agent/test_stream_interrupt_retry.py
similarity index 100%
rename from tests/run_agent/test_stream_interrupt_retry.py
rename to tests/agent/test_stream_interrupt_retry.py
diff --git a/tests/run_agent/test_stream_single_writer_65991.py b/tests/agent/test_stream_single_writer.py
similarity index 100%
rename from tests/run_agent/test_stream_single_writer_65991.py
rename to tests/agent/test_stream_single_writer.py
diff --git a/tests/run_agent/test_stream_stale_breaker_reset.py b/tests/agent/test_stream_stale_breaker_reset.py
similarity index 98%
rename from tests/run_agent/test_stream_stale_breaker_reset.py
rename to tests/agent/test_stream_stale_breaker_reset.py
index 919b9ea865..297a1cd7f4 100644
--- a/tests/run_agent/test_stream_stale_breaker_reset.py
+++ b/tests/agent/test_stream_stale_breaker_reset.py
@@ -27,7 +27,7 @@ from run_agent import AIAgent
def _make_agent_openrouter():
"""Minimal openrouter agent (skips __init__), mirroring
- tests/run_agent/test_switch_model_rollback.py."""
+ tests/agent/test_switch_model_rollback.py."""
agent = AIAgent.__new__(AIAgent)
agent.provider = "openrouter"
@@ -58,7 +58,7 @@ def _make_agent_openrouter():
def _make_fallback_agent(fallback_model):
"""Full-constructor agent for the fallback path, mirroring
- tests/run_agent/test_24996_fallback_exhaustion_cooldown.py."""
+ tests/agent/test_fallback_exhaustion_cooldown.py."""
with (
patch("model_tools.get_tool_definitions", return_value=[]),
patch("model_tools.check_toolset_requirements", return_value={}),
diff --git a/tests/run_agent/test_stream_stale_circuit_breaker.py b/tests/agent/test_stream_stale_circuit_breaker.py
similarity index 98%
rename from tests/run_agent/test_stream_stale_circuit_breaker.py
rename to tests/agent/test_stream_stale_circuit_breaker.py
index af6e6c26d6..a2bddeaa33 100644
--- a/tests/run_agent/test_stream_stale_circuit_breaker.py
+++ b/tests/agent/test_stream_stale_circuit_breaker.py
@@ -11,7 +11,7 @@ These tests cover the guard added to ``interruptible_streaming_api_call``:
- a successful stream resets the consecutive-stale streak;
- a stale-stream kill increments the consecutive-stale streak.
-The harness mirrors tests/run_agent/test_28161_anthropic_stream_pool_cleanup.py.
+The harness mirrors tests/agent/test_anthropic_stream_pool_cleanup.py.
"""
import threading
diff --git a/tests/run_agent/test_streamed_text_accumulation.py b/tests/agent/test_streamed_text_accumulation.py
similarity index 100%
rename from tests/run_agent/test_streamed_text_accumulation.py
rename to tests/agent/test_streamed_text_accumulation.py
diff --git a/tests/run_agent/test_streaming.py b/tests/agent/test_streaming.py
similarity index 100%
rename from tests/run_agent/test_streaming.py
rename to tests/agent/test_streaming.py
diff --git a/tests/run_agent/test_streaming_tool_call_repair.py b/tests/agent/test_streaming_tool_call_repair.py
similarity index 100%
rename from tests/run_agent/test_streaming_tool_call_repair.py
rename to tests/agent/test_streaming_tool_call_repair.py
diff --git a/tests/run_agent/test_strict_api_validation.py b/tests/agent/test_strict_api_validation.py
similarity index 100%
rename from tests/run_agent/test_strict_api_validation.py
rename to tests/agent/test_strict_api_validation.py
diff --git a/tests/run_agent/test_strip_reasoning_tags_cli.py b/tests/agent/test_strip_reasoning_tags_cli.py
similarity index 100%
rename from tests/run_agent/test_strip_reasoning_tags_cli.py
rename to tests/agent/test_strip_reasoning_tags_cli.py
diff --git a/tests/run_agent/test_summarize_api_error.py b/tests/agent/test_summarize_api_error.py
similarity index 100%
rename from tests/run_agent/test_summarize_api_error.py
rename to tests/agent/test_summarize_api_error.py
diff --git a/tests/run_agent/test_switch_model_bedrock_sigv4.py b/tests/agent/test_switch_model_bedrock_sigv4.py
similarity index 100%
rename from tests/run_agent/test_switch_model_bedrock_sigv4.py
rename to tests/agent/test_switch_model_bedrock_sigv4.py
diff --git a/tests/run_agent/test_switch_model_context.py b/tests/agent/test_switch_model_context.py
similarity index 100%
rename from tests/run_agent/test_switch_model_context.py
rename to tests/agent/test_switch_model_context.py
diff --git a/tests/run_agent/test_switch_model_fallback_prune.py b/tests/agent/test_switch_model_fallback_prune.py
similarity index 100%
rename from tests/run_agent/test_switch_model_fallback_prune.py
rename to tests/agent/test_switch_model_fallback_prune.py
diff --git a/tests/run_agent/test_switch_model_pool_reload_52727.py b/tests/agent/test_switch_model_pool_reload.py
similarity index 100%
rename from tests/run_agent/test_switch_model_pool_reload_52727.py
rename to tests/agent/test_switch_model_pool_reload.py
diff --git a/tests/run_agent/test_switch_model_reapplies_headers.py b/tests/agent/test_switch_model_reapplies_headers.py
similarity index 100%
rename from tests/run_agent/test_switch_model_reapplies_headers.py
rename to tests/agent/test_switch_model_reapplies_headers.py
diff --git a/tests/run_agent/test_switch_model_reasoning_override.py b/tests/agent/test_switch_model_reasoning_override.py
similarity index 100%
rename from tests/run_agent/test_switch_model_reasoning_override.py
rename to tests/agent/test_switch_model_reasoning_override.py
diff --git a/tests/run_agent/test_switch_model_rollback.py b/tests/agent/test_switch_model_rollback.py
similarity index 100%
rename from tests/run_agent/test_switch_model_rollback.py
rename to tests/agent/test_switch_model_rollback.py
diff --git a/tests/run_agent/test_switch_model_stale_base_url.py b/tests/agent/test_switch_model_stale_base_url.py
similarity index 100%
rename from tests/run_agent/test_switch_model_stale_base_url.py
rename to tests/agent/test_switch_model_stale_base_url.py
diff --git a/tests/run_agent/test_81641_text_turn_incremental_persistence.py b/tests/agent/test_text_turn_incremental_persistence.py
similarity index 100%
rename from tests/run_agent/test_81641_text_turn_incremental_persistence.py
rename to tests/agent/test_text_turn_incremental_persistence.py
diff --git a/tests/run_agent/test_thinking_only_sanitizer.py b/tests/agent/test_thinking_only_sanitizer.py
similarity index 100%
rename from tests/run_agent/test_thinking_only_sanitizer.py
rename to tests/agent/test_thinking_only_sanitizer.py
diff --git a/tests/run_agent/test_thinking_prefill_trailing_turn.py b/tests/agent/test_thinking_prefill_trailing_turn.py
similarity index 100%
rename from tests/run_agent/test_thinking_prefill_trailing_turn.py
rename to tests/agent/test_thinking_prefill_trailing_turn.py
diff --git a/tests/run_agent/test_thinking_sig_recovery_persistence.py b/tests/agent/test_thinking_sig_recovery_persistence.py
similarity index 100%
rename from tests/run_agent/test_thinking_sig_recovery_persistence.py
rename to tests/agent/test_thinking_sig_recovery_persistence.py
diff --git a/tests/run_agent/test_tls_fd_recycle_corruption.py b/tests/agent/test_tls_fd_recycle_corruption.py
similarity index 100%
rename from tests/run_agent/test_tls_fd_recycle_corruption.py
rename to tests/agent/test_tls_fd_recycle_corruption.py
diff --git a/tests/run_agent/test_token_persistence_non_cli.py b/tests/agent/test_token_persistence_non_cli.py
similarity index 100%
rename from tests/run_agent/test_token_persistence_non_cli.py
rename to tests/agent/test_token_persistence_non_cli.py
diff --git a/tests/run_agent/test_tool_activity_heartbeat.py b/tests/agent/test_tool_activity_heartbeat.py
similarity index 100%
rename from tests/run_agent/test_tool_activity_heartbeat.py
rename to tests/agent/test_tool_activity_heartbeat.py
diff --git a/tests/run_agent/test_tool_arg_coercion.py b/tests/agent/test_tool_arg_coercion.py
similarity index 100%
rename from tests/run_agent/test_tool_arg_coercion.py
rename to tests/agent/test_tool_arg_coercion.py
diff --git a/tests/run_agent/test_tool_batch_segmentation.py b/tests/agent/test_tool_batch_segmentation.py
similarity index 100%
rename from tests/run_agent/test_tool_batch_segmentation.py
rename to tests/agent/test_tool_batch_segmentation.py
diff --git a/tests/run_agent/test_tool_call_args_sanitizer.py b/tests/agent/test_tool_call_args_sanitizer.py
similarity index 100%
rename from tests/run_agent/test_tool_call_args_sanitizer.py
rename to tests/agent/test_tool_call_args_sanitizer.py
diff --git a/tests/run_agent/test_tool_call_guardrail_runtime.py b/tests/agent/test_tool_call_guardrail_runtime.py
similarity index 100%
rename from tests/run_agent/test_tool_call_guardrail_runtime.py
rename to tests/agent/test_tool_call_guardrail_runtime.py
diff --git a/tests/run_agent/test_tool_call_incremental_persistence.py b/tests/agent/test_tool_call_incremental_persistence.py
similarity index 100%
rename from tests/run_agent/test_tool_call_incremental_persistence.py
rename to tests/agent/test_tool_call_incremental_persistence.py
diff --git a/tests/run_agent/test_tool_executor_contextvar_propagation.py b/tests/agent/test_tool_executor_contextvar_propagation.py
similarity index 100%
rename from tests/run_agent/test_tool_executor_contextvar_propagation.py
rename to tests/agent/test_tool_executor_contextvar_propagation.py
diff --git a/tests/run_agent/test_tool_name_db_persistence.py b/tests/agent/test_tool_name_db_persistence.py
similarity index 100%
rename from tests/run_agent/test_tool_name_db_persistence.py
rename to tests/agent/test_tool_name_db_persistence.py
diff --git a/tests/run_agent/test_turn_completion_explainer.py b/tests/agent/test_turn_completion_explainer.py
similarity index 99%
rename from tests/run_agent/test_turn_completion_explainer.py
rename to tests/agent/test_turn_completion_explainer.py
index 6137423291..160bc18dc0 100644
--- a/tests/run_agent/test_turn_completion_explainer.py
+++ b/tests/agent/test_turn_completion_explainer.py
@@ -27,7 +27,7 @@ from run_agent import AIAgent
# --------------------------------------------------------------------------
-# Fixtures (mirrors tests/run_agent/test_tool_call_guardrail_runtime.py)
+# Fixtures (mirrors tests/agent/test_tool_call_guardrail_runtime.py)
# --------------------------------------------------------------------------
def _mock_response(content="Hello", finish_reason="stop", tool_calls=None):
msg = SimpleNamespace(content=content, tool_calls=tool_calls)
diff --git a/tests/run_agent/test_turn_liveness_watchdog.py b/tests/agent/test_turn_liveness_watchdog.py
similarity index 100%
rename from tests/run_agent/test_turn_liveness_watchdog.py
rename to tests/agent/test_turn_liveness_watchdog.py
diff --git a/tests/run_agent/test_unicode_ascii_codec.py b/tests/agent/test_unicode_ascii_codec.py
similarity index 100%
rename from tests/run_agent/test_unicode_ascii_codec.py
rename to tests/agent/test_unicode_ascii_codec.py
diff --git a/tests/run_agent/test_verification_continuation_budget.py b/tests/agent/test_verification_continuation_budget.py
similarity index 100%
rename from tests/run_agent/test_verification_continuation_budget.py
rename to tests/agent/test_verification_continuation_budget.py
diff --git a/tests/run_agent/test_vision_aware_preprocessing.py b/tests/agent/test_vision_aware_preprocessing.py
similarity index 100%
rename from tests/run_agent/test_vision_aware_preprocessing.py
rename to tests/agent/test_vision_aware_preprocessing.py
diff --git a/tests/agent/test_vision_routing_31179.py b/tests/agent/test_vision_routing.py
similarity index 100%
rename from tests/agent/test_vision_routing_31179.py
rename to tests/agent/test_vision_routing.py
diff --git a/tests/run_agent/test_vision_tool_messages.py b/tests/agent/test_vision_tool_messages.py
similarity index 100%
rename from tests/run_agent/test_vision_tool_messages.py
rename to tests/agent/test_vision_tool_messages.py
diff --git a/tests/run_agent/test_wait_state_visibility.py b/tests/agent/test_wait_state_visibility.py
similarity index 100%
rename from tests/run_agent/test_wait_state_visibility.py
rename to tests/agent/test_wait_state_visibility.py
diff --git a/tests/ci/test_classify_changes.py b/tests/ci/test_classify_changes.py
index c3c12e9c9e..e5f312c18c 100644
--- a/tests/ci/test_classify_changes.py
+++ b/tests/ci/test_classify_changes.py
@@ -170,7 +170,7 @@ CASES = {
_lanes(python=True, frontend=True, desktop_updater=True),
),
"desktop-update test → desktop_updater": (
- ["tests/test_desktop_update_windows_progress.py"],
+ ["tests/scripts/desktop_update/test_desktop_update_windows_progress.py"],
_lanes(python=True, python_prod=False, scan=True, desktop_updater=True),
),
"updater-process.ts → desktop_updater": (
@@ -380,14 +380,14 @@ def test_pull_request_changed_files_parses_gh_output(tmp_path, monkeypatch):
return subprocess.CompletedProcess(
args[0],
0,
- stdout="scripts/install.sh\ntests/test_install_sh_node_deps_workspaces.py\n",
+ stdout="scripts/install.sh\ntests/scripts/install/test_install_sh_node_deps_workspaces.py\n",
stderr="",
)
monkeypatch.setattr(_mod.subprocess, "run", fake_run)
assert pull_request_changed_files() == [
"scripts/install.sh",
- "tests/test_install_sh_node_deps_workspaces.py",
+ "tests/scripts/install/test_install_sh_node_deps_workspaces.py",
]
@@ -408,7 +408,7 @@ def test_main_recovers_pr_files_instead_of_fail_open_ci_review(monkeypatch, caps
monkeypatch.setattr(
_mod,
"pull_request_changed_files",
- lambda: ["scripts/install.sh", "tests/test_install_sh_node_deps_workspaces.py"],
+ lambda: ["scripts/install.sh", "tests/scripts/install/test_install_sh_node_deps_workspaces.py"],
)
monkeypatch.setattr(sys, "stdin", io.StringIO("\n"))
monkeypatch.delenv("GITHUB_OUTPUT", raising=False)
diff --git a/tests/cli/__init__.py b/tests/cli/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/tests/cli/conftest.py b/tests/cli/conftest.py
deleted file mode 100644
index f3afb04276..0000000000
--- a/tests/cli/conftest.py
+++ /dev/null
@@ -1,50 +0,0 @@
-"""Shared fixtures for CLI tests.
-
-prompt_toolkit / capsys isolation
----------------------------------
-``cli._cprint`` renders through ``prompt_toolkit.print_formatted_text``,
-which — when called with no explicit ``output=`` — lazily creates an
-``Output`` from ``sys.stdout`` **and caches it on the process-global default
-``AppSession``** (``prompt_toolkit.application.current._current_app_session``,
-a ``ContextVar`` with a module-level default). The cache is keyed to nothing
-and never re-reads ``sys.stdout``.
-
-Under pytest, ``capsys`` swaps ``sys.stdout`` for a fresh buffer per test.
-So the first CLI test that emits through ``_cprint`` (e.g. one exercising
-``/queue``, which prints a "Queued: …" line) locks prompt_toolkit's cached
-output onto *its* captured stdout. Every later ``capsys`` test that asserts
-on ``_cprint`` output then reads an empty buffer, because the render went to
-the first test's now-dead capture target. That is the mechanism behind the
-order-dependent ``test_resume_quiet_stderr`` failure: it passes in isolation
-and in its own file, but fails in a full ``tests/cli`` run.
-
-Reset the cached output before every CLI test so each one re-creates a fresh
-prompt_toolkit ``Output`` bound to its own ``sys.stdout`` on first use. This
-is a no-op when prompt_toolkit isn't importable and cheap otherwise (the
-property re-creates lazily).
-"""
-
-import pytest
-
-
-@pytest.fixture(autouse=True)
-def _reset_prompt_toolkit_output_cache():
- """Clear prompt_toolkit's cached AppSession output around each CLI test.
-
- See the module docstring for the capsys/prompt_toolkit interaction this
- guards against.
- """
-
- def _clear() -> None:
- try:
- from prompt_toolkit.application.current import get_app_session
-
- get_app_session()._output = None
- except Exception:
- # prompt_toolkit not importable / internal shape changed — the
- # tests that rely on this simply keep their prior behavior.
- pass
-
- _clear()
- yield
- _clear()
diff --git a/tests/cli/test_cli_skin_integration.py b/tests/cli/test_cli_skin_integration.py
deleted file mode 100644
index 4fa73c10e9..0000000000
--- a/tests/cli/test_cli_skin_integration.py
+++ /dev/null
@@ -1,89 +0,0 @@
-from types import SimpleNamespace
-from unittest.mock import MagicMock, patch
-
-from cli import HermesCLI, _rich_text_from_ansi
-from hermes_cli.skin_engine import get_active_skin, set_active_skin
-
-
-def _make_cli_stub():
- cli = HermesCLI.__new__(HermesCLI)
- cli._sudo_state = None
- cli._secret_state = None
- cli._approval_state = None
- cli._clarify_state = None
- cli._clarify_freetext = False
- cli._command_running = False
- cli._agent_running = False
- cli._voice_recording = False
- cli._voice_processing = False
- cli._voice_mode = False
- cli._command_spinner_frame = lambda: "⟳"
- cli._tui_style_base = {
- "prompt": "#fff",
- "input-area": "#fff",
- "input-rule": "#aaa",
- "prompt-working": "#888 italic",
- }
- cli._app = SimpleNamespace(style=None)
- cli._invalidate = MagicMock()
- return cli
-
-
-class TestCliSkinPromptIntegration:
-
- def test_ares_prompt_fragments_use_skin_symbol(self):
- cli = _make_cli_stub()
-
- set_active_skin("ares")
- assert cli._get_tui_prompt_fragments() == [("class:prompt", "⚔ ")]
-
- def test_secret_prompt_fragments_preserve_secret_state(self):
- cli = _make_cli_stub()
- cli._secret_state = {"response_queue": object()}
-
- set_active_skin("ares")
- assert cli._get_tui_prompt_fragments() == [("class:sudo-prompt", "🔑 ⚔ ")]
-
-
- def test_narrow_terminals_compact_voice_recording_prompt_fragments(self):
- cli = _make_cli_stub()
- cli._voice_recording = True
- cli._voice_recorder = SimpleNamespace(current_rms=3000)
-
- with patch.object(HermesCLI, "_get_tui_terminal_width", return_value=50):
- frags = cli._get_tui_prompt_fragments()
-
- assert frags[0][0] == "class:voice-recording"
- assert frags[0][1].startswith("●")
- assert "❯" not in frags[0][1]
-
-
-
- def test_apply_tui_skin_style_updates_running_app(self):
- cli = _make_cli_stub()
-
- set_active_skin("ares")
- assert cli._apply_tui_skin_style() is True
- assert cli._app.style is not None
- cli._invalidate.assert_called_once_with(min_interval=0.0)
-
- def test_handle_skin_command_refreshes_live_tui(self, capsys):
- cli = _make_cli_stub()
-
- with patch("cli.save_config_value", return_value=True):
- cli._handle_skin_command("/skin ares")
-
- output = capsys.readouterr().out
- assert "Skin set to: ares (saved)" in output
- assert "Prompt + TUI colors updated." in output
- assert cli._app.style is not None
-
-
-class TestAnsiRichTextHelper:
- def test_preserves_literal_brackets(self):
- text = _rich_text_from_ansi("[notatag] literal")
- assert text.plain == "[notatag] literal"
-
- def test_strips_ansi_but_keeps_plain_text(self):
- text = _rich_text_from_ansi("\x1b[31mred\x1b[0m")
- assert text.plain == "red"
diff --git a/tests/conftest.py b/tests/conftest.py
index f69c015404..45d67cf7a9 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -820,7 +820,7 @@ def _state_db_write_guard(request, monkeypatch):
# ``_methods`` dict at import time and keeps per-session state in module
# globals (sessions, child-run registry, config cache, DB handle). The
# canonical per-file process isolation above hides any leakage, but a direct
-# multi-file invocation (``pytest tests/tui_gateway/ tests/test_tui_gateway_server.py``,
+# multi-file invocation (``pytest tests/tui_gateway/ tests/tui_gateway/test_tui_gateway_server.py``,
# or plain ``pytest tests/``) shares one interpreter: a test that stubs
# ``_methods["slash.exec"]`` or leaves an active-session lease behind breaks
# unrelated tests in later files. This fixture snapshots the cheap-to-copy
@@ -1064,7 +1064,7 @@ def _wal_is_usable() -> bool:
# Same class of incident as the live-system guard above, different primitive:
# a test run spoke the string "partial answer complete" out of the developer's
# speakers. That string is a test fixture
-# (``tests/test_tui_gateway_server.py``'s fake ``final_response``), and the
+# (``tests/tui_gateway/test_tui_gateway_server.py``'s fake ``final_response``), and the
# route it took is fully in-process — no leaked shell variable required:
#
# 1. ``test_voice_toggle_tts_branch_also_carries_record_key`` drives the
diff --git a/tests/cron/test_cron_direct_api_call_62151.py b/tests/cron/test_cron_direct_api_call.py
similarity index 100%
rename from tests/cron/test_cron_direct_api_call_62151.py
rename to tests/cron/test_cron_direct_api_call.py
diff --git a/tests/cron/test_cron_emfile_stall_87644.py b/tests/cron/test_cron_emfile_stall.py
similarity index 100%
rename from tests/cron/test_cron_emfile_stall_87644.py
rename to tests/cron/test_cron_emfile_stall.py
diff --git a/tests/test_cron_manage_profile_scope.py b/tests/cron/test_cron_manage_profile_scope.py
similarity index 100%
rename from tests/test_cron_manage_profile_scope.py
rename to tests/cron/test_cron_manage_profile_scope.py
diff --git a/tests/cron/test_cron_run_stale_claim_reap_86721.py b/tests/cron/test_cron_run_stale_claim_reap.py
similarity index 100%
rename from tests/cron/test_cron_run_stale_claim_reap_86721.py
rename to tests/cron/test_cron_run_stale_claim_reap.py
diff --git a/tests/cron/test_87033_cronjob_gateway_liveness.py b/tests/cron/test_cronjob_gateway_liveness.py
similarity index 100%
rename from tests/cron/test_87033_cronjob_gateway_liveness.py
rename to tests/cron/test_cronjob_gateway_liveness.py
diff --git a/tests/test_estop.py b/tests/cron/test_estop.py
similarity index 100%
rename from tests/test_estop.py
rename to tests/cron/test_estop.py
diff --git a/tests/cron/test_jobs_shrink_merge_80624.py b/tests/cron/test_jobs_shrink_merge.py
similarity index 100%
rename from tests/cron/test_jobs_shrink_merge_80624.py
rename to tests/cron/test_jobs_shrink_merge.py
diff --git a/tests/cron/test_ticker_stall_60703.py b/tests/cron/test_ticker_stall.py
similarity index 100%
rename from tests/cron/test_ticker_stall_60703.py
rename to tests/cron/test_ticker_stall.py
diff --git a/tests/test_timezone.py b/tests/cron/test_timezone.py
similarity index 100%
rename from tests/test_timezone.py
rename to tests/cron/test_timezone.py
diff --git a/tests/relay/test_relay_format_hints.py b/tests/gateway/relay/test_relay_format_hints.py
similarity index 100%
rename from tests/relay/test_relay_format_hints.py
rename to tests/gateway/relay/test_relay_format_hints.py
diff --git a/tests/relay/test_relay_inchannel_continuable.py b/tests/gateway/relay/test_relay_inchannel_continuable.py
similarity index 100%
rename from tests/relay/test_relay_inchannel_continuable.py
rename to tests/gateway/relay/test_relay_inchannel_continuable.py
diff --git a/tests/relay/test_relay_prompt_ack_stream_isolation.py b/tests/gateway/relay/test_relay_prompt_ack_stream_isolation.py
similarity index 100%
rename from tests/relay/test_relay_prompt_ack_stream_isolation.py
rename to tests/gateway/relay/test_relay_prompt_ack_stream_isolation.py
diff --git a/tests/gateway/test_35809_auto_reset_clean_context.py b/tests/gateway/test_auto_reset_clean_context.py
similarity index 100%
rename from tests/gateway/test_35809_auto_reset_clean_context.py
rename to tests/gateway/test_auto_reset_clean_context.py
diff --git a/tests/gateway/test_10710_auto_reset_evicts_cached_agent.py b/tests/gateway/test_auto_reset_evicts_cached_agent.py
similarity index 100%
rename from tests/gateway/test_10710_auto_reset_evicts_cached_agent.py
rename to tests/gateway/test_auto_reset_evicts_cached_agent.py
diff --git a/tests/gateway/test_53175_cleanup_off_loop.py b/tests/gateway/test_cleanup_off_loop.py
similarity index 100%
rename from tests/gateway/test_53175_cleanup_off_loop.py
rename to tests/gateway/test_cleanup_off_loop.py
diff --git a/tests/gateway/test_compression_interrupt_demotion_56391.py b/tests/gateway/test_compression_interrupt_demotion.py
similarity index 100%
rename from tests/gateway/test_compression_interrupt_demotion_56391.py
rename to tests/gateway/test_compression_interrupt_demotion.py
diff --git a/tests/gateway/test_42039_duplicate_user_message.py b/tests/gateway/test_duplicate_user_message.py
similarity index 100%
rename from tests/gateway/test_42039_duplicate_user_message.py
rename to tests/gateway/test_duplicate_user_message.py
diff --git a/tests/test_empty_model_fallback.py b/tests/gateway/test_empty_model_fallback.py
similarity index 100%
rename from tests/test_empty_model_fallback.py
rename to tests/gateway/test_empty_model_fallback.py
diff --git a/tests/gateway/test_env_override_explicit_disable_48820.py b/tests/gateway/test_env_override_explicit_disable.py
similarity index 100%
rename from tests/gateway/test_env_override_explicit_disable_48820.py
rename to tests/gateway/test_env_override_explicit_disable.py
diff --git a/tests/gateway/test_71671_faulthandler_no_stderr.py b/tests/gateway/test_faulthandler_no_stderr.py
similarity index 100%
rename from tests/gateway/test_71671_faulthandler_no_stderr.py
rename to tests/gateway/test_faulthandler_no_stderr.py
diff --git a/tests/test_gateway_streaming_nested_config.py b/tests/gateway/test_gateway_streaming_nested_config.py
similarity index 100%
rename from tests/test_gateway_streaming_nested_config.py
rename to tests/gateway/test_gateway_streaming_nested_config.py
diff --git a/tests/gateway/test_internal_notification_marker_82888.py b/tests/gateway/test_internal_notification_marker.py
similarity index 100%
rename from tests/gateway/test_internal_notification_marker_82888.py
rename to tests/gateway/test_internal_notification_marker.py
diff --git a/tests/gateway/test_73771_media_resend_dedup.py b/tests/gateway/test_media_resend_dedup.py
similarity index 100%
rename from tests/gateway/test_73771_media_resend_dedup.py
rename to tests/gateway/test_media_resend_dedup.py
diff --git a/tests/gateway/test_73297_memory_flush_on_reset.py b/tests/gateway/test_memory_flush_on_reset.py
similarity index 100%
rename from tests/gateway/test_73297_memory_flush_on_reset.py
rename to tests/gateway/test_memory_flush_on_reset.py
diff --git a/tests/gateway/test_48031_model_switch_after_auto_reset.py b/tests/gateway/test_model_switch_after_auto_reset.py
similarity index 100%
rename from tests/gateway/test_48031_model_switch_after_auto_reset.py
rename to tests/gateway/test_model_switch_after_auto_reset.py
diff --git a/tests/gateway/test_64674_multiplex_primary_token_scope.py b/tests/gateway/test_multiplex_primary_token_scope.py
similarity index 100%
rename from tests/gateway/test_64674_multiplex_primary_token_scope.py
rename to tests/gateway/test_multiplex_primary_token_scope.py
diff --git a/tests/gateway/test_priority_path_compression_demotion_56391.py b/tests/gateway/test_priority_path_compression_demotion.py
similarity index 100%
rename from tests/gateway/test_priority_path_compression_demotion_56391.py
rename to tests/gateway/test_priority_path_compression_demotion.py
diff --git a/tests/test_profile_isolation_runtime.py b/tests/gateway/test_profile_isolation_runtime.py
similarity index 100%
rename from tests/test_profile_isolation_runtime.py
rename to tests/gateway/test_profile_isolation_runtime.py
diff --git a/tests/test_89315_replace_ownership_guard.py b/tests/gateway/test_replace_ownership_guard.py
similarity index 100%
rename from tests/test_89315_replace_ownership_guard.py
rename to tests/gateway/test_replace_ownership_guard.py
diff --git a/tests/gateway/test_35994_reset_button_deadlock.py b/tests/gateway/test_reset_button_deadlock.py
similarity index 100%
rename from tests/gateway/test_35994_reset_button_deadlock.py
rename to tests/gateway/test_reset_button_deadlock.py
diff --git a/tests/gateway/test_session_continuity_82616.py b/tests/gateway/test_session_continuity.py
similarity index 100%
rename from tests/gateway/test_session_continuity_82616.py
rename to tests/gateway/test_session_continuity.py
diff --git a/tests/gateway/test_session_split_brain_11016.py b/tests/gateway/test_session_split_brain.py
similarity index 100%
rename from tests/gateway/test_session_split_brain_11016.py
rename to tests/gateway/test_session_split_brain.py
diff --git a/tests/gateway/test_13121_shutdown_inflight_transcript_flush.py b/tests/gateway/test_shutdown_inflight_transcript_flush.py
similarity index 100%
rename from tests/gateway/test_13121_shutdown_inflight_transcript_flush.py
rename to tests/gateway/test_shutdown_inflight_transcript_flush.py
diff --git a/tests/gateway/test_silent_partial_delivery_95382.py b/tests/gateway/test_silent_partial_delivery.py
similarity index 100%
rename from tests/gateway/test_silent_partial_delivery_95382.py
rename to tests/gateway/test_silent_partial_delivery.py
diff --git a/tests/test_slack_thread_require_mention.py b/tests/gateway/test_slack_thread_require_mention.py
similarity index 100%
rename from tests/test_slack_thread_require_mention.py
rename to tests/gateway/test_slack_thread_require_mention.py
diff --git a/tests/gateway/test_25107_stale_base_url_api_mode.py b/tests/gateway/test_stale_base_url_api_mode.py
similarity index 100%
rename from tests/gateway/test_25107_stale_base_url_api_mode.py
rename to tests/gateway/test_stale_base_url_api_mode.py
diff --git a/tests/gateway/test_subagent_protection_30170.py b/tests/gateway/test_subagent_protection.py
similarity index 100%
rename from tests/gateway/test_subagent_protection_30170.py
rename to tests/gateway/test_subagent_protection.py
diff --git a/tests/gateway/test_telegram_closewait_limits_31599.py b/tests/gateway/test_telegram_closewait_limits.py
similarity index 100%
rename from tests/gateway/test_telegram_closewait_limits_31599.py
rename to tests/gateway/test_telegram_closewait_limits.py
diff --git a/tests/gateway/test_telegram_fallback_pool_release_71593.py b/tests/gateway/test_telegram_fallback_pool_release.py
similarity index 100%
rename from tests/gateway/test_telegram_fallback_pool_release_71593.py
rename to tests/gateway/test_telegram_fallback_pool_release.py
diff --git a/tests/gateway/test_telegram_prune_stale_topic_binding_31501.py b/tests/gateway/test_telegram_prune_stale_topic_binding.py
similarity index 100%
rename from tests/gateway/test_telegram_prune_stale_topic_binding_31501.py
rename to tests/gateway/test_telegram_prune_stale_topic_binding.py
diff --git a/tests/gateway/test_telegram_topic_profile_isolation_76423.py b/tests/gateway/test_telegram_topic_profile_isolation.py
similarity index 100%
rename from tests/gateway/test_telegram_topic_profile_isolation_76423.py
rename to tests/gateway/test_telegram_topic_profile_isolation.py
diff --git a/tests/gateway/test_telegram_topic_profile_routing_76423.py b/tests/gateway/test_telegram_topic_profile_routing.py
similarity index 100%
rename from tests/gateway/test_telegram_topic_profile_routing_76423.py
rename to tests/gateway/test_telegram_topic_profile_routing.py
diff --git a/tests/test_tini_shim.py b/tests/gateway/test_tini_shim.py
similarity index 98%
rename from tests/test_tini_shim.py
rename to tests/gateway/test_tini_shim.py
index 737cbaa8d1..aee5a63b6e 100644
--- a/tests/test_tini_shim.py
+++ b/tests/gateway/test_tini_shim.py
@@ -12,7 +12,7 @@ from pathlib import Path
import pytest
-REPO_ROOT = Path(__file__).resolve().parents[1]
+REPO_ROOT = Path(__file__).resolve().parents[2]
SHIM = REPO_ROOT / "docker" / "tini-shim.sh"
diff --git a/tests/gateway/test_transcript_read_failure_100788.py b/tests/gateway/test_transcript_read_failure.py
similarity index 100%
rename from tests/gateway/test_transcript_read_failure_100788.py
rename to tests/gateway/test_transcript_read_failure.py
diff --git a/tests/gateway/test_7100_transient_failure_transcript.py b/tests/gateway/test_transient_failure_transcript.py
similarity index 100%
rename from tests/gateway/test_7100_transient_failure_transcript.py
rename to tests/gateway/test_transient_failure_transcript.py
diff --git a/tests/gateway/test_watchdog_review_76354.py b/tests/gateway/test_watchdog_review.py
similarity index 100%
rename from tests/gateway/test_watchdog_review_76354.py
rename to tests/gateway/test_watchdog_review.py
diff --git a/tests/gateway/test_75349_whatsapp_multiplex_secret_scope.py b/tests/gateway/test_whatsapp_multiplex_secret_scope.py
similarity index 100%
rename from tests/gateway/test_75349_whatsapp_multiplex_secret_scope.py
rename to tests/gateway/test_whatsapp_multiplex_secret_scope.py
diff --git a/tests/test_yuanbao_integration.py b/tests/gateway/test_yuanbao_integration.py
similarity index 99%
rename from tests/test_yuanbao_integration.py
rename to tests/gateway/test_yuanbao_integration.py
index 7382dcd552..b40c1ea0c2 100644
--- a/tests/test_yuanbao_integration.py
+++ b/tests/gateway/test_yuanbao_integration.py
@@ -15,7 +15,7 @@ import sys
import os
# 确保 hermes-agent 根目录在 sys.path 中
-_REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+_REPO_ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
if _REPO_ROOT not in sys.path:
sys.path.insert(0, _REPO_ROOT)
diff --git a/tests/test_yuanbao_markdown.py b/tests/gateway/test_yuanbao_markdown.py
similarity index 96%
rename from tests/test_yuanbao_markdown.py
rename to tests/gateway/test_yuanbao_markdown.py
index 1d038e960a..42ce50028e 100644
--- a/tests/test_yuanbao_markdown.py
+++ b/tests/gateway/test_yuanbao_markdown.py
@@ -3,10 +3,10 @@ test_yuanbao_markdown.py - Unit tests for yuanbao_markdown.py
Run (no pytest needed):
cd /root/.openclaw/workspace/hermes-agent
- python3 tests/test_yuanbao_markdown.py -v
+ python3 tests/gateway/test_yuanbao_markdown.py -v
Or with pytest if available:
- python3 -m pytest tests/test_yuanbao_markdown.py -v
+ python3 -m pytest tests/gateway/test_yuanbao_markdown.py -v
"""
import sys
@@ -14,7 +14,7 @@ import os
import unittest
# Ensure project root is on the path
-sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
+sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..'))
from gateway.platforms import helpers as _mdchunk
from gateway.platforms.yuanbao import MarkdownProcessor
diff --git a/tests/test_yuanbao_pipeline.py b/tests/gateway/test_yuanbao_pipeline.py
similarity index 99%
rename from tests/test_yuanbao_pipeline.py
rename to tests/gateway/test_yuanbao_pipeline.py
index f15a5074fa..a8ee914e34 100644
--- a/tests/test_yuanbao_pipeline.py
+++ b/tests/gateway/test_yuanbao_pipeline.py
@@ -16,7 +16,7 @@ import os
import json
# Ensure project root is on the path
-_REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+_REPO_ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
if _REPO_ROOT not in sys.path:
sys.path.insert(0, _REPO_ROOT)
diff --git a/tests/test_yuanbao_proto.py b/tests/gateway/test_yuanbao_proto.py
similarity index 99%
rename from tests/test_yuanbao_proto.py
rename to tests/gateway/test_yuanbao_proto.py
index dcf1f92f2c..faf6f678ae 100644
--- a/tests/test_yuanbao_proto.py
+++ b/tests/gateway/test_yuanbao_proto.py
@@ -15,7 +15,7 @@ import sys
import os
# 确保 hermes-agent 根目录在 sys.path 中
-_REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+_REPO_ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
if _REPO_ROOT not in sys.path:
sys.path.insert(0, _REPO_ROOT)
diff --git a/tests/test_yuanbao_reconnect_set_active.py b/tests/gateway/test_yuanbao_reconnect_set_active.py
similarity index 97%
rename from tests/test_yuanbao_reconnect_set_active.py
rename to tests/gateway/test_yuanbao_reconnect_set_active.py
index 9bb8a02040..b52bc66e47 100644
--- a/tests/test_yuanbao_reconnect_set_active.py
+++ b/tests/gateway/test_yuanbao_reconnect_set_active.py
@@ -12,7 +12,7 @@ import os
import asyncio
from unittest.mock import AsyncMock, MagicMock, patch
-_REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+_REPO_ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
if _REPO_ROOT not in sys.path:
sys.path.insert(0, _REPO_ROOT)
diff --git a/tests/test_yuanbao_shutdown.py b/tests/gateway/test_yuanbao_shutdown.py
similarity index 97%
rename from tests/test_yuanbao_shutdown.py
rename to tests/gateway/test_yuanbao_shutdown.py
index be535f46c7..0b65cf53de 100644
--- a/tests/test_yuanbao_shutdown.py
+++ b/tests/gateway/test_yuanbao_shutdown.py
@@ -17,7 +17,7 @@ import sys
import os
import asyncio
-_REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+_REPO_ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
if _REPO_ROOT not in sys.path:
sys.path.insert(0, _REPO_ROOT)
diff --git a/tests/hermes_cli/conftest.py b/tests/hermes_cli/conftest.py
index 60728cdc8d..1db3887b7e 100644
--- a/tests/hermes_cli/conftest.py
+++ b/tests/hermes_cli/conftest.py
@@ -85,3 +85,49 @@ def isolated_update_runtime(monkeypatch, tmp_path, request):
monkeypatch.setattr(update_cmd_fleet, "_restart_macos_launchd_gateways", lambda *a, **k: None)
monkeypatch.setattr(update_inventory, "collect_runtime_inventory", lambda: None)
monkeypatch.setattr(update_receipt, "collect_fleet_versions", lambda *a, **k: [])
+
+
+# ---- prompt_toolkit / capsys isolation ----
+# ``cli._cprint`` renders through ``prompt_toolkit.print_formatted_text``,
+# which — when called with no explicit ``output=`` — lazily creates an
+# ``Output`` from ``sys.stdout`` **and caches it on the process-global default
+# ``AppSession``** (``prompt_toolkit.application.current._current_app_session``,
+# a ``ContextVar`` with a module-level default). The cache is keyed to nothing
+# and never re-reads ``sys.stdout``.
+#
+# Under pytest, ``capsys`` swaps ``sys.stdout`` for a fresh buffer per test.
+# So the first CLI test that emits through ``_cprint`` (e.g. one exercising
+# ``/queue``, which prints a "Queued: …" line) locks prompt_toolkit's cached
+# output onto *its* captured stdout. Every later ``capsys`` test that asserts
+# on ``_cprint`` output then reads an empty buffer, because the render went to
+# the first test's now-dead capture target. That is the mechanism behind the
+# order-dependent ``test_resume_quiet_stderr`` failure: it passes in isolation
+# and in its own file, but fails in a full ``tests/cli`` run.
+#
+# Reset the cached output before every CLI test so each one re-creates a fresh
+# prompt_toolkit ``Output`` bound to its own ``sys.stdout`` on first use. This
+# is a no-op when prompt_toolkit isn't importable and cheap otherwise (the
+# property re-creates lazily).
+
+
+@pytest.fixture(autouse=True)
+def _reset_prompt_toolkit_output_cache():
+ """Clear prompt_toolkit's cached AppSession output around each CLI test.
+
+ See the module docstring for the capsys/prompt_toolkit interaction this
+ guards against.
+ """
+
+ def _clear() -> None:
+ try:
+ from prompt_toolkit.application.current import get_app_session
+
+ get_app_session()._output = None
+ except Exception:
+ # prompt_toolkit not importable / internal shape changed — the
+ # tests that rely on this simply keep their prior behavior.
+ pass
+
+ _clear()
+ yield
+ _clear()
diff --git a/tests/test_active_session_exclusivity.py b/tests/hermes_cli/test_active_session_exclusivity.py
similarity index 100%
rename from tests/test_active_session_exclusivity.py
rename to tests/hermes_cli/test_active_session_exclusivity.py
diff --git a/tests/test_audio_playback_guard.py b/tests/hermes_cli/test_audio_playback_guard.py
similarity index 98%
rename from tests/test_audio_playback_guard.py
rename to tests/hermes_cli/test_audio_playback_guard.py
index c20c4cbe9e..4250be844a 100644
--- a/tests/test_audio_playback_guard.py
+++ b/tests/hermes_cli/test_audio_playback_guard.py
@@ -2,7 +2,7 @@
The incident: a plain test run spoke the words "partial answer complete" out
of a developer's speakers. That string is a fake ``final_response`` from
-``tests/test_tui_gateway_server.py``. The route was entirely in-process — no
+``tests/tui_gateway/test_tui_gateway_server.py``. The route was entirely in-process — no
leaked shell variable, so ``scripts/run_tests.sh``'s ``env -i`` was no help:
1. A test drives the ``voice.toggle`` RPC with ``action="tts"``. The
diff --git a/tests/hermes_cli/test_auth_toctou_file_modes.py b/tests/hermes_cli/test_auth_toctou_file_modes.py
index a9d92c2666..084f3dea63 100644
--- a/tests/hermes_cli/test_auth_toctou_file_modes.py
+++ b/tests/hermes_cli/test_auth_toctou_file_modes.py
@@ -8,7 +8,7 @@ create and chmod the file existed at the process umask (typically ``0o644``),
briefly exposing OAuth tokens to other local users on multi-user hosts. The
writers now go through ``utils.atomic_json_write(mode=0o600)`` whose mkstemp temp
file is ``O_EXCL`` at 0600 on creation (the cross-writer invariant lives in
-``tests/test_private_credential_writers.py``).
+``tests/agent/test_private_credential_writers.py``).
These tests stay green only while the token file and its parent directory
end up at ``0o600`` / ``0o700`` after every write. POSIX-only — the mode-bit
diff --git a/tests/cli/test_bang_shell_mode.py b/tests/hermes_cli/test_bang_shell_mode.py
similarity index 100%
rename from tests/cli/test_bang_shell_mode.py
rename to tests/hermes_cli/test_bang_shell_mode.py
diff --git a/tests/cli/test_bracketed_paste_timeout.py b/tests/hermes_cli/test_bracketed_paste_timeout.py
similarity index 100%
rename from tests/cli/test_bracketed_paste_timeout.py
rename to tests/hermes_cli/test_bracketed_paste_timeout.py
diff --git a/tests/cli/test_branch_command.py b/tests/hermes_cli/test_branch_command.py
similarity index 100%
rename from tests/cli/test_branch_command.py
rename to tests/hermes_cli/test_branch_command.py
diff --git a/tests/cli/test_busy_input_mode_command.py b/tests/hermes_cli/test_busy_input_mode_command.py
similarity index 100%
rename from tests/cli/test_busy_input_mode_command.py
rename to tests/hermes_cli/test_busy_input_mode_command.py
diff --git a/tests/cli/test_chat_q_exit_clear.py b/tests/hermes_cli/test_chat_q_exit_clear.py
similarity index 100%
rename from tests/cli/test_chat_q_exit_clear.py
rename to tests/hermes_cli/test_chat_q_exit_clear.py
diff --git a/tests/cli/test_cli_active_agent_ref_wiring.py b/tests/hermes_cli/test_cli_active_agent_ref_wiring.py
similarity index 100%
rename from tests/cli/test_cli_active_agent_ref_wiring.py
rename to tests/hermes_cli/test_cli_active_agent_ref_wiring.py
diff --git a/tests/cli/test_cli_approval_ui.py b/tests/hermes_cli/test_cli_approval_ui.py
similarity index 100%
rename from tests/cli/test_cli_approval_ui.py
rename to tests/hermes_cli/test_cli_approval_ui.py
diff --git a/tests/cli/test_cli_async_delegation_delivery.py b/tests/hermes_cli/test_cli_async_delegation_delivery.py
similarity index 100%
rename from tests/cli/test_cli_async_delegation_delivery.py
rename to tests/hermes_cli/test_cli_async_delegation_delivery.py
diff --git a/tests/cli/test_cli_background_busy_path.py b/tests/hermes_cli/test_cli_background_busy_path.py
similarity index 98%
rename from tests/cli/test_cli_background_busy_path.py
rename to tests/hermes_cli/test_cli_background_busy_path.py
index 9af07b0da1..e22ce84f13 100644
--- a/tests/cli/test_cli_background_busy_path.py
+++ b/tests/hermes_cli/test_cli_background_busy_path.py
@@ -15,7 +15,7 @@ the UI thread; the command's own ``CommandDef`` already declares
never consulted.
These tests exercise the detector without starting a prompt_toolkit app,
-mirroring tests/cli/test_cli_steer_busy_path.py.
+mirroring tests/hermes_cli/test_cli_steer_busy_path.py.
"""
from __future__ import annotations
diff --git a/tests/cli/test_cli_background_status_indicator.py b/tests/hermes_cli/test_cli_background_status_indicator.py
similarity index 100%
rename from tests/cli/test_cli_background_status_indicator.py
rename to tests/hermes_cli/test_cli_background_status_indicator.py
diff --git a/tests/cli/test_cli_background_tui_refresh.py b/tests/hermes_cli/test_cli_background_tui_refresh.py
similarity index 100%
rename from tests/cli/test_cli_background_tui_refresh.py
rename to tests/hermes_cli/test_cli_background_tui_refresh.py
diff --git a/tests/cli/test_cli_bracketed_paste_sanitizer.py b/tests/hermes_cli/test_cli_bracketed_paste_sanitizer.py
similarity index 100%
rename from tests/cli/test_cli_bracketed_paste_sanitizer.py
rename to tests/hermes_cli/test_cli_bracketed_paste_sanitizer.py
diff --git a/tests/cli/test_cli_browser_connect.py b/tests/hermes_cli/test_cli_browser_connect.py
similarity index 100%
rename from tests/cli/test_cli_browser_connect.py
rename to tests/hermes_cli/test_cli_browser_connect.py
diff --git a/tests/cli/test_cli_clarify_batch.py b/tests/hermes_cli/test_cli_clarify_batch.py
similarity index 99%
rename from tests/cli/test_cli_clarify_batch.py
rename to tests/hermes_cli/test_cli_clarify_batch.py
index a19575956c..809dbe42bd 100644
--- a/tests/cli/test_cli_clarify_batch.py
+++ b/tests/hermes_cli/test_cli_clarify_batch.py
@@ -5,7 +5,7 @@ thread (the way the agent thread calls it) and simulates the keybinding
handlers by calling the same helper methods they call
(``_clarify_batch_set_active`` for Tab, ``_clarify_batch_enter`` for
Enter, ``_clarify_batch_lock`` for the freetext submit path). No real
-terminal needed — mirrors tests/cli/test_cli_approval_ui.py.
+terminal needed — mirrors tests/hermes_cli/test_cli_approval_ui.py.
"""
import json
diff --git a/tests/cli/test_cli_cmd_backspace.py b/tests/hermes_cli/test_cli_cmd_backspace.py
similarity index 100%
rename from tests/cli/test_cli_cmd_backspace.py
rename to tests/hermes_cli/test_cli_cmd_backspace.py
diff --git a/tests/cli/test_cli_codex_context_reference.py b/tests/hermes_cli/test_cli_codex_context_reference.py
similarity index 100%
rename from tests/cli/test_cli_codex_context_reference.py
rename to tests/hermes_cli/test_cli_codex_context_reference.py
diff --git a/tests/cli/test_cli_context_warning.py b/tests/hermes_cli/test_cli_context_warning.py
similarity index 100%
rename from tests/cli/test_cli_context_warning.py
rename to tests/hermes_cli/test_cli_context_warning.py
diff --git a/tests/cli/test_cli_copy_command.py b/tests/hermes_cli/test_cli_copy_command.py
similarity index 100%
rename from tests/cli/test_cli_copy_command.py
rename to tests/hermes_cli/test_cli_copy_command.py
diff --git a/tests/cli/test_cli_delegate_background_notice.py b/tests/hermes_cli/test_cli_delegate_background_notice.py
similarity index 100%
rename from tests/cli/test_cli_delegate_background_notice.py
rename to tests/hermes_cli/test_cli_delegate_background_notice.py
diff --git a/tests/cli/test_cli_extension_hooks.py b/tests/hermes_cli/test_cli_extension_hooks.py
similarity index 100%
rename from tests/cli/test_cli_extension_hooks.py
rename to tests/hermes_cli/test_cli_extension_hooks.py
diff --git a/tests/cli/test_cli_external_editor.py b/tests/hermes_cli/test_cli_external_editor.py
similarity index 100%
rename from tests/cli/test_cli_external_editor.py
rename to tests/hermes_cli/test_cli_external_editor.py
diff --git a/tests/cli/test_cli_file_drop.py b/tests/hermes_cli/test_cli_file_drop.py
similarity index 100%
rename from tests/cli/test_cli_file_drop.py
rename to tests/hermes_cli/test_cli_file_drop.py
diff --git a/tests/cli/test_cli_first_run_setup.py b/tests/hermes_cli/test_cli_first_run_setup.py
similarity index 100%
rename from tests/cli/test_cli_first_run_setup.py
rename to tests/hermes_cli/test_cli_first_run_setup.py
diff --git a/tests/cli/test_cli_force_redraw.py b/tests/hermes_cli/test_cli_force_redraw.py
similarity index 100%
rename from tests/cli/test_cli_force_redraw.py
rename to tests/hermes_cli/test_cli_force_redraw.py
diff --git a/tests/cli/test_cli_goal_interrupt.py b/tests/hermes_cli/test_cli_goal_interrupt.py
similarity index 100%
rename from tests/cli/test_cli_goal_interrupt.py
rename to tests/hermes_cli/test_cli_goal_interrupt.py
diff --git a/tests/cli/test_cli_goal_kick_prompt.py b/tests/hermes_cli/test_cli_goal_kick_prompt.py
similarity index 100%
rename from tests/cli/test_cli_goal_kick_prompt.py
rename to tests/hermes_cli/test_cli_goal_kick_prompt.py
diff --git a/tests/cli/test_cli_goal_parked_resume.py b/tests/hermes_cli/test_cli_goal_parked_resume.py
similarity index 100%
rename from tests/cli/test_cli_goal_parked_resume.py
rename to tests/hermes_cli/test_cli_goal_parked_resume.py
diff --git a/tests/cli/test_cli_image_command.py b/tests/hermes_cli/test_cli_image_command.py
similarity index 100%
rename from tests/cli/test_cli_image_command.py
rename to tests/hermes_cli/test_cli_image_command.py
diff --git a/tests/cli/test_cli_init.py b/tests/hermes_cli/test_cli_init.py
similarity index 99%
rename from tests/cli/test_cli_init.py
rename to tests/hermes_cli/test_cli_init.py
index 059508991f..1438cd08ee 100644
--- a/tests/cli/test_cli_init.py
+++ b/tests/hermes_cli/test_cli_init.py
@@ -256,7 +256,7 @@ class TestPromptToolkitTerminalCompatibility:
The native-Windows arm (``_terminal_may_leak_cpr() is False``, plus
the ``PROMPT_TOOLKIT_NO_CPR`` override that outranks it) lives in
- ``tests/cli/test_cpr_local_leak.py`` under ``windows_only``, where it
+ ``tests/hermes_cli/test_cpr_local_leak.py`` under ``windows_only``, where it
runs against a real Windows console.
"""
from cli import _terminal_may_leak_cpr
diff --git a/tests/cli/test_cli_insights_command.py b/tests/hermes_cli/test_cli_insights_command.py
similarity index 100%
rename from tests/cli/test_cli_insights_command.py
rename to tests/hermes_cli/test_cli_insights_command.py
diff --git a/tests/cli/test_cli_interrupt_ack_race.py b/tests/hermes_cli/test_cli_interrupt_ack_race.py
similarity index 100%
rename from tests/cli/test_cli_interrupt_ack_race.py
rename to tests/hermes_cli/test_cli_interrupt_ack_race.py
diff --git a/tests/cli/test_cli_interrupt_drain_regression.py b/tests/hermes_cli/test_cli_interrupt_drain_regression.py
similarity index 100%
rename from tests/cli/test_cli_interrupt_drain_regression.py
rename to tests/hermes_cli/test_cli_interrupt_drain_regression.py
diff --git a/tests/cli/test_cli_interrupt_subagent.py b/tests/hermes_cli/test_cli_interrupt_subagent.py
similarity index 100%
rename from tests/cli/test_cli_interrupt_subagent.py
rename to tests/hermes_cli/test_cli_interrupt_subagent.py
diff --git a/tests/cli/test_cli_light_mode.py b/tests/hermes_cli/test_cli_light_mode.py
similarity index 100%
rename from tests/cli/test_cli_light_mode.py
rename to tests/hermes_cli/test_cli_light_mode.py
diff --git a/tests/cli/test_cli_loading_indicator.py b/tests/hermes_cli/test_cli_loading_indicator.py
similarity index 100%
rename from tests/cli/test_cli_loading_indicator.py
rename to tests/hermes_cli/test_cli_loading_indicator.py
diff --git a/tests/test_cli_manual_compress.py b/tests/hermes_cli/test_cli_manual_compress.py
similarity index 100%
rename from tests/test_cli_manual_compress.py
rename to tests/hermes_cli/test_cli_manual_compress.py
diff --git a/tests/cli/test_cli_markdown_rendering.py b/tests/hermes_cli/test_cli_markdown_rendering.py
similarity index 100%
rename from tests/cli/test_cli_markdown_rendering.py
rename to tests/hermes_cli/test_cli_markdown_rendering.py
diff --git a/tests/cli/test_cli_mcp_config_watch.py b/tests/hermes_cli/test_cli_mcp_config_watch.py
similarity index 100%
rename from tests/cli/test_cli_mcp_config_watch.py
rename to tests/hermes_cli/test_cli_mcp_config_watch.py
diff --git a/tests/cli/test_cli_new_session.py b/tests/hermes_cli/test_cli_new_session.py
similarity index 99%
rename from tests/cli/test_cli_new_session.py
rename to tests/hermes_cli/test_cli_new_session.py
index f35e218b6f..cbad4d58ac 100644
--- a/tests/cli/test_cli_new_session.py
+++ b/tests/hermes_cli/test_cli_new_session.py
@@ -135,7 +135,7 @@ def _prepare_cli_with_active_session(tmp_path):
# Bypass the destructive-slash confirmation gate — these tests focus on
# the new-session mechanics, not the confirm prompt itself (covered in
- # tests/cli/test_destructive_slash_confirm.py).
+ # tests/hermes_cli/test_destructive_slash_confirm.py).
cli._confirm_destructive_slash = lambda *_a, **_kw: "once"
return cli
diff --git a/tests/hermes_cli/test_cli_partial_update_hint_96900.py b/tests/hermes_cli/test_cli_partial_update_hint.py
similarity index 100%
rename from tests/hermes_cli/test_cli_partial_update_hint_96900.py
rename to tests/hermes_cli/test_cli_partial_update_hint.py
diff --git a/tests/cli/test_cli_pet_pane.py b/tests/hermes_cli/test_cli_pet_pane.py
similarity index 100%
rename from tests/cli/test_cli_pet_pane.py
rename to tests/hermes_cli/test_cli_pet_pane.py
diff --git a/tests/cli/test_cli_prefix_matching.py b/tests/hermes_cli/test_cli_prefix_matching.py
similarity index 100%
rename from tests/cli/test_cli_prefix_matching.py
rename to tests/hermes_cli/test_cli_prefix_matching.py
diff --git a/tests/cli/test_cli_preloaded_skills.py b/tests/hermes_cli/test_cli_preloaded_skills.py
similarity index 100%
rename from tests/cli/test_cli_preloaded_skills.py
rename to tests/hermes_cli/test_cli_preloaded_skills.py
diff --git a/tests/cli/test_cli_provider_resolution.py b/tests/hermes_cli/test_cli_provider_resolution.py
similarity index 100%
rename from tests/cli/test_cli_provider_resolution.py
rename to tests/hermes_cli/test_cli_provider_resolution.py
diff --git a/tests/cli/test_cli_queue_paste.py b/tests/hermes_cli/test_cli_queue_paste.py
similarity index 100%
rename from tests/cli/test_cli_queue_paste.py
rename to tests/hermes_cli/test_cli_queue_paste.py
diff --git a/tests/test_cli_quiet_stdout_leak.py b/tests/hermes_cli/test_cli_quiet_stdout_leak.py
similarity index 100%
rename from tests/test_cli_quiet_stdout_leak.py
rename to tests/hermes_cli/test_cli_quiet_stdout_leak.py
diff --git a/tests/cli/test_cli_reload_skills.py b/tests/hermes_cli/test_cli_reload_skills.py
similarity index 100%
rename from tests/cli/test_cli_reload_skills.py
rename to tests/hermes_cli/test_cli_reload_skills.py
diff --git a/tests/cli/test_cli_resume_command.py b/tests/hermes_cli/test_cli_resume_command.py
similarity index 100%
rename from tests/cli/test_cli_resume_command.py
rename to tests/hermes_cli/test_cli_resume_command.py
diff --git a/tests/cli/test_cli_retry.py b/tests/hermes_cli/test_cli_retry.py
similarity index 100%
rename from tests/cli/test_cli_retry.py
rename to tests/hermes_cli/test_cli_retry.py
diff --git a/tests/cli/test_cli_save_config_value.py b/tests/hermes_cli/test_cli_save_config_value.py
similarity index 100%
rename from tests/cli/test_cli_save_config_value.py
rename to tests/hermes_cli/test_cli_save_config_value.py
diff --git a/tests/cli/test_cli_secret_capture.py b/tests/hermes_cli/test_cli_secret_capture.py
similarity index 100%
rename from tests/cli/test_cli_secret_capture.py
rename to tests/hermes_cli/test_cli_secret_capture.py
diff --git a/tests/test_cli_session_store_shared_registry.py b/tests/hermes_cli/test_cli_session_store_shared_registry.py
similarity index 100%
rename from tests/test_cli_session_store_shared_registry.py
rename to tests/hermes_cli/test_cli_session_store_shared_registry.py
diff --git a/tests/cli/test_cli_shift_enter_newline.py b/tests/hermes_cli/test_cli_shift_enter_newline.py
similarity index 100%
rename from tests/cli/test_cli_shift_enter_newline.py
rename to tests/hermes_cli/test_cli_shift_enter_newline.py
diff --git a/tests/cli/test_cli_shutdown_memory_messages.py b/tests/hermes_cli/test_cli_shutdown_memory_messages.py
similarity index 100%
rename from tests/cli/test_cli_shutdown_memory_messages.py
rename to tests/hermes_cli/test_cli_shutdown_memory_messages.py
diff --git a/tests/test_cli_skin_integration.py b/tests/hermes_cli/test_cli_skin_integration.py
similarity index 86%
rename from tests/test_cli_skin_integration.py
rename to tests/hermes_cli/test_cli_skin_integration.py
index 02d7b11dc0..c094bcacfa 100644
--- a/tests/test_cli_skin_integration.py
+++ b/tests/hermes_cli/test_cli_skin_integration.py
@@ -45,6 +45,20 @@ class TestCliSkinPromptIntegration:
assert cli._get_tui_prompt_fragments() == [("class:sudo-prompt", "🔑 ⚔ ")]
+ def test_narrow_terminals_compact_voice_recording_prompt_fragments(self):
+ cli = _make_cli_stub()
+ cli._voice_recording = True
+ cli._voice_recorder = SimpleNamespace(current_rms=3000)
+
+ with patch.object(HermesCLI, "_get_tui_terminal_width", return_value=50):
+ frags = cli._get_tui_prompt_fragments()
+
+ assert frags[0][0] == "class:voice-recording"
+ assert frags[0][1].startswith("●")
+ assert "❯" not in frags[0][1]
+
+
+
def test_build_tui_style_dict_uses_skin_overrides(self):
cli = _make_cli_stub()
@@ -111,3 +125,6 @@ class TestAnsiRichTextHelper:
text = _rich_text_from_ansi("[notatag] literal")
assert text.plain == "[notatag] literal"
+ def test_strips_ansi_but_keeps_plain_text(self):
+ text = _rich_text_from_ansi("\x1b[31mred\x1b[0m")
+ assert text.plain == "red"
diff --git a/tests/cli/test_cli_status_bar.py b/tests/hermes_cli/test_cli_status_bar.py
similarity index 100%
rename from tests/cli/test_cli_status_bar.py
rename to tests/hermes_cli/test_cli_status_bar.py
diff --git a/tests/cli/test_cli_status_bar_goal.py b/tests/hermes_cli/test_cli_status_bar_goal.py
similarity index 100%
rename from tests/cli/test_cli_status_bar_goal.py
rename to tests/hermes_cli/test_cli_status_bar_goal.py
diff --git a/tests/cli/test_cli_status_command.py b/tests/hermes_cli/test_cli_status_command.py
similarity index 100%
rename from tests/cli/test_cli_status_command.py
rename to tests/hermes_cli/test_cli_status_command.py
diff --git a/tests/cli/test_cli_steer_busy_path.py b/tests/hermes_cli/test_cli_steer_busy_path.py
similarity index 100%
rename from tests/cli/test_cli_steer_busy_path.py
rename to tests/hermes_cli/test_cli_steer_busy_path.py
diff --git a/tests/cli/test_cli_terminal_response_sanitizer.py b/tests/hermes_cli/test_cli_terminal_response_sanitizer.py
similarity index 100%
rename from tests/cli/test_cli_terminal_response_sanitizer.py
rename to tests/hermes_cli/test_cli_terminal_response_sanitizer.py
diff --git a/tests/cli/test_cli_terminal_shortcuts.py b/tests/hermes_cli/test_cli_terminal_shortcuts.py
similarity index 100%
rename from tests/cli/test_cli_terminal_shortcuts.py
rename to tests/hermes_cli/test_cli_terminal_shortcuts.py
diff --git a/tests/cli/test_cli_tools_command.py b/tests/hermes_cli/test_cli_tools_command.py
similarity index 100%
rename from tests/cli/test_cli_tools_command.py
rename to tests/hermes_cli/test_cli_tools_command.py
diff --git a/tests/cli/test_cli_user_message_preview.py b/tests/hermes_cli/test_cli_user_message_preview.py
similarity index 100%
rename from tests/cli/test_cli_user_message_preview.py
rename to tests/hermes_cli/test_cli_user_message_preview.py
diff --git a/tests/cli/test_cli_yolo_resume_persistence.py b/tests/hermes_cli/test_cli_yolo_resume_persistence.py
similarity index 100%
rename from tests/cli/test_cli_yolo_resume_persistence.py
rename to tests/hermes_cli/test_cli_yolo_resume_persistence.py
diff --git a/tests/cli/test_cli_yolo_toggle.py b/tests/hermes_cli/test_cli_yolo_toggle.py
similarity index 100%
rename from tests/cli/test_cli_yolo_toggle.py
rename to tests/hermes_cli/test_cli_yolo_toggle.py
diff --git a/tests/test_code_skew.py b/tests/hermes_cli/test_code_skew.py
similarity index 100%
rename from tests/test_code_skew.py
rename to tests/hermes_cli/test_code_skew.py
diff --git a/tests/cli/test_command_palette.py b/tests/hermes_cli/test_command_palette.py
similarity index 100%
rename from tests/cli/test_command_palette.py
rename to tests/hermes_cli/test_command_palette.py
diff --git a/tests/test_compat_manifest_targets.py b/tests/hermes_cli/test_compat_manifest_targets.py
similarity index 98%
rename from tests/test_compat_manifest_targets.py
rename to tests/hermes_cli/test_compat_manifest_targets.py
index 461636586e..41ca26f557 100644
--- a/tests/test_compat_manifest_targets.py
+++ b/tests/hermes_cli/test_compat_manifest_targets.py
@@ -16,7 +16,7 @@ from pathlib import Path
import pytest
-ROOT = Path(__file__).resolve().parent.parent
+ROOT = Path(__file__).resolve().parent.parent.parent
MANIFEST = ROOT / "compat_manifest.json"
# This file resolves every pointer on purpose; the once-per-name plugin warning is expected here.
diff --git a/tests/cli/test_completion_backlog.py b/tests/hermes_cli/test_completion_backlog.py
similarity index 100%
rename from tests/cli/test_completion_backlog.py
rename to tests/hermes_cli/test_completion_backlog.py
diff --git a/tests/cli/test_compress_flags.py b/tests/hermes_cli/test_compress_flags.py
similarity index 100%
rename from tests/cli/test_compress_flags.py
rename to tests/hermes_cli/test_compress_flags.py
diff --git a/tests/cli/test_compress_focus.py b/tests/hermes_cli/test_compress_focus.py
similarity index 100%
rename from tests/cli/test_compress_focus.py
rename to tests/hermes_cli/test_compress_focus.py
diff --git a/tests/cli/test_compress_here.py b/tests/hermes_cli/test_compress_here.py
similarity index 100%
rename from tests/cli/test_compress_here.py
rename to tests/hermes_cli/test_compress_here.py
diff --git a/tests/cli/test_compress_type_ahead.py b/tests/hermes_cli/test_compress_type_ahead.py
similarity index 100%
rename from tests/cli/test_compress_type_ahead.py
rename to tests/hermes_cli/test_compress_type_ahead.py
diff --git a/tests/cli/test_cpr_local_leak.py b/tests/hermes_cli/test_cpr_local_leak.py
similarity index 100%
rename from tests/cli/test_cpr_local_leak.py
rename to tests/hermes_cli/test_cpr_local_leak.py
diff --git a/tests/cli/test_cprint_bg_thread.py b/tests/hermes_cli/test_cprint_bg_thread.py
similarity index 100%
rename from tests/cli/test_cprint_bg_thread.py
rename to tests/hermes_cli/test_cprint_bg_thread.py
diff --git a/tests/cli/test_ctrl_enter_newline.py b/tests/hermes_cli/test_ctrl_enter_newline.py
similarity index 100%
rename from tests/cli/test_ctrl_enter_newline.py
rename to tests/hermes_cli/test_ctrl_enter_newline.py
diff --git a/tests/cli/test_cwd_env_respect.py b/tests/hermes_cli/test_cwd_env_respect.py
similarity index 100%
rename from tests/cli/test_cwd_env_respect.py
rename to tests/hermes_cli/test_cwd_env_respect.py
diff --git a/tests/cli/test_destructive_slash_confirm.py b/tests/hermes_cli/test_destructive_slash_confirm.py
similarity index 100%
rename from tests/cli/test_destructive_slash_confirm.py
rename to tests/hermes_cli/test_destructive_slash_confirm.py
diff --git a/tests/cli/test_destructive_slash_inline_skip_e2e.py b/tests/hermes_cli/test_destructive_slash_inline_skip_e2e.py
similarity index 100%
rename from tests/cli/test_destructive_slash_inline_skip_e2e.py
rename to tests/hermes_cli/test_destructive_slash_inline_skip_e2e.py
diff --git a/tests/test_empty_session_hygiene.py b/tests/hermes_cli/test_empty_session_hygiene.py
similarity index 100%
rename from tests/test_empty_session_hygiene.py
rename to tests/hermes_cli/test_empty_session_hygiene.py
diff --git a/tests/hermes_cli/test_ensure_hermes_home_uid_34107.py b/tests/hermes_cli/test_ensure_hermes_home_uid.py
similarity index 100%
rename from tests/hermes_cli/test_ensure_hermes_home_uid_34107.py
rename to tests/hermes_cli/test_ensure_hermes_home_uid.py
diff --git a/tests/test_env_loader_op_bootstrap.py b/tests/hermes_cli/test_env_loader_op_bootstrap.py
similarity index 99%
rename from tests/test_env_loader_op_bootstrap.py
rename to tests/hermes_cli/test_env_loader_op_bootstrap.py
index f58dd11334..a562cd5244 100644
--- a/tests/test_env_loader_op_bootstrap.py
+++ b/tests/hermes_cli/test_env_loader_op_bootstrap.py
@@ -29,7 +29,7 @@ from unittest import mock
import pytest
# Make the worktree importable without depending on the installed wheel.
-ROOT = Path(__file__).resolve().parents[1]
+ROOT = Path(__file__).resolve().parents[2]
if str(ROOT) not in sys.path:
sys.path.insert(0, str(ROOT))
diff --git a/tests/cli/test_exit_delete_session.py b/tests/hermes_cli/test_exit_delete_session.py
similarity index 100%
rename from tests/cli/test_exit_delete_session.py
rename to tests/hermes_cli/test_exit_delete_session.py
diff --git a/tests/cli/test_exit_summary_resume_hint.py b/tests/hermes_cli/test_exit_summary_resume_hint.py
similarity index 100%
rename from tests/cli/test_exit_summary_resume_hint.py
rename to tests/hermes_cli/test_exit_summary_resume_hint.py
diff --git a/tests/cli/test_exit_watchdog_signal_arm.py b/tests/hermes_cli/test_exit_watchdog_signal_arm.py
similarity index 100%
rename from tests/cli/test_exit_watchdog_signal_arm.py
rename to tests/hermes_cli/test_exit_watchdog_signal_arm.py
diff --git a/tests/cli/test_fast_command.py b/tests/hermes_cli/test_fast_command.py
similarity index 100%
rename from tests/cli/test_fast_command.py
rename to tests/hermes_cli/test_fast_command.py
diff --git a/tests/cli/test_finite_chat_delegation.py b/tests/hermes_cli/test_finite_chat_delegation.py
similarity index 100%
rename from tests/cli/test_finite_chat_delegation.py
rename to tests/hermes_cli/test_finite_chat_delegation.py
diff --git a/tests/cli/test_focus_view.py b/tests/hermes_cli/test_focus_view.py
similarity index 100%
rename from tests/cli/test_focus_view.py
rename to tests/hermes_cli/test_focus_view.py
diff --git a/tests/test_gitlock.py b/tests/hermes_cli/test_gitlock.py
similarity index 100%
rename from tests/test_gitlock.py
rename to tests/hermes_cli/test_gitlock.py
diff --git a/tests/hermes_cli/test_25106_global_switch_persists_base_url_api_mode.py b/tests/hermes_cli/test_global_switch_persists_base_url_api_mode.py
similarity index 100%
rename from tests/hermes_cli/test_25106_global_switch_persists_base_url_api_mode.py
rename to tests/hermes_cli/test_global_switch_persists_base_url_api_mode.py
diff --git a/tests/cli/test_handoff_cleanup_race.py b/tests/hermes_cli/test_handoff_cleanup_race.py
similarity index 100%
rename from tests/cli/test_handoff_cleanup_race.py
rename to tests/hermes_cli/test_handoff_cleanup_race.py
diff --git a/tests/cli/test_handoff_slow_dispatch_timeout.py b/tests/hermes_cli/test_handoff_slow_dispatch_timeout.py
similarity index 100%
rename from tests/cli/test_handoff_slow_dispatch_timeout.py
rename to tests/hermes_cli/test_handoff_slow_dispatch_timeout.py
diff --git a/tests/cli/test_indicator_command.py b/tests/hermes_cli/test_indicator_command.py
similarity index 100%
rename from tests/cli/test_indicator_command.py
rename to tests/hermes_cli/test_indicator_command.py
diff --git a/tests/cli/test_interrupt_output_history_regression.py b/tests/hermes_cli/test_interrupt_output_history_regression.py
similarity index 100%
rename from tests/cli/test_interrupt_output_history_regression.py
rename to tests/hermes_cli/test_interrupt_output_history_regression.py
diff --git a/tests/test_iron_proxy_cli.py b/tests/hermes_cli/test_iron_proxy_cli.py
similarity index 100%
rename from tests/test_iron_proxy_cli.py
rename to tests/hermes_cli/test_iron_proxy_cli.py
diff --git a/tests/cli/test_kitty_keypad_parity.py b/tests/hermes_cli/test_kitty_keypad_parity.py
similarity index 100%
rename from tests/cli/test_kitty_keypad_parity.py
rename to tests/hermes_cli/test_kitty_keypad_parity.py
diff --git a/tests/test_lazy_secrets_dispatch.py b/tests/hermes_cli/test_lazy_secrets_dispatch.py
similarity index 98%
rename from tests/test_lazy_secrets_dispatch.py
rename to tests/hermes_cli/test_lazy_secrets_dispatch.py
index e48a613588..32e925001d 100644
--- a/tests/test_lazy_secrets_dispatch.py
+++ b/tests/hermes_cli/test_lazy_secrets_dispatch.py
@@ -35,7 +35,7 @@ def _run_hermes(args: list[str], timeout: int = 30) -> subprocess.CompletedProce
still exercises its full parser/dispatch/fetch code and exits 1, which
the assertions already accept.
"""
- repo_root = Path(__file__).parent.parent
+ repo_root = Path(__file__).parent.parent.parent
env = dict(os.environ)
env.update(
{
@@ -216,7 +216,7 @@ if 'cryptography.hazmat.bindings._rust' in sys.modules:
print('PASS: main() update --check path never loaded cryptography._rust')
sys.exit(0)
"""
- repo_root = Path(__file__).parent.parent
+ repo_root = Path(__file__).parent.parent.parent
probe = repo_root / "_test_main_update_crypto_probe.py"
probe.write_text(script)
try:
diff --git a/tests/test_lazy_secrets_import.py b/tests/hermes_cli/test_lazy_secrets_import.py
similarity index 97%
rename from tests/test_lazy_secrets_import.py
rename to tests/hermes_cli/test_lazy_secrets_import.py
index 7261c10000..8e3f4b5d7e 100644
--- a/tests/test_lazy_secrets_import.py
+++ b/tests/hermes_cli/test_lazy_secrets_import.py
@@ -8,7 +8,7 @@ from pathlib import Path
def _run_isolated(code: str) -> subprocess.CompletedProcess[str]:
"""Run a Python snippet in the repo root (not tests/)."""
- repo_root = Path(__file__).parent.parent
+ repo_root = Path(__file__).parent.parent.parent
return subprocess.run(
[sys.executable, "-c", code],
capture_output=True,
@@ -78,7 +78,7 @@ sys.exit(0)
"""Running hermes update --check should NOT load cryptography._rust."""
# Write a small script in the repo root so hermes_cli is importable,
# and use a filename that doesn't trigger the live-system guard.
- repo_root = Path(__file__).parent.parent
+ repo_root = Path(__file__).parent.parent.parent
script = repo_root / "_test_lazy_secrets_check.py"
script.write_text(
"""
diff --git a/tests/test_list_recent_user_messages_handoffs.py b/tests/hermes_cli/test_list_recent_user_messages_handoffs.py
similarity index 100%
rename from tests/test_list_recent_user_messages_handoffs.py
rename to tests/hermes_cli/test_list_recent_user_messages_handoffs.py
diff --git a/tests/cli/test_manual_compress.py b/tests/hermes_cli/test_manual_compress.py
similarity index 100%
rename from tests/cli/test_manual_compress.py
rename to tests/hermes_cli/test_manual_compress.py
diff --git a/tests/test_minimax_model_validation.py b/tests/hermes_cli/test_minimax_model_validation.py
similarity index 100%
rename from tests/test_minimax_model_validation.py
rename to tests/hermes_cli/test_minimax_model_validation.py
diff --git a/tests/test_minimax_oauth.py b/tests/hermes_cli/test_minimax_oauth.py
similarity index 100%
rename from tests/test_minimax_oauth.py
rename to tests/hermes_cli/test_minimax_oauth.py
diff --git a/tests/cli/test_moa_command.py b/tests/hermes_cli/test_moa_command.py
similarity index 100%
rename from tests/cli/test_moa_command.py
rename to tests/hermes_cli/test_moa_command.py
diff --git a/tests/hermes_cli/test_model_alias_credentials_83612.py b/tests/hermes_cli/test_model_alias_credentials.py
similarity index 100%
rename from tests/hermes_cli/test_model_alias_credentials_83612.py
rename to tests/hermes_cli/test_model_alias_credentials.py
diff --git a/tests/cli/test_model_picker_filter.py b/tests/hermes_cli/test_model_picker_filter.py
similarity index 100%
rename from tests/cli/test_model_picker_filter.py
rename to tests/hermes_cli/test_model_picker_filter.py
diff --git a/tests/hermes_cli/test_model_prefix_routing_87189.py b/tests/hermes_cli/test_model_prefix_routing.py
similarity index 100%
rename from tests/hermes_cli/test_model_prefix_routing_87189.py
rename to tests/hermes_cli/test_model_prefix_routing.py
diff --git a/tests/cli/test_modify_other_keys_aliases.py b/tests/hermes_cli/test_modify_other_keys_aliases.py
similarity index 100%
rename from tests/cli/test_modify_other_keys_aliases.py
rename to tests/hermes_cli/test_modify_other_keys_aliases.py
diff --git a/tests/cli/test_oneshot_resumed_session_persist.py b/tests/hermes_cli/test_oneshot_resumed_session_persist.py
similarity index 100%
rename from tests/cli/test_oneshot_resumed_session_persist.py
rename to tests/hermes_cli/test_oneshot_resumed_session_persist.py
diff --git a/tests/cli/test_partial_compress.py b/tests/hermes_cli/test_partial_compress.py
similarity index 100%
rename from tests/cli/test_partial_compress.py
rename to tests/hermes_cli/test_partial_compress.py
diff --git a/tests/cli/test_personality_none.py b/tests/hermes_cli/test_personality_none.py
similarity index 100%
rename from tests/cli/test_personality_none.py
rename to tests/hermes_cli/test_personality_none.py
diff --git a/tests/test_plugin_compat_notice.py b/tests/hermes_cli/test_plugin_compat_notice.py
similarity index 98%
rename from tests/test_plugin_compat_notice.py
rename to tests/hermes_cli/test_plugin_compat_notice.py
index c8574a86c2..f739e51154 100644
--- a/tests/test_plugin_compat_notice.py
+++ b/tests/hermes_cli/test_plugin_compat_notice.py
@@ -1,6 +1,6 @@
"""hermes_cli.plugin_compat: detect plugins on old import paths, tell the user, disable after the date.
-Kept with the compat layer (tests/test_compat_manifest_targets.py); deleted with it.
+Kept with the compat layer (tests/hermes_cli/test_compat_manifest_targets.py); deleted with it.
"""
from __future__ import annotations
@@ -14,7 +14,7 @@ import pytest
from hermes_cli import plugin_compat as pc
-ROOT = Path(__file__).resolve().parent.parent
+ROOT = Path(__file__).resolve().parent.parent.parent
pytestmark = pytest.mark.skipif(not (ROOT / "compat_manifest.json").exists(), reason="compat layer removed")
MANIFEST = {"tools.web_tools": {"prefers_gateway": "tools.tool_backend_helpers.prefers_gateway"},
diff --git a/tests/cli/test_prefill_config.py b/tests/hermes_cli/test_prefill_config.py
similarity index 100%
rename from tests/cli/test_prefill_config.py
rename to tests/hermes_cli/test_prefill_config.py
diff --git a/tests/cli/test_prepend_note_to_message.py b/tests/hermes_cli/test_prepend_note_to_message.py
similarity index 100%
rename from tests/cli/test_prepend_note_to_message.py
rename to tests/hermes_cli/test_prepend_note_to_message.py
diff --git a/tests/cli/test_prompt_stash.py b/tests/hermes_cli/test_prompt_stash.py
similarity index 100%
rename from tests/cli/test_prompt_stash.py
rename to tests/hermes_cli/test_prompt_stash.py
diff --git a/tests/cli/test_prompt_stash_cli.py b/tests/hermes_cli/test_prompt_stash_cli.py
similarity index 98%
rename from tests/cli/test_prompt_stash_cli.py
rename to tests/hermes_cli/test_prompt_stash_cli.py
index b613fcbd66..55235129e9 100644
--- a/tests/cli/test_prompt_stash_cli.py
+++ b/tests/hermes_cli/test_prompt_stash_cli.py
@@ -1,6 +1,6 @@
"""Tests for the Ctrl+S prompt stash wiring inside HermesCLI.
-The state machine itself is covered by tests/cli/test_prompt_stash.py. These
+The state machine itself is covered by tests/hermes_cli/test_prompt_stash.py. These
tests verify the cli.py side:
- HermesCLI.__init__ creates a PromptStash
- the layout hook makes room for the stash browse panel
@@ -8,7 +8,7 @@ tests verify the cli.py side:
- the status-bar indicator appears / disappears with stash contents
Follows the prompt_toolkit-stub construction pattern from
-tests/cli/test_cli_extension_hooks.py.
+tests/hermes_cli/test_cli_extension_hooks.py.
"""
from __future__ import annotations
diff --git a/tests/cli/test_prompt_text_input_thread_safety.py b/tests/hermes_cli/test_prompt_text_input_thread_safety.py
similarity index 100%
rename from tests/cli/test_prompt_text_input_thread_safety.py
rename to tests/hermes_cli/test_prompt_text_input_thread_safety.py
diff --git a/tests/test_pty_keepalive_ws.py b/tests/hermes_cli/test_pty_keepalive_ws.py
similarity index 100%
rename from tests/test_pty_keepalive_ws.py
rename to tests/hermes_cli/test_pty_keepalive_ws.py
diff --git a/tests/test_pty_session.py b/tests/hermes_cli/test_pty_session.py
similarity index 100%
rename from tests/test_pty_session.py
rename to tests/hermes_cli/test_pty_session.py
diff --git a/tests/cli/test_quick_commands.py b/tests/hermes_cli/test_quick_commands.py
similarity index 100%
rename from tests/cli/test_quick_commands.py
rename to tests/hermes_cli/test_quick_commands.py
diff --git a/tests/cli/test_quiet_turn_author.py b/tests/hermes_cli/test_quiet_turn_author.py
similarity index 100%
rename from tests/cli/test_quiet_turn_author.py
rename to tests/hermes_cli/test_quiet_turn_author.py
diff --git a/tests/cli/test_reasoning_command.py b/tests/hermes_cli/test_reasoning_command.py
similarity index 100%
rename from tests/cli/test_reasoning_command.py
rename to tests/hermes_cli/test_reasoning_command.py
diff --git a/tests/hermes_cli/test_regression_16767.py b/tests/hermes_cli/test_regression.py
similarity index 98%
rename from tests/hermes_cli/test_regression_16767.py
rename to tests/hermes_cli/test_regression.py
index bbbae4df58..5f0d8f4b40 100644
--- a/tests/hermes_cli/test_regression_16767.py
+++ b/tests/hermes_cli/test_regression.py
@@ -1,3 +1,5 @@
+"""Regression coverage for issue #16767."""
+
import sys
import hermes_cli.model_switch as ms
diff --git a/tests/test_resource_limits.py b/tests/hermes_cli/test_resource_limits.py
similarity index 100%
rename from tests/test_resource_limits.py
rename to tests/hermes_cli/test_resource_limits.py
diff --git a/tests/cli/test_resume_display.py b/tests/hermes_cli/test_resume_display.py
similarity index 100%
rename from tests/cli/test_resume_display.py
rename to tests/hermes_cli/test_resume_display.py
diff --git a/tests/cli/test_resume_model_restore.py b/tests/hermes_cli/test_resume_model_restore.py
similarity index 100%
rename from tests/cli/test_resume_model_restore.py
rename to tests/hermes_cli/test_resume_model_restore.py
diff --git a/tests/cli/test_resume_quiet_stderr.py b/tests/hermes_cli/test_resume_quiet_stderr.py
similarity index 100%
rename from tests/cli/test_resume_quiet_stderr.py
rename to tests/hermes_cli/test_resume_quiet_stderr.py
diff --git a/tests/cli/test_save_conversation_location.py b/tests/hermes_cli/test_save_conversation_location.py
similarity index 100%
rename from tests/cli/test_save_conversation_location.py
rename to tests/hermes_cli/test_save_conversation_location.py
diff --git a/tests/cli/test_session_boundary_hooks.py b/tests/hermes_cli/test_session_boundary_hooks.py
similarity index 100%
rename from tests/cli/test_session_boundary_hooks.py
rename to tests/hermes_cli/test_session_boundary_hooks.py
diff --git a/tests/test_session_detail_malformed_db.py b/tests/hermes_cli/test_session_detail_malformed_db.py
similarity index 100%
rename from tests/test_session_detail_malformed_db.py
rename to tests/hermes_cli/test_session_detail_malformed_db.py
diff --git a/tests/test_session_export_batch.py b/tests/hermes_cli/test_session_export_batch.py
similarity index 100%
rename from tests/test_session_export_batch.py
rename to tests/hermes_cli/test_session_export_batch.py
diff --git a/tests/test_session_search_context_batch.py b/tests/hermes_cli/test_session_search_context_batch.py
similarity index 100%
rename from tests/test_session_search_context_batch.py
rename to tests/hermes_cli/test_session_search_context_batch.py
diff --git a/tests/test_session_skill_previews.py b/tests/hermes_cli/test_session_skill_previews.py
similarity index 100%
rename from tests/test_session_skill_previews.py
rename to tests/hermes_cli/test_session_skill_previews.py
diff --git a/tests/test_session_system_prompt_dedup.py b/tests/hermes_cli/test_session_system_prompt_dedup.py
similarity index 100%
rename from tests/test_session_system_prompt_dedup.py
rename to tests/hermes_cli/test_session_system_prompt_dedup.py
diff --git a/tests/test_session_vacuum_config.py b/tests/hermes_cli/test_session_vacuum_config.py
similarity index 98%
rename from tests/test_session_vacuum_config.py
rename to tests/hermes_cli/test_session_vacuum_config.py
index 43adfdacba..08f9e175bd 100644
--- a/tests/test_session_vacuum_config.py
+++ b/tests/hermes_cli/test_session_vacuum_config.py
@@ -74,7 +74,7 @@ def test_shipped_template_does_not_pin_sessions_keys():
so code defaults (and future flips) apply."""
import yaml
- template = Path(__file__).resolve().parents[1] / "cli-config.yaml.example"
+ template = Path(__file__).resolve().parents[2] / "cli-config.yaml.example"
data = yaml.safe_load(template.read_text(encoding="utf-8")) or {}
assert "sessions" not in data
diff --git a/tests/cli/test_show_config_credential.py b/tests/hermes_cli/test_show_config_credential.py
similarity index 100%
rename from tests/cli/test_show_config_credential.py
rename to tests/hermes_cli/test_show_config_credential.py
diff --git a/tests/cli/test_single_query_clarify.py b/tests/hermes_cli/test_single_query_clarify.py
similarity index 100%
rename from tests/cli/test_single_query_clarify.py
rename to tests/hermes_cli/test_single_query_clarify.py
diff --git a/tests/cli/test_single_query_session_finalize.py b/tests/hermes_cli/test_single_query_session_finalize.py
similarity index 100%
rename from tests/cli/test_single_query_session_finalize.py
rename to tests/hermes_cli/test_single_query_session_finalize.py
diff --git a/tests/cli/test_slash_command_interrupt.py b/tests/hermes_cli/test_slash_command_interrupt.py
similarity index 100%
rename from tests/cli/test_slash_command_interrupt.py
rename to tests/hermes_cli/test_slash_command_interrupt.py
diff --git a/tests/cli/test_slash_confirm_windows.py b/tests/hermes_cli/test_slash_confirm_windows.py
similarity index 100%
rename from tests/cli/test_slash_confirm_windows.py
rename to tests/hermes_cli/test_slash_confirm_windows.py
diff --git a/tests/cli/test_slash_dispatch_table.py b/tests/hermes_cli/test_slash_dispatch_table.py
similarity index 100%
rename from tests/cli/test_slash_dispatch_table.py
rename to tests/hermes_cli/test_slash_dispatch_table.py
diff --git a/tests/cli/test_slash_undo_title_robustness.py b/tests/hermes_cli/test_slash_undo_title_robustness.py
similarity index 100%
rename from tests/cli/test_slash_undo_title_robustness.py
rename to tests/hermes_cli/test_slash_undo_title_robustness.py
diff --git a/tests/hermes_cli/test_startup_model_routing_87189.py b/tests/hermes_cli/test_startup_model_routing.py
similarity index 100%
rename from tests/hermes_cli/test_startup_model_routing_87189.py
rename to tests/hermes_cli/test_startup_model_routing.py
diff --git a/tests/cli/test_status_bar_git_branch.py b/tests/hermes_cli/test_status_bar_git_branch.py
similarity index 100%
rename from tests/cli/test_status_bar_git_branch.py
rename to tests/hermes_cli/test_status_bar_git_branch.py
diff --git a/tests/cli/test_status_lines_held_during_stream_box.py b/tests/hermes_cli/test_status_lines_held_during_stream_box.py
similarity index 100%
rename from tests/cli/test_status_lines_held_during_stream_box.py
rename to tests/hermes_cli/test_status_lines_held_during_stream_box.py
diff --git a/tests/cli/test_steer_inline_repaint_34569.py b/tests/hermes_cli/test_steer_inline_repaint.py
similarity index 100%
rename from tests/cli/test_steer_inline_repaint_34569.py
rename to tests/hermes_cli/test_steer_inline_repaint.py
diff --git a/tests/cli/test_stream_delta_think_tag.py b/tests/hermes_cli/test_stream_delta_think_tag.py
similarity index 100%
rename from tests/cli/test_stream_delta_think_tag.py
rename to tests/hermes_cli/test_stream_delta_think_tag.py
diff --git a/tests/cli/test_stream_flush_left.py b/tests/hermes_cli/test_stream_flush_left.py
similarity index 100%
rename from tests/cli/test_stream_flush_left.py
rename to tests/hermes_cli/test_stream_flush_left.py
diff --git a/tests/cli/test_stream_partial_line_flush.py b/tests/hermes_cli/test_stream_partial_line_flush.py
similarity index 100%
rename from tests/cli/test_stream_partial_line_flush.py
rename to tests/hermes_cli/test_stream_partial_line_flush.py
diff --git a/tests/cli/test_subagent_dock_collapse.py b/tests/hermes_cli/test_subagent_dock_collapse.py
similarity index 100%
rename from tests/cli/test_subagent_dock_collapse.py
rename to tests/hermes_cli/test_subagent_dock_collapse.py
diff --git a/tests/cli/test_subagent_dock_surface.py b/tests/hermes_cli/test_subagent_dock_surface.py
similarity index 100%
rename from tests/cli/test_subagent_dock_surface.py
rename to tests/hermes_cli/test_subagent_dock_surface.py
diff --git a/tests/cli/test_subagent_monitor.py b/tests/hermes_cli/test_subagent_monitor.py
similarity index 100%
rename from tests/cli/test_subagent_monitor.py
rename to tests/hermes_cli/test_subagent_monitor.py
diff --git a/tests/cli/test_subagent_monitor_prompts.py b/tests/hermes_cli/test_subagent_monitor_prompts.py
similarity index 100%
rename from tests/cli/test_subagent_monitor_prompts.py
rename to tests/hermes_cli/test_subagent_monitor_prompts.py
diff --git a/tests/cli/test_subagent_monitor_resize.py b/tests/hermes_cli/test_subagent_monitor_resize.py
similarity index 100%
rename from tests/cli/test_subagent_monitor_resize.py
rename to tests/hermes_cli/test_subagent_monitor_resize.py
diff --git a/tests/cli/test_subagent_monitor_ui.py b/tests/hermes_cli/test_subagent_monitor_ui.py
similarity index 100%
rename from tests/cli/test_subagent_monitor_ui.py
rename to tests/hermes_cli/test_subagent_monitor_ui.py
diff --git a/tests/cli/test_subagent_notification_display.py b/tests/hermes_cli/test_subagent_notification_display.py
similarity index 100%
rename from tests/cli/test_subagent_notification_display.py
rename to tests/hermes_cli/test_subagent_notification_display.py
diff --git a/tests/cli/test_subagent_shortcut.py b/tests/hermes_cli/test_subagent_shortcut.py
similarity index 100%
rename from tests/cli/test_subagent_shortcut.py
rename to tests/hermes_cli/test_subagent_shortcut.py
diff --git a/tests/cli/test_terminal_interrupt_recovery.py b/tests/hermes_cli/test_terminal_interrupt_recovery.py
similarity index 100%
rename from tests/cli/test_terminal_interrupt_recovery.py
rename to tests/hermes_cli/test_terminal_interrupt_recovery.py
diff --git a/tests/hermes_cli/test_terminal_io_broken_81521.py b/tests/hermes_cli/test_terminal_io_broken.py
similarity index 100%
rename from tests/hermes_cli/test_terminal_io_broken_81521.py
rename to tests/hermes_cli/test_terminal_io_broken.py
diff --git a/tests/cli/test_termios_drift_heal.py b/tests/hermes_cli/test_termios_drift_heal.py
similarity index 100%
rename from tests/cli/test_termios_drift_heal.py
rename to tests/hermes_cli/test_termios_drift_heal.py
diff --git a/tests/cli/test_tool_gen_start_dedupe.py b/tests/hermes_cli/test_tool_gen_start_dedupe.py
similarity index 100%
rename from tests/cli/test_tool_gen_start_dedupe.py
rename to tests/hermes_cli/test_tool_gen_start_dedupe.py
diff --git a/tests/cli/test_tool_progress_scrollback.py b/tests/hermes_cli/test_tool_progress_scrollback.py
similarity index 100%
rename from tests/cli/test_tool_progress_scrollback.py
rename to tests/hermes_cli/test_tool_progress_scrollback.py
diff --git a/tests/cli/test_transformed_stream_output.py b/tests/hermes_cli/test_transformed_stream_output.py
similarity index 100%
rename from tests/cli/test_transformed_stream_output.py
rename to tests/hermes_cli/test_transformed_stream_output.py
diff --git a/tests/cli/test_tui_rapid_enter_paste.py b/tests/hermes_cli/test_tui_rapid_enter_paste.py
similarity index 100%
rename from tests/cli/test_tui_rapid_enter_paste.py
rename to tests/hermes_cli/test_tui_rapid_enter_paste.py
diff --git a/tests/cli/test_tui_terminal_reset_on_exit.py b/tests/hermes_cli/test_tui_terminal_reset_on_exit.py
similarity index 100%
rename from tests/cli/test_tui_terminal_reset_on_exit.py
rename to tests/hermes_cli/test_tui_terminal_reset_on_exit.py
diff --git a/tests/cli/test_update_command.py b/tests/hermes_cli/test_update_command.py
similarity index 100%
rename from tests/cli/test_update_command.py
rename to tests/hermes_cli/test_update_command.py
diff --git a/tests/cli/test_version_command.py b/tests/hermes_cli/test_version_command.py
similarity index 100%
rename from tests/cli/test_version_command.py
rename to tests/hermes_cli/test_version_command.py
diff --git a/tests/cli/test_vim_mode_command.py b/tests/hermes_cli/test_vim_mode_command.py
similarity index 100%
rename from tests/cli/test_vim_mode_command.py
rename to tests/hermes_cli/test_vim_mode_command.py
diff --git a/tests/test_web_server_sessiondb_eventloop.py b/tests/hermes_cli/test_web_server_sessiondb_eventloop.py
similarity index 100%
rename from tests/test_web_server_sessiondb_eventloop.py
rename to tests/hermes_cli/test_web_server_sessiondb_eventloop.py
diff --git a/tests/test_web_server_status_topology_cache.py b/tests/hermes_cli/test_web_server_status_topology_cache.py
similarity index 100%
rename from tests/test_web_server_status_topology_cache.py
rename to tests/hermes_cli/test_web_server_status_topology_cache.py
diff --git a/tests/test_web_server.py b/tests/hermes_cli/test_web_server_ws_ping.py
similarity index 100%
rename from tests/test_web_server.py
rename to tests/hermes_cli/test_web_server_ws_ping.py
diff --git a/tests/hermes_cli/test_windows_gateway_job_teardown_48820.py b/tests/hermes_cli/test_windows_gateway_job_teardown.py
similarity index 100%
rename from tests/hermes_cli/test_windows_gateway_job_teardown_48820.py
rename to tests/hermes_cli/test_windows_gateway_job_teardown.py
diff --git a/tests/cli/test_worktree.py b/tests/hermes_cli/test_worktree.py
similarity index 100%
rename from tests/cli/test_worktree.py
rename to tests/hermes_cli/test_worktree.py
diff --git a/tests/cli/test_worktree_pushed_tier.py b/tests/hermes_cli/test_worktree_pushed_tier.py
similarity index 100%
rename from tests/cli/test_worktree_pushed_tier.py
rename to tests/hermes_cli/test_worktree_pushed_tier.py
diff --git a/tests/cli/test_worktree_security.py b/tests/hermes_cli/test_worktree_security.py
similarity index 100%
rename from tests/cli/test_worktree_security.py
rename to tests/hermes_cli/test_worktree_security.py
diff --git a/tests/cli/test_worktree_selfheal.py b/tests/hermes_cli/test_worktree_selfheal.py
similarity index 100%
rename from tests/cli/test_worktree_selfheal.py
rename to tests/hermes_cli/test_worktree_selfheal.py
diff --git a/tests/cli/test_worktree_sync_base.py b/tests/hermes_cli/test_worktree_sync_base.py
similarity index 100%
rename from tests/cli/test_worktree_sync_base.py
rename to tests/hermes_cli/test_worktree_sync_base.py
diff --git a/tests/state/test_append_message_after_close_94736.py b/tests/hermes_state/test_append_message_after_close.py
similarity index 100%
rename from tests/state/test_append_message_after_close_94736.py
rename to tests/hermes_state/test_append_message_after_close.py
diff --git a/tests/hermes_state/test_88197_automatic_ended_stamp.py b/tests/hermes_state/test_automatic_ended_stamp.py
similarity index 100%
rename from tests/hermes_state/test_88197_automatic_ended_stamp.py
rename to tests/hermes_state/test_automatic_ended_stamp.py
diff --git a/tests/test_background_review_session_isolation.py b/tests/hermes_state/test_background_review_session_isolation.py
similarity index 100%
rename from tests/test_background_review_session_isolation.py
rename to tests/hermes_state/test_background_review_session_isolation.py
diff --git a/tests/state/test_compression_lease_refresh_before_publish.py b/tests/hermes_state/test_compression_lease_refresh_before_publish.py
similarity index 100%
rename from tests/state/test_compression_lease_refresh_before_publish.py
rename to tests/hermes_state/test_compression_lease_refresh_before_publish.py
diff --git a/tests/state/test_compression_lineage_guard.py b/tests/hermes_state/test_compression_lineage_guard.py
similarity index 100%
rename from tests/state/test_compression_lineage_guard.py
rename to tests/hermes_state/test_compression_lineage_guard.py
diff --git a/tests/test_compression_watermark_commit.py b/tests/hermes_state/test_compression_watermark_commit.py
similarity index 100%
rename from tests/test_compression_watermark_commit.py
rename to tests/hermes_state/test_compression_watermark_commit.py
diff --git a/tests/test_conftest_wal_gate.py b/tests/hermes_state/test_conftest_wal_gate.py
similarity index 100%
rename from tests/test_conftest_wal_gate.py
rename to tests/hermes_state/test_conftest_wal_gate.py
diff --git a/tests/state/test_dedupe_migration_contention.py b/tests/hermes_state/test_dedupe_migration_contention.py
similarity index 100%
rename from tests/state/test_dedupe_migration_contention.py
rename to tests/hermes_state/test_dedupe_migration_contention.py
diff --git a/tests/test_delegate_cascade_49148.py b/tests/hermes_state/test_delegate_cascade.py
similarity index 100%
rename from tests/test_delegate_cascade_49148.py
rename to tests/hermes_state/test_delegate_cascade.py
diff --git a/tests/state/test_disk_full_error.py b/tests/hermes_state/test_disk_full_error.py
similarity index 100%
rename from tests/state/test_disk_full_error.py
rename to tests/hermes_state/test_disk_full_error.py
diff --git a/tests/hermes_state/test_empty_sweep_archived_transcript_95868.py b/tests/hermes_state/test_empty_sweep_archived_transcript.py
similarity index 100%
rename from tests/hermes_state/test_empty_sweep_archived_transcript_95868.py
rename to tests/hermes_state/test_empty_sweep_archived_transcript.py
diff --git a/tests/test_fts_cjk_bigram.py b/tests/hermes_state/test_fts_cjk_bigram.py
similarity index 99%
rename from tests/test_fts_cjk_bigram.py
rename to tests/hermes_state/test_fts_cjk_bigram.py
index 19c20e6460..8bcfbc8524 100644
--- a/tests/test_fts_cjk_bigram.py
+++ b/tests/hermes_state/test_fts_cjk_bigram.py
@@ -14,7 +14,7 @@ import pytest
from hermes_state import SessionDB
from hermes_state_common import FTS_CJK_STALE_KEY
-REPO = Path(__file__).resolve().parent.parent
+REPO = Path(__file__).resolve().parent.parent.parent
SRC = REPO / "native" / "fts5_cjk" / "fts5_cjk.c"
VENDOR = REPO / "native" / "fts5_cjk" / "vendor"
diff --git a/tests/state/test_fts_fresh_bootstrap_admission.py b/tests/hermes_state/test_fts_fresh_bootstrap_admission.py
similarity index 100%
rename from tests/state/test_fts_fresh_bootstrap_admission.py
rename to tests/hermes_state/test_fts_fresh_bootstrap_admission.py
diff --git a/tests/state/test_fts_holder_instance_scope.py b/tests/hermes_state/test_fts_holder_instance_scope.py
similarity index 100%
rename from tests/state/test_fts_holder_instance_scope.py
rename to tests/hermes_state/test_fts_holder_instance_scope.py
diff --git a/tests/state/test_fts_index_fail_open.py b/tests/hermes_state/test_fts_index_fail_open.py
similarity index 100%
rename from tests/state/test_fts_index_fail_open.py
rename to tests/hermes_state/test_fts_index_fail_open.py
diff --git a/tests/state/test_fts_orphan_shadow_repair.py b/tests/hermes_state/test_fts_orphan_shadow_repair.py
similarity index 100%
rename from tests/state/test_fts_orphan_shadow_repair.py
rename to tests/hermes_state/test_fts_orphan_shadow_repair.py
diff --git a/tests/state/test_fts_rebuild_admission.py b/tests/hermes_state/test_fts_rebuild_admission.py
similarity index 100%
rename from tests/state/test_fts_rebuild_admission.py
rename to tests/hermes_state/test_fts_rebuild_admission.py
diff --git a/tests/state/test_fts_runtime_rebuild.py b/tests/hermes_state/test_fts_runtime_rebuild.py
similarity index 100%
rename from tests/state/test_fts_runtime_rebuild.py
rename to tests/hermes_state/test_fts_runtime_rebuild.py
diff --git a/tests/test_fts_tool_write_bounds.py b/tests/hermes_state/test_fts_tool_write_bounds.py
similarity index 100%
rename from tests/test_fts_tool_write_bounds.py
rename to tests/hermes_state/test_fts_tool_write_bounds.py
diff --git a/tests/state/test_fts_trigram_cron_exclusion.py b/tests/hermes_state/test_fts_trigram_cron_exclusion.py
similarity index 100%
rename from tests/state/test_fts_trigram_cron_exclusion.py
rename to tests/hermes_state/test_fts_trigram_cron_exclusion.py
diff --git a/tests/state/test_fts_trigram_subagent_exclusion.py b/tests/hermes_state/test_fts_trigram_subagent_exclusion.py
similarity index 100%
rename from tests/state/test_fts_trigram_subagent_exclusion.py
rename to tests/hermes_state/test_fts_trigram_subagent_exclusion.py
diff --git a/tests/test_fts_update_of_narrowing.py b/tests/hermes_state/test_fts_update_of_narrowing.py
similarity index 100%
rename from tests/test_fts_update_of_narrowing.py
rename to tests/hermes_state/test_fts_update_of_narrowing.py
diff --git a/tests/test_guest_durability_barriers.py b/tests/hermes_state/test_guest_durability_barriers.py
similarity index 100%
rename from tests/test_guest_durability_barriers.py
rename to tests/hermes_state/test_guest_durability_barriers.py
diff --git a/tests/test_hermes_state.py b/tests/hermes_state/test_hermes_state.py
similarity index 100%
rename from tests/test_hermes_state.py
rename to tests/hermes_state/test_hermes_state.py
diff --git a/tests/test_hermes_state_compression_busy_retry.py b/tests/hermes_state/test_hermes_state_compression_busy_retry.py
similarity index 100%
rename from tests/test_hermes_state_compression_busy_retry.py
rename to tests/hermes_state/test_hermes_state_compression_busy_retry.py
diff --git a/tests/test_hermes_state_compression_locks.py b/tests/hermes_state/test_hermes_state_compression_locks.py
similarity index 100%
rename from tests/test_hermes_state_compression_locks.py
rename to tests/hermes_state/test_hermes_state_compression_locks.py
diff --git a/tests/test_hermes_state_conn_lock_audit.py b/tests/hermes_state/test_hermes_state_conn_lock_audit.py
similarity index 98%
rename from tests/test_hermes_state_conn_lock_audit.py
rename to tests/hermes_state/test_hermes_state_conn_lock_audit.py
index f8669924fd..1b05f788b7 100644
--- a/tests/test_hermes_state_conn_lock_audit.py
+++ b/tests/hermes_state/test_hermes_state_conn_lock_audit.py
@@ -34,7 +34,7 @@ _ALLOWED_UNLOCKED_FNS = frozenset({
def _repo_root() -> Path:
- return Path(__file__).resolve().parents[1]
+ return Path(__file__).resolve().parents[2]
def _nearest_enclosing_fn(tree: ast.AST) -> dict:
diff --git a/tests/test_hermes_state_ids.py b/tests/hermes_state/test_hermes_state_ids.py
similarity index 100%
rename from tests/test_hermes_state_ids.py
rename to tests/hermes_state/test_hermes_state_ids.py
diff --git a/tests/test_hermes_state_readonly_preflight.py b/tests/hermes_state/test_hermes_state_readonly_preflight.py
similarity index 100%
rename from tests/test_hermes_state_readonly_preflight.py
rename to tests/hermes_state/test_hermes_state_readonly_preflight.py
diff --git a/tests/test_hermes_state_wal_fallback.py b/tests/hermes_state/test_hermes_state_wal_fallback.py
similarity index 100%
rename from tests/test_hermes_state_wal_fallback.py
rename to tests/hermes_state/test_hermes_state_wal_fallback.py
diff --git a/tests/test_journal_mode_config.py b/tests/hermes_state/test_journal_mode_config.py
similarity index 100%
rename from tests/test_journal_mode_config.py
rename to tests/hermes_state/test_journal_mode_config.py
diff --git a/tests/test_journal_mode_upgrade_warning.py b/tests/hermes_state/test_journal_mode_upgrade_warning.py
similarity index 100%
rename from tests/test_journal_mode_upgrade_warning.py
rename to tests/hermes_state/test_journal_mode_upgrade_warning.py
diff --git a/tests/state/test_no_locked_readers_gate.py b/tests/hermes_state/test_no_locked_readers_gate.py
similarity index 100%
rename from tests/state/test_no_locked_readers_gate.py
rename to tests/hermes_state/test_no_locked_readers_gate.py
diff --git a/tests/state/test_no_more_rows_retry.py b/tests/hermes_state/test_no_more_rows_retry.py
similarity index 100%
rename from tests/state/test_no_more_rows_retry.py
rename to tests/hermes_state/test_no_more_rows_retry.py
diff --git a/tests/hermes_state/test_94895_orphan_sweep_cross_backend.py b/tests/hermes_state/test_orphan_sweep_cross_backend.py
similarity index 100%
rename from tests/hermes_state/test_94895_orphan_sweep_cross_backend.py
rename to tests/hermes_state/test_orphan_sweep_cross_backend.py
diff --git a/tests/test_98924_readonly_fts_decode_error.py b/tests/hermes_state/test_readonly_fts_decode_error.py
similarity index 100%
rename from tests/test_98924_readonly_fts_decode_error.py
rename to tests/hermes_state/test_readonly_fts_decode_error.py
diff --git a/tests/test_schema_read_probe.py b/tests/hermes_state/test_schema_read_probe.py
similarity index 100%
rename from tests/test_schema_read_probe.py
rename to tests/hermes_state/test_schema_read_probe.py
diff --git a/tests/test_search_slow_query_log.py b/tests/hermes_state/test_search_slow_query_log.py
similarity index 100%
rename from tests/test_search_slow_query_log.py
rename to tests/hermes_state/test_search_slow_query_log.py
diff --git a/tests/test_session_db_context_manager.py b/tests/hermes_state/test_session_db_context_manager.py
similarity index 100%
rename from tests/test_session_db_context_manager.py
rename to tests/hermes_state/test_session_db_context_manager.py
diff --git a/tests/test_session_db_profile_stamp.py b/tests/hermes_state/test_session_db_profile_stamp.py
similarity index 100%
rename from tests/test_session_db_profile_stamp.py
rename to tests/hermes_state/test_session_db_profile_stamp.py
diff --git a/tests/test_session_db_read_conn_pool.py b/tests/hermes_state/test_session_db_read_conn_pool.py
similarity index 100%
rename from tests/test_session_db_read_conn_pool.py
rename to tests/hermes_state/test_session_db_read_conn_pool.py
diff --git a/tests/test_session_db_read_path_split.py b/tests/hermes_state/test_session_db_read_path_split.py
similarity index 100%
rename from tests/test_session_db_read_path_split.py
rename to tests/hermes_state/test_session_db_read_path_split.py
diff --git a/tests/test_session_delete_profile_isolation.py b/tests/hermes_state/test_session_delete_profile_isolation.py
similarity index 100%
rename from tests/test_session_delete_profile_isolation.py
rename to tests/hermes_state/test_session_delete_profile_isolation.py
diff --git a/tests/state/test_session_git_metadata_generation.py b/tests/hermes_state/test_session_git_metadata_generation.py
similarity index 100%
rename from tests/state/test_session_git_metadata_generation.py
rename to tests/hermes_state/test_session_git_metadata_generation.py
diff --git a/tests/state/test_session_model_usage_pk_heal.py b/tests/hermes_state/test_session_model_usage_pk_heal.py
similarity index 100%
rename from tests/state/test_session_model_usage_pk_heal.py
rename to tests/hermes_state/test_session_model_usage_pk_heal.py
diff --git a/tests/state/test_session_turn_lease.py b/tests/hermes_state/test_session_turn_lease.py
similarity index 100%
rename from tests/state/test_session_turn_lease.py
rename to tests/hermes_state/test_session_turn_lease.py
diff --git a/tests/test_session_workspace_binding.py b/tests/hermes_state/test_session_workspace_binding.py
similarity index 100%
rename from tests/test_session_workspace_binding.py
rename to tests/hermes_state/test_session_workspace_binding.py
diff --git a/tests/test_sqlite_lock_safe_inspection.py b/tests/hermes_state/test_sqlite_lock_safe_inspection.py
similarity index 100%
rename from tests/test_sqlite_lock_safe_inspection.py
rename to tests/hermes_state/test_sqlite_lock_safe_inspection.py
diff --git a/tests/test_sqlite_wal_reset_gate.py b/tests/hermes_state/test_sqlite_wal_reset_gate.py
similarity index 100%
rename from tests/test_sqlite_wal_reset_gate.py
rename to tests/hermes_state/test_sqlite_wal_reset_gate.py
diff --git a/tests/test_stale_tool_call_marker_session_repair.py b/tests/hermes_state/test_stale_tool_call_marker_session_repair.py
similarity index 100%
rename from tests/test_stale_tool_call_marker_session_repair.py
rename to tests/hermes_state/test_stale_tool_call_marker_session_repair.py
diff --git a/tests/test_state_db_fts_segment_collision_probe.py b/tests/hermes_state/test_state_db_fts_segment_collision_probe.py
similarity index 100%
rename from tests/test_state_db_fts_segment_collision_probe.py
rename to tests/hermes_state/test_state_db_fts_segment_collision_probe.py
diff --git a/tests/state/test_state_db_holders.py b/tests/hermes_state/test_state_db_holders.py
similarity index 100%
rename from tests/state/test_state_db_holders.py
rename to tests/hermes_state/test_state_db_holders.py
diff --git a/tests/state/test_state_db_lock_fail_closed.py b/tests/hermes_state/test_state_db_lock_fail_closed.py
similarity index 100%
rename from tests/state/test_state_db_lock_fail_closed.py
rename to tests/hermes_state/test_state_db_lock_fail_closed.py
diff --git a/tests/test_state_db_malformed_repair.py b/tests/hermes_state/test_state_db_malformed_repair.py
similarity index 100%
rename from tests/test_state_db_malformed_repair.py
rename to tests/hermes_state/test_state_db_malformed_repair.py
diff --git a/tests/test_state_db_notadb_fail_closed.py b/tests/hermes_state/test_state_db_notadb_fail_closed.py
similarity index 100%
rename from tests/test_state_db_notadb_fail_closed.py
rename to tests/hermes_state/test_state_db_notadb_fail_closed.py
diff --git a/tests/test_state_db_repair_live_writer_guard.py b/tests/hermes_state/test_state_db_repair_live_writer_guard.py
similarity index 100%
rename from tests/test_state_db_repair_live_writer_guard.py
rename to tests/hermes_state/test_state_db_repair_live_writer_guard.py
diff --git a/tests/test_state_db_repair_loop_cap.py b/tests/hermes_state/test_state_db_repair_loop_cap.py
similarity index 100%
rename from tests/test_state_db_repair_loop_cap.py
rename to tests/hermes_state/test_state_db_repair_loop_cap.py
diff --git a/tests/test_state_db_repair_loop_mtime.py b/tests/hermes_state/test_state_db_repair_loop_mtime.py
similarity index 100%
rename from tests/test_state_db_repair_loop_mtime.py
rename to tests/hermes_state/test_state_db_repair_loop_mtime.py
diff --git a/tests/test_state_db_repair_non_destructive.py b/tests/hermes_state/test_state_db_repair_non_destructive.py
similarity index 100%
rename from tests/test_state_db_repair_non_destructive.py
rename to tests/hermes_state/test_state_db_repair_non_destructive.py
diff --git a/tests/test_state_db_stats.py b/tests/hermes_state/test_state_db_stats.py
similarity index 100%
rename from tests/test_state_db_stats.py
rename to tests/hermes_state/test_state_db_stats.py
diff --git a/tests/state/test_state_db_wal_unlink_race.py b/tests/hermes_state/test_state_db_wal_unlink_race.py
similarity index 100%
rename from tests/state/test_state_db_wal_unlink_race.py
rename to tests/hermes_state/test_state_db_wal_unlink_race.py
diff --git a/tests/test_state_db_write_durability.py b/tests/hermes_state/test_state_db_write_durability.py
similarity index 100%
rename from tests/test_state_db_write_durability.py
rename to tests/hermes_state/test_state_db_write_durability.py
diff --git a/tests/test_state_synchronous_pragma.py b/tests/hermes_state/test_state_synchronous_pragma.py
similarity index 98%
rename from tests/test_state_synchronous_pragma.py
rename to tests/hermes_state/test_state_synchronous_pragma.py
index ce34c8d025..e6ec50ecf6 100644
--- a/tests/test_state_synchronous_pragma.py
+++ b/tests/hermes_state/test_state_synchronous_pragma.py
@@ -222,7 +222,7 @@ def test_example_config_documents_the_key():
"""An unpinnable durability level is the bug; docs are half the fix."""
from pathlib import Path
- text = Path(__file__).resolve().parents[1] / "cli-config.yaml.example"
+ text = Path(__file__).resolve().parents[2] / "cli-config.yaml.example"
body = text.read_text(encoding="utf-8")
assert "synchronous: FULL" in body
@@ -239,7 +239,7 @@ def test_example_config_calls_the_darwin_rule_a_floor_not_a_pin():
from pathlib import Path
body = (
- Path(__file__).resolve().parents[1] / "cli-config.yaml.example"
+ Path(__file__).resolve().parents[2] / "cli-config.yaml.example"
).read_text(encoding="utf-8")
assert "floor, not a pin" in body
assert "EXTRA is honored" in body
diff --git a/tests/hermes_state/test_86366_tail_rewind_semantics.py b/tests/hermes_state/test_tail_rewind_semantics.py
similarity index 100%
rename from tests/hermes_state/test_86366_tail_rewind_semantics.py
rename to tests/hermes_state/test_tail_rewind_semantics.py
diff --git a/tests/test_wal_checkpoint_strategy.py b/tests/hermes_state/test_wal_checkpoint_strategy.py
similarity index 100%
rename from tests/test_wal_checkpoint_strategy.py
rename to tests/hermes_state/test_wal_checkpoint_strategy.py
diff --git a/tests/hermes_state/test_86366_watermark_interplay.py b/tests/hermes_state/test_watermark_interplay.py
similarity index 100%
rename from tests/hermes_state/test_86366_watermark_interplay.py
rename to tests/hermes_state/test_watermark_interplay.py
diff --git a/tests/state/test_write_lock_patience.py b/tests/hermes_state/test_write_lock_patience.py
similarity index 100%
rename from tests/state/test_write_lock_patience.py
rename to tests/hermes_state/test_write_lock_patience.py
diff --git a/tests/state/test_writer_conn_thread_safety.py b/tests/hermes_state/test_writer_conn_thread_safety.py
similarity index 100%
rename from tests/state/test_writer_conn_thread_safety.py
rename to tests/hermes_state/test_writer_conn_thread_safety.py
diff --git a/tests/test_zeroed_state_db.py b/tests/hermes_state/test_zeroed_state_db.py
similarity index 100%
rename from tests/test_zeroed_state_db.py
rename to tests/hermes_state/test_zeroed_state_db.py
diff --git a/tests/perf_guards/test_pattern_b_scaling.py b/tests/perf_guards/test_pattern_b_scaling.py
index 7d3dead17e..fce2d94c5a 100644
--- a/tests/perf_guards/test_pattern_b_scaling.py
+++ b/tests/perf_guards/test_pattern_b_scaling.py
@@ -127,7 +127,7 @@ class TestListSessionsRichQueryBound:
"""Rows from ``list_sessions_rich`` plus the writer-conn statement count.
Uses the house trace-callback idiom (``statements.append`` — see
- tests/test_hermes_state.py) so failures can dump the captured SQL.
+ tests/hermes_state/test_hermes_state.py) so failures can dump the captured SQL.
"""
statements: list[str] = []
db._conn.set_trace_callback(statements.append)
diff --git a/tests/plugins/model_providers/test_deepseek_profile.py b/tests/plugins/model_providers/test_deepseek_profile.py
index 90788b00a0..d56d68cb8d 100644
--- a/tests/plugins/model_providers/test_deepseek_profile.py
+++ b/tests/plugins/model_providers/test_deepseek_profile.py
@@ -141,7 +141,7 @@ class TestDeepSeekModelGating:
class TestDeepSeekFullKwargsIntegration:
"""End-to-end: the transport's full kwargs match DeepSeek's live wire format.
- The live test harness in ``tests/run_agent/test_deepseek_v4_thinking_live.py``
+ The live test harness in ``tests/agent/test_deepseek_v4_thinking_live.py``
sends ``{"reasoning_effort": "high", "extra_body": {"thinking": {"type":
"enabled"}}}``. Confirm the transport produces that exact shape when wired
through the registered DeepSeek profile.
diff --git a/tests/test_honcho_client_concurrency.py b/tests/plugins/test_honcho_client_concurrency.py
similarity index 100%
rename from tests/test_honcho_client_concurrency.py
rename to tests/plugins/test_honcho_client_concurrency.py
diff --git a/tests/test_honcho_client_config.py b/tests/plugins/test_honcho_client_config.py
similarity index 100%
rename from tests/test_honcho_client_config.py
rename to tests/plugins/test_honcho_client_config.py
diff --git a/tests/test_honcho_session_context.py b/tests/plugins/test_honcho_session_context.py
similarity index 100%
rename from tests/test_honcho_session_context.py
rename to tests/plugins/test_honcho_session_context.py
diff --git a/tests/test_honcho_startup_fail_open.py b/tests/plugins/test_honcho_startup_fail_open.py
similarity index 100%
rename from tests/test_honcho_startup_fail_open.py
rename to tests/plugins/test_honcho_startup_fail_open.py
diff --git a/tests/test_plugin_storage.py b/tests/plugins/test_plugin_storage.py
similarity index 100%
rename from tests/test_plugin_storage.py
rename to tests/plugins/test_plugin_storage.py
diff --git a/tests/test_plugin_utils.py b/tests/plugins/test_plugin_utils.py
similarity index 100%
rename from tests/test_plugin_utils.py
rename to tests/plugins/test_plugin_utils.py
diff --git a/tests/test_plugins_manage_profile_scope.py b/tests/plugins/test_plugins_manage_profile_scope.py
similarity index 100%
rename from tests/test_plugins_manage_profile_scope.py
rename to tests/plugins/test_plugins_manage_profile_scope.py
diff --git a/tests/test_secret_scope_plugin_families.py b/tests/plugins/test_secret_scope_plugin_families.py
similarity index 100%
rename from tests/test_secret_scope_plugin_families.py
rename to tests/plugins/test_secret_scope_plugin_families.py
diff --git a/tests/test_telegram_polling_progress_ptb.py b/tests/plugins/test_telegram_polling_progress_ptb.py
similarity index 100%
rename from tests/test_telegram_polling_progress_ptb.py
rename to tests/plugins/test_telegram_polling_progress_ptb.py
diff --git a/tests/test_transform_api_error_classification_hook.py b/tests/plugins/test_transform_api_error_classification_hook.py
similarity index 100%
rename from tests/test_transform_api_error_classification_hook.py
rename to tests/plugins/test_transform_api_error_classification_hook.py
diff --git a/tests/test_transform_llm_output_hook.py b/tests/plugins/test_transform_llm_output_hook.py
similarity index 100%
rename from tests/test_transform_llm_output_hook.py
rename to tests/plugins/test_transform_llm_output_hook.py
diff --git a/tests/test_transform_tool_result_hook.py b/tests/plugins/test_transform_tool_result_hook.py
similarity index 100%
rename from tests/test_transform_tool_result_hook.py
rename to tests/plugins/test_transform_tool_result_hook.py
diff --git a/tests/run_agent/__init__.py b/tests/run_agent/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/tests/test_desktop_update_linux_gate.py b/tests/scripts/desktop_update/test_desktop_update_linux_gate.py
similarity index 95%
rename from tests/test_desktop_update_linux_gate.py
rename to tests/scripts/desktop_update/test_desktop_update_linux_gate.py
index f7aa76f9b6..d88b1fab1f 100644
--- a/tests/test_desktop_update_linux_gate.py
+++ b/tests/scripts/desktop_update/test_desktop_update_linux_gate.py
@@ -16,7 +16,7 @@ from pathlib import Path
import pytest
-POSIX_SH = Path(__file__).resolve().parent.parent / "scripts" / "desktop-update" / "posix.sh"
+POSIX_SH = Path(__file__).resolve().parent.parent.parent.parent / "scripts" / "desktop-update" / "posix.sh"
pytestmark = pytest.mark.linux_only
diff --git a/tests/test_desktop_update_shim_profile_cleanup.py b/tests/scripts/desktop_update/test_desktop_update_shim_profile_cleanup.py
similarity index 97%
rename from tests/test_desktop_update_shim_profile_cleanup.py
rename to tests/scripts/desktop_update/test_desktop_update_shim_profile_cleanup.py
index 980387d721..bc4d4a0449 100644
--- a/tests/test_desktop_update_shim_profile_cleanup.py
+++ b/tests/scripts/desktop_update/test_desktop_update_shim_profile_cleanup.py
@@ -39,7 +39,7 @@ def test_shim_removes_only_its_owned_profile(tmp_path, outcome):
"HERMES_UPDATE_SHIM_GRACE_SECONDS": "1", "HERMES_SELFTEST_FAIL": "1" if outcome == "error" else ""}
process = subprocess.Popen(
["bash", "-c", 'while [ ! -f "$HOME/start" ]; do sleep .05; done; exec bash "$@"', "probe",
- str(Path(__file__).resolve().parents[1] / "scripts/desktop-update/posix.sh"),
+ str(Path(__file__).resolve().parents[3] / "scripts/desktop-update/posix.sh"),
"--install-root", str(install), "--self-test-ui"],
env=env, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True,
)
diff --git a/tests/test_desktop_update_shim_progress.py b/tests/scripts/desktop_update/test_desktop_update_shim_progress.py
similarity index 99%
rename from tests/test_desktop_update_shim_progress.py
rename to tests/scripts/desktop_update/test_desktop_update_shim_progress.py
index 5af2ab9e96..53033683eb 100644
--- a/tests/test_desktop_update_shim_progress.py
+++ b/tests/scripts/desktop_update/test_desktop_update_shim_progress.py
@@ -18,7 +18,7 @@ from urllib.request import urlopen
import pytest
-REPO_ROOT = Path(__file__).resolve().parent.parent
+REPO_ROOT = Path(__file__).resolve().parent.parent.parent.parent
SHIM_DIR = REPO_ROOT / "scripts" / "desktop-update"
# posix.sh re-execs itself through these to detach from Electron's process
diff --git a/tests/test_desktop_update_tcc_heal.py b/tests/scripts/desktop_update/test_desktop_update_tcc_heal.py
similarity index 99%
rename from tests/test_desktop_update_tcc_heal.py
rename to tests/scripts/desktop_update/test_desktop_update_tcc_heal.py
index a8eae77c49..a9610ed4d1 100644
--- a/tests/test_desktop_update_tcc_heal.py
+++ b/tests/scripts/desktop_update/test_desktop_update_tcc_heal.py
@@ -28,7 +28,7 @@ from pathlib import Path
import pytest
-REPO_ROOT = Path(__file__).resolve().parent.parent
+REPO_ROOT = Path(__file__).resolve().parent.parent.parent.parent
POSIX_SH = REPO_ROOT / "scripts" / "desktop-update" / "posix.sh"
GOOD_STUB = "#!/bin/bash\n# bootable interpreter stub\nexit 0\n"
diff --git a/tests/test_desktop_update_windows_cwd.py b/tests/scripts/desktop_update/test_desktop_update_windows_cwd.py
similarity index 96%
rename from tests/test_desktop_update_windows_cwd.py
rename to tests/scripts/desktop_update/test_desktop_update_windows_cwd.py
index 7847da0497..c2ddbe8542 100644
--- a/tests/test_desktop_update_windows_cwd.py
+++ b/tests/scripts/desktop_update/test_desktop_update_windows_cwd.py
@@ -12,7 +12,7 @@ import pytest
pytestmark = pytest.mark.windows_only
-REPO_ROOT = Path(__file__).resolve().parent.parent
+REPO_ROOT = Path(__file__).resolve().parent.parent.parent.parent
WINDOWS_UPDATE_PS1 = REPO_ROOT / "scripts" / "desktop-update" / "windows.ps1"
diff --git a/tests/test_desktop_update_windows_pipe_drain.py b/tests/scripts/desktop_update/test_desktop_update_windows_pipe_drain.py
similarity index 99%
rename from tests/test_desktop_update_windows_pipe_drain.py
rename to tests/scripts/desktop_update/test_desktop_update_windows_pipe_drain.py
index d3cd1e8ee8..850f40bb46 100644
--- a/tests/test_desktop_update_windows_pipe_drain.py
+++ b/tests/scripts/desktop_update/test_desktop_update_windows_pipe_drain.py
@@ -52,7 +52,7 @@ from pathlib import Path
import pytest
-REPO_ROOT = Path(__file__).resolve().parent.parent
+REPO_ROOT = Path(__file__).resolve().parent.parent.parent.parent
WINDOWS_PS1 = REPO_ROOT / "scripts" / "desktop-update" / "windows.ps1"
diff --git a/tests/test_desktop_update_windows_progress.py b/tests/scripts/desktop_update/test_desktop_update_windows_progress.py
similarity index 98%
rename from tests/test_desktop_update_windows_progress.py
rename to tests/scripts/desktop_update/test_desktop_update_windows_progress.py
index 70708d754b..f9ee92abb5 100644
--- a/tests/test_desktop_update_windows_progress.py
+++ b/tests/scripts/desktop_update/test_desktop_update_windows_progress.py
@@ -22,7 +22,7 @@ import pytest
pytestmark = pytest.mark.windows_only
-REPO_ROOT = Path(__file__).resolve().parent.parent
+REPO_ROOT = Path(__file__).resolve().parent.parent.parent.parent
WINDOWS_UPDATE_PS1 = REPO_ROOT / "scripts" / "desktop-update" / "windows.ps1"
diff --git a/tests/test_desktop_update_windows_python_handoff.py b/tests/scripts/desktop_update/test_desktop_update_windows_python_handoff.py
similarity index 98%
rename from tests/test_desktop_update_windows_python_handoff.py
rename to tests/scripts/desktop_update/test_desktop_update_windows_python_handoff.py
index e63eb9dd28..679483a0c1 100644
--- a/tests/test_desktop_update_windows_python_handoff.py
+++ b/tests/scripts/desktop_update/test_desktop_update_windows_python_handoff.py
@@ -32,7 +32,7 @@ import re
from pathlib import Path
-REPO_ROOT = Path(__file__).resolve().parent.parent
+REPO_ROOT = Path(__file__).resolve().parent.parent.parent.parent
WINDOWS_PS1 = REPO_ROOT / "scripts" / "desktop-update" / "windows.ps1"
diff --git a/tests/test_desktop_update_windows_retry_policy.py b/tests/scripts/desktop_update/test_desktop_update_windows_retry_policy.py
similarity index 96%
rename from tests/test_desktop_update_windows_retry_policy.py
rename to tests/scripts/desktop_update/test_desktop_update_windows_retry_policy.py
index 8fafbca24c..c97105bea9 100644
--- a/tests/test_desktop_update_windows_retry_policy.py
+++ b/tests/scripts/desktop_update/test_desktop_update_windows_retry_policy.py
@@ -9,7 +9,7 @@ from pathlib import Path
import pytest
-REPO_ROOT = Path(__file__).resolve().parent.parent
+REPO_ROOT = Path(__file__).resolve().parent.parent.parent.parent
RETRY_POLICY = REPO_ROOT / "scripts" / "desktop-update" / "retry-policy.ps1"
diff --git a/tests/test_desktop_update_windows_timestamp.py b/tests/scripts/desktop_update/test_desktop_update_windows_timestamp.py
similarity index 95%
rename from tests/test_desktop_update_windows_timestamp.py
rename to tests/scripts/desktop_update/test_desktop_update_windows_timestamp.py
index 5efd0cc04b..e8bd8306d9 100644
--- a/tests/test_desktop_update_windows_timestamp.py
+++ b/tests/scripts/desktop_update/test_desktop_update_windows_timestamp.py
@@ -15,7 +15,7 @@ from __future__ import annotations
from pathlib import Path
-REPO_ROOT = Path(__file__).resolve().parent.parent
+REPO_ROOT = Path(__file__).resolve().parent.parent.parent.parent
WINDOWS_UPDATE_PS1 = REPO_ROOT / "scripts" / "desktop-update" / "windows.ps1"
diff --git a/tests/test_desktop_update_windows_ui_delivery.py b/tests/scripts/desktop_update/test_desktop_update_windows_ui_delivery.py
similarity index 98%
rename from tests/test_desktop_update_windows_ui_delivery.py
rename to tests/scripts/desktop_update/test_desktop_update_windows_ui_delivery.py
index dcd7b17c96..3558ee385d 100644
--- a/tests/test_desktop_update_windows_ui_delivery.py
+++ b/tests/scripts/desktop_update/test_desktop_update_windows_ui_delivery.py
@@ -16,7 +16,7 @@ from urllib.request import Request, urlopen
import pytest
pytestmark = pytest.mark.windows_only
-SCRIPT = Path(__file__).resolve().parents[1] / "scripts/desktop-update/windows.ps1"
+SCRIPT = Path(__file__).resolve().parents[3] / "scripts/desktop-update/windows.ps1"
@contextmanager
diff --git a/tests/test_install_autostash_conflict_recovery.py b/tests/scripts/install/test_install_autostash_conflict_recovery.py
similarity index 99%
rename from tests/test_install_autostash_conflict_recovery.py
rename to tests/scripts/install/test_install_autostash_conflict_recovery.py
index 94e19aeec5..210a082df4 100644
--- a/tests/test_install_autostash_conflict_recovery.py
+++ b/tests/scripts/install/test_install_autostash_conflict_recovery.py
@@ -15,7 +15,7 @@ from pathlib import Path
import pytest
-REPO_ROOT = Path(__file__).resolve().parent.parent
+REPO_ROOT = Path(__file__).resolve().parent.parent.parent.parent
INSTALL_SH = REPO_ROOT / "scripts" / "install.sh"
INSTALL_PS1 = REPO_ROOT / "scripts" / "install.ps1"
POWERSHELL = next(
diff --git a/tests/test_install_clone_throttle_fallback.py b/tests/scripts/install/test_install_clone_throttle_fallback.py
similarity index 98%
rename from tests/test_install_clone_throttle_fallback.py
rename to tests/scripts/install/test_install_clone_throttle_fallback.py
index cb47688932..973649e8ff 100644
--- a/tests/test_install_clone_throttle_fallback.py
+++ b/tests/scripts/install/test_install_clone_throttle_fallback.py
@@ -31,7 +31,7 @@ from pathlib import Path
import pytest
-REPO_ROOT = Path(__file__).resolve().parent.parent
+REPO_ROOT = Path(__file__).resolve().parent.parent.parent.parent
INSTALL_SH = REPO_ROOT / "scripts" / "install.sh"
pytestmark = pytest.mark.skipif(
diff --git a/tests/test_install_commit_pin_rollback.py b/tests/scripts/install/test_install_commit_pin_rollback.py
similarity index 98%
rename from tests/test_install_commit_pin_rollback.py
rename to tests/scripts/install/test_install_commit_pin_rollback.py
index 4d6de7986c..390867c6fe 100644
--- a/tests/test_install_commit_pin_rollback.py
+++ b/tests/scripts/install/test_install_commit_pin_rollback.py
@@ -28,7 +28,7 @@ from pathlib import Path
import pytest
-REPO_ROOT = Path(__file__).resolve().parent.parent
+REPO_ROOT = Path(__file__).resolve().parent.parent.parent.parent
INSTALL_SH = REPO_ROOT / "scripts" / "install.sh"
pytestmark = pytest.mark.skipif(
diff --git a/tests/test_install_diverged_update.py b/tests/scripts/install/test_install_diverged_update.py
similarity index 97%
rename from tests/test_install_diverged_update.py
rename to tests/scripts/install/test_install_diverged_update.py
index a042d8c483..04cb2db0a7 100644
--- a/tests/test_install_diverged_update.py
+++ b/tests/scripts/install/test_install_diverged_update.py
@@ -14,7 +14,7 @@ from __future__ import annotations
import re
from pathlib import Path
-REPO_ROOT = Path(__file__).resolve().parent.parent
+REPO_ROOT = Path(__file__).resolve().parent.parent.parent.parent
INSTALL_SH = REPO_ROOT / "scripts" / "install.sh"
INSTALL_PS1 = REPO_ROOT / "scripts" / "install.ps1"
diff --git a/tests/test_install_lockfile_churn.py b/tests/scripts/install/test_install_lockfile_churn.py
similarity index 98%
rename from tests/test_install_lockfile_churn.py
rename to tests/scripts/install/test_install_lockfile_churn.py
index 15861e4d01..0c02f636e6 100644
--- a/tests/test_install_lockfile_churn.py
+++ b/tests/scripts/install/test_install_lockfile_churn.py
@@ -19,7 +19,7 @@ from pathlib import Path
import pytest
-REPO_ROOT = Path(__file__).resolve().parent.parent
+REPO_ROOT = Path(__file__).resolve().parent.parent.parent.parent
INSTALL_SH = REPO_ROOT / "scripts" / "install.sh"
INSTALL_PS1 = REPO_ROOT / "scripts" / "install.ps1"
diff --git a/tests/test_install_macos_launcher.py b/tests/scripts/install/test_install_macos_launcher.py
similarity index 97%
rename from tests/test_install_macos_launcher.py
rename to tests/scripts/install/test_install_macos_launcher.py
index fd46c347a7..7729ed3e03 100644
--- a/tests/test_install_macos_launcher.py
+++ b/tests/scripts/install/test_install_macos_launcher.py
@@ -10,7 +10,7 @@ import subprocess
from pathlib import Path
-REPO_ROOT = Path(__file__).resolve().parent.parent
+REPO_ROOT = Path(__file__).resolve().parent.parent.parent.parent
INSTALL_SH = REPO_ROOT / "scripts" / "install.sh"
diff --git a/tests/test_install_no_initial_commit.py b/tests/scripts/install/test_install_no_initial_commit.py
similarity index 98%
rename from tests/test_install_no_initial_commit.py
rename to tests/scripts/install/test_install_no_initial_commit.py
index 321ddd0b40..7f028f5f82 100644
--- a/tests/test_install_no_initial_commit.py
+++ b/tests/scripts/install/test_install_no_initial_commit.py
@@ -21,7 +21,7 @@ from pathlib import Path
import pytest
-REPO_ROOT = Path(__file__).resolve().parent.parent
+REPO_ROOT = Path(__file__).resolve().parent.parent.parent.parent
INSTALL_SH = REPO_ROOT / "scripts" / "install.sh"
INSTALL_PS1 = REPO_ROOT / "scripts" / "install.ps1"
diff --git a/tests/test_install_ps1_ascii_only.py b/tests/scripts/install/test_install_ps1_ascii_only.py
similarity index 97%
rename from tests/test_install_ps1_ascii_only.py
rename to tests/scripts/install/test_install_ps1_ascii_only.py
index 71638620c2..30bfa9ed48 100644
--- a/tests/test_install_ps1_ascii_only.py
+++ b/tests/scripts/install/test_install_ps1_ascii_only.py
@@ -30,7 +30,7 @@ from __future__ import annotations
from pathlib import Path
-REPO_ROOT = Path(__file__).resolve().parent.parent
+REPO_ROOT = Path(__file__).resolve().parent.parent.parent.parent
INSTALL_PS1 = REPO_ROOT / "scripts" / "install.ps1"
diff --git a/tests/test_install_ps1_browser_install.py b/tests/scripts/install/test_install_ps1_browser_install.py
similarity index 93%
rename from tests/test_install_ps1_browser_install.py
rename to tests/scripts/install/test_install_ps1_browser_install.py
index 0a6b9b556a..372f648a52 100644
--- a/tests/test_install_ps1_browser_install.py
+++ b/tests/scripts/install/test_install_ps1_browser_install.py
@@ -8,14 +8,14 @@ only via `hermes update`'s npx cache warm or an actual browser-tool call's
lazy npx resolution.
Linux CI cannot execute the PowerShell installer, so verification here is
-source-text-level only, matching tests/test_install_sh_browser_install.py
-and tests/test_install_ps1_ascii_only.py.
+source-text-level only, matching tests/scripts/install/test_install_sh_browser_install.py
+and tests/scripts/install/test_install_ps1_ascii_only.py.
"""
import re
from pathlib import Path
-REPO_ROOT = Path(__file__).resolve().parent.parent
+REPO_ROOT = Path(__file__).resolve().parent.parent.parent.parent
INSTALL_PS1 = REPO_ROOT / "scripts" / "install.ps1"
diff --git a/tests/test_install_ps1_managed_node_swap.py b/tests/scripts/install/test_install_ps1_managed_node_swap.py
similarity index 98%
rename from tests/test_install_ps1_managed_node_swap.py
rename to tests/scripts/install/test_install_ps1_managed_node_swap.py
index 7ab389a95c..d17d06f2b9 100644
--- a/tests/test_install_ps1_managed_node_swap.py
+++ b/tests/scripts/install/test_install_ps1_managed_node_swap.py
@@ -26,7 +26,7 @@ from __future__ import annotations
import re
from pathlib import Path
-REPO_ROOT = Path(__file__).resolve().parent.parent
+REPO_ROOT = Path(__file__).resolve().parent.parent.parent.parent
INSTALL_PS1 = REPO_ROOT / "scripts" / "install.ps1"
diff --git a/tests/test_install_ps1_managed_python_provenance.py b/tests/scripts/install/test_install_ps1_managed_python_provenance.py
similarity index 99%
rename from tests/test_install_ps1_managed_python_provenance.py
rename to tests/scripts/install/test_install_ps1_managed_python_provenance.py
index 7cccce360e..2678bec980 100644
--- a/tests/test_install_ps1_managed_python_provenance.py
+++ b/tests/scripts/install/test_install_ps1_managed_python_provenance.py
@@ -15,7 +15,7 @@ from tests.install_ps1_fake_uv import compile_fake_uv
pytestmark = pytest.mark.windows_only
-_INSTALL_PS1 = Path(__file__).resolve().parents[1] / "scripts" / "install.ps1"
+_INSTALL_PS1 = Path(__file__).resolve().parents[3] / "scripts" / "install.ps1"
def test_fresh_install_manifest_orders_repo_before_checkout_scoped_python(
diff --git a/tests/test_install_ps1_native_stderr_eap.py b/tests/scripts/install/test_install_ps1_native_stderr_eap.py
similarity index 97%
rename from tests/test_install_ps1_native_stderr_eap.py
rename to tests/scripts/install/test_install_ps1_native_stderr_eap.py
index 6afcd0cb87..7ec4df9fbe 100644
--- a/tests/test_install_ps1_native_stderr_eap.py
+++ b/tests/scripts/install/test_install_ps1_native_stderr_eap.py
@@ -11,7 +11,7 @@ from __future__ import annotations
import re
from pathlib import Path
-REPO_ROOT = Path(__file__).resolve().parent.parent
+REPO_ROOT = Path(__file__).resolve().parent.parent.parent.parent
INSTALL_PS1 = REPO_ROOT / "scripts" / "install.ps1"
diff --git a/tests/test_install_ps1_node_path_for_npm.py b/tests/scripts/install/test_install_ps1_node_path_for_npm.py
similarity index 95%
rename from tests/test_install_ps1_node_path_for_npm.py
rename to tests/scripts/install/test_install_ps1_node_path_for_npm.py
index 9f3a6cb432..47f296f39c 100644
--- a/tests/test_install_ps1_node_path_for_npm.py
+++ b/tests/scripts/install/test_install_ps1_node_path_for_npm.py
@@ -10,7 +10,7 @@ from __future__ import annotations
import re
from pathlib import Path
-REPO_ROOT = Path(__file__).resolve().parent.parent
+REPO_ROOT = Path(__file__).resolve().parent.parent.parent.parent
INSTALL_PS1 = REPO_ROOT / "scripts" / "install.ps1"
diff --git a/tests/test_install_ps1_python_fallback_venv.py b/tests/scripts/install/test_install_ps1_python_fallback_venv.py
similarity index 98%
rename from tests/test_install_ps1_python_fallback_venv.py
rename to tests/scripts/install/test_install_ps1_python_fallback_venv.py
index 91c2286942..58ef44f1a8 100644
--- a/tests/test_install_ps1_python_fallback_venv.py
+++ b/tests/scripts/install/test_install_ps1_python_fallback_venv.py
@@ -28,7 +28,7 @@ from pathlib import Path
import pytest
-_INSTALL_PS1 = Path(__file__).resolve().parents[1] / "scripts" / "install.ps1"
+_INSTALL_PS1 = Path(__file__).resolve().parents[3] / "scripts" / "install.ps1"
@pytest.fixture(scope="module")
diff --git a/tests/test_install_ps1_resolver_strictmode.py b/tests/scripts/install/test_install_ps1_resolver_strictmode.py
similarity index 98%
rename from tests/test_install_ps1_resolver_strictmode.py
rename to tests/scripts/install/test_install_ps1_resolver_strictmode.py
index dbdf921c7f..c5d9d60b7a 100644
--- a/tests/test_install_ps1_resolver_strictmode.py
+++ b/tests/scripts/install/test_install_ps1_resolver_strictmode.py
@@ -24,7 +24,7 @@ from __future__ import annotations
import re
from pathlib import Path
-_INSTALL_PS1 = Path(__file__).resolve().parents[1] / "scripts" / "install.ps1"
+_INSTALL_PS1 = Path(__file__).resolve().parents[3] / "scripts" / "install.ps1"
def _ps1() -> str:
diff --git a/tests/test_install_ps1_uv_install_fallback.py b/tests/scripts/install/test_install_ps1_uv_install_fallback.py
similarity index 98%
rename from tests/test_install_ps1_uv_install_fallback.py
rename to tests/scripts/install/test_install_ps1_uv_install_fallback.py
index 0871fd94d1..9435c07a9d 100644
--- a/tests/test_install_ps1_uv_install_fallback.py
+++ b/tests/scripts/install/test_install_ps1_uv_install_fallback.py
@@ -32,7 +32,7 @@ from pathlib import Path
import pytest
-_INSTALL_PS1 = Path(__file__).resolve().parents[1] / "scripts" / "install.ps1"
+_INSTALL_PS1 = Path(__file__).resolve().parents[3] / "scripts" / "install.ps1"
_GITHUB_INSTALLER_URL = (
"https://github.com/astral-sh/uv/releases/latest/download/uv-installer.ps1"
diff --git a/tests/test_install_ps1_uv_powershell_host.py b/tests/scripts/install/test_install_ps1_uv_powershell_host.py
similarity index 98%
rename from tests/test_install_ps1_uv_powershell_host.py
rename to tests/scripts/install/test_install_ps1_uv_powershell_host.py
index ea442ce484..89df184ead 100644
--- a/tests/test_install_ps1_uv_powershell_host.py
+++ b/tests/scripts/install/test_install_ps1_uv_powershell_host.py
@@ -24,7 +24,7 @@ from pathlib import Path
import pytest
-_INSTALL_PS1 = Path(__file__).resolve().parents[1] / "scripts" / "install.ps1"
+_INSTALL_PS1 = Path(__file__).resolve().parents[3] / "scripts" / "install.ps1"
@pytest.fixture(scope="module")
diff --git a/tests/test_install_ps1_venv_process_tree.py b/tests/scripts/install/test_install_ps1_venv_process_tree.py
similarity index 98%
rename from tests/test_install_ps1_venv_process_tree.py
rename to tests/scripts/install/test_install_ps1_venv_process_tree.py
index 23e817e281..05df59c6ee 100644
--- a/tests/test_install_ps1_venv_process_tree.py
+++ b/tests/scripts/install/test_install_ps1_venv_process_tree.py
@@ -20,7 +20,7 @@ import pytest
from tests.install_ps1_fake_uv import compile_fake_uv
-REPO_ROOT = Path(__file__).resolve().parent.parent
+REPO_ROOT = Path(__file__).resolve().parent.parent.parent.parent
INSTALL_PS1 = REPO_ROOT / "scripts" / "install.ps1"
POWERSHELL = next(
(candidate for candidate in ("powershell", "pwsh") if shutil.which(candidate)),
diff --git a/tests/test_install_ps1_venv_recreate_safety.py b/tests/scripts/install/test_install_ps1_venv_recreate_safety.py
similarity index 97%
rename from tests/test_install_ps1_venv_recreate_safety.py
rename to tests/scripts/install/test_install_ps1_venv_recreate_safety.py
index 934a97fae0..542d3d582d 100644
--- a/tests/test_install_ps1_venv_recreate_safety.py
+++ b/tests/scripts/install/test_install_ps1_venv_recreate_safety.py
@@ -8,7 +8,7 @@ leaving a half-installed venv that the next health/blocker probe cannot use.
from pathlib import Path
-INSTALL_PS1 = Path(__file__).resolve().parents[1] / "scripts" / "install.ps1"
+INSTALL_PS1 = Path(__file__).resolve().parents[3] / "scripts" / "install.ps1"
def _function_body(source: str, function_name: str) -> str:
diff --git a/tests/test_install_ps1_venv_rename_abort.py b/tests/scripts/install/test_install_ps1_venv_rename_abort.py
similarity index 97%
rename from tests/test_install_ps1_venv_rename_abort.py
rename to tests/scripts/install/test_install_ps1_venv_rename_abort.py
index a779841709..e59ca8ec93 100644
--- a/tests/test_install_ps1_venv_rename_abort.py
+++ b/tests/scripts/install/test_install_ps1_venv_rename_abort.py
@@ -13,7 +13,7 @@ from pathlib import Path
import pytest
-_INSTALL_PS1 = Path(__file__).resolve().parents[1] / "scripts" / "install.ps1"
+_INSTALL_PS1 = Path(__file__).resolve().parents[3] / "scripts" / "install.ps1"
@pytest.fixture(scope="module")
diff --git a/tests/test_install_ps1_venv_transaction_boundary.py b/tests/scripts/install/test_install_ps1_venv_transaction_boundary.py
similarity index 96%
rename from tests/test_install_ps1_venv_transaction_boundary.py
rename to tests/scripts/install/test_install_ps1_venv_transaction_boundary.py
index 52f4183ec8..b0ddf23911 100644
--- a/tests/test_install_ps1_venv_transaction_boundary.py
+++ b/tests/scripts/install/test_install_ps1_venv_transaction_boundary.py
@@ -17,12 +17,12 @@ The contract locked here:
baseline-import gate passes (``Complete-VenvTransaction``).
The script only runs on Windows, so Linux CI locks the contract at the
-source level, same approach as tests/test_install_ps1_venv_recreate_safety.py.
+source level, same approach as tests/scripts/install/test_install_ps1_venv_recreate_safety.py.
"""
from pathlib import Path
-INSTALL_PS1 = Path(__file__).resolve().parents[1] / "scripts" / "install.ps1"
+INSTALL_PS1 = Path(__file__).resolve().parents[3] / "scripts" / "install.ps1"
def _function_body(source: str, function_name: str) -> str:
diff --git a/tests/test_install_ps1_web_server_syntax_probe.py b/tests/scripts/install/test_install_ps1_web_server_syntax_probe.py
similarity index 96%
rename from tests/test_install_ps1_web_server_syntax_probe.py
rename to tests/scripts/install/test_install_ps1_web_server_syntax_probe.py
index 14f99f725c..6378d509a0 100644
--- a/tests/test_install_ps1_web_server_syntax_probe.py
+++ b/tests/scripts/install/test_install_ps1_web_server_syntax_probe.py
@@ -17,7 +17,7 @@ import re
from pathlib import Path
-REPO_ROOT = Path(__file__).resolve().parent.parent
+REPO_ROOT = Path(__file__).resolve().parent.parent.parent.parent
INSTALL_PS1 = REPO_ROOT / "scripts" / "install.ps1"
diff --git a/tests/test_install_scripts_computer_use.py b/tests/scripts/install/test_install_scripts_computer_use.py
similarity index 97%
rename from tests/test_install_scripts_computer_use.py
rename to tests/scripts/install/test_install_scripts_computer_use.py
index e213a2653d..cb6d8975f1 100644
--- a/tests/test_install_scripts_computer_use.py
+++ b/tests/scripts/install/test_install_scripts_computer_use.py
@@ -8,7 +8,7 @@ binary is still missing (see test_web_routers_tools_install_on_enable.py).
from pathlib import Path
-REPO_ROOT = Path(__file__).resolve().parent.parent
+REPO_ROOT = Path(__file__).resolve().parent.parent.parent.parent
INSTALL_SH = REPO_ROOT / "scripts" / "install.sh"
INSTALL_PS1 = REPO_ROOT / "scripts" / "install.ps1"
diff --git a/tests/test_install_sh_acp_launcher.py b/tests/scripts/install/test_install_sh_acp_launcher.py
similarity index 98%
rename from tests/test_install_sh_acp_launcher.py
rename to tests/scripts/install/test_install_sh_acp_launcher.py
index 27cb4c8f1a..0a5e30a2e1 100644
--- a/tests/test_install_sh_acp_launcher.py
+++ b/tests/scripts/install/test_install_sh_acp_launcher.py
@@ -16,7 +16,7 @@ import subprocess
from pathlib import Path
from unittest.mock import patch
-INSTALL_SH = Path(__file__).resolve().parent.parent / "scripts" / "install.sh"
+INSTALL_SH = Path(__file__).resolve().parent.parent.parent.parent / "scripts" / "install.sh"
ACP_BLOCK = re.compile(
r'( rm -f "\$command_link_dir/hermes-acp".*?'
diff --git a/tests/test_install_sh_bootstrap_marker.py b/tests/scripts/install/test_install_sh_bootstrap_marker.py
similarity index 98%
rename from tests/test_install_sh_bootstrap_marker.py
rename to tests/scripts/install/test_install_sh_bootstrap_marker.py
index 6eab5d6599..ec403006f3 100644
--- a/tests/test_install_sh_bootstrap_marker.py
+++ b/tests/scripts/install/test_install_sh_bootstrap_marker.py
@@ -16,7 +16,7 @@ from pathlib import Path
import pytest
-REPO_ROOT = Path(__file__).resolve().parent.parent
+REPO_ROOT = Path(__file__).resolve().parent.parent.parent.parent
INSTALL_SH = REPO_ROOT / "scripts" / "install.sh"
diff --git a/tests/test_install_sh_browser_install.py b/tests/scripts/install/test_install_sh_browser_install.py
similarity index 99%
rename from tests/test_install_sh_browser_install.py
rename to tests/scripts/install/test_install_sh_browser_install.py
index a080bcdde2..e2f5f0323c 100644
--- a/tests/test_install_sh_browser_install.py
+++ b/tests/scripts/install/test_install_sh_browser_install.py
@@ -8,7 +8,7 @@ unsupported distribution.
from pathlib import Path
-REPO_ROOT = Path(__file__).resolve().parent.parent
+REPO_ROOT = Path(__file__).resolve().parent.parent.parent.parent
INSTALL_SH = REPO_ROOT / "scripts" / "install.sh"
diff --git a/tests/test_install_sh_install_method_stamp.py b/tests/scripts/install/test_install_sh_install_method_stamp.py
similarity index 96%
rename from tests/test_install_sh_install_method_stamp.py
rename to tests/scripts/install/test_install_sh_install_method_stamp.py
index b2a1b7da8c..6ca5f09ed2 100644
--- a/tests/test_install_sh_install_method_stamp.py
+++ b/tests/scripts/install/test_install_sh_install_method_stamp.py
@@ -20,7 +20,7 @@ from __future__ import annotations
import re
from pathlib import Path
-REPO_ROOT = Path(__file__).resolve().parent.parent
+REPO_ROOT = Path(__file__).resolve().parent.parent.parent.parent
INSTALL_SH = REPO_ROOT / "scripts" / "install.sh"
diff --git a/tests/test_install_sh_node_deps_failure.py b/tests/scripts/install/test_install_sh_node_deps_failure.py
similarity index 98%
rename from tests/test_install_sh_node_deps_failure.py
rename to tests/scripts/install/test_install_sh_node_deps_failure.py
index c42e0cb32c..ae60258b1b 100644
--- a/tests/test_install_sh_node_deps_failure.py
+++ b/tests/scripts/install/test_install_sh_node_deps_failure.py
@@ -8,7 +8,7 @@ import subprocess
from pathlib import Path
-REPO_ROOT = Path(__file__).resolve().parent.parent
+REPO_ROOT = Path(__file__).resolve().parent.parent.parent.parent
INSTALL_SH = REPO_ROOT / "scripts" / "install.sh"
diff --git a/tests/test_install_sh_node_deps_workspaces.py b/tests/scripts/install/test_install_sh_node_deps_workspaces.py
similarity index 98%
rename from tests/test_install_sh_node_deps_workspaces.py
rename to tests/scripts/install/test_install_sh_node_deps_workspaces.py
index 176e9efad3..a5e9236dd3 100644
--- a/tests/test_install_sh_node_deps_workspaces.py
+++ b/tests/scripts/install/test_install_sh_node_deps_workspaces.py
@@ -18,7 +18,7 @@ from pathlib import Path
import pytest
-REPO_ROOT = Path(__file__).resolve().parent.parent
+REPO_ROOT = Path(__file__).resolve().parent.parent.parent.parent
INSTALL_SH = REPO_ROOT / "scripts" / "install.sh"
pytestmark = pytest.mark.skipif(shutil.which("bash") is None, reason="needs bash")
diff --git a/tests/test_install_sh_node_global_prefix.py b/tests/scripts/install/test_install_sh_node_global_prefix.py
similarity index 97%
rename from tests/test_install_sh_node_global_prefix.py
rename to tests/scripts/install/test_install_sh_node_global_prefix.py
index d4293a0d59..f2ac4cb929 100644
--- a/tests/test_install_sh_node_global_prefix.py
+++ b/tests/scripts/install/test_install_sh_node_global_prefix.py
@@ -14,7 +14,7 @@ node/npm/npx), scoped to the bundled Node via its prefix-local global npmrc.
from pathlib import Path
-REPO_ROOT = Path(__file__).resolve().parent.parent
+REPO_ROOT = Path(__file__).resolve().parent.parent.parent.parent
INSTALL_SH = REPO_ROOT / "scripts" / "install.sh"
NODE_BOOTSTRAP = REPO_ROOT / "scripts" / "lib" / "node-bootstrap.sh"
diff --git a/tests/test_install_sh_node_npm_check.py b/tests/scripts/install/test_install_sh_node_npm_check.py
similarity index 96%
rename from tests/test_install_sh_node_npm_check.py
rename to tests/scripts/install/test_install_sh_node_npm_check.py
index 6fa33e9bae..2c1f84319c 100644
--- a/tests/test_install_sh_node_npm_check.py
+++ b/tests/scripts/install/test_install_sh_node_npm_check.py
@@ -9,7 +9,7 @@ when the install actually failed.
from pathlib import Path
-REPO_ROOT = Path(__file__).resolve().parent.parent
+REPO_ROOT = Path(__file__).resolve().parent.parent.parent.parent
INSTALL_SH = REPO_ROOT / "scripts" / "install.sh"
diff --git a/tests/test_install_sh_node_prerelease.py b/tests/scripts/install/test_install_sh_node_prerelease.py
similarity index 98%
rename from tests/test_install_sh_node_prerelease.py
rename to tests/scripts/install/test_install_sh_node_prerelease.py
index 2b2a613ed9..3f07dbb87e 100644
--- a/tests/test_install_sh_node_prerelease.py
+++ b/tests/scripts/install/test_install_sh_node_prerelease.py
@@ -19,7 +19,7 @@ import re
import subprocess
from pathlib import Path
-REPO_ROOT = Path(__file__).resolve().parent.parent
+REPO_ROOT = Path(__file__).resolve().parent.parent.parent.parent
INSTALL_SH = REPO_ROOT / "scripts" / "install.sh"
NODE_BOOTSTRAP = REPO_ROOT / "scripts" / "lib" / "node-bootstrap.sh"
diff --git a/tests/test_install_sh_node_probe_87460.py b/tests/scripts/install/test_install_sh_node_probe.py
similarity index 99%
rename from tests/test_install_sh_node_probe_87460.py
rename to tests/scripts/install/test_install_sh_node_probe.py
index 83e50e10a4..6bdd9282ba 100644
--- a/tests/test_install_sh_node_probe_87460.py
+++ b/tests/scripts/install/test_install_sh_node_probe.py
@@ -19,7 +19,7 @@ import tarfile
from pathlib import Path
-REPO_ROOT = Path(__file__).resolve().parent.parent
+REPO_ROOT = Path(__file__).resolve().parent.parent.parent.parent
INSTALL_SH = REPO_ROOT / "scripts" / "install.sh"
diff --git a/tests/test_install_sh_node_tarball_without_xz.py b/tests/scripts/install/test_install_sh_node_tarball_without_xz.py
similarity index 97%
rename from tests/test_install_sh_node_tarball_without_xz.py
rename to tests/scripts/install/test_install_sh_node_tarball_without_xz.py
index 468306acc9..24c02cce79 100644
--- a/tests/test_install_sh_node_tarball_without_xz.py
+++ b/tests/scripts/install/test_install_sh_node_tarball_without_xz.py
@@ -16,7 +16,7 @@ from pathlib import Path
import pytest
-REPO_ROOT = Path(__file__).resolve().parent.parent
+REPO_ROOT = Path(__file__).resolve().parent.parent.parent.parent
INDEX = "node-v26.7.0-linux-x64.tar.xz\nnode-v26.7.0-linux-x64.tar.gz\n"
diff --git a/tests/test_install_sh_pythonpath_sanitization.py b/tests/scripts/install/test_install_sh_pythonpath_sanitization.py
similarity index 94%
rename from tests/test_install_sh_pythonpath_sanitization.py
rename to tests/scripts/install/test_install_sh_pythonpath_sanitization.py
index 0fd4c14d92..533a1da3e1 100644
--- a/tests/test_install_sh_pythonpath_sanitization.py
+++ b/tests/scripts/install/test_install_sh_pythonpath_sanitization.py
@@ -8,7 +8,7 @@ must sanitize those vars both during installation and at runtime launch.
from pathlib import Path
-REPO_ROOT = Path(__file__).resolve().parent.parent
+REPO_ROOT = Path(__file__).resolve().parent.parent.parent.parent
INSTALL_SH = REPO_ROOT / "scripts" / "install.sh"
diff --git a/tests/test_install_sh_reuse_supported_python.py b/tests/scripts/install/test_install_sh_reuse_supported_python.py
similarity index 97%
rename from tests/test_install_sh_reuse_supported_python.py
rename to tests/scripts/install/test_install_sh_reuse_supported_python.py
index 82805ec971..d5f477d1fa 100644
--- a/tests/test_install_sh_reuse_supported_python.py
+++ b/tests/scripts/install/test_install_sh_reuse_supported_python.py
@@ -10,7 +10,7 @@ from pathlib import Path
import pytest
-REPO_ROOT = Path(__file__).resolve().parent.parent
+REPO_ROOT = Path(__file__).resolve().parent.parent.parent.parent
INSTALL_SH = REPO_ROOT / "scripts" / "install.sh"
diff --git a/tests/test_install_sh_root_fhs_uv_python_path.py b/tests/scripts/install/test_install_sh_root_fhs_uv_python_path.py
similarity index 97%
rename from tests/test_install_sh_root_fhs_uv_python_path.py
rename to tests/scripts/install/test_install_sh_root_fhs_uv_python_path.py
index 0f1c5fa725..31cb2695d0 100644
--- a/tests/test_install_sh_root_fhs_uv_python_path.py
+++ b/tests/scripts/install/test_install_sh_root_fhs_uv_python_path.py
@@ -10,7 +10,7 @@ and the shared ``/usr/local/bin/hermes`` wrapper fails for non-root users with
from pathlib import Path
-REPO_ROOT = Path(__file__).resolve().parent.parent
+REPO_ROOT = Path(__file__).resolve().parent.parent.parent.parent
INSTALL_SH = REPO_ROOT / "scripts" / "install.sh"
diff --git a/tests/test_install_sh_setup_wizard_tty_probe.py b/tests/scripts/install/test_install_sh_setup_wizard_tty_probe.py
similarity index 98%
rename from tests/test_install_sh_setup_wizard_tty_probe.py
rename to tests/scripts/install/test_install_sh_setup_wizard_tty_probe.py
index a9f8a26e75..b36cd322ef 100644
--- a/tests/test_install_sh_setup_wizard_tty_probe.py
+++ b/tests/scripts/install/test_install_sh_setup_wizard_tty_probe.py
@@ -20,7 +20,7 @@ from pathlib import Path
import pytest
-REPO_ROOT = Path(__file__).resolve().parent.parent
+REPO_ROOT = Path(__file__).resolve().parent.parent.parent.parent
INSTALL_SH = REPO_ROOT / "scripts" / "install.sh"
# Every function in scripts/install.sh that previously gated on a bare
diff --git a/tests/test_install_sh_symlink_stomp.py b/tests/scripts/install/test_install_sh_symlink_stomp.py
similarity index 98%
rename from tests/test_install_sh_symlink_stomp.py
rename to tests/scripts/install/test_install_sh_symlink_stomp.py
index 0fbe508509..21deab8f85 100644
--- a/tests/test_install_sh_symlink_stomp.py
+++ b/tests/scripts/install/test_install_sh_symlink_stomp.py
@@ -22,7 +22,7 @@ from pathlib import Path
-REPO_ROOT = Path(__file__).resolve().parent.parent
+REPO_ROOT = Path(__file__).resolve().parent.parent.parent.parent
INSTALL_SH = REPO_ROOT / "scripts" / "install.sh"
diff --git a/tests/test_install_sh_termux_network_prereqs.py b/tests/scripts/install/test_install_sh_termux_network_prereqs.py
similarity index 92%
rename from tests/test_install_sh_termux_network_prereqs.py
rename to tests/scripts/install/test_install_sh_termux_network_prereqs.py
index 891cf54d13..ebd1dac460 100644
--- a/tests/test_install_sh_termux_network_prereqs.py
+++ b/tests/scripts/install/test_install_sh_termux_network_prereqs.py
@@ -3,7 +3,7 @@
from pathlib import Path
-REPO_ROOT = Path(__file__).resolve().parent.parent
+REPO_ROOT = Path(__file__).resolve().parent.parent.parent.parent
INSTALL_SH = REPO_ROOT / "scripts" / "install.sh"
diff --git a/tests/test_install_sh_termux_python_bounds.py b/tests/scripts/install/test_install_sh_termux_python_bounds.py
similarity index 99%
rename from tests/test_install_sh_termux_python_bounds.py
rename to tests/scripts/install/test_install_sh_termux_python_bounds.py
index 443873bf64..f4fac1d3e6 100644
--- a/tests/test_install_sh_termux_python_bounds.py
+++ b/tests/scripts/install/test_install_sh_termux_python_bounds.py
@@ -10,7 +10,7 @@ import sys
from pathlib import Path
-REPO_ROOT = Path(__file__).resolve().parent.parent
+REPO_ROOT = Path(__file__).resolve().parent.parent.parent.parent
INSTALL_SH = REPO_ROOT / "scripts" / "install.sh"
SETUP_HERMES_SH = REPO_ROOT / "setup-hermes.sh"
diff --git a/tests/test_install_sh_uv_lock_config.py b/tests/scripts/install/test_install_sh_uv_lock_config.py
similarity index 98%
rename from tests/test_install_sh_uv_lock_config.py
rename to tests/scripts/install/test_install_sh_uv_lock_config.py
index 59d5bba96d..f9f6dca113 100644
--- a/tests/test_install_sh_uv_lock_config.py
+++ b/tests/scripts/install/test_install_sh_uv_lock_config.py
@@ -10,7 +10,7 @@ import subprocess
import pytest
-REPO_ROOT = Path(__file__).resolve().parent.parent
+REPO_ROOT = Path(__file__).resolve().parent.parent.parent.parent
INSTALL_SCRIPTS = (
REPO_ROOT / "scripts" / "install.sh",
REPO_ROOT / "setup-hermes.sh",
diff --git a/tests/test_install_unmerged_index.py b/tests/scripts/install/test_install_unmerged_index.py
similarity index 99%
rename from tests/test_install_unmerged_index.py
rename to tests/scripts/install/test_install_unmerged_index.py
index ade18486c9..cc15998de7 100644
--- a/tests/test_install_unmerged_index.py
+++ b/tests/scripts/install/test_install_unmerged_index.py
@@ -20,7 +20,7 @@ from pathlib import Path
import pytest
-REPO_ROOT = Path(__file__).resolve().parent.parent
+REPO_ROOT = Path(__file__).resolve().parent.parent.parent.parent
INSTALL_SH = REPO_ROOT / "scripts" / "install.sh"
INSTALL_PS1 = REPO_ROOT / "scripts" / "install.ps1"
diff --git a/tests/test_run_tests_parallel.py b/tests/scripts/test_run_tests_parallel.py
similarity index 97%
rename from tests/test_run_tests_parallel.py
rename to tests/scripts/test_run_tests_parallel.py
index f83e9a5563..aeee6cfa0e 100644
--- a/tests/test_run_tests_parallel.py
+++ b/tests/scripts/test_run_tests_parallel.py
@@ -65,7 +65,7 @@ def _pid_alive(pid: int) -> bool:
def test_progress_output_tolerates_legacy_stdout_encoding(tmp_path: Path) -> None:
"""Progress glyphs must not crash the runner on non-UTF-8 consoles."""
- repo_root = Path(__file__).resolve().parent.parent
+ repo_root = Path(__file__).resolve().parent.parent.parent
runner = repo_root / "scripts" / "run_tests_parallel.py"
probe_dir = tmp_path / "probe"
@@ -111,7 +111,7 @@ def test_grandchild_leak_is_killed_by_runner(tmp_path: Path) -> None:
3. Wait for the grandchild PID to vanish (poll for ~5s).
4. Assert the runner exited cleanly AND the grandchild is dead.
"""
- repo_root = Path(__file__).resolve().parent.parent
+ repo_root = Path(__file__).resolve().parent.parent.parent
runner = repo_root / "scripts" / "run_tests_parallel.py"
assert runner.exists(), f"runner missing at {runner}"
@@ -253,7 +253,7 @@ def _make_probe_dir(tmp_path: Path) -> Path:
def _run_runner(probe_dir: Path, *extra: str) -> subprocess.CompletedProcess:
- repo_root = Path(__file__).resolve().parent.parent
+ repo_root = Path(__file__).resolve().parent.parent.parent
runner = repo_root / "scripts" / "run_tests_parallel.py"
return subprocess.run(
[sys.executable, str(runner), "--paths", str(probe_dir),
@@ -297,7 +297,7 @@ def test_bare_value_flag_keeps_its_value(tmp_path: Path) -> None:
def test_positional_path_not_treated_as_flag(tmp_path: Path) -> None:
"""A positional path arg still overrides discovery (not routed to pytest)."""
probe_dir = _make_probe_dir(tmp_path)
- repo_root = Path(__file__).resolve().parent.parent
+ repo_root = Path(__file__).resolve().parent.parent.parent
runner = repo_root / "scripts" / "run_tests_parallel.py"
# Pass the probe dir positionally (no --paths), plus a bare -q.
proc = subprocess.run(
@@ -314,7 +314,7 @@ def test_positional_path_not_treated_as_flag(tmp_path: Path) -> None:
def test_file_retry_self_heals_and_prints_both_attempts(tmp_path: Path) -> None:
"""A pass-on-retry is green, loud, and retains the failing traceback."""
- repo_root = Path(__file__).resolve().parent.parent
+ repo_root = Path(__file__).resolve().parent.parent.parent
runner = repo_root / "scripts" / "run_tests_parallel.py"
marker = tmp_path / "ran-once"
probe = tmp_path / "test_flaky_probe.py"
@@ -386,7 +386,7 @@ def test_node_id_selector_runs_the_named_test(tmp_path: Path) -> None:
"""``file.py::test_alpha`` runs that test instead of discovering nothing."""
probe_dir = _make_probe_dir(tmp_path)
target = probe_dir / "test_flagprobe.py"
- repo_root = Path(__file__).resolve().parent.parent
+ repo_root = Path(__file__).resolve().parent.parent.parent
proc = subprocess.run(
[sys.executable, str(repo_root / "scripts" / "run_tests_parallel.py"),
f"{target}::test_alpha", "-j", "1", "--file-timeout", "30"],
@@ -404,7 +404,7 @@ def test_explicit_k_wins_over_node_id_inference(tmp_path: Path) -> None:
"""A caller's own ``-k`` is not overridden by the node-id translation."""
probe_dir = _make_probe_dir(tmp_path)
target = probe_dir / "test_flagprobe.py"
- repo_root = Path(__file__).resolve().parent.parent
+ repo_root = Path(__file__).resolve().parent.parent.parent
proc = subprocess.run(
[sys.executable, str(repo_root / "scripts" / "run_tests_parallel.py"),
f"{target}::test_alpha", "-k", "test_beta",
@@ -430,7 +430,7 @@ def test_multiple_absolute_paths_split_on_pathsep(tmp_path: Path) -> None:
(dir_b / "test_flagprobe_b.py").write_text(
"def test_gamma():\n assert True\n"
)
- repo_root = Path(__file__).resolve().parent.parent
+ repo_root = Path(__file__).resolve().parent.parent.parent
runner = repo_root / "scripts" / "run_tests_parallel.py"
proc = subprocess.run(
[sys.executable, str(runner),
diff --git a/tests/test_run_tests_parallel_stdio.py b/tests/scripts/test_run_tests_parallel_stdio.py
similarity index 97%
rename from tests/test_run_tests_parallel_stdio.py
rename to tests/scripts/test_run_tests_parallel_stdio.py
index 2add57c3e2..f1dbad3084 100644
--- a/tests/test_run_tests_parallel_stdio.py
+++ b/tests/scripts/test_run_tests_parallel_stdio.py
@@ -14,7 +14,7 @@ import io
import sys
from pathlib import Path
-REPO_ROOT = Path(__file__).resolve().parents[1]
+REPO_ROOT = Path(__file__).resolve().parents[2]
_RUNNER_PATH = REPO_ROOT / "scripts" / "run_tests_parallel.py"
diff --git a/tests/test_evidence_store.py b/tests/skills/test_evidence_store.py
similarity index 99%
rename from tests/test_evidence_store.py
rename to tests/skills/test_evidence_store.py
index 33bac2dcdb..7d6cadc32f 100644
--- a/tests/test_evidence_store.py
+++ b/tests/skills/test_evidence_store.py
@@ -3,7 +3,7 @@ from pathlib import Path
import importlib.util
# Load the hyphenated script name dynamically
-repo_root = Path(__file__).parent.parent
+repo_root = Path(__file__).parent.parent.parent
script_path = repo_root / "optional-skills" / "security" / "oss-forensics" / "scripts" / "evidence-store.py"
spec = importlib.util.spec_from_file_location("evidence_store", str(script_path))
diff --git a/tests/test_minisweagent_path.py b/tests/test_minisweagent_path.py
deleted file mode 100644
index 965e4cfd59..0000000000
--- a/tests/test_minisweagent_path.py
+++ /dev/null
@@ -1,2 +0,0 @@
-# This file intentionally left empty.
-# minisweagent_path.py was removed — see PR #2804.
diff --git a/tests/test_model_picker_scroll.py b/tests/test_model_picker_scroll.py
deleted file mode 100644
index 4698aef956..0000000000
--- a/tests/test_model_picker_scroll.py
+++ /dev/null
@@ -1,100 +0,0 @@
-"""Tests for the scrolling viewport logic in _curses_prompt_choice (issue #5755).
-
-The "More providers" submenu has 13 entries (11 extended + custom + cancel).
-Before the fix, _curses_prompt_choice rendered items starting unconditionally
-from index 0 with no scroll offset. On terminals shorter than ~16 rows, items
-near the bottom were never drawn. When the cursor wrapped from 0 to the last
-item (Cancel) via UP-arrow, the highlight rendered off-screen, leaving the menu
-looking like only "Cancel" existed.
-
-The fix adds a scroll_offset that tracks the cursor so the highlighted item
-is always within the visible window. These tests exercise that logic in
-isolation without requiring a real TTY.
-"""
-
-
-
-
-# ---------------------------------------------------------------------------
-# Pure scroll-offset logic extracted from _curses_menu for unit testing
-# ---------------------------------------------------------------------------
-
-def _compute_scroll_offset(cursor: int, scroll_offset: int, visible: int, n_choices: int) -> int:
- """Mirror of the scroll adjustment block inside _curses_menu."""
- if cursor < scroll_offset:
- scroll_offset = cursor
- elif cursor >= scroll_offset + visible:
- scroll_offset = cursor - visible + 1
- scroll_offset = max(0, min(scroll_offset, max(0, n_choices - visible)))
- return scroll_offset
-
-
-def _visible_indices(cursor: int, scroll_offset: int, visible: int, n_choices: int):
- """Return the list indices that would be rendered for the given state."""
- scroll_offset = _compute_scroll_offset(cursor, scroll_offset, visible, n_choices)
- return list(range(scroll_offset, min(scroll_offset + visible, n_choices)))
-
-
-# ---------------------------------------------------------------------------
-# Tests: scroll offset calculation
-# ---------------------------------------------------------------------------
-
-class TestScrollOffsetLogic:
- N = 13 # typical extended-providers list length
-
- def test_cursor_at_zero_no_scroll(self):
- """Start position: offset stays 0, first items visible."""
- assert _compute_scroll_offset(0, 0, 8, self.N) == 0
-
- def test_cursor_within_window_unchanged(self):
- """Cursor inside the current window: offset unchanged."""
- assert _compute_scroll_offset(5, 0, 8, self.N) == 0
-
-
- def test_cursor_wraps_to_cancel_via_up(self):
- """UP from index 0 wraps to last item; last item must be visible."""
- wrapped_cursor = (0 - 1) % self.N # == 12
- indices = _visible_indices(wrapped_cursor, 0, 8, self.N)
- assert wrapped_cursor in indices
-
-
- def test_visible_window_never_exceeds_list(self):
- """Offset is clamped so the window never starts past the list end."""
- offset = _compute_scroll_offset(12, 0, 20, self.N) # window larger than list
- assert offset == 0
-
-
- def test_list_fits_in_window_no_scroll_needed(self):
- """If all choices fit in the visible window, offset is always 0."""
- for cursor in range(self.N):
- offset = _compute_scroll_offset(cursor, 0, 20, self.N)
- assert offset == 0, f"cursor={cursor} should not scroll when window > list"
-
- def test_cursor_always_in_visible_range(self):
- """Invariant: cursor is always within the rendered window after adjustment."""
- visible = 5
- for cursor in range(self.N):
- indices = _visible_indices(cursor, 0, visible, self.N)
- assert cursor in indices, f"cursor={cursor} not in visible={indices}"
-
- def test_full_navigation_down_cursor_always_visible(self):
- """Simulate pressing DOWN through all items; cursor always in view."""
- visible = 6
- scroll_offset = 0
- cursor = 0
- for _ in range(self.N + 2): # wrap around twice
- scroll_offset = _compute_scroll_offset(cursor, scroll_offset, visible, self.N)
- rendered = list(range(scroll_offset, min(scroll_offset + visible, self.N)))
- assert cursor in rendered, f"cursor={cursor} not in rendered={rendered}"
- cursor = (cursor + 1) % self.N
-
- def test_full_navigation_up_cursor_always_visible(self):
- """Simulate pressing UP through all items; cursor always in view."""
- visible = 6
- scroll_offset = 0
- cursor = 0
- for _ in range(self.N + 2):
- scroll_offset = _compute_scroll_offset(cursor, scroll_offset, visible, self.N)
- rendered = list(range(scroll_offset, min(scroll_offset + visible, self.N)))
- assert cursor in rendered, f"cursor={cursor} not in rendered={rendered}"
- cursor = (cursor - 1) % self.N
diff --git a/tests/test_process_loop_event_loop_warning.py b/tests/test_process_loop_event_loop_warning.py
deleted file mode 100644
index 7251060ef8..0000000000
--- a/tests/test_process_loop_event_loop_warning.py
+++ /dev/null
@@ -1,130 +0,0 @@
-"""Tests for the process_loop RuntimeWarning fix -- issue #19285.
-
-In Python 3.10+, calling asyncio.get_event_loop() from a non-main thread
-that has no current event loop emits a DeprecationWarning (3.10/3.11) or
-RuntimeWarning (3.12+). The fix replaces get_event_loop() with
-get_running_loop(), which raises RuntimeError (no warning) when there is no
-running loop.
-"""
-
-import asyncio
-import threading
-import warnings
-
-
-class TestGetRunningLoopReplacement:
-
- def test_get_running_loop_raises_runtime_error_not_warning(self):
- warnings_caught = []
-
- def _thread_target():
- with warnings.catch_warnings(record=True) as w:
- warnings.simplefilter("always")
- try:
- asyncio.get_running_loop()
- except RuntimeError:
- pass
- warnings_caught.extend(w)
-
- t = threading.Thread(target=_thread_target, daemon=True)
- t.start()
- t.join(timeout=5)
-
- runtime_warnings = [
- x for x in warnings_caught
- if issubclass(x.category, RuntimeWarning)
- ]
- assert runtime_warnings == [], (
- f"Unexpected RuntimeWarning(s): {[str(w.message) for w in runtime_warnings]}"
- )
-
- def test_get_running_loop_is_silent_get_event_loop_is_not(self):
- caught_from_running = []
-
- def _test_get_running_loop():
- with warnings.catch_warnings(record=True) as w:
- warnings.simplefilter("always")
- try:
- asyncio.get_running_loop()
- except RuntimeError:
- pass
- caught_from_running.extend(w)
-
- t = threading.Thread(target=_test_get_running_loop, daemon=True)
- t.start()
- t.join(timeout=5)
-
- assert all(
- not issubclass(w.category, RuntimeWarning)
- for w in caught_from_running
- ), "get_running_loop() must never emit RuntimeWarning"
-
- def test_get_running_loop_returns_loop_when_running(self):
- async def _check():
- loop = asyncio.get_running_loop()
- assert loop is not None
- assert loop.is_running()
-
- asyncio.run(_check())
-
- def test_no_warning_from_background_thread_with_fix(self):
- warnings_caught = []
-
- def _thread_target():
- with warnings.catch_warnings(record=True) as w:
- warnings.simplefilter("always")
- try:
- current_loop = asyncio.get_running_loop()
- except RuntimeError:
- current_loop = None
- except Exception:
- current_loop = None
- assert current_loop is None
- warnings_caught.extend(w)
-
- t = threading.Thread(target=_thread_target, daemon=True)
- t.start()
- t.join(timeout=5)
-
- runtime_warnings = [
- x for x in warnings_caught
- if issubclass(x.category, RuntimeWarning)
- ]
- assert runtime_warnings == [], (
- f"RuntimeWarning emitted despite fix: "
- f"{[str(w.message) for w in runtime_warnings]}"
- )
-
- def test_fixed_pattern_in_process_loop_context(self):
- results = {}
- warnings_list = []
-
- def _process_loop_simulation():
- with warnings.catch_warnings(record=True) as w:
- warnings.simplefilter("always")
- try:
- current_loop = asyncio.get_running_loop()
- except RuntimeError:
- current_loop = None
- except Exception:
- current_loop = None
- results["current_loop"] = current_loop
- warnings_list.extend(w)
-
- t = threading.Thread(
- target=_process_loop_simulation,
- name="Thread-3 (process_loop)",
- daemon=True,
- )
- t.start()
- t.join(timeout=5)
-
- assert results.get("current_loop") is None
- runtime_warnings = [
- x for x in warnings_list
- if issubclass(x.category, RuntimeWarning)
- ]
- assert runtime_warnings == [], (
- f"process_loop simulation still emits RuntimeWarning: "
- f"{[str(w.message) for w in runtime_warnings]}"
- )
diff --git a/tests/test_tests_tree_layout.py b/tests/test_tests_tree_layout.py
new file mode 100644
index 0000000000..1cfd6d504e
--- /dev/null
+++ b/tests/test_tests_tree_layout.py
@@ -0,0 +1,76 @@
+"""The tests/ tree mirrors the source tree.
+
+``scripts/run_tests.sh tests//`` is how a change gets its regression
+coverage run, so a test filed under a directory that does not correspond to
+the code it exercises is a test nobody runs when that code changes. Two
+drifts had accumulated: parallel directories for one source package
+(``tests/cli`` beside ``tests/hermes_cli``, ``tests/run_agent`` beside
+``tests/agent``, ``tests/state`` beside ``tests/hermes_state``) and ~250
+loose files at ``tests/`` root that belonged to a package.
+
+Filenames also stopped carrying issue numbers: ``test_89315_x.py`` reads as
+noise in a directory listing and the number belongs in the docstring, where
+``git log -S`` and a reader can both find it with context.
+"""
+
+from __future__ import annotations
+
+import re
+from pathlib import Path
+
+REPO_ROOT = Path(__file__).resolve().parent.parent
+TESTS_ROOT = REPO_ROOT / "tests"
+
+# tests// directories that do not mirror a source directory but are
+# legitimate homes: cross-cutting suites, fixtures, and script-family tests.
+_NON_MIRROR_DIRS = {
+ "ci", "conformance", "dashboard", "desktop", "docker", "e2e", "evals",
+ "fakes", "fixtures", "honcho_plugin", "install", "integration", "manual",
+ "monitoring", "openviking_plugin", "perf_guards", "scripts", "secret_sources",
+ "security", "skills", "verify", "website", "computer_use", "hermes_state",
+}
+
+# Root-level modules whose tests sit directly in tests/ (no package to mirror).
+_ROOT_MODULE_STEMS = {p.stem for p in REPO_ROOT.glob("*.py")}
+
+_ISSUE_NUMBER = re.compile(r"(^test_\d{4,6}_)|(_\d{4,6}\.py$)")
+
+
+def _source_dirs() -> set[str]:
+ return {
+ p.name
+ for p in REPO_ROOT.iterdir()
+ if p.is_dir() and (p / "__init__.py").exists()
+ }
+
+
+def test_every_test_directory_mirrors_a_source_directory_or_is_declared():
+ """No sibling directory for a source package that already has one."""
+ source_dirs = _source_dirs()
+ offenders = sorted(
+ d.name
+ for d in TESTS_ROOT.iterdir()
+ if d.is_dir()
+ and d.name not in ("__pycache__",)
+ and d.name not in source_dirs
+ and d.name not in _NON_MIRROR_DIRS
+ )
+ assert not offenders, (
+ "tests/ directories that mirror no source package: "
+ f"{offenders}. Put the tests under tests// (tests/agent, "
+ "tests/hermes_cli, ...) or, for a genuinely cross-cutting suite, add the "
+ "name to _NON_MIRROR_DIRS in this file with a reason."
+ )
+
+
+def test_no_issue_numbers_in_test_filenames():
+ """Issue numbers live in docstrings, where the reader gets context."""
+ offenders = sorted(
+ str(p.relative_to(REPO_ROOT))
+ for p in TESTS_ROOT.rglob("test_*.py")
+ if _ISSUE_NUMBER.search(p.name)
+ )
+ assert not offenders, (
+ f"Issue numbers in test filenames: {offenders}. Drop the number from the "
+ "name and cite the issue in the module docstring instead."
+ )
diff --git a/tests/test_yaml_indent_consistency_31999.py b/tests/test_yaml_indent_consistency.py
similarity index 100%
rename from tests/test_yaml_indent_consistency_31999.py
rename to tests/test_yaml_indent_consistency.py
diff --git a/tests/test_browser_vault.py b/tests/tools/test_browser_vault.py
similarity index 99%
rename from tests/test_browser_vault.py
rename to tests/tools/test_browser_vault.py
index 4487162e9d..30317f934d 100644
--- a/tests/test_browser_vault.py
+++ b/tests/tools/test_browser_vault.py
@@ -23,7 +23,7 @@ from unittest.mock import patch
import pytest
-sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
+sys.path.insert(0, str(Path(__file__).resolve().parent.parent.parent))
from agent.vault_login_classifier import ( # noqa: E402
ClassifiedLoginControl,
diff --git a/tests/test_compaction_tool_refresh.py b/tests/tools/test_compaction_tool_refresh.py
similarity index 98%
rename from tests/test_compaction_tool_refresh.py
rename to tests/tools/test_compaction_tool_refresh.py
index 408303dfe7..5a3669dddb 100644
--- a/tests/test_compaction_tool_refresh.py
+++ b/tests/tools/test_compaction_tool_refresh.py
@@ -13,7 +13,7 @@ import sys
import unittest
from unittest.mock import patch
-sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
+sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", ".."))
class _Agent:
diff --git a/tests/tools/test_hub_lock_non_utf8_68053.py b/tests/tools/test_hub_lock_non_utf8.py
similarity index 100%
rename from tests/tools/test_hub_lock_non_utf8_68053.py
rename to tests/tools/test_hub_lock_non_utf8.py
diff --git a/tests/test_model_tools.py b/tests/tools/test_model_tools.py
similarity index 100%
rename from tests/test_model_tools.py
rename to tests/tools/test_model_tools.py
diff --git a/tests/test_plugin_compat_warning.py b/tests/tools/test_plugin_compat_warning.py
similarity index 91%
rename from tests/test_plugin_compat_warning.py
rename to tests/tools/test_plugin_compat_warning.py
index 45a7d799da..2dc9d9779d 100644
--- a/tests/test_plugin_compat_warning.py
+++ b/tests/tools/test_plugin_compat_warning.py
@@ -1,6 +1,6 @@
"""The plugin-compat layer tells plugin authors where a name went, once, and internal code never trips it.
-Kept alongside the compat layer (tests/test_compat_manifest_targets.py); both are deleted with it.
+Kept alongside the compat layer (tests/hermes_cli/test_compat_manifest_targets.py); both are deleted with it.
"""
import importlib
import json
@@ -9,7 +9,7 @@ from pathlib import Path
import pytest
-ROOT = Path(__file__).resolve().parent.parent
+ROOT = Path(__file__).resolve().parent.parent.parent
MANIFEST = ROOT / "compat_manifest.json"
pytestmark = pytest.mark.skipif(not MANIFEST.exists(), reason="compat layer removed (scheduled revert)")
diff --git a/tests/test_plugin_skills.py b/tests/tools/test_plugin_skills.py
similarity index 100%
rename from tests/test_plugin_skills.py
rename to tests/tools/test_plugin_skills.py
diff --git a/tests/tools/test_pre_transcription_hook.py b/tests/tools/test_pre_transcription_hook.py
index b9335db290..c650a32929 100644
--- a/tests/tools/test_pre_transcription_hook.py
+++ b/tests/tools/test_pre_transcription_hook.py
@@ -19,7 +19,7 @@ Covers:
kwargs — no signature change.
Mirrors the ``transform_tool_result`` hook test conventions from
-``tests/test_transform_tool_result_hook.py``.
+``tests/plugins/test_transform_tool_result_hook.py``.
"""
from __future__ import annotations
diff --git a/tests/test_subprocess_home_isolation.py b/tests/tools/test_subprocess_home_isolation.py
similarity index 100%
rename from tests/test_subprocess_home_isolation.py
rename to tests/tools/test_subprocess_home_isolation.py
diff --git a/tests/tools/test_94248_timeout_transport_drain.py b/tests/tools/test_timeout_transport_drain.py
similarity index 100%
rename from tests/tools/test_94248_timeout_transport_drain.py
rename to tests/tools/test_timeout_transport_drain.py
diff --git a/tests/test_toolset_distributions.py b/tests/tools/test_toolset_distributions.py
similarity index 100%
rename from tests/test_toolset_distributions.py
rename to tests/tools/test_toolset_distributions.py
diff --git a/tests/test_toolsets.py b/tests/tools/test_toolsets.py
similarity index 100%
rename from tests/test_toolsets.py
rename to tests/tools/test_toolsets.py
diff --git a/tests/test_voice_max_recording_seconds.py b/tests/tools/test_voice_max_recording_seconds.py
similarity index 100%
rename from tests/test_voice_max_recording_seconds.py
rename to tests/tools/test_voice_max_recording_seconds.py
diff --git a/tests/tui_gateway/test_94895_backend_heartbeat.py b/tests/tui_gateway/test_backend_heartbeat.py
similarity index 100%
rename from tests/tui_gateway/test_94895_backend_heartbeat.py
rename to tests/tui_gateway/test_backend_heartbeat.py
diff --git a/tests/tui_gateway/test_entry_picker_prewarm.py b/tests/tui_gateway/test_entry_picker_prewarm.py
index a4bb4d17a2..530cb9530b 100644
--- a/tests/tui_gateway/test_entry_picker_prewarm.py
+++ b/tests/tui_gateway/test_entry_picker_prewarm.py
@@ -17,7 +17,7 @@ guard is covered in ``tests/hermes_cli/test_picker_prewarm.py``):
- A prewarm import/start failure is swallowed (fire-and-forget contract) and
must not prevent ``main()`` from reaching the read loop.
-Harness: same style as tests/test_tui_entry_mcp_owner.py — import
+Harness: same style as tests/tui_gateway/test_tui_entry_mcp_owner.py — import
``tui_gateway.entry`` and monkeypatch its module attributes, running the real
``main()`` with stubbed I/O collaborators (no subprocess, no real gateway).
"""
diff --git a/tests/test_lazy_session_regressions.py b/tests/tui_gateway/test_lazy_session_regressions.py
similarity index 100%
rename from tests/test_lazy_session_regressions.py
rename to tests/tui_gateway/test_lazy_session_regressions.py
diff --git a/tests/tui_gateway/test_106459_routing_provenance_reopen.py b/tests/tui_gateway/test_routing_provenance_reopen.py
similarity index 98%
rename from tests/tui_gateway/test_106459_routing_provenance_reopen.py
rename to tests/tui_gateway/test_routing_provenance_reopen.py
index 2570c54ed2..7219d63f83 100644
--- a/tests/tui_gateway/test_106459_routing_provenance_reopen.py
+++ b/tests/tui_gateway/test_routing_provenance_reopen.py
@@ -76,7 +76,7 @@ def db(tmp_path: Path):
@pytest.fixture
def turn_env(monkeypatch, tmp_path, db):
- """The immediate-prompt harness of tests/test_tui_gateway_server.py, with a real SessionDB."""
+ """The immediate-prompt harness of tests/tui_gateway/test_tui_gateway_server.py, with a real SessionDB."""
for name, stub in {
"_emit": lambda *_a, **_k: None, "make_stream_renderer": lambda _cols: None,
"render_message": lambda _raw, _cols: None, "_wire_callbacks": lambda _sid: None,
diff --git a/tests/test_slash_worker_watchdog.py b/tests/tui_gateway/test_slash_worker_watchdog.py
similarity index 100%
rename from tests/test_slash_worker_watchdog.py
rename to tests/tui_gateway/test_slash_worker_watchdog.py
diff --git a/tests/test_tui_entry_mcp_owner.py b/tests/tui_gateway/test_tui_entry_mcp_owner.py
similarity index 100%
rename from tests/test_tui_entry_mcp_owner.py
rename to tests/tui_gateway/test_tui_entry_mcp_owner.py
diff --git a/tests/test_tui_gateway_event_replay.py b/tests/tui_gateway/test_tui_gateway_event_replay.py
similarity index 100%
rename from tests/test_tui_gateway_event_replay.py
rename to tests/tui_gateway/test_tui_gateway_event_replay.py
diff --git a/tests/test_tui_gateway_loop_noise.py b/tests/tui_gateway/test_tui_gateway_loop_noise.py
similarity index 100%
rename from tests/test_tui_gateway_loop_noise.py
rename to tests/tui_gateway/test_tui_gateway_loop_noise.py
diff --git a/tests/test_tui_gateway_queue_on_busy.py b/tests/tui_gateway/test_tui_gateway_queue_on_busy.py
similarity index 100%
rename from tests/test_tui_gateway_queue_on_busy.py
rename to tests/tui_gateway/test_tui_gateway_queue_on_busy.py
diff --git a/tests/test_tui_gateway_server.py b/tests/tui_gateway/test_tui_gateway_server.py
similarity index 100%
rename from tests/test_tui_gateway_server.py
rename to tests/tui_gateway/test_tui_gateway_server.py
diff --git a/tests/test_tui_gateway_server_crash_history.py b/tests/tui_gateway/test_tui_gateway_server_crash_history.py
similarity index 100%
rename from tests/test_tui_gateway_server_crash_history.py
rename to tests/tui_gateway/test_tui_gateway_server_crash_history.py
diff --git a/tests/test_tui_gateway_ws.py b/tests/tui_gateway/test_tui_gateway_ws.py
similarity index 100%
rename from tests/test_tui_gateway_ws.py
rename to tests/tui_gateway/test_tui_gateway_ws.py
diff --git a/tests/test_tui_mcp_late_refresh.py b/tests/tui_gateway/test_tui_mcp_late_refresh.py
similarity index 100%
rename from tests/test_tui_mcp_late_refresh.py
rename to tests/tui_gateway/test_tui_mcp_late_refresh.py
diff --git a/tests/test_ws_keepalive_config.py b/tests/tui_gateway/test_ws_keepalive_config.py
similarity index 100%
rename from tests/test_ws_keepalive_config.py
rename to tests/tui_gateway/test_ws_keepalive_config.py
diff --git a/website/docs/developer-guide/acp-internals.md b/website/docs/developer-guide/acp-internals.md
index e739d80871..a9cd83b748 100644
--- a/website/docs/developer-guide/acp-internals.md
+++ b/website/docs/developer-guide/acp-internals.md
@@ -175,7 +175,7 @@ ACP temporarily installs an approval callback on the terminal tool during prompt
## Related files
-- `tests/acp/` — ACP test suite
+- `tests/acp_adapter/` — ACP test suite
- `toolsets.py` — `hermes-acp` toolset definition
- `hermes_cli/main.py` — `hermes acp` CLI subcommand
- `pyproject.toml` — `[acp]` optional dependency + `hermes-acp` script
diff --git a/website/docs/developer-guide/adding-providers.md b/website/docs/developer-guide/adding-providers.md
index 5d11383b69..ba8e276bd5 100644
--- a/website/docs/developer-guide/adding-providers.md
+++ b/website/docs/developer-guide/adding-providers.md
@@ -327,12 +327,12 @@ At minimum, touch the tests that guard provider wiring.
Common places:
- `tests/hermes_cli/test_runtime_provider_resolution.py`
-- `tests/cli/test_cli_provider_resolution.py`
+- `tests/hermes_cli/test_cli_provider_resolution.py`
- `tests/hermes_cli/test_model_switch_custom_providers.py` (and adjacent `tests/hermes_cli/test_model_switch_*.py`)
- `tests/hermes_cli/test_setup_model_provider.py`
-- `tests/run_agent/test_provider_parity.py`
-- `tests/run_agent/test_run_agent.py`
-- `tests/test__adapter.py` for a native provider
+- `tests/agent/test_provider_parity.py`
+- `tests/agent/test_run_agent.py`
+- `tests/agent/test__adapter.py` for a native provider
For docs-only examples, the exact file set may differ. The point is to cover:
@@ -347,7 +347,7 @@ Run the targeted tests (or use `scripts/run_tests.sh`, which runs each file in i
```bash
source venv/bin/activate
-python -m pytest tests/hermes_cli/test_runtime_provider_resolution.py tests/cli/test_cli_provider_resolution.py tests/hermes_cli/test_setup_model_provider.py tests/run_agent/test_provider_parity.py -q
+python -m pytest tests/hermes_cli/test_runtime_provider_resolution.py tests/hermes_cli/test_cli_provider_resolution.py tests/hermes_cli/test_setup_model_provider.py tests/agent/test_provider_parity.py -q
```
For deeper changes, run the full suite before pushing:
diff --git a/website/docs/developer-guide/egress-internals.md b/website/docs/developer-guide/egress-internals.md
index 50852a8a36..4b0e5eaea6 100644
--- a/website/docs/developer-guide/egress-internals.md
+++ b/website/docs/developer-guide/egress-internals.md
@@ -46,18 +46,18 @@ tools/environments/docker.py
_HERMES_EGRESS_NODE_OPTIONS_APPEND
sentinel, enforce_on_docker precedence.
-tests/test_iron_proxy.py Hermetic tests (~70). Binary install
+tests/agent/test_iron_proxy.py Hermetic tests (~70). Binary install
path, config build, mappings I/O,
subprocess lifecycle, docker arg builder,
deny CIDR defaults, bind policy, CA
TOCTOU, ensure_audit_log behaviour, etc.
-tests/test_iron_proxy_cli.py CLI handler unit tests (~20). Argparse
+tests/hermes_cli/test_iron_proxy_cli.py CLI handler unit tests (~20). Argparse
wiring, fail-loud paths, BWS refresh
wire-up, dest='egress_command'
regression guard.
-tests/test_iron_proxy_e2e.py Live E2E (gated on HERMES_RUN_E2E=1).
+tests/agent/test_iron_proxy_e2e.py Live E2E (gated on HERMES_RUN_E2E=1).
Real iron-proxy binary, real curl,
end-to-end token swap verified.
```
@@ -298,10 +298,10 @@ iron-proxy writes line-delimited JSON to `~/.hermes/proxy/iron-proxy.log` on the
```bash
# Hermetic suite (no network, no real binary)
-scripts/run_tests.sh tests/test_iron_proxy.py tests/test_iron_proxy_cli.py
+scripts/run_tests.sh tests/agent/test_iron_proxy.py tests/hermes_cli/test_iron_proxy_cli.py
# Live E2E (real binary, real curl, real CONNECT tunnel)
-HERMES_RUN_E2E=1 scripts/run_tests.sh tests/test_iron_proxy_e2e.py
+HERMES_RUN_E2E=1 scripts/run_tests.sh tests/agent/test_iron_proxy_e2e.py
# Live PTY smoke against `hermes egress`
HERMES_HOME=/tmp/hermes-egress-test python3 -m hermes_cli.main egress --help
diff --git a/website/docs/developer-guide/memory-provider-plugin.md b/website/docs/developer-guide/memory-provider-plugin.md
index 00f8fc948e..86b1f60de5 100644
--- a/website/docs/developer-guide/memory-provider-plugin.md
+++ b/website/docs/developer-guide/memory-provider-plugin.md
@@ -342,7 +342,7 @@ data_dir = Path("~/.hermes/my-provider").expanduser()
## Testing
-See `tests/agent/test_memory_provider.py` and adjacent memory tests (`tests/agent/test_memory_session_switch.py`, `tests/agent/test_memory_user_id.py`, `tests/run_agent/test_memory_provider_init.py`) for end-to-end patterns.
+See `tests/agent/test_memory_provider.py` and adjacent memory tests (`tests/agent/test_memory_session_switch.py`, `tests/agent/test_memory_user_id.py`, `tests/agent/test_memory_provider_init.py`) for end-to-end patterns.
```python
from agent.memory_manager import MemoryManager
diff --git a/website/docs/developer-guide/provider-runtime.md b/website/docs/developer-guide/provider-runtime.md
index 5cb620a6d1..203965c833 100644
--- a/website/docs/developer-guide/provider-runtime.md
+++ b/website/docs/developer-guide/provider-runtime.md
@@ -211,7 +211,7 @@ Cron jobs **do** support fallback: `run_job()` reads `fallback_providers` (or le
Fallback behavior is exercised across several suites:
-- `tests/run_agent/test_fallback_credential_isolation.py` — credential isolation between primary and fallback
+- `tests/agent/test_fallback_credential_isolation.py` — credential isolation between primary and fallback
- `tests/hermes_cli/test_fallback_cmd.py` — the `/fallback` CLI command
- `tests/gateway/test_fallback_eviction.py` — gateway eviction of failed providers
diff --git a/website/docs/user-guide/cli.md b/website/docs/user-guide/cli.md
index 6c4ed6526a..2ca8cf6a70 100644
--- a/website/docs/user-guide/cli.md
+++ b/website/docs/user-guide/cli.md
@@ -202,7 +202,7 @@ Start a line with `!` to run it as a shell command instead of sending it to the
```
> !git status
> !ls -la
-> !pytest -x tests/cli
+> !pytest -x tests/hermes_cli
```
- **Zero cost.** The model is never invoked — no API call, no tokens, no latency.