ci(pr-risk): publish the advisory grade as a neutral Check Run - #15442
ci(pr-risk): publish the advisory grade as a neutral Check Run#15442mattmillerai wants to merge 8 commits into
Conversation
The `risk:*` label is mutable and carries no history, so a grade that is later disputed cannot be checked against the reasoning that produced it. Opting into the grader's `check_run` input publishes the tier and the per-axis reason table as a commit-attached Check Run. The upstream conclusion is hardcoded `neutral` and the publish step always exits 0, so nothing here can fail a PR. `checks: write` is already granted in this caller — every caller on this pin owes that grant whether or not it opts in, because GitHub validates the nested `publish-check` job's declaration at startup regardless of its `if:`.
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Comment |
🎭 Playwright: ✅ 2006 passed, 0 failed📊 Browser Reports
📦 Bundle Size
⚡ Performance Report
Absolute values
Raw data{
"timestamp": "2026-08-25T22:28:58.724Z",
"gitSha": "8ce728127629a9de3c7f55ba85c8693e727c55cf",
"branch": "matt/be-8038-pr-risk-check-run",
"measurements": [
{
"name": "canvas-idle",
"durationMs": 2055.770999999993,
"styleRecalcs": 7,
"styleRecalcDurationMs": 7.084999999999997,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 532.282,
"heapDeltaBytes": 11893412,
"heapUsedBytes": 80833344,
"domNodes": -280,
"jsHeapTotalBytes": 4448256,
"scriptDurationMs": 10.304,
"eventListeners": -185,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-idle",
"durationMs": 2015.67399999999,
"styleRecalcs": 5,
"styleRecalcDurationMs": 4.997999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 531.161,
"heapDeltaBytes": 10452448,
"heapUsedBytes": 79503916,
"domNodes": -287,
"jsHeapTotalBytes": 4972544,
"scriptDurationMs": 10.722999999999999,
"eventListeners": -155,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-mouse-sweep",
"durationMs": 2437.049000000002,
"styleRecalcs": 78,
"styleRecalcDurationMs": 43.254,
"layouts": 12,
"layoutDurationMs": 3.8679999999999994,
"taskDurationMs": 1463.692,
"heapDeltaBytes": -8345628,
"heapUsedBytes": 61366740,
"domNodes": -278,
"jsHeapTotalBytes": 6283264,
"scriptDurationMs": 196.73200000000003,
"eventListeners": -187,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000012,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-mouse-sweep",
"durationMs": 2238.028999999983,
"styleRecalcs": 78,
"styleRecalcDurationMs": 42.821999999999996,
"layouts": 12,
"layoutDurationMs": 3.392,
"taskDurationMs": 1289.951,
"heapDeltaBytes": -5333892,
"heapUsedBytes": 57759256,
"domNodes": -282,
"jsHeapTotalBytes": 4972544,
"scriptDurationMs": 168.228,
"eventListeners": -185,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1739.847999999995,
"styleRecalcs": 32,
"styleRecalcDurationMs": 18.026999999999997,
"layouts": 6,
"layoutDurationMs": 0.579,
"taskDurationMs": 409.46999999999997,
"heapDeltaBytes": 4300032,
"heapUsedBytes": 73366480,
"domNodes": 76,
"jsHeapTotalBytes": 4718592,
"scriptDurationMs": 15.091000000000003,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1739.4330000000764,
"styleRecalcs": 32,
"styleRecalcDurationMs": 19.415999999999997,
"layouts": 6,
"layoutDurationMs": 0.6609999999999999,
"taskDurationMs": 422.46400000000006,
"heapDeltaBytes": 4310168,
"heapUsedBytes": 73397772,
"domNodes": 77,
"jsHeapTotalBytes": 4718592,
"scriptDurationMs": 14.378,
"eventListeners": 21,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "dom-widget-clipping",
"durationMs": 734.8810000000299,
"styleRecalcs": 9,
"styleRecalcDurationMs": 6.140999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 435.811,
"heapDeltaBytes": -6741416,
"heapUsedBytes": 62253820,
"domNodes": 14,
"jsHeapTotalBytes": 6029312,
"scriptDurationMs": 32.821999999999996,
"eventListeners": 2,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "dom-widget-clipping",
"durationMs": 719.5150000000012,
"styleRecalcs": 9,
"styleRecalcDurationMs": 6.1049999999999995,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 447.37200000000007,
"heapDeltaBytes": -6765352,
"heapUsedBytes": 62138664,
"domNodes": 14,
"jsHeapTotalBytes": 6029312,
"scriptDurationMs": 33.082,
"eventListeners": 2,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-idle",
"durationMs": 2073.336999999981,
"styleRecalcs": 5,
"styleRecalcDurationMs": 4.718999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 631.6529999999999,
"heapDeltaBytes": -20871708,
"heapUsedBytes": 69755452,
"domNodes": -279,
"jsHeapTotalBytes": 4186112,
"scriptDurationMs": 45.571,
"eventListeners": -183,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-idle",
"durationMs": 2031.2020000000075,
"styleRecalcs": 3,
"styleRecalcDurationMs": 3.119,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 635.84,
"heapDeltaBytes": -17182460,
"heapUsedBytes": 73503596,
"domNodes": -274,
"jsHeapTotalBytes": -1843200,
"scriptDurationMs": 50.495999999999995,
"eventListeners": -153,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "large-graph-pan",
"durationMs": 3470.6639999999993,
"styleRecalcs": 66,
"styleRecalcDurationMs": 11.350999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 2790.717,
"heapDeltaBytes": 13964792,
"heapUsedBytes": 98280044,
"domNodes": -283,
"jsHeapTotalBytes": 4636672,
"scriptDurationMs": 1011.108,
"eventListeners": -181,
"totalBlockingTimeMs": 29,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-pan",
"durationMs": 3562.475000000063,
"styleRecalcs": 64,
"styleRecalcDurationMs": 10.251,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 2834.3369999999995,
"heapDeltaBytes": -6914432,
"heapUsedBytes": 84764920,
"domNodes": -279,
"jsHeapTotalBytes": -569344,
"scriptDurationMs": 999.2670000000002,
"eventListeners": -151,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "large-graph-zoom",
"durationMs": 4546.895000000006,
"styleRecalcs": 66,
"styleRecalcDurationMs": 11.760000000000003,
"layouts": 60,
"layoutDurationMs": 7.512,
"taskDurationMs": 2720.289,
"heapDeltaBytes": 7527096,
"heapUsedBytes": 80009500,
"domNodes": -265,
"jsHeapTotalBytes": -1880064,
"scriptDurationMs": 937.4290000000001,
"eventListeners": -181,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-zoom",
"durationMs": 4566.350000000057,
"styleRecalcs": 66,
"styleRecalcDurationMs": 11.93,
"layouts": 60,
"layoutDurationMs": 7.635,
"taskDurationMs": 2755.781,
"heapDeltaBytes": -27823912,
"heapUsedBytes": 68306060,
"domNodes": -234,
"jsHeapTotalBytes": 1826816,
"scriptDurationMs": 913.5790000000001,
"eventListeners": -147,
"totalBlockingTimeMs": 2,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "legacy-node-drag",
"durationMs": 3225.9409999999775,
"styleRecalcs": 41,
"styleRecalcDurationMs": 5.169,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 2153.5769999999998,
"heapDeltaBytes": -1918500,
"heapUsedBytes": 70353216,
"domNodes": -287,
"jsHeapTotalBytes": -307200,
"scriptDurationMs": 223.17,
"eventListeners": -1,
"totalBlockingTimeMs": 1,
"frameDurationMs": 16.666666666666636,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "legacy-node-drag",
"durationMs": 3253.6569999999756,
"styleRecalcs": 41,
"styleRecalcDurationMs": 4.812,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 2208.087,
"heapDeltaBytes": 12692008,
"heapUsedBytes": 80604680,
"domNodes": 0,
"jsHeapTotalBytes": 1728512,
"scriptDurationMs": 224.909,
"eventListeners": 188,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "minimap-idle",
"durationMs": 2074.391999999989,
"styleRecalcs": 4,
"styleRecalcDurationMs": 4.161999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 679.8319999999999,
"heapDeltaBytes": -20957944,
"heapUsedBytes": 71543252,
"domNodes": -279,
"jsHeapTotalBytes": 3137536,
"scriptDurationMs": 46.235,
"eventListeners": -183,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "minimap-idle",
"durationMs": 2052.6270000000295,
"styleRecalcs": 3,
"styleRecalcDurationMs": 3.228999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 654.033,
"heapDeltaBytes": -18684196,
"heapUsedBytes": 73231304,
"domNodes": -278,
"jsHeapTotalBytes": -794624,
"scriptDurationMs": 48.232000000000006,
"eventListeners": -153,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 737.6760000000218,
"styleRecalcs": 44,
"styleRecalcDurationMs": 9.406,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 530.617,
"heapDeltaBytes": 18276968,
"heapUsedBytes": 87906992,
"domNodes": 14,
"jsHeapTotalBytes": 5505024,
"scriptDurationMs": 140.951,
"eventListeners": 8,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 770.6380000000763,
"styleRecalcs": 45,
"styleRecalcDurationMs": 9.233000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 540.5550000000001,
"heapDeltaBytes": 18215640,
"heapUsedBytes": 87836116,
"domNodes": 16,
"jsHeapTotalBytes": 5242880,
"scriptDurationMs": 138.66600000000003,
"eventListeners": 8,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-idle",
"durationMs": 2029.6480000000088,
"styleRecalcs": 8,
"styleRecalcDurationMs": 7.877000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 519.905,
"heapDeltaBytes": -628812,
"heapUsedBytes": 69091148,
"domNodes": -296,
"jsHeapTotalBytes": 4972544,
"scriptDurationMs": 8.827,
"eventListeners": -231,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-idle",
"durationMs": 2011.406000000079,
"styleRecalcs": 8,
"styleRecalcDurationMs": 7.9159999999999995,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 531.146,
"heapDeltaBytes": 342588,
"heapUsedBytes": 69727848,
"domNodes": -297,
"jsHeapTotalBytes": 4448256,
"scriptDurationMs": 9.732999999999999,
"eventListeners": -231,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1956.6059999999652,
"styleRecalcs": 79,
"styleRecalcDurationMs": 44.163000000000004,
"layouts": 16,
"layoutDurationMs": 4.963,
"taskDurationMs": 1209.268,
"heapDeltaBytes": 6849308,
"heapUsedBytes": 76569948,
"domNodes": -297,
"jsHeapTotalBytes": 5758976,
"scriptDurationMs": 128.366,
"eventListeners": -231,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1882.5560000000223,
"styleRecalcs": 76,
"styleRecalcDurationMs": 38.376999999999995,
"layouts": 16,
"layoutDurationMs": 4.742,
"taskDurationMs": 1159.261,
"heapDeltaBytes": -4324080,
"heapUsedBytes": 65464648,
"domNodes": -295,
"jsHeapTotalBytes": 5758976,
"scriptDurationMs": 121.29100000000001,
"eventListeners": -201,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "subgraph-transition-enter",
"durationMs": 1401.4930000000732,
"styleRecalcs": 19,
"styleRecalcDurationMs": 33.137,
"layouts": 14,
"layoutDurationMs": 15.276000000000002,
"taskDurationMs": 1014.3969999999998,
"heapDeltaBytes": -7556960,
"heapUsedBytes": 83068688,
"domNodes": 13671,
"jsHeapTotalBytes": 11010048,
"scriptDurationMs": 33.42400000000001,
"eventListeners": 2383,
"totalBlockingTimeMs": 141,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "viewport-pan-sweep",
"durationMs": 12601.600999999959,
"styleRecalcs": 245,
"styleRecalcDurationMs": 36.552,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 9711.132,
"heapDeltaBytes": -23049016,
"heapUsedBytes": 67652116,
"domNodes": -282,
"jsHeapTotalBytes": 4710400,
"scriptDurationMs": 3348.129,
"eventListeners": -157,
"totalBlockingTimeMs": 20,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "viewport-pan-sweep",
"durationMs": 13175.092000000062,
"styleRecalcs": 245,
"styleRecalcDurationMs": 36.098,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 10013.537999999999,
"heapDeltaBytes": 7669232,
"heapUsedBytes": 91439588,
"domNodes": -283,
"jsHeapTotalBytes": 4341760,
"scriptDurationMs": 3475.276,
"eventListeners": -157,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-idle",
"durationMs": 18173.598000000027,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 17232.589,
"heapDeltaBytes": -34855172,
"heapUsedBytes": 176591988,
"domNodes": -6548,
"jsHeapTotalBytes": -5115904,
"scriptDurationMs": 359.4870000000001,
"eventListeners": -7475,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.776666666666642,
"p95FrameDurationMs": 16.80000000000291
},
{
"name": "vue-large-graph-idle",
"durationMs": 18448.28699999994,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 17690.170000000002,
"heapDeltaBytes": -35243412,
"heapUsedBytes": 176726264,
"domNodes": -6548,
"jsHeapTotalBytes": -3321856,
"scriptDurationMs": 373.824,
"eventListeners": -7473,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.776666666666642,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "vue-large-graph-pan",
"durationMs": 21935.65700000005,
"styleRecalcs": 178,
"styleRecalcDurationMs": 21.836000000000023,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 21537.601,
"heapDeltaBytes": -44290708,
"heapUsedBytes": 165566676,
"domNodes": -6548,
"jsHeapTotalBytes": -4362240,
"scriptDurationMs": 945.852,
"eventListeners": -7469,
"totalBlockingTimeMs": 129,
"frameDurationMs": 17.776666666666642,
"p95FrameDurationMs": 16.80000000000291
},
{
"name": "vue-large-graph-pan",
"durationMs": 22095.169000000056,
"styleRecalcs": 172,
"styleRecalcDurationMs": 20.540999999999976,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 21782.141,
"heapDeltaBytes": -38924080,
"heapUsedBytes": 165535460,
"domNodes": -6548,
"jsHeapTotalBytes": -798720,
"scriptDurationMs": 949.77,
"eventListeners": -7469,
"totalBlockingTimeMs": 27,
"frameDurationMs": 17.779999999999927,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "workflow-execution",
"durationMs": 505.56399999993573,
"styleRecalcs": 18,
"styleRecalcDurationMs": 23.613000000000003,
"layouts": 3,
"layoutDurationMs": 1.4429999999999998,
"taskDurationMs": 151.11100000000002,
"heapDeltaBytes": 5812924,
"heapUsedBytes": 71956296,
"domNodes": 148,
"jsHeapTotalBytes": 262144,
"scriptDurationMs": 9.105,
"eventListeners": 99,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "workflow-execution",
"durationMs": 473.7609999999677,
"styleRecalcs": 14,
"styleRecalcDurationMs": 22.830000000000002,
"layouts": 3,
"layoutDurationMs": 0.811,
"taskDurationMs": 125.913,
"heapDeltaBytes": 5481248,
"heapUsedBytes": 72046004,
"domNodes": 121,
"jsHeapTotalBytes": 0,
"scriptDurationMs": 7.4209999999999985,
"eventListeners": 97,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
}
]
} |
Codecov Report✅ All modified and coverable lines are covered by tests. @@ Coverage Diff @@
## ci/bump-pr-risk #15442 +/- ##
===================================================
- Coverage 82.53% 82.53% -0.01%
===================================================
Files 1934 1934
Lines 110810 110810
Branches 32329 33295 +966
===================================================
- Hits 91462 91460 -2
- Misses 19018 19020 +2
Partials 330 330
Flags with carried forward coverage won't be shown. Click here to find out more. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
🔍 Cursor Review — Consolidated panel
Triggered by @mattmillerai.
Found 6 finding(s).
| Severity | Count |
|---|---|
| 🟠 High | 1 |
| 🟡 Medium | 1 |
| 🟢 Low | 3 |
| ⚪ Nit | 1 |
Panel: 8/8 reviewers contributed findings.
Review found the opt-in comment overclaimed in three ways, each verified against the reusable workflow at the pinned SHA: - "can never fail a PR" holds for the hardcoded neutral conclusion but not for absence. The job's if: skips fork and Dependabot PRs, and upstream's publish-check gates on the kill switch even on a manual dispatch while the grade job deliberately does not, so a dispatched re-grade with the switch off relabels and publishes nothing. Marking the check required would block those PRs forever. - "the immutable record" reads as one per commit. Upstream only POSTs to /check-runs and never updates, so every re-grade of the same head sha appends another check; a commit can carry several with differing tiers. Commit-scoping also means two PRs on one sha get indistinguishable checks. - Publishing perturbs the next grade of the same sha: an existing check makes the rollup non-empty, so the grader's 120s not-yet-registered grace window stops applying to a re-grade, which can settle a poll early and floor the reversibility axis. Comment-only; the check_run: true opt-in is unchanged.
4dd404d to
197cc0b
Compare
Conflict was the pin only: the base rebased and moved the caller from github-workflows 6178233 to f8eec04, while this branch still carried the older pin under its `check_run: true` opt-in. Took the base's newer pin (f8eec04, 2026-08-20 > 6178233, 2026-08-17) and kept this branch's `check_run: true` plus the three-limits comment block, which sits outside the conflicted hunk. Re-verified every claim that block makes against pr-risk.yml at f8eec04 rather than carrying them forward on trust: - `check_run` and `check_name` are still declared inputs. - `publish-check` gates on `needs.gate.outputs.enabled == 'true' && inputs.check_run`, with no workflow_dispatch escape — while `grade` adds `|| github.event_name == 'workflow_dispatch'`. The "a dispatched re-grade under the kill switch relabels but publishes nothing" asymmetry holds. - The publish step is `gh api -X POST .../check-runs` with no update path and a hardcoded `conclusion: neutral`, so "appends, never revises" and "can never fail a PR" both still hold. - grade-targets.sh still sets `empty_deadline=$(date +%s) + 120` for the "checks have not registered yet" window, so the perturbs-the-next-grade note keeps its number. - The caller permissions block still covers the union upstream declares across gate/grade/publish-check (contents:read, issues:write, pull-requests:write, checks:write, actions:read, statuses:read).
197cc0b to
3cc39c6
Compare
…38-pr-risk-check-run # Conflicts: # .github/workflows/ci-pr-risk.yml
3cc39c6 to
663fd1d
Compare
Conflict was the pin only: the base moved the caller from github-workflows eaee6df to 762c92f, while this branch still carried the older pin under its `check_run: true` opt-in. Took the base's newer pin (762c92f, 2026-08-25 > eaee6df, 2026-08-21) and kept this branch's `check_run: true` plus the three-limits comment block, which sits outside the conflicted hunk. Re-verified against pr-risk.yml at 762c92f rather than carrying forward on trust: `check_run`/`check_name` are still declared inputs and the `publish-check` job (gated on `needs.gate.outputs.enabled == 'true' && inputs.check_run`) still exists at this pin.
STACKED — merging lands on
ci/bump-pr-risk(owned by @cloud-code-bot, #15379), NOTmain.Summary
Opt the PR risk grader into publishing its verdict as a neutral
PR risk (advisory)Check Run, so a graded PR carries a commit-attached record of the tier and the per-axis reasoning behind it — not just the mutablerisk:*label. One input, no pin change.Changes
check_run: truein thewith:block of thepr-riskjob, plus the file-header sentence describing what the caller emits and a comment block recording the three limits of the artifact (see Limits).neutralupstream and the publish step always exits 0, so it cannot fail a PR even if a repo later marks it required.Why this is stacked instead of based on
maincheck_rundoes not exist on the pinmaincurrently carries (e4a8f7c, 2026-08-04) — that revision of the reusable workflow has nocheck_runinput and nopublish-checkjob at all. Addingcheck_run: trueon top of it would be an undefinedwith:input, which GitHub rejects at workflow startup, i.e. every PR would lose its grade entirely rather than gain a check. So this is based on #15379, which moves the pin to a revision where the input exists —6178233when this branch opened, andf8eec04after #15379 rebased onto a newer upstream SHA. The input is declared at both.Per the source request I did not bump the pin myself; #15379 owns that. When it moved
6178233→f8eec04, this branch took the base's pin verbatim in the merge and kept only thecheck_run: trueopt-in as its own diff.It is already working on this PR
pull_requestruns the caller from the merge ref, so this branch's own CI exercisedcheck_run: trueand published the check. This is the direct evidence that the input exists at the base pin and that the opt-in does what it claims:A
neutralconclusion renders asskippingingh pr checks, which is why it does not read as a new check in the rollup.Limits
Recorded in the workflow comment rather than left implicit, because each one was raised in review and each is real. All three are upstream-owned; a one-line caller opt-in cannot reach any of them.
if:skips fork and Dependabot PRs, and upstream'spublish-checkgates on the kill switch (needs.gate.outputs.enabled == 'true') even for a manual dispatch, whilegradedeliberately does not. So a dispatched re-grade withRISK_CONFIG {"enabled": false}still rewrites the label and publishes nothing — the dispute/backfill path is exactly the one that can produce no record. Never markPR risk (advisory)required in branch protection: the skipped PRs would block forever on a check that is never created.POSTs to/check-runs; it never looks up or updates an existing run of the same name. Every re-grade of the same head sha (reopen,ready_for_reviewafteropened, a re-run, a dispatch) appends another check, so a commit can carry several with differing tiers and nothing marking which is current. The check is also commit-scoped while the grade is PR-scoped, so two PRs sharing a head sha get indistinguishable checks. Read the newest; the run log stays the tiebreak.checks_statenon-null, so the grader's 120s "checks have not registered yet" grace window stops applying to a re-grade, which can settle a poll early and floor the reversibility axis. Bounded to re-grades of the same sha (a fresh push has no prior check on its commit), and advisory throughout.Review Focus
checks: writeis already in the caller's block — every caller on this pin owes that grant whether or not it opts in, because GitHub validates the nestedpublish-checkjob's declared permissions at startup regardless of itsif:. Verified the caller block is a superset of the union of all three upstream jobs at the current pinf8eec04(gate{};gradecontents:read/issues:write/pull-requests:write/checks:read/actions:read/statuses:read;publish-checkchecks:write/contents:read/pull-requests:read).neutral, and the grader's rollup ladder isFAILURE → PENDING → SUCCESS → NEUTRAL → null; a neutral context can only turn anullrollup intoNEUTRAL, and the reversibility axis tests$checks == null or $checks != "SUCCESS", which treats those two identically. So a re-grade of the same head SHA cannot grade worse on that axis because of the check this PR causes to be published. The check is not self-excluded (self-exclusion keys oncheckSuite.workflowRun.databaseIdand a REST-created check run has noworkflowRun), which is what makes the third limit above bite — the tier is unaffected by the ladder, but the timing of the poll loop is not.ci/bump-pr-riskonto a newer upstream SHA, this branch needs a rebase onto the new base before it can merge.check_nameis left at its upstream default,PR risk (advisory).Verification
Workflow-only diff, no
src/touched, so the Vitest/Playwright suites are not implicated and were not run — that is stated rather than papered over.actionlint .github/workflows/ci-pr-risk.yml→ 0 findings.withresolves toworkflows_ref,enabled,pr_number,pr_numbers,check_run: true; theuses:ref andworkflows_refare the identical 40-hex SHAf8eec04e7923f4719d5b7dbc342e5c15580e22ba(the lock-step pin contract the reusable workflow enforces).main'se4a8f7c, declaredtype: boolean, default: falseat6178233, and still declared at the current base pinf8eec04.publish-check'sif:and its POST-only publish loop inpr-risk.yml, theempty_deadline/settled_oncepoll loop inscripts/pr-risk/grade-targets.sh.What this does not do
risk:*label; none has an advisory Check Run, and none gains one retroactively — each picks one up on its next push, reopen, or ready-for-review event. A manualworkflow_dispatchwithpr_numbersis the backfill lever (subject to the kill-switch limit above).Provenance
check_run: trueand identicaluses:/workflows_refpins; upstream input-existence check at three pins (absent atmain'se4a8f7c, present at6178233and at the current base pinf8eec04); end-to-end confirmation that the neutralPR risk (advisory)check (Risk: R3) published on this PR's own head commit; each documented limit read out of the pinned upstream source (publish-check'sif:and POST-only publish loop; theempty_deadline/settled_oncepoll loop ingrade-targets.sh). After merging the rebased base, every one of those claims was re-read againstf8eec04rather than carried forward on trust —check_run/check_namestill declared;publish-checkstillneeds.gate.outputs.enabled == 'true' && inputs.check_runwith noworkflow_dispatchescape whilegradekeeps one; the publish step still POST-only with a hardcodedneutral;grade-targets.shstillempty_deadline=$(date +%s) + 120; the caller permissions block still a superset of the upstream union.actionlintre-run clean on the merged file. Repo test suites not run — nosrc/change.main, because the input does not exist atmain's current pin (see above). All six review findings were adjudicated against the pinned upstream source: the High severity one (undefined input → startup failure) is refuted by the check this PR already published; the other five are confirmed but upstream-owned and unfixable from a caller, so they are recorded as comments at the opt-in site instead of being coded around. No follow-up tickets filed — each is advisory-only and belongs to Comfy-Org/github-workflows, not this repo. The base branch was later force-pushed onto a newer upstream SHA (the "rebase hazard" this body called out), which conflicted this branch on the pin lines; resolved by taking the base'sf8eec04and keeping this branch'scheck_run: trueand comment block, so the diff against the base is unchanged in substance.