Enforce issue-write tool gates to match task capability resolver (Issue #69)

Extract TASK_CAPABILITY_MAP into task_capability_map.py as the single source
of truth shared by gitea_resolve_task_capability and issue-mutating tools.
Gate gitea_create_issue, gitea_close_issue, gitea_mark_issue, and
gitea_set_issue_labels with structured #142 permission reports. Add regression
tests proving resolver-denied tasks fail closed at the raw tool layer.

Implements mcp-control-plane #69.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This commit is contained in:
2026-07-05 21:09:34 -04:00
co-authored by Claude Opus 4.8
parent c89ae2eb5c
commit 00394d43b7
6 changed files with 413 additions and 100 deletions
+15 -9
View File
@@ -19,6 +19,9 @@ from mcp_server import gitea_create_issue # noqa: E402
from review_proofs import assess_duplicate_search_proof as proof_assess # noqa: E402
FAKE_AUTH = "Basic dGVzdDp0ZXN0"
ISSUE_WRITE_ENV = {
"GITEA_ALLOWED_OPERATIONS": "gitea.issue.create",
}
CANONICAL_TITLE = (
"Add hard queue-target resolution wall before PR inventory "
"or empty-queue claims"
@@ -118,7 +121,8 @@ class TestCreateIssueMCPGate(unittest.TestCase):
mock_get_all.return_value = [
{"number": 201, "title": CANONICAL_TITLE, "state": "open"},
]
result = gitea_create_issue(title=CANONICAL_TITLE.upper(), remote="prgs")
with patch.dict(__import__("os").environ, ISSUE_WRITE_ENV, clear=True):
result = gitea_create_issue(title=CANONICAL_TITLE.upper(), remote="prgs")
self.assertFalse(result["performed"])
self.assertEqual(result["duplicate_gate"], VERDICT_DUPLICATE)
mock_api.assert_not_called()
@@ -135,13 +139,14 @@ class TestCreateIssueMCPGate(unittest.TestCase):
{"number": 201, "title": CANONICAL_TITLE, "state": "open"},
]
mock_api.return_value = {"number": 210, "html_url": "https://gitea.prgs.cc/issues/210"}
result = gitea_create_issue(
title=CANONICAL_TITLE,
body="Follow-up scope",
remote="prgs",
allow_duplicate_override=True,
split_from_issue=201,
)
with patch.dict(__import__("os").environ, ISSUE_WRITE_ENV, clear=True):
result = gitea_create_issue(
title=CANONICAL_TITLE,
body="Follow-up scope",
remote="prgs",
allow_duplicate_override=True,
split_from_issue=201,
)
self.assertEqual(result["number"], 210)
payload = mock_api.call_args[0][3]
self.assertIn("Operator-approved split from #201.", payload["body"])
@@ -156,7 +161,8 @@ class TestCreateIssueMCPGate(unittest.TestCase):
):
mock_role_check.return_value = (True, [])
mock_api.return_value = {"number": 1, "html_url": "https://gitea.example.com/issues/1"}
result = gitea_create_issue(title="Unique new issue", body="body text")
with patch.dict(__import__("os").environ, ISSUE_WRITE_ENV, clear=True):
result = gitea_create_issue(title="Unique new issue", body="body text")
self.assertEqual(result["number"], 1)