Merge pull request 'feat: add dedicated reconciliation controller-handoff schema (Closes #303)' (#373) from feat/issue-303-reconciliation-handoff into master

This commit was merged in pull request #373.
This commit is contained in:
2026-07-07 08:42:40 -05:00
2 changed files with 277 additions and 0 deletions
+115
View File
@@ -1242,6 +1242,121 @@ def assess_workspace_mutation_consistency(report_text, observed_commands=None):
}
# ── Reconciliation controller-handoff schema (Issue #303) ────────────────────
#
# Already-landed PR reconciliation is neither author issue work nor
# reviewer merge work; its handoff needs its own schema. Stale
# author/reviewer fields fail validation, and observed git ref
# mutations (fetch) must be reported under the precise category.
RECONCILIATION_ELIGIBILITY_CLASS = "ALREADY_LANDED_RECONCILE_REQUIRED"
RECONCILIATION_HANDOFF_FIELDS = (
("Task", ("task",)),
("Repo", ("repo",)),
("Role/profile", ("role/profile", "role", "profile")),
("Identity", ("identity",)),
("Selected PR", ("selected pr",)),
("PR live state", ("pr live state",)),
("Candidate head SHA", ("candidate head sha",)),
("Target branch", ("target branch",)),
("Target branch SHA", ("target branch sha",)),
("Ancestor proof", ("ancestor proof",)),
("Linked issue", ("linked issue",)),
("Linked issue live status", ("linked issue live status",)),
("Eligibility class", ("eligibility class",)),
("Capabilities proven", ("capabilities proven",)),
("Missing capabilities", ("missing capabilities",)),
("PR comments posted", ("pr comments posted",)),
("Issue comments posted", ("issue comments posted",)),
("PRs closed", ("prs closed",)),
("Issues closed", ("issues closed",)),
("Git ref mutations", ("git ref mutations",)),
("Worktree mutations", ("worktree mutations",)),
("MCP/Gitea mutations", ("mcp/gitea mutations",)),
("External-state mutations", ("external-state mutations",)),
("Read-only diagnostics", ("read-only diagnostics",)),
("Blocker", ("blocker",)),
("Safe next action", ("safe next action",)),
("No review/merge confirmation", ("no review/merge",)),
)
RECONCILIATION_FORBIDDEN_FIELDS = (
"pr number opened",
"pinned reviewed head",
"scratch worktree used",
"workspace mutations",
"issue lock proof",
"claim/comment status",
)
def assess_reconciliation_handoff(report_text, observed_commands=None):
"""#303: validate the dedicated reconciliation handoff schema.
Requires a Controller Handoff section carrying every reconciliation
field, ``Eligibility class: ALREADY_LANDED_RECONCILE_REQUIRED``, and
no stale author/reviewer fields. *observed_commands* is the git
command log; an observed fetch/pull must be reported under ``Git ref
mutations`` (never claimed none).
Returns {'complete', 'downgraded', 'reasons'}; fails closed.
"""
section = _handoff_section_lines(report_text)
if section is None:
return {
"complete": False,
"downgraded": True,
"reasons": [
"reconciliation report has no section titled exactly "
f"'{HANDOFF_HEADING}'"
],
}
fields = {}
for line in section:
stripped = line.strip().lstrip("-*").strip()
if ":" in stripped:
k, v = stripped.split(":", 1)
fields[k.strip().lower()] = v.strip()
reasons = []
for name, aliases in RECONCILIATION_HANDOFF_FIELDS:
if not any(label.startswith(alias)
for label in fields for alias in aliases):
reasons.append(
f"reconciliation handoff missing required field: {name}"
)
for stale in RECONCILIATION_FORBIDDEN_FIELDS:
if any(label.startswith(stale) for label in fields):
reasons.append(
f"reconciliation handoff must not include stale field "
f"'{stale}'"
)
eligibility = fields.get("eligibility class", "")
if eligibility and RECONCILIATION_ELIGIBILITY_CLASS not in eligibility.upper():
reasons.append(
"reconciliation handoff eligibility class must be "
f"'{RECONCILIATION_ELIGIBILITY_CLASS}' (got '{eligibility}')"
)
_, ref_cmds = _classify_git_commands(observed_commands)
if ref_cmds and _field_claims_none(fields, "git ref mutations"):
reasons.append(
"observed git ref mutation not reported under 'Git ref "
f"mutations': {ref_cmds[0]}"
)
return {
"complete": not reasons,
"downgraded": bool(reasons),
"reasons": reasons,
}
# ── Already-landed report wording (Issue #298) ───────────────────────────────
#
# An already-landed PR is reconciliation-only: it must never be called
+162
View File
@@ -0,0 +1,162 @@
"""Tests for the dedicated reconciliation controller-handoff schema (#303).
Already-landed PR reconciliation runs must emit the reconciliation
schema, not stale author/reviewer handoff fields; observed git ref
mutations (fetch) must be reported, and legacy fields fail validation.
"""
import sys
import unittest
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
from review_proofs import assess_reconciliation_handoff # noqa: E402
def _good_handoff(**overrides):
fields = {
"Task": "Reconcile already-landed open PRs",
"Repo": "prgs/Scaled-Tech-Consulting/Gitea-Tools",
"Role/profile": "prgs-reconciler",
"Identity": "jcwalker3",
"Selected PR": "278",
"PR live state": "open",
"Candidate head SHA": "2a544b7",
"Target branch": "master",
"Target branch SHA": "fa0cb12",
"Ancestor proof": "candidate head is ancestor of target branch SHA",
"Linked issue": "263",
"Linked issue live status": "open (fetched live this session)",
"Eligibility class": "ALREADY_LANDED_RECONCILE_REQUIRED",
"Capabilities proven": "gitea.pr.comment",
"Missing capabilities": "gitea.pr.close",
"PR comments posted": "1 (PR #278 reconciliation notice)",
"Issue comments posted": "none",
"PRs closed": "none",
"Issues closed": "none",
"Git ref mutations": "git fetch prgs master",
"Worktree mutations": "none",
"MCP/Gitea mutations": "PR comment on #278",
"External-state mutations": "none",
"Read-only diagnostics": "gitea_view_pr 278, gitea_view_issue 263",
"Blocker": "PR close capability missing",
"Safe next action": "operator closes PR #278 or grants close capability",
"No review/merge confirmation": "no review or merge mutations performed",
}
fields.update(overrides)
lines = ["Controller Handoff"]
lines += [f"- {k}: {v}" for k, v in fields.items() if v is not None]
return "\n".join(lines) + "\n"
class TestReconciliationSchemaPasses(unittest.TestCase):
def test_blocked_reconciliation_passes(self):
result = assess_reconciliation_handoff(
_good_handoff(),
observed_commands=["git fetch prgs master"],
)
self.assertTrue(result["complete"])
self.assertEqual(result["reasons"], [])
def test_successful_pr_close_passes(self):
report = _good_handoff(**{
"Missing capabilities": "none",
"PRs closed": "PR #278",
"Blocker": "none",
"Safe next action": "none; reconciliation complete",
})
result = assess_reconciliation_handoff(
report, observed_commands=["git fetch prgs master"]
)
self.assertTrue(result["complete"])
def test_comment_only_reconciliation_passes(self):
report = _good_handoff(**{
"Git ref mutations": "none",
})
result = assess_reconciliation_handoff(report, observed_commands=[])
self.assertTrue(result["complete"])
def test_noop_already_closed_passes(self):
report = _good_handoff(**{
"PR live state": "closed",
"PR comments posted": "none",
"MCP/Gitea mutations": "none",
"Git ref mutations": "none",
"Blocker": "none",
"Safe next action": "none; PR already closed",
})
result = assess_reconciliation_handoff(report, observed_commands=[])
self.assertTrue(result["complete"])
class TestSchemaViolationsFail(unittest.TestCase):
def test_missing_linked_issue_proof_fails(self):
report = _good_handoff(**{"Linked issue live status": None})
result = assess_reconciliation_handoff(report, observed_commands=[])
self.assertFalse(result["complete"])
self.assertTrue(
any("linked issue live status" in r.lower()
for r in result["reasons"])
)
def test_wrong_eligibility_class_fails(self):
report = _good_handoff(**{"Eligibility class": "REVIEW_ELIGIBLE"})
result = assess_reconciliation_handoff(report, observed_commands=[])
self.assertFalse(result["complete"])
def test_missing_handoff_section_fails(self):
result = assess_reconciliation_handoff(
"no handoff here", observed_commands=[]
)
self.assertFalse(result["complete"])
class TestStaleFieldsFail(unittest.TestCase):
def test_pr_number_opened_fails(self):
report = _good_handoff() + "- PR number opened: 278\n"
result = assess_reconciliation_handoff(report, observed_commands=[])
self.assertFalse(result["complete"])
self.assertTrue(
any("pr number opened" in r.lower() for r in result["reasons"])
)
def test_pinned_reviewed_head_fails(self):
report = _good_handoff() + "- Pinned reviewed head: 2a544b7\n"
result = assess_reconciliation_handoff(report, observed_commands=[])
self.assertFalse(result["complete"])
def test_scratch_worktree_used_fails(self):
report = _good_handoff() + "- Scratch worktree used: False\n"
result = assess_reconciliation_handoff(report, observed_commands=[])
self.assertFalse(result["complete"])
def test_workspace_mutations_fails(self):
report = _good_handoff() + "- Workspace mutations: None\n"
result = assess_reconciliation_handoff(report, observed_commands=[])
self.assertFalse(result["complete"])
def test_author_lock_fields_fail(self):
report = (
_good_handoff()
+ "- Issue lock proof: locked before diff\n"
+ "- Claim/comment status: claimed\n"
)
result = assess_reconciliation_handoff(report, observed_commands=[])
self.assertFalse(result["complete"])
class TestObservedFetchMustBeReported(unittest.TestCase):
def test_fetch_with_ref_mutations_none_fails(self):
report = _good_handoff(**{"Git ref mutations": "none"})
result = assess_reconciliation_handoff(
report, observed_commands=["git fetch prgs master"]
)
self.assertFalse(result["complete"])
self.assertTrue(
any("git ref mutation" in r.lower() for r in result["reasons"])
)
if __name__ == "__main__":
unittest.main()