diff --git a/.github/workflows/ci-full.yml b/.github/workflows/ci-full.yml new file mode 100644 index 00000000..b94c012b --- /dev/null +++ b/.github/workflows/ci-full.yml @@ -0,0 +1,51 @@ +name: CI (full matrix) + +# Weekly compatibility run across the supported Node versions +# (engine `>=18`). PR-time CI only runs Node 20 for budget reasons; +# this catches Node 18 / 22 regressions within seven days, and can be +# triggered on-demand via workflow_dispatch before a release. + +on: + schedule: + - cron: '15 4 * * 1' + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: ci-full-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + test: + name: test (node ${{ matrix.node }}) + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + node: [18, 22] + + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Setup Node + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + with: + node-version: ${{ matrix.node }} + + - name: Install + run: npm install --ignore-scripts + + - name: Unit tests + run: npm test + + - name: Static check CLI entrypoint + run: node --check bin/multiagent-safety.js + + - name: Check scripts/ ↔ templates/scripts/ symlink parity + run: bash scripts/check-script-symlinks.sh + + - name: Package dry run + run: npm pack --dry-run diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5c0242c9..521d913d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,24 +1,40 @@ name: CI +# Budget-friendly trigger surface. Branch protection on `main` forces +# all changes through a PR, so PR-time CI is sufficient — post-merge +# `push: main` CI was pure duplication and is dropped here. Run a full +# matrix on-demand via the workflow_dispatch trigger, or weekly via +# `ci-full.yml`. on: - push: - branches: - - main pull_request: branches: - main + types: [opened, reopened, synchronize, ready_for_review] + paths-ignore: + - '**/*.md' + - 'docs/**' + - 'openspec/**' + - '.github/ISSUE_TEMPLATE/**' + - '.github/PULL_REQUEST_TEMPLATE.md' + - '.changeset/**' + workflow_dispatch: permissions: contents: read +# One in-flight run per ref. Rapid pushes to an agent PR cancel the +# prior run instead of letting both finish on Actions minutes. +concurrency: + group: ci-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: test: - name: test (node ${{ matrix.node }}) + name: test (node 20) + # Draft PRs skip CI to save minutes during in-flight agent work. + # CI auto-fires on `ready_for_review`. + if: github.event_name != 'pull_request' || github.event.pull_request.draft == false runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - node: [18, 20, 22] steps: - name: Checkout @@ -27,7 +43,7 @@ jobs: - name: Setup Node uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: - node-version: ${{ matrix.node }} + node-version: 20 - name: Install run: npm install --ignore-scripts diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index a43e694a..d13080ed 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -1,18 +1,22 @@ name: CodeQL +# CodeQL is the single most expensive workflow per run on this repo. +# Per-PR / per-push triggers were the biggest line item on the monthly +# Actions bill, so they are dropped here. The weekly schedule keeps +# security coverage; `workflow_dispatch` is the on-demand escape hatch +# for ad-hoc audits. on: - push: - branches: - - main - pull_request: - branches: - - main schedule: - cron: '35 3 * * 1' + workflow_dispatch: permissions: contents: read +concurrency: + group: codeql-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: analyze: name: Analyze (javascript-typescript) diff --git a/.github/workflows/cr.yml b/.github/workflows/cr.yml index ae8768cf..e810f503 100644 --- a/.github/workflows/cr.yml +++ b/.github/workflows/cr.yml @@ -2,14 +2,30 @@ name: Code Review on: pull_request: - types: [opened, reopened, synchronize] + types: [opened, reopened, synchronize, ready_for_review] permissions: contents: read pull-requests: write +# Code review runs OpenAI API calls — cancel superseded runs on the +# same PR so rapid pushes don't fan-out the model bill. +concurrency: + group: cr-${{ github.workflow }}-${{ github.event.pull_request.number }} + cancel-in-progress: true + jobs: review: + # Draft PRs and automated agent-lane PRs (head branch `agent/*`) + # skip review. The agent flow lands hundreds of PRs per month; AI + # review on each one burns both Actions minutes and OpenAI tokens + # without adding signal. Human-authored PRs (any non-`agent/*` + # head branch) still get reviewed; agent PRs can opt-in by adding + # a `needs-review` label, which a maintainer can re-run via + # `workflow_dispatch`-style label trigger if desired. + if: >- + github.event.pull_request.draft == false && + !startsWith(github.event.pull_request.head.ref, 'agent/') runs-on: ubuntu-latest env: OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 2f9f2200..4e17defb 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -1,11 +1,13 @@ name: OpenSSF Scorecard +# Scorecard ran on every push to main, which compounded the per-merge +# Actions bill. The weekly schedule + branch-protection-rule trigger +# keeps the OpenSSF result fresh; `workflow_dispatch` covers ad-hoc +# audits. on: branch_protection_rule: schedule: - cron: '25 3 * * 1' - push: - branches: [main] workflow_dispatch: permissions: read-all diff --git a/openspec/changes/agent-claude-budget-friendly-ci-templates-2026-05-14-00-52/.openspec.yaml b/openspec/changes/agent-claude-budget-friendly-ci-templates-2026-05-14-00-52/.openspec.yaml new file mode 100644 index 00000000..93831bd2 --- /dev/null +++ b/openspec/changes/agent-claude-budget-friendly-ci-templates-2026-05-14-00-52/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-05-13 diff --git a/openspec/changes/agent-claude-budget-friendly-ci-templates-2026-05-14-00-52/notes.md b/openspec/changes/agent-claude-budget-friendly-ci-templates-2026-05-14-00-52/notes.md new file mode 100644 index 00000000..e07e081a --- /dev/null +++ b/openspec/changes/agent-claude-budget-friendly-ci-templates-2026-05-14-00-52/notes.md @@ -0,0 +1,16 @@ +# agent-claude-budget-friendly-ci-templates-2026-05-14-00-52 (minimal / T1) + +Branch: `agent//` + +Describe the change in a sentence or two. Commit message is the spec of record. + +## Handoff + +- Handoff: change=`agent-claude-budget-friendly-ci-templates-2026-05-14-00-52`; branch=`agent//`; scope=`TODO`; action=`continue this sandbox or finish cleanup after a usage-limit/manual takeover`. +- Copy prompt: Continue `agent-claude-budget-friendly-ci-templates-2026-05-14-00-52` on branch `agent//`. Work inside the existing sandbox, review `openspec/changes/agent-claude-budget-friendly-ci-templates-2026-05-14-00-52/notes.md`, continue from the current state instead of creating a new sandbox, and when the work is done run `gx branch finish --branch agent// --base dev --via-pr --wait-for-merge --cleanup`. + +## Cleanup + +- [ ] Run: `gx branch finish --branch agent// --base dev --via-pr --wait-for-merge --cleanup` +- [ ] Record PR URL + `MERGED` state in the completion handoff. +- [ ] Confirm sandbox worktree is gone (`git worktree list`, `git branch -a`). diff --git a/openspec/changes/agent-claude-budget-friendly-ci-templates-2026-05-14-00-52/proposal.md b/openspec/changes/agent-claude-budget-friendly-ci-templates-2026-05-14-00-52/proposal.md new file mode 100644 index 00000000..1e11f531 --- /dev/null +++ b/openspec/changes/agent-claude-budget-friendly-ci-templates-2026-05-14-00-52/proposal.md @@ -0,0 +1,30 @@ +## Why + +Gitguardex agent flows (`gx branch start` lanes) land high-volume PRs per month. Every PR currently fans out across CI, CodeQL, Scorecard, and AI Code Review with no skip mechanism, which dominates the GitHub Actions bill — to the point that the org's spending limit has been hit and blocked merges on multiple repos this month. The cost lands on humans too: even non-agent PRs eat duplicate `push:main` builds after merge. + +## What Changes + +Apply a coordinated budget posture across the gitguardex repo's own workflows AND seed a `templates/github/workflows/` directory that bootstraps the same defaults into every project that uses gitguardex. + +Live workflows in this repo: + +- `ci.yml` — drop `push: main` trigger (PR-time runs cover correctness under branch protection); drop the Node 18/22 matrix from PR-time (moved to a weekly `ci-full.yml`); add `paths-ignore` for docs/openspec/changeset paths; add `concurrency: cancel-in-progress`; gate the `test` job on `pull_request.draft == false`; add `ready_for_review` to the trigger list. +- `ci-full.yml` *(new)* — weekly schedule + `workflow_dispatch` runs the full Node 18 / 22 matrix that no longer runs per-PR. +- `codeql.yml` — drop `push:main` and `pull_request` triggers; keep weekly schedule + `branch_protection_rule` + `workflow_dispatch`; add concurrency. +- `cr.yml` — add concurrency; add `ready_for_review` trigger; skip on draft PRs and on `agent/*` head branches (the largest single CR-bill cut for agent-heavy repos). +- `scorecard.yml` — drop `push:main`; keep weekly schedule + `branch_protection_rule` + `workflow_dispatch`. + +Templates seeded for downstream projects (`templates/github/workflows/`): + +- `ci.yml` — same posture as the live `ci.yml` with placeholder steps. +- `ci-full.yml` — same posture as the live `ci-full.yml` with placeholder steps. +- `cr.yml` — mirrors the live `cr.yml` (including the `agent/*` skip pattern). +- `README.md` — documents the four trims, when to keep them, and when to relax. + +## Impact + +- **Repo CI bill drops materially.** Per-PR runs are now: one CI job on a single Node version, AI review only on human PRs, no CodeQL/Scorecard per PR. Cross-version compat coverage moves to a weekly schedule. +- **Per-PR feedback loop changes**: agents iterating in draft mode get no CI feedback until they promote to ready-for-review. Trade-off accepted: agents already run `pnpm test`/`pnpm typecheck`/`pnpm lint` in their worktrees before opening PRs; the CI gate is a final check, not the inner loop. +- **No AI code review on agent PRs.** Documented as a maintainer opt-in via a `needs-review` label or by stripping the `agent/*` guard from `cr.yml`. +- **Templates make this the default for every gitguardex-managed project**, so future repos don't have to discover the same posture independently. +- **No code changes outside `.github/workflows/` and `templates/github/`.** No runtime behavior change, no test changes, no scripts touched. diff --git a/openspec/changes/agent-claude-budget-friendly-ci-templates-2026-05-14-00-52/specs/ci-workflow-budget/spec.md b/openspec/changes/agent-claude-budget-friendly-ci-templates-2026-05-14-00-52/specs/ci-workflow-budget/spec.md new file mode 100644 index 00000000..5324249c --- /dev/null +++ b/openspec/changes/agent-claude-budget-friendly-ci-templates-2026-05-14-00-52/specs/ci-workflow-budget/spec.md @@ -0,0 +1,37 @@ +## ADDED Requirements + +### Requirement: Live workflows carry a budget posture +Every GitHub Actions workflow in `.github/workflows/` of this repo SHALL: + +- declare `concurrency:` with a per-ref group name and `cancel-in-progress: true`, **OR** be explicitly exempted with an inline comment explaining why superseded runs must not cancel (e.g. release publication on a tag), +- omit the `push: branches: [main]` trigger when the workflow's purpose is "validate before merge" (CI, CodeQL, Scorecard), since branch protection forces all changes through a PR and post-merge re-runs are duplication, +- include `ready_for_review` in `pull_request.types` when the workflow's per-PR job is gated by `pull_request.draft == false`. + +#### Scenario: ci.yml gated on draft +- **GIVEN** the live `.github/workflows/ci.yml` +- **WHEN** parsed by a YAML loader +- **THEN** it MUST declare `concurrency: cancel-in-progress: true` +- **AND** its `test` job MUST set `if: github.event_name != 'pull_request' || github.event.pull_request.draft == false` +- **AND** its `on.pull_request.types` MUST contain `ready_for_review` +- **AND** it MUST NOT declare a `push: branches: [main]` trigger. + +#### Scenario: codeql.yml runs on schedule, not per-PR +- **GIVEN** the live `.github/workflows/codeql.yml` +- **WHEN** parsed by a YAML loader +- **THEN** its `on` block MUST contain a `schedule:` entry +- **AND** its `on` block MUST contain `workflow_dispatch:` +- **AND** its `on` block MUST NOT contain a `pull_request:` or `push:` trigger. + +#### Scenario: cr.yml skips agent/* head branches +- **GIVEN** the live `.github/workflows/cr.yml` +- **WHEN** parsed by a YAML loader +- **THEN** its `review` job's `if:` expression MUST include `!startsWith(github.event.pull_request.head.ref, 'agent/')`. + +### Requirement: Templates seed the same posture in downstream projects +The `templates/github/workflows/` directory SHALL carry workflow files that bootstrap the same budget posture into a downstream gitguardex-managed project. + +#### Scenario: Templates exist and parse +- **GIVEN** the `templates/github/workflows/` directory in this repo +- **THEN** it MUST contain `ci.yml`, `ci-full.yml`, `cr.yml`, and `README.md` +- **AND** each `.yml` file MUST parse cleanly with a standard YAML loader +- **AND** each `.yml` template MUST carry the same `concurrency:` + `if: draft == false` (or equivalent agent-skip) posture as the live file it mirrors. diff --git a/openspec/changes/agent-claude-budget-friendly-ci-templates-2026-05-14-00-52/tasks.md b/openspec/changes/agent-claude-budget-friendly-ci-templates-2026-05-14-00-52/tasks.md new file mode 100644 index 00000000..9988cf4b --- /dev/null +++ b/openspec/changes/agent-claude-budget-friendly-ci-templates-2026-05-14-00-52/tasks.md @@ -0,0 +1,34 @@ +## Definition of Done + +This change is complete only when **all** of the following are true: + +- Every checkbox below is checked. +- The agent branch reaches `MERGED` state on `origin` and the PR URL + state are recorded in the completion handoff. +- If any step blocks (test failure, conflict, ambiguous result), append a `BLOCKED:` line under section 4 explaining the blocker and **STOP**. Do not tick remaining cleanup boxes; do not silently skip the cleanup pipeline. + +## Handoff + +- Handoff: change=`agent-claude-budget-friendly-ci-templates-2026-05-14-00-52`; branch=`agent/claude/budget-friendly-ci-templates-2026-05-14-00-52`; scope=`TODO`; action=`continue this sandbox or finish cleanup after a usage-limit/manual takeover`. +- Copy prompt: Continue `agent-claude-budget-friendly-ci-templates-2026-05-14-00-52` on branch `agent/claude/budget-friendly-ci-templates-2026-05-14-00-52`. Work inside the existing sandbox, review `openspec/changes/agent-claude-budget-friendly-ci-templates-2026-05-14-00-52/tasks.md`, continue from the current state instead of creating a new sandbox, and when the work is done run `gx branch finish --branch agent/claude/budget-friendly-ci-templates-2026-05-14-00-52 --base main --via-pr --wait-for-merge --cleanup`. + +## 1. Specification + +- [x] 1.1 Finalize proposal scope and acceptance criteria — see `proposal.md`. +- [x] 1.2 Define normative requirements in `specs/ci-workflow-budget/spec.md`. + +## 2. Implementation + +- [x] 2.1 Trim live workflows: `ci.yml`, `ci-full.yml` (new), `codeql.yml`, `cr.yml`, `scorecard.yml`. +- [x] 2.2 Seed `templates/github/workflows/` with `ci.yml`, `ci-full.yml`, `cr.yml`, and `README.md` carrying the same budget posture. + +## 3. Verification + +- [x] 3.1 `npm test` green; `bash scripts/check-script-symlinks.sh` green. +- [x] 3.2 `js-yaml` parse over all 8 modified/new workflow files. +- [ ] 3.3 Run `openspec validate agent-claude-budget-friendly-ci-templates-2026-05-14-00-52 --type change --strict`. + +## 4. Cleanup (mandatory; run before claiming completion) + +- [ ] 4.1 Run the cleanup pipeline: `gx branch finish --branch agent/claude/budget-friendly-ci-templates-2026-05-14-00-52 --base main --via-pr --wait-for-merge --cleanup`. This handles commit -> push -> PR create -> merge wait -> worktree prune in one invocation. +- [ ] 4.2 Record the PR URL and final merge state (`MERGED`) in the completion handoff. +- [ ] 4.3 Confirm the sandbox worktree is gone (`git worktree list` no longer shows the agent path; `git branch -a` shows no surviving local/remote refs for the branch). diff --git a/templates/github/workflows/README.md b/templates/github/workflows/README.md new file mode 100644 index 00000000..a132f6e1 --- /dev/null +++ b/templates/github/workflows/README.md @@ -0,0 +1,67 @@ +# `templates/github/workflows/` — budget-friendly CI defaults + +Workflow files in this directory are copied into a gitguardex-managed +project's `.github/workflows/` directory when bootstrapping. They are +the **default** budget posture for projects that use `gx branch start` +to drive agent iterations. + +Agent flows land a high volume of PRs per month. Without these trims, +every PR + every post-merge push fans out across CI, CodeQL, Scorecard, +and Code Review — which dominates the GitHub Actions bill for any +multi-agent repo. The trims below cut that cost without giving up +correctness coverage. + +## What's trimmed and why + +1. **`concurrency: cancel-in-progress: true`** scoped per workflow + ref + so rapid pushes to the same agent branch cancel the prior run + instead of letting both finish on Actions minutes. + +2. **`if: github.event.pull_request.draft == false`** on every job that + shouldn't run on a draft PR, paired with + `pull_request.types: [..., ready_for_review]` in the trigger list so + CI fires the moment the PR is promoted out of draft. + +3. **`if: !startsWith(head.ref, 'agent/')`** on the Code Review job + (`cr.yml`) — skip AI review on automated agent-lane PRs. AI review + on hundreds of agent PRs per month burns both Actions minutes and + OpenAI tokens without adding signal; human-authored PRs (any non- + `agent/*` head branch) still get reviewed. + +4. **No `push: main` trigger** in `ci.yml` — branch protection on + `main` forces all changes through a PR, so PR-time CI is sufficient + and post-merge CI on `main` was pure duplication. Use + `workflow_dispatch` for ad-hoc full runs. + +5. **`paths-ignore`** for docs / openspec / template-only changes — skip + CI on changes that don't affect runtime behavior. + +## Customizing + +- Replace `placeholder` steps in `ci.yml` with your build/test/lint + commands. +- Keep the `concurrency:`, `if:`, and `paths-ignore:` patterns. They + are the load-bearing part of the budget posture; removing them undoes + the win. + +## When to skip the draft-skip pattern + +If your CI is fast (≤ 2 min) and you want continuous validation as +agents iterate, drop the `if: pull_request.draft == false` job guard. +The concurrency cancel alone still prevents minute pile-up. + +## When to re-enable AI code review on agent PRs + +If your team relies on AI review as a true gating signal (not just +advisory), remove the `!startsWith(head.ref, 'agent/')` guard in +`cr.yml`. Expect the OpenAI bill to scale linearly with merge volume. + +## What about CodeQL / Scorecard? + +The gitguardex repo itself runs CodeQL and Scorecard on the **weekly +schedule + `workflow_dispatch`** only — not on per-PR / per-push +triggers. Those workflows are long-running (5–10 min for CodeQL) and +were the largest single line item on the monthly Actions bill before +this change. If your project needs per-PR CodeQL gating for compliance +reasons, re-add the `pull_request` trigger and accept the cost; for +most repos, weekly + on-demand is the right default. diff --git a/templates/github/workflows/ci-full.yml b/templates/github/workflows/ci-full.yml new file mode 100644 index 00000000..2b52042c --- /dev/null +++ b/templates/github/workflows/ci-full.yml @@ -0,0 +1,46 @@ +# Optional companion to `ci.yml`. Drop in alongside it when your +# project supports multiple runtimes / OS combinations and you want +# coverage across all of them without paying per-PR. +# +# Strategy: PR-time `ci.yml` runs the primary runtime only (cheap). +# This workflow runs the full matrix on the weekly schedule, and +# on-demand via `workflow_dispatch` before a release. +# +# Customize the matrix rows below to match your supported runtimes. + +name: CI (full matrix) + +on: + schedule: + - cron: '15 4 * * 1' + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: ci-full-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + test: + name: test (node ${{ matrix.node }}) + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + node: [18, 22] + + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Setup Node + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + with: + node-version: ${{ matrix.node }} + + # Replace below with your build/test/lint steps. Keep them parallel + # to `ci.yml` so the weekly matrix matches what runs per-PR. + - name: Project verification placeholder + run: echo "Replace this step with your build/test/lint commands." diff --git a/templates/github/workflows/ci.yml b/templates/github/workflows/ci.yml new file mode 100644 index 00000000..daf256fc --- /dev/null +++ b/templates/github/workflows/ci.yml @@ -0,0 +1,56 @@ +# Budget-friendly CI default for gitguardex-managed projects. +# +# Four trims keep Actions minutes low while agent branches iterate: +# 1. `concurrency: cancel-in-progress` — rapid pushes to the same ref +# kill the prior run instead of letting both finish. +# 2. Job-level `if: pull_request.draft == false` plus the +# `ready_for_review` PR trigger — draft PRs skip CI, and CI fires +# automatically the moment the PR is promoted out of draft. +# 3. `paths-ignore` for docs / openspec / template-only changes — +# skip CI on changes that don't affect runtime behavior. +# 4. No `push: main` trigger — branch-protection-required PR runs +# already cover correctness, and post-merge CI on main is pure +# duplication. Use `workflow_dispatch` for ad-hoc full runs. +# +# Copy this file to `.github/workflows/ci.yml` in your project and +# replace the placeholder `steps:` block with your build/test/lint +# commands. + +name: CI + +on: + pull_request: + branches: + - main + types: [opened, reopened, synchronize, ready_for_review] + paths-ignore: + - '**/*.md' + - 'docs/**' + - 'openspec/**' + - '.github/ISSUE_TEMPLATE/**' + - '.github/PULL_REQUEST_TEMPLATE.md' + - '.changeset/**' + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: ci-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + build: + if: github.event_name != 'pull_request' || github.event.pull_request.draft == false + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + # Replace below with your build/test/lint steps. Examples: + # - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + # with: { node-version: '20' } + # - run: npm ci + # - run: npm test + - name: Project verification placeholder + run: echo "Replace this step with your build/test/lint commands." diff --git a/templates/github/workflows/cr.yml b/templates/github/workflows/cr.yml index ae8768cf..4aa6c813 100644 --- a/templates/github/workflows/cr.yml +++ b/templates/github/workflows/cr.yml @@ -2,14 +2,28 @@ name: Code Review on: pull_request: - types: [opened, reopened, synchronize] + types: [opened, reopened, synchronize, ready_for_review] permissions: contents: read pull-requests: write +# Budget-friendly default for gitguardex-managed projects: cancel +# superseded runs on the same PR so rapid agent pushes don't fan-out +# the OpenAI bill. +concurrency: + group: cr-${{ github.workflow }}-${{ github.event.pull_request.number }} + cancel-in-progress: true + jobs: review: + # Skip on draft PRs and on `agent/*` head branches. Gitguardex + # agent flows land high-volume PRs that don't benefit from AI + # review; skipping them is the single largest CR-bill cut. + # Review fires on `ready_for_review` automatically for human PRs. + if: >- + github.event.pull_request.draft == false && + !startsWith(github.event.pull_request.head.ref, 'agent/') runs-on: ubuntu-latest env: OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}