Skip to content

feat(groom): reusable groom.yml — two-phase finder→verifier, GitHub-issue sink (BE-3872)#49

Merged
mattmillerai merged 2 commits into
mainfrom
matt/be-3872-groom-reusable-workflow
Jul 21, 2026
Merged

feat(groom): reusable groom.yml — two-phase finder→verifier, GitHub-issue sink (BE-3872)#49
mattmillerai merged 2 commits into
mainfrom
matt/be-3872-groom-reusable-workflow

Conversation

@mattmillerai

Copy link
Copy Markdown
Contributor

ELI-5

We already have a robot that reviews pull requests (cursor-review.yml). This adds a second robot, groom, that goes the other way: on a schedule it reads a whole repo (not a diff) and looks for cleanups worth doing — duplicated code, dead code, inconsistent patterns. One agent proposes candidates, a second, independent agent plays skeptic and confirms/rejects each, and the survivors are filed as GitHub issues (labeled groom) for humans to pick up. It never writes code, opens a PR, or merges anything — it only files issues. A durable ledger (built in Phase 1) remembers what it already filed or what a human rejected, so it never nags you twice.

What this does

Adds .github/workflows/groom.yml as a workflow_call reusable, cloning the cursor-review topology and reusing the Phase-1 briefs + ledger under .github/groom/ as the single source of truth:

  • gate job — validates sink and applies the volume gate (skip a repo with no merges in cadence days; fail-open).
  • audit job (contents: read only) — checks out a clean default-branch copy, substitutes the {{…}} placeholders in finder.md/verifier.md, runs the finder then a fresh independent verifier via anthropics/claude-code-action, and uploads the verified findings. This job holds no write credentials — it is structurally incapable of writing to GitHub.
  • file job — dedups survivors (CONFIRM/DOWNGRADE only) against the durable ledger and opens groom-labeled GitHub issues as the configured bot (Comfy: cloud-code-bot via bot_app_id + BOT_APP_PRIVATE_KEY), embedding the ledger's signature marker so the next run won't re-file. Security-adjacent findings get groom-security (investigate, don't auto-implement).

Triggers (schedule + workflow_dispatch, never on-PR) and the required concurrency: group live in the caller (documented in the header + README), matching how cursor-review's triggers live in its caller. A workflow-level concurrency group also serializes runs per target repo as a backstop against the ledger's TOCTOU race.

Inputs

themes, cadence, volume_gate, sink (default github), max_findings, scope_label/scope_desc, model, workflows_ref, bot_app_id, dry_run. Secrets: ANTHROPIC_API_KEY (required), BOT_APP_PRIVATE_KEY (when bot_app_id set).

Acceptance criteria

  • groom.yml reusable exists; files verified findings as GitHub issues, acted as cloud-code-bot, no credentials in the agent step (the audit job is contents: read-only and the agent gets only ANTHROPIC_API_KEY).
  • ✅ Dedup hook consults the rejection-memory store (.github/groom/ledger.py) before filing, reusing its exact signature marker.
  • ✅ SHA-pinnable like the other reusables; documented in README + AGENTS.md. Third-party actions are SHA-pinned per the AGENTS.md convention.
  • Dry-run parity: the placeholder substitution reproduces the studio supervisor-groom.sh briefs verbatim (same .github/groom/*.md), and dry_run prints exactly what it would file for a spot-check. Live parity against a studio run on a test repo is the remaining manual verification step — see below.
  • ⚠️ Linear agent-ok sink is deferred (needs org LINEAR_API_KEY), per the ticket — sink: linear fails the run loudly rather than silently filing nothing.

Verification done

  • actionlint clean on groom.yml.
  • .github/groom ledger tests: 37 passing (dep unchanged).
  • agents-md-integrity checker passes (1 pre-existing CODEOWNERS warning, unrelated).
  • Local smoke tests: placeholder substitution leaves no leftover tokens in either brief; the signature_marker reuse round-trips; the filing logic verified for dry-run, live-command shape, max_findings capping (with deferral warning), invalid-signature skip, and groom-security routing.
  • Not runnable from here: a live workflow_dispatch on a test repo requires the org ANTHROPIC_API_KEY + cloud-code-bot install on that repo — that end-to-end run (and the studio-parity spot-check) is the reviewer/operator step before moving the v1 tag.

Judgment calls (flagged)

  • themes default: I could not read epic BE-3870, so I set the default to the finder brief's own five dimensions verbatim — a no-op that preserves studio parity by default while letting a caller narrow it. If the epic's "safe starter set" is a specific narrower list, adjust the default.
  • Bot filing: modeled on cursor-review's bot_app_id/BOT_APP_PRIVATE_KEY (consumer supplies its own App). Comfy uses cloud-code-bot.
  • Negative-claim (sink != github) path: this is a not-yet-built feature with a clear error, not a regression removing an existing capability — there is no existing linear-sink path in this new workflow to redirect to, and the ticket explicitly defers Linear. So it ships as a loud guard.
  • Checkout ref: the audit checks out the triggering ref (the default branch on schedule); a workflow_dispatch from a non-main branch would groom that branch. Acceptable and arguably desirable; noted for awareness.

…ssue sink (BE-3872)

Build the split-C groomer as a workflow_call reusable, cloning the
cursor-review topology: a read-only FINDER agent scans a clean
default-branch checkout (whole-repo, not a diff) for high-value
refactors; an INDEPENDENT VERIFIER (fresh session) re-checks each as
CONFIRM/DOWNGRADE/REJECT with a stable dedup signature; survivors are
deduped against the durable GitHub-issue-state ledger and filed as
`groom`-labeled GitHub issues (security-adjacent → `groom-security`,
investigate-only).

Finds only — no commits, no PRs, never merges. Briefs + ledger under
.github/groom/ are the single source of truth (Phase 1). The agent step
holds no write credentials: the `audit` job is contents:read only, so
filing runs in a separate `file` job as the configured bot
(cloud-code-bot), per model-gap-detector.yml. dry_run reports intended
issues without opening them (parity spot-checks). Third-party actions
SHA-pinned per AGENTS.md; documented in README + AGENTS.md.
@mattmillerai mattmillerai added agent-coded Authored by the agent-work loop cursor-review Multi-model cursor review labels Jul 21, 2026
@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 52 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 22ccc6bd-9bec-4405-9486-3123c2586991

📥 Commits

Reviewing files that changed from the base of the PR and between eda264b and 447941d.

📒 Files selected for processing (3)
  • .github/workflows/groom.yml
  • AGENTS.md
  • README.md
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch matt/be-3872-groom-reusable-workflow
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch matt/be-3872-groom-reusable-workflow

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 Cursor Review — Consolidated panel

Triggered by @mattmillerai.

Found 10 finding(s).

Severity Count
🔴 Critical 1
🟠 High 2
🟡 Medium 6
🟢 Low 1

Panel: 8/8 reviewers contributed findings.

Comment thread .github/workflows/groom.yml Outdated
Comment thread .github/workflows/groom.yml
Comment thread .github/workflows/groom.yml Outdated
Comment thread .github/workflows/groom.yml Outdated
Comment thread .github/workflows/groom.yml
Comment thread .github/workflows/groom.yml
Comment thread .github/workflows/groom.yml Outdated
Comment thread .github/workflows/groom.yml
Comment thread .github/workflows/groom.yml Outdated
Comment thread .github/workflows/groom.yml Outdated
…n allowlist + filing (BE-3872)

Resolves the review-panel findings on the reusable groom workflow:

- Security: drop Bash(sed|awk|find|rg) from both agent allowlists — each can
  shell out (awk BEGIN{system}, find -exec, sed s///e, rg --pre), which would
  let a prompt-injected finder exfiltrate ANTHROPIC_API_KEY. Agents keep
  Read/Glob/Grep + non-executing git/cat/ls/head/tail/wc.
- Independence: split the single audit job into separate audit_find and
  audit_verify jobs. The verifier now runs on its OWN fresh checkout with the
  finder's JSON passed as an artifact, so the finder's Write can no longer
  tamper with the code the verifier reads or the brief it follows.
- Filing: track per-issue failures and exit non-zero so a filing outage
  surfaces instead of a silent green check; also catch subprocess.TimeoutExpired
  (does not inherit from CalledProcessError) as a per-issue failure.
- Robustness: guard `body: null` (.strip on None) and missing signature; parse
  max_findings via float+floor and reject negatives (a negative cap sliced from
  the end); normalize the security flag so string "false"/"0" isn't truthy.
- Contract: security/auth findings are always kept regardless of theme (the
  appended themes instruction now says so); document that the github.token
  filing fallback needs the caller to grant issues:write (a reusable workflow
  can't elevate the caller's token).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@mattmillerai
mattmillerai merged commit 07154fb into main Jul 21, 2026
3 checks passed
@mattmillerai
mattmillerai deleted the matt/be-3872-groom-reusable-workflow branch July 21, 2026 19:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agent-coded Authored by the agent-work loop cursor-review Multi-model cursor review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant