feat: add Controller Handoff + Thread State Ledger validation (#507)
Introduce thread_state_ledger_validator for the mandatory two-comment workflow: tagged [CONTROLLER HANDOFF] paired with [THREAD STATE LEDGER]. Wire validation into final_report_validator and gitea_create_issue_comment, add runbook docs, worked examples, and tests. Closes #507 Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This commit is contained in:
@@ -548,6 +548,7 @@ import reconciliation_workflow # noqa: E402
|
||||
import review_merge_state_machine # noqa: E402
|
||||
import pr_work_lease # noqa: E402
|
||||
import native_mcp_preference # noqa: E402
|
||||
import thread_state_ledger_validator # noqa: E402
|
||||
|
||||
|
||||
# Keyed issue-lock storage (#443): per remote/org/repo/issue files under
|
||||
@@ -5217,6 +5218,23 @@ def gitea_list_issue_comments(
|
||||
return {"success": True, "issue_number": issue_number, "comments": out}
|
||||
|
||||
|
||||
def _two_comment_workflow_comment_gate(body: str) -> list[str]:
|
||||
"""Fail closed on invalid tagged workflow comments (#507)."""
|
||||
text = body or ""
|
||||
reasons: list[str] = []
|
||||
if thread_state_ledger_validator.has_controller_handoff_marker(text):
|
||||
result = thread_state_ledger_validator.assess_controller_handoff_comment(
|
||||
text
|
||||
)
|
||||
reasons.extend(result.get("reasons") or [])
|
||||
if thread_state_ledger_validator.has_thread_state_ledger_marker(text):
|
||||
result = thread_state_ledger_validator.assess_thread_state_ledger_comment(
|
||||
text
|
||||
)
|
||||
reasons.extend(result.get("reasons") or [])
|
||||
return reasons
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
def gitea_create_issue_comment(
|
||||
issue_number: int,
|
||||
@@ -5259,6 +5277,8 @@ def gitea_create_issue_comment(
|
||||
reasons = list(gate_reasons)
|
||||
if not (body or "").strip():
|
||||
reasons.append("comment body must be a non-empty string")
|
||||
if not reasons:
|
||||
reasons.extend(_two_comment_workflow_comment_gate(body))
|
||||
if reasons:
|
||||
blocked = {"success": False, "performed": False,
|
||||
"issue_number": issue_number, "reasons": reasons}
|
||||
|
||||
Reference in New Issue
Block a user