- Distinguish client_namespace vs offline_spawn probe sources; only IDE client probes prove namespace health and feed mutation gates. - Record assessments in session; gate gitea_submit_pr_review and gitea_merge_pr when client-namespace health is unhealthy/non-proven. - Mark test_mcp_conn.py offline-only; align recovery docs to client reconnect (no PID-kill/config-touch as canonical recovery). - Rebase onto current master so #590 ledger isolation keeps TestMergePR.test_unknown_profile_blocks green.
This commit is contained in:
+54
-5
@@ -2825,6 +2825,35 @@ _TERMINAL_REVIEW_ACTIONS = frozenset({"approve", "request_changes"})
|
||||
# remote + profile identity with TTL.
|
||||
_REVIEW_DECISION_LOCK: dict | None = None
|
||||
|
||||
# Session-scoped live MCP namespace health assessments (#543).
|
||||
# Keyed by namespace name. Only client_namespace entries authorize mutations.
|
||||
_LIVE_NAMESPACE_HEALTH: dict[str, dict] = {}
|
||||
|
||||
|
||||
def _record_live_namespace_health(assessment: dict | None) -> None:
|
||||
"""Store a namespace health assessment for mutation gates."""
|
||||
if not isinstance(assessment, dict):
|
||||
return
|
||||
ns = str(assessment.get("namespace") or "").strip()
|
||||
if not ns:
|
||||
return
|
||||
_LIVE_NAMESPACE_HEALTH[ns] = {
|
||||
"namespace": ns,
|
||||
"healthy": bool(assessment.get("healthy")),
|
||||
"ide_namespace_proven": bool(assessment.get("ide_namespace_proven")),
|
||||
"probe_source": assessment.get("probe_source"),
|
||||
"blocks_merge_workflow": bool(assessment.get("blocks_merge_workflow")),
|
||||
"error_type": assessment.get("error_type"),
|
||||
"required_tool": assessment.get("required_tool"),
|
||||
}
|
||||
|
||||
|
||||
def _live_namespace_health_gate(task: str) -> list[str]:
|
||||
"""Fail closed on recorded broken/non-client IDE namespace health (#543)."""
|
||||
return mcp_namespace_health.mutation_gate_from_session(
|
||||
task, _LIVE_NAMESPACE_HEALTH
|
||||
)
|
||||
|
||||
|
||||
def _decision_lock_binding(lock: dict | None = None) -> dict:
|
||||
"""Resolve key fields for durable decision-lock storage."""
|
||||
@@ -3515,6 +3544,11 @@ def _evaluate_pr_review_submission(
|
||||
reasons.extend(workflow_blockers)
|
||||
reasons.extend(review_workflow_load.recovery_handoff_without_replay())
|
||||
return result
|
||||
if live:
|
||||
ns_gate = _live_namespace_health_gate("review_pr")
|
||||
if ns_gate:
|
||||
reasons.extend(ns_gate)
|
||||
return result
|
||||
|
||||
if action not in _REVIEW_ACTIONS:
|
||||
reasons.append(
|
||||
@@ -4616,6 +4650,12 @@ def gitea_merge_pr(
|
||||
reasons.extend(review_workflow_load.recovery_handoff_without_replay())
|
||||
return result
|
||||
|
||||
# Gate 0b — recorded live MCP namespace health must not be broken (#543).
|
||||
ns_gate = _live_namespace_health_gate("merge_pr")
|
||||
if ns_gate:
|
||||
reasons.extend(ns_gate)
|
||||
return result
|
||||
|
||||
# Gate 1 — valid merge method (no API call on a bad method).
|
||||
if do not in _MERGE_METHODS:
|
||||
reasons.append(
|
||||
@@ -9203,16 +9243,22 @@ def gitea_assess_mcp_namespace_health(
|
||||
config_path: str | None = None,
|
||||
profile: str | None = None,
|
||||
configured: bool = True,
|
||||
probe_source: str | None = None,
|
||||
) -> dict:
|
||||
"""Read-only: classify live MCP namespace health for required Gitea tools.
|
||||
"""Classify MCP namespace health for required Gitea tools (#543).
|
||||
|
||||
Static FastMCP registration is not enough to prove a namespace works: IDE
|
||||
clients can keep a registered tool list while live calls fail with
|
||||
``client is closing: EOF``. This diagnostic accepts static registration
|
||||
evidence plus live invocation evidence and returns fail-closed guidance for
|
||||
reviewer/merger workflows.
|
||||
``client is closing: EOF``. Pass live IDE invocation evidence with
|
||||
``probe_source='client_namespace'``. Offline subprocess probes
|
||||
(``test_mcp_conn.py``) must use ``probe_source='offline_spawn'`` and never
|
||||
count as IDE proof.
|
||||
|
||||
Assessments are recorded in the session so live
|
||||
``gitea_submit_pr_review`` / ``gitea_merge_pr`` can fail closed when a
|
||||
client-namespace probe reported unhealthy.
|
||||
"""
|
||||
return mcp_namespace_health.classify_namespace_probe(
|
||||
result = mcp_namespace_health.classify_namespace_probe(
|
||||
namespace,
|
||||
required_tool=required_tool,
|
||||
registered_tools=registered_tools,
|
||||
@@ -9221,7 +9267,10 @@ def gitea_assess_mcp_namespace_health(
|
||||
config_path=config_path,
|
||||
profile=profile,
|
||||
configured=configured,
|
||||
probe_source=probe_source,
|
||||
)
|
||||
_record_live_namespace_health(result)
|
||||
return result
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
|
||||
Reference in New Issue
Block a user