ci: run CI and the PR title check for the merge queue (#867)

* ci: run CI and the PR title check for the merge queue

A merge queue tests each queued change on top of the ones ahead of it and
waits for the required checks, CI result and PR title, on that merge
group. Both workflows now run on merge_group, reading the base and head
from either event. The title was checked on the pull request, so the
queue run reports success without a title to read.

* docs: explain stacked pull requests and the merge queue

Agents had to be told what a stacked pull request is each time.
CONTRIBUTING.md now covers stacks with gh stack, keeping them linear,
and landing them through the merge queue; AGENTS.md links to it, and
tools/AGENTS.md records that required checks must run on merge_group.

* docs: shorten the stacked pull request and merge queue guidance

The AGENTS.md line loads into every agent context, so keep it to the
rules; CONTRIBUTING.md keeps the commands.
This commit is contained in:
Danila Poyarkov 2026-10-04 13:51:51 +04:00 committed by GitHub
parent f55b887d2b
commit b2499d8342
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 20 additions and 5 deletions

View file

@ -3,6 +3,8 @@ name: CI
on:
# Every base, so stacked pull requests are checked against the PR below them.
pull_request:
# The merge queue checks each queued change on top of the ones ahead of it.
merge_group:
permissions:
contents: read
@ -28,13 +30,13 @@ jobs:
- name: Fetch comparison base
env:
CI_BASE_SHA: ${{ github.event.pull_request.base.sha }}
CI_BASE_SHA: ${{ github.event.pull_request.base.sha || github.event.merge_group.base_sha }}
run: git fetch --no-tags --depth=1 origin "$CI_BASE_SHA"
- name: Select validation scope
id: classify
env:
CI_BASE_SHA: ${{ github.event.pull_request.base.sha }}
CI_BASE_SHA: ${{ github.event.pull_request.base.sha || github.event.merge_group.base_sha }}
run: bun tools/ci/policy/src/classify.ts
commit-messages:
@ -49,8 +51,8 @@ jobs:
- uses: ./.github/actions/setup-bun
- name: Validate PR commit messages
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
BASE_SHA: ${{ github.event.pull_request.base.sha || github.event.merge_group.base_sha }}
HEAD_SHA: ${{ github.event.pull_request.head.sha || github.event.merge_group.head_sha }}
run: |
if ! bun run check:commits --from "$BASE_SHA" --to "$HEAD_SHA" --verbose; then
echo '::error title=Commit messages::Check the messages listed above. Use type(scope): description, for example fix: preserve selection. See CONTRIBUTING.md#commit-messages.'

View file

@ -4,12 +4,14 @@ on:
# Every base, so stacked pull requests are checked against the PR below them.
pull_request:
types: [opened, synchronize, reopened, edited]
# A queued change was checked as a pull request; the queue still needs the result.
merge_group:
permissions:
contents: read
concurrency:
group: pr-title-${{ github.event.pull_request.number }}
group: pr-title-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
@ -23,6 +25,7 @@ jobs:
persist-credentials: false
- uses: ./.github/actions/setup-bun
- name: Validate PR title
if: github.event_name == 'pull_request'
env:
PR_TITLE: ${{ github.event.pull_request.title }}
COMMITLINT_PR_TITLE: '1'

View file

@ -100,6 +100,7 @@ Follow `packages/docs/development/testing.md` and `tests/AGENTS.md`. Package-loc
- Conventional Commits (`feat`, `fix`, `refactor`, `perf`, `docs`, `test`, `build`, `ci`, `chore`); short imperative subjects, rationale in the body; release commits are exactly `Release vX.Y.Z`. Preserve product casing: DOM/CSS, HTML, JSX, Tailwind, Kiwi, `.fig`, MCP, CLI, AI, ACP, i18n. Validate with `bun run check:commits --last` or `--from`/`--to`.
- PR titles are Conventional Commits because GitHub uses them as merge subjects; branch-update merges get explicit subjects such as `chore: merge master into <branch>`. Do not rewrite published history solely to normalize messages.
- Disclose AI assistance in the PR's AI assistance section, never as commit authorship, `Co-authored-by` trailers, promotional signatures, or session links. Preserve human co-author credits and third-party notices.
- Stacked pull requests: `gh stack`, rebase never merge, land from the top layer via the merge queue (`CONTRIBUTING.md#stacked-pull-requests`).
- Issues, PR descriptions, and public comments use concise concrete technical prose: lead with the problem and outcome, add a short example when needed, avoid filler, promotional claims, decorative emojis, unnecessary tables, and file-by-file inventories. Link long logs or design notes.
## Code review

View file

@ -66,6 +66,14 @@ Pull requests must be reviewable without guessing the author's intent.
- Complete the AI assistance section. If an LLM materially helped create or modify the PR, list the model names you know. Write `None` otherwise. This is review context, not authorship attribution; prompts and transcripts are not required.
- Keep the body primarily in English. Code identifiers, file paths, logs, error messages, and short quoted examples may use their original language.
### Stacked pull requests
A stack is a chain of pull requests: the bottom one targets `master`, each one above targets the branch below. CI and branch protection treat every layer as targeting `master`.
- Manage stacks with [`gh stack`](https://github.com/github/gh-stack) (`init`, `add`, `submit`, `sync`; add `--remote origin` with several remotes); a stacked pull request's base cannot be edited.
- Keep it linear: rebase the layers when `master` or a lower branch moves; never merge `master` in.
- Merge from the top layer through the merge queue; it lands every layer below too. Auto-merge is unavailable for stacks.
### Reviewability
Do not submit placeholder PRs. Remove template comments before opening a PR. Do not leave dangling issue references such as `Fixes #`, `TODO`, `TBD`, empty headings, unfilled sections, or similar unfinished text.

View file

@ -20,6 +20,7 @@ Private tooling lives under `tools/<role>/<domain>/{src,tests}`; Steiger enforce
- `.github/workflows/ci.yml` and `heavy-tests.yml` define validation gates. PR CI always classifies changed paths through `tools/ci/policy/src/policy.ts`: root docs, package READMEs, every `AGENTS.md`, `packages/docs` Markdown and assets, and skill Markdown are docs-only; runtime prompt Markdown, executable examples, configuration, and unknown paths require code validation.
- Docs-only changes run documentation integrity and the docs build; everything else runs the full suites. The aggregate `CI result` gate requires successful classification and every applicable job; failures, cancellations, and unexpected skips cannot pass. Do not restore workflow-level path filtering on required CI.
- `commitlint.config.ts` enforces commit structure in the **Commit messages** job; the separate **PR title** workflow validates titles. Preserve the `Release vX.Y.Z` exception and product casing when changing rules; the known AI co-author check does not rewrite base history. Gate policy lives in `tools/ci/policy/src/policy.ts`.
- Required checks must also run on `merge_group`, the merge queue's event; read base and head from `merge_group.base_sha`/`head_sha` there (`ci.yml`, `pr-title.yml`).
- App and docs production workflows run on `v*` tags or `workflow_dispatch`, not ordinary `master` pushes. `build.yml` checks the tooling out under `.pipeline/` and runs `tools/release/release-packages` from there.
## Releases