From 56e24022a550ab7db197019ae47c37fc0bb92129 Mon Sep 17 00:00:00 2001 From: Ramkumar Ethiraj <64294490+rmkr-dev@users.noreply.github.com> Date: Fri, 30 Oct 2026 05:10:04 +0000 Subject: [PATCH 1/2] Add GitHub rulesets and merge queue guardrails skill --- skillbook/skills.json | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/skillbook/skills.json b/skillbook/skills.json index c49bcf4..54b149b 100644 --- a/skillbook/skills.json +++ b/skillbook/skills.json @@ -857,6 +857,26 @@ ], "formatNotes": "All three. Copilot: put these rules in path-scoped `.github/instructions/dependency-updates.instructions.md` with applyTo for `.github/dependabot.yml`, `renovate.json`, `.github/renovate.json5`, and `.github/workflows/dependabot-auto-merge.yml`, or in `.github/skills/dependency-update-automation-guardrails/SKILL.md`; one always-on line in `.github/copilot-instructions.md` (\"actions pinned by SHA; no auto-merge without required checks\"). Claude Code: `.claude/skills/dependency-update-automation-guardrails/SKILL.md`, plus a CLAUDE.md line naming the repo's update bot and auto-merge scope. Codex: a \"Dependency updates\" section in root `AGENTS.md` (or the same SKILL.md under `.agents/skills/`). Pair with maven-dependency-cve-hygiene-guardrails for BOM, enforcer, and CVE triage, gha-devsecops-pipeline for workflow permissions and required checks, and artifact-provenance-signing-guardrails for digest-only deploys. This skill owns how update PRs are grouped, delayed, pinned, and merged.", "body": "# Skill: Dependency Update Automation Guardrails\n\n## Goal\nDependency updates arrive on a predictable schedule, in a small number of well-grouped PRs, after a release has aged long enough for a bad or malicious version to be pulled. Only low-risk updates merge on their own, and only after the same required checks a human PR must pass. Security fixes skip the queue, majors and framework minors always get a human, and every action and base image is pinned to something immutable.\n\n## When to apply\n- Adding or changing `.github/dependabot.yml`, `renovate.json`, `.github/renovate.json5`, or a shared Renovate preset\n- A repository drowns in one-PR-per-library noise, or updates sit unmerged for months\n- Someone proposes auto-merging dependency PRs, or adds a workflow that approves or merges them\n- Maven or Gradle builds using the Spring Boot parent, BOM, or dependency-management plugin, plus Dockerfiles and GitHub Actions workflows that also need updates\n- A supply-chain review asks how quickly a freshly published version can land on main\n\n## Pick one bot per ecosystem\n| Concern | Dependabot (`.github/dependabot.yml`) | Renovate (`renovate.json`) |\n| --- | --- | --- |\n| Grouping | `groups` with `patterns`, `exclude-patterns`, `update-types`, `dependency-type`, `applies-to`; `multi-ecosystem-groups` across ecosystems | `packageRules` with `groupName`; presets such as `group:springBoot`, `group:allNonMajor` |\n| Release age | `cooldown` (`default-days`, `semver-major-days`, `semver-minor-days`, `semver-patch-days`, `include`, `exclude`); a 3-day default applies to version updates | `minimumReleaseAge` with `internalChecksFilter: \"strict\"` (the default); `waitForAllUpdates` on groups |\n| Schedule | `schedule.interval` (`daily`, `weekly`, `monthly`, `cron` with `cronjob`), `day`, `time`, `timezone` | `schedule`, `automergeSchedule`, `timezone` |\n| Auto-merge | not built in: a `pull_request` workflow with `dependabot/fetch-metadata` and `gh pr merge --auto` | `automerge` per rule, `automergeType`, `platformAutomerge` (on by default) |\n| Action pinning | updates SHA-pinned `uses:` lines and keeps the version comment | `helpers:pinGitHubActionDigests` (in `config:best-practices`) |\n| Security fixes | security updates, separate limit of 10 open PRs, `applies-to: security-updates` groups | `vulnerabilityAlerts`, optional `osvVulnerabilityAlerts` |\n\nRunning both bots on the same manifests produces duplicate, conflicting PRs. Choose one per ecosystem and disable the other (Dependabot version updates off for that ecosystem, or Renovate `enabled: false` for that manager). Dependabot security alerts can stay on as a signal either way.\n\n## Baseline rules\n1. **Cover every ecosystem in the repo.** One entry each for `maven` or `gradle`, `github-actions` (directory `/`), `docker` (each Dockerfile directory), plus `docker-compose`, `helm`, or `terraform` if present. Use `directories` with globs for multi-module or monorepo layouts and `exclude-paths` for fixtures and vendored code. A Dockerfile nobody scans is the base image that quietly ages for two years.\n2. **Group by risk, not just by name.** A typical set per ecosystem: one group for patch and minor of build and test tooling (plugins, JUnit, Mockito, Testcontainers), one for patch and minor of runtime libraries, the Spring Boot group on its own, and majors as individual PRs. Put security updates in their own `applies-to: security-updates` group or leave them ungrouped so a fix never waits behind an unrelated failing bump. Remember Dependabot puts a dependency in the first group it matches, so order groups from most specific to most general.\n3. **Schedule for when people are around.** Weekly on a working-day morning in the team's timezone (`timezone: \"Asia/Kolkata\"`, or whatever the team uses) suits most services; daily only for repos with fast CI and an owner who reads the PRs. Keep `open-pull-requests-limit` small (3 to 5) so a backlog cannot swamp CI. Security updates are not bound by the schedule.\n4. **Make new releases wait.** Set a release-age delay: 3 to 7 days for patch and minor, 14 days or more for anything that will auto-merge and for majors. Dependabot: `cooldown`. Renovate: `minimumReleaseAge` with the strict internal-checks filter, and `waitForAllUpdates: true` on groups so a group PR does not open half-aged. Exempt your own internal packages with `cooldown.exclude` or a separate rule, and remember that security updates bypass the delay by design.\n5. **Pin what can move under you.** GitHub Actions by full 40-character commit SHA with a `# vX.Y.Z` comment; both bots update the SHA and the comment together. Docker base images by tag plus `@sha256:` digest so the bot can raise digest updates for rebuilt images. Never use `latest` tags or floating branch refs in workflows or Dockerfiles.\n6. **Treat Spring Boot as one unit.** With `spring-boot-starter-parent` or the `spring-boot-dependencies` BOM, let the bot bump the Boot version and let the BOM move the managed libraries. Do not add version overrides for managed libraries just to receive bot PRs; every override in a `` block or Gradle `ext` must carry a comment with the reason (usually a CVE) and be removed once Boot catches up. Group `org.springframework.boot:*` (Renovate: `group:springBoot`) together with the Spring Boot Gradle plugin. Boot patches can auto-merge after cooldown if the test suite is real; Boot minors and majors need a human and a read of the release notes, because minors remove deprecated APIs and change defaults.\n7. **Auto-merge is narrow and earns its place.** Only patch, and optionally minor, for build tooling, test libraries, and GitHub Actions, plus digest-only image updates, after the release-age delay. Never auto-merge majors, Spring Boot minors, security-sensitive libraries (auth, crypto, JWT, TLS, serialization), database drivers, or anything in a package with a history of breaking patches. Write the list down in the config, not in someone's head.\n8. **Required checks are the gate, not the bot.** Auto-merge relies on branch protection or a ruleset on main that requires the real CI checks (build, tests, SCA) and up-to-date branches. With no required checks, `gh pr merge --auto` or Renovate platform auto-merge can merge a red PR. Turn on the repository's \"Allow auto-merge\" setting rather than writing workflows that merge directly, and prefer a merge queue for busy repos (it needs a GitHub App or fine-grained token, because the default `GITHUB_TOKEN` cannot enqueue).\n9. **Keep the auto-merge workflow minimal and safe.** Trigger on `pull_request`, never `pull_request_target` with a checkout of the PR head. Run only when `github.event.pull_request.user.login == 'dependabot[bot]'` and the repository matches. Grant job-level `contents: write` and `pull-requests: write` only. Pin `dependabot/fetch-metadata` by SHA and branch on its `update-type` and `dependency-type` outputs, then call `gh pr merge --auto` with the repo's normal merge method. Do not check out code, run scripts, or use secrets in this job. Dependabot runs get Dependabot secrets, not Actions secrets.\n10. **Private registries by reference.** Dependabot `registries` with `${{secrets.NAME}}` references or OIDC (`tenant-id`, `client-id`) for Azure Artifacts and ACR; Renovate `hostRules` with encrypted or environment-provided secrets. No credentials in the config files. Keep `insecure-external-code-execution` off.\n11. **Majors are planned work.** Open majors one at a time (Renovate `:separateMultipleMajorReleases`, Dependabot one PR per major by not grouping majors), link the upgrade guide in the PR, and use `ignore` with `update-types: [\"version-update:semver-major\"]` only with a comment and an issue that tracks the migration. Do not leave a permanent ignore that hides a framework end-of-life.\n12. **Config is code.** CODEOWNERS on `.github/dependabot.yml`, Renovate config, and the auto-merge workflow. Validate config in CI (`npx --package renovate -- renovate-config-validator --strict` for Renovate; for Dependabot, check the repository's Dependabot status page after merge). Review the Renovate Dependency Dashboard or Dependabot logs weekly for failed updates; a silently failing updater is worse than none.\n\n## Workflow\n1. **Inventory.** List every manifest: `pom.xml` files, `build.gradle(.kts)` and `gradle/libs.versions.toml`, Dockerfiles, compose files, Helm charts, workflow files. Note which bot covers each today and whether actions and images are pinned.\n2. **Choose the bot per ecosystem** and remove the overlap.\n3. **Write groups, schedules, limits, and cooldowns** following rules 2 to 4.\n4. **Pin actions and base images** (rule 5), in a separate PR from the bot config so each change is reviewable.\n5. **Set up the gate.** Confirm required checks and \"Allow auto-merge\" on main, then add the auto-merge rules or workflow from rules 7 to 9.\n6. **Dry run.** For Renovate, run the config validator and `renovate --platform=local` from the repo root (a lookup-only dry run), or read the onboarding PR. For Dependabot, trigger \"Check for updates\" on the dependency graph page and read the logs. Confirm the PR count and grouping match the plan.\n7. **Prove the gate.** Push a deliberately failing test into a patch-group PR branch and confirm it does not merge. Confirm a major and a Spring Boot minor get no auto-merge.\n\n## Review checklist\n- [ ] Every ecosystem in the repo is covered by exactly one bot\n- [ ] Groups separate tooling, runtime libraries, Spring Boot, and majors; security updates are not stuck behind version groups\n- [ ] Schedule, timezone, and a small open-PR limit are set\n- [ ] A release-age delay is configured; longer for anything that auto-merges; internal packages exempted deliberately\n- [ ] GitHub Actions are pinned by full SHA with version comments; base images by digest; no `latest`\n- [ ] Spring Boot moves as one group; every managed-version override has a reason and an exit\n- [ ] Auto-merge covers only patch or minor of tooling, test libraries, actions, and digests; never majors, Boot minors, or security-sensitive libraries\n- [ ] Main requires the real CI checks; repository auto-merge is enabled; merge queue uses an App or fine-grained token if present\n- [ ] The auto-merge workflow uses `pull_request`, checks the actor, pins fetch-metadata by SHA, has job-level minimal permissions, and checks out nothing\n- [ ] Registry credentials are secret references or OIDC; nothing literal in config\n- [ ] Ignored majors have a comment and a tracking issue\n- [ ] Bot config and auto-merge workflow have CODEOWNERS and are validated in CI\n\n## Copy-ready SKILL.md stub\n```markdown\n---\nname: dependency-update-automation-guardrails\ndescription: Use when adding or changing Dependabot or Renovate config, or auto-merge for dependency PRs (Maven, Gradle, GitHub Actions, Docker) — one bot per ecosystem, risk-based groups, scheduled updates with small PR limits, release-age cooldowns, SHA-pinned actions and digest-pinned images, Spring Boot updated as one BOM group, auto-merge only for patch or minor tooling and test dependencies behind required checks, safe pull_request workflows, and secrets by reference.\n---\n\nFollow Dependency Update Automation Guardrails: cover every ecosystem with exactly one bot. Group updates by risk (tooling, runtime libraries, Spring Boot, majors separately) and keep security updates out of version groups. Schedule weekly in the team's timezone with a small open-PR limit, and set a release-age delay (Dependabot cooldown, Renovate minimumReleaseAge), longer for anything that auto-merges. Pin GitHub Actions by full SHA and base images by digest. Treat Spring Boot as one BOM group and justify every managed-version override. Auto-merge only patch or minor of tooling, test libraries, actions, and digests, and only behind required CI checks with repository auto-merge enabled. Never auto-merge majors, Spring Boot minors, or security-sensitive libraries. Auto-merge workflows run on pull_request, check the actor, pin fetch-metadata by SHA, use minimal job permissions, and check out nothing. Keep registry credentials as secret references or OIDC, and review bot config like code.\n```\n" + }, + { + "id": "github-rulesets-merge-queue-guardrails", + "title": "GitHub Rulesets and Merge Queue as Code", + "whenToUse": "Creating or changing GitHub branch or tag protection — rulesets stored as JSON or Terraform in the repo and applied only from reviewed changes, default-branch rules with stale-review dismissal, code owners, last-push approval, and thread resolution, required checks named by exact job and pinned to the GitHub Actions app, one always-running gate job instead of path-filtered required workflows, merge_group triggers before enabling a merge queue, protected release tags, few pull_request-mode bypass actors, App tokens for automation, a staged migration off classic protection, and scheduled drift detection.", + "tools": [ + "copilot", + "claude-code", + "codex" + ], + "tags": [ + "github", + "rulesets", + "merge-queue", + "devsecops", + "github-actions", + "developer-workflow" + ], + "formatNotes": "All three. Copilot: path-scoped `.github/instructions/rulesets.instructions.md` with applyTo for `.github/rulesets/**` and `.github/workflows/**`, or `.github/skills/github-rulesets-merge-queue-guardrails/SKILL.md`; one always-on line in `.github/copilot-instructions.md` (\"required workflows run unfiltered, listen to merge_group, and end in a ci-ok gate job\"). Claude Code: `.claude/skills/github-rulesets-merge-queue-guardrails/SKILL.md`, plus a CLAUDE.md line naming the gate job and where rulesets live. Codex: a \"Branch rules\" section in root `AGENTS.md` (or the same SKILL.md under `.agents/skills/`). Pair with gha-devsecops-pipeline for workflow permissions, dependency-update-automation-guardrails for bot auto-merge, and secure-pr-review-guardrails for what reviewers check. This skill owns what may reach main and how.", + "body": "# Skill: GitHub Rulesets and Merge Queue as Code\n\n## Goal\nThe rules that protect `main` and release tags live in the repository as reviewed files, not as clicks in Settings. Every merge passes the same named checks, a merge queue tests the exact commit that lands when the branch is busy, bypass is rare and visible, and a scheduled job notices when someone changes a rule by hand.\n\n## When to apply\n- Moving a repository or organization from classic branch protection to rulesets\n- Adding a merge queue, or a team keeps hitting \"branch is out of date\" and re-running CI after every merge\n- Required checks are named by hand, some PRs hang on \"Expected — Waiting for status to be reported\", or a workflow uses path filters and is also required\n- Someone asks for a bypass, an admin override, or a bot that merges without review\n- Release tags (`v*`) can be moved, deleted, or created by anyone with write access\n\n## Rulesets in one table\n| Concern | Classic branch protection | Rulesets |\n| --- | --- | --- |\n| Scope | one pattern per rule, repo only | repo, organization, or enterprise; `~DEFAULT_BRANCH`, `~ALL`, fnmatch include and exclude |\n| Layering | one rule wins per branch | every matching ruleset applies; the strictest setting of each rule wins |\n| Targets | branches | `branch`, `tag`, and `push` (file path, size, extension) rulesets |\n| Trial run | none | `enforcement: evaluate` (Enterprise) reports what would fail without blocking |\n| Bypass | admins, plus a few allow lists | explicit `bypass_actors` with `always`, `pull_request`, or `exempt` mode |\n| As code | REST only | REST and `gh ruleset`, JSON import and export in the UI, Terraform `github_repository_ruleset` |\n| Merge queue | separate setting | `merge_queue` rule (repository-level rulesets only) |\n\nClassic protection and rulesets both apply when they overlap, so migrate one branch at a time and delete the classic rule only after the ruleset is active and verified.\n\n## Baseline rules\n1. **Keep the source of truth in the repo.** Store each ruleset as JSON under `.github/rulesets/` (or as Terraform with the `integrations/github` provider for many repos). Apply with `gh api --method PUT repos/{owner}/{repo}/rulesets/{id} --input file.json` (POST for the first create), from a reviewed PR, by a person or a workflow that holds an admin-scoped token. Never apply from an unreviewed branch.\n2. **One default-branch ruleset, one tag ruleset, layered org rules.** Default branch: `deletion`, `non_fast_forward`, `pull_request`, `required_status_checks`, and optionally `required_linear_history`, `required_signatures`, `merge_queue`, `code_scanning`. Tags matching `v*`: `creation`, `update`, and `deletion` restricted to the release workflow or a release team. Put org-wide minimums (no force push, no deletion of `main`) in an organization ruleset and repo specifics in the repo.\n3. **Pull request rule with real review settings.** `required_approving_review_count` of 1 or more, `dismiss_stale_reviews_on_push: true`, `require_code_owner_review: true` where CODEOWNERS exists, `require_last_push_approval: true` so the last pusher cannot self-approve, `required_review_thread_resolution: true`, and `allowed_merge_methods` matching the repo's settings.\n4. **Required checks are named precisely.** The context is the check run name, which for Actions is the job `name:` (matrix jobs add the matrix values, e.g. `test (21)`). Pin the source with `integration_id` (15368 is GitHub Actions) so another app or a commit status with the same name cannot satisfy it. Give jobs stable, unique names and never rename a required job in the same PR that changes the ruleset.\n5. **Required workflows must always report.** A workflow with `paths:` or `branches:` filters that does not run leaves its required check pending forever. Keep the trigger unfiltered and skip inside: a small `changes` job (e.g. `dorny/paths-filter` pinned by SHA) plus a final gate job such as `ci-ok` with `if: always()` that fails when any needed job failed or was cancelled. Require only the gate job.\n6. **Merge queue for busy branches.** Add `merge_group:` to every workflow that produces a required check, beside `pull_request:`, or the queue waits until timeout. Start with `grouping_strategy: ALLGREEN`, `max_entries_to_build` around 5, `min_entries_to_merge: 1`, `check_response_timeout_minutes` a little above your slowest required job, and the same `merge_method` as the repo. Queue branches are named `gh-readonly-queue//...`; do not deploy from them and do not run secrets-heavy jobs only because of `merge_group`. With a queue, turn off \"require branches to be up to date\" (`strict_required_status_checks_policy`), because the queue already tests the merged result.\n7. **Bypass is an exception with a name.** Prefer no bypass actors. If needed, use a team or a specific GitHub App with `bypass_mode: pull_request`, so bypass happens in the open on a PR, never `always` for humans. Keep `exempt` for automation that genuinely must skip rules, and note that exempt actors leave no bypass entry. Bypass has been reported not to apply to auto-merge completion and queue entry, so a bot that needs it should call the merge endpoint directly rather than relying on `--auto`.\n8. **Bots go through the gate too.** Dependency bots and release bots open PRs and wait for the same checks. The default `GITHUB_TOKEN` cannot add a PR to a merge queue and its pushes do not trigger new workflow runs; use a GitHub App token with the narrowest permissions for automation that must enqueue or push.\n9. **Trial before enforcement.** On Enterprise, create new rulesets with `enforcement: evaluate` for a week and read Rule Insights. Elsewhere, enable on a low-traffic repo first, then roll out. Use `gh ruleset check main` to see which rules apply to a branch and `gh ruleset view` to read the live result.\n10. **Detect drift.** A scheduled workflow (weekly, working hours) reads the live rulesets with a token that can see bypass actors, normalizes the JSON (drop `id`, `node_id`, `_links`, timestamps, `current_user_can_bypass`), diffs it against `.github/rulesets/`, and opens an issue on any difference. It reports; it does not silently re-apply.\n11. **Changes are reviewed like code.** CODEOWNERS on `.github/rulesets/` and the apply and drift workflows; the PR description says which rule changes and why; loosening a rule (fewer reviewers, new bypass actor, removed check) needs a security or platform owner's approval.\n\n## Workflow\n1. **Export.** `gh api repos/{owner}/{repo}/rulesets` and each `rulesets/{id}`, plus classic protection from `branches/main/protection`. Save normalized JSON as the starting point.\n2. **Inventory checks.** List the check runs on a recent green PR (`gh pr checks `); match each to a job name and app; mark which ones are path-filtered.\n3. **Fix the workflows first.** Unfiltered triggers, a gate job, `merge_group:` triggers, stable job names. Merge that before touching rules.\n4. **Write the rulesets** (rules 2 to 7) as files, review, then apply.\n5. **Migrate.** Activate the ruleset, confirm with `gh ruleset check`, then delete the overlapping classic protection.\n6. **Prove it.** A PR with a failing test cannot merge; a PR without approval cannot merge; a direct push and a force push to `main` are rejected; a non-release actor cannot create a `v*` tag; with a queue, two PRs merge in order and a failing one is removed from the queue.\n7. **Turn on drift detection** and document the apply process in `CONTRIBUTING.md`.\n\n## Review checklist\n- [ ] Rulesets live as JSON or Terraform in the repo, applied from reviewed changes\n- [ ] Default branch blocks deletion and force push and requires PRs with stale-review dismissal, code owners, last-push approval, and thread resolution\n- [ ] Required checks use exact job names and `integration_id`; one always-running gate job is required\n- [ ] No required check comes from a path-filtered or branch-filtered workflow\n- [ ] Every required workflow listens to `merge_group`; queue settings and timeout fit CI duration\n- [ ] Release tags are protected from creation, update, and deletion except by the release path\n- [ ] Bypass actors are few, named, and `pull_request` mode for humans\n- [ ] Automation that enqueues or pushes uses an App token, not `GITHUB_TOKEN`\n- [ ] Classic protection removed only after the ruleset was verified\n- [ ] A scheduled drift check opens an issue on differences\n- [ ] CODEOWNERS covers the ruleset files and workflows\n\n## Copy-ready SKILL.md stub\n```markdown\n---\nname: github-rulesets-merge-queue-guardrails\ndescription: Use when creating or changing GitHub branch or tag protection, rulesets, required status checks, or a merge queue: rulesets stored as JSON or Terraform in the repo, PR reviews with stale-dismissal, code owners, and last-push approval, required checks pinned by exact job name and app, an always-running gate job instead of path-filtered required workflows, merge_group triggers, protected release tags, minimal pull_request-mode bypass, App tokens for automation, and scheduled drift detection.\n---\n\nFollow GitHub Rulesets and Merge Queue as Code: keep rulesets as reviewed files in the repo and apply them only from merged changes. Protect the default branch from deletion and force push, require PRs with stale-review dismissal, code owner review, last-push approval, and resolved threads. Require exact check names with integration_id, and require one always-running gate job rather than path-filtered workflows. Add merge_group triggers to every required workflow before enabling the merge queue, and drop strict up-to-date checks once the queue is on. Protect v* tags so only the release path can create, move, or delete them. Keep bypass actors minimal and in pull_request mode for humans. Use GitHub App tokens for automation that must enqueue or push. Migrate from classic protection one branch at a time, prove the rules with failing PRs and rejected pushes, and run a scheduled drift check that opens an issue.\n```\n" } ] } From b4309811d04361a2bb372aad3c5e3c1137daf0f5 Mon Sep 17 00:00:00 2001 From: Ramkumar Ethiraj <64294490+rmkr-dev@users.noreply.github.com> Date: Fri, 30 Oct 2026 05:10:04 +0000 Subject: [PATCH 2/2] Add prompt for migrating branch protection to rulesets --- promptbook/prompts.json | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/promptbook/prompts.json b/promptbook/prompts.json index 2688245..a4013dd 100644 --- a/promptbook/prompts.json +++ b/promptbook/prompts.json @@ -859,6 +859,26 @@ ], "formatNotes": "All three. Point the tool at `.github/dependabot.yml` or the Renovate config, every `pom.xml` or `build.gradle(.kts)` and `gradle/libs.versions.toml`, the Dockerfiles, `.github/workflows/`, and CODEOWNERS, and paste the output of `gh api repos/{owner}/{repo}/branches/main/protection` (or the ruleset) so required checks are known. Copilot: dependency-updates path instructions plus those files in agent mode (or save it as `.github/prompts/configure-grouped-dependency-updates.prompt.md`). Claude Code: the dependency-update-automation-guardrails skill plus the files. Codex: AGENTS.md dependency updates section, then the diff.", "body": "Task: Configure grouped, scheduled dependency updates with release-age delays and a narrow, check-gated auto-merge for this repository. Cover Maven or Gradle, GitHub Actions, and Docker. Finish with a minimal diff, a grouping plan, and a test that proves a failing or high-risk update does not merge.\n\nContext placeholders (fill from the repo or ask only if missing):\n- Bot: {{BOT}} (Dependabot, Renovate, or \"recommend one\"; one bot per ecosystem)\n- Build: {{BUILD}} (Maven with spring-boot-starter-parent, Maven with the spring-boot-dependencies BOM, or Gradle with version catalog and the Spring Boot plugin)\n- Manifest locations: {{MANIFESTS}} (e.g. /, /services/*, Dockerfiles in /docker and /services/*/)\n- Other ecosystems: {{OTHER_ECOSYSTEMS}} (docker-compose, helm, terraform, or none)\n- Schedule and timezone: {{SCHEDULE}} (e.g. weekly, Monday 09:00, Asia/Kolkata)\n- Release-age delay: {{COOLDOWN}} (e.g. 5 days patch/minor, 14 days for auto-merged and major)\n- Auto-merge scope: {{AUTOMERGE_SCOPE}} (e.g. patch and minor of Maven plugins, test libraries, GitHub Actions, image digests; or \"none\")\n- Never auto-merge: {{NEVER_AUTOMERGE}} (e.g. majors, Spring Boot minors, spring-security, nimbus-jose-jwt, jackson, JDBC drivers)\n- Required checks on main: {{REQUIRED_CHECKS}} (e.g. build, test, dependency-scan; or \"unknown\")\n- Merge method and queue: {{MERGE_METHOD}} (merge, squash, or rebase; merge queue yes or no)\n- Private registries: {{REGISTRIES}} (e.g. Azure Artifacts Maven feed, ACR; or none)\n\nRequirements:\n\n1. Inventory\n - Table: ecosystem | manifest paths | covered by (Dependabot, Renovate, none) | pinned (actions by SHA, images by digest, yes or no) | open bot PRs today | oldest open bot PR\n - Flag: ecosystems with no coverage, the same manifest covered by two bots, `@v4`-style or branch refs in `uses:`, `latest` or tag-only base images, managed-version overrides without a reason, literal registry credentials\n\n2. Groups and schedule\n - Per ecosystem: a tooling and test group (patch and minor), a runtime library group (patch and minor), a Spring Boot group (`org.springframework.boot:*` plus the Boot Gradle plugin; Renovate `group:springBoot`), majors ungrouped\n - Security updates: own group with `applies-to: security-updates` (Dependabot) or default vulnerability handling (Renovate); never blocked by a version group\n - Order Dependabot groups from most specific to most general\n - {{SCHEDULE}} with explicit timezone; `open-pull-requests-limit` (Dependabot) or `prConcurrentLimit` and `prHourlyLimit` (Renovate) between 3 and 5\n - Use `directories` globs or Renovate defaults for {{MANIFESTS}}; `exclude-paths` for fixtures\n\n3. Release-age delay\n - Dependabot: `cooldown` with `default-days` and semver-specific days from {{COOLDOWN}}; `exclude` for internal group IDs\n - Renovate: `minimumReleaseAge` per rule, `internalChecksFilter: \"strict\"`, `waitForAllUpdates: true` on groups\n - Longer delay for anything inside {{AUTOMERGE_SCOPE}}; state that security updates bypass it\n\n4. Pinning\n - Separate commit: pin every `uses:` to a full commit SHA with a `# vX.Y.Z` comment; pin base images as `image:tag@sha256:`\n - Renovate: extend `config:best-practices` (or add `helpers:pinGitHubActionDigests` and `docker:pinDigests`)\n - Don't invent SHAs or digests: resolve them with `gh api repos///commits/ --jq .sha` and `docker buildx imagetools inspect :`, or leave a placeholder and list it\n\n5. Spring Boot\n - Let Boot and its BOM move managed libraries; list every existing override with its reason; propose removing the ones Boot already covers\n - Boot patch inside auto-merge only if {{AUTOMERGE_SCOPE}} says so and the test suite covers startup and integration; Boot minor and major never auto-merge\n - Gradle: keep the Boot plugin and `org.springframework.boot` platform in the same group\n\n6. Auto-merge gate\n - Confirm {{REQUIRED_CHECKS}} via `gh api repos/{owner}/{repo}/branches/main/protection` or the rulesets API; if none are required, stop and output the ruleset change instead of enabling auto-merge\n - Confirm repository \"Allow auto-merge\" is on (`gh repo view --json autoMergeAllowed`)\n - Dependabot: `.github/workflows/dependabot-auto-merge.yml` on `pull_request` only; `if` on `github.event.pull_request.user.login == 'dependabot[bot]'` and the repository name; job-level `contents: write`, `pull-requests: write`; `dependabot/fetch-metadata` pinned by SHA; `gh pr merge --auto --{{MERGE_METHOD}}` only when `update-type` and `dependency-names` fall within {{AUTOMERGE_SCOPE}} and outside {{NEVER_AUTOMERGE}}; no checkout, no scripts, no secrets\n - Renovate: `packageRules` with `matchUpdateTypes`, `matchPackageNames` or `matchManagers`, `automerge: true`; explicit `automerge: false` rule for {{NEVER_AUTOMERGE}} placed after it\n - Merge queue: document that `GITHUB_TOKEN` cannot enqueue and that a GitHub App token is needed\n\n7. Registries and secrets\n - {{REGISTRIES}} as Dependabot `registries` with `${{secrets.NAME}}` or OIDC (`tenant-id`, `client-id`), or Renovate `hostRules` with encrypted values; list the Dependabot secrets to create, without values\n\n8. Ownership and validation\n - CODEOWNERS for the bot config and the auto-merge workflow\n - CI step for Renovate: `npx --yes --package renovate -- renovate-config-validator --strict`\n - Majors ignored only with a comment and a tracking issue\n\n9. Proof\n - Push a failing test to a patch-group branch: it must not merge\n - A major and a Spring Boot minor: no auto-merge, reviewers requested\n - A security update: opens even during cooldown and outside the schedule\n\nConstraints:\n- Minimal diff. Don't change application code, build plugins, or CI jobs beyond pinning and the auto-merge workflow.\n- Never use `pull_request_target`, never check out PR code in the auto-merge job, and never grant workflow-level write permissions.\n- Don't enable auto-merge on a branch without required checks.\n- Don't invent SHAs, digests, group IDs, secret names, or check names; use placeholders and state assumptions.\n- Optimize for GitHub Copilot, Claude Code, and Codex: give concrete diffs and a checklist.\n\nOutput format:\n1. Inventory table with flagged issues\n2. Grouping plan: group | ecosystem | matches | update types | cooldown | auto-merge yes or no | why\n3. Patch: bot config, pinning commit, auto-merge workflow or Renovate rules, CODEOWNERS, validator step\n4. Branch protection or ruleset changes needed (as `gh api` commands to review, not to run)\n5. Proof plan with expected results\n6. Risks and follow-ups (e.g. overrides to remove, majors to plan)\n\nStart with the inventory. Pin before you automate, and gate before you auto-merge.\n" + }, + { + "id": "migrate-branch-protection-to-rulesets", + "title": "Migrate Branch Protection to Rulesets with a Merge Queue", + "whenToUse": "Move a repository's classic branch and tag protection into rulesets stored as code: export the current rules, inventory required checks, fix path-filtered workflows with a gate job and merge_group triggers, write default-branch and release-tag ruleset files with a merge queue if warranted, add an apply process and a weekday drift check, and prove that failing, unapproved, force-pushed, and tag-moving changes are blocked.", + "tools": [ + "copilot", + "claude-code", + "codex" + ], + "tags": [ + "github", + "rulesets", + "merge-queue", + "devsecops", + "github-actions", + "developer-workflow" + ], + "formatNotes": "All three. Point the tool at `.github/workflows/`, CODEOWNERS, and any existing `.github/rulesets/` or Terraform, and paste the output of `gh api repos/{owner}/{repo}/rulesets`, `gh api repos/{owner}/{repo}/branches/main/protection`, and `gh pr checks `. Copilot: rulesets path instructions plus those files in agent mode (or save it as `.github/prompts/migrate-branch-protection-to-rulesets.prompt.md`). Claude Code: the github-rulesets-merge-queue-guardrails skill plus the files. Codex: AGENTS.md branch rules section, then the diff. The prompt outputs commands to review; it never applies rules itself.", + "body": "Task: Move this repository's branch and tag protection into rulesets stored as code, make every required check reliable, and add a merge queue if it is warranted. Finish with ruleset files, workflow fixes, an apply and drift-check process, and a proof plan.\n\nContext placeholders (fill from the repo or ask only if missing):\n- Repository: {{OWNER_REPO}}\n- Plan and owner type: {{PLAN}} (personal, organization on Team, or Enterprise Cloud; decides whether evaluate mode and org rulesets exist)\n- Default branch and release branches: {{BRANCHES}} (e.g. main, release/*)\n- Release tag pattern: {{TAG_PATTERN}} (e.g. v*)\n- Required reviews: {{REVIEWS}} (e.g. 1 approval, code owners, last-push approval)\n- Checks that must pass: {{CHECKS}} (e.g. build, test, dependency-scan, CodeQL; or \"discover from a recent PR\")\n- Merge method: {{MERGE_METHOD}} (merge, squash, or rebase)\n- Merge queue: {{MERGE_QUEUE}} (yes, no, or \"recommend\")\n- Bypass needs: {{BYPASS}} (e.g. release app for tags, platform team on PRs only; or none)\n- Apply method: {{APPLY}} (gh api from a reviewed PR, or Terraform integrations/github)\n\nRequirements:\n\n1. Current state\n - Export with `gh api repos/{{OWNER_REPO}}/rulesets` (and each `rulesets/{id}`) and `gh api repos/{{OWNER_REPO}}/branches//protection`\n - Table: target | source (classic or ruleset) | rule | setting | gap\n - Flag: force push or deletion allowed, no PR rule, admins exempt by default, required checks with no app pinned, tags unprotected, bypass actors with `always`\n\n2. Check inventory\n - From `gh pr checks` on a recent green PR and `.github/workflows/`: check name | workflow file | job id | app | triggers | path or branch filters | matrix\n - Flag required candidates that are path-filtered, renamed recently, duplicated across workflows, or missing `merge_group`\n\n3. Workflow fixes (separate commit, merged first)\n - Unfiltered `pull_request` and `merge_group` triggers on required workflows; move path logic into a `changes` job with a SHA-pinned filter action\n - A final gate job (e.g. `ci-ok`) with `needs:` on all required jobs, `if: always()`, failing when any need failed or was cancelled\n - Stable, unique job `name:` values; no secrets or deploy steps that run only on `merge_group`\n\n4. Ruleset files\n - `.github/rulesets/default-branch.json`: conditions with `~DEFAULT_BRANCH` (plus {{BRANCHES}}), `deletion`, `non_fast_forward`, `pull_request` from {{REVIEWS}} with `dismiss_stale_reviews_on_push`, `require_last_push_approval`, `required_review_thread_resolution`, `allowed_merge_methods` [{{MERGE_METHOD}}]; `required_status_checks` with the gate job and `integration_id: 15368` for Actions checks\n - `.github/rulesets/release-tags.json`: target `tag`, include `refs/tags/{{TAG_PATTERN}}`, `creation`, `update`, `deletion`; bypass only the release actor from {{BYPASS}}\n - Merge queue (if {{MERGE_QUEUE}}): `merge_queue` with `merge_method`, `grouping_strategy: ALLGREEN`, `max_entries_to_build`, `min_entries_to_merge`, `max_entries_to_merge`, `min_entries_to_merge_wait_minutes`, `check_response_timeout_minutes` from CI duration; set `strict_required_status_checks_policy: false`\n - `bypass_actors`: only {{BYPASS}}, `pull_request` mode for people and teams; explain any `exempt`\n - `enforcement: evaluate` first if {{PLAN}} is Enterprise, otherwise a staged rollout note\n - Terraform equivalent if {{APPLY}} is Terraform\n\n5. Apply and drift\n - Apply script or workflow: POST on first create, PUT by id after; run from a reviewed merge with a token that has Administration write; never from a PR branch\n - Scheduled drift workflow on weekdays: fetch live rulesets, normalize (drop `id`, `node_id`, `_links`, `created_at`, `updated_at`, `current_user_can_bypass`, `source`), diff, open or update one issue; no automatic re-apply\n - CODEOWNERS for `.github/rulesets/` and both workflows\n\n6. Migration\n - Order: workflow fixes, rulesets active, `gh ruleset check ` verified, then delete classic protection\n - Note that both systems apply while they overlap\n\n7. Proof\n - PR with failing test: blocked; PR with no approval: blocked; author pushing after approval: approval required again\n - Direct push and force push to the default branch: rejected\n - Non-release actor creating or moving a `{{TAG_PATTERN}}` tag: rejected\n - With a queue: two PRs merge in order; a failing PR is removed; a docs-only PR still gets a green gate\n\nConstraints:\n- Do not apply rulesets or delete classic protection; output files and the commands to review.\n- Don't invent actor ids, team ids, app ids (other than 15368 for GitHub Actions), or check names; use placeholders and list what must be looked up.\n- Minimal workflow diff; do not change build logic.\n- Never give a human `always` bypass, and never let automation enqueue with `GITHUB_TOKEN`.\n- Optimize for GitHub Copilot, Claude Code, and Codex: give concrete files and a checklist.\n\nOutput format:\n1. Current-state table with gaps\n2. Check inventory table with flags\n3. Patch: workflow fixes, ruleset JSON (or Terraform), apply and drift workflows, CODEOWNERS\n4. Migration steps with the exact `gh` commands to review\n5. Proof plan with expected results\n6. Risks and follow-ups (e.g. checks to rename, bypass to remove, org-level rules to propose)\n\nStart with the export. Fix the checks before you require them, and require them before you queue.\n" } ] }