Implement S23 runtime health slice

This commit is contained in:
Svrnty 2026-05-28 21:38:50 -04:00
parent c0ff59097c
commit cf723141a4
8 changed files with 466 additions and 1 deletions

View File

@ -2,7 +2,7 @@
**Upstream version:** v0.51.118 **Upstream version:** v0.51.118
**Plugin version:** 0.5.0 **Plugin version:** 0.5.0
**Total dependencies:** 34 (25 public API · 0 forced internal · 9 frontend) **Total dependencies:** 39 (29 public API · 0 forced internal · 10 frontend)
> **Auto-generated by `scripts/ast-connection-map.py`. Do not hand-edit.** > **Auto-generated by `scripts/ast-connection-map.py`. Do not hand-edit.**
> To change a justification, edit the `# CONNECTION:` comment above the > To change a justification, edit the `# CONNECTION:` comment above the
@ -25,12 +25,16 @@
| `plugin.py:46` | `api.inject_script` | `api.inject_script(f"/plugins/{STATIC_PREFIX}/bte.js")` | | `plugin.py:46` | `api.inject_script` | `api.inject_script(f"/plugins/{STATIC_PREFIX}/bte.js")` |
| `plugin.py:48` | `api.inject_stylesheet` | `api.inject_stylesheet(f"/plugins/{STATIC_PREFIX}/umbrella_inline.css")` | | `plugin.py:48` | `api.inject_stylesheet` | `api.inject_stylesheet(f"/plugins/{STATIC_PREFIX}/umbrella_inline.css")` |
| `plugin.py:49` | `api.inject_script` | `api.inject_script(f"/plugins/{STATIC_PREFIX}/umbrella_inline.js")` | | `plugin.py:49` | `api.inject_script` | `api.inject_script(f"/plugins/{STATIC_PREFIX}/umbrella_inline.js")` |
| `plugin.py:51` | `api.inject_stylesheet` | `api.inject_stylesheet(f"/plugins/{STATIC_PREFIX}/cortex-os/runtime-health/runtime_health.c` |
| `plugin.py:52` | `api.inject_script` | `api.inject_script(f"/plugins/{STATIC_PREFIX}/cortex-os/runtime-health/runtime_health.js")` |
| `routes/adwright.py:68` | `api.logger` | `log = api.logger("svrnty.routes.adwright")` | | `routes/adwright.py:68` | `api.logger` | `log = api.logger("svrnty.routes.adwright")` |
| `routes/adwright.py:69` | `api.register_route` | `api.register_route(` | | `routes/adwright.py:69` | `api.register_route` | `api.register_route(` |
| `routes/adwright.py:71` | `api.register_route` | `api.register_route(` | | `routes/adwright.py:71` | `api.register_route` | `api.register_route(` |
| `routes/bte_proxy.py:90` | `api.logger` | `log = api.logger("svrnty.routes.bte_proxy")` | | `routes/bte_proxy.py:90` | `api.logger` | `log = api.logger("svrnty.routes.bte_proxy")` |
| `routes/bte_proxy.py:91` | `api.register_route` | `api.register_route("/api/bte/proxy", "GET", _handle_proxy)` | | `routes/bte_proxy.py:91` | `api.register_route` | `api.register_route("/api/bte/proxy", "GET", _handle_proxy)` |
| `routes/bte_proxy.py:92` | `api.register_route` | `api.register_route("/api/bte/proxy", "POST", _handle_proxy)` | | `routes/bte_proxy.py:92` | `api.register_route` | `api.register_route("/api/bte/proxy", "POST", _handle_proxy)` |
| `routes/cortex_os_runtime_health.py:26` | `api.logger` | `log = api.logger("svrnty.routes.cortex_os_runtime_health")` |
| `routes/cortex_os_runtime_health.py:27` | `api.register_route` | `api.register_route(ROUTE_PATH, ROUTE_METHOD, _handle_runtime_health)` |
| `routes/transcribe.py:37` | `api.logger` | `log = api.logger("svrnty.routes.transcribe")` | | `routes/transcribe.py:37` | `api.logger` | `log = api.logger("svrnty.routes.transcribe")` |
| `routes/transcribe.py:38` | `api.register_route` | `api.register_route("/api/transcribe", "POST", _handle_transcribe)` | | `routes/transcribe.py:38` | `api.register_route` | `api.register_route("/api/transcribe", "POST", _handle_transcribe)` |
| `routes/transcribe.py:39` | `api.register_audio_attachment_processor` | `api.register_audio_attachment_processor(_transcribe_audio_attachments)` | | `routes/transcribe.py:39` | `api.register_audio_attachment_processor` | `api.register_audio_attachment_processor(_transcribe_audio_attachments)` |
@ -63,4 +67,5 @@ _None. Plugin uses only the public API._ ✓
| `static/adwright.js` | 606 | `/api/adwright/provision-creds` | | `static/adwright.js` | 606 | `/api/adwright/provision-creds` |
| `static/umbrella.js` | 57 | `/api/umbrella` | | `static/umbrella.js` | 57 | `/api/umbrella` |
| `static/app.js` | 165 | `/api/vault/status` | | `static/app.js` | 165 | `/api/vault/status` |
| `static/cortex-os/runtime-health/runtime_health.js` | 4 | `/api/cortex-os/runtime-health` |

