mirror of
https://github.com/Comfy-Org/github-workflows.git
synced 2026-11-05 00:17:37 +00:00
[PR #431] [MERGED] feat(cursor-review): probe the direct Anthropic key and fall back to Cursor when it is capped #432
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#432
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/431
Author: @mattmillerai
Created: 10/15/2026
Status: ✅ Merged
Merged: 10/15/2026
Merged by: @mattmillerai
Base:
main← Head:matt/be-20888-anthropic-direct-probe📝 Commits (4)
51386e5feat(cursor-review): probe the direct Anthropic key before a round and fall back to Cursor when it is cappedce1a871fix(cursor-review): name each fallen lane's own reason in the fallback note886c548fix(cursor-review): harden the Anthropic key probe per the panel's review round6649e7ffix(cursor-review): publish only the probe's reason class, never the API's raw message📊 Changes
10 files changed (+994 additions, -38 deletions)
View changed files
📝
.github/cursor-approve/tests/test_axis_direct.py(+25 -9)📝
.github/cursor-review/README.md(+3 -3)📝
.github/cursor-review/post-review.py(+12 -0)➕
.github/cursor-review/tests/test_anthropic_probe.py(+466 -0)📝
.github/cursor-review/tests/test_auto_approve.py(+1 -1)📝
.github/cursor-review/tests/test_post_review.py(+10 -0)📝
.github/workflows/cursor-axis-base.yml(+241 -12)📝
.github/workflows/cursor-review.yml(+229 -6)📝
docs/callers/cursor-approve.md(+4 -4)📝
docs/callers/cursor-review.md(+3 -3)📄 Description
ELI-5
The review panel, its judge and two approval axes can talk to Anthropic directly with an API key. When that key hits its organization's monthly spend limit, every direct call fails, the round ends with no decision, and a human had to delete repo variables to put everything back on Cursor. Now each round first makes one tiny (1-token) call with the key. If Anthropic refuses it for a usage limit, billing, a bad key, or keeps saying "overloaded" after retries, that run quietly uses the Cursor lanes instead, and says so loudly in the run. A typo'd model id still fails the run.
What changed
cursor-review.yml›gate: new stepProbe the direct Anthropic key(GitHub-hosted, checks out nothing, runs only for a round that will actually run and only when the key is present). OnePOST /v1/messageswithmax_tokens: 1per distinct configured model (anthropic_direct_model,judge_direct_model). Classification: 2xx → usable; 400 "usage limit" / billing error / credit balance / 402 → unusable; 401/403 → unusable; 408/429/5xx/529/no answer (or any exception) → retried after 15 s and 45 s, within a wall-clockPROBE_BUDGET_SECONDS, then unusable; 404 /not_found_error→ invalid, except a dash-free CLI model alias (opus,sonnet), which is not probed; any other refusal (an unrecognised 4xx, or a redirect, which is never followed with the key) → unusable. Never prints the key, request headers or response headers, and never publishes the API's raw message (it can name the organization): only the reason class, plus a usage limit's parsed reset time. All outputs are written once, at the end, andcontinue-on-errorapplies: if the probe itself crashes or times out, the outputs stay empty and preflight behaves exactly as today.preflight›Define panel models: anunusablelane is handled exactly like an absent key. The direct cells stay off and the Cursorclaude-*cells stay in the panel; the Cursor judge runs. Each lane gets one::warning::with the reason class (and reset time), plus one job-summary line. If the panel list has no Cursorclaude-*id, the warning, summary and posted note say no Anthropic review cell ran.invalid→::error::+ exit 1 (no fallback on a typo), checked after the openai/anthropic id-collision warning.post-review.py: new optional--runner-note, rendered as an italic banner under the review header (same as--ledger-note). The post step passesgate'santhropic_fallback_note, so a round whose direct lanes fell back says so on the PR.cursor-axis-base.yml: newaxis-probejob (GitHub-hosted,contents: read, no checkout, nouses:) ahead of the two runners. It first runs the checks a fallback must not skip (checkout axis,checkout: true, plain id, nocontext_sources, key present), then the byte-identical probe script (a test pins the parity), thenPick the runner→runner=direct|cursor.axisruns when the input is empty or the probe picked cursor;axis-directruns only when it picked direct. Unknown model → the probe job fails, so neither runner runs and the axis has no verdict, same as today.docs/callers/cursor-review.md,docs/callers/cursor-approve.md,.github/cursor-review/README.mdand the workflow input/secret descriptions now say that a capped or unusable key falls back to Cursor for that run, and how to see that in the run.No new inputs or secrets, so today's caller files work unchanged.
Judgment calls
axis-probe; the old check inaxis-directstays as a backstop.not_found_errorcounts as a config error; a 400's wording is never guessed at ("invalid max_tokens for this model" must not fail a valid config). Any other 4xx is treated as unusable so that it can't take the approver down.cursor-axis-base.yml. That job is GitHub-hosted, checks out nothing, uses no actions and never runs onruns_on. The confinement test now asserts exactly that.How has this been tested?
python3 -m unittest discover -s .github/cursor-review/tests -p 'test_*.py': all OK. Newtest_anthropic_probe.pyruns the real gate step against a local HTTP stub that replays recorded responses: 200, the usage-limit 400 (reset time kept), the billing 400, 401, 429 then 200 (2 calls, direct kept), 529 on every attempt (3 calls, unusable), 404 (invalid), a 404 on an alias (not probed), any 400 including model-worded ones (unusable, never invalid), a 302 (refused, target never contacted), a truncated error body (retried, no crash), the retry budget, a key or org id in the error message (never published), one model shared by both lanes (1 call), a non-plain id (not probed). It also runs preflight's realDefine panel models: unusable → Cursor claude cells and Cursor judge, 2 warnings, 2 summary lines; unusable with noclaude-*in the panel → says no Anthropic cell ran; usable or not probed → unchanged; invalid → exit 1, but the id-collision warning first. It runs the axisPick the runnerandCheck the direct inputssteps, checks gate/axis script parity, and checks the post step's--runner-notewiring.test_post_review.pyadds a test that renders the runner note.python3 -m unittest discover -s .github/cursor-approve/tests -p 'test_*.py': 328 OK (test_axis_direct.pyupdated for the three-job layout and the key confinement)..github/workflow-pinssuite 551 OK;check_workflow_pins.pyOK;check-org-repo-literals.shOK;agents-md-integrityandpublic-repo-hygienesuites OK; both workflows parse as YAML.axis_direct_model,anthropic_direct_model,judge_direct_model,ANTHROPIC_API_KEY, "no fallback"/"never falls back"), 9 updated. The remaining ~1,700 lines it found are matches on common words (content,text,body,bash, …) and were not reviewed one by one.Residual
max_tokens: 1for some future model with a 400, that round falls back to Cursor rather than running direct. Watch the first real round after the direct variables are restored.::warning::, the job summaries, and the check that ran (axis: <axis>instead ofaxis: <axis> (direct)). Adding a runner field to the verdict artifact/card is a separate change.claude-*id gets no Anthropic cell on a fallback. That is the same panel an absent key gives; this PR only makes it say so. Adding a Cursor fallback model is a separate design call.Provenance
🤖 Generated with Claude Code
🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.