[ci-governance] Owner BP flip: make qa-review + security-review + reserved-path-review merge-blocking on main #3141

Open
opened 2026-06-22 03:09:49 +00:00 by core-devops · 0 comments
Member

Owner action: add qa-review / security-review / reserved-path-review to branch protection

CTO-authorized: make the three review gates truly merge-blocking on main.

Background / mechanism analysis

These three checks post commit statuses today but are NOT in branch_protections/main.status_check_contexts, so a PR merges once its 4 actually-required contexts are green regardless of the review verdicts (proven: #3131 merged with qa-review red).

Path A (sentinel wiring) is NOT viable. CI / all-required is a plain needs: aggregator over CI's OWN jobs (ci.yml). Gitea Actions has no cross-workflow needs:, and the prior poll-the-status-API design was deliberately removed (runner-squat RCA, 2026-06-01). The review contexts are emitted by separate workflow files on pull_request_target / pull_request_review events, so the sentinel structurally cannot gate them. This is exactly why ci-required-drift.py F4 exists (cross-workflow required contexts must live in BP directly + have a live emitter).

Path B (owner BP flip) is the only mechanism.

The exact owner action (after the SSOT PR merges)

Order matters: merge the required-contexts.txt PR FIRST, then PATCH BP. (lint_no_coe_on_required fails if live_BP - allowlist is non-empty — allowlist must be a superset of BP.)

PATCH branch_protections/main.status_check_contexts to ADD (keep existing 4):

qa-review / approved (pull_request_target)
security-review / approved (pull_request_target)
reserved-path-review / reserved-path-review (pull_request_target)

Full target list (7):

  • CI / all-required (pull_request)
  • E2E API Smoke Test / E2E API Smoke Test (pull_request)
  • Handlers Postgres Integration / Handlers Postgres Integration (pull_request)
  • Secret scan / Scan diff for credential-shaped strings (pull_request)
  • qa-review / approved (pull_request_target)
  • security-review / approved (pull_request_target)
  • reserved-path-review / reserved-path-review (pull_request_target)

Why (pull_request_target) (not (pull_request_review))

The (pull_request_target) variant fires on every PR open/synchronize and starts RED (review-check.sh exits 1 → job conclusion publishes failure) when there is no APPROVE — fail-closed, no false-green, no deadlock. The pull_request_review path explicitly RE-POSTS the same (pull_request_target) context to flip it green on a genuine APPROVE (mirrors the working sop-checklist pattern). Requiring (pull_request_review) instead would deadlock (absent until a review is submitted = pending forever).

Fail-closed proof

  • No review yet → qa-review / approved (pull_request_target) = failure (red) → BLOCKS.
  • Genuine non-author APPROVE → success (green) → unblocks.

reserved-path-review only goes red for PRs touching .gitea/reserved-paths.txt; otherwise it posts success — so it does not block ordinary PRs.

## Owner action: add qa-review / security-review / reserved-path-review to branch protection CTO-authorized: make the three review gates **truly merge-blocking** on `main`. ### Background / mechanism analysis These three checks post commit statuses today but are NOT in `branch_protections/main.status_check_contexts`, so a PR merges once its 4 actually-required contexts are green regardless of the review verdicts (proven: #3131 merged with qa-review red). **Path A (sentinel wiring) is NOT viable.** `CI / all-required` is a plain `needs:` aggregator over CI's OWN jobs (`ci.yml`). Gitea Actions has no cross-workflow `needs:`, and the prior poll-the-status-API design was deliberately removed (runner-squat RCA, 2026-06-01). The review contexts are emitted by separate workflow files on `pull_request_target` / `pull_request_review` events, so the sentinel structurally cannot gate them. This is exactly why `ci-required-drift.py` F4 exists (cross-workflow required contexts must live in BP directly + have a live emitter). **Path B (owner BP flip) is the only mechanism.** ### The exact owner action (after the SSOT PR merges) Order matters: merge the `required-contexts.txt` PR FIRST, then PATCH BP. (`lint_no_coe_on_required` fails if `live_BP - allowlist` is non-empty — allowlist must be a superset of BP.) PATCH `branch_protections/main.status_check_contexts` to ADD (keep existing 4): ``` qa-review / approved (pull_request_target) security-review / approved (pull_request_target) reserved-path-review / reserved-path-review (pull_request_target) ``` Full target list (7): - CI / all-required (pull_request) - E2E API Smoke Test / E2E API Smoke Test (pull_request) - Handlers Postgres Integration / Handlers Postgres Integration (pull_request) - Secret scan / Scan diff for credential-shaped strings (pull_request) - qa-review / approved (pull_request_target) - security-review / approved (pull_request_target) - reserved-path-review / reserved-path-review (pull_request_target) ### Why `(pull_request_target)` (not `(pull_request_review)`) The `(pull_request_target)` variant fires on every PR open/synchronize and starts RED (review-check.sh exits 1 → job conclusion publishes `failure`) when there is no APPROVE — fail-closed, no false-green, no deadlock. The `pull_request_review` path explicitly RE-POSTS the same `(pull_request_target)` context to flip it green on a genuine APPROVE (mirrors the working `sop-checklist` pattern). Requiring `(pull_request_review)` instead would deadlock (absent until a review is submitted = pending forever). ### Fail-closed proof - No review yet → `qa-review / approved (pull_request_target)` = failure (red) → BLOCKS. - Genuine non-author APPROVE → success (green) → unblocks. reserved-path-review only goes red for PRs touching `.gitea/reserved-paths.txt`; otherwise it posts success — so it does not block ordinary PRs.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: molecule-ai/molecule-core#3141