mirror of
https://github.com/Comfy-Org/github-workflows.git
synced 2026-11-05 00:17:37 +00:00
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#254
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/246
Author: @mattmillerai
Created: 8/28/2026
Status: ✅ Merged
Merged: 8/28/2026
Merged by: @mattmillerai
Base:
main← Head:matt/be-4691-restore-the-cursor-review-blocking-gate-dropped-by-31-or📝 Commits (3)
2444d45fix(cursor-review): restore the blocking gate dropped by #31 (BE-4691)7d33b7fdocs(cursor-review): correct the stale panel count — 3 labs, 6 cells (BE-4691)140de31fix(cursor-review): close the blocking gate's fail-open paths (BE-4691)📊 Changes
12 files changed (+1006 additions, -82 deletions)
View changed files
📝
.github/cursor-review/README.md(+42 -33)📝
.github/cursor-review/post-review.py(+95 -2)➕
.github/cursor-review/tests/test_post_review_delivery.py(+286 -0)➕
.github/cursor-review/tests/test_workflow_inputs_docs.py(+141 -0)📝
.github/workflow-pins/tests/test_check_workflow_pins.py(+5 -2)📝
.github/workflows/cursor-review.yml(+292 -17)📝
.github/workflows/test-cursor-review-scripts.yml(+11 -0)📝
AGENTS.md(+1 -1)📝
CONTRIBUTING.md(+1 -1)📝
README.md(+1 -1)📝
SECURITY.md(+1 -1)📝
docs/callers/cursor-review.md(+130 -24)📄 Description
ELI5
The review panel leaves findings on a PR, but nothing today makes anyone deal with them before merging — like a building inspector who mails you a defect list you're free to ignore. This restores the opt-in Blocking gate: a repo can now require that every finding thread is explicitly resolved (or fixed, which outdates it) before the merge button unlocks. It's off by default, so nothing changes for any caller until a repo flips it on.
Motivation
The gate shipped in #16 — feat(cursor-review): optional blocking gate for unresolved findings (BE-1891) — and was deleted by accident in #31 — fix(cursor-review): recover judge findings after prose, not first JSON region (BE-3160) — whose stated purpose was judge extraction; the regression went unnoticed for ~10 days. #80 — docs(callers): per-workflow setup guides, README restructure, public-repo hygiene (BE-4680) — could only fix the docs half, since restoring a merge-gating job is a workflow behavior change, and deferred the restore to its own ticket. That ticket held pending a product call on whether an unresolved-findings gate is wanted at all; that call has now been made in favor of restoring it. The restore has to be re-fitted rather than reverted: the job graph has since gained a credential split, so the observable "the review actually landed" signal moved from
consolidatetopost-review.Provenance
python3 -m unittest discover -s .github/cursor-review/tests -p 'test_*.py': 383 passed, 0 failed (the inputs-vs-docs drift suite plus the newtest_post_review_delivery.py);python3 -m unittest discover -s .github/workflow-pins/tests -p 'test_*.py': 316 passed, 0 failed at the guard census of 18 sites;python3 .github/workflow-pins/check_workflow_pins.py: 11 workflows, none defaulted, every ref checkout guarded;python3 .github/agents-md-integrity/check_agents_md.py --root .: passed;actionlint .github/workflows/cursor-review.yml .github/workflows/test-cursor-review-scripts.yml: no new findings (one pre-existingjob.workflow_shaschema false positive, present on main); the newuser.type == "Bot"dedupe filter exercised against a hand-built reviews payload (bot review counts, spoofed-user and null-user reviews do not, no jq error on a nulluser). All PR checks green.blocking: true.Reviewer context
bump-cursor-review-callers.yml's path filter, so the fleet SHA-bump PRs carry it to consumers — all of them on unchangedblocking: falsesemantics until they opt in. NoSkip-caller-bumptrailer, deliberately: this is behavioral.blocking: true(this repo's ownci-cursor-review.ymlincluded) — that is per-repo policy with two switches, decided by each repo's owners.Summary
blocking:input (defaultfalse) and the Blocking gate job #31 deleted.gate-unresolved.pynever left the tree (build-ledger imports it) and is unchanged.blockingis on, instead of onlyshould_run == 'true'. A skipped required check counts as passing, so the #16 condition let any "don't review" run (an unrelated label toggled,skip-cursor-reviewpresent, the trigger label removed) mint a green gate over open finding threads. Event-independence also makespull_request_review_threadand plain PR events pure gate re-checks — panel, DMs, and comments all stay skipped — so a required check flips green the moment the last thread is resolved and reports on every push instead of sticking at "Expected".post-review— the terminal write since the credential split; aconsolidatethat succeeded into a failed post still posted nothing — and refuses to pass whendiff-sizeitself failed (the #16 guard only exempted the over-cap verdict).always(), not!cancelled().post-reviewneeds!cancelled()because it WRITES and must not race its replacement; this job only re-reads live thread state, so a superseded run and its replacement can only agree — while!cancelled()would skip it on cancellation, and GitHub counts a skipped required check as passing.Review round
The four-lab panel found ten fail-open paths in the first cut of the gate. Nine are fixed:
post-review.pyexits 0 after a read-only-token 403 (review written to the job summary, never the PR), after the body-onlyReview failedreview, after the no-findings review posted when every panel cell errored, and after the 422 inline fallback — each leaving the thread query empty. It now states delivery positively on$GITHUB_OUTPUT(delivered, plus the split between findings that carry a thread and findings that reached the body only), and the gate requires the statement. Unset reads as false, so a run that died before deciding is closed by default.should_runempty, skipped every guard phrased on it, and greened the check while the Gate job sat red. Now the first thing checked.user.type == "Bot"— the type, not a login, since the poster is caller-configurable viabot_app_id.always()bullet above).merge_group/push/workflow_dispatch) left argparse exiting 2 behind a permanently red check with a traceback for an explanation. Now a named, actionable failure.docs/callers/cursor-review.mdwas not intest-cursor-review-scripts.yml'spaths:filter, so a docs-only PR deleting theblockingrow (the #31 scenario) ran no job.blocking: trueover the label-only triggers and label-keyed concurrency group the new gotcha calls bricking; it now carries the blocking caller shape.Deferred, with the limit documented instead: the gate does not prove the current head SHA was reviewed. A thread stops counting once its hunk changes (
isOutdated), so under a label-gated caller a cosmetic edit to the flagged lines can green the check with no re-review. Requiring a review at the head SHA is a caller-contract change — every label-gated blocking caller would go red on every push until it widenedtypes:or enrolled auto-label — so it belongs behind its own product call, not in the restore. The setup guide now says so plainly and points atcursor-review-auto-label.yml/run_without_label: trueas the close..github/cursor-review/README.md(file inventory, pipeline note, a live "make the review blocking" section, knobs table),docs/callers/cursor-review.md(two-switch contract,blockinginput row, a blocking-gate gotchas section covering the trigger shape, waiver semantics, and who can resolve), the top-level README catalog row, and the AGENTS.md worked example.test_workflow_inputs_docs.py: two-way set equality betweenon.workflow_call.inputsand both docs tables, so the next accidental input deletion fails CI loudly instead of surviving in the docs.gate-unresolved.pyfromworkflows_refin its own job and carries its own empty-ref guard.test_post_review_delivery.py: every path on whichpost-review.pyexits 0, asserted against what it now claims about delivery — so the gate's fail-closed property is pinned by a test rather than by a comment.Test plan
blocking: false) runs unchanged — gate job skipped, review still postsblocking: trueWITHOUT marking the check required; confirm the check goes red on unresolved threads and green after resolving the last one via apull_request_review_threadevent<caller job id> / Blocking gaterequired in that repo's rulesetblockinginput, default offFixes BE-4691
🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.