diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index d7e1284..5b53087 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -8,9 +8,11 @@ name: Stale PRs (reusable) # • Any new commit/comment removes `stale` and resets the clock # • Issues are left untouched (days-before-issue-* = -1) # -# The Slack digest header names the source repo (`github.repository`) so two -# repos' batches posted to the same channel are unambiguous — a bare "#158" -# looks identical whether it's from cloud or comfy-infra. +# The Slack digest names the source repo (`github.repository`) in the header AND +# on every individual PR line (`Comfy-Org/cloud#158`), so two repos' batches +# posted to the same channel are unambiguous — a bare "#158" looks identical +# whether it's from cloud or comfy-infra, and a line quoted or read on its own +# still says which repo it came from. # # Caller pattern (place in source repo at .github/workflows/stale.yml). The # caller owns the schedule + the workflow_dispatch dry-run toggle; this reusable @@ -133,9 +135,9 @@ jobs: close-pr-message: ${{ inputs.close_pr_message }} debug-only: ${{ inputs.dry_run || false }} - # Surface what the bot did in Slack, not just in-PR. The header names the - # source repo so batches from different repos posted to the same channel - # are visually distinguishable. + # Surface what the bot did in Slack, not just in-PR. The header and every + # PR line name the source repo so batches from different repos posted to + # the same channel are unambiguous line-by-line. - name: Slack heads-up (marked stale / closed) # Skip during dry-run previews and when no PR was touched this run. If # no SLACK_BOT_TOKEN is configured the curl below degrades to a warning @@ -165,21 +167,25 @@ jobs: fi # actions/stale outputs JSON arrays of the PRs it staled/closed this run. - # Build "• title" lines; derive the URL from repo + number so we - # don't depend on html_url being present in the output objects. Escape - # Slack mrkdwn control chars (& < >) in the title so a crafted title - # can't inject / pings or masked links. + # Build "• title" lines; the repo name is carried on + # EVERY line (not just the header) so a line read, quoted, or forwarded + # on its own still says which repo it belongs to. Derive the URL from + # repo + number so we don't depend on html_url being present in the + # output objects. Escape Slack mrkdwn control chars (& < >) in the title + # so a crafted title can't inject / pings or masked + # links. ($REPO is `github.repository`, which GitHub + # constrains to [A-Za-z0-9._-]+/[A-Za-z0-9._-]+, so it needs no escaping.) fmt() { - echo "${1:-[]}" | jq -r --arg base "$SERVER_URL/$REPO/pull" ' + echo "${1:-[]}" | jq -r --arg base "$SERVER_URL/$REPO/pull" --arg repo "$REPO" ' def slack_escape: gsub("&";"&") | gsub("<";"<") | gsub(">";">"); - .[]? | "• <\($base)/\(.number)|#\(.number)> \((.title // "") | slack_escape)"' + .[]? | "• <\($base)/\(.number)|\($repo)#\(.number)> \((.title // "") | slack_escape)"' } WARN_LINES=$(fmt "$STALED") CLOSE_LINES=$(fmt "$CLOSED") # Name the repo in the header so two repos' batches in the same channel - # aren't confused (a bare "#158" is ambiguous). Per-PR links already - # resolve correctly via $REPO above. + # aren't confused (a bare "#158" is ambiguous). Every PR line repeats it + # (see fmt() above) so no single line is ambiguous on its own either. TEXT=":hourglass_flowing_sand: *Stale PR bot — ${REPO}*" if [ -n "$WARN_LINES" ]; then # actions/stale treats days-before-pr-close <= 0 as "never close", so diff --git a/README.md b/README.md index 9687148..9f47a52 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ This repo is **public** so any repo — public or private, inside or outside the | [`assign-reviewers.yml`](.github/workflows/assign-reviewers.yml) | Auto-requests expertise-aware, load-balanced PR reviewers with new-folk randomization. Matches changed paths against a caller-repo `.github/reviewers.yml` (path-glob → reviewers, plus a `default_pool`), drops the author + `vars.REVIEWER_EXCLUDE`, ranks candidates by open review load (steering off anyone at/over `vars.REVIEWER_LOAD_CAP`), and may swap a slot for a `vars.REVIEWER_GROWTH_POOL` member. Requests go through the CLOUD_CODE_BOT app token so they work on fork PRs. Requires `vars.APP_ID` + `CLOUD_CODE_BOT_PRIVATE_KEY`. | | [`assign-prs-to-author.yml`](.github/workflows/assign-prs-to-author.yml) | Housekeeping — assigns every open PR with no assignees to its author (bot-authored PRs skipped by default). Run on a schedule from a thin caller; useful when a team tracks PR ownership via assignees. The calling job needs `pull-requests: write` and `issues: write`. | | [`pr-size.yml`](.github/workflows/pr-size.yml) | PR-size cap — fails (or, in `mode: warn`, only reports) when a PR's net diff exceeds `max_lines` non-generated changed lines, keeping diffs reviewable. Excludes dependency lockfiles, `linguist-generated` files (read from the base ref, so a PR can't exempt itself), Go generated-code markers, and per-repo `extra_lockfiles` / `extra_generated_globs`. A `bypass_label` (default `oversized-ok`) waves through a legitimately large change; a sticky bot comment explains overages when `bot_app_id` + `BOT_APP_PRIVATE_KEY` are supplied (degrades to status + step summary without them). Counting logic + tests live in [`scripts/check-pr-size/`](scripts/check-pr-size). | -| [`stale.yml`](.github/workflows/stale.yml) | Stale-PR sweeper (`actions/stale`) plus a Slack digest of what it touched. PRs inactive for N days are labeled `stale`; still-inactive PRs are closed. The digest header names the source repo so batches from different repos posted to the same channel are unambiguous. Thresholds, messages, exempt labels, and the Slack channel are inputs; the caller owns the schedule + dry-run toggle. The calling job needs `pull-requests: write` and `issues: write`. Optional `SLACK_BOT_TOKEN`. | +| [`stale.yml`](.github/workflows/stale.yml) | Stale-PR sweeper (`actions/stale`) plus a Slack digest of what it touched. PRs inactive for N days are labeled `stale`; still-inactive PRs are closed. The digest names the source repo in the header **and on every PR line** (`Comfy-Org/cloud#3523`) so batches from different repos posted to the same channel are unambiguous, line by line. Thresholds, messages, exempt labels, and the Slack channel are inputs; the caller owns the schedule + dry-run toggle. The calling job needs `pull-requests: write` and `issues: write`. Optional `SLACK_BOT_TOKEN`. | | [`groom.yml`](.github/workflows/groom.yml) | Scheduled/dispatch org-wide **code-cleanup sweep** (finds only — no commits, no PRs, never merges). A read-only FINDER agent scans a clean default-branch checkout (whole-repo, not a diff) for high-value refactors; an INDEPENDENT VERIFIER agent (fresh session) re-checks each as CONFIRM/DOWNGRADE/REJECT with a stable dedup signature; survivors are deduped against a durable GitHub-issue-state ledger and filed as `groom`-labeled GitHub issues (security-adjacent ones get `groom-security` — investigate, don't auto-implement). Mirrors the cursor-review topology: briefs + ledger live in [`.github/groom/`](.github/groom) as the single source of truth. The agent step holds no write credentials (the `audit` job is `contents: read` only, per `model-gap-detector.yml`); filing runs in a separate job as the bot you configure via `bot_app_id` (Comfy: cloud-code-bot). `dry_run` reports what it would file without opening issues. The calling job must grant `contents: read` + `issues: write` + `pull-requests: read` — declared by the `file` / `build_select` jobs (needed even with `bot_app_id` set); GitHub rejects a shorter grant at startup. The finder/verifier/builder agent jobs invoke the Claude CLI directly and mint no GitHub token, so they need nothing beyond `contents: read` (`id-token: write` is no longer required; a caller that still grants it is harmless). Requires `ANTHROPIC_API_KEY` (+ `BOT_APP_PRIVATE_KEY` when `bot_app_id` is set). **Opt-in auto-builder** (`builder: true`, BE-4003): the top `max_prs` (default 5) CONFIRMED, non-security findings become **review-gated PRs** (full CI + cursor-review, **never auto-merged**) instead of issues; a credential-free `build` job emits only a patch artifact and a separate `build_pr` job opens the PR as the bot, preserving the security boundary. The ledger's PR-state (open/merged/closed) stops a built finding being re-proposed. Requires `bot_app_id`. | | [`agents-md-integrity.yml`](.github/workflows/agents-md-integrity.yml) | Enforces the Comfy `AGENTS.md` standard on the caller repo: a top-level `AGENTS.md` must exist and stay under a hard line ceiling (`max_lines`, default 200; warns over `warn_lines`, default 150), a `CLAUDE.md` (if present) must be a thin `@AGENTS.md` shim rather than a divergent copy, no legacy `.cursorrules` (gated `forbid_cursorrules`), every nested monorepo `AGENTS.md` needs a sibling `@AGENTS.md` shim and to be under the ceiling (gated `check_nested`), and `AGENTS.md` should have a CODEOWNERS DRI (`require_codeowners`, warn-only by default). Fails with a non-zero exit + GitHub annotations so it wires in as a required status check. The checker lives in [`.github/agents-md-integrity/`](.github/agents-md-integrity) (pin `workflows_ref` to the same ref as `uses:`); no secrets required. |