View File

@ -47,6 +47,9 @@ def register(api):
# Inline Umbrella graph for the Hermes Workspace right panel. # Inline Umbrella graph for the Hermes Workspace right panel.
api.inject_stylesheet(f"/plugins/{STATIC_PREFIX}/umbrella_inline.css") api.inject_stylesheet(f"/plugins/{STATIC_PREFIX}/umbrella_inline.css")
api.inject_script(f"/plugins/{STATIC_PREFIX}/umbrella_inline.js") api.inject_script(f"/plugins/{STATIC_PREFIX}/umbrella_inline.js")
# Cortex OS Runtime Health slice: read-only route status display.
api.inject_stylesheet(f"/plugins/{STATIC_PREFIX}/cortex-os/runtime-health/runtime_health.css")
api.inject_script(f"/plugins/{STATIC_PREFIX}/cortex-os/runtime-health/runtime_health.js")
log.info("static + assets wired at /plugins/%s/", STATIC_PREFIX) log.info("static + assets wired at /plugins/%s/", STATIC_PREFIX)
# Routes — each feature lives in its own module under routes/. # Routes — each feature lives in its own module under routes/.
@ -77,4 +80,5 @@ def _phase2_routes():
"adwright", # P2.C — Adwright tool panel routes (PRD §5+§6) ✓ "adwright", # P2.C — Adwright tool panel routes (PRD §5+§6) ✓
"bte_proxy", # P2.D — BTE Command Center same-origin proxy (PRD §3) ✓ "bte_proxy", # P2.D — BTE Command Center same-origin proxy (PRD §3) ✓
"umbrella", # P2.E — cortex-os umbrella graph viz (UMBRELLA-VIZ-PRD) ✓ "umbrella", # P2.E — cortex-os umbrella graph viz (UMBRELLA-VIZ-PRD) ✓
"cortex_os_runtime_health", # S23.0-I4 — Cortex OS Runtime Health read-only slice ✓
] ]

View File

