Child of umbrella #655 (governed MCP restart coordination); builds on the #657 restart-path inventory. Adds a central coordinator that evaluates live control-plane state before a restart and returns a blast-radius impact preview, so operators and the web console (#642/#652) can see what a restart would disrupt before concurrent LLM work is destroyed. Changes - restart_coordinator.py (new) — pure classification: inventory -> impact report DTO (RestartImpactReport/SessionImpact/LeaseImpact). Verdicts: safe / unsafe / override. Never restarts anything; fails closed on an incomplete inventory. - control_plane_db.py — additive ControlPlaneDB.list_sessions() read-only session inventory (the process-level unit a restart kills). - gitea_mcp_server.py — new dry-run MCP tool gitea_request_mcp_restart: gathers sessions/leases/terminal-lock from the #613 DB, calls the coordinator, returns the report. Override authority is read from the environment, never self-asserted (#630/#710 F1 pattern). Apply is gated by a later drain proof (non-goal here). - docs/mcp-restart-coordinator.md + docs/mcp-restart-impact-sample.json — doc and a real dry-run sample report. - docs/mcp-tool-inventory.md — register the new tool (inventory sync). - tests/test_restart_coordinator.py (new) — 15 tests: multi-session fixtures, deny-when-critical-section-open, fail-closed deny, override, terminal lock, stale heartbeat, JSON-serializable DTO, list_sessions. Tests: pytest tests/test_restart_coordinator.py -> 15 passed. Full suite: 13 failed / 4753 passed; all 13 reproduce identically on clean master @ef14622 (0 regressions). The residual test_issue_781 doc-registry failure is a pre-existing baseline gap for gitea_rebind_dirty_same_claimant_author_session (merged #864, undocumented on master) — out of scope for #658. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
149 lines
3.7 KiB
JSON
149 lines
3.7 KiB
JSON
{
|
|
"coordinator_version": "1.0.0-issue-658",
|
|
"evaluated_at": "2026-07-24T06:00:00+00:00",
|
|
"dry_run": true,
|
|
"restart_performed": false,
|
|
"inventory_complete": true,
|
|
"incomplete_reasons": [],
|
|
"verdict": "unsafe",
|
|
"allow_restart": false,
|
|
"override_would_allow": true,
|
|
"operator_override": false,
|
|
"blast_radius": "high",
|
|
"reasons": [
|
|
"live work would be disrupted; restart denied without operator override",
|
|
"1 critical section(s) in flight (active lease with a live owner)"
|
|
],
|
|
"affected_sessions": [
|
|
{
|
|
"session_id": "prgs-author-30988-d6f43c25",
|
|
"role": "author",
|
|
"profile": "prgs-author",
|
|
"pid": 1,
|
|
"status": "active",
|
|
"alive": true,
|
|
"heartbeat_stale": false,
|
|
"is_requester": false,
|
|
"live": true
|
|
},
|
|
{
|
|
"session_id": "prgs-reviewer-4157-0ce9",
|
|
"role": "reviewer",
|
|
"profile": "prgs-reviewer",
|
|
"pid": 1,
|
|
"status": "active",
|
|
"alive": true,
|
|
"heartbeat_stale": false,
|
|
"is_requester": true,
|
|
"live": true
|
|
}
|
|
],
|
|
"affected_leases": [
|
|
{
|
|
"lease_id": "lease-abc",
|
|
"session_id": "prgs-author-30988-d6f43c25",
|
|
"role": "author",
|
|
"phase": "implementing",
|
|
"freshness": "active",
|
|
"work_kind": "issue",
|
|
"work_number": 658,
|
|
"worktree_path": "/repo/branches/feat-issue-658",
|
|
"disruptive": true,
|
|
"is_mutation": true,
|
|
"is_critical_section": true
|
|
},
|
|
{
|
|
"lease_id": "lease-dead",
|
|
"session_id": "prgs-author-91485",
|
|
"role": "author",
|
|
"phase": "allocated",
|
|
"freshness": "stale_dead_process",
|
|
"work_kind": "issue",
|
|
"work_number": 651,
|
|
"worktree_path": null,
|
|
"disruptive": false,
|
|
"is_mutation": false,
|
|
"is_critical_section": false
|
|
}
|
|
],
|
|
"critical_sections": [
|
|
{
|
|
"lease_id": "lease-abc",
|
|
"session_id": "prgs-author-30988-d6f43c25",
|
|
"role": "author",
|
|
"phase": "implementing",
|
|
"freshness": "active",
|
|
"work_kind": "issue",
|
|
"work_number": 658,
|
|
"worktree_path": "/repo/branches/feat-issue-658",
|
|
"disruptive": true,
|
|
"is_mutation": true,
|
|
"is_critical_section": true
|
|
}
|
|
],
|
|
"affected_issues": [
|
|
658
|
|
],
|
|
"affected_prs": [],
|
|
"mutations": [
|
|
{
|
|
"lease_id": "lease-abc",
|
|
"session_id": "prgs-author-30988-d6f43c25",
|
|
"role": "author",
|
|
"phase": "implementing",
|
|
"freshness": "active",
|
|
"work_kind": "issue",
|
|
"work_number": 658,
|
|
"worktree_path": "/repo/branches/feat-issue-658",
|
|
"disruptive": true,
|
|
"is_mutation": true,
|
|
"is_critical_section": true
|
|
}
|
|
],
|
|
"terminal_lock": null,
|
|
"ack_state": {
|
|
"prgs-author-30988-d6f43c25": "pending"
|
|
},
|
|
"prior_recovery_attempts": [
|
|
{
|
|
"kind": "client_reconnect",
|
|
"at": "2026-07-24T06:00:00+00:00",
|
|
"outcome": "insufficient"
|
|
}
|
|
],
|
|
"counts": {
|
|
"sessions_total": 2,
|
|
"sessions_live_other": 1,
|
|
"leases_total": 2,
|
|
"leases_disruptive": 1,
|
|
"critical_sections": 1,
|
|
"mutations": 1,
|
|
"affected_issues": 1,
|
|
"affected_prs": 0,
|
|
"prior_recovery_attempts": 1
|
|
},
|
|
"audit_record": {
|
|
"event": "restart_impact_evaluated",
|
|
"coordinator_version": "1.0.0-issue-658",
|
|
"evaluated_at": "2026-07-24T06:00:00+00:00",
|
|
"dry_run": true,
|
|
"operator_override": false,
|
|
"requesting_session_id": "prgs-reviewer-4157-0ce9",
|
|
"inventory_complete": true,
|
|
"verdict": "unsafe",
|
|
"allow_restart": false,
|
|
"blast_radius": "high",
|
|
"counts": {
|
|
"sessions_total": 2,
|
|
"sessions_live_other": 1,
|
|
"leases_total": 2,
|
|
"leases_disruptive": 1,
|
|
"critical_sections": 1,
|
|
"mutations": 1,
|
|
"affected_issues": 1,
|
|
"affected_prs": 0,
|
|
"prior_recovery_attempts": 1
|
|
}
|
|
}
|
|
}
|