refactor(guard): decompose handle_status into status blockers, phase metadata, and skill summary helpers

This commit is contained in:
Violin
2026-07-25 14:25:12 +01:00
parent 3d93298150
commit fb27692e41
@@ -40,32 +40,18 @@ def handle_target(a, **kwargs):
return _json("ok", value=value)
@_serialise_errors
def handle_status(a, **kwargs):
if not str(a.get("eng_dir") or "").strip():
raise ValueError("eng_dir is required")
eng_dir = state.resolve_eng_dir(a.get("eng_dir", ""))
bootstrap_result = bootstrap.check_bootstrap(eng_dir, auto_repair=False)
tasks = ptt.parse_ptt(eng_dir / "state" / "ptt.md")
ptt_result = ptt.validate_ptt(tasks)
active = ptt.find_active_task(tasks) if not ptt_result.errors else None
current_phase = active.phase if active else None
pending = state.get_pending_sync(eng_dir)
credit_limit = int(
(pending or {}).get("credit_limit") or state.sync_credit_limit(current_phase)
)
credit = state.sync_credit_remaining(eng_dir, current_phase)
counts = state.read_counts(eng_dir)
session_id = state.resolve_session_id(eng_dir)
marker = eng_dir / "state" / f".skill-loaded-{session_id}" if session_id else None
legacy_marker = str(marker) if marker and marker.is_file() else None
binding, binding_reason = (
binding_readiness(eng_dir, task_id=active.id, session_id=session_id)
if active and session_id
else (None, "no active task or session")
)
route = resolve_skill_route(current_phase or "RECON")
def _evaluate_status_blockers(
eng_dir: Path,
bootstrap_result,
ptt_result,
active,
session_id: str | None,
binding_reason: str | None,
pending: dict | None,
credit: int,
current_phase: str | None,
) -> list[dict]:
"""Evaluate and collect all current engagement workflow blockers."""
blockers = [
{
"code": "bootstrap",
@@ -125,8 +111,12 @@ def handle_status(a, **kwargs):
"next_action": "Review engagement state, then call violin_heartbeat_done",
}
)
return blockers
phase_requirements = {
def _build_phase_requirements() -> dict:
"""Build requirement mapping for all phases."""
return {
phase.value: {
"ptt_phase": "EXPLOITATION" if phase is Phase.POST_EXPLOITATION else phase.value,
"hypothesis_required": requires_hypothesis(phase),
@@ -135,10 +125,75 @@ def handle_status(a, **kwargs):
}
for phase in Phase
}
def _build_status_skill_summary(
session_id: str | None,
binding,
binding_reason: str | None,
route,
legacy_marker: str | None,
) -> dict:
"""Format skill binding and route summary dictionary."""
return {
"session_id": session_id or None,
"binding": binding,
"binding_ready": binding_reason is None,
"binding_reason": binding_reason,
"route_candidates": list(route.allowed),
"legacy_marker": legacy_marker,
"legacy_marker_status": "obsolete" if legacy_marker else "absent",
"recovery": (
"Select a route candidate with violin_record_ptt; repeat after the preparation "
"result returns to the model"
),
}
@_serialise_errors
def handle_status(a, **kwargs):
if not str(a.get("eng_dir") or "").strip():
raise ValueError("eng_dir is required")
eng_dir = state.resolve_eng_dir(a.get("eng_dir", ""))
bootstrap_result = bootstrap.check_bootstrap(eng_dir, auto_repair=False)
tasks = ptt.parse_ptt(eng_dir / "state" / "ptt.md")
ptt_result = ptt.validate_ptt(tasks)
active = ptt.find_active_task(tasks) if not ptt_result.errors else None
current_phase = active.phase if active else None
pending = state.get_pending_sync(eng_dir)
credit_limit = int(
(pending or {}).get("credit_limit") or state.sync_credit_limit(current_phase)
)
credit = state.sync_credit_remaining(eng_dir, current_phase)
counts = state.read_counts(eng_dir)
session_id = state.resolve_session_id(eng_dir)
marker = eng_dir / "state" / f".skill-loaded-{session_id}" if session_id else None
legacy_marker = str(marker) if marker and marker.is_file() else None
binding, binding_reason = (
binding_readiness(eng_dir, task_id=active.id, session_id=session_id)
if active and session_id
else (None, "no active task or session")
)
route = resolve_skill_route(current_phase or "RECON")
blockers = _evaluate_status_blockers(
eng_dir,
bootstrap_result,
ptt_result,
active,
session_id,
binding_reason,
pending,
credit,
current_phase,
)
phase_requirements = _build_phase_requirements()
pending_commands = [
{"command": item.get("command", ""), "required_phase": item.get("phase", "")}
for item in (pending or {}).get("commands") or []
]
heartbeat_pending = state.has_heartbeat_pending(eng_dir)
return _json(
"blocked" if blockers else "ok",
engagement=str(eng_dir),
@@ -165,18 +220,8 @@ def handle_status(a, **kwargs):
heartbeat_reason=state.get_heartbeat_reason(eng_dir),
command_count=counts["commands"],
message_count=counts["messages"],
skill={
"session_id": session_id or None,
"binding": binding,
"binding_ready": binding_reason is None,
"binding_reason": binding_reason,
"route_candidates": list(route.allowed),
"legacy_marker": legacy_marker,
"legacy_marker_status": "obsolete" if legacy_marker else "absent",
"recovery": (
"Select a route candidate with violin_record_ptt; repeat after the preparation "
"result returns to the model"
),
},
skill=_build_status_skill_summary(
session_id, binding, binding_reason, route, legacy_marker
),
runtime=runtime_backend.runtime_readiness(eng_dir),
)