@ -0,0 +1,142 @@
"""GET /api/cortex-os/runtime-health - Cortex OS Runtime Health slice.
Public API surface used: api.register_route, api.logger.
No forced internal dependencies. This module does not import Hermes internals.
"""
from __future__ import annotations
import json
import re
from typing import Any
ROUTE_PATH = "/api/cortex-os/runtime-health"
ROUTE_METHOD = "GET"
CONTRACT_ID = "runtime-health/v0.1"
CHECKED_AT = "2026-05-29T00:00:00Z"
_FORBIDDEN_TEXT = re.compile(
r"(https?://|/home/|workspaces/|\b\d{2,5}\b|token|secret|cookie|traceback|exception|\.env)",
re.IGNORECASE,
)
def register(api: Any) -> None:
"""Wire the read-only Runtime Health route."""
log = api.logger("svrnty.routes.cortex_os_runtime_health")
api.register_route(ROUTE_PATH, ROUTE_METHOD, _handle_runtime_health)
log.info("cortex os runtime health endpoint registered")
def _handle_runtime_health(handler: Any, parsed: Any) -> bool:
"""Handler signature matches the plugin loader contract."""
if getattr(handler, "command", ROUTE_METHOD) != ROUTE_METHOD:
_write_json(handler, 405, _error_envelope("method_not_allowed", "read only route"))
return True
if getattr(parsed, "query", ""):
_write_json(handler, 400, _error_envelope("query_not_allowed", "query targets are not accepted"))
return True
_write_json(handler, 200, {"ok": True, "result": runtime_health_payload(), "error": None})
return True
def runtime_health_payload(host_signals: dict[str, Any] | None = None) -> dict[str, Any]:
"""Return the host-neutral Runtime Health envelope."""
signals = _summarize_host_signals(host_signals or {})
status = _derive_status(signals)
return {
"contract_id": CONTRACT_ID,
"checked_at": CHECKED_AT,
"status": status,
"readiness": "runtime_not_started",
"summary": _summary_for(status),
"authority": {
"read_only": True,
"runtime_state_mutation": False,
"tool_callable_authority": False,
"mcp_exposure": False,
"profile_exposure_change": False,
"memory_domain_access": False,
"delegated_memory_grant": False,
"sharing": False,
"installer_automation": False,
"product_readiness_claim": False,
},
"signals": signals,
"warnings": ["deterministic_host_surface_inputs_only"],
"errors": [],
"redactions": ["host_specific_values", "raw_paths", "raw_urls", "secrets", "raw_payloads"],
"source_trace": {
"host_adapter": "hermes",
"host_surfaces": ["health", "agent_health", "dashboard_status"],
"live_probe": False,
"raw_payload_passthrough": False,
},
}
def _summarize_host_signals(host_signals: dict[str, Any]) -> list[dict[str, str]]:
names = ["health", "agent_health", "dashboard_status"]
if not host_signals:
return [
{"name": name, "status": "unknown", "detail": "not_probed"}
for name in names
]
return [
{
"name": name,
"status": _clean_status(host_signals.get(name, "unknown")),
"detail": _bounded_text(host_signals.get(f"{name}_detail", "declared_surface")),
}
for name in names
]
def _derive_status(signals: list[dict[str, str]]) -> str:
statuses = {signal["status"] for signal in signals}
if "unavailable" in statuses:
return "unavailable"
if "degraded" in statuses:
return "degraded"
if statuses == {"healthy"}:
return "healthy"
return "unknown"
def _clean_status(value: Any) -> str:
text = str(value).strip().lower()
return text if text in {"healthy", "degraded", "unavailable", "unknown"} else "unknown"
def _summary_for(status: str) -> str:
if status == "healthy":
return "Runtime Health signals are healthy."
if status == "degraded":
return "Runtime Health signals are degraded."
if status == "unavailable":
return "Runtime Health signals are unavailable."
return "Runtime Health has not been live-probed in this slice."
def _bounded_text(value: Any) -> str:
text = str(value).strip().replace("\n", " ")
if not text:
return "redacted"
if _FORBIDDEN_TEXT.search(text):
return "redacted"
return text[:80]
def _error_envelope(code: str, message: str) -> dict[str, Any]:
return {"ok": False, "result": None, "error": {"code": code, "message": message}}
def _write_json(handler: Any, status_code: int, payload: dict[str, Any]) -> None:
body = json.dumps(payload, sort_keys=True).encode("utf-8")
handler.send_response(status_code)
handler.send_header("Content-Type", "application/json; charset=utf-8")
handler.send_header("Content-Length", str(len(body)))
handler.send_header("Cache-Control", "no-store")
handler.end_headers()
handler.wfile.write(body)

View File

@ -0,0 +1,72 @@
.cortex-os-runtime-health {
border: 1px solid rgba(19, 82, 121, 0.22);
border-radius: 8px;
margin: 16px;
max-width: 520px;
padding: 14px 16px;
background: #f7fbff;
color: #18212b;
font: 14px/1.45 system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}
.cortex-os-runtime-health h2 {
margin: 0 0 8px;
font-size: 15px;
font-weight: 700;
}
.cortex-os-runtime-health__badge {
display: inline-flex;
align-items: center;
min-height: 22px;
padding: 0 8px;
border-radius: 999px;
background: #dde8f0;
color: #18212b;
font-size: 12px;
font-weight: 700;
text-transform: uppercase;
}
.cortex-os-runtime-health[data-state="healthy"] .cortex-os-runtime-health__badge {
background: #d8f1df;
color: #0d5b27;
}
.cortex-os-runtime-health[data-state="degraded"] .cortex-os-runtime-health__badge,
.cortex-os-runtime-health[data-state="unknown"] .cortex-os-runtime-health__badge,
.cortex-os-runtime-health[data-state="loading"] .cortex-os-runtime-health__badge {
background: #fff0c9;
color: #6a4a00;
}
.cortex-os-runtime-health[data-state="unavailable"] .cortex-os-runtime-health__badge,
.cortex-os-runtime-health[data-state="error"] .cortex-os-runtime-health__badge,
.cortex-os-runtime-health[data-state="redacted"] .cortex-os-runtime-health__badge {
background: #ffe0dd;
color: #7a1e17;
}
.cortex-os-runtime-health__summary {
margin: 10px 0;
}
.cortex-os-runtime-health__signals {
display: grid;
grid-template-columns: minmax(120px, 1fr) minmax(92px, auto);
gap: 6px 12px;
margin: 0;
}
.cortex-os-runtime-health__signals dt,
.cortex-os-runtime-health__signals dd {
margin: 0;
}
.cortex-os-runtime-health__signals dt {
color: #44515f;
}
.cortex-os-runtime-health__signals dd {
font-weight: 700;
}

