mirror of
https://github.com/Comfy-Org/github-workflows.git
synced 2026-11-05 00:17:37 +00:00
[PR #18] [MERGED] feat(assign-reviewers): expertise-aware load-balanced reviewer assignment workflow (BE-1943) #26
Labels
No labels
cursor-review-catalog-drift
groom
groom
pull-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Comfy-Org/github-workflows#26
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
📋 Pull Request Information
Original PR: https://github.com/Comfy-Org/github-workflows/pull/18
Author: @mattmillerai
Created: 6/30/2026
Status: ✅ Merged
Merged: 7/3/2026
Merged by: @mattmillerai
Base:
main← Head:matt/be-1943-assign-reviewers📝 Commits (1)
c354055feat(assign-reviewers): add expertise-aware load-balanced reviewer assignment workflow (BE-1943)📊 Changes
2 files changed (+359 additions, -0 deletions)
View changed files
➕
.github/workflows/assign-reviewers.yml(+358 -0)📝
README.md(+1 -0)📄 Description
ELI-5
When you open a PR, this new reusable workflow looks at which files you changed,
checks a little config file in your repo that says "people who know area X are
A and B," and automatically asks the right folks to review — while spreading the
load so nobody gets buried, and occasionally tapping someone newer so they get
review reps. It never breaks your PR if anything goes sideways; worst case it
just doesn't assign anyone.
What this adds
A new reusable workflow
assign-reviewers.yml(on: workflow_call) thatauto-requests expertise-aware, load-balanced PR reviewers with new-folk
randomization. Structurally modeled on
cursor-review-auto-label.yml(workflow_call header-comment style,
vars-driven config, app-token step,draft/skip handling, defensive "never fail the PR" logging).
Contract
reviewer_config_path(default.github/reviewers.yml),num_reviewers(number, default 2),skip_label(defaultskip-auto-assign).CLOUD_CODE_BOT_PRIVATE_KEY(required); usesvars.APP_IDfor the app token.vars:REVIEWER_GROWTH_POOL,REVIEWER_LOAD_CAP,REVIEWER_EXCLUDE.Job logic (app-token step → single
github-scriptstep): early-exit ondraft / bot author / skip label / already-has-reviewers → list changed paths →
read+parse
reviewers.ymlfrom the PR head SHA → match globs to candidateexperts (else
default_pool) → drop author + excludes → rank ascending by openreview load (steer off at/over
REVIEWER_LOAD_CAP) → deterministic per-PRnew-folk swap from
REVIEWER_GROWTH_POOL→ request topnum_reviewersvia theapp-token octokit (try/catch, never fails the PR).
Permissions: job-level
contents: readonly. Reviewer requests go throughthe app token, so no
pull-requests: writeonGITHUB_TOKEN— which also dodgesthe fork-PR read-only-token failure from #12.
Acceptance criteria
(
zizmor --offlinereports no findings — same single sub-thresholdconcurrency-limitsnote the existingcursor-review-auto-label.ymlcarries).actions/create-github-app-tokenandactions/github-scriptpinned to fullSHAs with version comments (matching
detect-unreviewed-merge.yml/bump-cursor-review-callers.ymlstyle).v1tag" is a post-merge step (a tag can't point at anunmerged SHA) — left for the merger per the README Versioning section.
Judgment calls
github-scriptdoes not bundlejs-yamlor
minimatch, so I wrote a small glob→RegExp matcher and a YAML parser scopedto the documented
reviewers.ymlschema (both flow[a, b]and block- asequences). Both are covered by a 32-case unit suite I ran against the helpers
extracted from the committed workflow (not just standalone copies). The
schema is documented in the header comment; any parse failure is caught and
degrades to a no-op.
actions/checkout. I fetch the config via the contents API instead, sothe ticket's checkout option isn't used — hence only the two actions I actually
use are SHA-pinned.
(so a PR's own config edits apply), falling back to the base SHA when the head
isn't resolvable via the base repo's contents API (fork PRs). The base copy is
also the trusted one, so a fork can't inject reviewers by editing the config.
org:${owner}(the caller's org) rather than ahardcoded
org:Comfy-Org, so the workflow is genuinely reusable.swap fires, the newcomer is added rather than replacing the sole expert, so we
don't trade away coverage; the final
slice(0, num_reviewers)still caps the count.Out of scope
No consumer wiring (that's the cloud caller child ticket per the ticket).
🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.