mirror of
https://github.com/Comfy-Org/github-workflows.git
synced 2026-11-05 00:17:37 +00:00
[PR #109] [MERGED] feat(groom): bail_sink — suppress builder bail issues, and document what max_findings actually caps (BE-6157) #120
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#120
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/109
Author: @mattmillerai
Created: 8/2/2026
Status: ✅ Merged
Merged: 8/4/2026
Merged by: @mattmillerai
Base:
main← Head:matt/be-6157-groom-bail-sink📝 Commits (3)
df3b281feat(groom): bail_sink — suppress builder bail issues, and document what max_findings actually caps (BE-6157)d44030ffix(groom): sanitize the bail signature, exempt the secret-scan withhold from bail_sink=none (BE-6157)b3ee61eMerge remote-tracking branch 'origin/main' into matt/be-6157-groom-bail-sink📊 Changes
5 files changed (+354 additions, -18 deletions)
View changed files
📝
.github/groom/README.md(+36 -0)📝
.github/groom/config.py(+62 -0)📝
.github/groom/tests/test_config.py(+125 -0)📝
.github/workflows/groom.yml(+130 -17)📝
README.md(+1 -1)📄 Description
ELI-5
Groom has a knob called
max_findingsthat says it caps "the number of NEW issues opened in one run". Someone set it to0on a repo, expecting no issues — and got one anyway. The knob wasn't broken: it caps the findings thefilejob opens, while the auto-builder files a different kind of issue from a different job when a patch is too big to open as a PR. The setting did what it documented; it did not do what it implied. This PR says so out loud in the docs, and addsbail_sink— the knob that actually silences those builder bail issues, settable live fromGROOM_CONFIGwith no PR.What changed
1. Documentation (the trap itself).
max_findings' description ingroom.ymlnow names its scope (thefilejob's findings issues, after dedup) and explicitly disclaims builder bail issues, pointing atbail_sink. Mirrored in.github/groom/config.py's_OPERATIONAL_KEYScomment,.github/groom/README.md(new "Builder bail-outs" section) and the root README catalog row.2. New
bail_sinkinput, defaultissue— today's behavior, unchanged for every current caller.issue(default) — a bail files agroom-labeled issue, exactly as now.none— files nothing. Insteadbuild_premits::warning::bail_sink=none — NOT filing a bail issue for '<title>' (idx=N, signature=<sig>): <reason>and appends a run-summary line, so a suppressed bail is visible in the run, not invisible.linearis reserved for the siblingsink: linearticket and is not implemented here. It is refused by name, with its own warning, and falls back toissue.3. Placement:
_OPERATIONAL_KEYS, not_LOCKED_KEYS— per the ticket.bail_sinkcan only make groom quieter, never grant it privilege, so a repo can set it viavars.GROOM_CONFIGwithout a PR. That is the whole point: the operator who setmax_findings: 0can get real silence the same way.pr_size_limitstays locked (a reviewed commit in the caller) — the config.py docstring now spells out that suppressing the bail is not a reason to unlock raising the ceiling (ticket item 3).4. Single allowlist.
config.pyownsBAIL_SINKSand exportsnormalize_bail_sink, whichbuild_pr's inline Python imports rather than re-deriving the list. Everything not provablynone— empty, unknown,linear, a dropped-by-coercion value — resolves toissue. The fail-safe direction is deliberate: a redundant issue is recoverable, a silently discarded CONFIRMED finding is not.Verification
python3 -m unittest discover -s .github/groom/tests -p 'test_*.py'— 183 passed (was 170; +13 forbail_sink). Also ran the cursor-review and agents-md suites,shellcheckon bump-callers, andcheck_agents_md.py --root .— all green (the CODEOWNERS warning is pre-existing).actionlintclean ongroom.ymlandtest-groom-scripts.yml; the workflow parses and all six inline Python blocks compile.build_pr's bail branch is inline Python inside a YAMLrun:block, so it cannot be imported by the unit suite. I extracted it into a scratch harness with a fake decision/result and a stubghon PATH, and ran it four ways:BAIL_SINK=none→ warning + summary line, noghinvocation, exit 0; unset → filed the issue exactly as before, no summary line;linear→ filed (fail-safe);NONE→ suppressed (case-insensitive). A model-authored title containing a newline and a literal::error::was flattened onto one line, as intended.normalize_bail_sink's fail-safe defaulting, and — via a newTestBailSinkWiringclass that readsgroom.yml— the four wiring points a silent regression would live in (the input + itsissuedefault, the defaults-layer entry, theBAIL_SINK:env read, theif bail_sink == "none":branch and its warning).test-groom-scripts.yml's path filter now also watches.github/workflows/groom.ymlso a workflow-only edit that unhooks one of those actually runs the suite.Judgment calls / corrections worth reading
bail()helper in thebuildjob truncates/tmp/out/patch.diffbefore upload — deliberately, because the same helper carries the secret-scan withhold, where publishing the patch is the thing being prevented. Preserving the patch on the size/CI-privileged bails only would change artifact contents for current callers, against the "byte-identical" criterion. So the recovery path forbail_sink: noneis the::warning::(title + bail reason + dedup signature) and the run-summary line, not the patch bytes. The docs I wrote say exactly that; they do not claim the patch is recoverable.nonesuppresses every bail, including the prompt-injection secret-scan withhold. I kept it uniform rather than carving out an exception: a carve-out needs a machine-readable bail kind inresult.json, which is scope the ticket did not ask for, and the secret-scan path already prints its own::error::on the run page independently. Called out in the input description and both READMEs.nonemeans the finding is not recorded in the ledger, so a later run can re-propose and re-bail on it, re-spending builder tokens. Unavoidable without filing something — a durable record is what thelinearsink phase is for. Documented on the input so nobody discovers it from a bill.from config import normalize_bail_sinkcouplesbuild_prto theworkflows_refassets. A caller that pinsuses:to this SHA butworkflows_ref:to an older one gets an ImportError. This is a pre-existing coupling, not a new one — the same job already importsbuilder_pr_bodyfromledger.pyat that path — the bumper moves both pins together, and the import runs before anygit push, so it cannot orphan a branch.GITHUB_STEP_SUMMARYwrites inbuild_prwere folded into thesummarize()helper the new path needed. Behavior-identical (verified in the harness above); it avoids a third copy of the same four lines.🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.
enabled#319