mirror of
https://github.com/Comfy-Org/github-workflows.git
synced 2026-11-05 00:17:37 +00:00
[PR #120] [MERGED] feat(pr-risk): opt-in Check Run + sticky comment with risk concentration and a dispute checkbox #129
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#129
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/120
Author: @mattmillerai
Created: 8/4/2026
Status: ✅ Merged
Merged: 8/4/2026
Merged by: @mattmillerai
Base:
main← Head:matt/be-6326-pr-risk-publish-surfaces📝 Commits (3)
fead856feat(pr-risk): opt-in Check Run + sticky comment with risk concentration and a dispute checkbox4d54d54fix(pr-risk): never lose a dispute or duplicate a sticky on a transient read (BE-6326)5baabcdrefactor(pr-risk): collapse the sticky comment to one line + a📊 Changes
10 files changed (+1329 additions, -11 deletions)
View changed files
📝
.github/workflows/pr-risk.yml(+155 -5)📝
.github/workflows/test-pr-risk.yml(+9 -1)📝
README.md(+1 -1)📝
scripts/pr-risk/README.md(+24 -0)📝
scripts/pr-risk/grade-pr-risk.sh(+27 -2)📝
scripts/pr-risk/grade-targets.sh(+95 -0)➕
scripts/pr-risk/publish-risk-surfaces.sh(+584 -0)📝
scripts/pr-risk/tests/test_grade_pr_risk.sh(+23 -1)📝
scripts/pr-risk/tests/test_grade_targets.sh(+44 -1)➕
scripts/pr-risk/tests/test_publish_risk_surfaces.sh(+367 -0)📄 Description
ELI-5
The risk grader stamps a
risk:R0..risk:R3sticker on every PR, and that sticker never says why. This adds two things you can turn on if you want them: a comment that explains the grade ("94% of this diff is R0/R1; the 6% that puts the path floor at R3 is these two files, 40 lines") with a tickbox for "this grade is wrong", and a Check Run that pins the grade to the exact commit so there's a permanent record. Both are off unless you switch them on, and neither can ever fail your build.What this adds
Two publish surfaces, both
falseby default:sticky_comment: true— ONE comment, created once and updated in place (N pushes leave one comment), carrying a per-file breakdown, the concentration sentence, and a dispute checkbox whose state round-trips into arisk-grade-disputedlabel. It rides thepull-requests: writethe label already needs, so switching it on requires no permission change.check_run: true— the tier and reason as a Check Run on the head commit, withconclusion: neutralhardcoded. Published from a separate job that holdschecks: write.Judgment calls
Per-file breakdown = the PATH axis, and it says so. The ticket asked me to pick between deriving per-path tiers from the same rules or reporting by matched class. I derived per-path tiers: the grader now emits
risk.axes.path_floor.files, matched with the same rules as the floor, reporting-only.worstover those per-file floors equals the floor by construction, and a test pins that equality so the breakdown cannot quietly become a second grading model. Because only the path axis is per-file at all, each row is labelled a contribution, and whenever the headline tier exceeds the path floor the concentration sentence names the axis that supplied it and quotes its reason — without that, a docs-only diff headlinesR2(no green checks) directly above "all 600 lines are R0" and the explanation contradicts the tier it explains.The Check Run is a separate job, and that is the two-job split the ticket asked about. Not primarily for elegance: raising the grade job's
checks: readtochecks: writewould be rejected before any step ran for every caller already enrolled with the reviewed permissions block inpr-risk.yml's header — a reusable workflow can only narrow the caller's token. Splitting it keeps the new grant opt-in and gives the posture BE-5508 wanted: all PR-controlled text is escaped in the grading job (narrow token), and the job holdingchecks: writereads nothing from a pull request — its whole input is a pre-rendered, pre-escaped string.Dispute state: the checkbox is the source of truth, the label mirrors it. PR #100 OR-ed the label back into the state, which made the label one-way — once set, no un-tick could ever clear it. Here ticking applies
risk-grade-disputedand un-ticking removes it. The cost is one narrow race (a tick landing between the read-back and the PATCH is overwritten), which is why the body is re-read by id immediately before the write rather than reused from the paginated scan.risk-grade-disputedstays distinct from the human-ownedrisk-dispute, which the grader still never touches.Hardening (ported, and tested rather than estimated)
|, backticks and newlines in a path; rendered raw into a table it breaks out of its code span and its row, letting an author inject a pre-ticked- [x] **This grade is wrong**that the next re-grade reads back as genuine reviewer disagreement — corrupting the exact calibration signal the checkbox exists to collect. Newlines are flattened (nothing PR-controlled may start a line), pipes are backslash-escaped, and a backtick or backslash forces fully-escaped plain text. Same treatment for anunknownreport's reason, which quotes PR-authored path names. Covered by a test using a filename containing all three plus a literal pre-ticked checkbox and an<img>tag.find_stickymatches — if they drifted, every push would POST a new comment instead of updating.Botand an allow-listed login and the marker at position 0. Marker-only matching would let a PR author pre-post a comment carrying the (public) marker and thereafter control the preserved checkbox state.Verification
All five suites green locally, plus shellcheck on every touched script:
test_publish_risk_surfaces.sh— 48 passed, 0 failed (new)test_grade_pr_risk.sh— 62 passed (was 57; +5 for the per-file-floor invariant)test_grade_targets.sh— 91 passed ·test_apply_risk_label.sh— 24 ·test_resolve_enabled.sh— 21shellcheck -xclean on all scripts and suites; both workflow files parse.test-pr-risk.ymlruns the new suite and shellchecks the new script.What a reviewer should check hardest
if: ... && inputs.check_run) declareschecks: writethe caller did not grant. I could not exercise that from here. If GitHub validates eagerly at graph construction instead of at job start, every existing caller breaks on the SHA bump — which would make the fix "gate the whole job behind a caller-sideif:instead", not a code change here. This is the one line I would want confirmed against a real run before the tag moves.surfacesas a job output. Job outputs share a ~1MB budget, so the rendered summary is capped at 12,000 chars per target before it travels (CHECK_SUMMARY_CAP). A 50-target batch therefore fits; without the cap it would silently truncate every target's payload, not just the offenders.Notes
unknownpath (which reportsunknown, neverR0) is unchanged and covered by tests including the empty and non-JSON record cases. So the negative-claim falsification rule does not apply.🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.