mirror of
https://github.com/Comfy-Org/github-workflows.git
synced 2026-11-05 00:17:37 +00:00
[PR #83] [MERGED] feat(groom): path input scopes an audit to one directory, without resetting the cadence clock (BE-4757) #95
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#95
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/83
Author: @mattmillerai
Created: 7/27/2026
Status: ✅ Merged
Merged: 8/11/2026
Merged by: @mattmillerai
Base:
main← Head:matt/be-4757-groom-path-scoping📝 Commits (9)
5e46c7dfeat(groom):pathinput scopes an audit to one directory, without resetting the cadence clock (BE-4757)f297fccfix(groom): per-scope cadence clock, site traversal, and scope-brief accuracy (BE-4757)fbcf3d0fix(groom): enforce path scope after the verifier + close four scoping holes (BE-4757)99f189efix(groom): submodule + line-separator + byte-cap + verdict-enum scope hardening (BE-4757)a8cddb7Merge remote-tracking branch 'origin/main' into matt/be-4757-groom-path-scoping5f7234bfix: resolve review feedbacke488b6aMerge origin/main into matt/be-4757-groom-path-scoping9fb8594Merge origin/main into matt/be-4757-groom-path-scopingbff4947fix(groom): update scoped-cadence test for evidence-based audit detection📊 Changes
10 files changed (+2193 additions, -88 deletions)
View changed files
📝
.github/groom/README.md(+20 -12)📝
.github/groom/interval.py(+116 -29)➕
.github/groom/scope.py(+770 -0)📝
.github/groom/tests/test_interval.py(+138 -3)➕
.github/groom/tests/test_scope.py(+819 -0)📝
.github/groom/verifier.md(+7 -3)📝
.github/workflows/groom.yml(+292 -21)📝
.github/workflows/test-groom-scripts.yml(+21 -14)📝
AGENTS.md(+9 -5)📝
README.md(+1 -1)📄 Description
ELI-5
Groom currently audits your whole repo or nothing. This adds a
pathinput so you can say "just look atservices/apithis time" — and, importantly, makes that actually true rather than a polite request in a prompt. It also makes sure a small scoped run doesn't trick groom into thinking it already did the big whole-repo sweep this week.What changed
New input
path(type: string,required: false,default: ''). Empty is today's whole-repo behavior; all 11 live callers pass nothing and are unaffected. Exposed as a normal input, not gated ongithub.event_name, so a monorepo caller can pin a permanently scoped run the way the studio fleet already groomsComfy-Org/cloud's two subtrees as independent units.It constrains, it does not instruct.
scope_label/scope_descare prompt substitutions — settingscope_desc: "only services/api"today asks and enforces nothing. Layered enforcement in new.github/groom/scope.py:gatejob, before any billed agent: absolute paths,..components, empty components, backslashes, control chars and anything outside a conservative[A-Za-z0-9._-]per-component charset are rejected. A dotted directory name (services/my..svc) is legitimate and is accepted — only a..component is dangerous.os.path.realpath(the Pythoncd && pwd -P) before the prefix compare, so a symlinked component can't escape and a trailing separator can't report a false escape. Also proves the directory exists, so a typo'd dispatch fails loudly instead of auditing an empty file list and reporting "clean".git ls-files), truncated at 1500 with the truncation announced.::warning::. Runs on the finder output because that is wheresiteslive — which also avoids paying the verifier to re-check findings that would be dropped anyway.The checkout stays full on purpose: a refactor in
services/apilegitimately referencescommon/, and a sparse checkout would blind the finder to the context that makes a finding correct.The cadence clock — the headline requirement.
workflow_dispatchbypasses the interval gate by design, so a scoped run does reach the finder. Without a fix, a manualpath: services/apirun would become the repo's "last real groom" and suppress the next scheduled whole-repo tick for a fullGROOM_INTERVAL_DAYS— a partial audit stamping "done" over the full one.I picked option (a), distinguishing the job name, over (b)
run-name. Reason:run-nameis a property of the caller workflow file, not the called reusable — a run created byci-groom.ymltakesci-groom.yml'srun-name, and the reusable's is ignored. Option (b) would therefore require editing all 11 callers to teach each one aboutpath, which directly contradicts "existing callers unaffected". Option (a) is implementable entirely inside the reusable. The coupling the ticket flags is real, so I made it explicit and pinned on both sides:groom.ymlrenames the finder jobAudit — finder (scoped)whenpathis set,interval.pygrew_SCOPED_JOB_MARKERwith a comment on each side saying "keep in sync", andScopedRunDoesNotResetCadenceintest_interval.pyasserts it end-to-end through the realevaluate(), with a control case proving an unscoped run in the same fixture does suppress the tick (so the assertion can't pass because the gate is broken).Dedup stays content-derived.
verifier.md's signature template moved from{{REPO_BASENAME}}:{{SCOPE_LABEL}}:<slug>to{{REPO_BASENAME}}:{{SIG_SCOPE}}:<slug>, andSIG_SCOPEis wired to the caller's rawscope_label, never the path-derived one. So a scoped run and a whole-repo run produce the same signature for the same defect and suppress each other — and, just as important, already-filed issues (whose markers carrywhole-repo) keep deduping, which a straight "drop the scope component" rewrite would have broken by re-filing every open finding once.Filed issues name their scope. When
pathis set andscope_label/scope_descare still at their documented defaults, they're derived (services/api, "theservices/apidirectory"). An explicit caller value always wins.Tests
python3 -m unittest discover -s .github/groom/tests -p 'test_*.py'— 125 tests, green (97 before). Newtest_scope.pymirrorsscan-path-scoping-test.sh's structure and names the same four invariants in the order they'd hurt.actionlintclean on both touched workflows..github/workflows/groom.ymladded totest-groom-scripts.yml's path filter, sincetest_scope.pynow asserts against the workflow's ownSIG_SCOPEwiring.Judgment calls
services/api/a.goandcommon/x.gois kept. Requiring every site in scope would suppress exactly the cross-cutting findings the full checkout was deliberately kept to enable. What gets dropped is a finding whose evidence lies entirely outside the scope (including one with no locatable sites, which can't be attributed). Called out because the acceptance criterion is compatible with either reading.scope.py.scope_label/scope_descnow have whitespace runs collapsed before reaching$GITHUB_OUTPUT, where an embedded newline would truncate the value and inject a bogus output key. YAML>-inputs already fold to one line, so this is byte-identical for every real caller.pr_size_limitand human review remain the backstops on patch breadth.(scoped)is deliberate. A caller whose own job name contained(scoped)would make a real whole-repo run stop counting — i.e. groom runs more often. Same fail-open bias as every other branch ininterval.py; never the silent under-run.Unmet criterion
AC 7's
docs/callers/groom.mdrow is NOT in this PR — that guide does not exist onmain; it lands in #80 (docs/caller-setup-guides-and-public-hygiene, still open). The ticket sanctions either "rebase on it or add the row when merging", and I chose not to stack: stacking would block this behind a docs PR for one table row. Documentation here instead goes to the three places that do exist onmain— thepathinput's own description (which covers the cadence interaction that AC 7 wants in the footguns section), the caller-pattern block ingroom.yml's header, thegroom.ymlrow inREADME.md, andAGENTS.md's layout section. Whichever of #80 and this PR merges second should add thepathrow + the cadence footgun todocs/callers/groom.md.Not applicable: negative-claim falsification
This change adds a capability rather than denying one. The only deny paths it introduces (
UnsafePathErroron/etc,../../etc, a non-existent directory) reject malformed input, not a product capability a user could reach another way — and each is paired with a positive test proving the legitimate neighbouring form (services/agent/internal/api,services/my..svc) is accepted, which is the anti-over-rejection half.🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.