feat(webui): implement Phase 2 recovery controls & playbooks (#644)
This commit is contained in:
@@ -270,26 +270,47 @@ def _probe_error_card(snapshot: SystemHealthSnapshot) -> str:
|
||||
|
||||
|
||||
def _recovery_card() -> str:
|
||||
"""Sanctioned recovery pointers only — never a manual process kill (#630)."""
|
||||
return (
|
||||
"<section class='health-card'>"
|
||||
"<h3>Recovery</h3>"
|
||||
"<p class='muted'>This dashboard is read-only. Restart and reload "
|
||||
"controls arrive in Phase 2 (#642); until then recovery runs through "
|
||||
"the sanctioned client reconnect / operator restart path.</p>"
|
||||
"<ul class='reasons'>"
|
||||
"<li><a href='/runtime'>Runtime health</a> — active profile, workflow "
|
||||
"hashes, and shell health.</li>"
|
||||
"<li><a href='/sessions'>Runtime and sessions</a> — namespaces, session "
|
||||
"rows, worktree bindings, and contamination markers (#641).</li>"
|
||||
"<li>Reconnect the MCP client from the IDE, then re-run the blocked "
|
||||
"cycle. Never kill the daemon process manually: unmanaged kills are "
|
||||
"recorded as runtime contamination (#630).</li>"
|
||||
"<li>See <code>docs/webui-local-dev.md</code> for the documented "
|
||||
"recovery sequence.</li>"
|
||||
"</ul>"
|
||||
"</section>"
|
||||
)
|
||||
"""Sanctioned recovery controls & playbooks (#644, Phase 2)."""
|
||||
try:
|
||||
from webui import console_recovery
|
||||
diag = console_recovery.diagnose_recovery()
|
||||
status_badge = f"<span class='status-pill {diag.status}'>{diag.status}</span>"
|
||||
playbook_lis = ""
|
||||
for pb in diag.playbooks:
|
||||
elig = "eligible" if pb.eligible else "disabled"
|
||||
playbook_lis += (
|
||||
f"<li><strong>{pb.label}</strong> (<code>{pb.playbook_id}</code>) — "
|
||||
f"<span class='badge {elig}'>{elig}</span>: {pb.description} "
|
||||
f"<em class='muted'>({pb.reason})</em></li>"
|
||||
)
|
||||
reasons_html = ""
|
||||
if diag.reasons:
|
||||
items = "".join(f"<li>{r}</li>" for r in diag.reasons)
|
||||
reasons_html = f"<ul class='reasons'>{items}</ul>"
|
||||
else:
|
||||
reasons_html = "<p class='clean-note'>No recovery actions currently required. Control plane is healthy.</p>"
|
||||
|
||||
return (
|
||||
"<section class='health-card recovery-card'>"
|
||||
f"<h3>Sanctioned Recovery Controls (Phase 2 #644) {status_badge}</h3>"
|
||||
"<p class='muted'>Guided recovery wizard: Diagnose → Preview → Confirm → Verify. "
|
||||
"Reconnect the MCP client from the IDE, then re-run the blocked cycle. "
|
||||
"Never kill the daemon process manually: unmanaged kills are recorded as runtime contamination (#630).</p>"
|
||||
f"{reasons_html}"
|
||||
"<h4>Available Recovery Playbooks</h4>"
|
||||
f"<ul class='playbooks-list'>{playbook_lis}</ul>"
|
||||
"<p class='meta'>APIs: <code>/api/v1/system/recovery/diagnose</code>, "
|
||||
"<code>/api/v1/system/recovery/preview</code>, <code>/api/v1/system/recovery/apply</code>, "
|
||||
"<code>/api/v1/system/recovery/verify</code>.</p>"
|
||||
"</section>"
|
||||
)
|
||||
except Exception as exc:
|
||||
return (
|
||||
"<section class='health-card'>"
|
||||
"<h3>Sanctioned Recovery Controls (Phase 2 #644)</h3>"
|
||||
f"<p class='error'>Recovery diagnostics unavailable: {exc}</p>"
|
||||
"</section>"
|
||||
)
|
||||
|
||||
|
||||
def render_system_health_page(snapshot: SystemHealthSnapshot) -> str:
|
||||
|
||||
Reference in New Issue
Block a user