fix: resolve conflicts for PR #385
Merge prgs/master; keep work-issue author routing (#139) alongside reconciler task entries from master (#309/#310).
This commit is contained in:
@@ -14,6 +14,7 @@ is usable from prompts, harness assertions, and tests. The MCP-level gates
|
||||
here weakens or replaces them.
|
||||
"""
|
||||
|
||||
import hashlib
|
||||
import re
|
||||
|
||||
import issue_duplicate_gate
|
||||
@@ -1699,11 +1700,31 @@ def build_final_report(checkout_proof, inventory, validation, contamination,
|
||||
if report_text and _QUEUE_STATUS_REPORT_HINT.search(report_text)
|
||||
else {"proven": True, "block": False, "reasons": [], "violations": []}
|
||||
)
|
||||
pr_queue_cleanup_report = (
|
||||
assess_pr_queue_cleanup_report(report_text)
|
||||
if report_text and _PR_QUEUE_CLEANUP_REPORT_HINT.search(report_text)
|
||||
else {"proven": True, "block": False, "reasons": [], "violations": []}
|
||||
)
|
||||
proof_wording = (
|
||||
assess_proof_wording(report_text)
|
||||
if report_text
|
||||
else {"proven": True, "block": False, "reasons": [], "violations": []}
|
||||
)
|
||||
reconcile_inventory = (
|
||||
assess_reconcile_inventory_report(report_text)
|
||||
if report_text and _RECONCILE_INVENTORY_HINT.search(report_text)
|
||||
else {"proven": True, "block": False, "reasons": []}
|
||||
)
|
||||
reconcile_linked_issue = (
|
||||
assess_reconcile_linked_issue_report(report_text)
|
||||
if report_text and _RECONCILE_LINKED_ISSUE_HINT.search(report_text)
|
||||
else {"proven": True, "block": False, "reasons": []}
|
||||
)
|
||||
already_landed_handoff = (
|
||||
assess_already_landed_handoff_report(report_text)
|
||||
if report_text
|
||||
else {"proven": True, "block": False, "reasons": []}
|
||||
)
|
||||
inventory_worktree = (
|
||||
assess_inventory_worktree_report(report_text)
|
||||
if report_text
|
||||
@@ -1870,6 +1891,26 @@ def build_final_report(checkout_proof, inventory, validation, contamination,
|
||||
"queue-status report violates loaded workflow proof gates (#339)"
|
||||
)
|
||||
downgrade_reasons.extend(queue_status_report.get("reasons", []))
|
||||
if not reconcile_inventory.get("proven"):
|
||||
downgrade_reasons.append(
|
||||
"reconciliation PR inventory lacks pagination proof (#308)"
|
||||
)
|
||||
downgrade_reasons.extend(reconcile_inventory.get("reasons", []))
|
||||
if not reconcile_linked_issue.get("proven"):
|
||||
downgrade_reasons.append(
|
||||
"reconciliation linked issue status lacks live fetch proof (#300)"
|
||||
)
|
||||
downgrade_reasons.extend(reconcile_linked_issue.get("reasons", []))
|
||||
if not pr_queue_cleanup_report.get("proven"):
|
||||
downgrade_reasons.append(
|
||||
"PR-only cleanup report violates cleanup-mode proof gates (#390)"
|
||||
)
|
||||
downgrade_reasons.extend(pr_queue_cleanup_report.get("reasons", []))
|
||||
if not already_landed_handoff.get("proven"):
|
||||
downgrade_reasons.append(
|
||||
"already-landed controller handoff has stale or inconsistent fields (#299)"
|
||||
)
|
||||
downgrade_reasons.extend(already_landed_handoff.get("reasons", []))
|
||||
if not inventory_worktree.get("proven"):
|
||||
downgrade_reasons.append(
|
||||
"inventory pagination or worktree fields inconsistent (#293)"
|
||||
@@ -1968,6 +2009,24 @@ def build_final_report(checkout_proof, inventory, validation, contamination,
|
||||
"queue_status_violations": list(
|
||||
queue_status_report.get("violations") or []
|
||||
),
|
||||
"reconcile_inventory_proven": bool(reconcile_inventory.get("proven")),
|
||||
"reconcile_inventory_violations": list(
|
||||
reconcile_inventory.get("reasons") or []
|
||||
),
|
||||
"reconcile_linked_issue_proven": bool(reconcile_linked_issue.get("proven")),
|
||||
"reconcile_linked_issue_violations": list(
|
||||
reconcile_linked_issue.get("reasons") or []
|
||||
),
|
||||
"pr_queue_cleanup_report_proven": bool(
|
||||
pr_queue_cleanup_report.get("proven")
|
||||
),
|
||||
"pr_queue_cleanup_violations": list(
|
||||
pr_queue_cleanup_report.get("reasons") or []
|
||||
),
|
||||
"already_landed_handoff_proven": bool(already_landed_handoff.get("proven")),
|
||||
"already_landed_handoff_violations": list(
|
||||
already_landed_handoff.get("reasons") or []
|
||||
),
|
||||
"inventory_worktree_proven": bool(inventory_worktree.get("proven")),
|
||||
"inventory_worktree_violations": list(
|
||||
inventory_worktree.get("reasons") or []
|
||||
@@ -3376,6 +3435,67 @@ def assess_issue_filing_duplicate_summary(
|
||||
}
|
||||
|
||||
|
||||
# ── Canonical review-workflow citation and version proof (Issue #296) ────────
|
||||
#
|
||||
# The canonical PR review/merge workflow lives in
|
||||
# skills/llm-project-workflow/workflows/review-merge-pr.md and is exposed
|
||||
# via mcp_get_skill_guide. Review reports must prove they loaded it and
|
||||
# cite the version hash used, so stale or shortened prompt copies are
|
||||
# rejected.
|
||||
|
||||
REVIEW_WORKFLOW_MARKERS = (
|
||||
"workflows/review-merge-pr.md",
|
||||
"review-merge-pr.md",
|
||||
)
|
||||
|
||||
|
||||
def compute_workflow_hash(workflow_text):
|
||||
"""Return the short (12-hex) sha256 version hash of a workflow text."""
|
||||
digest = hashlib.sha256((workflow_text or "").encode("utf-8"))
|
||||
return digest.hexdigest()[:12]
|
||||
|
||||
|
||||
def assess_review_workflow_source(report_text, *, canonical_hash=None):
|
||||
"""#296: review reports must cite the canonical workflow and version.
|
||||
|
||||
The report must reference the canonical workflow file and carry a
|
||||
populated ``Workflow version`` (or ``Workflow hash``) field. When
|
||||
*canonical_hash* — ``compute_workflow_hash`` of the current
|
||||
workflows/review-merge-pr.md content — is supplied, the cited hash
|
||||
must match it, rejecting stale copies.
|
||||
|
||||
Returns {'complete', 'downgraded', 'reasons'}; fails closed.
|
||||
"""
|
||||
lower = (report_text or "").lower()
|
||||
reasons = []
|
||||
|
||||
if not any(marker in lower for marker in REVIEW_WORKFLOW_MARKERS):
|
||||
reasons.append(
|
||||
"review report missing canonical workflow source citation "
|
||||
"(workflows/review-merge-pr.md)"
|
||||
)
|
||||
|
||||
fields = _report_labeled_fields(report_text)
|
||||
version = fields.get("workflow version") or fields.get("workflow hash")
|
||||
if not version or version.strip().lower().startswith(("none", "unknown")):
|
||||
reasons.append(
|
||||
"review report missing populated 'Workflow version' field "
|
||||
"(version/hash of the canonical workflow used)"
|
||||
)
|
||||
elif canonical_hash and canonical_hash.lower() not in version.lower():
|
||||
reasons.append(
|
||||
f"review report cites stale workflow version '{version}'; "
|
||||
f"current canonical hash is '{canonical_hash}' — reload the "
|
||||
"workflow via mcp_get_skill_guide"
|
||||
)
|
||||
|
||||
return {
|
||||
"complete": not reasons,
|
||||
"downgraded": bool(reasons),
|
||||
"reasons": reasons,
|
||||
}
|
||||
|
||||
|
||||
def assess_create_issue_workflow_source(report_text: str) -> dict:
|
||||
"""#337: create-issue reports must cite the canonical workflow file."""
|
||||
lower = (report_text or "").lower()
|
||||
@@ -4917,6 +5037,31 @@ _QUEUE_STATUS_REPORT_HINT = re.compile(
|
||||
re.I,
|
||||
)
|
||||
|
||||
_RECONCILE_INVENTORY_HINT = re.compile(
|
||||
r"already[- ]landed reconciliation|reconcile[- ]landed|"
|
||||
r"open pr inventory|inventory pagination proof",
|
||||
re.I,
|
||||
)
|
||||
|
||||
_RECONCILE_LINKED_ISSUE_HINT = re.compile(
|
||||
r"already[- ]landed|reconcil|linked issue(?:\s+live)?\s+status",
|
||||
re.I,
|
||||
)
|
||||
|
||||
_PR_QUEUE_CLEANUP_REPORT_HINT = re.compile(
|
||||
r"workflows/pr-queue-cleanup\.md|task:\s*pr-queue-cleanup|"
|
||||
r"pr[- ]only queue cleanup",
|
||||
re.I,
|
||||
)
|
||||
|
||||
|
||||
def assess_pr_queue_cleanup_report(report_text: str | None) -> dict:
|
||||
"""#390: validate PR-only cleanup final reports (one PR per run)."""
|
||||
from pr_queue_cleanup import assess_pr_queue_cleanup_report as _assess
|
||||
|
||||
return _assess(report_text or "")
|
||||
|
||||
|
||||
_GATE_PASSED_VALUE = re.compile(r"\bpassed\b", re.I)
|
||||
|
||||
_NOT_APPLICABLE_VALUE = re.compile(
|
||||
@@ -5331,6 +5476,27 @@ def assess_validation_worktree_edit_report(report_text, **kwargs):
|
||||
return _assess(report_text, **kwargs)
|
||||
|
||||
|
||||
def assess_reconcile_inventory_report(report_text, **kwargs):
|
||||
"""#308: prove PR inventory pagination in reconciliation reports."""
|
||||
from reviewer_reconcile_inventory import assess_reconcile_inventory_report as _assess
|
||||
|
||||
return _assess(report_text, **kwargs)
|
||||
|
||||
|
||||
def assess_reconcile_linked_issue_report(report_text, **kwargs):
|
||||
"""#300: prove linked issue status was fetched live in reconciliation handoffs."""
|
||||
from reviewer_reconcile_linked_issue import assess_reconcile_linked_issue_report as _assess
|
||||
|
||||
return _assess(report_text, **kwargs)
|
||||
|
||||
|
||||
def assess_already_landed_handoff_report(report_text, **kwargs):
|
||||
"""#299: reject stale fields after the already-landed gate fires."""
|
||||
from reviewer_already_landed_handoff import assess_already_landed_handoff_report as _assess
|
||||
|
||||
return _assess(report_text, **kwargs)
|
||||
|
||||
|
||||
def assess_inventory_worktree_report(report_text, **kwargs):
|
||||
"""#293: prove inventory pagination and consistent worktree reporting."""
|
||||
from reviewer_inventory_worktree import assess_inventory_worktree_report as _assess
|
||||
|
||||
Reference in New Issue
Block a user