From 3f1f029a38f1f2277e6683722aace9cfe193cb0e Mon Sep 17 00:00:00 2001 From: "Molecule AI Dev Engineer A (Kimi)" Date: Wed, 3 Jun 2026 00:32:20 +0000 Subject: [PATCH] ci(gate): add pull_request_review fallback trigger for qa-review and security-review #2020 clean-gate-proof revealed that pull_request_review_approved does not fire reliably on this Gitea instance, while pull_request_review DOES (sop-tier-check successfully triggers on it, producing live sop-tier-check / tier-check (pull_request_review) contexts). Changes to BOTH qa-review.yml and security-review.yml: - Add pull_request_review with types: [submitted] as a defensive fallback trigger alongside the existing pull_request_review_approved. - Update job-level if: guard to accept EITHER event, but only when github.event.review.type == 'pull_request_review_approved' for the pull_request_review path. This ensures COMMENT and REQUEST_CHANGES review submissions are skipped at the job level (no misleading green contexts). - Update explicit POST step guard similarly so the branch-protection- required (pull_request_target) context is posted on both event paths. - Retain pull_request_review_approved as a forward-compatible trigger in case a future Gitea version fires it reliably. Trust boundary unchanged: BASE ref checkout, no PR-head code execution. Token separation unchanged: evaluator uses SOP_TIER_CHECK_TOKEN (read); POST uses STATUS_POST_TOKEN (write). Refs: internal#760 --- .gitea/workflows/qa-review.yml | 34 +++++++++++++++------------ .gitea/workflows/security-review.yml | 35 +++++++++++++++++----------- 2 files changed, 41 insertions(+), 28 deletions(-) diff --git a/.gitea/workflows/qa-review.yml b/.gitea/workflows/qa-review.yml index 33aee528e..644b5cb94 100644 --- a/.gitea/workflows/qa-review.yml +++ b/.gitea/workflows/qa-review.yml @@ -9,20 +9,18 @@ # Triggers on: # - `pull_request_target`: opened, synchronize, reopened # → initial status posts when PR opens / re-pushes +# - `pull_request_review` types: [submitted] +# → defensive fallback. Empirical evidence (live status audit) +# `pull_request_review_approved` does not always fire on this +# Gitea instance, while `pull_request_review` does (verified +# by sop-tier-check live status context). The job guard checks +# `github.event.review.type == 'pull_request_review_approved'` +# so only APPROVE reviews run the evaluator; COMMENT and +# REQUEST_CHANGES are skipped at the job level. # - `pull_request_review_approved` -# → re-evaluate when a team member submits an APPROVE review so -# the gate flips immediately (no wait for the next push or -# slash-command). Gitea Actions uses the specific event name -# `pull_request_review_approved` (not the GitHub-style -# `pull_request_review` catch-all). Verified via Gitea source -# code audit (go-gitea/gitea main, modules/webhook/type.go + -# services/actions/notifier.go). The event payload carries -# `review.type="pull_request_review_approved"`; there is no -# `review.state` field. Branch-protection requires the -# `(pull_request_target)` context variant, so the -# pull_request_review_approved path EXPLICITLY POSTS the -# required context via the API. Trust boundary preserved -# (BASE ref, no PR-head). +# → retained as a forward-compatible trigger in case a future +# Gitea version fires it reliably. The same job guard covers +# both events. # - comment refires are handled by `sop-checklist.yml` review-refire job # → `/qa-recheck` slash-command re-evaluates this gate. # Workflow name = `qa-review` ; job name = `approved`. @@ -97,6 +95,8 @@ name: qa-review on: pull_request_target: types: [opened, synchronize, reopened] + pull_request_review: + types: [submitted] pull_request_review_approved: permissions: @@ -114,7 +114,8 @@ jobs: # Comment-triggered refires live in sop-checklist.yml review-refire job. if: | github.event_name == 'pull_request_target' || - github.event_name == 'pull_request_review_approved' + github.event_name == 'pull_request_review_approved' || + (github.event_name == 'pull_request_review' && github.event.review.type == 'pull_request_review_approved') runs-on: ubuntu-latest steps: - name: Privilege check (A1.1 — INFORMATIONAL log only, NOT a gate) @@ -185,7 +186,10 @@ jobs: # for the explicit status POST. Evaluator step stays on # SOP_TIER_CHECK_TOKEN (read-only) per deliberate security # separation: eval computes, POST writes, never the same cred. - if: github.event_name == 'pull_request_review_approved' && always() + if: | + (github.event_name == 'pull_request_review_approved' || + (github.event_name == 'pull_request_review' && github.event.review.type == 'pull_request_review_approved')) + && always() env: GITEA_TOKEN: ${{ secrets.STATUS_POST_TOKEN }} GITEA_HOST: git.moleculesai.app diff --git a/.gitea/workflows/security-review.yml b/.gitea/workflows/security-review.yml index 0ff5b79b6..0001b844f 100644 --- a/.gitea/workflows/security-review.yml +++ b/.gitea/workflows/security-review.yml @@ -7,23 +7,28 @@ # See `qa-review.yml` header for the full A1-α / A1.1 / A4 / A5 design # rationale; everything below is identical in shape. # -# A1-α addendum (internal#760): `pull_request_review_approved` trigger -# added so the security gate flips immediately when a team member submits -# an APPROVE review. Gitea Actions uses the specific event name -# `pull_request_review_approved` (not the GitHub-style `pull_request_review` -# catch-all). Verified via Gitea source code audit (go-gitea/gitea main, -# modules/webhook/type.go + services/actions/notifier.go). The event -# payload carries `review.type="pull_request_review_approved"`; there is -# no `review.state` field. Branch-protection requires the -# `(pull_request_target)` context variant, so the -# pull_request_review_approved path EXPLICITLY POSTS the required context -# via the API. Trust boundary preserved (BASE ref, no PR-head). +# A1-α addendum (internal#760): review-event trigger added so the security +# gate flips immediately when a team member submits an APPROVE review. +# We listen to BOTH `pull_request_review` (types: [submitted]) and +# `pull_request_review_approved`. Empirical evidence (live status audit) +# `pull_request_review_approved` does not always fire on this Gitea +# instance, while `pull_request_review` does (verified by sop-tier-check +# live status context). The job guard checks +# `github.event.review.type == 'pull_request_review_approved'` so only +# APPROVE reviews run the evaluator; COMMENT and REQUEST_CHANGES are +# skipped at the job level. `pull_request_review_approved` is retained +# as a forward-compatible trigger. Branch-protection requires the +# `(pull_request_target)` context variant, so the review-event path +# EXPLICITLY POSTS the required context via the API. Trust boundary +# preserved (BASE ref, no PR-head). name: security-review on: pull_request_target: types: [opened, synchronize, reopened] + pull_request_review: + types: [submitted] pull_request_review_approved: permissions: @@ -41,7 +46,8 @@ jobs: # Comment-triggered refires live in sop-checklist.yml review-refire job. if: | github.event_name == 'pull_request_target' || - github.event_name == 'pull_request_review_approved' + github.event_name == 'pull_request_review_approved' || + (github.event_name == 'pull_request_review' && github.event.review.type == 'pull_request_review_approved') runs-on: ubuntu-latest steps: - name: Privilege check (A1.1 — INFORMATIONAL log only, NOT a gate) @@ -98,7 +104,10 @@ jobs: # for the explicit status POST. Evaluator step stays on # SOP_TIER_CHECK_TOKEN (read-only) per deliberate security # separation: eval computes, POST writes, never the same cred. - if: github.event_name == 'pull_request_review_approved' && always() + if: | + (github.event_name == 'pull_request_review_approved' || + (github.event_name == 'pull_request_review' && github.event.review.type == 'pull_request_review_approved')) + && always() env: GITEA_TOKEN: ${{ secrets.STATUS_POST_TOKEN }} GITEA_HOST: git.moleculesai.app -- 2.52.0