diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2274e3987..28752e7a8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -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.' diff --git a/.github/workflows/pr-title.yml b/.github/workflows/pr-title.yml index 8c893a608..8538074b9 100644 --- a/.github/workflows/pr-title.yml +++ b/.github/workflows/pr-title.yml @@ -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' diff --git a/AGENTS.md b/AGENTS.md index e253446eb..1e1a9b11e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -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 `. 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 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4271c6694..736eddebc 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -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. diff --git a/tools/AGENTS.md b/tools/AGENTS.md index 03451932d..3299e9114 100644 --- a/tools/AGENTS.md +++ b/tools/AGENTS.md @@ -20,6 +20,7 @@ Private tooling lives under `tools///{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