---
task_mode: review-merge-pr
canonical: true
final_report_schema: ../schemas/review-merge-final-report.md
---

# Review and merge PR workflow (canonical)

**Task mode:** `review-merge-pr`

This file is the canonical PR review/merge workflow for Gitea-Tools. Load it
before any PR mutation. Final report schema:
[`schemas/review-merge-final-report.md`](../schemas/review-merge-final-report.md).

**BLOCKED + DIAGNOSE (universal default):** If at any point you cannot perform a required step (skill not available, terminal broken, capability missing, wrong profile, guard blocks, worktree misbound, instructions unavailable, preflight fails, etc.), STOP. Clearly state BLOCKED. Use the standard [`../templates/blocked-diagnose-report.md`](../templates/blocked-diagnose-report.md) template. Only safe non-mutating recovery. Report using the template. Do not continue or use any fallback (temp scripts, direct API, etc.) unless controller authorizes. All blocker classes listed in llm-project-workflow/SKILL.md must trigger this.

**Native MCP failure is a hard stop (#695):** If the native MCP namespace dies (EOF, capability disconnect, session death), STOP. Do **not** import `gitea_mcp_server` from a standalone process, do **not** run offline helpers (`offline_mcp_helper.py`, `offline_mcp_runner.py`, `run_quarantine.py`), and do **not** set direct-import / keychain-bypass / raw-token environment variables. Reconnect the official MCP daemon only. Contaminated approvals must be controller-quarantined; they never authorize merge.

**Default task prompt:**

> Review the next eligible open PR in this project. Merge it only if every
> identity, capability, author-safety, inventory, queue-ordering, worktree,
> validation, already-landed, prior-review, final-report, mutation-ledger,
> proof-wording, and merge gate passes.

Do not improvise around the gates. Follow project skills, MCP gates, and
workflow rules exactly.

---

## 0. Load the canonical workflow first

Before starting PR work, **find the latest CTH comment** on the PR or linked
issue thread. Treat that CTH as the current handoff state. Post a new
**CTH: Reviewer Handoff**, **CTH: Merger Handoff**, **CTH: Blocker**, or
**CTH: Supersession Notice** when you finish, block, skip, supersede,
approve, request changes, or hand off. See `docs/canonical-thread-handoff.md`.

Before starting PR work, check whether the project provides a canonical PR review/merge workflow through a project skill, runbook, or MCP helper.

If available, load it first and report:

* workflow source
* workflow version, commit, or hash
* whether this prompt conflicts with the loaded workflow

If the canonical workflow cannot be loaded and the project requires it, stop and produce a recovery handoff only.

## 0A. Workflow-load and session boundary anchor (#403)

The MCP gate is the authority — not local file viewing.

Before any reviewer mutation:

1. Record pre-review commands with `gitea_record_pre_review_command` when they
   are not automatically classified (inventory/diagnostic commands may be
   recorded explicitly for proof).
2. Call `gitea_load_review_workflow` to establish workflow hash proof **and**
   session boundary state in the same in-process session proof.
3. Do not claim the workflow was loaded by reading
   `skills/llm-project-workflow/workflows/review-merge-pr.md` as a local file;
   that narrative does not satisfy the validator.

Allowed before workflow load (classify as `read_only_inventory` or
`diagnostic`):

* `gitea_whoami`, `gitea_resolve_task_capability`, `gitea_list_prs`,
  `gitea_view_pr`, `gitea_get_runtime_context`
* `git fetch` / `git remote update` for inventory
* `git status`, `git worktree list` (read-only)

Boundary violations (block downstream reviewer mutations even after load):

* validation commands (`pytest`, `python -m unittest`) in the main checkout
* local profile/credential/config inspection (`profiles.json`, `gitea-mcp.json`,
  `.env`, keychain dumps)
* MCP repair (`pkill`, MCP config edits)
* git mutations before workflow load

Final reports must include a structured **Workflow-load helper result** block
copied from `gitea_load_review_workflow`, including at minimum:

* `workflow_hash`
* `final_report_schema_hash`
* `boundary_status` (`clean` or `violation`)

## 1. Start with live identity, profile, runtime, and capability checks

Prove:

* authenticated identity
* active reviewer profile
* repo/project
* runtime context
* exact capabilities needed for inventory, review, request-changes, approve, comment, and merge

A nearby capability does not count.

Examples:

* `create_issue` does not authorize `issue_comment`
* `review_pr` does not authorize `merge_pr`
* `issue_comment` does not authorize `create_issue`
* `review_pr` does not authorize `request_changes` unless the capability explicitly covers that operation
* `review_pr` does not authorize `approve` unless the capability explicitly covers that operation

If capability cannot be proven, stop and produce a recovery handoff only.

## 2. Stop immediately on blocked infrastructure

If any of the following appears, stop immediately:

* `infra_stop`
* MCP reconnect failure
* stale capability state
* dirty control checkout
* dirty review worktree
* missing capability
* workspace mismatch
* stale target branch state
* broken canonical workflow loading
* failed required preflight
* capability resolver warning that says the current state may be unsafe
* stale or inconsistent runtime context

Do not continue PR selection, validation, review, approval, merge, cleanup, or reconciliation.

Produce an executable recovery handoff only.

Blocked recovery handoffs must not include direct approve or merge replay commands.

Blocked handoffs must say to rerun the full workflow after the blocker clears.

Do not preserve stale `ready to merge`, `approved`, or `all gates passed` state across MCP failures, reconnects, or capability re-resolution.

## 3. Author safety

If you are the PR author, stop immediately.

Do not approve, request changes, comment as reviewer, merge, close, or mutate the PR.

Produce a recovery handoff only.

If author identity cannot be proven, stop and produce a recovery handoff only.

If authenticated identity changes during the run, restart identity/profile/capability checks before any further mutation.

## 4. Main checkout rule

The main project checkout must stay on `master`, `main`, or `dev`.

Do not review, test, fix, merge, resolve conflicts, or cleanup from the main checkout.

Do not run tests in the main checkout.

Do not run baseline validation in the main checkout.

All PR validation and baseline comparison work must happen under the project’s `branches/` directory.

No exceptions for small fixes, docs, tests, cleanup, conflict resolution, emergencies, or “just one file.”

Updating the main checkout is allowed only after a successful merge and only if the project workflow explicitly allows stable-branch update.

Do not update the main checkout if merge failed, was blocked, or produced reconciliation-only status.

If the main checkout is dirty before selection, stop and produce a recovery handoff.

If the main checkout becomes dirty during the run, stop and produce a recovery handoff unless the only change is the explicitly allowed post-merge stable-branch fast-forward.

## 5. No raw MCP repair during normal review

Do not run `pkill`, kill MCP processes, edit MCP config, restart servers, or perform control-checkout repair during normal PR review.

If MCP repair is required, stop PR review and produce a separate `CONTROL-CHECKOUT REPAIR MODE` handoff.

Do not mix MCP repair mode with PR review/merge mode.

Do not use successful repair as permission to resume the same review flow. After repair, rerun the full workflow from the beginning.

## 6. No background task tools

Do not use `schedule`, `manage_task`, background jobs, async waits, or delayed task tools during PR review/merge.

Use direct commands and MCP tools only.

If a required action cannot complete synchronously, stop and produce a recovery handoff.

Long synchronous commands, such as a test suite, are allowed only if they are run directly and reported with exact command, working directory, and result.

Do not say “I will check later,” “I will monitor,” or “I will continue in the background.”

## 7. No local Gitea fallback during normal review

During normal reviewer workflows, do not read Gitea profile secret files.

Do not inspect or open files such as:

* `profiles.json`
* local token stores
* credential files
* local Gitea auth/profile config files
* `.env` files containing Gitea credentials
* keychain dumps
* token helper outputs

Do not run local Gitea helper scripts when MCP tools are available.

Use MCP tools for Gitea operations.

Local fallback is allowed only in explicit recovery mode when MCP is unavailable and identity/profile/capability can be independently proven.

If local fallback is used, report:

* why MCP was unavailable
* exact identity proof
* exact profile proof
* exact repo proof
* exact capability proof
* exact local command used

Do not use local fallback to bypass MCP gates.

## 8. Build a complete live PR inventory

List all open PRs.

Follow pagination until the tool proves there are no more pages.

Do not assume inventory is complete.

Do not claim `oldest eligible PR`, `next eligible PR`, or complete queue inventory unless pagination is proven.

Pagination proof must not rely on assumed default API page size.

Inventory is complete only if one of the following is proven:

* the MCP response explicitly says there is no next page / `has_more=false` / final page
* the workflow traversed pages until an empty page or explicit final page was returned
* the tool response includes total-count or pagination metadata proving all open PRs were returned
* the request explicitly set `page` / `limit` / `per_page`, and the response explicitly proves the server honored that page size and did not truncate results

Do not say “inventory complete” merely because the result count is less than an assumed default page size.

If the tool returns exactly the requested page size, assume there may be more pages unless the tool proves otherwise.

If pagination metadata is absent and the tool cannot page, report `INVENTORY_PAGINATION_UNPROVEN` and stop unless the canonical workflow explicitly permits continuing with an incomplete inventory.

For each open PR, identify:

* PR number
* title
* author
* base branch
* head branch
* draft/WIP state
* mergeability
* linked issue
* current head SHA
* whether the PR appears already landed

Final report must include pagination/final-page proof.

## 9. Queue ordering proof rule

State the queue ordering policy before selecting a PR.

If the project uses oldest-first review, explicitly sort or reason by PR number or created date.

Do not rely on the API response order unless the tool proves that order matches the project policy.

If newer PRs appear before the selected PR in the API response, explain that they are newer and not earlier in the queue.

Final report must include:

* queue ordering policy used
* whether API order matched that policy
* selected PR’s position under that policy
* earlier PRs considered
* earlier PRs skipped and why

If queue ordering cannot be proven, stop and produce a recovery handoff.

## 10. Select the next actionable PR using project rules

If the open PR queue is empty:
* First, look at approvals next to see if there are pending approvals or approved PRs that need attention/merge.
* Next, look at issues next to see if there are open issues requiring action/fixes.

Do not review your own PR.

Do not review stale, draft, blocked, duplicate, already-owned, dependency-blocked, already-landed, already-requested-changes work unless the rules explicitly allow it.

Do not skip an earlier actionable PR without documenting why.

Use these terms precisely:

* `Oldest open PR requiring action`
* `Eligibility class`
* `Next review/merge eligible PR`

Do not call an already-landed PR `eligible for review` or `eligible for merge`.

If the oldest open PR is already landed, classify it as:

`ALREADY_LANDED_RECONCILE_REQUIRED`

If the oldest open PR is `ALREADY_LANDED_RECONCILE_REQUIRED`, stop normal review immediately unless project policy explicitly allows skipping reconciliation blockers.

Report:

`Queue blocked by already-landed PR requiring reconciliation`

Do not keep rerunning normal review on the same already-landed PR.

If the selected PR is not the oldest open PR requiring action, report why every earlier PR was not actionable.

## 11. Skip-prior-PR rule

Do not skip an earlier open PR without live proof or explicitly labeled prior proof from current-session review feedback.

If skipping because of prior `REQUEST_CHANGES`, fetch current review feedback and verify the head SHA has not changed since the blocking review.

If skipping because of non-mergeable status, fetch live PR state and provide conflict proof.

Report for every skipped earlier PR:

* skipped PR number
* current head SHA
* blocking category
* blocking review decision, if applicable
* blocking review head SHA, if applicable
* mergeability result, if applicable
* conflict proof command/tool, if applicable
* conflicting files, if available
* whether the head changed after the blocker
* reason it remains blocked

If blocker proof cannot be fetched, classify the PR as:

`BLOCKER_STATUS_UNVERIFIED`

Then stop or produce a recovery handoff according to project policy.

Do not skip an earlier PR based only on memory from a previous session.

## 11A. Skipped PRs are read-only

Skipping an earlier PR must be a read-only classification step.

Do not submit `REQUEST_CHANGES`, approve, comment, merge, close, or otherwise mutate a skipped PR.

If an earlier PR already has unresolved `REQUEST_CHANGES` at the same head SHA, do not duplicate the review. Fetch the review feedback, verify the head SHA is unchanged, classify the PR as blocked, and move on only if project policy allows skipping blocked PRs.

If the head SHA changed after the blocking review, the PR is not safely skippable based on the old blocker. Re-evaluate it according to queue policy.

The final report must distinguish:

* prior blocker reused
* blocker revalidated live in this session
* blocker status unverified

Do not claim live proof unless the proof command or MCP tool ran in the current session.

If you mutate a skipped PR, stop immediately and report a workflow violation.

## 12. Non-mergeable skip proof rule

Do not skip a non-mergeable earlier PR without live proof.

For every skipped non-mergeable PR, report:

* PR number
* current head SHA
* mergeability result
* command/tool used to prove conflict
* conflicting files, if available
* whether head changed since any prior blocker

If conflict proof cannot be retrieved, classify the PR as:

`MERGEABILITY_UNVERIFIED`

Then stop or produce a recovery handoff according to project policy.

Conflict proof must be obtained in a session-owned diagnostic worktree under `branches/`, unless the MCP tool itself provides exact conflict files and proof.

Do not perform conflict proof in the main checkout.

## 13. Prior request-changes override rule

Before approving a PR, fetch current PR review feedback.

If the PR has a prior `REQUEST_CHANGES` review, report:

* blocking reviewer
* blocking review timestamp
* blocking review head SHA
* current head SHA
* blocker text
* whether the head changed since the blocker

If the head changed after the request-changes review, verify the new head addresses the blocker before approving.

If the head did not change after the request-changes review, do not approve unless you prove one of:

* the earlier blocker was incorrect
* the validation environment was wrong
* the issue was resolved externally in a way that does not require PR head changes
* project policy explicitly permits overriding that blocker with proof

If this proof is missing, do not approve.

If the selected PR still has unresolved `REQUEST_CHANGES` at the same head SHA and no override proof exists, submit `REQUEST_CHANGES` only if no equivalent unresolved request-changes review already exists at that same head SHA. Otherwise stop with a recovery handoff or classify it blocked according to project policy.

Final report must include the prior blocker, current head, whether head changed, and why approval is safe.

## 14. Pin the PR head SHA before validation

Re-fetch the selected PR.

Record the current head SHA.

Call this the `candidate head SHA`.

Do not call it the `reviewed head SHA` until validation and diff review have actually passed.

If the head SHA changes before review submission or merge, restart review or stop.

If the head SHA changes after approval but before merge, do not merge. Restart review or stop.

Final report must include:

* candidate head SHA
* reviewed head SHA, only if validation and diff review passed
* whether head changed during the run

## 15. Refresh target branch before ancestry checks

Fetch the PR target branch from the remote before checking whether the PR head is already landed.

Do not rely on stale local `master`, `main`, or `dev`.

Record the fetched target branch SHA used for ancestry checks.

If the target branch cannot be fetched or verified, stop and produce a recovery handoff.

`git fetch`, `git remote update`, and any command that updates refs must be reported under `Git ref mutations`, not read-only diagnostics.

## 16. Already-landed PR gate

After pinning the PR head SHA and refreshing the target branch, check whether the PR head SHA is already an ancestor of the target branch.

If the PR head SHA is already landed on the target branch, stop normal review/merge flow.

Do not approve the PR.

Do not request changes unless there is a real code/review blocker.

Do not call the merge API.

Produce an `ALREADY_LANDED_RECONCILE_REQUIRED` handoff instead.

The handoff must include:

* PR number/title
* eligibility class
* candidate head SHA
* reviewed head SHA: `none`
* target branch
* target branch SHA
* ancestor proof
* linked issue status, if verified
* recommended reconciliation action
* required capabilities for reconciliation

Any close/comment/issue mutation requires separate exact capability proof.

Do not tell the user or another agent to manually close a PR or issue unless the handoff clearly says capability must be proven first or an authorized human must do it.

## 17. Linked issue verification

If the PR claims to close or link an issue, fetch the linked issue live before reporting its status.

If the linked issue was not fetched live in the current session, report:

`Linked issue status: not verified in this session`

Do not claim `issue open`, `issue closed`, or `issue resolved` without live proof.

Any PR close, issue close, or comment recommendation must say exact capability must be proven first, unless an authorized human performs it.

The linked issue number in diagnostics, final report, and controller handoff must match the selected PR.

Do not include stale issue numbers from prior PRs.

If linked issue status changes after merge, fetch the issue again before reporting post-merge status.

## 18. Review worktree ownership rule

Prefer a fresh session-owned review worktree under `branches/`, named like:

`branches/review-pr<PR_NUMBER>-<branch-name>`

Do not reuse a branch-named or existing worktree unless safe-reuse proof passes.

Safe-reuse proof must include:

* exact worktree path
* worktree is inside `branches/`
* worktree is not the main checkout
* worktree is not owned by another active task/session
* clean tracked state
* clean untracked state
* current branch/head before reset
* reset target SHA
* explicit project policy allowing reuse/reset

Do not run `git reset --hard`, `git clean`, checkout, or other destructive worktree commands unless the worktree is session-owned or safe-reuse proof passes.

If safe-reuse proof cannot be produced, create a fresh session-owned review worktree.

## 19. Reviewer worktree no-edit rule

The PR validation worktree is for validating the submitted PR head only.

Do not edit files in the PR validation worktree.

Do not apply local fixes in the PR validation worktree.

Do not run official validation after modifying the PR validation worktree.

If you need to test a possible fix, create a separate diagnostic scratch worktree and label it clearly:

`Diagnostic local experiment — not PR-head validation`

Any diagnostic edits must be reported under `File edits by reviewer`.

Diagnostic test results must not be reported as official PR validation results.

Do not say `File edits by reviewer: none` if any file was edited in any review, diagnostic, baseline, or artifact worktree.

## 20. Validate in a session-owned worktree under `branches/`

Prove:

* exact review worktree path
* worktree is inside `branches/`
* worktree is clean before validation
* PR head is checked out in that worktree
* review worktree HEAD matches candidate head SHA

Run required validation:

* tests
* compile checks
* lint checks
* diff checks
* secret/provenance checks
* dangerous artifact checks
* project-specific validation

If validation fails, request changes with exact blockers, unless an equivalent unresolved request-changes review already exists at the same head SHA.

If an equivalent unresolved request-changes review already exists at the same head SHA, do not duplicate it. Stop with a final report or recovery handoff according to project policy.

If validation cannot run, do not approve or merge.

## 21. Official validation integrity rule

Record the PR head SHA before validation.

Prove the validation worktree is clean before validation.

Run validation against the unmodified PR head.

After validation, prove the validation worktree is still clean.

If validation fails, submit `REQUEST_CHANGES` using the unmodified PR-head failure, unless an equivalent unresolved request-changes review already exists at the same head SHA.

If the worktree was edited after validation for diagnosis, report it separately and do not use post-edit test results as official validation.

If official validation was contaminated by a local edit, stop and produce a recovery handoff.

Official validation integrity status must be one of:

* `passed`
* `failed`
* `not run`
* `contaminated — recovery required`

Do not invent a softer status.

## 21A. Validation failure history rule

Every validation failure observed during the review session must appear in the
final report, even if a later rerun passes.

Before claiming `Validation: passed`, list every failed validation command from
the session under `Validation failure history`.

For each failure, report:

* command
* failing test or error
* suspected cause
* whether it reproduced
* whether it exists on baseline master
* evidence for whether it is or is not PR-caused

If a later rerun passes, also report:

* what changed between runs
* whether environmental state was cleaned (for example `/tmp` lock files)
* why the pass is trustworthy

If the cause is unknown, do not erase the earlier failure with plain
`Validation: passed`. Use a status such as
`passed after transient failure investigation`.

`gitea_validate_review_final_report` rejects reports that omit known earlier
validation failures when `validation_session.observed_failures` is supplied.

## 21B. Validation status taxonomy (#406)

When the final report summarizes how validation concluded, use one of these
**validation status** labels (distinct from per-command pass/fail entries):

* `passed` — raw PR-head validation passed on the unmodified head.
* `failed` — raw PR-head validation failed and no allowed resolution path
  was proven.
* `baseline-equivalent failure accepted` — only when a clean baseline
  worktree under `branches/` proves matching failure signatures on the target
  branch (baseline path, target SHA, exact commands, failure lists, and
  `failure signatures match: true`).
* `raw-head failure resolved by merge simulation` — raw PR-head validation
  failed, but merge simulation into the current target passed cleanly; report
  merge simulation under `Worktree/index mutations` with full #317 proof.
* `passed after transient failure investigation` — a later run passed after an
  earlier failure in the same session; document the failure history (#396).

Do not use `baseline-equivalent failure accepted` when only merge simulation
resolved the failure. Do not use bare `passed` when raw PR-head validation
failed unless one of the resolution statuses above applies.

## 22. Baseline validation rule

Do not run tests in the main checkout.

If the PR full suite fails and you need to compare failures against `master`, create a clean baseline worktree under `branches/`, such as:

`branches/baseline-master-pr<PR_NUMBER>`

Baseline comparison must include:

* baseline worktree path
* baseline target SHA
* PR head SHA
* exact command run on both worktrees
* baseline failures
* PR failures
* proof the failure signatures match
* proof the baseline worktree was clean before and after validation
* proof the PR worktree was clean before and after validation

Do not approve a PR with full-suite failures unless:

* project policy allows baseline comparison
* baseline proof is complete
* failure signatures match
* new/changed tests pass
* failures are proven unrelated to the PR

If full-suite failures differ or proof is incomplete, submit `REQUEST_CHANGES`, unless an equivalent unresolved request-changes review already exists at the same head SHA.

Do not claim “same as master” unless the clean baseline worktree proof is included.

Do not claim “full-suite failures are pre-existing” unless baseline proof is complete and the failure signatures match.

## 23. Validation command proof rule

Before any diff, test, or compile validation, record in the same command
transcript or final report:

* `pwd` or explicit working directory
* `git rev-parse HEAD`
* `git status --short --branch`
* expected PR head SHA (candidate head SHA)

Validation commands must use one of:

* `git -C <review_worktree> ...`
* `cd <review_worktree> && ...` in the same command
* tool-provided explicit working-directory metadata

Do not rely on inferred shell cwd from a prior command in a different block.

`gitea_validate_review_final_report` rejects validation claims without
cwd/HEAD proof when `validation_session` is supplied.

Baseline validation must document baseline worktree path, baseline target SHA,
cwd proof, exact baseline command, and baseline result using the same rules.

Report the exact validation command as executed.

Report the working directory where validation ran.

If using bare `pytest`, also report:

* `which pytest`
* `pytest --version`
* whether it resolves to the project venv

Prefer the project venv executable when available, for example:

`/Users/jasonwalker/Development/Gitea-Tools/venv/bin/pytest tests/`

Do not summarize validation as just `pytest passed` unless the exact command, working directory, and result are included.

For every validation command, report:

* command
* working directory
* exit code or pass/fail result
* summary count if available
* whether it was official PR-head validation, baseline validation, or diagnostic-only validation

## 24. Local merge simulation rule

A local merge simulation is not read-only.

Commands such as the following must be reported as worktree/index mutations:

* `git merge --no-commit`
* `git merge --abort`
* `git reset`
* `git checkout`
* `git clean`
* `git worktree add`
* `git worktree remove`

If merge simulation is needed, prefer a separate session-owned diagnostic worktree.

If merge simulation is run in the review worktree, prove:

* worktree clean before simulation
* exact merge command
* result/conflict status
* abort/reset command
* worktree clean after abort

Do not list merge simulation under read-only diagnostics.

Do not call the worktree clean after simulation unless clean status was actually checked after abort/reset.

If conflicting files are reported, they must come from current-session proof.

## 25. Review the actual diff

Check:

* correctness
* tests
* scope
* security boundaries
* workflow rule compliance
* whether the PR really closes or satisfies the linked issue
* unrelated changes
* dangerous generated artifacts
* secrets
* provenance markers
* temporary agent files
* whether the PR is redundant because the diff is already present on the target branch
* whether the PR modifies reviewer workflow, MCP gates, profiles, routing, or authorization boundaries
* whether documentation and tests match behavior

Do not approve based only on test pass/fail status. Review the diff.

Report files reviewed.

## 26. Review decision

If blockers exist, submit `REQUEST_CHANGES`, unless an equivalent unresolved request-changes review already exists at the same head SHA.

If non-blocking notes exist, comment clearly only if exact comment capability is proven.

If everything passes, approve only if exact approve/review capability is proven.

Do not approve and merge unless both actions are separately authorized.

Submit review only after dry-run review passes if the tool supports dry run.

Do not approve already-landed, redundant, stale, or reconciliation-only PRs.

Do not approve a PR with unresolved prior `REQUEST_CHANGES` unless the prior-request-changes override rule is satisfied.

Do not submit review mutations on skipped PRs.

## 26A. Terminal review mutation hard-stop

A live review mutation is terminal for the current reviewer run except for same-PR merge continuation after approval.

Terminal review mutations include:

* `REQUEST_CHANGES`
* `APPROVED`
* review comment submission
* PR comment submission used as review feedback
* any tool that records final review decision state
* any tool that changes PR review state
* any tool that consumes a session review-mutation budget

After `REQUEST_CHANGES` or blocking feedback, stop immediately and produce the final report.

After `APPROVED`, continue only to merge preflight and merge for the same selected PR, if merge is authorized and all merge gates pass.

Do not mutate one PR and then continue reviewing, approving, requesting changes, commenting on, or merging another PR in the same run.

Do not consume a live review mutation on an earlier skipped PR.

If a live mutation happens on a non-selected or skipped PR, stop immediately and report the workflow violation.

If a tool blocks further review mutation because a live mutation already occurred, stop immediately. Do not continue validation of another PR.

The final report must identify:

* the terminal mutation
* PR number affected by the terminal mutation
* whether same-PR merge continuation was allowed
* whether the run stopped as required

## 26A-1. Stale durable review-decision lock cleanup (#594)

After #559, the review-decision lock is durable under
`~/.cache/gitea-tools/session-state/` (for example
`review_decision_lock-prgs-reviewer.json`). That durability can outlive the work
it protects: a terminal `approve` / `request_changes` on a PR that is already
**merged or closed** still hard-stops later unrelated reviews under #332.

**Do not** delete session-state files by hand. Use the canonical MCP path.

### When cleanup is allowed

Use `gitea_cleanup_stale_review_decision_lock` when:

1. `gitea_mark_final_review_decision` / live review is blocked by
   `terminal review mutation already consumed ... (#332)`.
2. Live Gitea state for the **last terminal mutation's PR** is **merged** or
   **closed** (no same-PR merge sequence remains).
3. Active profile identity matches the durable lock (reviewer profile with
   `gitea.pr.review` for `apply=true`).
4. Optional pin: pass `expected_terminal_pr` to the prior terminal PR number
   (for example `586` when resuming after a merged #586 lock).

Recommended sequence:

```text
gitea_whoami
gitea_resolve_task_capability(task="cleanup_stale_review_decision_lock")
gitea_cleanup_stale_review_decision_lock(apply=false, remote=..., org=..., repo=...)
# inspect is_moot / cleanup_allowed / last_terminal_pr
gitea_cleanup_stale_review_decision_lock(
    apply=true,
    expected_terminal_pr=<last_terminal_pr>,
    remote=..., org=..., repo=...,
)
gitea_resolve_task_capability(task="review_pr")
gitea_load_review_workflow()
# continue formal mark + submit for the *new* PR
```

Successful `apply=true` clears memory + durable store and returns an audit
record (and posts a durable PR comment on the terminal PR when
`post_audit_comment` is true and comment permission allows).

Same-profile auto-expire: after `gitea_merge_pr` succeeds for the PR that this
profile just approved, the decision lock for that profile is cleared
automatically. Cross-profile locks (for example reviewer approve, merger merge)
still require `gitea_cleanup_stale_review_decision_lock`.

### When cleanup is forbidden

Refuse / fail-closed — keep #332 hard-stop — when:

* the last terminal PR is still **open** (active review or merge sequence may
  still apply)
* live PR state cannot be fetched (ambiguous)
* no lock or no terminal live mutation exists
* profile identity does not match the lock
* apply requested without authenticated identity or `gitea.pr.review`
* `expected_terminal_pr` does not match the last terminal PR

Do **not** use cleanup to:

* bypass #332 on an open PR
* replace `gitea_authorize_review_correction` for a mistaken review on a still
  active PR (#211)
* auto-approve or auto-merge any PR
* justify `rm` of session-state files

## 26B. Per-PR reviewer lease (#407)

Parallel reviewer sessions are allowed only when each session holds a distinct,
live PR lease.

Before validation or review mutation on a selected PR:

1. Call `gitea_acquire_reviewer_pr_lease` with worktree path, candidate head SHA,
   and target branch SHA.
2. Post heartbeats via `gitea_heartbeat_reviewer_pr_lease` before validation,
   after validation, before review mutation, and before merge.
3. Do not approve, request changes, or merge unless the in-session lease
   matches the selected PR.

If PR head or target branch advances during the lease, stop and refresh
inventory before continuing.

Final reports must include lease session id, acquisition proof, heartbeat
status, and release/blocked status.

## 26B-1. Merger lease adoption (#536)

When review and merge are **different sessions**, the merger must adopt the
reviewer's lease — never manually seed `reviewer_pr_lease._SESSION_LEASE`.

Before merge in a merger-only session:

1. Confirm `approval_at_current_head` via `gitea_get_pr_review_feedback`.
2. Call `gitea_adopt_merger_pr_lease` from a clean merger worktree under
   `branches/`, passing `expected_head_sha` pinned to the approved head.
3. Quote the adoption comment id and `adopted_from_session_id` in the handoff.
4. Proceed to `gitea_merge_pr` only after adoption succeeds.

Manual in-process lease seeding is rejected by mutation gates (fail closed).

## 26C. Conflict-fix lease and stale-head protection (#399)

Before validating, approving, or merging a PR:

1. Check for an active conflict-fix lease on the PR; stop if one is active.
2. Pin `expected_head_sha` before validation and pass it to
   `gitea_mark_final_review_decision`, `gitea_submit_pr_review`, and
   `gitea_merge_pr`.
3. Re-fetch live PR head immediately before approval and merge; refuse when
   live head differs from the reviewed SHA.

Final reports must state:

* reviewed head SHA
* final live head SHA before approval
* final live head SHA before merge
* whether any push occurred during validation

## 26D. PR synchronization and conflict-remediation lifecycle

**Do not treat “approved” as the final readiness state.** For every approved
open PR, call `gitea_assess_pr_sync_status` (native MCP only) and route by
`recommended_next_action`:

| Action | Meaning | Next role / tools |
|--------|---------|-------------------|
| `merge_now` | Valid approval at exact current head; conflict-free; update not required; checks ok | Merger: sanctioned merge workflow only — **do not** update the branch |
| `update_branch_by_merge` | Behind live base; protection requires current base; Gitea can merge base without conflicts | **Author only:** `gitea_update_pr_branch_by_merge` with pinned `expected_pr_head_sha` + `expected_base_head_sha` |
| `author_conflict_remediation` | Conflicts / not auto-updatable | **Author only:** existing `branches/` worktree, issue lock, merge master, resolve, test, push; never force-push/rebase |
| `fresh_review_required` | Head changed after approval, or update/remediation produced a new head | Independent reviewer at the **new exact head**; old approvals/leases/verdicts are void |
| `blocked` | Other gate (checks, incomplete facts, closed PR, …) | Diagnose; do not merge or update |

### Hard rules

* Pin both expected PR head and expected base head for every update. Either
  race → fail closed with no partial mutation.
* Never rebase or force-push author branches.
* Never update an author branch from a reviewer or merger profile.
* Never preserve approval, reviewer lease, merger lease, or prepared verdict
  across a head change.
* Process PRs **sequentially**: synchronize/remediate one → review new head →
  merge → refresh live `master` → reassess the next PR. Do not update every
  open PR at once (each merge restales the rest).
* Conflict remediation only in the existing dedicated issue/PR worktree under
  `branches/`. Do not delete that worktree until the updated PR is merged and
  cleanup eligibility is proven.
* Post-merge: canonical cleanup handoff to reconciler (section 28).

### After `gitea_update_pr_branch_by_merge` succeeds

1. Treat former-head approval as invalidated.
2. Release/supersede obsolete reviewer and merger leases.
3. Route `recommended_next_action=fresh_review_required` at the new head.
4. Independent re-review, then merger lease/adopt + merge at the new head only.

All Gitea reads/mutations use native MCP. Never substitute direct API, curl,
tea/gh, Web UI mutation by an LLM, database changes, raw Git push, or token
access.

## 27. Merge rules

Before merge, call `gitea_assess_pr_sync_status` when the PR is approved/open
and may be behind base. Only proceed with merge when
`recommended_next_action` is `merge_now` and approval remains at the current
head. Then rerun fresh live checks:

* whoami
* active profile/runtime
* exact merge capability
* author safety
* PR re-fetch
* reviewed head SHA unchanged
* visible APPROVED review applies to the **current live PR head SHA** (`approval_at_current_head`); if the head moved after approval, re-review at the new head before merge (#471)
* target branch freshly fetched
* PR still open
* PR still mergeable
* PR head is not already landed
* required checks still pass
* linked issue handling is correct
* review worktree is clean

Merge only after all gates pass.

Do not merge if:

* MCP reconnect failed
* capability state is stale
* worktree is dirty
* PR head changed
* approval is stale (approved SHA ≠ current live head SHA)
* validation failed
* inventory was incomplete
* PR is already landed
* target branch is stale
* you are the PR author
* prior request-changes override proof is missing
* terminal review mutation occurred on a different PR earlier in the run
* final decision was not marked for the selected PR, if the workflow requires final-decision marking

If the merge tool requires an explicit confirmation string, report:

* first gated/no-op call, if any
* confirmation string used
* which call actually performed the merge

Rejected, dry-run, or confirmation-gated merge calls must not be reported as performed merge mutations.

## 28. After merge

Confirm:

* merge result
* target branch updated
* merged commit/SHA, if available
* selected PR head SHA is now ancestor of target branch
* linked issue status if the PR claims to close an issue

Clean only the session-owned `branches/` review worktree if the project workflow explicitly allows cleanup.

Do not delete source branches from reviewer mode with raw git commands. Commands
such as `git branch -d <branch>` and `git push <remote> --delete <branch>` are
not proof of authorized cleanup and bypass MCP role/capability gates. Merged PR
source branch cleanup must use an explicit MCP cleanup tool such as
`gitea_cleanup_merged_pr_branch` or another approved cleanup helper with exact
`gitea.branch.delete` authority, merged-PR proof, no open PR using the branch,
target-branch ancestry proof, a `branches/` worktree path, and cleanup mutations
reported separately from review mutations.
Review, baseline, and merge-simulation worktrees created during this run are
transient and are removed automatically at successful completion once they are
clean, carry no open PR, and hold no active lease (#401). Use
`gitea_audit_worktree_cleanup` (read-only) to classify `branches/` entries; only
`clean_stale_removable` and `detached_review_leftover` may be removed, one-by-one,
after `git worktree list` proof plus per-worktree proof of path, branch/HEAD,
clean/dirty status, no active PR/lease, and the removal result. Dirty,
active-PR, active-issue, and leased worktrees are never deleted automatically; a
failed removal must be reported with the leftover path and reason.

Do not delete or mutate unrelated branches/worktrees.

Do not touch the main checkout except to update the stable branch after merge if explicitly allowed by the workflow.

Any stable-branch update after merge is a git mutation and must be reported.

Do not update the main checkout if merge failed, was blocked, or produced reconciliation-only status.

If any local artifact is created after final cleanup, run and report a new final status check.

## 28A. Post-merge cleanup proof checklist (#402)

Successful tool execution is not proof that cleanup was authorized. Before claiming remote branch deletion or local worktree removal, the final report must carry the full safety checklist below. If any gate is missing, report `CLEANUP_SKIPPED` with the exact blocker — never perform cleanup and never claim it was performed.

### Remote branch deletion checklist

When `gitea_delete_branch` (or equivalent) deletes the merged PR head branch, report:

* Delete-branch capability resolved: name the task (`delete_branch` / `cleanup_branch` / `reconcile_merged_cleanups`) and permission (`gitea.branch.delete`) with resolver proof before the delete call
* Merge result: merged
* Merge commit SHA: full 40-character SHA
* Merged PR head branch / deleted branch: exact branch name (must match)
* Branch protection: none / branch is not protected
* Open PR inventory proof: no other open PR references the branch
* Active heartbeat/claim/lease: none

### Local worktree removal checklist

When removing session-owned review/simulation worktrees under `branches/`, report:

* Session-owned worktree path: exact path under `branches/`
* Pre-removal tracked state: clean
* Pre-removal untracked state: clean
* Git worktree list after removal: command output or equivalent proof

### Skipped cleanup

If any gate fails, report:

* Cleanup outcome: `CLEANUP_SKIPPED`
* Cleanup blocker: exact missing gate (for example `gitea.branch.delete capability not resolved`)

Skipped cleanup with an exact blocker passes validation. Performed-cleanup claims without the checklist fail validation.

## 28B. MCP-native cleanup only (#517)

Post-merge cleanup of leases, comments, branches, and worktrees must go through
explicit MCP tools — never raw git, curl/API scripts, or ad hoc helper scripts.

Merger and reviewer sessions must **not** perform cleanup inline. Record:

* **Merge mutations** — only `gitea_merge_pr` (or review mutations for review-only runs)
* **Cleanup mutations** — only authorized reconciler MCP tools, cited by exact tool name

Authorized cleanup tools include `gitea_reconcile_merged_cleanups`,
`gitea_cleanup_post_merge_moot_lease`, `gitea_delete_branch`, and
`gitea_cleanup_merged_pr_branch` (when available). Lease cleanup uses
append-only release comments — never delete another session's lease comment.

Hand cleanup to a `prgs-reconciler` session with `gitea.branch.delete` capability
proof. Raw `git branch -d`, `git push --delete`, and comment-deletion API calls
are blocked in final-report validation.

Template: `templates/post-merge-cleanup-handoff.md`.

## 29. Recovery handoff rules

If blocked, produce a recovery handoff with:

* exact blocker
* failed tool/function, if any
* repo/project
* selected PR, if one was safely selected
* eligibility class
* candidate head SHA, if known
* reviewed head SHA, only if validation and diff review passed
* target branch and target branch SHA, if known
* linked issue status, or `not verified in this session`
* worktree path, if created
* exact state reached before stopping
* safe next action
* statement that no unsafe mutation was attempted

Blocked handoffs must not include direct approve or merge replay commands.

Blocked handoffs must say to rerun the full workflow after the blocker clears.

If the blocker is a terminal review mutation already consumed in the current run, the handoff must say that the next run must start from the beginning and must not reuse stale ready/approved state.

If the referenced terminal PR is already **merged or closed** on live Gitea, the durable #332 decision lock is **moot**. Do **not** delete session-state files by hand. Use `gitea_cleanup_stale_review_decision_lock` (assess with `apply=false`, then `apply=true` with `expected_terminal_pr` set) from the reviewer profile after identity/profile checks (#594). Cleanup is **forbidden** while that PR is still open.

## 30. Final report must be precise

Include:

* canonical workflow source/version/hash, if available
* authenticated identity/profile
* repo/project
* complete PR inventory proof, including pagination/final-page proof
* queue ordering policy used
* selected PR number/title/author
* eligibility class
* author-safety result
* skipped earlier PRs and live blocker proof or prior blocker proof, if any
* prior request-changes proof, if any
* candidate head SHA
* target branch and target branch SHA used for ancestry checks
* reviewed head SHA, only if validation and diff review passed
* already-landed gate result
* linked issue proof/status, if applicable
* main checkout branch
* main checkout dirty state
* review worktree used: true/false
* review worktree path
* review worktree inside `branches/`: true/false
* review worktree HEAD state: detached/branch/none
* review worktree dirty before validation: true/false/not applicable
* review worktree dirty after validation: true/false/not applicable
* baseline validation worktree, if used
* baseline validation result, if used
* files reviewed
* validation commands and results
* official validation integrity status
* diagnostic scratch experiments, if any
* local merge simulation result, if any
* review decision
* terminal review mutation, if any
* merge preflight results, if merge was attempted
* merge result, if merged
* cleanup result
* blockers, if stopped
* confirmation that the main checkout was not used for task work
* final proof that no unsupported proof wording is used

If the report and actual tool/command log disagree, fix the report before final output.

## 31. Final report must distinguish mutation types

Do not use the legacy field `Workspace mutations`.

Use only precise categories:

* File edits by reviewer:
* Worktree/index mutations:
* Git ref mutations:
* MCP/Gitea mutations:
* Review mutations:
* Merge mutations:
* Cleanup mutations:
* External-state mutations:
* Read-only diagnostics:

`git fetch`, `git remote update`, and any command that updates refs must be listed under `Git ref mutations`, not read-only diagnostics.

If `git reset --hard`, checkout, clean, worktree add/remove, merge simulation, merge abort, or similar commands occurred, report them under `Worktree/index mutations`.

Use precise wording:

* `File edits by reviewer: none`
* `Worktree/index mutations: ...`
* `Git ref mutations: ...`
* `MCP/Gitea mutations: ...`

Do not collapse review, merge, cleanup, or external-state mutations into vague wording.

## 31B. Mutation-capability table (#405)

Every performed mutation requires exact capability proof resolved **before** that
mutation executes. Nearby capabilities never authorize a different operation —
`review_pr` does not authorize `merge_pr`, and `merge_pr` does not authorize
`delete_branch` / `gitea.branch.delete`.

When any mutation beyond a bare review occurs (merge, branch delete, issue
close/comment, etc.), the final report must include a **mutation-capability table**
with one row per performed mutation:

* mutation (tool/action name)
* exact task/capability resolved (for example `merge_pr` / `gitea.pr.merge`)
* result
* order/timestamp proof that capability was resolved before the mutation

If exact capability proof is missing, skip the mutation or stop the workflow —
never claim a performed mutation without its row. Post-hoc capability proof after
the mutation fails validation.

## 31A. Local artifact and report consistency rule

Do not create local walkthrough, notes, markdown, JSON, or report artifacts during reviewer runs unless the canonical workflow or operator explicitly requires it.

If any file is edited, created, generated, or written, report it under `File edits by reviewer`.

For each file write, report:

* exact path
* whether it was inside the repo
* whether it was tracked or untracked
* why it was created
* whether final `git status` was run after the write

Do not say `File edits by reviewer: none` if any file write occurred.

Do not write files after the final clean-status check unless you rerun and report a new final clean-status check.

If the action log says `Edited`, `Created`, `Wrote`, or equivalent, the mutation ledger must include that file.

If the file was created outside the repo, still report it under `File edits by reviewer` and label it `outside repo`.

If the file was created inside the repo, report whether it is tracked or untracked.

If an artifact was created only for operator handoff, state that explicitly.

Default behavior: do not create `walkthrough.md` or similar artifacts unless the workflow explicitly requires it.

## 32. Controller handoff consistency rule

The controller handoff must use the same canonical schema and facts as the final report.

If the already-landed gate fires, the controller handoff must include:

* Eligibility class: `ALREADY_LANDED_RECONCILE_REQUIRED`
* Candidate head SHA:
* Reviewed head SHA: `none`
* Review worktree used: `false`
* Review worktree path: `none`
* Git ref mutations:
* Review decision: `none`
* Merge result: `none`

Do not use these legacy fields:

* `Pinned reviewed head`
* `Scratch worktree used`
* `Workspace mutations`
* `Mutations: None` when any git ref, MCP, Gitea, review, merge, cleanup, or external-state mutation occurred

The narrative report and controller handoff must agree on:

* eligibility class
* candidate head SHA
* reviewed head SHA
* mutation state
* worktree usage
* review decision
* terminal review mutation
* merge result
* linked issue status

If they disagree, fix the report before final output.

## 33. Forbidden final-report claims unless proven

Do not claim:

* `oldest eligible PR`
* `next eligible PR`
* `reviewed head SHA`
* `ready to merge`
* `workspace mutations none`
* `scratch worktree false`
* `all gates passed`
* `inventory complete`
* `already landed`
* `merged`
* `issue closed`
* `manual close required`
* `target branch up to date`
* `read-only merge simulation`
* `no file edits by reviewer`
* `same as master`
* `full-suite failures are pre-existing`
* `prior request-changes resolved`
* `local fallback was safe`
* `live proof`
* `live blocker proof`
* `skipped PR`
* `terminal mutation none`
* `no unsafe mutation`

unless the corresponding proof is included.

If anything blocks safe review or merge, stop immediately and produce an executable recovery handoff.

Do not improvise around the gates.

Do not preserve stale `ready to merge` state across MCP failures or reconnects.

Do not include approve or merge replay commands in a blocked handoff. Say to restart the full workflow after the blocker clears.

## 34. Proof wording enforcement

The following phrases are forbidden unless directly supported by current-session evidence:

* live proof
* live blocker proof
* inventory complete
* same as master
* no file edits
* full-suite failures are pre-existing
* issue closed
* merged
* target branch up to date
* all gates passed
* next eligible PR
* oldest eligible PR
* no unsafe mutation
* worktree clean
* linked issue verified
* PR still mergeable
* head unchanged
* final page
* no next page

If the proof comes from prior review feedback rather than a command/tool run in the current session, label it as prior proof, not live proof.

If relying on prior review feedback at unchanged head SHA, say:

`Prior blocker reused; head SHA unchanged since blocking review.`

Do not say:

`Live blocker proof`

unless the blocker was actually revalidated in the current session.

If a tool call was rejected, confirmation-gated, dry-run-only, or no-op, report it separately from performed mutations.

A dry run is not a mutation.

A confirmation-gated rejected call is not a performed mutation.

A successful merge call is a merge mutation.

A successful review submission is a review mutation.

A final-decision marking call is a review mutation if it records durable review state or consumes review-mutation budget.

## 35. Duplicate request-changes prevention

Before submitting `REQUEST_CHANGES`, fetch current review feedback.

If an unresolved `REQUEST_CHANGES` review already exists at the same head SHA with the same blocker, do not submit another `REQUEST_CHANGES`.

Report:

* prior blocking reviewer
* prior blocking timestamp
* prior blocking head SHA
* current head SHA
* blocker text
* why the blocker still applies
* that no duplicate review mutation was submitted

If the head SHA changed or the old blocker is not equivalent, evaluate normally.

Do not consume the session’s review mutation budget just to duplicate an existing blocker.

## 36. Final self-check before output

Before final output, check the report for contradictions.

Verify:

* if any file was edited, `File edits by reviewer` is not `none`
* if any worktree was added/removed, `Worktree/index mutations` lists it
* if any fetch happened, `Git ref mutations` lists it
* if any review was submitted, `Review mutations` lists it
* if any merge was performed, `Merge mutations` lists it
* if any cleanup happened, `Cleanup mutations` lists it
* if any issue/PR external state changed, `External-state mutations` lists it
* if pagination is claimed complete, final-page proof is present
* if live proof is claimed, the proof ran in the current session
* if same-as-master is claimed, baseline proof is complete
* if issue closed is claimed, linked issue was fetched after merge
* if merged is claimed, merge result and target ancestry proof are present
* if selected PR is claimed next eligible, every earlier PR has proof-backed skip reasoning
* if a terminal mutation happened, no other PR was reviewed or mutated afterward

If any contradiction exists, fix the final report before output.

## 37. Controller handoff schema

End every run with a controller handoff using this schema.

Do not omit fields. Use `none` or `not verified in this session` where appropriate.

Controller Handoff:

* Task:
* Repo:
* Role:
* Identity:
* Active profile:
* Runtime context:
* Selected PR:
* Linked issue:
* Eligibility class:
* Queue ordering policy:
* Inventory pagination proof:
* Earlier PRs skipped:
* Candidate head SHA:
* Reviewed head SHA:
* Target branch:
* Target branch SHA:
* Already-landed gate:
* Author-safety result:
* Prior request-changes state:
* Review worktree used:
* Review worktree path:
* Review worktree inside branches:
* Review worktree HEAD state:
* Review worktree dirty before validation:
* Review worktree dirty after validation:
* Baseline worktree used:
* Baseline worktree path:
* Files reviewed:
* Validation:
* Validation failure history:
* Validation cwd/HEAD proof:
* Official validation integrity status:
* Terminal review mutation:
* Review decision:
* Merge preflight:
* Merge result:
* Linked issue status:
* Main checkout branch:
* Main checkout dirty state:
* Main checkout updated:
* File edits by reviewer:
* Worktree/index mutations:
* Git ref mutations:
* MCP/Gitea mutations:
* Review mutations:
* Merge mutations:
* Cleanup mutations:
* External-state mutations:
* Read-only diagnostics:
* Blockers:
* Current status:
* Safe next action:
* Safety statement:

## 38. Stop conditions summary

Stop immediately and produce a recovery handoff if:

* canonical workflow is required but cannot be loaded
* identity/profile/capability cannot be proven
* authenticated identity is the PR author
* runtime context is blocked
* infra stop appears
* MCP reconnect fails
* capability state is stale
* inventory pagination cannot be proven
* queue ordering cannot be proven
* earlier PR blocker status cannot be verified
* selected PR is already landed
* target branch cannot be fetched
* linked issue cannot be verified when required for decision
* review worktree is dirty
* validation cannot run
* validation fails and request-changes cannot be safely submitted
* baseline comparison is required but incomplete
* prior request-changes override proof is missing
* PR head changes before review or merge
* merge preflight fails
* terminal review mutation occurs on a different PR
* any report contradiction cannot be resolved

Blocked handoffs must not include direct approve or merge replay commands.

Blocked handoffs must say to rerun the full workflow after the blocker clears.

Do not improvise around the gates.
