ci: re-enable pr-review bot on glm-5.3-flash with recalibrated output contract

Re-enable the automated PR review (pull_request_target + /oc-review
comments) on zai-coding-plan/glm-5.3-flash via ZHIPU_API_KEY.

Recalibrate the output contract based on an audit of 69 past reviews
(22 PRs, Aug 15-18): 18 of 27 BLOCKED verdicts were template-policing,
3.1 reviews per PR, ~8.4K chars per comment.

- BLOCKED now means code/merge problems only; handoff/template gaps move
  to a separate one-line Handoff flag that never changes the verdict
- re-reviews emit deltas (verdict + what changed) instead of full
  re-emission; push-triggered re-reviews are throttled to one per 15
  minutes (manual /oc-review always runs)
- hard length budgets by change class; the applied-guidance table is
  gone (guidance is applied silently, named only when it produced a
  finding); empty evidence/security sections are omitted
- evidence demands are single-shot and escapable, never raised for
  dependency bumps, string edits, server code, or packaging
- the comment opens with a one-line maintainer verdict (merge / merge
  after X / don't merge because Y), enforced by the workflow
- triage-prs skill treats review:* labels as a pre-sort for verdict
  reviews
This commit is contained in:
Bohdan Triapitsyn
2026-08-28 11:24:23 +03:00
parent b8465ae133
commit aff407288d
3 changed files with 72 additions and 38 deletions
+47 -11
View File
@@ -1,7 +1,12 @@
name: pr-review
on:
workflow_dispatch:
pull_request_target:
types: [opened, synchronize, reopened, ready_for_review, converted_to_draft]
issue_comment:
types: [created]
pull_request_review_comment:
types: [created]
concurrency:
# PR conversation comments arrive as `issue_comment` events, so their PR number
@@ -100,8 +105,40 @@ jobs:
echo "safe=true" >> "$GITHUB_OUTPUT"
- name: Mark review pending
- name: Throttle push-burst reviews
id: throttle
if: steps.pr.outputs.draft == 'false' && steps.safety.outputs.safe == 'true'
env:
GH_TOKEN: ${{ github.token }}
PR_NUMBER: ${{ steps.pr.outputs.number }}
EVENT_NAME: ${{ github.event_name }}
EVENT_ACTION: ${{ github.event.action }}
run: |
# Manual commands always run; only push-triggered re-reviews are throttled,
# so a push burst cannot produce a review per push.
if [ "$EVENT_NAME" != "pull_request_target" ] || [ "$EVENT_ACTION" != "synchronize" ]; then
echo "skip=false" >> "$GITHUB_OUTPUT"
exit 0
fi
last_review_at="$(gh api "repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/comments" --paginate \
| jq -r '[.[] | select(.user.login == "openchamber-bot[bot]" and (.body | contains("<!-- oc-review-meta "))) | .created_at] | last // empty')"
if [ -z "$last_review_at" ]; then
echo "skip=false" >> "$GITHUB_OUTPUT"
exit 0
fi
age="$(( $(date +%s) - $(date -d "$last_review_at" +%s) ))"
if [ "$age" -lt 900 ]; then
echo "Last review was ${age}s ago; skipping push-triggered re-review (15m throttle)."
echo "skip=true" >> "$GITHUB_OUTPUT"
else
echo "skip=false" >> "$GITHUB_OUTPUT"
fi
- name: Mark review pending
if: steps.pr.outputs.draft == 'false' && steps.safety.outputs.safe == 'true' && steps.throttle.outputs.skip != 'true'
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
PR_NUMBER: ${{ steps.pr.outputs.number }}
@@ -199,7 +236,7 @@ jobs:
run: sleep 30
- name: Install opencode
if: steps.pr.outputs.draft == 'false' && steps.safety.outputs.safe == 'true'
if: steps.pr.outputs.draft == 'false' && steps.safety.outputs.safe == 'true' && steps.throttle.outputs.skip != 'true'
run: |
set -o pipefail
install_log="$(mktemp)"
@@ -232,16 +269,16 @@ jobs:
exit "$((curl_status || install_status))"
- name: Record review start
if: steps.pr.outputs.draft == 'false' && steps.safety.outputs.safe == 'true'
if: steps.pr.outputs.draft == 'false' && steps.safety.outputs.safe == 'true' && steps.throttle.outputs.skip != 'true'
id: review-start
run: echo "started_at=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> "$GITHUB_OUTPUT"
- name: Review pull request
if: steps.pr.outputs.draft == 'false' && steps.safety.outputs.safe == 'true'
if: steps.pr.outputs.draft == 'false' && steps.safety.outputs.safe == 'true' && steps.throttle.outputs.skip != 'true'
id: review-run
env:
REVIEW_TIMEOUT: 30m
OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
ZHIPU_API_KEY: ${{ secrets.ZHIPU_API_KEY }}
GH_TOKEN: ${{ steps.app-token.outputs.token }}
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
PR_URL: ${{ steps.pr.outputs.url }}
@@ -297,7 +334,7 @@ jobs:
- name: Verify and enforce review verdict
id: verdict
if: always() && steps.pr.outputs.draft == 'false' && steps.safety.outputs.safe == 'true'
if: always() && steps.pr.outputs.draft == 'false' && steps.safety.outputs.safe == 'true' && steps.throttle.outputs.skip != 'true'
env:
GH_TOKEN: ${{ github.token }}
PR_NUMBER: ${{ steps.pr.outputs.number }}
@@ -383,9 +420,8 @@ jobs:
fail_automation "Review comment does not identify the expected HEAD."
fi
if ! printf '%s' "$body" | grep -Fq '<h3>Applied Repository Guidance</h3>' || \
! printf '%s' "$body" | grep -Fq '| Source | Why applicable | Rules/invariants evaluated |'; then
fail_automation "Review comment does not contain the required applied-guidance record."
if ! printf '%s' "$body" | grep -Fq '**For the maintainer:**'; then
fail_automation "Review comment does not contain the maintainer verdict line."
fi
expected_marker="<!-- oc-review-meta {\"head\":\"$REVIEW_HEAD_SHA\",\"verdict\":\"$verdict\"} -->"
@@ -419,7 +455,7 @@ jobs:
} >> "$GITHUB_STEP_SUMMARY"
- name: Mark automation failure
if: always() && steps.pr.outputs.draft == 'false' && steps.verdict.outcome != 'success' && steps.safety.outputs.safe != 'false'
if: always() && steps.pr.outputs.draft == 'false' && steps.verdict.outcome != 'success' && steps.safety.outputs.safe != 'false' && steps.throttle.outputs.skip != 'true'
env:
GH_TOKEN: ${{ github.token }}
PR_NUMBER: ${{ steps.pr.outputs.number }}