ci: enforce PMC vote on format-spec changes via CI gate#7399
Open
wjones127 wants to merge 2 commits into
Open
Conversation
Format-specification changes (the `.proto` definitions and `docs/src/format/**`) require 3 binding +1 votes from PMC members, but nothing structurally enforced it — the requirement was only a reminder comment, caught socially if at all. This adds a CI gate that blocks merging a format-spec change until it has the required votes. A touched PR is labelled `format-change` and the gate publishes a `format-spec-vote` commit status that stays red until: - 3 PMC members have approved the PR (excluding the author), counted only on the latest commit so new pushes invalidate stale approvals; - no PMC member has an outstanding "Request changes" review (a veto); and - the 1-week voting period (from when the label was first applied) has elapsed. The gate posts a live tally comment and runs on PR events, review events, and an 8-hourly cron (so the voting-period clock is re-checked even when no event fires). It uses pull_request_target to label/status fork PRs but never checks out or runs PR code. A PMC member can waive a trivial edit by removing the `format-change` label. The PMC roster moves to `docs/src/community/pmc.yaml` as the source of truth; `ci/sync_pmc_docs.py` regenerates the table in pmc.md (checked in docs-check), and the gate reads the roster from the base checkout so a PR cannot enlarge the electorate. The old reminder-only job is removed. To activate, add `format-spec-vote` as a required status check on protected branches. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
wjones127
commented
Jun 22, 2026
Responds to PR review feedback: - Rewrite the gate in Python (PyGithub) instead of JS, for readability — most maintainers are more familiar with Python. - Apply the `format-change` label via the existing path labeler (.github/labeler-area.yml) instead of the gate. The gate now reads the label and leaves PRs without it alone, posting only a passing status (so the required check never blocks non-format PRs). Trivial edits are waived with a `format-waived` label applied by a PMC member. - Render the PMC roster table at docs build time via an MkDocs hook (docs/hooks/pmc_roster.py) instead of a committed table kept in sync by a script; pmc.yaml stays the source of truth. Drops ci/sync_pmc_docs.py. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements the accepted part of #7112: a CI gate that structurally blocks merging a format-specification change until it has the required PMC votes, replacing the previous reminder-only comment that was enforced socially, if at all.
How it works
The path labeler (
.github/labeler-area.yml) applies theformat-changelabel to PRs that touch the format spec (protos/**/*.protoordocs/src/format/**). The gate (ci/format_vote_gate.py, run byformat-vote-gate.yml) reads that label and publishes aformat-spec-votecommit status that stays red until all hold:-1veto cannot be overruled).format-changelabel was first applied.It posts/updates a single live-tally comment and runs on PR events, review events, and an 8-hourly
cron. The cron has no PR context, so it sweeps every openformat-changePR — this is what re-checks the voting-period clock when no PR event fires (e.g. the week elapses days after the third approval). It usespull_request_targetso it can status/comment fork PRs, but never checks out or runs PR code — it reads the trusted base checkout and the API only.PRs without the
format-changelabel are left alone: the gate posts only a passingformat-spec-votestatus (so the required check never blocks unrelated PRs) and does nothing else. A PMC member waives a trivial edit (typo, wording, formatting) by applying theformat-waivedlabel.PMC roster
docs/src/community/pmc.yamlis the source of truth. The roster table inpmc.mdis rendered at docs build time by an MkDocs hook (docs/hooks/pmc_roster.py), and the gate reads the roster from the base checkout so a PR can't enlarge the electorate by editing it.Tests
ci/test_format_vote_gate.py(pytest) covers the pure vote-counting logic — latest-review-wins, stale-approval filtering, author/non-PMC/dismissed exclusion, and verdict priority — run byci-scripts.yml.Activation (after merge)
Add
format-spec-voteas a required status check on protected branches (main+ release branches). It's posted on every PR — success immediately for non-format PRs — so it never leaves a required check pending.Note
On this PR itself the gate run errors red, because
pull_request_targetchecks out the base branch, which doesn't yet containci/format_vote_gate.py. That resolves once merged; don't make the check required until then.🤖 Generated with Claude Code