Skip to content

ci(pr-risk): publish the advisory grade as a neutral Check Run - #15442

Open
mattmillerai wants to merge 6 commits into
ci/bump-pr-riskfrom
matt/be-8038-pr-risk-check-run
Open

ci(pr-risk): publish the advisory grade as a neutral Check Run#15442
mattmillerai wants to merge 6 commits into
ci/bump-pr-riskfrom
matt/be-8038-pr-risk-check-run

Conversation

@mattmillerai

@mattmillerai mattmillerai commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

STACKED — merging lands on ci/bump-pr-risk (owned by @cloud-code-bot, #15379), NOT main.

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 mutable risk:* label. One input, no pin change.

Changes

  • What: check_run: true in the with: block of the pr-risk job, plus the file-header sentence describing what the caller emits and a comment block recording the three limits of the artifact (see Limits).
  • Breaking: none. The published check's conclusion is hardcoded neutral upstream 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 main

check_run does not exist on the pin main currently carries (e4a8f7c, 2026-08-04) — that revision of the reusable workflow has no check_run input and no publish-check job at all. Adding check_run: true on top of it would be an undefined with: 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 — 6178233 when this branch opened, and f8eec04 after #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 6178233f8eec04, this branch took the base's pin verbatim in the merge and kept only the check_run: true opt-in as its own diff.

It is already working on this PR

pull_request runs the caller from the merge ref, so this branch's own CI exercised check_run: true and 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:

$ gh api repos/Comfy-Org/ComfyUI_frontend/commits/1f7b816832.../check-runs
{"id": 96281900642, "name": "PR risk (advisory)", "status": "completed",
 "conclusion": "neutral", "output_title": "Risk: R3"}

A neutral conclusion renders as skipping in gh 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.

  • Absence, not conclusion, is the failure mode. The job's if: skips fork and Dependabot PRs, and upstream's publish-check gates on the kill switch (needs.gate.outputs.enabled == 'true') even for a manual dispatch, while grade deliberately does not. So a dispatched re-grade with RISK_CONFIG {"enabled": false} still rewrites the label and publishes nothing — the dispute/backfill path is exactly the one that can produce no record. Never mark PR risk (advisory) required in branch protection: the skipped PRs would block forever on a check that is never created.
  • Not one per commit. The publisher only ever 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_review after opened, 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.
  • It perturbs the next grade of the same sha. An existing check makes checks_state non-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

  • Permissions: unchanged. checks: write is already in the caller's block — every caller on this pin owes that grant whether or not it opts in, because GitHub validates the nested publish-check job's declared permissions at startup regardless of its if:. Verified the caller block is a superset of the union of all three upstream jobs at the current pin f8eec04 (gate {}; grade contents:read/issues:write/pull-requests:write/checks:read/actions:read/statuses:read; publish-check checks:write/contents:read/pull-requests:read).
  • No tier feedback loop. The published check is neutral, and the grader's rollup ladder is FAILURE → PENDING → SUCCESS → NEUTRAL → null; a neutral context can only turn a null rollup into NEUTRAL, 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 on checkSuite.workflowRun.databaseId and a REST-created check run has no workflowRun), 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.
  • Rebase hazard: the base branch is bot-owned. If the caller-bump fleet force-pushes ci/bump-pr-risk onto a newer upstream SHA, this branch needs a rebase onto the new base before it can merge.
  • check_name is 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.
  • YAML parse of the caller: with resolves to workflows_ref, enabled, pr_number, pr_numbers, check_run: true; the uses: ref and workflows_ref are the identical 40-hex SHA f8eec04e7923f4719d5b7dbc342e5c15580e22ba (the lock-step pin contract the reusable workflow enforces).
  • Input existence checked against the reusable workflow source at all three pins: absent at main's e4a8f7c, declared type: boolean, default: false at 6178233, and still declared at the current base pin f8eec04.
  • End-to-end: the neutral check published on this PR's own head commit (above).
  • Each documented limit read out of the pinned source — publish-check's if: and its POST-only publish loop in pr-risk.yml, the empty_deadline/settled_once poll loop in scripts/pr-risk/grade-targets.sh.

What this does not do

  • It does not backfill. Check Runs attach to a commit, and this only publishes on grades that happen after it merges. 175 of the 747 currently-open PRs in this repo already carry a 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 manual workflow_dispatch with pr_numbers is the backfill lever (subject to the kill-switch limit above).
  • Unexercised artifact: the source request cites an upstream investigation (39 PRs whose CI label exceeded an offline regrade) recorded in a tracker I have no access to. I could not read it, so I neither confirmed nor relied on that count; nothing in this diff depends on it.

Provenance

  • Authored by: agent-work loop
  • Verified: actionlint on the changed workflow: 0 findings; YAML parse confirming check_run: true and identical uses:/workflows_ref pins; upstream input-existence check at three pins (absent at main's e4a8f7c, present at 6178233 and at the current base pin f8eec04); end-to-end confirmation that the neutral PR 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's if: and POST-only publish loop; the empty_deadline/settled_once poll loop in grade-targets.sh). After merging the rebased base, every one of those claims was re-read against f8eec04 rather than carried forward on trust — check_run/check_name still declared; publish-check still needs.gate.outputs.enabled == 'true' && inputs.check_run with no workflow_dispatch escape while grade keeps one; the publish step still POST-only with a hardcoded neutral; grade-targets.sh still empty_deadline=$(date +%s) + 120; the caller permissions block still a superset of the upstream union. actionlint re-run clean on the merged file. Repo test suites not run — no src/ change.
  • Deviations: stacked on ci: bump pr-risk to github-workflows@eaee6df #15379 rather than opened against main, because the input does not exist at main'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's f8eec04 and keeping this branch's check_run: true and comment block, so the diff against the base is unchanged in substance.

cloud-code-bot Bot and others added 2 commits August 17, 2026 23:11
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:`.
@mattmillerai mattmillerai added the agent-coded Opened by the agent-work code loop label Aug 20, 2026
@mattmillerai
mattmillerai marked this pull request as ready for review August 20, 2026 01:18
@coderabbitai

coderabbitai Bot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: cedfe31a-28db-4482-84b4-7171caae98c4

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Comment @coderabbitai help to get the list of available commands.

@dosubot dosubot Bot added the size:XS This PR changes 0-9 lines, ignoring generated files. label Aug 20, 2026
@github-actions

github-actions Bot commented Aug 20, 2026

Copy link
Copy Markdown

🎭 Playwright: ✅ 1839 passed, 0 failed · 1 flaky

📊 Browser Reports
  • chromium: View Report (✅ 1818 / ❌ 0 / ⚠️ 1 / ⏭️ 5)
  • chromium-2x: View Report (✅ 2 / ❌ 0 / ⚠️ 0 / ⏭️ 0)
  • chromium-0.5x: View Report (✅ 1 / ❌ 0 / ⚠️ 0 / ⏭️ 0)
  • mobile-chrome: View Report (✅ 18 / ❌ 0 / ⚠️ 0 / ⏭️ 0)

📦 Bundle Size

⏳ Size data collection in progress…

⚡ Performance Report

canvas-idle: · 60.0 avg FPS · 59.9 P5 FPS ✅ (target: ≥52) · 0ms TBT · 75.1 MB heap
canvas-mouse-sweep: · 60.0 avg FPS · 59.9 P5 FPS ✅ (target: ≥52) · 0ms TBT · 58.6 MB heap
canvas-zoom-sweep: · 60.0 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 0ms TBT · 61.2 MB heap
dom-widget-clipping: · 60.0 avg FPS · 59.9 P5 FPS ✅ (target: ≥52) · 0ms TBT · 68.4 MB heap
large-graph-idle: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 68.2 MB heap
large-graph-pan: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 76.4 MB heap
large-graph-zoom: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 66.6 MB heap
legacy-node-drag: · 60.0 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 0ms TBT · 66.8 MB heap
minimap-idle: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 66.4 MB heap
subgraph-dom-widget-clipping: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 69.1 MB heap
subgraph-idle: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 74.5 MB heap
subgraph-mouse-sweep: · 60.0 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 0ms TBT · 75.5 MB heap
subgraph-transition-enter: · 60.0 avg FPS · 59.9 P5 FPS ✅ (target: ≥52) · 131ms TBT · 82.7 MB heap
viewport-pan-sweep: · 60.0 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 0ms TBT · 73.9 MB heap
vue-large-graph-idle: · 56.2 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 0ms TBT · 163.5 MB heap
vue-large-graph-pan: · 56.3 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 145ms TBT · 182.6 MB heap
workflow-execution: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 61.2 MB heap

ℹ️ No baseline found — significance unavailable.

Absolute values
Metric Value
canvas-idle: avg frame time 17ms
canvas-idle: p95 frame time 17ms
canvas-idle: layout duration 0ms
canvas-idle: style recalc duration 7ms
canvas-idle: layout count 0
canvas-idle: style recalc count 8
canvas-idle: task duration 595ms
canvas-idle: script duration 9ms
canvas-idle: TBT 0ms
canvas-idle: heap used 75.1 MB
canvas-idle: DOM nodes -283
canvas-idle: event listeners -151
canvas-mouse-sweep: avg frame time 17ms
canvas-mouse-sweep: p95 frame time 17ms
canvas-mouse-sweep: layout duration 4ms
canvas-mouse-sweep: style recalc duration 42ms
canvas-mouse-sweep: layout count 12
canvas-mouse-sweep: style recalc count 76
canvas-mouse-sweep: task duration 903ms
canvas-mouse-sweep: script duration 109ms
canvas-mouse-sweep: TBT 0ms
canvas-mouse-sweep: heap used 58.6 MB
canvas-mouse-sweep: DOM nodes -282
canvas-mouse-sweep: event listeners -166
canvas-zoom-sweep: avg frame time 17ms
canvas-zoom-sweep: p95 frame time 17ms
canvas-zoom-sweep: layout duration 1ms
canvas-zoom-sweep: style recalc duration 17ms
canvas-zoom-sweep: layout count 6
canvas-zoom-sweep: style recalc count 33
canvas-zoom-sweep: task duration 355ms
canvas-zoom-sweep: script duration 10ms
canvas-zoom-sweep: TBT 0ms
canvas-zoom-sweep: heap used 61.2 MB
canvas-zoom-sweep: DOM nodes 78
canvas-zoom-sweep: event listeners 19
dom-widget-clipping: avg frame time 17ms
dom-widget-clipping: p95 frame time 17ms
dom-widget-clipping: layout duration 0ms
dom-widget-clipping: style recalc duration 8ms
dom-widget-clipping: layout count 0
dom-widget-clipping: style recalc count 12
dom-widget-clipping: task duration 352ms
dom-widget-clipping: script duration 51ms
dom-widget-clipping: TBT 0ms
dom-widget-clipping: heap used 68.4 MB
dom-widget-clipping: DOM nodes 19
dom-widget-clipping: event listeners 1
large-graph-idle: avg frame time 17ms
large-graph-idle: p95 frame time 17ms
large-graph-idle: layout duration 0ms
large-graph-idle: style recalc duration 9ms
large-graph-idle: layout count 0
large-graph-idle: style recalc count 9
large-graph-idle: task duration 683ms
large-graph-idle: script duration 17ms
large-graph-idle: TBT 0ms
large-graph-idle: heap used 68.2 MB
large-graph-idle: DOM nodes -276
large-graph-idle: event listeners -151
large-graph-pan: avg frame time 17ms
large-graph-pan: p95 frame time 17ms
large-graph-pan: layout duration 0ms
large-graph-pan: style recalc duration 18ms
large-graph-pan: layout count 0
large-graph-pan: style recalc count 70
large-graph-pan: task duration 1217ms
large-graph-pan: script duration 320ms
large-graph-pan: TBT 0ms
large-graph-pan: heap used 76.4 MB
large-graph-pan: DOM nodes -277
large-graph-pan: event listeners -163
large-graph-zoom: avg frame time 17ms
large-graph-zoom: p95 frame time 17ms
large-graph-zoom: layout duration 8ms
large-graph-zoom: style recalc duration 16ms
large-graph-zoom: layout count 60
large-graph-zoom: style recalc count 65
large-graph-zoom: task duration 1395ms
large-graph-zoom: script duration 389ms
large-graph-zoom: TBT 0ms
large-graph-zoom: heap used 66.6 MB
large-graph-zoom: DOM nodes -137
large-graph-zoom: event listeners -74
legacy-node-drag: avg frame time 17ms
legacy-node-drag: p95 frame time 17ms
legacy-node-drag: layout duration 0ms
legacy-node-drag: style recalc duration 10ms
legacy-node-drag: layout count 0
legacy-node-drag: style recalc count 46
legacy-node-drag: task duration 1473ms
legacy-node-drag: script duration 478ms
legacy-node-drag: TBT 0ms
legacy-node-drag: heap used 66.8 MB
legacy-node-drag: DOM nodes 12
legacy-node-drag: event listeners 186
minimap-idle: avg frame time 17ms
minimap-idle: p95 frame time 17ms
minimap-idle: layout duration 0ms
minimap-idle: style recalc duration 6ms
minimap-idle: layout count 0
minimap-idle: style recalc count 7
minimap-idle: task duration 680ms
minimap-idle: script duration 17ms
minimap-idle: TBT 0ms
minimap-idle: heap used 66.4 MB
minimap-idle: DOM nodes -284
minimap-idle: event listeners -150
subgraph-dom-widget-clipping: avg frame time 17ms
subgraph-dom-widget-clipping: p95 frame time 17ms
subgraph-dom-widget-clipping: layout duration 0ms
subgraph-dom-widget-clipping: style recalc duration 9ms
subgraph-dom-widget-clipping: layout count 0
subgraph-dom-widget-clipping: style recalc count 46
subgraph-dom-widget-clipping: task duration 393ms
subgraph-dom-widget-clipping: script duration 121ms
subgraph-dom-widget-clipping: TBT 0ms
subgraph-dom-widget-clipping: heap used 69.1 MB
subgraph-dom-widget-clipping: DOM nodes 18
subgraph-dom-widget-clipping: event listeners 7
subgraph-idle: avg frame time 17ms
subgraph-idle: p95 frame time 17ms
subgraph-idle: layout duration 0ms
subgraph-idle: style recalc duration 9ms
subgraph-idle: layout count 0
subgraph-idle: style recalc count 9
subgraph-idle: task duration 532ms
subgraph-idle: script duration 8ms
subgraph-idle: TBT 0ms
subgraph-idle: heap used 74.5 MB
subgraph-idle: DOM nodes -283
subgraph-idle: event listeners -151
subgraph-mouse-sweep: avg frame time 17ms
subgraph-mouse-sweep: p95 frame time 17ms
subgraph-mouse-sweep: layout duration 5ms
subgraph-mouse-sweep: style recalc duration 36ms
subgraph-mouse-sweep: layout count 16
subgraph-mouse-sweep: style recalc count 75
subgraph-mouse-sweep: task duration 793ms
subgraph-mouse-sweep: script duration 83ms
subgraph-mouse-sweep: TBT 0ms
subgraph-mouse-sweep: heap used 75.5 MB
subgraph-mouse-sweep: DOM nodes -283
subgraph-mouse-sweep: event listeners -152
subgraph-transition-enter: avg frame time 17ms
subgraph-transition-enter: p95 frame time 17ms
subgraph-transition-enter: layout duration 13ms
subgraph-transition-enter: style recalc duration 32ms
subgraph-transition-enter: layout count 13
subgraph-transition-enter: style recalc count 18
subgraph-transition-enter: task duration 961ms
subgraph-transition-enter: script duration 18ms
subgraph-transition-enter: TBT 131ms
subgraph-transition-enter: heap used 82.7 MB
subgraph-transition-enter: DOM nodes 13673
subgraph-transition-enter: event listeners 2375
viewport-pan-sweep: avg frame time 17ms
viewport-pan-sweep: p95 frame time 17ms
viewport-pan-sweep: layout duration 0ms
viewport-pan-sweep: style recalc duration 44ms
viewport-pan-sweep: layout count 0
viewport-pan-sweep: style recalc count 251
viewport-pan-sweep: task duration 4113ms
viewport-pan-sweep: script duration 991ms
viewport-pan-sweep: TBT 0ms
viewport-pan-sweep: heap used 73.9 MB
viewport-pan-sweep: DOM nodes -252
viewport-pan-sweep: event listeners -132
vue-large-graph-idle: avg frame time 18ms
vue-large-graph-idle: p95 frame time 17ms
vue-large-graph-idle: layout duration 0ms
vue-large-graph-idle: style recalc duration 0ms
vue-large-graph-idle: layout count 0
vue-large-graph-idle: style recalc count 0
vue-large-graph-idle: task duration 17514ms
vue-large-graph-idle: script duration 127ms
vue-large-graph-idle: TBT 0ms
vue-large-graph-idle: heap used 163.5 MB
vue-large-graph-idle: DOM nodes -8312
vue-large-graph-idle: event listeners -16387
vue-large-graph-pan: avg frame time 18ms
vue-large-graph-pan: p95 frame time 17ms
vue-large-graph-pan: layout duration 0ms
vue-large-graph-pan: style recalc duration 20ms
vue-large-graph-pan: layout count 0
vue-large-graph-pan: style recalc count 175
vue-large-graph-pan: task duration 20840ms
vue-large-graph-pan: script duration 411ms
vue-large-graph-pan: TBT 145ms
vue-large-graph-pan: heap used 182.6 MB
vue-large-graph-pan: DOM nodes -8312
vue-large-graph-pan: event listeners -16385
workflow-execution: avg frame time 17ms
workflow-execution: p95 frame time 17ms
workflow-execution: layout duration 1ms
workflow-execution: style recalc duration 15ms
workflow-execution: layout count 2
workflow-execution: style recalc count 9
workflow-execution: task duration 82ms
workflow-execution: script duration 7ms
workflow-execution: TBT 0ms
workflow-execution: heap used 61.2 MB
workflow-execution: DOM nodes 121
workflow-execution: event listeners 38
Raw data
{
  "timestamp": "2026-08-21T07:30:41.715Z",
  "gitSha": "45f3b24efeff7babd7419d41836a53f4ef1c393d",
  "branch": "matt/be-8038-pr-risk-check-run",
  "measurements": [
    {
      "name": "canvas-idle",
      "durationMs": 2141.9550000000527,
      "styleRecalcs": 7,
      "styleRecalcDurationMs": 6.171000000000001,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 680.3760000000001,
      "heapDeltaBytes": 15596100,
      "heapUsedBytes": 76857100,
      "domNodes": -284,
      "jsHeapTotalBytes": 4710400,
      "scriptDurationMs": 10.9,
      "eventListeners": -151,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "canvas-idle",
      "durationMs": 2042.7349999999933,
      "styleRecalcs": 9,
      "styleRecalcDurationMs": 8.65,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 509.30299999999994,
      "heapDeltaBytes": 18782936,
      "heapUsedBytes": 80538692,
      "domNodes": -282,
      "jsHeapTotalBytes": 4448256,
      "scriptDurationMs": 7.585,
      "eventListeners": -151,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "canvas-mouse-sweep",
      "durationMs": 1974.316999999985,
      "styleRecalcs": 78,
      "styleRecalcDurationMs": 51.214999999999996,
      "layouts": 12,
      "layoutDurationMs": 4.699,
      "taskDurationMs": 988.4219999999999,
      "heapDeltaBytes": 5163552,
      "heapUsedBytes": 66614012,
      "domNodes": -283,
      "jsHeapTotalBytes": 5234688,
      "scriptDurationMs": 123.196,
      "eventListeners": -181,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "canvas-mouse-sweep",
      "durationMs": 1807.2220000000243,
      "styleRecalcs": 73,
      "styleRecalcDurationMs": 33.102,
      "layouts": 12,
      "layoutDurationMs": 3.357,
      "taskDurationMs": 817.908,
      "heapDeltaBytes": -4814828,
      "heapUsedBytes": 56221604,
      "domNodes": -280,
      "jsHeapTotalBytes": 5234688,
      "scriptDurationMs": 95.669,
      "eventListeners": -151,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "canvas-zoom-sweep",
      "durationMs": 1723.0090000000473,
      "styleRecalcs": 33,
      "styleRecalcDurationMs": 16.902,
      "layouts": 6,
      "layoutDurationMs": 0.5790000000000001,
      "taskDurationMs": 361.72099999999995,
      "heapDeltaBytes": 3142608,
      "heapUsedBytes": 64408548,
      "domNodes": 79,
      "jsHeapTotalBytes": 4980736,
      "scriptDurationMs": 10.148000000000001,
      "eventListeners": 19,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "canvas-zoom-sweep",
      "durationMs": 1738.0660000000034,
      "styleRecalcs": 32,
      "styleRecalcDurationMs": 16.121,
      "layouts": 6,
      "layoutDurationMs": 0.707,
      "taskDurationMs": 349.002,
      "heapDeltaBytes": 2483488,
      "heapUsedBytes": 63860348,
      "domNodes": 77,
      "jsHeapTotalBytes": 4194304,
      "scriptDurationMs": 9.252,
      "eventListeners": 19,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "dom-widget-clipping",
      "durationMs": 579.9339999999802,
      "styleRecalcs": 11,
      "styleRecalcDurationMs": 8.03,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 370.21000000000004,
      "heapDeltaBytes": 10834264,
      "heapUsedBytes": 71311408,
      "domNodes": 18,
      "jsHeapTotalBytes": 5505024,
      "scriptDurationMs": 54.74900000000001,
      "eventListeners": 0,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.699999999999818
    },
    {
      "name": "dom-widget-clipping",
      "durationMs": 558.5849999999937,
      "styleRecalcs": 12,
      "styleRecalcDurationMs": 7.358999999999999,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 333.769,
      "heapDeltaBytes": 10710864,
      "heapUsedBytes": 72140564,
      "domNodes": 20,
      "jsHeapTotalBytes": 5242880,
      "scriptDurationMs": 48.18899999999999,
      "eventListeners": 2,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "large-graph-idle",
      "durationMs": 2072.47000000001,
      "styleRecalcs": 9,
      "styleRecalcDurationMs": 8.739999999999998,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 684.6290000000001,
      "heapDeltaBytes": -5909124,
      "heapUsedBytes": 70167100,
      "domNodes": -282,
      "jsHeapTotalBytes": -2363392,
      "scriptDurationMs": 17.021,
      "eventListeners": -151,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "large-graph-idle",
      "durationMs": 2071.390000000065,
      "styleRecalcs": 9,
      "styleRecalcDurationMs": 8.913,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 680.749,
      "heapDeltaBytes": -2858300,
      "heapUsedBytes": 72874000,
      "domNodes": -270,
      "jsHeapTotalBytes": -1314816,
      "scriptDurationMs": 17.512000000000004,
      "eventListeners": -151,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "large-graph-pan",
      "durationMs": 2196.9369999999913,
      "styleRecalcs": 70,
      "styleRecalcDurationMs": 17.350000000000005,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 1242.684,
      "heapDeltaBytes": 12894076,
      "heapUsedBytes": 88706848,
      "domNodes": -284,
      "jsHeapTotalBytes": 4415488,
      "scriptDurationMs": 325.244,
      "eventListeners": -179,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "large-graph-pan",
      "durationMs": 2167.1010000000024,
      "styleRecalcs": 70,
      "styleRecalcDurationMs": 18.314,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 1191.429,
      "heapDeltaBytes": -5157356,
      "heapUsedBytes": 71548048,
      "domNodes": -269,
      "jsHeapTotalBytes": -565248,
      "scriptDurationMs": 314.083,
      "eventListeners": -147,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "large-graph-zoom",
      "durationMs": 3264.1179999999963,
      "styleRecalcs": 65,
      "styleRecalcDurationMs": 16.667,
      "layouts": 60,
      "layoutDurationMs": 8.416,
      "taskDurationMs": 1451.78,
      "heapDeltaBytes": -7699588,
      "heapUsedBytes": 70259316,
      "domNodes": -286,
      "jsHeapTotalBytes": 3928064,
      "scriptDurationMs": 391.515,
      "eventListeners": -155,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "large-graph-zoom",
      "durationMs": 3164.0409999999974,
      "styleRecalcs": 65,
      "styleRecalcDurationMs": 15.865,
      "layouts": 60,
      "layoutDurationMs": 7.934,
      "taskDurationMs": 1338.953,
      "heapDeltaBytes": -8853664,
      "heapUsedBytes": 69374344,
      "domNodes": 12,
      "jsHeapTotalBytes": 5804032,
      "scriptDurationMs": 387.444,
      "eventListeners": 8,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.699999999999818
    },
    {
      "name": "legacy-node-drag",
      "durationMs": 2327.6950000000056,
      "styleRecalcs": 46,
      "styleRecalcDurationMs": 10.738000000000001,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 1508.6360000000002,
      "heapDeltaBytes": -14236816,
      "heapUsedBytes": 69590912,
      "domNodes": 12,
      "jsHeapTotalBytes": 8425472,
      "scriptDurationMs": 481.30699999999996,
      "eventListeners": 186,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "legacy-node-drag",
      "durationMs": 2206.8080000000236,
      "styleRecalcs": 46,
      "styleRecalcDurationMs": 9.616,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 1436.871,
      "heapDeltaBytes": -13710080,
      "heapUsedBytes": 70543124,
      "domNodes": 12,
      "jsHeapTotalBytes": 8425472,
      "scriptDurationMs": 474.15299999999996,
      "eventListeners": 186,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.670000000000012,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "minimap-idle",
      "durationMs": 2028.8529999999696,
      "styleRecalcs": 6,
      "styleRecalcDurationMs": 5.603000000000001,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 725.746,
      "heapDeltaBytes": -12389128,
      "heapUsedBytes": 69969156,
      "domNodes": -284,
      "jsHeapTotalBytes": 3403776,
      "scriptDurationMs": 19.809,
      "eventListeners": -151,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "minimap-idle",
      "durationMs": 2068.9889999999878,
      "styleRecalcs": 8,
      "styleRecalcDurationMs": 5.971000000000001,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 634.803,
      "heapDeltaBytes": -12992380,
      "heapUsedBytes": 69204456,
      "domNodes": -283,
      "jsHeapTotalBytes": 3928064,
      "scriptDurationMs": 14.238000000000001,
      "eventListeners": -149,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "subgraph-dom-widget-clipping",
      "durationMs": 611.814000000038,
      "styleRecalcs": 47,
      "styleRecalcDurationMs": 10.738,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 402.75699999999995,
      "heapDeltaBytes": 10748020,
      "heapUsedBytes": 72403016,
      "domNodes": 20,
      "jsHeapTotalBytes": 5242880,
      "scriptDurationMs": 123.37700000000001,
      "eventListeners": 8,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "subgraph-dom-widget-clipping",
      "durationMs": 584.2229999999518,
      "styleRecalcs": 45,
      "styleRecalcDurationMs": 8.186999999999998,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 382.259,
      "heapDeltaBytes": 11134348,
      "heapUsedBytes": 72473044,
      "domNodes": 16,
      "jsHeapTotalBytes": 4980736,
      "scriptDurationMs": 118.03800000000001,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.699999999999818
    },
    {
      "name": "subgraph-idle",
      "durationMs": 2048.9130000000273,
      "styleRecalcs": 8,
      "styleRecalcDurationMs": 10.395000000000001,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 542.1949999999999,
      "heapDeltaBytes": 16180744,
      "heapUsedBytes": 77763296,
      "domNodes": -282,
      "jsHeapTotalBytes": 4448256,
      "scriptDurationMs": 8.783000000000003,
      "eventListeners": -151,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "subgraph-idle",
      "durationMs": 2018.0820000000494,
      "styleRecalcs": 9,
      "styleRecalcDurationMs": 7.755,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 521.6999999999999,
      "heapDeltaBytes": 16952468,
      "heapUsedBytes": 78539380,
      "domNodes": -283,
      "jsHeapTotalBytes": 4448256,
      "scriptDurationMs": 7.724,
      "eventListeners": -151,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.670000000000012,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "subgraph-mouse-sweep",
      "durationMs": 1739.2970000000219,
      "styleRecalcs": 76,
      "styleRecalcDurationMs": 39.443999999999996,
      "layouts": 16,
      "layoutDurationMs": 4.779,
      "taskDurationMs": 832.681,
      "heapDeltaBytes": 20536156,
      "heapUsedBytes": 81906948,
      "domNodes": -282,
      "jsHeapTotalBytes": 5496832,
      "scriptDurationMs": 84.783,
      "eventListeners": -151,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "subgraph-mouse-sweep",
      "durationMs": 1747.2999999999956,
      "styleRecalcs": 74,
      "styleRecalcDurationMs": 32.374,
      "layouts": 16,
      "layoutDurationMs": 4.558,
      "taskDurationMs": 753.546,
      "heapDeltaBytes": 14845700,
      "heapUsedBytes": 76490304,
      "domNodes": -283,
      "jsHeapTotalBytes": 4448256,
      "scriptDurationMs": 82.11500000000001,
      "eventListeners": -153,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "subgraph-transition-enter",
      "durationMs": 1435.559000000012,
      "styleRecalcs": 18,
      "styleRecalcDurationMs": 32.06,
      "layouts": 13,
      "layoutDurationMs": 13.303000000000003,
      "taskDurationMs": 960.786,
      "heapDeltaBytes": -4005508,
      "heapUsedBytes": 86665016,
      "domNodes": 13673,
      "jsHeapTotalBytes": 11010048,
      "scriptDurationMs": 18.426,
      "eventListeners": 2375,
      "totalBlockingTimeMs": 131,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "viewport-pan-sweep",
      "durationMs": 8279.106999999953,
      "styleRecalcs": 251,
      "styleRecalcDurationMs": 46.559999999999995,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 4176.005,
      "heapDeltaBytes": -1633448,
      "heapUsedBytes": 74450268,
      "domNodes": -269,
      "jsHeapTotalBytes": 483328,
      "scriptDurationMs": 995.5759999999999,
      "eventListeners": -133,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "viewport-pan-sweep",
      "durationMs": 8194.036000000096,
      "styleRecalcs": 251,
      "styleRecalcDurationMs": 40.766,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 4050.908,
      "heapDeltaBytes": 4883168,
      "heapUsedBytes": 80610008,
      "domNodes": -235,
      "jsHeapTotalBytes": -303104,
      "scriptDurationMs": 986.948,
      "eventListeners": -131,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "vue-large-graph-idle",
      "durationMs": 18382.514000000014,
      "styleRecalcs": 0,
      "styleRecalcDurationMs": 0,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 17331.870000000003,
      "heapDeltaBytes": -43238256,
      "heapUsedBytes": 166091964,
      "domNodes": -8312,
      "jsHeapTotalBytes": -15142912,
      "scriptDurationMs": 119.34700000000001,
      "eventListeners": -16389,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 17.780000000000047,
      "p95FrameDurationMs": 16.80000000000291
    },
    {
      "name": "vue-large-graph-idle",
      "durationMs": 18562.19199999998,
      "styleRecalcs": 0,
      "styleRecalcDurationMs": 0,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 17697.052,
      "heapDeltaBytes": -45320920,
      "heapUsedBytes": 176794960,
      "domNodes": -8312,
      "jsHeapTotalBytes": -14618624,
      "scriptDurationMs": 134.74099999999999,
      "eventListeners": -16385,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 17.779999999999927,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "vue-large-graph-pan",
      "durationMs": 21353.775999999983,
      "styleRecalcs": 174,
      "styleRecalcDurationMs": 20.498999999999988,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 20780.958,
      "heapDeltaBytes": -26061328,
      "heapUsedBytes": 191387788,
      "domNodes": -8312,
      "jsHeapTotalBytes": -14131200,
      "scriptDurationMs": 424.4169999999999,
      "eventListeners": -16385,
      "totalBlockingTimeMs": 192,
      "frameDurationMs": 17.776666666666642,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "vue-large-graph-pan",
      "durationMs": 21544.426999999927,
      "styleRecalcs": 176,
      "styleRecalcDurationMs": 19.743999999999986,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 20899.295000000002,
      "heapDeltaBytes": -30533708,
      "heapUsedBytes": 191603412,
      "domNodes": -8312,
      "jsHeapTotalBytes": -14950400,
      "scriptDurationMs": 397.026,
      "eventListeners": -16385,
      "totalBlockingTimeMs": 98,
      "frameDurationMs": 17.776666666666642,
      "p95FrameDurationMs": 16.80000000000291
    },
    {
      "name": "workflow-execution",
      "durationMs": 116.87300000005507,
      "styleRecalcs": 8,
      "styleRecalcDurationMs": 13.88,
      "layouts": 2,
      "layoutDurationMs": 0.52,
      "taskDurationMs": 75.23800000000001,
      "heapDeltaBytes": 3052448,
      "heapUsedBytes": 64297108,
      "domNodes": 111,
      "jsHeapTotalBytes": 262144,
      "scriptDurationMs": 6.397999999999999,
      "eventListeners": 49,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.670000000000012,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "workflow-execution",
      "durationMs": 131.0280000000148,
      "styleRecalcs": 9,
      "styleRecalcDurationMs": 15.918000000000001,
      "layouts": 2,
      "layoutDurationMs": 1.4069999999999998,
      "taskDurationMs": 88.799,
      "heapDeltaBytes": 3032860,
      "heapUsedBytes": 64132968,
      "domNodes": 130,
      "jsHeapTotalBytes": 262144,
      "scriptDurationMs": 7.272999999999998,
      "eventListeners": 27,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.700000000000728
    }
  ]
}

@github-actions github-actions Bot added the risk:R3 PR risk grade (advisory shadow check; grader-owned) label Aug 20, 2026
@mattmillerai mattmillerai added the cursor-review Trigger multi-model Cursor agent review on this PR label Aug 20, 2026
@codecov

codecov Bot commented Aug 20, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

@@                 Coverage Diff                 @@
##           ci/bump-pr-risk   #15442      +/-   ##
===================================================
- Coverage            82.06%   81.75%   -0.32%     
===================================================
  Files                 1887     1887              
  Lines               122840   107167   -15673     
  Branches             39638    33048    -6590     
===================================================
- Hits                100814    87612   -13202     
+ Misses               21522    19212    -2310     
+ Partials               504      343     -161     
Flag Coverage Δ
unit 73.29% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.
see 192 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 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.

Comment thread .github/workflows/ci-pr-risk.yml
Comment thread .github/workflows/ci-pr-risk.yml
Comment thread .github/workflows/ci-pr-risk.yml
Comment thread .github/workflows/ci-pr-risk.yml Outdated
Comment thread .github/workflows/ci-pr-risk.yml
Comment thread .github/workflows/ci-pr-risk.yml Outdated
mattmillerai and others added 2 commits August 19, 2026 22:48
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.
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).
…38-pr-risk-check-run

# Conflicts:
#	.github/workflows/ci-pr-risk.yml
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agent-coded Opened by the agent-work code loop cursor-review Trigger multi-model Cursor agent review on this PR risk:R3 PR risk grade (advisory shadow check; grader-owned) size:XS This PR changes 0-9 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant