feat(groom): configurable run cadence via GROOM_INTERVAL_DAYS interval gate (BE-4004)#56
feat(groom): configurable run cadence via GROOM_INTERVAL_DAYS interval gate (BE-4004)#56mattmillerai wants to merge 3 commits into
Conversation
…l gate (BE-4004) GitHub Actions `schedule:` cron is static in the workflow file — there is no native runtime "every N days". Replace groom's hardcoded weekly cron with the standard frequent-base-cron + runtime-gate pattern so cadence is a live knob: - ci-groom.yml now fires DAILY; the effective cadence is a new runtime gate. - New reusable input `interval_days` (default 7 = today's weekly behavior), wired in the caller to repo Actions variable GROOM_INTERVAL_DAYS. Changing the variable retunes cadence (weekly -> every-3-days -> daily) with no file edit. - `.github/groom/interval.py`: the gate. At run start it derives the last REAL groom run from this caller's Actions run history (a run counts only if its finder job actually ran, so interval-skip ticks never reset the clock) and early-exits cheaply — before the finder — unless interval_days have elapsed. Durable across stateless CI runs, no net-new secret, only `actions: read`. Pure decision logic split from the gh I/O shell + a unittest suite, mirroring ledger.py. - `workflow_dispatch` always bypasses the interval gate (manual override). - Fail-open on any history-read error (mirrors the volume gate). The gate runs in the existing `gate` job before the finder; the volume gate is kept as a second throttle with its window matched to the effective cadence.
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in: 26 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the 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. 📝 WalkthroughWalkthroughThe groom workflows now run on a daily base schedule and apply a configurable runtime interval gate. The gate inspects audited workflow history, supports manual-dispatch bypasses, fails open on errors, and is integrated with the existing volume gate. ChangesGroom cadence control
Sequence Diagram(s)sequenceDiagram
participant GroomWorkflow
participant interval.py
participant GitHubActions
participant VolumeGate
GroomWorkflow->>interval.py: Evaluate interval gate
interval.py->>GitHubActions: Read workflow runs and finder jobs
GitHubActions-->>interval.py: Return run history
interval.py-->>GroomWorkflow: Return interval result
GroomWorkflow->>VolumeGate: Evaluate merged-PR volume when interval allows
VolumeGate-->>GroomWorkflow: Return volume result
GroomWorkflow-->>GroomWorkflow: Set should_run from both gate results
🚥 Pre-merge checks | ✅ 2✅ Passed checks (2 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
✨ Simplify code
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@AGENTS.md`:
- Around line 55-59: Update the interval.py description in AGENTS.md to state
that the cadence gate derives its state from GitHub Actions run history, rather
than GitHub issue and PR state. Keep the issue/PR storage description associated
only with the ledger behavior and preserve the existing GROOM_INTERVAL_DAYS
context.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 679677d0-a97e-48ad-837f-ec1ed95ed544
📒 Files selected for processing (7)
.github/groom/README.md.github/groom/interval.py.github/groom/tests/test_interval.py.github/workflows/ci-groom.yml.github/workflows/groom.ymlAGENTS.mdREADME.md
The cadence gate derives state from Actions run history, not GitHub issue+PR state (that's the ledger's store); drop the misplaced sentence. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ELI-5
Groom used to run on a hardcoded weekly cron — to change how often it runs you had to edit the workflow file. GitHub Actions crons can't be "every N days" at runtime, so this switches groom to the standard trick: fire a cheap tick every day, but only actually do work every
Ndays, whereNis a live knob you set with a repo variable (GROOM_INTERVAL_DAYS, default7= today's weekly). A tick that's too soon exits in a few seconds before the expensive finder ever starts. Change the variable, cadence changes — no PR, no YAML edit.The interval gate lives in the new
groom.yml, soci-groom.ymlpins a placeholder (@REPLACE_AT_MERGE_WITH_THIS_PRS_SQUASH_SHA) that fails loud (unresolvable ref / failed assets checkout) until bumped. This is the same reusable-lands-then-bump-caller two-step the pilot used (#49 → #53), compressed into one PR. After squash-merge, bump bothuses:andworkflows_refin.github/workflows/ci-groom.ymlto this PR's merge SHA. groom is schedule/dispatch-only (never on-PR), so nothing breaks on the PR itself; the placeholder just guarantees the daily cron can't silently run against the old (gate-less) reusable.(No
bump-groom-callers.ymlautomation exists yet — groom has a single caller. A dedicated bumper is a reasonable follow-up if more repos enroll.)What changed
ci-groom.yml(caller): weekly cron → daily17 9 * * *; grantsactions: read; wiresinterval_days+cadenceto${{ vars.GROOM_INTERVAL_DAYS || '7' }}.groom.yml(reusable): newinterval_daysinput (default 7); thegatejob now checks out the assets, runs the interval gate before the finder, then the (unchanged) volume gate, composing both intoshould_run. Gate job gainsactions: read..github/groom/interval.py+ tests: the gate. Derives the last real groom run from this caller's Actions run history — a run counts only if itsAudit — finderjob actually ran, so interval-skip ticks never reset the clock — and early-exits unlessinterval_dayselapsed. Pure decision logic split from theghI/O shell, mirroringledger.py. Fail-open on any history-read error..github/groom/README.md(new section), AGENTS.md.Acceptance criteria
GROOM_INTERVAL_DAYS(default 7); changing it changes frequency with no workflow-file edit.gatejob, before the finder).workflow_dispatchalways runs regardless of the interval.actions: read).Judgment calls (worth a reviewer's eye)
GROOM_LAST_RUNrepo variable. The ticket sanctioned either. A repo variable needs aVariables: writecredential the run doesn't otherwise carry — and a missing grant would fail silently into a daily over-spend. Run history is durable, needs onlyactions: read, and has no silent-failure trap.inputwired to the var (rather than the reusable readingvars.GROOM_INTERVAL_DAYSdirectly). The caller evaluates the var in its own context — unambiguous — matching howcadence/dry_run/volume_gateare already wired. Same "no file edit" ergonomics.workflow_dispatchbypasses the interval gate but not the pre-existing volume gate for a non-dry-run dispatch. That volume-gate-on-dispatch behavior is a deliberate spend guard the pilot chose (volume_gate: dry_run != 'true'); the ticket's "always runs regardless of the interval" is about the interval gate, so I left the volume gate alone (Chesterton's fence). A dry-run dispatch bypasses both, as before.github.workflow_refresolves to the caller (ci-groom.yml), not the reusable — the OIDCworkflow_ref(caller) vsjob_workflow_ref(reusable) distinction. If that assumption were ever wrong, the run-history query returns nothing → the gate fails open (runs), never crashes.interval.py.Testing
python3 -m unittest discover -s .github/groom/tests→ 54 passed (16 new for the interval gate: within/at/after-interval, skip-ticks-don't-reset, dispatch-always-runs, no-history/API-error fail-open, job-name matching).actionlint(with shellcheck on the embeddedrun:blocks) clean on both workflows.