A reviewer workflow approved and attempted to merge PR #278 although its
head commit was already an ancestor of master, ending in a Gitea 405 and
manual reconciliation. Add the missing pre-mutation gate:
- assess_already_landed_review_gate classifies the pinned candidate
head against the fetched target branch: NORMAL_REVIEW_CANDIDATE,
ALREADY_LANDED_RECONCILE_REQUIRED, or GATE_NOT_PROVEN (fail closed).
Already-landed PRs block approval and the merge API, allow
request-changes only for a real blocker, and require a reconciliation
handoff (PR number/title, candidate head SHA, target branch + SHA,
ancestor proof, linked issue status, recommended action, capability
proof for close/comment mutations). Unchecked ancestry, invalid or
missing SHAs, and a head changed since pinning all fail closed.
Mergeability stays a separate gate.
- assess_already_landed_report_state rejects APPROVED / MERGED /
READY_TO_MERGE wording and missing ALREADY_LANDED_RECONCILE_REQUIRED
state when the session's gate fired, complementing the text-marker
rules from #327 which need the report to admit the landed state.
Tests cover already-landed, normal, non-mergeable, changed-head,
unchecked-ancestry, and invalid-SHA gate paths plus all report-state
verdicts.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>