fix(gateway): report runtime source version in /health, not stale dist-info metadata

/health and /health/detailed resolve the version via importlib.metadata
first, falling back to hermes_cli.__version__. On editable/source
checkouts — including the standard git-based install that hermes-setup
performs — hermes_agent-*.dist-info can survive a source update
unchanged, so the health endpoints keep reporting the previous release
even though the running code (CLI, dashboard, release tags) is newer.
Stale metadata does not raise, so the source fallback never fires.

Flip the preference: use hermes_cli.__version__ (the runtime source of
truth shared by the CLI and dashboard) first, and fall back to
distribution metadata only when the source import fails. The
never-raise contract of the version probe is unchanged.

Observed live: CLI, dashboard, and pyproject all reported 0.18.2 while
/health returned 0.18.0 from a stale hermes_agent-0.18.0.dist-info left
behind by a source update.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Kennedy Umege 2026-07-08 12:15:56 +01:00 committed by Teknium
parent 87e31cb7d1
commit eff293115d
2 changed files with 37 additions and 11 deletions

View file

@ -96,23 +96,25 @@ logger = logging.getLogger(__name__)
def _hermes_version() -> str:
"""Return the hermes-agent version string, or "dev" if it can't be resolved.
"""Return the canonical Hermes Agent version string.
Tries the installed package metadata first (authoritative for a pip/uv
install), then the in-tree ``hermes_cli.__version__`` (covers editable /
source checkouts where metadata may be stale or absent). Never raises
a version probe must not be able to break the health endpoint.
``hermes_cli.__version__`` is the runtime source of truth used by the CLI,
dashboard, portal tags, and release script. Prefer it over installed
distribution metadata because editable/source checkouts can retain stale
``hermes_agent-*.dist-info`` after a source update until the environment is
reinstalled. Never raises a version probe must not be able to break the
health endpoint.
"""
try:
from importlib.metadata import version
return version("hermes-agent")
except Exception:
pass
try:
from hermes_cli import __version__
return __version__
except Exception:
pass
try:
from importlib.metadata import version
return version("hermes-agent")
except Exception:
return "dev"

View file

@ -30,6 +30,7 @@ from gateway.platforms.api_server import (
ResponseStore,
_IdempotencyCache,
_derive_chat_session_id,
_hermes_version,
_redact_api_error_text,
check_api_server_requirements,
cors_middleware,
@ -776,6 +777,29 @@ class TestHealthEndpoint:
assert isinstance(data["version"], str)
assert data["version"] != ""
def test_health_version_prefers_runtime_source_over_stale_metadata(self):
"""Editable installs can leave importlib.metadata at an older release.
The health endpoint must report the running Hermes source version, not
stale ``hermes_agent-*.dist-info`` metadata from before a source update.
"""
from hermes_cli import __version__
with patch("importlib.metadata.version", return_value="0.18.0"):
assert _hermes_version() == __version__
@pytest.mark.asyncio
async def test_health_endpoint_prefers_runtime_version_over_stale_metadata(self, adapter):
from hermes_cli import __version__
app = _create_app(adapter)
with patch("importlib.metadata.version", return_value="0.18.0"):
async with TestClient(TestServer(app)) as cli:
resp = await cli.get("/health")
assert resp.status == 200
data = await resp.json()
assert data["version"] == __version__
@pytest.mark.asyncio
async def test_v1_health_alias_returns_ok(self, adapter):
"""GET /v1/health should return the same response as /health."""