Add repo-name disambiguation and complete-inventory proofs for blind PR review #185
@@ -18,6 +18,54 @@ import re
|
||||
|
||||
_FULL_SHA = re.compile(r"^[0-9a-f]{40}$")
|
||||
|
||||
|
||||
# Repo name disambiguation rules for blind PR queue review.
|
||||
# User phrases referencing the "MCP Gitea tool" (or similar) must resolve to
|
||||
# Gitea-Tools repo, not be confused with mcp-control-plane.
|
||||
# If ambiguous (e.g. just "open PRs"), check both configured repos.
|
||||
REPO_ALIASES = {
|
||||
"gitea-tools": "Scaled-Tech-Consulting/Gitea-Tools",
|
||||
"gitea tool": "Scaled-Tech-Consulting/Gitea-Tools",
|
||||
"mcp gitea tool": "Scaled-Tech-Consulting/Gitea-Tools",
|
||||
"gitea mcp tool": "Scaled-Tech-Consulting/Gitea-Tools",
|
||||
"gitea-tools repo": "Scaled-Tech-Consulting/Gitea-Tools",
|
||||
"mcp-control-plane": "Scaled-Tech-Consulting/mcp-control-plane",
|
||||
"mcp control plane": "Scaled-Tech-Consulting/mcp-control-plane",
|
||||
}
|
||||
|
||||
|
||||
def resolve_repos_from_user_reference(
|
||||
reference: str, configured: list[str] | None = None
|
||||
) -> list[str]:
|
||||
"""Resolve a user reference string to the list of target repos to inventory.
|
||||
|
||||
- Exact aliases for Gitea-Tools map only to Gitea-Tools.
|
||||
- mcp-control-plane aliases map only to it.
|
||||
- Empty, ambiguous, or general "open PRs" default to all configured repos
|
||||
(both by default).
|
||||
- Returns subset of configured; never invents new repos.
|
||||
"""
|
||||
if configured is None:
|
||||
configured = [
|
||||
"Scaled-Tech-Consulting/Gitea-Tools",
|
||||
"Scaled-Tech-Consulting/mcp-control-plane",
|
||||
]
|
||||
if not reference or not reference.strip():
|
||||
return list(configured)
|
||||
|
||||
ref_lower = reference.lower()
|
||||
matched = []
|
||||
for alias, full_name in REPO_ALIASES.items():
|
||||
if alias in ref_lower:
|
||||
if full_name not in matched:
|
||||
matched.append(full_name)
|
||||
if matched:
|
||||
# return only the matched ones that are in configured, preserving order
|
||||
return [r for r in configured if r in matched]
|
||||
# no specific alias match → check all (complete inventory required)
|
||||
return list(configured)
|
||||
|
||||
|
||||
SAFE_NEXT_ACTION_UNKNOWN_CONTAMINATION = (
|
||||
"evidence missing: report contamination as unknown and "
|
||||
"choose another PR or stop"
|
||||
|
||||
@@ -181,11 +181,21 @@ Worktree folder = branch with `/` replaced by `-`
|
||||
that the diff base is the PR base branch. If `HEAD` does not match the
|
||||
pinned head, **stop before review/merge**
|
||||
(`review_proofs.verify_pinned_head_checkout`).
|
||||
6. **Inventory proof (#173):** a blind queue review must prove listing
|
||||
completeness before claiming "only PRs found": both configured
|
||||
repositories checked, open-PR filters stated, pagination handled or
|
||||
explicitly not needed, and the total open PR count per repo reported
|
||||
(`review_proofs.assess_inventory_completeness`).
|
||||
6. **Inventory proof (#173 + repo disambiguation hardening):** a blind queue
|
||||
review must prove listing completeness before claiming "only PRs found".
|
||||
Use repo-name disambiguation:
|
||||
- "Gitea-Tools" / "gitea tool" / "MCP Gitea tool" / "gitea MCP tool" /
|
||||
"gitea-tools repo" resolve **only** to `Scaled-Tech-Consulting/Gitea-Tools`.
|
||||
- "mcp-control-plane" resolves only to `Scaled-Tech-Consulting/mcp-control-plane`.
|
||||
- Ambiguous ("open PRs", no explicit repo, "MCP Gitea tooling") → inventory
|
||||
**both** configured repos.
|
||||
Report must state exactly which repo(s) were checked. If only one checked:
|
||||
"Only <repo> was checked. Other configured repos were not checked. This is
|
||||
not a complete queue inventory." Never let a single-repo zero hide PRs in
|
||||
the other.
|
||||
Both configured repos must be reported with state filter, pagination proof,
|
||||
and open-PR count (`review_proofs.assess_inventory_completeness` and
|
||||
`resolve_repos_from_user_reference`).
|
||||
7. Inspect the full diff; confirm scope matches the linked issue; flag unrelated files.
|
||||
8. Run the tests. Validation reporting must include the exact command and
|
||||
exact results: pass/fail, counts of tests passed/skipped/failed, any
|
||||
|
||||
@@ -5,6 +5,19 @@ Copy, fill the `<...>` fields, and paste as the task prompt.
|
||||
```text
|
||||
Task: review PR #<pr> for issue #<n>.
|
||||
|
||||
Repo name disambiguation (Gitea-Tools blind review hardening):
|
||||
- "Gitea-Tools", "gitea tool", "MCP Gitea tool", "gitea MCP tool", "gitea-tools repo"
|
||||
→ MUST resolve to `Scaled-Tech-Consulting/Gitea-Tools` (never treat as mcp-control-plane).
|
||||
- "mcp-control-plane", "mcp control plane" → only `Scaled-Tech-Consulting/mcp-control-plane`.
|
||||
- If user says "open PRs", "the queue", "MCP Gitea tooling" without explicit repo,
|
||||
or reference is ambiguous: check BOTH configured repos:
|
||||
`Scaled-Tech-Consulting/Gitea-Tools` and `Scaled-Tech-Consulting/mcp-control-plane`.
|
||||
- In the final report, always state exactly which repo(s) were checked.
|
||||
If only one was checked: explicitly say "Only <repo> was checked. Other
|
||||
configured repos were not checked. This is not a complete queue inventory."
|
||||
- A single-repo "no open PRs" result MUST NOT be reported as global "no open PRs"
|
||||
if the other configured repo was not inventoried.
|
||||
|
||||
Rules (llm-project-workflow):
|
||||
- Review in a SEPARATE detached review worktree, never the author's folder.
|
||||
- You must NOT be the PR author. If the authenticated user == PR author, stop.
|
||||
|
||||
@@ -25,6 +25,7 @@ from review_proofs import ( # noqa: E402
|
||||
assess_self_review_contamination,
|
||||
assess_validation_report,
|
||||
build_final_report,
|
||||
resolve_repos_from_user_reference,
|
||||
verify_pinned_head_checkout,
|
||||
)
|
||||
|
||||
@@ -489,5 +490,91 @@ class TestStdoutIsolation(unittest.TestCase):
|
||||
self.fail(f"stderr write failed: {exc}")
|
||||
|
||||
|
||||
class TestRepoNameDisambiguation(unittest.TestCase):
|
||||
"""Harness assertions for repo name disambiguation (new blind-review hardening).
|
||||
|
||||
"MCP Gitea tool" etc. must resolve to Gitea-Tools, not silently default to
|
||||
mcp-control-plane. "open PRs" or ambiguous must check both. Single-repo
|
||||
zero-result must not hide PRs in the other configured repo.
|
||||
"""
|
||||
|
||||
CONFIGURED = [
|
||||
"Scaled-Tech-Consulting/Gitea-Tools",
|
||||
"Scaled-Tech-Consulting/mcp-control-plane",
|
||||
]
|
||||
|
||||
def test_gitea_tools_aliases_resolve_to_gitea_tools_only(self):
|
||||
for ref in [
|
||||
"MCP Gitea tool",
|
||||
"gitea tool",
|
||||
"Gitea-Tools",
|
||||
"gitea mcp tool",
|
||||
"gitea-tools repo",
|
||||
]:
|
||||
result = resolve_repos_from_user_reference(ref, self.CONFIGURED)
|
||||
self.assertEqual(result, ["Scaled-Tech-Consulting/Gitea-Tools"])
|
||||
|
||||
def test_mcp_control_plane_alias_resolves_only_to_it(self):
|
||||
result = resolve_repos_from_user_reference(
|
||||
"mcp-control-plane", self.CONFIGURED
|
||||
)
|
||||
self.assertEqual(result, ["Scaled-Tech-Consulting/mcp-control-plane"])
|
||||
|
||||
def test_ambiguous_or_empty_defaults_to_both(self):
|
||||
self.assertEqual(
|
||||
resolve_repos_from_user_reference("open PRs", self.CONFIGURED),
|
||||
self.CONFIGURED,
|
||||
)
|
||||
self.assertEqual(
|
||||
resolve_repos_from_user_reference("", self.CONFIGURED),
|
||||
self.CONFIGURED,
|
||||
)
|
||||
self.assertEqual(
|
||||
resolve_repos_from_user_reference("review the queue", self.CONFIGURED),
|
||||
self.CONFIGURED,
|
||||
)
|
||||
|
||||
def test_single_repo_zero_result_does_not_hide_other(self):
|
||||
# Simulate a run that only inventoried mcp because of bad alias resolution.
|
||||
# The inventory must still require Gitea-Tools to claim "no open PRs".
|
||||
mcp_only_reports = [
|
||||
{
|
||||
"repo": "Scaled-Tech-Consulting/mcp-control-plane",
|
||||
"state_filter": "open",
|
||||
"pagination_complete": True,
|
||||
"open_pr_count": 0,
|
||||
}
|
||||
]
|
||||
result = assess_inventory_completeness(
|
||||
repo_reports=mcp_only_reports,
|
||||
required_repos=self.CONFIGURED,
|
||||
)
|
||||
self.assertFalse(result["complete"])
|
||||
self.assertTrue(
|
||||
any("Gitea-Tools" in r for r in result["reasons"])
|
||||
)
|
||||
|
||||
def test_full_inventory_of_both_is_required_for_exhaustive_claim(self):
|
||||
both_reports = [
|
||||
{
|
||||
"repo": "Scaled-Tech-Consulting/Gitea-Tools",
|
||||
"state_filter": "open",
|
||||
"pagination_complete": True,
|
||||
"open_pr_count": 1,
|
||||
},
|
||||
{
|
||||
"repo": "Scaled-Tech-Consulting/mcp-control-plane",
|
||||
"state_filter": "open",
|
||||
"pagination_complete": True,
|
||||
"open_pr_count": 0,
|
||||
},
|
||||
]
|
||||
result = assess_inventory_completeness(
|
||||
repo_reports=both_reports, required_repos=self.CONFIGURED
|
||||
)
|
||||
self.assertTrue(result["complete"])
|
||||
self.assertTrue(result["can_claim_exhaustive"])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user