feat: update operator guide and skill registry for role-aware task routing (#144)

- mcp_get_control_plane_guide now instructs to ALWAYS resolve task capability first (gitea_resolve_task_capability), verify identity/profile, use correct namespace.
- Added hard stops for resolve-first, namespace separation, issue.comment distinct from pr.comment.
- Added/updated skills: gitea-resolve-task-capability (new), and steps/notes in issue-authoring, pr-creation, pr-review, pr-merge, issue-comments to call resolver first.
- Guide docstring and guidance updated for AC: do not hardcode, resolve before act, reviewer for review/merge, author for authoring, issue comments require gitea.issue.comment.
- Tests: 4 new covering guide for author/reviewer, missing issue-comment, static-profile safe next action; EXPECTED_SKILLS updated.
- No secrets/URLs; synthetic tests; no prod behavior change.

Closes #144
This commit is contained in:
2026-07-05 06:29:27 -04:00
parent d73d238c37
commit 2592c6ba7e
2 changed files with 122 additions and 14 deletions
+77 -14
View File
@@ -1631,6 +1631,14 @@ _HARD_STOPS = [
"mergeable signal is honoured, never overridden.",
"Do not rewrite the live profiles config; config changes are "
"operator-owned.",
"Do not hardcode Gitea identities or assume profile roles from prompts. "
"Always call gitea_whoami to verify the active identity and profile, and "
"call gitea_resolve_task_capability to check required task permissions.",
"Use the correct namespace/profile: PR review and merge require a reviewer "
"namespace (e.g. gitea-reviewer), and branch/PR creation requires an author "
"namespace (e.g. gitea-author).",
"Issue comments require explicit gitea.issue.comment permission. PR "
"comments (gitea.pr.comment) do not imply or satisfy issue comment requirements.",
]
_GUIDE_RULES = {
@@ -1678,19 +1686,23 @@ _GUIDE_RULES = {
"task, STOP and report instead of proceeding.",
"Runtime identity (whoami) is the source of truth; config-declared "
"roles are metadata only.",
"Do not hardcode Gitea identities; always dynamically verify your "
"identity and resolve task capability via gitea_resolve_task_capability.",
],
}
_COMMON_WORKFLOWS = [
"task routing: resolve task capability via gitea_resolve_task_capability "
"to check required permission/role kind and identify the safe next action.",
"issue authoring: verify identity, create/claim the issue, keep scope "
"explicit (remote/org/repo).",
"implementation: claim issue, branch from fresh master, implement only "
"the issue scope, test, open a PR referencing the issue, stop.",
"PR review: verify reviewer identity, pin the head SHA, validate "
"independently, post a verdict; never review your own work.",
"PR merge: reviewer identity + eligibility check + pinned head + "
"explicit 'MERGE PR <n>' confirmation, only with operator "
"authorization.",
"PR review: verify reviewer identity (must be a reviewer profile/namespace), "
"pin the head SHA, validate independently, post a verdict; never review your own work.",
"PR merge: reviewer identity (must be a reviewer profile/namespace) + "
"eligibility check + pinned head + explicit 'MERGE PR <n>' confirmation, "
"only with operator authorization.",
]
# Skill registry (#128). status: 'available' = backed by tools in this
@@ -1704,8 +1716,13 @@ _PROJECT_SKILLS = {
"issue before implementation (gitea_mark_issue).",
"required_operations": ["gitea.read"],
"status": "available",
"notes": "Always pass remote/org/repo explicitly.",
"notes": "Always pass remote/org/repo explicitly. Resolve task "
"capability first to confirm author profile and "
"gitea.issue.* permissions.",
"steps": [
"Resolve task first: gitea_resolve_task_capability to confirm "
"author namespace and required permissions (e.g. for "
"create_issue or comment_issue).",
"Verify identity with gitea_whoami (explicit remote).",
"Check the issue queue with gitea_list_issues.",
"Create with gitea_create_issue or claim with gitea_mark_issue "
@@ -1722,6 +1739,8 @@ _PROJECT_SKILLS = {
"required_operations": ["gitea.pr.create"],
"status": "available",
"steps": [
"Resolve task first: gitea_resolve_task_capability(task='create_pr') "
"to confirm author profile.",
"Branch from fresh master (git pull first).",
"Implement only the claimed issue's scope; stage files "
"explicitly.",
@@ -1740,6 +1759,8 @@ _PROJECT_SKILLS = {
"required_operations": ["gitea.pr.review"],
"status": "available",
"steps": [
"Resolve task first: gitea_resolve_task_capability(task='review_pr') "
"to confirm reviewer namespace and avoid author-profile blocks.",
"Verify reviewer identity with gitea_whoami; the PR author "
"must be a different user.",
"Pin the PR head SHA (gitea_view_pr) before validating.",
@@ -1757,6 +1778,8 @@ _PROJECT_SKILLS = {
"required_operations": ["gitea.pr.merge"],
"status": "available",
"steps": [
"Resolve task first: gitea_resolve_task_capability(task='merge_pr') "
"to confirm reviewer namespace.",
"Confirm operator authorization for this specific merge.",
"Run gitea_check_pr_eligibility action='merge' and resolve "
"every reason it reports.",
@@ -1774,8 +1797,11 @@ _PROJECT_SKILLS = {
"required_operations": ["gitea.issue.comment"],
"status": "available",
"notes": "Listing needs only gitea.read; posting needs "
"gitea.issue.comment, gated separately from gitea.pr.*.",
"gitea.issue.comment, gated separately from gitea.pr.*. "
"Resolve with gitea_resolve_task_capability first.",
"steps": [
"Resolve task first: gitea_resolve_task_capability(task='comment_issue') "
"to confirm gitea.issue.comment and correct profile/namespace.",
"List with gitea_list_issue_comments (explicit issue number).",
"Post with gitea_create_issue_comment; markdown body, no "
"secrets or raw URLs.",
@@ -1783,6 +1809,27 @@ _PROJECT_SKILLS = {
"versa.",
],
},
"gitea-resolve-task-capability": {
"description": "Determine the required profile, permission, role kind, "
"and safe next action (namespace switch or relaunch) "
"for a Gitea task before acting.",
"when_to_use": "Before any mutating action, review, or comment; "
"to select correct author/reviewer namespace and "
"avoid permission failures.",
"required_operations": ["gitea.read"],
"status": "available",
"steps": [
"Call gitea_resolve_task_capability with the intended task "
"(e.g. 'create_issue', 'review_pr', 'comment_issue').",
"Inspect 'allowed_in_current_session', 'required_role_kind', "
"'matching_configured_profile', 'different_mcp_namespace_required', "
"and 'exact_safe_next_action'.",
"If not allowed, follow the exact safe next action (do not "
"hardcode identity or guess).",
"Verify the active profile matches the required role before "
"proceeding.",
],
},
"profile-switching": {
"description": "Change the active MCP identity between author and "
"reviewer profiles.",
@@ -1880,6 +1927,11 @@ def mcp_get_control_plane_guide(
profile-aware; if identity cannot be resolved it instructs the session
to STOP.
Always resolve task capability (gitea_resolve_task_capability) before
acting to determine required role/namespace. Issue comments require
gitea.issue.comment (distinct from gitea.pr.comment); review/merge
require reviewer namespace.
Args:
remote: Known instance — 'dadeschools' or 'prgs'.
host: Override the Gitea host.
@@ -1903,33 +1955,44 @@ def mcp_get_control_plane_guide(
"status": "verified" if username else "unresolved",
"instruction": (
"Identity verified — confirm it matches the role this task "
"requires before acting."
"requires before acting. If mismatched or unclear, call "
"gitea_resolve_task_capability to check the correct profile or namespace."
if username else
"STOP: the authenticated identity could not be resolved. Do "
"not perform any mutating operation; report to the operator "
"and wait."),
"not perform any mutating operation; call gitea_resolve_task_capability "
"to find the required profile, report to the operator, and wait."),
}
if _reveal_endpoints():
identity["server"] = h
guidance = []
guidance = [
"ALWAYS resolve the task capability first: call "
"gitea_resolve_task_capability (task=...) before any action. "
"Do not hardcode identity or guess the launcher profile. "
"Verify the active identity/profile matches the required role "
"for the task. Use the correct namespace (author vs reviewer) "
"as reported."
]
if not username:
guidance.append(
"STOP: identity unresolved — no mutating operations until the "
"operator fixes credentials/profile and gitea_whoami verifies.")
"operator fixes credentials/profile. Call gitea_resolve_task_capability "
"to check required profile/permissions, and verify via gitea_whoami.")
if role == "author":
guidance.append(
"Author profile: creating branches, commits, issues, and PRs "
"is allowed; review, approve, and merge are forbidden for this "
"profile — never attempt them, and never review or merge your "
"own PR from any profile.")
"own PR from any profile. Issue comments require "
"gitea.issue.comment (separate from gitea.pr.comment).")
elif role == "reviewer":
guidance.append(
"Reviewer profile: review/approve/merge may proceed ONLY after "
"gitea_check_pr_eligibility passes and the PR head SHA is "
"pinned (expected_head_sha); the PR author must be a different "
"user, and merging additionally requires explicit operator "
"authorization plus the 'MERGE PR <n>' confirmation.")
"authorization plus the 'MERGE PR <n>' confirmation. "
"PR comments do not imply issue comments.")
elif role == "mixed":
guidance.append(
"WARNING: this profile allows both authoring and "