mirror of
https://github.com/Comfy-Org/github-workflows.git
synced 2026-11-05 00:17:37 +00:00
[PR #65] [MERGED] fix(groom): run the three agent phases via the Claude CLI directly (BE-4214) #74
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#74
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/65
Author: @mattmillerai
Created: 7/23/2026
Status: ✅ Merged
Merged: 7/24/2026
Merged by: @mattmillerai
Base:
main← Head:matt/be-4214-groom-claude-cli📝 Commits (5)
70098aefix(groom): run the three agent phases via the Claude CLI directly (BE-4214)d938c34fix(groom): harden the direct-CLI agent steps from the review panel (BE-4214)ada848bfix(groom): close the agent-write escalation paths from round 2 (BE-4214)9b63c5efix(groom): close the round-3 review findings on the direct-CLI agent steps (BE-4214)eab3fa1fix(groom): scope the agent write tools to close the runner-filesystem write class (BE-4214)📊 Changes
3 files changed (+623 additions, -59 deletions)
View changed files
📝
.github/workflows/ci-groom.yml(+9 -4)📝
.github/workflows/groom.yml(+613 -54)📝
README.md(+1 -1)📄 Description
ELI-5
Groom's three AI phases (finder, verifier, builder) were being run from the
wrong folder. The repo they're supposed to read gets checked out into a
repo/subfolder, but the agent was started one level above it — and up theregit logdoesn't work at all, because that spot isn't a git repo. Every gitcommand the agent was allowed to run therefore failed, and it burned through
its whole 40-turn budget flailing without ever writing its results file.
We couldn't just tell the old tool "start in
repo/" — the action we wereusing has no setting for that. So this PR runs the Claude CLI directly instead,
started inside the clone, with the exact same restricted tool allowlist. It
also raises the turn budget to what a real run actually needs, and saves the
agent's execution log as a downloadable artifact so next time something goes
wrong you can just read it off the run page.
What changed
Replaces
anthropics/claude-code-actionwith a directclaude -pinvocation inaudit_find→ "Run finder",audit_verify→ "Run verifier", andbuild→"Run builder".
working-directory: ${{ env.GROOM_CLONE }}— the actual fix. cwd is nowthe clone, so
Bash(git log:*)/Bash(git grep:*)etc. resolve. Theallowlists, the briefs (
.github/groom/*.md), the prompt-build steps,GROOM_CLONE, therepo/checkout layout,FINDER_OUT/VERIFIER_OUT/BUILDER_OUT, and all assert/validate/capture logic are unchanged.healthy finder run measured ~82 turns ≈ 12.3 min; 150 turns ≈ 22 min fits the
40-min job timeout, and the builder's 100 ≈ 15 min fits its 30-min timeout.
npm install -g @anthropic-ai/claude-code@2.1.217, once peragent job. Pinned (not floating) because the CLI is executable supply chain
for a step that reads untrusted repo content.
groom-{finder,verifier,builder}-exec, uploadedbefore each assert/validate/capture gate so they survive a starved run.
id-token: writedropped from the three agent jobs, the header comment,the
ci-groom.ymlcaller, and the README. Each agent job is nowcontents: readonly — a security improvement: the direct CLI mints noGitHub token into the agent env at all.
--setting-sources "" --strict-mcp-config— security-critical now thatcwd is the untrusted clone: without them the CLI would load the target
repo's
.claude/settings.json(hooks = arbitrary shell, outside thepermission engine) and its
.mcp.json.inputs.modelrouted throughenv:instead of interpolated into the runscript — see "judgment calls" below.
The
|| echo "::warning::…"on each invocation is deliberate:claude -pexitsnon-zero on
error_max_turnsand friends, and the existing assert/validate/capture steps are the intended gates (matching current behavior, where the
action step always "succeeded" and the assert step decided).
Verification
Local (done):
actionlintclean across all workflows.test-groom-scripts.ymlis path-filtered to.github/groom/**, untouched here — ran it anyway.) cursor-review (40) andagents-md-integrity (18) suites also green; AGENTS.md integrity check passes.
groom.yml; exactly 3npm install -g @anthropic-ai/claude-code@pins; noid-tokengrant anywhere.Premise falsification (rather than trusting the ticket):
action.ymlat the pinned SHA
b76a0776; its full input list has noworking-directory/cwd/ repo-path input (onlypath_to_claude_code_executableandpath_to_bun_executable, both unrelated).--max-turnsis not inthat version's
--helpoutput, so I tested it rather than assuming: ran theexact flag set against CLI 2.1.217 with a deliberately invalid API key. All
flags parsed (no "unknown option"), execution reached the API and returned a
401 — and the emitted result JSON contained
subtype,num_turns, andpermission_denials, confirming the artifact carries the promiseddiagnostics.
--setting-sources ""is accepted (help documents it as acomma-separated list of
user,project,local; empty ⇒ none loaded).Not done — needs a maintainer (the one remaining acceptance criterion):
E2E
workflow_dispatchgroom againstComfy-Org/comfy-cloud-mcp-serverwiththat caller's
uses:pointed at this branch. The pilot caller pins the reusableby ref, so I can't trigger it from here. Expected: "Run finder" completes in
~10–15 min, "Assert finder produced JSON" passes with a non-zero candidate
count, the
groom-finder-execartifact is present, verifier proceeds.Judgment calls
2.1.217, not the current-latest2.1.218. The ticket says both"pin the version current at implementation time" and "2.1.217 is what the fix
was validated on". I took the validated one — 2.1.218 shipped without being
exercised against this recipe, and this is the version I verified the flags
against above.
inputs.modelmoved intoenv:(MODEL: ${{ inputs.model }}+--model "$MODEL") rather than the ticket's literal--model "${{ inputs.model }}". Direct interpolation puts a caller-suppliedstring into a shell command; every other input in this workflow (
SINK,CADENCE,THEMES,PR_SIZE_LIMIT, …) already reachesrun:viaenv:, sothe literal version would have been the only script-interpolation in the file.
ci-groom.yml'suses:SHA pin is deliberately left alone. Dropping thecaller's
id-token: writeis safe in every merge ordering: the reusable atthe currently-pinned SHA (
07154fb) declares noid-tokenat all, so thereduced grant cannot trip GitHub's startup validation, and that version's
agent job declares
contents: readonly — so no OIDC token was reachablethere regardless of the caller's grant. Bumping the pin stays a separate step.
Notes
opus turns ≈ $7.5/repo, versus ~$2.80 for the previous truncated-and-useless
runs. Existing
timeout-minutes(40/40/30) already cover the new caps.it restores the git capability the agents were supposed to have, so the
negative-claim falsification rule doesn't apply. The premise-falsification
work above was done anyway, since the whole rewrite rests on two claims.
id-token: writeremoval: it was added by #64(BE-3873) explicitly and only because
claude-code-actionmints its GitHubtoken via OIDC. That action is gone here, so the reason is gone with it.
🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.