Merge pull request 'Harden LLM role/task alignment and PR review feedback discovery' (#169) from feat/issue-167-review-feedback-discovery into master

This commit was merged in pull request #169.
This commit is contained in:
2026-07-05 12:17:43 -05:00
3 changed files with 757 additions and 0 deletions
+63
View File
@@ -398,6 +398,69 @@ touching anything.
files, detected secret, or any production/deploy behavior — **stop, report the
blocker, and take no mutating action.** Fail closed; never work around a gate.
## Task/role alignment (#167)
The **requested task** decides what a session may do — not the credential it
happens to hold. Resolve the task first with
`gitea_resolve_task_capability(task=...)`: it returns `stop_required` and
`task_role_guidance` alongside the permission decision. An LLM asked to
review must never degrade into author work just because it is connected as
an author.
| Requested task | Required identity/profile | Allowed | Forbidden | Stop when |
|---|---|---|---|---|
| Review PR (`review_pr`) | reviewer (e.g. `sysadmin` / `prgs-reviewer`) | read, gated review verdicts | commits, pushes, file edits, author comments, merge without eligibility | active profile is an author profile — stop immediately; do **not** switch to author-side fixes unless the operator explicitly re-tasks |
| Address PR change requests (`address_pr_change_requests`) | author (e.g. `jcwalker3` / `prgs-author`) | commit/push fixes to the PR branch, PR comment summarizing fixes | review verdicts, approve, request-changes, merge | active profile lacks branch push |
| Merge PR (`merge_pr`) | reviewer/merger | gated merge after eligibility + approval | merging own PR, merging without pinned head match | active profile is an author profile, or any merge gate fails |
| Comment on issue discussion (`comment_issue`) | any profile with `gitea.issue.comment` | issue thread comments | review verdicts, closing via comment | permission missing (`gitea.pr.comment` does **not** imply it) |
| Comment on PR (`comment_pr`) | any profile with `gitea.pr.comment` | PR thread comments | review verdicts | permission missing |
| Author implementation (`create_branch`/`push_branch`/`create_pr`) | author | branch, commit, push, open PR | self-review, self-merge | profile lacks the author permissions |
If the task is review/merge and the session is an author profile, the only
correct outputs are: the read-only PR queue inventory (#164), the structured
permission report (#142), and a stop. Ask the operator to reconnect to the
reviewer namespace; a credential or profile swap in the same session never
cures same-session authorship.
## Review feedback discovery (MCP-native)
Formal review verdicts (APPROVED / REQUEST_CHANGES / COMMENT) live on the
review endpoints, **not** in the issue-comment thread. Never infer review
state from issue comments — use `gitea_get_pr_review_feedback(pr_number=...)`
(read-only, requires `gitea.read`). It reports:
- every submitted review: reviewer, verdict, redacted body, timestamp, and
the head SHA it reviewed;
- `latest_review_state_by_reviewer` (PENDING drafts never count);
- `has_blocking_change_requests` / `approval_visible` (dismissed reviews do
not block);
- `current_head_sha` vs `latest_reviewed_head_sha`, `review_feedback_stale`,
and `author_pushed_after_request_changes` — so a reviewer can see whether
feedback predates new commits, and an author can see whether fixes have
been pushed since the REQUEST_CHANGES.
A permission block returns `feedback_not_attempted: true` with a structured
permission report — distinct from a successful "no reviews yet" result, so a
blocked lookup is never misread as "no feedback exists".
## Validation reporting discipline (#167)
Validation results in handoffs and PR bodies must state exactly what ran and
what happened. Build the validation section with
`build_validation_report(...)` (in `mcp_server.py`) or follow its contract by
hand — every command is one of:
- `passed` — the exact command ran and succeeded;
- `failed` — must include the exact command **and its output**; never
paraphrase ("shell-invocation quirks" is not a status);
- `skipped` — deliberately not run; name the reason and any targeted check
that replaced it;
- `not-run` — was required but never executed; say so plainly.
Never imply full-suite success unless the full-suite command itself passed
(`full_suite_passed: true`). A report that hides a failed or skipped check
is worse than a failing report.
## Controller Handoff (required, every task)
Every task — implementation, review, merge, triage, documentation,