feat(reviewer): wire PR inventory trust gate into live queue path (#196)
Run pr_inventory_trust_gate on empty gitea_review_pr inventory results, add assess_reviewer_queue_inventory for multi-repo completeness checks, and require trusted_empty before empty-queue claims in reports/templates. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This commit is contained in:
@@ -1312,6 +1312,93 @@ def pr_inventory_trust_gate(
|
||||
}
|
||||
|
||||
|
||||
def _split_repo_slug(full_repo: str) -> tuple[str | None, str | None]:
|
||||
parts = (full_repo or "").split("/", 1)
|
||||
if len(parts) == 2:
|
||||
return parts[0].strip() or None, parts[1].strip() or None
|
||||
return None, None
|
||||
|
||||
|
||||
def assess_reviewer_queue_inventory(
|
||||
repo_reports: list[dict] | None,
|
||||
required_repos: list[str] | None = None,
|
||||
*,
|
||||
user_context: str | None = None,
|
||||
) -> dict:
|
||||
"""Canonical reviewer queue path: completeness plus per-repo trust gates (#196).
|
||||
|
||||
Any repository reporting ``open_pr_count == 0`` must pass
|
||||
``pr_inventory_trust_gate`` with ``trusted_empty`` before an empty-queue
|
||||
claim is allowed. A bare ``[]`` from ``gitea_list_prs`` is never sufficient.
|
||||
"""
|
||||
required = list(required_repos or [
|
||||
"Scaled-Tech-Consulting/Gitea-Tools",
|
||||
"Scaled-Tech-Consulting/mcp-control-plane",
|
||||
])
|
||||
completeness = assess_inventory_completeness(repo_reports, required)
|
||||
|
||||
trust_gates: dict[str, dict] = {}
|
||||
blockers: list[str] = []
|
||||
can_claim_empty = bool(completeness.get("complete"))
|
||||
|
||||
for report in repo_reports or []:
|
||||
repo = (report.get("repo") or "").strip()
|
||||
count = report.get("open_pr_count")
|
||||
if not isinstance(count, int) or count != 0:
|
||||
continue
|
||||
|
||||
org, repo_name = _split_repo_slug(repo)
|
||||
list_response = report.get("list_prs_response")
|
||||
if list_response is None:
|
||||
list_response = []
|
||||
|
||||
gate = pr_inventory_trust_gate(
|
||||
list_response,
|
||||
remote=report.get("remote"),
|
||||
org=org,
|
||||
repo=repo_name,
|
||||
state=report.get("state_filter"),
|
||||
authenticated_profile=report.get("authenticated_profile"),
|
||||
local_remote_url=report.get("local_remote_url"),
|
||||
user_context=user_context or report.get("user_context"),
|
||||
corroboration_open_pr_counter=report.get(
|
||||
"corroboration_open_pr_counter"
|
||||
),
|
||||
has_finality_metadata=report.get("pagination_complete") is True,
|
||||
)
|
||||
trust_gates[repo] = gate
|
||||
status = gate.get("status")
|
||||
if status != "trusted_empty":
|
||||
can_claim_empty = False
|
||||
blockers.append(
|
||||
f"repository '{repo}': empty PR list trust gate is "
|
||||
f"'{status}'; cannot claim 'no open PRs'"
|
||||
)
|
||||
blockers.extend(gate.get("reasons") or [])
|
||||
|
||||
return {
|
||||
"complete": bool(completeness.get("complete")),
|
||||
"can_claim_empty_queue": can_claim_empty,
|
||||
"can_claim_exhaustive": (
|
||||
bool(completeness.get("can_claim_exhaustive")) and can_claim_empty
|
||||
),
|
||||
"inventory_reasons": list(completeness.get("reasons") or []),
|
||||
"trust_gates": trust_gates,
|
||||
"blockers": blockers,
|
||||
"reasons": list(completeness.get("reasons") or []) + blockers,
|
||||
}
|
||||
|
||||
|
||||
def format_pr_inventory_trust_gate_report(gate: dict) -> list[str]:
|
||||
"""Render trust-gate lines for MCP inventory output."""
|
||||
lines = [f"pr_inventory_trust_gate.status: {gate.get('status', 'unknown')}"]
|
||||
if gate.get("corroborated"):
|
||||
lines.append("pr_inventory_trust_gate.corroborated: true")
|
||||
for reason in gate.get("reasons") or []:
|
||||
lines.append(f"pr_inventory_trust_gate.reason: {reason}")
|
||||
return lines
|
||||
|
||||
|
||||
def assess_duplicate_search_proof(report_text, matches):
|
||||
"""#207: reject LLM duplicate summaries that omit known title matches."""
|
||||
return issue_duplicate_gate.assess_duplicate_search_proof(
|
||||
|
||||
Reference in New Issue
Block a user