View File

@ -0,0 +1,107 @@
(function () {
"use strict";
var endpoint = "/api/cortex-os/runtime-health";
var states = ["healthy", "degraded", "unavailable", "unknown", "loading", "error", "redacted"];
if (window.__cortexOsRuntimeHealthLoaded) {
return;
}
window.__cortexOsRuntimeHealthLoaded = true;
function ensurePanel() {
var panel = document.querySelector("[data-cortex-os-runtime-health]");
if (panel) {
return panel;
}
panel = document.createElement("section");
panel.className = "cortex-os-runtime-health";
panel.setAttribute("data-cortex-os-runtime-health", "true");
panel.setAttribute("data-state", "loading");
var title = document.createElement("h2");
title.textContent = "Cortex OS Runtime Health";
var badge = document.createElement("span");
badge.className = "cortex-os-runtime-health__badge";
badge.setAttribute("data-role", "status");
badge.textContent = "loading";
var summary = document.createElement("p");
summary.className = "cortex-os-runtime-health__summary";
summary.setAttribute("data-role", "summary");
summary.textContent = "Checking Runtime Health.";
var list = document.createElement("dl");
list.className = "cortex-os-runtime-health__signals";
list.setAttribute("data-role", "signals");
panel.appendChild(title);
panel.appendChild(badge);
panel.appendChild(summary);
panel.appendChild(list);
var target = document.querySelector("main") || document.body;
target.appendChild(panel);
return panel;
}
function setState(panel, state, summary, signals) {
var nextState = states.indexOf(state) >= 0 ? state : "unknown";
panel.setAttribute("data-state", nextState);
panel.querySelector("[data-role='status']").textContent = nextState;
panel.querySelector("[data-role='summary']").textContent = summary || "redacted";
renderSignals(panel.querySelector("[data-role='signals']"), signals || []);
}
function renderSignals(list, signals) {
list.textContent = "";
signals.slice(0, 3).forEach(function (signal) {
var name = document.createElement("dt");
var value = document.createElement("dd");
name.textContent = signal.name || "redacted";
value.textContent = signal.status || "unknown";
list.appendChild(name);
list.appendChild(value);
});
}
function renderPayload(panel, payload) {
if (!payload || payload.ok !== true || !payload.result) {
setState(panel, "error", "Runtime Health is unavailable.", []);
return;
}
setState(panel, payload.result.status, payload.result.summary, payload.result.signals);
}
function loadRuntimeHealth() {
var panel = ensurePanel();
setState(panel, "loading", "Checking Runtime Health.", []);
fetch(endpoint, {
method: "GET",
headers: { Accept: "application/json" },
credentials: "same-origin",
cache: "no-store"
})
.then(function (response) {
if (!response.ok) {
throw new Error("runtime-health-unavailable");
}
return response.json();
})
.then(function (payload) {
renderPayload(panel, payload);
})
.catch(function () {
setState(panel, "error", "Runtime Health is unavailable.", []);
});
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", loadRuntimeHealth, { once: true });
} else {
loadRuntimeHealth();
}
})();

View File

