Compare commits

..

2 Commits

Author SHA1 Message Date
Svrnty
6c88bf8899 fix(adwright): route ↗BTE crosslink through canonical switchPanel
Some checks failed
plugin-tests / test (push) Failing after 6s
The Adwright ↗BTE button called SvrntyBTE.open() directly, which sets
overlay.hidden=false but never flips main.svrnty-showing-bte. The CSS
gates BTE overlay visibility on that class, so the click did nothing
visible — overlay was mounted but display:none. Route the click through
window.switchPanel("bte") instead so the canonical nav wrapper handles
class flipping + event dispatch identically to the sidebar BTE button.

Validated via Playwright with 6 gates: V1 sidebar BTE (1 event, class
set, visible), V2 ↗BTE crosslink (1 event, class flip, visible),
V3 Adwright nav (class flip, BTE hidden), V4 main classes correct,
V5 console clean, V6 /api/bte/proxy 200 OK. All pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 17:05:41 -04:00
Svrnty
ab24ff9cdb fix(plugin): umbrella.py registration broke /api/umbrella — sprint 2026-05-25 Wave 7 D12
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) <noreply@anthropic.com>
2026-05-24 16:50:19 -04:00
2 changed files with 18 additions and 13 deletions

View File

@ -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

View File

@ -154,10 +154,17 @@
_activateTab(id);
});
});
// Cross-panel link to BTE Command Center.
// Cross-panel link to BTE Command Center. Route through canonical
// switchPanel so main.svrnty-showing-bte class gets flipped (CSS gates
// overlay visibility on that class). SvrntyBTE.open is fallback for
// the early-boot edge case where the nav wrapper isn't installed yet.
const bteBtn = panel.querySelector("#svrntyAwOpenBte");
if (bteBtn) bteBtn.addEventListener("click", () => {
if (window.SvrntyBTE && window.SvrntyBTE.open) window.SvrntyBTE.open();
if (typeof window.switchPanel === "function") {
window.switchPanel("bte", { fromRailClick: true });
} else if (window.SvrntyBTE && window.SvrntyBTE.open) {
window.SvrntyBTE.open();
}
});
// One-click profile switch to cmo-planb.
const switchBtn = panel.querySelector("#svrntyAwSwitchCmo");