feat: harden PR queue reconciliation against stale/conflicting state (#166)
- Add updated_at (list_prs), + merged_at/merge_commit_sha/closed_at/updated_at (view_pr) for staleness detection in tool output and inventory. - Surface updated_at in gitea_review_pr inventory report. - Strengthen gitea-pr-review skill steps, _COMMON_WORKFLOWS, _GUIDE_RULES with explicit live reconciliation checklist, 'do not trust prior handoffs', stop on conflict. - Add dedicated live queue reconciliation runbook section in docs/llm-workflow-runbooks.md. - Add passthrough tests and assertions for new fields. - All gates, redaction, author/reviewer separation untouched. - Tests pass, py_compile clean, diff clean, secret sweep clean. Closes #166
This commit is contained in:
+35
-6
@@ -402,8 +402,9 @@ def gitea_list_prs(
|
||||
repo: Override the repository name.
|
||||
|
||||
Returns:
|
||||
List of dicts with 'number', 'title', 'state', 'head', 'base', and
|
||||
'mergeable' ('url' only with the reveal opt-in).
|
||||
List of dicts with 'number', 'title', 'state', 'head', 'base',
|
||||
'mergeable', 'updated_at' ('url' only with the reveal opt-in).
|
||||
The additional 'updated_at' aids stale/conflicting queue detection.
|
||||
"""
|
||||
h, o, r = _resolve(remote, host, org, repo)
|
||||
auth = _auth(h)
|
||||
@@ -418,6 +419,7 @@ def gitea_list_prs(
|
||||
"head": pr["head"]["ref"],
|
||||
"base": pr["base"]["ref"],
|
||||
"mergeable": pr.get("mergeable"),
|
||||
"updated_at": pr.get("updated_at"),
|
||||
}
|
||||
results.append(_with_optional_url(entry, pr.get("html_url")))
|
||||
return results
|
||||
@@ -441,7 +443,10 @@ def gitea_view_pr(
|
||||
repo: Override the repository name.
|
||||
|
||||
Returns:
|
||||
dict with PR details.
|
||||
dict with PR details including 'updated_at', 'merged_at',
|
||||
'merge_commit_sha', 'closed_at' (when present) to support
|
||||
live-state reconciliation and staleness detection against
|
||||
prior reports or handoffs.
|
||||
"""
|
||||
h, o, r = _resolve(remote, host, org, repo)
|
||||
auth = _auth(h)
|
||||
@@ -456,6 +461,10 @@ def gitea_view_pr(
|
||||
"base": pr["base"]["ref"],
|
||||
"mergeable": pr.get("mergeable"),
|
||||
"user": pr.get("user", {}).get("login", ""),
|
||||
"updated_at": pr.get("updated_at"),
|
||||
"merged_at": pr.get("merged_at"),
|
||||
"merge_commit_sha": pr.get("merge_commit_sha"),
|
||||
"closed_at": pr.get("closed_at"),
|
||||
}
|
||||
return _with_optional_url(result, pr.get("html_url"))
|
||||
|
||||
@@ -1404,6 +1413,7 @@ def gitea_review_pr(
|
||||
"linked_issues": list(set(linked)),
|
||||
"labels": labels,
|
||||
"head_sha": pr_head_sha,
|
||||
"updated_at": pr.get("updated_at"),
|
||||
"requires_non_author_reviewer": req_non_author
|
||||
})
|
||||
except Exception as e:
|
||||
@@ -1429,6 +1439,7 @@ def gitea_review_pr(
|
||||
f" Author: {item['author']}\n"
|
||||
f" Base/Head: {item['base']} / {item['head']}\n"
|
||||
f" Head SHA: {item['head_sha']}\n"
|
||||
f" Updated: {item.get('updated_at') or 'unknown'}\n"
|
||||
f" Mergeability: {item['mergeable']}\n"
|
||||
f" Linked Issues: {item['linked_issues']}\n"
|
||||
f" Labels: {item['labels']}\n"
|
||||
@@ -1968,6 +1979,12 @@ _GUIDE_RULES = {
|
||||
"Do not hardcode Gitea identities; always dynamically verify your "
|
||||
"identity and resolve task capability via gitea_resolve_task_capability.",
|
||||
],
|
||||
"live_state_reconciliation": [
|
||||
"Never rely on prior handoffs, chat history, or cached reports for PR queue state.",
|
||||
"Before review or merge decisions: always call gitea_list_prs + gitea_view_pr (for the specific PR), re-fetch remotes, pull master --ff-only, and compare live fields (state, mergeable, head SHA, updated_at, merged_at, merge_commit_sha) against any prior claims.",
|
||||
"Conflicting or stale PR state (e.g. prior said merged but live open, or head/updated mismatch) is a hard blocker: report it explicitly and stop until live state is unambiguous.",
|
||||
"After merge, re-list PRs and re-view the PR (plus verify master contains the merge) before claiming completion.",
|
||||
],
|
||||
}
|
||||
|
||||
_COMMON_WORKFLOWS = [
|
||||
@@ -1978,10 +1995,15 @@ _COMMON_WORKFLOWS = [
|
||||
"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 (must be a reviewer profile/namespace), "
|
||||
"pin the head SHA, validate independently, post a verdict; never review your own work.",
|
||||
"reconcile live state first (list_prs + view_pr selected PR immediately; "
|
||||
"verify state/mergeable/head SHA/updated_at/merged info vs any prior reports; "
|
||||
"git fetch --prune and pull --ff-only master; check linked issue state); "
|
||||
"pin the head SHA; treat conflicting/stale reports as blocker and stop; "
|
||||
"validate independently, post a verdict; never review your own work. "
|
||||
"After merge, re-list and re-view.",
|
||||
"PR merge: reviewer identity (must be a reviewer profile/namespace) + "
|
||||
"eligibility check + pinned head + explicit 'MERGE PR <n>' confirmation, "
|
||||
"only with operator authorization.",
|
||||
"only with operator authorization. Reconcile live state before deciding.",
|
||||
]
|
||||
|
||||
# Skill registry (#128). status: 'available' = backed by tools in this
|
||||
@@ -2042,11 +2064,18 @@ _PROJECT_SKILLS = {
|
||||
"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.",
|
||||
"Reconcile live queue state FIRST (do not trust prior handoffs): "
|
||||
"call gitea_list_prs, then immediately gitea_view_pr on the target; "
|
||||
"verify state, mergeable, head vs current, updated_at/merged info, "
|
||||
"linked issue; git fetch --prune; git checkout master; git pull <remote> master --ff-only. "
|
||||
"If prior report conflicts with live (e.g. 'merged' but live open, or head/updated mismatch), "
|
||||
"report inconsistency and STOP before review/merge.",
|
||||
"Pin the reviewed head SHA (gitea_view_pr) before validating.",
|
||||
"Validate independently: scope vs the linked issue, tests, "
|
||||
"diff check, secret sweep.",
|
||||
"Post the verdict with gitea_review_pr / "
|
||||
"gitea_submit_pr_review.",
|
||||
"After any merge, re-list open PRs and re-view the PR to confirm landed state.",
|
||||
"Do not merge unless the operator explicitly authorizes it.",
|
||||
],
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user