@ -62,12 +62,15 @@ def test_loader_register_wires_our_plugin(loader, monkeypatch):
assert ("GET", "/api/vault/status") in loader._ROUTES assert ("GET", "/api/vault/status") in loader._ROUTES
assert ("GET", "/api/umbrella") in loader._ROUTES assert ("GET", "/api/umbrella") in loader._ROUTES
assert ("GET", "/api/umbrella/doc") in loader._ROUTES assert ("GET", "/api/umbrella/doc") in loader._ROUTES
assert ("GET", "/api/cortex-os/runtime-health") in loader._ROUTES
# Static + injected URLs # Static + injected URLs
assert "svrnty" in loader._STATIC assert "svrnty" in loader._STATIC
assert "/plugins/svrnty/app.css" in loader._STYLESHEETS assert "/plugins/svrnty/app.css" in loader._STYLESHEETS
assert "/plugins/svrnty/app.js" in loader._SCRIPTS assert "/plugins/svrnty/app.js" in loader._SCRIPTS
assert "/plugins/svrnty/umbrella_inline.css" in loader._STYLESHEETS assert "/plugins/svrnty/umbrella_inline.css" in loader._STYLESHEETS
assert "/plugins/svrnty/umbrella_inline.js" in loader._SCRIPTS assert "/plugins/svrnty/umbrella_inline.js" in loader._SCRIPTS
assert "/plugins/svrnty/cortex-os/runtime-health/runtime_health.css" in loader._STYLESHEETS
assert "/plugins/svrnty/cortex-os/runtime-health/runtime_health.js" in loader._SCRIPTS
# Audio processor for voice-message transcription # Audio processor for voice-message transcription
assert len(loader._AUDIO_PROCESSORS) == 1 assert len(loader._AUDIO_PROCESSORS) == 1

View File

@ -0,0 +1,87 @@
import io
import json
from types import SimpleNamespace
from unittest.mock import Mock
from routes import cortex_os_runtime_health as route
class FakeHandler:
def __init__(self, command="GET"):
self.command = command
self.status_code = None
self.headers = []
self.wfile = io.BytesIO()
def send_response(self, status_code):
self.status_code = status_code
def send_header(self, name, value):
self.headers.append((name, value))
def end_headers(self):
return None
def payload(self):
return json.loads(self.wfile.getvalue().decode("utf-8"))
def test_register_wires_get_runtime_health_route():
api = Mock()
api.logger.return_value = Mock()
route.register(api)
api.register_route.assert_called_once_with(
"/api/cortex-os/runtime-health",
"GET",
route._handle_runtime_health,
)
def test_get_returns_runtime_health_envelope():
handler = FakeHandler()
assert route._handle_runtime_health(handler, SimpleNamespace(query="")) is True
payload = handler.payload()
assert handler.status_code == 200
assert payload["ok"] is True
assert payload["result"]["contract_id"] == "runtime-health/v0.1"
assert payload["result"]["status"] == "unknown"
assert payload["result"]["source_trace"]["live_probe"] is False
assert payload["result"]["authority"]["runtime_state_mutation"] is False
def test_host_signal_mapping_is_closed_and_redacted():
payload = route.runtime_health_payload(
{
"health": "healthy",
"agent_health": "degraded",
"dashboard_status": "unavailable",
"health_detail": "http://localhost:8000/raw",
"agent_health_detail": "/home/svrnty/private",
"dashboard_status_detail": "token=abc123",
}
)
assert payload["status"] == "unavailable"
assert {signal["detail"] for signal in payload["signals"]} == {"redacted"}
assert payload["source_trace"]["host_surfaces"] == [
"health",
"agent_health",
"dashboard_status",
]
def test_non_get_and_query_targets_are_rejected():
post_handler = FakeHandler(command="POST")
query_handler = FakeHandler()
route._handle_runtime_health(post_handler, SimpleNamespace(query=""))
route._handle_runtime_health(query_handler, SimpleNamespace(query="target=raw"))
assert post_handler.status_code == 405
assert post_handler.payload()["ok"] is False
assert query_handler.status_code == 400
assert query_handler.payload()["error"]["code"] == "query_not_allowed"

View File

@ -0,0 +1,45 @@
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
JS = ROOT / "static" / "cortex-os" / "runtime-health" / "runtime_health.js"
CSS = ROOT / "static" / "cortex-os" / "runtime-health" / "runtime_health.css"
def test_runtime_health_assets_exist_and_render_all_states():
js = JS.read_text(encoding="utf-8")
css = CSS.read_text(encoding="utf-8")
assert "/api/cortex-os/runtime-health" in js
for state in ["healthy", "degraded", "unavailable", "unknown", "loading", "error", "redacted"]:
assert state in js
assert state in css
def test_runtime_health_display_has_no_hidden_write_surface():
js = JS.read_text(encoding="utf-8")
forbidden = [
"localStorage",
"sessionStorage",
"document.cookie",
"<form",
".submit(",
"POST",
"PUT",
"PATCH",
"DELETE",
"/api/gateway/status",
"/api/tools",
"/api/mcp",
]
for snippet in forbidden:
assert snippet not in js
def test_runtime_health_css_is_scoped_to_panel():
css = CSS.read_text(encoding="utf-8")
assert ".cortex-os-runtime-health" in css
assert "body {" not in css
assert "#app" not in css