mirror of
https://github.com/Comfy-Org/github-workflows.git
synced 2026-11-05 00:17:37 +00:00
[PR #56] [MERGED] feat(groom): configurable run cadence via GROOM_INTERVAL_DAYS interval gate (BE-4004) #70
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#70
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/56
Author: @mattmillerai
Created: 7/22/2026
Status: ✅ Merged
Merged: 7/27/2026
Merged by: @mattmillerai
Base:
main← Head:matt/be-4004-groom-interval-days📝 Commits (10+)
dd2ac0efeat(groom): configurable run cadence via GROOM_INTERVAL_DAYS interval gate (BE-4004)bfb6a5eMerge branch 'main' into matt/be-4004-groom-interval-days8eb8e0edocs(AGENTS): fix interval.py state description per CodeRabbitacf6445Merge remote-tracking branch 'origin/main' into matt/be-4004-groom-interval-days160fdeafix(groom): normalize the volume-gate window through interval.py (BE-4004)5195cbeMerge branch 'main' into matt/be-4004-groom-interval-days7ec610bfix(groom): absorb cron jitter in the interval gate; string cadence inputs (BE-4004)55edfa6fix(groom): reject non-finite cadence, grant gate issues/PR read, fix comment (BE-4004)2297266Merge main into be-4004-groom-interval-days507669cdocs(groom): a missing actions:read grant rejects the run, it does not fail open📊 Changes
7 files changed (+903 additions, -44 deletions)
View changed files
📝
.github/groom/README.md(+69 -0)➕
.github/groom/interval.py(+345 -0)➕
.github/groom/tests/test_interval.py(+262 -0)📝
.github/workflows/ci-groom.yml(+48 -19)📝
.github/workflows/groom.yml(+172 -20)📝
AGENTS.md(+5 -3)📝
README.md(+2 -2)📄 Description
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.⚠️ REQUIRED MERGE STEP — bump the caller pin
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.🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.