mirror of
https://github.com/Comfy-Org/github-workflows.git
synced 2026-11-05 00:17:37 +00:00
[PR #166] [MERGED] feat(pr-derisk): /derisk split plan with grader-computed floors (BE-7380) #175
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#175
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/166
Author: @mattmillerai
Created: 8/14/2026
Status: ✅ Merged
Merged: 8/14/2026
Merged by: @mattmillerai
Base:
main← Head:matt/be-7380-pr-derisk📝 Commits (3)
5592eb3feat(pr-derisk):/derisksplit plan with grader-computed floors (BE-7380)59e438bfix(pr-derisk): compare against the PATH floor, anchor the commenter gate, bound the model phase (BE-7380)444fb6cfix(pr-derisk): dropgrep -qfrom the suite's has/no helpers — it raced pipefail (BE-7380)📊 Changes
15 files changed (+2547 additions, -10 deletions)
View changed files
📝
.github/bump-callers/README.md(+3 -2)➕
.github/workflows/bump-pr-derisk-callers.yml(+268 -0)➕
.github/workflows/pr-derisk.yml(+650 -0)➕
.github/workflows/test-pr-derisk.yml(+64 -0)📝
AGENTS.md(+21 -5)📝
README.md(+1 -0)📝
docs/callers/README.md(+2 -0)➕
docs/callers/pr-derisk.md(+104 -0)➕
scripts/pr-derisk/README.md(+117 -0)➕
scripts/pr-derisk/collect-pr-inputs.sh(+152 -0)➕
scripts/pr-derisk/plan-derisk.sh(+475 -0)➕
scripts/pr-derisk/publish-derisk-comment.sh(+284 -0)➕
scripts/pr-derisk/resolve-enabled.sh(+79 -0)➕
scripts/pr-derisk/tests/test_plan_derisk.sh(+305 -0)📝
scripts/pr-risk/README.md(+22 -3)📄 Description
ELI-5
The risk grader already tells you how risky a PR is and which files hold the grade up. It does not tell you what to do about it. This adds the next rung: you comment
/deriskon a pull request, and a bot replies with a concrete plan for splitting it into a chain of smaller PRs — one small one that carries all the risk, and the rest in cheap lanes. A model proposes which files go together; it is never allowed to say how risky anything is. Every risk tier in that comment is recomputed by the actual grader from the files the model assigned, so a model that hallucinates "this lands R0" cannot put that number in front of a reviewer. It is off everywhere by default, nothing is gated or filed, and the comment is the entire product.What this adds
.github/workflows/pr-derisk.yml— new reusable workflow,issue_comment-triggered. Agatejob (no token scopes) resolves the enablement switch and the commenter gate; aplanjob re-grades the PR, makes ONE model call, computes the split floors, and posts one sticky comment.enabled:defaults to false, andvars.DERISK_CONFIGoverrides it in both directions (kill switch with no PR), matching the pr-risk convention.scripts/pr-derisk/—collect-pr-inputs.sh(re-grade + fetch the diff, capped),plan-derisk.sh(the single model call, partition validation, floor computation),publish-derisk-comment.sh(the sticky renderer),resolve-enabled.sh, plus a README and a 42-assertion hermetic suite with the model layer stubbed viaMODEL_RESPONSE_FILE— no network, no API key..github/workflows/bump-pr-derisk-callers.yml— the fleet entrypoint over the sharedbump-callers.sh. It watchesscripts/pr-risk/**as well as its own tree, because a/deriskrun executes the pr-risk grader at the same pinned ref..github/workflows/test-pr-derisk.yml,docs/callers/pr-derisk.md, and the README / AGENTS.md / caller-index / bump-callers-README / pr-risk-README rows that keep the catalog in sync.The invariant this is built around
grade = worst(path_floor, provenance, reversibility)is deterministic, andgrade-pr-risk.sh --stdingrades synthetic records with no network. So each proposed step's path floor is computed by the actual judge over a synthetic scorecard record built from that step's real files, change types and line counts. The merge step rebuilds each step object field-by-field from a fixed list, so anytier/floor/riskkey the model emitted despite the instruction is dropped rather than merged. The headline verdict is arithmetic over those grader floors, never over the model's prose — which is what makes the single-class-monolith honesty rule ("N smaller single-concern R3s, same lane") a property of the renderer rather than a request in a prompt.Only
axes.path_floor.tieris read back, deliberately: the other two axes of a synthetic record would be values this script invented, and printing an invented number beside a computed one is exactly the model-claimed floor the design exists to prevent. The comment says so, and names provenance and the future check rollup as the unknowns — the same wording discipline the v0 readout (BE-7378) landed with.Security posture
issue_commentworkflows run from the caller's default branch, so a PR cannot edit the workflow that serves it. On top of that: the commenter is gated onauthor_association(defaultOWNER,MEMBER,COLLABORATOR— a property the API attributes, not a string the commenter writes); no PR code is ever checked out (the diff is read over the API and handed to a model as text); theworkflows_refshape+ancestry guard is byte-identical topr-risk.yml's and a test fails the build if the two drift; the only write in the whole run is one PR comment. Untrusted diff in, advisory comment out — and every PR-controlled string, the model's own output included, is markdown-escaped by the renderer (a filename can otherwise forge a table row).Defects fixed in review of the draft
Three things I found reviewing my own working tree before committing, each worth calling out because a reader would otherwise take the comment at its word:
WATCHED_ASSETSlisted onlyscripts/pr-deriskwhile thepaths:filter watchedscripts/pr-risktoo —test_paths_contract.shfailed on it. Both trees are now listed, so deleting either pathspec line is a build failure rather than a silent narrowing.WATCHED_EXEClistedapply-risk-label.shandpublish-risk-surfaces.sh, justified by a comment claiminggrade-targets.shhard-dies without them at source time. It does not — those checks live insidemain(), which itsBASH_SOURCE == $0footer runs only on a direct invocation, andcollect-pr-inputs.shonly sources it for two helpers. Left in, a pr-risk-side decommission of a file no/deriskrun touches would have frozen this fleet behind a false::warning::. Both entries removed and the comment corrected.collect-pr-inputs.shdefinedlog/warn/dieand then sourcedgrade-targets.sh, which redefineslog/dieat file scope — so every diagnostic after that point went out under a[grade-targets]prefix naming a script that did not emit it. They are re-declared after the source.Verification
shellcheck -x scripts/pr-derisk/*.sh scripts/pr-derisk/tests/*.shclean;bash scripts/pr-derisk/tests/test_plan_derisk.sh→ 42 passed, 0 failed (covers the floor computation end-to-end through the real grader, the exact-coverage rejection + single re-prompt, the deterministic fallbacks, the single-class-monolith verdict, markdown-forgery resistance via a crafted filename and step name, the comment-length bound, the sticky marker, and the enablement switch in all five degrade directions).bash .github/bump-callers/tests/test_paths_contract.sh→ 56/0;test_preflight.sh,scripts/pr-risk/tests/test_pin_contract.shandcheck_workflow_pins.pyall pass; all three new workflow files parse as YAML.Judgment calls, and what I could NOT exercise
ANTHROPIC_API_KEYsecret's visibility is unverified. The ticket's decision 1 explicitly asks to "confirm at implementation time that the secret's visibility covers Comfy-Org/github-workflows and any repo whose caller runs the model step". I have no access to org secret settings from this environment, so I did not confirm it. It is the one named artifact in the ticket I could not exercise, and it is why this saysRefs, notCloses— the enrolment half of the ticket is untested until someone with org-settings access checks it./deriskrun happened. There is no enrolled caller (by design — decision 3 pins ship-off-everywhere), so the end-to-end path has only been exercised hermetically with the model stubbed. The first real run is the pilot flip, not this PR.PR_DERISK_CALLERSis not set, andALLOW_EMPTY: "true"is deliberate for now. With no enrolled callers, an empty roster is the accurate state and a hard-fail would reddenmainon every commit to this rung. The workflow header says in as many words that this flips to"false"in the same change as the first enrolment — left at"true"past that point it degrades into exactly the silent pin drift the fleet exists to prevent.GITHUB_TOKEN, per the ticket's pinned default (no workflow cascade), andenabledships off everywhere per decision 3. Neither is revisited here.AGENTS.mdis 245 lines, over its own 200-line ceiling. It was already 229 onmainand this adds 16; the integrity checker is not enrolled as a caller on this repo, so nothing gates it. Trimming it back under 200 is a rewrite of sections this change does not touch, so I left it — flagging rather than quietly widening it.planjob's model step holds the API key and no GitHub token; the publish step holds the token and no key. That separation is intentional and worth keeping if this file is edited later.Refs BE-7380
🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.