From 08e09217bffce25eb444fd1a67fd0ada69aec1a3 Mon Sep 17 00:00:00 2001 From: Tianyi Cui <53024+tianyicui@users.noreply.github.com> Date: Mon, 6 Jul 2026 01:04:06 +0800 Subject: [PATCH] docs(rfc): record parallel GitHub CI gates --- .github/workflows/ci.yml | 2 ++ docs/rfc/INDEX.md | 1 + .../2026-07-06-parallel-github-ci-gates.md | 32 +++++++++++++++++++ 3 files changed, 35 insertions(+) create mode 100644 docs/rfc/implemented/process/2026-07-06-parallel-github-ci-gates.md diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f8b7d4f41b..813fa71234 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -105,6 +105,8 @@ jobs: command: pnpm run verify-mermaid - name: rfc classification command: pnpm run verify-rfc-classification + - name: rfc format + command: pnpm run verify-rfc-format - name: type equivalence command: pnpm run verify-type-equiv - name: translation pairing diff --git a/docs/rfc/INDEX.md b/docs/rfc/INDEX.md index 0c5a11a4f9..4c57c28475 100644 --- a/docs/rfc/INDEX.md +++ b/docs/rfc/INDEX.md @@ -141,6 +141,7 @@ Generated by `pnpm run gen-rfc-index` from the RFC tree — never edit by hand; | [Generate the RFC index tables](implemented/process/2026-07-04-generate-rfc-index-tables.md) | 2026-07-04 | | [Generated persistence log event catalog](implemented/process/2026-07-04-persistence-log-catalog.md) | 2026-07-04 | | [One gated in-file format for RFCs](implemented/process/2026-07-05-uniform-rfc-format.md) | 2026-07-05 | +| [Parallel GitHub CI gates](implemented/process/2026-07-06-parallel-github-ci-gates.md) | 2026-07-06 | ### Testing diff --git a/docs/rfc/implemented/process/2026-07-06-parallel-github-ci-gates.md b/docs/rfc/implemented/process/2026-07-06-parallel-github-ci-gates.md new file mode 100644 index 0000000000..3c3be0782f --- /dev/null +++ b/docs/rfc/implemented/process/2026-07-06-parallel-github-ci-gates.md @@ -0,0 +1,32 @@ +# RFC: Parallel GitHub CI gates + +Status: implemented + +## Problem + +The keyless GitHub CI gates are mostly orthogonal: typecheck, lint, documentation freshness, coverage, snapshot replay, build, package-publication hygiene, demo smoke, and built-bin smoke fail for different reasons and do not need each other's runtime state. Running them as one ordered job makes the workflow wall clock equal the sum of those gates, and running the whole chain on multiple Node versions spends the same expensive repo-wide signal twice when the compatibility question is narrower than the full quality suite. + +The hard part is the artifact boundary. `publint`, `verify-node-next-types`, and built-bin smoke tests need the built `lib/` outputs, while most gates only need source and dependencies. A blind fan-out either races those artifact consumers before `pnpm run build` has emitted declarations and bundles, or repeats the build in every artifact-dependent job. + +## Decision + +[CI](../../../../.github/workflows/ci.yml) uses Node 24 as the primary quality lane and fans out independent source-only work into matrix jobs. The `quality` matrix runs constraints, typecheck, lint, coverage, snapshot replay, and the echo-agent demo smoke independently. The `docs` matrix expands the `doc-sync` members into leaf jobs, including the RFC classification and format gates, so documentation failures report at the gate that failed instead of hiding behind one aggregate step. + +Build output is produced once by a `build` job and uploaded as a short-retention artifact. Artifact consumers run behind that boundary: the `artifact-gate` matrix downloads the built package tree and runs `pnpm run hygiene` plus the built-bin smoke tests. Node-version compatibility stays explicit but narrower: the Node 26 lane runs typecheck and unit tests, while the full quality/documentation/artifact surface runs on the package engine floor. + +Both CI workflows cache the pnpm store after enabling Corepack. The real-API e2e workflow keeps its serial test execution because the e2e config deliberately disables file parallelism for API-quota stability; its speedup is dependency-cache reuse, not concurrent model calls. + +## Alternatives considered + +- **Keep the full serial chain in a Node matrix** - simplest to reason about, but it duplicates repo-wide gates that do not produce Node-version-specific signal and leaves every PR waiting for the sum of all gates. +- **Run every gate independently with no build artifact handoff** - maximizes fan-out, but the publication and built-bin checks are defined over built `lib/` outputs and would either fail, skip, or rebuild the same tree in several jobs. +- **Build inside every artifact-dependent job** - preserves correctness but shifts the bottleneck from the serial chain to repeated `tsc -b` and bundling work. +- **Parallelize real-API e2e files** - rejected because the e2e suite's Vitest config uses `fileParallelism: false` to stay within shared API-key quota and avoid rate-limit flakes. + +## Consequences + +PR feedback arrives as many smaller checks rather than one large status. That makes failures easier to localize and lets independent gates finish as soon as their own runner is done, at the cost of more GitHub job setup overhead and a larger workflow file. + +The split introduces a maintenance obligation: when `package.json` adds or removes a `doc-sync` member, the docs matrix needs the matching leaf job. That obligation is intentional because CI is now the parallel execution plan for the same gate vocabulary, not a separate quality policy. + +The Node 26 signal is narrower than the primary Node 24 signal. It proves the source graph and unit suite on the newer runtime without doubling documentation, coverage, publication, and smoke checks whose failures are not expected to vary by Node minor version.