{"repo":"NousResearch/hermes-agent","public_id":"49234","display_label":"Noisy raft CLI warning logged when raft unused","summary":"Reports and a fix for an unconditional warning message about the raft CLI being logged repeatedly, even when the raft platform plugin is disabled or the CLI is not installed.","subsystem":"agent","counts":{"members":3,"open_issues":0,"open_issues_distinct":0,"open_prs":0,"closed_issues":2,"merged_prs":1,"attempt_prs":0},"landed_fix":{"status":"merged_to_main","basis":"structural","pr":49240,"merged_at":"2026-06-20T00:12:59+00:00"},"resolution":{"state":"partially_resolved","by":"pr-49240"},"freshness":{"state":"verified","last_verified_at":"2026-06-20T00:12:59+00:00","is_stale":false},"grouping":{"backbone":"STRUCTURAL_PARTIAL","link_density":0.4533333333333333},"fix_edges":[{"issue_number":49234,"pr_number":49240,"tier":"verified","basis":"closing_ref","confidence":0.95,"recoverable_from_timeline":true,"pr_merged_at":"2026-06-20T00:12:59+00:00"}],"members":[{"kind":"issue","number":49234,"title":"[Bug]: Raft platform plugin floods logs with WARNING every ~10s for users without raft CLI","url":null,"state":"closed"},{"kind":"issue","number":49336,"title":"[Bug] Unconditional warning '[raft] raft CLI not found in PATH' logged even when Raft platform is disabled","url":null,"state":"closed"},{"kind":"pr","number":49240,"title":"fix(plugins): silence raft check_fn log spam for users without raft CLI","url":null,"state":"merged","close_reason":{"category":"merged"},"merged":true,"is_fix_pr":true,"fix_tier":"verified","files_changed":["plugins/platforms/raft/adapter.py","tests/plugins/test_raft_check_fn_silent.py"],"fix_hunks":[{"file":"plugins/platforms/raft/adapter.py","hunks":["@@ -98,12 +98,20 @@\n \n \n def check_raft_requirements() -> bool:\n-    \"\"\"Check if Raft channel dependencies are available.\"\"\"\n+    \"\"\"Check if Raft channel dependencies are available.\n+\n+    Intentionally silent on failure — this is a passive probe registered as\n+    the platform's ``check_fn``. It is called on every\n+    ``load_gateway_config()`` (message handling, display lookups, agent\n+    turns), so logging here floods the logs for every user without the\n+    ``raft`` CLI installed. The caller (``gateway/platform_registry.py``\n+    ``create_adapter()``) emits its own warning when requirements are not met\n+    and an adapter is actually requested. This matches the convention used by\n+    other platform adapters (e.g. ``teams/adapter.py``).\n+    \"\"\"\n     if not AIOHTTP_AVAILABLE:\n-        logger.warning(\"[raft] aiohttp is not installed — install with: pip install aiohttp\")\n         return False\n     if not shutil.which(\"raft\"):\n-        logger.warning(\"[raft] raft CLI not found in PATH — install from https://raft.build\")\n         return False\n     return True\n "]},{"file":"tests/plugins/test_raft_check_fn_silent.py","hunks":["@@ -0,0 +1,75 @@\n+\"\"\"Regression tests for the raft platform plugin's check_fn.\n+\n+The raft platform adapter's ``check_raft_requirements()`` is registered as\n+the platform's ``check_fn``. This function is invoked on every\n+``load_gateway_config()`` call (dozens of times during normal gateway\n+operation). It must therefore be a *silent* predicate — returning True/False\n+without logging — otherwise every user without the ``raft`` CLI installed\n+gets their logs flooded with WARNING messages every few seconds.\n+\n+See: https://github.com/NousResearch/hermes-agent/issues/49234\n+\"\"\"\n+\n+import logging\n+from unittest.mock import patch\n+\n+import pytest\n+\n+\n[redacted:email]\n+def raft_check():\n+    \"\"\"Import check_raft_requirements fresh (adapter self-manages sys.path).\"\"\"\n+    from plugins.platforms.raft.adapter import check_raft_requirements\n+\n+    return check_raft_requirements\n+\n+\n+def test_check_returns_false_when_raft_cli_missing(raft_check):\n+    \"\"\"check_fn returns False when raft CLI is not in PATH.\"\"\"\n+    with patch(\"plugins.platforms.raft.adapter.shutil.which\", return_value=None), \\\n+         patch(\"plugins.platforms.raft.adapter.AIOHTTP_AVAILABLE\", True):\n+        assert raft_check() is False\n+\n+\n+def test_check_returns_false_when_aiohttp_missing(raft_check):\n+    \"\"\"check_fn returns False when aiohttp dependency is unavailable.\"\"\"\n+    with patch(\"plugins.platforms.raft.adapter.AIOHTTP_AVAILABLE\", False):\n+        assert raft_check() is False\n+\n+\n+def test_check_returns_true_when_all_deps_present(raft_check):\n+    \"\"\"check_fn returns True when all dependencies are available.\"\"\"\n+    with patch(\"plugins.platforms.raft.adapter.shutil.which\", return_value=\"/usr/bin/raft\"), \\\n+         patch(\"plugins.platforms.raft.adapter.AIOHTTP_AVAILABLE\", True):\n+        assert raft_check() is True\n+\n+\n+def test_check_silent_when_raft_cli_missing(raft_check, caplog):\n+    \"\"\"check_fn must NOT log a WARNING when raft CLI is missing.\n+\n+    This is the regression guard for issue #49234 — logging inside check_fn\n+    causes log spam because the function is called on every config load.\n+    \"\"\"\n+    with patch(\"plugins.platforms.raft.adapter.shutil.which\", return_value=None), \\\n+         patch(\"plugins.platforms.raft.adapter.AIOHTTP_AVAILABLE\", True):\n+        with caplog.at_level(logging.WARNING, logger=\"plugins.platforms.raft.adapter\"):\n+            raft_check()\n+\n+    warnings = [r for r in caplog.records if r.levelno >= logging.WARNING]\n+    assert warnings == [], (\n+        f\"check_raft_requirements must be silent (no WARNING logs), \"\n+        f\"but emitted: {[r.getMessage() for r in warnings]}\"\n+    )\n+\n+\n+def test_check_silent_when_aiohttp_missing(raft_check, caplog):\n+    \"\"\"check_fn must NOT log a WARNING when aiohttp is missing.\"\"\"\n+    with patch(\"plugins.platforms.raft.adapter.AIOHTTP_AVAILABLE\", False):\n+        with caplog.at_level(logging.WARNING, logger=\"plugins.platforms.raft.adapter\"):\n+            raft_check()\n+\n+    warnings = [r for r in caplog.records if r.levelno >= logging.WARNING]\n+    assert warnings == [], (\n+        f\"check_raft_requirements must be silent (no WARNING logs), \"\n+        f\"but emitted: {[r.getMessage() for r in warnings]}\"\n+    )\n"]}]}],"trust":{"default_tier":"verified","includes_suggested":false}}