test+docs: exercise moved-name call sites; document the agent_compat contract

Review follow-up for the Hermes Agent moved-name fix.

Tests: replace the AST source guard with behavior tests that run each
affected WebUI entry point against two fake Agent shapes, pre_split (name on
the original module) and pointer_removed (name only in its new home):
approval session identity bind/reset, MCP runtime status, /reload-mcp,
Claude Code credential read, LM Studio reasoning options, and kanban
connections (closed after use), board counts, SSE event feed, and dispatch.
All 8 pointer_removed cases fail against the pre-fix call sites; the
pre_split cases pass on both.

Docs: ARCHITECTURE.md gains the api/agent_compat.py inventory entry and
section 4.9 covering ownership (Agent's compat_manifest.json), resolution
order, call-site rules, and the removal policy.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Mike McHargueandClaude Opus 5 authored and nesquena-hermes committed 2026-09-17 21:26:50 +00:00
1 parent 1f7e5e78da
commit 8abf95335b
2 files changed
+233 -27

No files matched your search

+34
View File
@@ -56,6 +56,7 @@ actions. The topbar remains focused on conversation context and the workspace/fi
.dockerignore Excludes .git, tests/, .env* from Docker builds
api/
__init__.py Package marker
agent_compat.py Resolver for Hermes Agent names moved to sibling modules (compatibility-only)
auth.py Optional password authentication, signed cookies, passkeys/WebAuthn
config.py Discovery, globals, model detection, reloadable config
helpers.py HTTP helpers: j(), bad(), require(), safe_resolve(), security headers
@@ -418,6 +419,39 @@ whose default `SessionDB()` path remains frozen at module import. Keep this fall
compatibility-only: new goal semantics belong in Hermes Agent's native manager rather
than a second WebUI implementation.
### 4.9 Hermes Agent Moved-Name Compatibility
Hermes Agent owns its module layout. Its September 2026 decomposition moved names the
WebUI uses (for example `tools.approval.set_current_session_key` to
`tools.approval_context`) into `<stem>_<topic>` sibling modules. The old paths resolve
only through temporary PLUGIN-COMPAT `__getattr__` pointers that emit
`HermesPluginCompatWarning` and are removed on schedule. The Agent's
`compat_manifest.json` is the authoritative map of what moved where.
WebUI code reaches a moved name only through
`api.agent_compat.agent_attr(owner, name, home, default=...)`, which resolves in this
order:
1. the owner module's own namespace: pre-split Agents, and tests that stub the original
module in `sys.modules` or patch the name onto it;
2. the `home` module: split Agents, with or without the old-path pointer (no warning);
3. plain attribute access on the owner: non-module test doubles.
It raises like the import it replaces (or returns `default`), so each call site keeps its
existing fallback. Current users: approval session identity and MCP discovery
(`streaming.py`), `/reload-mcp` (`commands.py`), MCP runtime status (`routes.py`), Claude
Code credential linking (`oauth.py`), LM Studio reasoning options (`config.py`), and
kanban connections and dispatch (`kanban_bridge.py`).
Import names that are still native to their module directly. Never
`from <old module> import <moved name>`, and never feature-detect a moved name with
`hasattr`/`getattr` on the old module: once the pointers are removed those silently turn
"moved" into "missing" behind the call sites' broad `except` blocks. When a later Agent
split moves another name, route it through `agent_attr` and add pre-split and
pointer-removed cases to `tests/test_agent_compat.py`. The resolver is
compatibility-only: delete it, and import directly from the new homes, once the WebUI
stops supporting Agents that predate the split.
---
## 5. Frontend Architecture: Current State