From 14151bab4fee5fed2d432a301a0c9dd93b0c4a78 Mon Sep 17 00:00:00 2001 From: Jonathan Zhang Date: Fri, 1 May 2026 10:03:34 -0700 Subject: [PATCH 1/2] ci: add risk classifier + Codex review gate for sensitive paths --- .github/risk-paths.yml | 95 +++++++++++++++++++++++++++ .github/workflows/pr-codex-review.yml | 30 +++++++++ 2 files changed, 125 insertions(+) create mode 100644 .github/risk-paths.yml create mode 100644 .github/workflows/pr-codex-review.yml diff --git a/.github/risk-paths.yml b/.github/risk-paths.yml new file mode 100644 index 0000000..6a23a29 --- /dev/null +++ b/.github/risk-paths.yml @@ -0,0 +1,95 @@ +# Risk classification rules for the path classifier. +# +# Consumed by topcoder1/ci-workflows/.github/workflows/pr-classify.yml. The +# classifier resolves to the HIGHEST class matched by any changed file: +# blocked > sensitive > standard > safe_test > safe_deps > safe_config > trivial +# +# Files matching no pattern fall through to `standard` (gets normal Claude +# review pipeline, no Codex). `sensitive` triggers an extra Codex review for +# vendor diversity on high-impact code paths. +# +# === CUSTOMIZE THE `sensitive:` LIST FOR THIS REPO === +# The starter list below catches generic high-risk patterns. Add the +# repo-specific paths where bugs actually hurt: api, db, schema, scanning, +# auth, billing, customer-facing classification logic, etc. +# +# When adding a sensitive path, also add it to .github/CODEOWNERS so the +# CODEOWNER human-review gate matches the auto-merge gate. Drift between +# the two files defeats the purpose. + +blocked: + - 'Dockerfile*' + - 'docker-compose*.yaml' + - 'docker-compose*.yml' + - '**/.env*' + - '**/secrets*' + - '.github/workflows/**' + - '.github/risk-paths.yml' + - '.github/CODEOWNERS' + - 'infra/**' + - 'terraform/**' + - 'k8s/**' + +sensitive: + # CUSTOMIZE: paths where bugs cause customer-visible regressions, data + # loss, security exposure, or production outage. Examples below — adjust + # for the repo's actual layout. + # + # - 'src/**/api/**' # public API surface + # - 'src/**/db/**' # schema, models, migrations + # - 'migrations/**' + # - 'alembic/**' + # - 'src/**/auth/**' # authn / authz + # - 'src/**/billing/**' # payment / pricing + # - 'src/**/scanning/**' # production runners + # - 'scripts/rotate_*.py' # credential rotation + +# safe_test: failing tests can't ship; passing tests can't break runtime. +safe_test: + - 'tests/**' + - '**/test_*.py' + - '**/*_test.py' + - '**/*.test.ts' + - '**/*.test.js' + - '**/*.spec.ts' + - '**/*.spec.js' + - '**/__tests__/**' + +# safe_deps: dependency manifests. Dependabot patch+minor are auto-merged +# separately; humans editing manifests may add/remove deps semantically +# but those still get the standard Claude review pipeline. +safe_deps: + - 'pyproject.toml' + - 'uv.lock' + - 'requirements*.txt' + - 'package.json' + - 'package-lock.json' + - 'pnpm-lock.yaml' + - 'go.mod' + - 'go.sum' + - 'Cargo.toml' + - 'Cargo.lock' + - 'Gemfile' + - 'Gemfile.lock' + +# safe_config: tooling only; failures surface in lint/check immediately. +safe_config: + - 'ruff.toml' + - '.ruff.toml' + - 'pre-commit*.yaml' + - '.pre-commit-config.yaml' + - '.editorconfig' + - '.vscode/**' + - 'mypy.ini' + - 'pytest.ini' + - 'eslint.config.*' + - 'prettier.*' + - '.prettierrc*' + +# trivial: zero runtime impact. +trivial: + - '**/*.md' + - 'LICENSE' + - '.gitignore' + - '.github/ISSUE_TEMPLATE/**' + - 'docs/**' diff --git a/.github/workflows/pr-codex-review.yml b/.github/workflows/pr-codex-review.yml new file mode 100644 index 0000000..798dffc --- /dev/null +++ b/.github/workflows/pr-codex-review.yml @@ -0,0 +1,30 @@ +name: PR Codex Review + +# OpenAI Codex CLI as a third-party reviewer for vendor diversity. +# Runs ONLY on risk:sensitive PRs (per .github/risk-paths.yml). Pairs with +# Claude Review (which runs on every PR via pr-review.yml) to give +# independent-model coverage on high-impact code paths. + +on: + pull_request: + types: [opened, synchronize] + +permissions: + contents: read + pull-requests: write + +concurrency: + group: codex-${{ github.event.pull_request.number }} + cancel-in-progress: true + +jobs: + classify: + if: github.event.pull_request.draft == false + uses: topcoder1/ci-workflows/.github/workflows/pr-classify.yml@main + + review: + needs: classify + if: needs.classify.outputs.risk_class == 'sensitive' + uses: topcoder1/ci-workflows/.github/workflows/codex-review.yml@main + secrets: + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} From c1446e38a68f4999e7f7b73041e05031492d7fe6 Mon Sep 17 00:00:00 2001 From: Jonathan Zhang Date: Fri, 1 May 2026 10:04:06 -0700 Subject: [PATCH 2/2] =?UTF-8?q?ci-workflows:=20populate=20sensitive:=20lis?= =?UTF-8?q?t=20=E2=80=94=20classifier=20+=20scripts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/risk-paths.yml | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/.github/risk-paths.yml b/.github/risk-paths.yml index 6a23a29..4590f4f 100644 --- a/.github/risk-paths.yml +++ b/.github/risk-paths.yml @@ -31,18 +31,14 @@ blocked: - 'k8s/**' sensitive: - # CUSTOMIZE: paths where bugs cause customer-visible regressions, data - # loss, security exposure, or production outage. Examples below — adjust - # for the repo's actual layout. - # - # - 'src/**/api/**' # public API surface - # - 'src/**/db/**' # schema, models, migrations - # - 'migrations/**' - # - 'alembic/**' - # - 'src/**/auth/**' # authn / authz - # - 'src/**/billing/**' # payment / pricing - # - 'src/**/scanning/**' # production runners - # - 'scripts/rotate_*.py' # credential rotation + # The classifier itself — a bug here breaks gating across the entire fleet. + - '.github/scripts/classify.mjs' + # Reusable workflows that downstream repos depend on. (workflows are in the + # `blocked:` class above so this is mostly belt-and-suspenders, but if + # someone moves a file out of .github/workflows/ this catches it.) + - '.github/scripts/**' + # Installer scripts — same fleet-wide blast radius as classify.mjs + - 'scripts/**' # safe_test: failing tests can't ship; passing tests can't break runtime. safe_test: