## Problem
The standard review silently does not run on PRs that get labelled by a
bot right after opening. https://github.com/twentyhq/twenty/pull/23854
is an example: no `PR Review #23854` run exists in `ci-privileged` at
all.
| time | what |
|---|---|
| 10:08:35 | PR opened |
| 10:08:39 | `twenty-eng-sync[bot]` adds the `-PR: draft` label |
| 10:08:40 | dispatch run for `opened` starts, cancelled during "Set up
job" |
| 10:08:43 | dispatch run for `labeled` is skipped by the job `if` |
Concurrency is evaluated before the job-level `if`, so the `labeled` run
preempts and cancels the in-flight `opened` run and is then skipped
itself (`-PR: draft` does not start with `pr-review-`). The sync bot
labels within ~4 seconds of open, which is faster than the app-token
mint step, so the `opened` dispatch loses this race essentially every
time that label is applied.
`opened` is the only event that resolves to the `standard` check, so
with no later push the PR gets no review at all. Same class of gap as
the one #23708 closed, moved down a layer: the trigger exists now but
gets cancelled.
## Fix
Scope the concurrency group by event action, and only cancel in-progress
runs for `synchronize`. Rapid consecutive pushes still de-duplicate;
`opened`, `ready_for_review` and `labeled` no longer cancel each other.
<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/23856?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
## Why
A PR opened directly as non-draft (the normal member flow: push branch →
`gh pr create`) fires no dispatcher trigger — the initial commits
arrived before the PR existed, so they're an `opened` event, not
`synchronize`. With no `opened` trigger, such a PR gets **no review at
all** unless it's later pushed to or manually labelled. This is live
today: #23697 and #23707 are core-team PRs sitting with the bot's `-PR:
draft` label but zero "PR Review" status.
## Change
Add `opened` back to the dispatcher, and forward the triggering PR event
to the orchestrator:
```yaml
types: [opened, ready_for_review, synchronize, labeled]
# ...
-f pr_number="$PR_NUMBER" -f event="$EVENT"
```
The orchestrator (twentyhq/ci-privileged#65) maps **`opened` → standard
review only**; `security` + `triage` stay on pushes / ready-for-review.
So opening a PR gives core-team authors the standard (architectural)
review early, without firing the full gate on open, and the "opened and
never pushed again" hole is closed.
No author-role logic lives here — the dispatcher just forwards
`pr_number` + `event`; all who-gets-what policy is resolved in the
orchestrator.
## Merge order
Depends on **twentyhq/ci-privileged#65** (adds the `event` input). Merge
that first — it's backward-compatible (empty `event` = today's auto-gate
behaviour), so nothing breaks in between.
<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/23708?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
Addresses the review feedback on #23418 (Paul + Copilot + cubic) and
switches the manual trigger from comments to **labels**, consistent with
the e2e labels.
## What changed
- **Manual reviews are label-driven** — add `pr-review-security` /
`pr-review-triage` / `pr-review-standard`. Labeling requires write
access (team-only). The **`/pr-review` comment trigger is removed.**
- Like the e2e labels, a check runs on every push **while its label is
present** (the orchestrator reads the PR's current labels each run) — so
`pr-review-standard` keeps the deep review current until removed.
- **Dispatcher is now dumb** — it forwards only `pr_number`. All
resolution + validation lives in the privileged orchestrator (Paul's
suggestion: it fetches PR metadata, incl. labels, there anyway). This
fixes the bot findings (regex allowlist bypass, `/pr-review`→standard
default, delimiter edge cases) at the source.
- `cancel-in-progress: true` (latest-push-wins, matching the previous
dispatcher).
Fires on non-draft PR events (the auto `security,triage` gate) and on
`pr-review-*` label adds.
## Depends on
A companion change to the privileged CI (reads labels +
resolves/validates checks) — merge that first; it's backward-compatible,
so nothing breaks in between.