docs: define task-scoped Gitea MCP execution profile model (#12) #21

Merged
jcwalker3 merged 1 commits from feature/12-gitea-execution-profile-model into master 2026-07-01 12:15:06 -05:00
+215
View File
@@ -0,0 +1,215 @@
# Gitea MCP Execution Profiles
## Purpose
This document defines the **task-scoped execution profile model** for the
`gitea-mcp` package of the MCP Control Plane. It describes *what a profile is*,
the metadata each profile carries, and the safety rules that govern which
profile may perform which Gitea operation.
This issue defines the **model only**. It does not implement runtime profile
loading, profile switching, or any review/merge behavior — see
[Relationship to roadmap issues](#relationship-to-roadmap-issues).
## Principle: LLMs are not roles
The central rule of this model:
```text
The LLM is not the role.
The MCP credential/profile used for the task is the role.
```
An LLM session is not permanently an "author," a "reviewer," or a "merger."
Any LLM session may perform any of these roles — but only while operating
through a **task-appropriate execution profile** whose authenticated Gitea
identity and allowed operations fit the task.
Consequences:
- A task selects a profile; a profile is not assigned to a model.
- The same LLM may act as author for one task and reviewer for another, by
using different profiles — never by escalating a single profile.
- Two roles that must not be held by one identity (e.g. author and merger of
the same PR) are separated by using **different authenticated identities**,
not by trusting the LLM to behave.
## Profile model
A Gitea MCP execution profile is a named, declarative description of an
authenticated capability set. Each profile defines the following fields:
| Field | Type | Meaning |
|-------|------|---------|
| `profile_name` | string | Stable identifier, e.g. `gitea-reviewer`. |
| `authenticated_username` | string | The Gitea login this profile authenticates as (verified at runtime via `gitea_whoami`, not trusted from config). |
| `allowed_operations` | list | Operation categories this profile may perform. |
| `forbidden_operations` | list | Operation categories this profile must never perform. |
| `token_source_name` | string | The *name* of the secret source (e.g. env var name or secret key). **Never the token value.** |
| `audit_label` | string | Short label attached to audit records for actions by this profile. |
| `can_approve_prs` | bool | May submit an approving PR review. |
| `can_merge_prs` | bool | May merge a PR. |
| `can_push_branches` | bool | May push branches / create commits. |
| `can_mutate_issues` | bool | May create/edit/label/close issues and comment. |
| `can_author_impl_prs` | bool | May author implementation PRs (branch + commit + open PR). |
`token_source_name` records **where** a token comes from (a variable or key
name), never the token itself. Token values are never part of a profile object,
never logged, never returned by a tool, and never committed.
## Example profiles
The following are the reference profiles. Booleans express intended capability
boundaries; they are the model, not a runtime enforcement mechanism yet.
### `gitea-issue-manager`
- **allowed:** `read`, `issue.create`, `issue.comment`, `issue.label`, `issue.close`
- **forbidden:** `pr.approve`, `pr.merge`, `branch.push`
- `can_approve_prs`: `false`
- `can_merge_prs`: `false`
- `can_push_branches`: `false`
- `can_mutate_issues`: `true`
- `can_author_impl_prs`: `false`
### `gitea-author`
- **allowed:** `read`, `branch.push`, `pr.create`, `pr.comment`, `issue.comment`
- **forbidden:** `pr.approve`, `pr.merge`
- `can_approve_prs`: `false`
- `can_merge_prs`: `false`
- `can_push_branches`: `true`
- `can_mutate_issues`: `false` (may comment, may not manage)
- `can_author_impl_prs`: `true`
### `gitea-reviewer`
- **allowed:** `read`, `pr.comment`, `pr.review`, `pr.approve`, `pr.request_changes`
- **forbidden:** `pr.merge`, `branch.push`
- `can_approve_prs`: `true`
- `can_merge_prs`: `false`
- `can_push_branches`: `false`
- `can_mutate_issues`: `false`
- `can_author_impl_prs`: `false`
### `gitea-merger`
- **allowed:** `read`, `pr.merge`
- **forbidden:** `pr.approve`, `branch.push`, `pr.create`
- `can_approve_prs`: `false` (a merger must not also be the sole approver)
- `can_merge_prs`: `true`
- `can_push_branches`: `false`
- `can_mutate_issues`: `false`
- `can_author_impl_prs`: `false`
### `gitea-owner`
- **allowed:** broad administrative access; use sparingly and never for routine
LLM workflow tasks.
- **forbidden:** nothing structurally, which is exactly why it must not be the
default profile for automated work.
- `can_approve_prs`: `true`
- `can_merge_prs`: `true`
- `can_push_branches`: `true`
- `can_mutate_issues`: `true`
- `can_author_impl_prs`: `true`
> `gitea-owner` exists for human/administrative use. Automated LLM workflows
> should prefer the narrowest sufficient profile. An all-powerful profile is a
> convenience, not a role, and it does not exempt a session from the
> self-review / self-merge rule below.
## Allowed and forbidden operations
Operations are grouped into coarse categories so profiles stay readable:
- `read` — view issues, PRs, files, identity (`gitea_whoami`).
- `issue.*``create`, `comment`, `label`, `close`.
- `pr.*``create`, `comment`, `review`, `approve`, `request_changes`, `merge`.
- `branch.push` — push branches / create commits.
Rules:
- `forbidden_operations` always wins over `allowed_operations`. If an operation
appears in both, it is forbidden.
- An operation not present in `allowed_operations` is treated as **not
allowed** (deny by default).
- These categories are descriptive for this issue. Their runtime enforcement is
out of scope here (see roadmap links).
## Identity and fail-closed rules
Before **any** mutating action, a workflow must know both:
1. **The active profile** — which profile is in effect for this task.
2. **The authenticated identity** — the real Gitea login, verified via
`gitea_whoami` (issue #11), not read from configuration and trusted.
Fail-closed requirements:
- If the active profile is unknown → **stop; do not mutate.**
- If the authenticated identity cannot be determined → **stop; do not mutate.**
- If the requested operation is not in the profile's `allowed_operations`, or is
in `forbidden_operations`**stop; do not mutate.**
- Ambiguity is treated as denial. The safe default is always "do not act."
Read-only actions may proceed without a resolved profile, but must still never
expose token or credential material.
## Self-review and self-merge prevention
A profile/session **must not approve or merge a PR authored by the same
authenticated Gitea user.**
- The check compares the profile's *verified* `authenticated_username`
(from `gitea_whoami`) against the **PR author**.
- If they match, `pr.approve` and `pr.merge` fail closed, regardless of what the
profile's capability booleans say.
- This is why author and merger/reviewer roles are separated by **identity**,
not by prompt or by a single escalating profile. It is also why this was the
concrete blocker discovered while dogfooding PR #8 for issue #52.
## Token and secret handling
- Token **values** are never logged, never returned by any tool, and never
committed to the repository.
- Profiles reference a `token_source_name` (a variable/key *name*) only.
- `Authorization` headers and raw credentials must never appear in tool output,
audit records, or error messages.
## Separation from other MCP boundaries
`gitea-mcp` profile work stays within the Gitea trust boundary. It must **not**
add or absorb Jenkins, Ops, GlitchTip, Release, deploy, rollback, migration,
restart, or production behavior. Those belong to their own MCP packages under
the "one server per trust boundary" model described in
[`tool-boundaries.md`](tool-boundaries.md) and
[`credential-isolation.md`](credential-isolation.md).
## Relationship to roadmap issues
This document defines the **model only**. Related work is tracked separately
under roadmap [#10](https://gitea.prgs.cc/Scaled-Tech-Consulting/Gitea-Tools/issues/10):
- **#11** — Authenticated-user identity lookup (`gitea_whoami`). *Complete;
this model depends on it for verified identity.*
- **#19** — Runtime profile configuration via environment (loading real
profiles/tokens). *Not this issue.*
- **#13** — Read-only profile discovery (exposing the active profile). *Not this
issue.*
- **#14** — PR author / reviewer eligibility checks. *Not this issue.*
- **#15** — Gated PR review/approve actions. *Not this issue.*
- **#16** — Gated PR merge workflow. *Not this issue.*
- **#18** — Audit logging of mutating actions with profile metadata. *Not this
issue.*
## Non-goals
- Do **not** implement runtime profile switching or selection here.
- Do **not** implement multi-token loading here.
- Do **not** implement approve, merge, or eligibility workflows here.
- Do **not** expose, log, or commit any token or secret.
- Do **not** add Jenkins, Ops, GlitchTip, Release, deploy, or production
behavior.
- Do **not** create an all-powerful server; `gitea-owner` is administrative, not
a default automation role.