diff --git a/.github/workflows/pr-codex-review.yml b/.github/workflows/pr-codex-review.yml index c2e77d6..e280eb5 100644 --- a/.github/workflows/pr-codex-review.yml +++ b/.github/workflows/pr-codex-review.yml @@ -5,9 +5,13 @@ name: PR Codex Review # Claude Review (which runs on every PR via pr-review.yml) to give # independent-model coverage on high-impact code paths. +# ready_for_review is required in the type list: every draft-phase run +# (opened, synchronize) skips on the draft gate below, so without it a PR +# opened as draft and later marked ready never gets Codex review — the +# ready click fires no run, and only an incidental post-ready push would. on: pull_request: - types: [opened, synchronize, reopened] + types: [opened, synchronize, reopened, ready_for_review] # Skip the entire Codex track on bootstrap-paths. The classifier labels # these risk:blocked, and this caller only reviews risk:sensitive — so # running it would burn a classify job just to produce a skipped review. @@ -29,8 +33,18 @@ permissions: contents: read pull-requests: write +# The group key partitions runs by the payload's draft flag, not just +# per-PR. A `synchronize` run whose payload was snapshotted while the PR +# was still draft is a guaranteed no-op (the draft gate below), yet in a +# shared group it can cancel — or, if queued, replace regardless of +# cancel-in-progress — the ready_for_review run, so a sensitive/blocked +# PR taken draft → ready would still merge with zero Codex review (same +# race as pr-classify.yml, attaxion_dev#303, 2026-07-06). +# Quarantining draft-payload runs in their own group fixes that while +# non-draft runs keep today's supersede semantics: a newer push still +# cancels an in-flight stale run before it can review pre-push state. concurrency: - group: codex-${{ github.event.pull_request.number }} + group: codex-${{ github.event.pull_request.number }}-${{ github.event.pull_request.draft && 'draft' || 'live' }} cancel-in-progress: true jobs: