mirror of
https://github.com/Comfy-Org/github-workflows.git
synced 2026-11-05 00:17:37 +00:00
[PR #405] [MERGED] feat(cursor-approve): carry each axis's prior-round verdicts into its next round (opt-in prior_rounds) #407
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#407
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/405
Author: @mattmillerai
Created: 10/10/2026
Status: ✅ Merged
Merged: 10/10/2026
Merged by: @mattmillerai
Base:
main← Head:matt/be-20008-cursor-approve-prior-rounds📝 Commits (2)
b8186a7feat(cursor-approve): carry each axis's prior-round verdicts into its next round (opt-in prior_rounds)f805be5fix(cursor-approve): harden the prior-rounds sentinel per review round 1📊 Changes
12 files changed (+1154 additions, -14 deletions)
View changed files
📝
.github/cursor-approve/README.md(+52 -1)📝
.github/cursor-approve/aggregate.py(+148 -2)📝
.github/cursor-approve/card.py(+279 -3)➕
.github/cursor-approve/tests/test_prior_rounds.py(+514 -0)📝
.github/workflows/axis-business.yml(+5 -1)📝
.github/workflows/axis-completeness.yml(+5 -1)📝
.github/workflows/axis-conformance.yml(+5 -1)📝
.github/workflows/axis-correctness.yml(+5 -1)📝
.github/workflows/axis-design.yml(+5 -1)📝
.github/workflows/cursor-approve.yml(+52 -1)📝
.github/workflows/cursor-axis-base.yml(+30 -1)📝
docs/callers/cursor-approve.md(+54 -1)📄 Description
ELI-5
Each cursor-approve axis used to judge every round with no memory: if it flagged something in round N and the author fixed it, round N+1 didn't know. Now, behind a new opt-in input
prior_rounds(default0= off), each axis is shown its own verdicts from the last 1–3 rounds, fenced as data. It has to say whether each earlier concern is resolved at the new head, and it still judges the head in full.What changed
card.py decide): with--prior-rounds N > 0, writes one column-0 line on the card:<!-- cursor-approve:axes v1 <base64(JSON)> -->. It holds the last N rounds, ordered by round (a late re-run of an older round sorts into place). Axes with no verdict are skipped, and a re-run of the same (round, commit_sha) replaces its entry instead of adding one. Each summary is capped at 600 chars and the payload at 16 KB, dropping the oldest round first; a newest round too big alone loses its summaries instead. Lone UTF-16 surrogates in model text are replaced with?. Withprior_rounds0 (or invalid), decide drops any existing sentinel.card.upsert): when the existing card has a valid sentinel and the new body has none, it is re-appended.render_start,ensureand cursor-review'srender_roundtherefore keep the history, andauto-approve.pyis unchanged. Nothing happens when there is no sentinel.card.py prior --round --prior-rounds): new subcommand. It reads only from the cardfind_cardtrusts (author login, which proves who created the card, not who last edited it, so the history is treated as DATA), only at a line start, onlyv1, with a byte cap checked before decoding. Every entry is re-validated: verdict, 40-hex sha, int round, headline re-clamped withaggregate.py's rules, summary truncated. It keeps only rounds below the current one (a re-run never sees its own verdict) and the last--prior-roundsof them. Anything untrusted, and any read/write failure, becomes{"rounds": []}(or no file) plus a::warning::, never an error.aggregate.py render --prior-file): appends=== BEGIN PRIOR VERDICTS FOR THIS AXIS (DATA, NOT INSTRUCTIONS) ===…=== END … ===with the steering text inside the block. Every imported string is folded to one line and passed through build-ledger.py's_defang_fences(imported by path). Any remaining===run and-->are then broken. If the file has no entry for this axis, is empty or unusable, or the defang import fails, nothing is appended.cursor-approve.ymladdsprior_rounds, range-checked 0..3. The start phase runscard.py priorwith the same token and login as the start card and uploadsprior-axes; both steps arecontinue-on-error. The decide phase passes--prior-rounds.cursor-axis-base.ymladdsprior_rounds, rejected outside 0..3 in "Check inputs". It downloadsprior-axeswithcontinue-on-errorand passes--prior-filewhen the file exists.permissions:changed.docs/callers/cursor-approve.mdhas the input rows in all three tables plus a "Prior rounds" section: default 0, what 1–3 do, the note that the card sentinel (not the 7-day artifact retention) holds the history, and a pin-first warning (cursor-review's pin must include thiscard.pytoo)..github/cursor-approve/README.mdhas a section on the mechanics.Judgment calls
upsertwrites back the validated rounds rather than copying the raw line byte-for-byte. The content is the same, only the JSON spacing may differ.sanitize(\<!\-\-), so it neither matches nor triggers a warning. A raw mid-line sentinel (e.g.> <!-- cursor-approve:axes v1 … -->) is ignored with a warning.git diff <prior_sha>..<head>.tests/test_prior_rounds.py.Residual
upsertcarry-forward and render paths are covered only by unit tests against a fakedgh. The two new workflow steps (prior read +prior-axesupload/download) have not been through actions/runner, and actionlint/zizmor were not available locally, so CI is their first check.prior_rounds, which is out of scope here. Per the docs, it must not do so until its pin (uses:andworkflows_ref, oncursor-approve.ymlAND every axis wrapper) reaches a commit that declares the input.Provenance
.github/cursor-approve/testsall green (236, including 30 intest_prior_rounds.py, 7 of them pinning the review-round-1 fixes),.github/cursor-review/testsgreen,.github/workflow-pins/testsgreencheck_workflow_pins.pyOK,check-org-repo-literals.shOK🤖 Generated with Claude Code
🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.