"""HTML views for the Runtime and session view (Phase 1, #641).
Read-only composition of runtime health (#430) and inventory sessions /
namespaces / worktrees (#636). Surfaces stale and contamination indicators
when detectable. Recovery links point only at sanctioned reconnect/restart
docs — never at manual process kill (#630).
"""
from __future__ import annotations
from html import escape
from typing import Sequence
from webui.inventory import STATUS_OK
from webui.layout import render_page
from webui.session_loader import (
ContaminationMarker,
SessionRow,
SessionViewSnapshot,
)
def _badge(text: str, css: str) -> str:
return f'{escape(text)}'
def _flags(flags: Sequence[str], *, css: str) -> str:
if not flags:
return '—'
return " ".join(_badge(flag, css) for flag in flags)
def _unproven_cell(status: str) -> str:
"""Render an ownership column whose backing inventory section failed to read.
Never "none" and never "unbound": an unreadable source proves nothing about
ownership, and claiming otherwise is the exact failure the
``ownership_authority_complete`` invariant exists to prevent.
"""
return (
f'unknown (inventory {escape(status)})
'
f'{_badge("authority unproven", "badge-health-degraded")}'
)
def _runtime_banner(snapshot: SessionViewSnapshot) -> str:
runtime = snapshot.runtime
stale = runtime.stale_runtime_warning
stale_html = ""
if stale:
stale_html = (
f'
'
f"Stale runtime: {escape(stale)}
"
)
identity = runtime.authenticated_username or "unresolved"
if runtime.identity_error:
identity = f"unresolved ({runtime.identity_error})"
return f"""
Runtime context
| Profile | {escape(runtime.profile_name)} |
| Role kind | {escape(runtime.role_kind)} |
| Identity | {escape(str(identity))} |
| Remote / host |
{escape(runtime.remote)} · {escape(runtime.host)} |
| Local HEAD |
{escape(runtime.repo_sha or "unknown")} |
| Remote master |
{escape(runtime.remote_master_sha or "unknown")} |
| Commits behind |
{escape(str(runtime.commits_behind_master if runtime.commits_behind_master is not None else "unknown"))} |
Full runtime detail: /runtime ·
Inventory API: /api/v1/inventory
{stale_html}
"""
def _summary_bar(snapshot: SessionViewSnapshot) -> str:
total = len(snapshot.sessions)
stale = snapshot.stale_session_count
contaminated = snapshot.contaminated_session_count
active_markers = len(snapshot.active_contamination)
inv_status = snapshot.inventory.status
authority_complete = snapshot.ownership_authority_complete
authority_text = "complete" if authority_complete else "incomplete"
authority_css = "badge-health-ok" if authority_complete else "badge-health-degraded"
return f"""
Sessions: {total}
Stale: {stale}
Contaminated: {contaminated}
Active markers: {active_markers}
Inventory: {escape(inv_status)}
Ownership authority: {authority_text}
"""
def _ownership_caveat(snapshot: SessionViewSnapshot) -> str:
"""Name the unreadable ownership sections, or render nothing when all read."""
if snapshot.ownership_authority_complete:
return ""
degraded = ", ".join(
f"{name}: {status}"
for name, status in snapshot.ownership_section_status.items()
if status != STATUS_OK
)
return (
''
"Ownership authority incomplete: "
f"{escape(degraded)}. Columns marked unknown could not be read. "
"No session below may be treated as holding no lease or no worktree "
"binding — absence of evidence is not evidence of absence.
"
)
def _render_session_row(row: SessionRow) -> str:
pid = "—" if row.pid is None else str(row.pid)
pid_alive = "—" if row.pid_alive is None else ("alive" if row.pid_alive else "dead")
pid_css = (
"badge-health-ok"
if row.pid_alive is True
else ("badge-blocked" if row.pid_alive is False else "badge-health-skipped")
)
# An empty tuple only means "holds none" when its source read cleanly.
if row.lease_authority != STATUS_OK:
lease_cell = _unproven_cell(row.lease_authority)
else:
leases = (
", ".join(f"{escape(lid)}" for lid in row.lease_ids)
if row.lease_ids
else 'none'
)
work = (
", ".join(escape(ref) for ref in row.work_refs)
if row.work_refs
else '—'
)
lease_cell = (
f'{leases}{work}
'
)
if row.worktree_authority != STATUS_OK:
worktrees = _unproven_cell(row.worktree_authority)
else:
worktrees = (
"
".join(f"{escape(path)}" for path in row.worktree_paths)
if row.worktree_paths
else 'unbound'
)
return f"""
{escape(row.session_id)} |
{escape(str(row.role or "—"))} / {escape(str(row.profile or "—"))}
ns: {escape(str(row.namespace or "—"))}
|
{escape(pid)}
{_badge(pid_alive, pid_css)}
|
{escape(str(row.status or "—"))} {escape(str(row.last_heartbeat_at or ""))} |
{lease_cell} |
{worktrees} |
{_flags(row.stale_flags, css="badge-stale")} |
{_flags(row.contamination_flags, css="badge-blocked")} |
"""
def _sessions_table(snapshot: SessionViewSnapshot) -> str:
rows: Sequence[SessionRow] = snapshot.sessions
if not rows:
sessions_status = snapshot.ownership_section_status.get("sessions", STATUS_OK)
if sessions_status != STATUS_OK:
return (
'Session inventory is '
f"{escape(sessions_status)} — the session list "
"could not be read. This is not evidence that no sessions "
"exist.
"
)
return (
'No control-plane sessions recorded. Inventory may '
"be unavailable, or no MCP workers have registered yet.
"
)
body = "".join(_render_session_row(row) for row in rows)
return f"""
| Session |
Role / profile / namespace |
PID |
Status |
Leases / work |
Worktree binding |
Stale |
Contamination |
{body}
"""
def _namespaces_section(snapshot: SessionViewSnapshot) -> str:
section = snapshot.inventory.section("namespaces")
if section is None:
return (
'Namespaces
'
'
Namespaces section not loaded.
'
)
if not section.ok:
return f"""
Namespaces {_badge(section.status, "badge-health-degraded")}
{escape(section.reason or "unavailable")}
"""
rows = []
for item in section.items:
caps = item.get("capability_summary") or {}
cap_bits = ", ".join(
name for name, ok in sorted(caps.items()) if ok
) or "none"
rows.append(
""
f"{escape(str(item.get('mcp_namespace') or '—'))} | "
f"{escape(str(item.get('profile_name') or '—'))} | "
f"{escape(str(item.get('role') or '—'))} | "
f"{escape(cap_bits)} | "
f"{'yes' if item.get('active') else 'no'} | "
"
"
)
reason = (
f'{escape(section.reason)}
'
if section.reason
else ""
)
return f"""
Namespaces / capabilities
{reason}
| Namespace |
Profile |
Role |
Capabilities |
Active in process |
{"".join(rows) if rows else '| No namespace rows. |
'}
"""
def _worktrees_section(snapshot: SessionViewSnapshot) -> str:
section = snapshot.inventory.section("worktrees")
if section is None:
return ""
if not section.ok and not section.items:
return f"""
Worktrees {_badge(section.status, "badge-health-degraded")}
{escape(section.reason or "unavailable")}
"""
rows = []
for item in section.items[:50]:
rows.append(
""
f"{escape(str(item.get('rel_path') or item.get('path') or '—'))} | "
f"{escape(str(item.get('branch') or '—'))} | "
f"{escape(str(item.get('classification') or '—'))} | "
f"{'yes' if item.get('registered_worktree') else 'no'} | "
f"{'dirty' if item.get('dirty') else 'clean'} | "
"
"
)
more = ""
if len(section.items) > 50:
more = f'Showing 50 of {len(section.items)}. Full list: /worktrees.
'
return f"""
Worktree bindings
Registered issue worktrees under branches/. Hygiene detail: /worktrees.
| Path |
Branch |
Classification |
Registered |
State |
{"".join(rows) if rows else '| No worktrees recorded. |
'}
{more}
"""
def _contamination_section(markers: Sequence[ContaminationMarker]) -> str:
if not markers:
return (
'Contamination markers
'
'
No contamination kinds inspected.
'
)
rows = []
for marker in markers:
active = marker.to_dict()["active"]
status = "ACTIVE" if active else ("cleared" if marker.cleared else "absent")
css = "badge-blocked" if active else "badge-health-ok"
rows.append(
""
f"{escape(marker.kind)} | "
f"{_badge(status, css)} | "
f"{escape(marker.reason_class or '—')} | "
f"{escape(marker.session_id or '—')} | "
f"{escape(marker.command_summary or marker.summary)} | "
"
"
)
return f"""
Contamination markers (#630 / #671)
Durable markers only — never silent when present. Clearance is reconciler-only.
| Kind |
State |
Reason class |
Session |
Summary |
{"".join(rows)}
"""
def _recovery_section(snapshot: SessionViewSnapshot) -> str:
items = []
for doc in snapshot.recovery_docs:
items.append(
""
f"{escape(doc['path'])} — "
f"{escape(doc['label'])}: {escape(doc['note'])}"
""
)
return f"""
Sanctioned recovery (read-only)
This view does not restart, kill, or take over sessions.
Manual pkill / kill of MCP daemons is contamination (#630), not recovery.
{"".join(items)}
- Prefer IDE/client reconnect (
/mcp reconnect) or an operator-owned restart recorded in the restart inventory.
"""
def render_sessions_page(snapshot: SessionViewSnapshot) -> str:
"""Render the full HTML body for the runtime/session view."""
error_block = ""
if snapshot.fetch_error:
error_block = (
f'Partial load: '
f"{escape(snapshot.fetch_error)}
"
)
if snapshot.runtime.fetch_error:
error_block += (
f'Runtime note: '
f"{escape(snapshot.runtime.fetch_error)}
"
)
body = f"""
{error_block}
{_runtime_banner(snapshot)}
{_summary_bar(snapshot)}
Sessions
Control-plane sessions correlated with leases and worktree bindings.
Stale and contamination flags are fail-soft: absence of a marker is not proof of cleanliness when inventory is degraded.
Lease and worktree columns read unknown (inventory …) when their source could not be loaded.
{_ownership_caveat(snapshot)}
{_sessions_table(snapshot)}
{_namespaces_section(snapshot)}
{_worktrees_section(snapshot)}
{_contamination_section(snapshot.contamination_markers)}
{_recovery_section(snapshot)}
"""
return render_page(title="Sessions", body_html=f"""Runtime and sessions
Phase 1 read-only view (#641). Combines runtime health (#430) with
unified inventory sessions/namespaces/worktrees (#636). No restart or session-takeover controls.
{body}""")