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:
@@ -0,0 +1,341 @@
|
||||
"""Worked examples for the two-comment workflow (#507)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
HEAD_SHA = "08202f7eaa6d09b2eca8f2960126994a4b22646b"
|
||||
|
||||
EXAMPLES: list[tuple[str, str, str]] = []
|
||||
|
||||
|
||||
def _example(name: str, handoff: str, ledger: str) -> tuple[str, str, str]:
|
||||
return (name, handoff.strip(), ledger.strip())
|
||||
|
||||
|
||||
EXAMPLES.append(
|
||||
_example(
|
||||
"approved_review_posted",
|
||||
f"""
|
||||
[CONTROLLER HANDOFF] PR #487 / Issue #485 — review approved
|
||||
|
||||
Server-side mutation ledger:
|
||||
- gitea_submit_pr_review → APPROVED review posted to Gitea (comment id 6566)
|
||||
|
||||
Blockers:
|
||||
- none
|
||||
""",
|
||||
f"""
|
||||
[THREAD STATE LEDGER] PR #487 — APPROVED review posted to Gitea
|
||||
|
||||
What is true now:
|
||||
- PR state: open
|
||||
- Current head SHA: {HEAD_SHA}
|
||||
- Server-side decision state: APPROVED review posted to Gitea
|
||||
- Local verdict/state: APPROVE verdict prepared locally
|
||||
- Latest known validation: tests passed locally
|
||||
|
||||
What changed:
|
||||
- APPROVED review posted to Gitea at pinned head
|
||||
|
||||
What is blocked:
|
||||
- Blocker classification: no blocker
|
||||
|
||||
Who/what acts next:
|
||||
- Next actor: merger
|
||||
- Required action: merge on explicit operator command
|
||||
- Do not do: re-post APPROVE
|
||||
- Resume from: PR #487 review feedback
|
||||
""",
|
||||
)
|
||||
)
|
||||
|
||||
EXAMPLES.append(
|
||||
_example(
|
||||
"approve_blocked_before_posting",
|
||||
f"""
|
||||
[CONTROLLER HANDOFF] PR #487 / Issue #485 — approve blocked
|
||||
|
||||
Server-side mutation ledger:
|
||||
- none — no server-side state changed
|
||||
|
||||
Blockers:
|
||||
- process/rule blocker: MCP process root still control checkout
|
||||
""",
|
||||
f"""
|
||||
[THREAD STATE LEDGER] PR #487 — APPROVE verdict prepared locally; post blocked
|
||||
|
||||
What is true now:
|
||||
- PR state: open
|
||||
- Current head SHA: {HEAD_SHA}
|
||||
- Server-side decision state: no server-side state changed
|
||||
- Local verdict/state: APPROVE verdict prepared locally
|
||||
- Latest known validation: tests passed locally
|
||||
|
||||
What changed:
|
||||
- Review validation completed locally; APPROVE not posted
|
||||
|
||||
What is blocked:
|
||||
- Blocker classification: process/rule blocker
|
||||
|
||||
Who/what acts next:
|
||||
- Next actor: operator
|
||||
- Required action: reconnect reviewer MCP to branches worktree
|
||||
- Do not do: retry APPROVE from root-bound session
|
||||
- Resume from: lease + mark_final_review_decision + submit
|
||||
""",
|
||||
)
|
||||
)
|
||||
|
||||
EXAMPLES.append(
|
||||
_example(
|
||||
"request_changes_posted",
|
||||
f"""
|
||||
[CONTROLLER HANDOFF] PR #200 — request changes
|
||||
|
||||
Server-side mutation ledger:
|
||||
- gitea_submit_pr_review → REQUEST_CHANGES posted to Gitea
|
||||
|
||||
Blockers:
|
||||
- none
|
||||
""",
|
||||
"""
|
||||
[THREAD STATE LEDGER] PR #200 — REQUEST_CHANGES posted to Gitea
|
||||
|
||||
What is true now:
|
||||
- PR state: open
|
||||
- Server-side decision state: REQUEST_CHANGES posted to Gitea
|
||||
- Local verdict/state: REQUEST_CHANGES prepared locally
|
||||
- Latest known validation: tests passed locally
|
||||
|
||||
What changed:
|
||||
- REQUEST_CHANGES posted to Gitea
|
||||
|
||||
What is blocked:
|
||||
- Blocker classification: no blocker
|
||||
|
||||
Who/what acts next:
|
||||
- Next actor: author
|
||||
- Required action: address review feedback and push fixes
|
||||
- Do not do: merge
|
||||
- Resume from: PR review comments
|
||||
""",
|
||||
)
|
||||
)
|
||||
|
||||
EXAMPLES.append(
|
||||
_example(
|
||||
"merge_completed",
|
||||
f"""
|
||||
[CONTROLLER HANDOFF] PR #487 — merge performed
|
||||
|
||||
Server-side mutation ledger:
|
||||
- gitea_merge_pr → merge performed
|
||||
|
||||
Blockers:
|
||||
- none
|
||||
""",
|
||||
f"""
|
||||
[THREAD STATE LEDGER] PR #487 — merge performed
|
||||
|
||||
What is true now:
|
||||
- PR state: merged
|
||||
- Current head SHA: {HEAD_SHA}
|
||||
- Server-side decision state: merge performed
|
||||
- Local verdict/state: merge not attempted locally before MCP merge
|
||||
- Latest known validation: full suite not rerun
|
||||
|
||||
What changed:
|
||||
- merge performed via gitea_merge_pr
|
||||
|
||||
What is blocked:
|
||||
- Blocker classification: no blocker
|
||||
|
||||
Who/what acts next:
|
||||
- Next actor: reconciler
|
||||
- Required action: verify linked issue closure policy
|
||||
- Do not do: re-merge
|
||||
- Resume from: post-merge cleanup checklist
|
||||
""",
|
||||
)
|
||||
)
|
||||
|
||||
EXAMPLES.append(
|
||||
_example(
|
||||
"merge_blocked",
|
||||
f"""
|
||||
[CONTROLLER HANDOFF] PR #487 — merge not performed
|
||||
|
||||
Server-side mutation ledger:
|
||||
- gitea_merge_pr attempted → merge not performed (stale approval gate)
|
||||
|
||||
Blockers:
|
||||
- stale head blocker: approval not at current head
|
||||
""",
|
||||
f"""
|
||||
[THREAD STATE LEDGER] PR #487 — merge not performed
|
||||
|
||||
What is true now:
|
||||
- PR state: open
|
||||
- Current head SHA: {HEAD_SHA}
|
||||
- Server-side decision state: merge not performed
|
||||
- Local verdict/state: merge not performed
|
||||
- Latest known validation: tests passed locally
|
||||
|
||||
What changed:
|
||||
- merge attempt blocked by stale-head gate
|
||||
|
||||
What is blocked:
|
||||
- Blocker classification: stale head
|
||||
|
||||
Who/what acts next:
|
||||
- Next actor: reviewer
|
||||
- Required action: re-review at current head
|
||||
- Do not do: merge until approval_at_current_head is true
|
||||
- Resume from: gitea_get_pr_review_feedback
|
||||
""",
|
||||
)
|
||||
)
|
||||
|
||||
EXAMPLES.append(
|
||||
_example(
|
||||
"reconciler_closure",
|
||||
f"""
|
||||
[CONTROLLER HANDOFF] PR #99 — reconciler closure
|
||||
|
||||
Server-side mutation ledger:
|
||||
- gitea_reconcile_already_landed_pr → PR closed on Gitea
|
||||
|
||||
Blockers:
|
||||
- none
|
||||
""",
|
||||
"""
|
||||
[THREAD STATE LEDGER] PR #99 — reconciler closure complete
|
||||
|
||||
What is true now:
|
||||
- PR state: closed
|
||||
- Server-side decision state: server-side state changed
|
||||
- Local verdict/state: no server-side state changed locally before MCP close
|
||||
- Latest known validation: ancestor proof passed
|
||||
|
||||
What changed:
|
||||
- superseded PR closed by reconciler
|
||||
|
||||
What is blocked:
|
||||
- Blocker classification: no blocker
|
||||
|
||||
Who/what acts next:
|
||||
- Next actor: controller
|
||||
- Required action: verify queue has no duplicate open PR
|
||||
- Do not do: reopen without canonical PR proof
|
||||
- Resume from: queue inventory
|
||||
""",
|
||||
)
|
||||
)
|
||||
|
||||
EXAMPLES.append(
|
||||
_example(
|
||||
"environment_tooling_blocker",
|
||||
f"""
|
||||
[CONTROLLER HANDOFF] PR #487 — tooling blocker
|
||||
|
||||
Server-side mutation ledger:
|
||||
- none — no server-side state changed
|
||||
|
||||
Blockers:
|
||||
- environment/tooling blocker: gitea-reviewer MCP process root mismatch
|
||||
""",
|
||||
f"""
|
||||
[THREAD STATE LEDGER] PR #487 — lease attempt blocked
|
||||
|
||||
What is true now:
|
||||
- PR state: open
|
||||
- Current head SHA: {HEAD_SHA}
|
||||
- Server-side decision state: no server-side state changed
|
||||
- Local verdict/state: APPROVE verdict prepared locally
|
||||
- Latest known validation: tests passed locally
|
||||
|
||||
What changed:
|
||||
- lease attempt blocked by workspace guard
|
||||
|
||||
What is blocked:
|
||||
- Blocker classification: environment/tooling blocker
|
||||
|
||||
Who/what acts next:
|
||||
- Next actor: operator
|
||||
- Required action: set GITEA_AUTHOR_WORKTREE and /mcp reconnect
|
||||
- Do not do: acquire lease from control checkout
|
||||
- Resume from: gitea_get_runtime_context
|
||||
""",
|
||||
)
|
||||
)
|
||||
|
||||
EXAMPLES.append(
|
||||
_example(
|
||||
"stale_head_blocker",
|
||||
f"""
|
||||
[CONTROLLER HANDOFF] PR #300 — stale head
|
||||
|
||||
Server-side mutation ledger:
|
||||
- none — no server-side state changed
|
||||
|
||||
Blockers:
|
||||
- stale head blocker: live head differs from pinned reviewed head
|
||||
""",
|
||||
"""
|
||||
[THREAD STATE LEDGER] PR #300 — stale approval
|
||||
|
||||
What is true now:
|
||||
- PR state: open
|
||||
- Server-side decision state: no server-side state changed
|
||||
- Local verdict/state: approval_at_current_head is false (stale head)
|
||||
- Latest known validation: full suite not rerun
|
||||
|
||||
What changed:
|
||||
- author pushed after approval
|
||||
|
||||
What is blocked:
|
||||
- Blocker classification: stale head
|
||||
|
||||
Who/what acts next:
|
||||
- Next actor: reviewer
|
||||
- Required action: fresh review at current head
|
||||
- Do not do: merge with stale approval
|
||||
- Resume from: gitea_get_pr_review_feedback
|
||||
""",
|
||||
)
|
||||
)
|
||||
|
||||
EXAMPLES.append(
|
||||
_example(
|
||||
"duplicate_canonicalization_blocker",
|
||||
f"""
|
||||
[CONTROLLER HANDOFF] Issue filing — duplicate found
|
||||
|
||||
Server-side mutation ledger:
|
||||
- gitea_create_issue_comment on #505 with missing acceptance criteria
|
||||
|
||||
Blockers:
|
||||
- duplicate/canonicalization blocker: #505 tracks CTH umbrella
|
||||
""",
|
||||
"""
|
||||
[THREAD STATE LEDGER] Issue #507 — filed as focused split from #505
|
||||
|
||||
What is true now:
|
||||
- Issue state: open
|
||||
- Server-side decision state: server-side state changed
|
||||
- Local verdict/state: no server-side state changed before issue create
|
||||
- Latest known validation: duplicate search completed
|
||||
|
||||
What changed:
|
||||
- new issue #507 created after duplicate assessment
|
||||
|
||||
What is blocked:
|
||||
- Blocker classification: no blocker
|
||||
|
||||
Who/what acts next:
|
||||
- Next actor: author
|
||||
- Required action: implement #507 two-comment validator
|
||||
- Do not do: recreate duplicate CTH issue
|
||||
- Resume from: issue #507 body
|
||||
""",
|
||||
)
|
||||
)
|
||||
Reference in New Issue
Block a user