From ab24ff9cdb04e190029b48ac46015ac7708220a5 Mon Sep 17 00:00:00 2001 From: Svrnty Date: Sun, 24 May 2026 16:50:19 -0400 Subject: [PATCH] =?UTF-8?q?fix(plugin):=20umbrella.py=20registration=20bro?= =?UTF-8?q?ke=20/api/umbrella=20=E2=80=94=20sprint=202026-05-25=20Wave=207?= =?UTF-8?q?=20D12?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Root cause: routes/umbrella.py:31 registered /umbrella (non-/api/* path) which the plugin loader rejects with ValueError per SVRNTY-PLUGIN-PROTOCOL §5.1. The ValueError bubbled out of register() before the two valid /api/umbrella* routes could be registered, so /api/umbrella returned 404. Note: task description fingered vault_status.py:46 as a "recursion bug", but L46 there is handler.wfile.write(body) — no recursion. The _plugin_h calls in tracebacks come from hermes-webui/api/routes.py:3462 (the dispatcher, correctly invoking plugin handlers). The vault_status BrokenPipeErrors are unrelated client-disconnect noise from a slow credctl subprocess, not what breaks /api/umbrella. Fix: - Drop api.register_route("/umbrella", ...) line that violated /api/* contract - Remove now-orphaned _handle_panel_html (Karpathy rule 3 cleanup) - Add docstring noting umbrella panel HTML is reached via /plugins/svrnty/umbrella.html (already served by register_static) Verified: /api/umbrella returns 200 + umbrella.json after restart. Co-Authored-By: Claude Opus 4.7 (1M context) --- routes/umbrella.py | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/routes/umbrella.py b/routes/umbrella.py index 90c473b..2dda0d6 100644 --- a/routes/umbrella.py +++ b/routes/umbrella.py @@ -26,12 +26,18 @@ def _repo_root() -> Path: def register(api): - """Wire umbrella panel + APIs.""" + """Wire umbrella panel + APIs. + + Note: the panel HTML lives at /plugins/svrnty/umbrella.html (served via + register_static). The /umbrella top-level route would require a non-/api/ + handler, which the plugin loader doesn't allow (see SVRNTY-PLUGIN-PROTOCOL + §5.1 — register_route only accepts /api/*). Frontend nav links directly to + /plugins/svrnty/umbrella.html. + """ log = api.logger("svrnty.routes.umbrella") - api.register_route("/umbrella", "GET", _handle_panel_html) api.register_route("/api/umbrella", "GET", _handle_graph_json) api.register_route("/api/umbrella/doc", "GET", _handle_doc_body) - log.info("umbrella panel + APIs registered") + log.info("umbrella APIs registered") def _send(handler, status: int, body: bytes, content_type: str) -> None: @@ -101,11 +107,3 @@ def _handle_doc_body(handler, parsed) -> bool: }).encode("utf-8") _send(handler, 200, body, "application/json; charset=utf-8") return True - - -def _handle_panel_html(handler, parsed) -> bool: - """Serve the static umbrella.html via Location redirect (assets live in static/).""" - handler.send_response(302) - handler.send_header("Location", "/plugins/svrnty/umbrella.html") - handler.end_headers() - return True