mirror of
https://github.com/Comfy-Org/github-workflows.git
synced 2026-11-05 00:17:37 +00:00
[PR #362] [MERGED] feat(cursor-approve): status card and a standing request-changes on every round outcome (BE-19489) #365
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#365
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/362
Author: @mattmillerai
Created: 10/7/2026
Status: ✅ Merged
Merged: 10/7/2026
Merged by: @mattmillerai
Base:
main← Head:matt/be-19489-status-card-every-outcome📝 Commits (4)
bacfab6feat(cursor-approve): status card and a standing request-changes on every round outcome (BE-19489)3c4914dfeat(cursor-approve): block on a withheld axes decision; name the caller's review label (BE-19489)6705c70fix(cursor-approve): address panel review on #362 (BE-19489)5a89473fix(cursor-approve): leave the standing block when decide's own review is withdrawn or fails (BE-19489)📊 Changes
10 files changed (+1354 additions, -80 deletions)
View changed files
📝
.github/cursor-approve/card.py(+193 -18)📝
.github/cursor-approve/tests/test_card.py(+132 -0)📝
.github/cursor-review/README.md(+1 -1)📝
.github/cursor-review/auto-approve.py(+469 -25)📝
.github/cursor-review/tests/test_approve_external.py(+106 -12)📝
.github/cursor-review/tests/test_auto_approve.py(+341 -14)📝
.github/workflows/cursor-approve.yml(+7 -3)📝
.github/workflows/cursor-review.yml(+15 -3)📝
docs/callers/cursor-approve.md(+71 -1)📝
docs/callers/cursor-review.md(+19 -3)📄 Description
ELI-5
When the robot reviewer finished a round without approving a PR, the PR often showed nothing at all. That happened most when the robot couldn't decide, for example because one of its reviewers crashed. Authors, and the agents that drive PRs to merge, couldn't tell whether they had to do anything. Now every round updates one status comment. It says what happened, why, and what to do next. It also has two hidden tags that agents can read. When the robot can't decide, it also leaves a "changes requested" review, so a PR whose threads are all resolved doesn't look approved. The next round that approves removes that review.
What changed
approve_max_severityis set and the author passesapprove_authors):pass,changes_requested,no_decisionandcapped. An authorapprove_authorsdoesn't list gets no card. The card is still one comment, found by<!-- cursor-approve-card -->and edited in place.decide_gate, verbatim.changes_requested, one line per gating finding: severity,file:line, a thread link (#discussion_r<id>, matched from the open-thread read decide already makes) and why it gated. The reason is delta, a live repeat, High/Critical, orfullscope.<!-- cursor-approve-state: pass|changes_requested|no_decision|capped -->and<!-- cursor-approve-next: none|resolve_then_relabel|relabel|human -->.docs/callers/cursor-approve.md#the-status-card-contract.no_decisionrelabel.human, and the card names the cause.cursor-review-auto-approvemarker, reviewed-SHA marker and base marker.dismiss_own_change_requests. That covers decide's--defer-approvalpath (already the case), decide's posted APPROVE (new: an explicit dismissal, not just the supersede) and cursor-approve'sapprove-external(new).cappedis unchanged: no new request-changes. An unlisted author gets nothing. Behaviour is the same withdefer_approvalon or off.changes_requested, with those threads linked and next stepresolve_then_relabel.post-reviewnow alsoneeds: round-cap.diff-sizealready depended on it, so the ordering doesn't change.--card true --round --max-rounds --run-url.auto-approve.py'sgh. They never change the decision, and a failed write is only a warning.approve-externalnow leaves the same standing REQUEST_CHANGES onnot_approved,error,supersededandneeds_human. The body has the decide card's reasons (shared throughcard.decide_reasons) and next step, plus the reviewed-SHA marker.vetoed,own_prand an approval post none.supersededdecide that finishes after a newer run already approved the live head posts none. As the approver's latest review, it would override that approval.review_labelinput instead of a hard-codedcursor-review. The value is passed as--review-label "$REVIEW_LABEL". A value that isn't a plain label name is not echoed, and the line says "the review label" instead, so it never names a label the repo may not have.cursor-approve.ymlhas noreview_labelinput, so the cards its own start and decide phases write, and its decide-phase block, keep the defaultcursor-reviewtext.auto-approve.pydocstring,.github/cursor-review/README.md,docs/callers/cursor-review.mdanddocs/callers/cursor-approve.md.Design choice (card placement)
cursor-review's
decidewrites the card.phase: startdoesn't run on more outcomes.consolidated.json), the open threads and their comment ids,decide_gate's reasons and the scope's per-finding gating reason all live in thepost-reviewjob. Sending them to cursor-approve would mean new structured reusable-workflow outputs. Those are flat strings, so we'd have to serialise and re-validate model output across a trust boundary.approve-startonapprove_gate == 'pass' || 'capped'. Running start on more outcomes would mean every caller rewrites itsif:and passes more inputs before a single not-approved card appears. Comfy-Org/cloud#12306 is the motivating case: no job after cursor-review ran at all.pass, cursor-approve's start phase still overwrites the card with the axes table, and its decide phase writes the verdicts. Both write as theAPPROVER_TOKENidentity, which is how they find the same comment. The docs say to pass the sameAPPROVER_TOKENto both. The axes still run only onpass.Testing
cd .github/cursor-review && python3 -m unittest discover -s tests -qcd .github/cursor-approve && python3 -m unittest discover -s tests -qcd .github/workflow-pins && python3 -m unittest discover -s tests -qbash .github/bump-callers/tests/*.shactionlintoncursor-review.ymlandcursor-approve.ymljob.workflow_shaand SC2018/SC2019 findings. The count is unchanged frommain.New tests:
StatusCardAndStandingBlockTest,NoDecisionNextTestandThreadUrlTestintest_auto_approve.py.test_approve_external.py.ContractMarkersintest_card.py.What they cover:
decide_gaterender verbatim.approve-external.cappedposts no new review.approve-externalposts REQUEST_CHANGES carrying the card's reasons and next step and the SHA marker, a later approval withdraws it, andvetoedandown_prpost none. Model-written axis reasons are sanitized, and a latesupersededrun doesn't override a newer approval.review_labelis used in the next-step text; an unsafe value is not echoed and the line falls back to "the review label".All of the new cases fail when run against
origin/mainsources: 23 in cursor-review and 17 in cursor-approve. The exceptions are the guards for an unlisted author and for a non-approval keeping its review, which already held. Five existing tests were updated, because they asserted that an untrusted round posts no review.Not exercised end to end on a live PR. The behaviour is covered by the unit harnesses, which fake
gh.Provenance
check_workflow_pins.py,check_agents_md.pyand the org-repo-literal lint pass.gh.docs/callers/cursor-approve.mdand tracked separately.🤖 Generated with Claude Code
🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.