data-client / rules
reactive/data-client/.cursor/rules/ci-config.mdc
CircleCI and GitHub Actions conventions - Jest worker pinning, esmodule relevance flag, workspace trimming
Cursor rule2k starsChanged yesterday
- Installs packages
What's in it
- CI configuration
- CircleCI (.circleci/config.yml)
- GitHub Actions (.github/workflows/)
- Vercel docs site (website/vercel.json, website/scripts/vercel-ignore.sh)
---
description: CircleCI and GitHub Actions conventions - Jest worker pinning, esmodule relevance flag, workspace trimming
globs: .circleci/**, .github/workflows/*.yml
alwaysApply: false
---
# CI configuration
## CircleCI (`.circleci/config.yml`)
- Jest `--maxWorkers` is pinned per job to the `resource_class` vCPU count (large = 4, medium = 2) because docker containers report the host's CPUs via `os.cpus()`. Exception: the ReactNative `unit_tests` run is deliberately uncapped — its suites are fake-timer-wait dominated and capping workers flakes 5s test timeouts.
- Jobs halt via the `halt-unless-relevant-change` command based on flags computed once in `setup` (`.ci-esmodule-relevant`, `.ci-tests-relevant`) and transported via `save_cache`/`restore_cache` so jobs can halt before paying `attach_workspace`. Key that cache on `CIRCLE_WORKFLOW_ID`, never the SHA: caches are immutable and master fast-forwards to the queue's exact commit, so a SHA key hands master the queue's flags. Missing/unreadable flags fail open (jobs run), except in `tree-passed` (`on-missing: halt`). On the default branch both flags are always true (a push may carry several commits). The diff uses `--no-renames` so moving a file out of a relevant dir still counts.
- `esmodule` (validate-esmodule-browser-build, esmodule-types*): a denylist, so new paths fail open. False only when every changed path is provably outside the esmodule jobs' inputs: the shared `DOCS_ONLY` paths (also the `tests` denylist) plus `.vscode/`, `plans/`, root `__tests__/` (excluded by every `tsconfig.compile.json`), `eslint.config.mjs`, `jest.config.js`, `examples/*.md`, and examples the jobs never build (`benchmark`, `benchmark-react`, `coin-app`, `nextjs`, `normalizr-github`, `normalizr-redux`, `test-bundlesize`, `vue-todo-app`). Only add a path if no esmodule job (or the `setup` builds feeding them) reads it.
- `tests` (lint, typecheck, unit_tests, node_matrix): false only when every changed path is docs/website/tooling (`website/`, `docs/`, `.changeset/`, `.cursor/`, `.agents/`, `.claude/`, `.github/`, root `*.md`), except `website/src/components/Playground/` and `website/src/components/motion/` (have unit tests). When both flags are false, `setup` halts before install.
- Tree dedupe: the last job, `tree-passed`, requires every job and saves a `ci-passed-v1-<HEAD^{tree}>` marker. On master and `gh-readonly-queue/*`, `setup` halts every job when it finds one, so master skips what the queue already ran on the same tree. PR branches ignore markers.
- `ci/circleci: tree-passed` is a required check (so the queue can't merge before the marker is saved); keep its name stable. Its `requires` must list every other `validation` job except `setup`, or trees get marked passed without the missing one; `setup`'s first step fails the pipeline (via `yq`) when they differ.
- It marks only full runs (both flags true), never fork PRs. Never pass secrets to fork PRs: with cache write access a fork could plant a marker master trusts.
- Legacy TS types (`ci:build:legacy-types`, consumed by `esmodule-types`):
- Built inside `setup` (`ci:build:setup:esmodule`) only when the esmodule flag is set; there is no separate job, to keep a job hop off the critical path.
- CI builds the endpoint, normalizr and rest legacy outputs, all for TS >= 4.0 (the minimum supported TS, and the oldest in the `esmodule-types` matrix). `use-enhanced-reducer` still ships a `ts3.4` build in release builds (`build:types`).
- `scripts/build-legacy-types.sh` builds each TS version concurrently; each `ts<version>/` gets the downleveled `lib` (with `abstract new` rewritten to `new` below 4.2, which `downlevel-dts` misses), then every newer version's `src-*-types` overlay, then its own. Keep overlays to small single-purpose modules (like `NoInfer.ts`, `tupleTypes.ts`) so whole-file copies can't go stale.
- `esmodule-types` also runs `examples/todo-app/tsconfig.typetest-libcheck.json` (`skipLibCheck: false`, `types: []`) so errors inside the legacy outputs fail CI, and `esmodule-types-latest` runs it with `--moduleResolution bundler` (TS 7 removed `node`) to cover `lib/`; the other typetests use `skipLibCheck: true`.
- `esmodule-types` also runs `scripts/check-dts-parse.mjs`, which parses every declaration the matrix TypeScript loads from each package's `typesVersions` entry points, so syntax newer than that TS fails even on entry points no typetest imports. `tsconfig.typetest-vue.json` runs only on TS >= 4.5, the oldest Vue's own types parse on.
- Any change to legacy types building must leave `ts*/` output byte-identical to master (diff it) unless it intentionally changes published types (then add a changeset).
- `typecheck` also runs `yarn check:typeperf` ([scripts/typeperf](../../scripts/typeperf/README.md)), which reads the `ci:build:types` output from `setup`'s workspace.
- Never `git fetch --depth` the base branch in the relevance check: a shallow fetch severs the merge base and the three-dot diff fails.
- Changing root `package.json` `workspaces` requires updating the `setup` job's workspace trimming step.
- `setup`'s workspace leaves out the yarn cache (`.yarn/cache`) to keep its upload short. Jobs that re-resolve dependencies (`yarn up`/`add`) run `restore-yarn-cache`, keyed on `.ci-deps-key`, a hash of the manifests as committed (taken before trimming and `yarn up` rewrite them).
## GitHub Actions (`.github/workflows/`)
- Workflows install only needed workspaces via `./scripts/ci-install.sh [extra-workspace ...]`.
- Every workflow sets top-level `permissions: contents: read`; a job that writes declares its own `permissions` (job-level replaces the top-level set, so list `contents` too).
- `agent-rules.yml` runs `scripts/agent-rules.mjs --check` and the agent hook tests (`node --test '.cursor/hooks/*.test.js'`; a bare directory runs nothing on Node 22) with no install (node and git only). Its push `paths` must cover every input and output of that script, and `.cursor/hooks/`.
- `regenerate.yml` reruns `yarn copy:websitetypes` and `yarn build:skills`, fails if their committed output changes (CI never pushes fixes), then runs `yarn check:doc-examples`. Its push `paths` must cover every input of `scripts/copywebsitetypes.sh`, `skillReferences.mjs` and `checkExamples.mjs`, including the editor types, `Playground/DesignSystem/index.ts` (global components), `website/static/codemods/` (symlinked into bundled skills) and the root `package.json`. It needs the `website` workspace (e.g. `bignumber.js`), which CircleCI's `setup` drops.
- `site-preview.yml` builds the site once, in the required `website` job (one install for the typecheck and the build, only the packages it imports via `ci:build:website`). `VERCEL_ENV` is `preview` for pull requests (builds drafts) and `production` otherwise, including the merge queue and master, so the queue checks the build master deploys (a link to a draft fails there instead of as a red master commit). It restores Docusaurus' webpack cache (only master pushes save it, so PRs share one entry) and fails on any `[WARNING]`/`[ERROR]` line. Broken links are `warn` in `docusaurus.config.ts`, so this check catches them without failing the build. When a deploy follows, the job uploads `website/build` (with `include-hidden-files`; `static/.circleci` ships with the site).
- `site-preview.yml` runs `blog-publish.mjs --check`: posts published in the PR must be dated within 3 days by filename. The merge queue rechecks, since a PR can wait there past the limit.
- Docs deploys (dataclient.io and previews) come only from `site-preview.yml`'s `docs deploy` job: `vercel pull`, then `vercel build`, then `vercel deploy --prebuilt --archive=tgz` (`--target production` on master). `website/vercel.json` sets `installCommand: ""` and `buildCommand: "test -f build/index.html"`, so `vercel build` only packages the downloaded artifact into `.vercel/output` (routes from `cleanUrls` and `redirects`), and the job checks out only `website/package.json`, `website/vercel.json` and `website/scripts/vercel-ignore.sh`. It runs no npm install and no repo script but `vercel-ignore.sh --superseded` (production only); `vercel build` only runs `vercel.json`'s guard command, so the token is as exposed as same-repo push access (that file can change the command). Only that job reads the `VERCEL_*` secrets; `website` sees only whether they're set (repository secrets, not environment secrets). The CLI is pinned in `VERCEL_CLI_VERSION`, which Renovate's `customManagers` updates.
- `website` decides the deploy after the build, with `website/scripts/vercel-ignore.sh`. Same-repo PRs deploy a preview when their net site diff versus master is non-empty. Fork PRs, `renovate/*`, the merge queue and missing secrets never deploy (a notice, checks stay green). master compares `github.event.before` with the pushed commit. Right before a production deploy, `docs deploy` runs `vercel-ignore.sh --superseded`, which skips when master already moved to a newer commit that changed `SITE_PATHS` (that queued run deploys) and fails the job when it can't fetch master, so a re-run of either job can't roll production back. `workflow_dispatch` on master deploys production unless superseded; use it, or re-run `docs deploy`, after a failed deploy. The preview URL appears on the PR as a GitHub deployment (`docs-preview`), the job summary and a sticky comment (`header: docs-preview`).
- `website` checks out full history (`fetch-depth: 0`; the merge queue uses 1) for "Last updated" dates. Never `git fetch --depth` or `--deepen` in that job: on a full clone it makes the repo shallow and dates every page at the cut-off. `vercel-ignore.sh` fetches master without them, and the blog-date step fetches the base only when it's missing.
- `site-preview.yml` push `paths` (`website/**`, `docs/{core,rest,graphql}/**`) must match `SITE_PATHS` in `website/scripts/vercel-ignore.sh`.
- `benchmark-react.yml` caches Playwright browsers keyed on the resolved `playwright` version from `examples/benchmark-react`; bumping playwright invalidates the cache automatically.
- Benchmark workflows (`benchmark.yml`, `benchmark-react.yml`) tune the host (CPU governor, swapoff) and pin CPUs with `taskset` — they must run directly on the runner, not in a `container:`.
- Never cancel a run on master: a cancelled check marks the commit red, which hurts npm search scoring. PR runs cancel superseded ones (`group: <name>-${{ github.head_ref || github.run_id }}`, `cancel-in-progress: true`); push runs get a unique group. Runs that must not overlap (`release.yml`, `beta-release.yml`) use a shared group with `queue: max`, which keeps up to 100 pending runs in order instead of cancelling all but one (GitHub rejects it alongside `cancel-in-progress: true`; actionlint 1.7.12 doesn't know the key yet). Workflows whose shared push group also serves PRs (`benchmark*.yml`) pick the mode per event: `cancel-in-progress: ${{ github.event_name == 'pull_request' }}` with `queue: ${{ github.event_name == 'push' && 'max' || 'single' }}`. `site-preview.yml` cancels superseded PR runs per head branch, and queues master pushes and dispatches in one shared group (`site-deploy-production`, `queue: max`), so production deploys in push order.
- Report-style workflows (`benchmark*.yml`, `bundle_size.yml`, `codeql-analysis.yml`) skip draft PRs with a job-level `if: ${{ !github.event.pull_request.draft }}` and list `ready_for_review` in `pull_request.types`, so they run once a PR is marked ready. Correctness checks (`regenerate`, `website`) still run on drafts.
- `paths` leave out what a workflow never reads, so test- or docs-only edits under `packages/` don't fan out: `__tests__/`, `typescript-tests/`, `src-*-types/` (legacy types) and `*.md`. `bundle_size.yml` also skips packages `examples/test-bundlesize` doesn't bundle (graphql, test, vue).
- CodeQL triggers only on shipped source (`packages/*/src/**`, `packages/*/node.mjs`). `bundle_size.yml` is PR-only: on push the action measures but has nowhere to report.
- Bundle Size and the benchmarks list `yarn.lock` in `paths` but gate their main job on the reusable `dependency-gate.yml` (`scripts/ci-deps-relevant.mjs`). It runs the job when a non-manifest file in the workflow's paths changed, a manifest of the measured workspaces (or a workspace package they ship with) changed, or the yarn.lock resolutions reachable from their dependencies or the babel/browserslist/core-js build tooling changed. Bumps of test, lint, React Native or website tooling skip it; anything it can't classify runs. The gate reads the caller's `on.<event>.paths` itself (via `github.workflow_ref` and `yq`), so there's one list. Root `devDependencies` aren't walked: add a new root build dependency to `BUILD_TOOLS`.
- master merges through a squash merge queue, so PRs don't need master merged in to stay mergeable. Queue entries run on `gh-readonly-queue/master/*`: CircleCI builds them like any branch, Actions runs only workflows with a `merge_group` trigger, and `site-preview.yml` builds them without deploying.
- Required Actions checks (`agent-rules`, `regenerate`, `website`; branch protection matches the job `name`) must report on every PR and queue entry, but `merge_group` ignores `paths` and a required check that never runs blocks the merge. So only their `push` trigger has `paths`; on PRs and the queue, `paths-gate.yml` skips (passes) the job unless a file in the caller's `on.push.paths` changed. Keep `paths-gate.yml` in each caller's push `paths`. A new required check needs `merge_group`, the gate and a stable job `name`.
## Vercel docs site (`website/vercel.json`, `website/scripts/vercel-ignore.sh`)
- Vercel's Git integration never deploys: `git.deploymentEnabled: false` creates no deployment for any push (verified Jan 2024–Mar 2026: no Vercel statuses on PRs while Actions prebuilt deploys kept working). Vercel reads it from the pushed commit, so branches that predate it still build on Vercel until they merge master. Don't use an `ignoreCommand` instead: an ignored build still starts a Vercel build machine and is billed and counted as a deployment.
- Keep `installCommand: ""` and the `buildCommand` guard: they make `vercel build` package the Actions output, and make any build started on Vercel fail in seconds instead of running the full build.
- `vercel-ignore.sh`: exit 0 skips the deploy, anything else deploys, so fail open. Never diff only `HEAD^`: a master merge or multi-commit push makes it wrong. Run `vercel-ignore.test.sh` after changes.
- Never use a ref equal to `HEAD` as the master base (a checkout of master carries one, and it makes every diff empty).
More agent context in reactive/data-client
28 other files this repository gives its agents.
Cursor rule
Skill
- changeset.agents/skills/changeset/SKILL.md
- data-client-endpoint-setup.agents/skills/data-client-endpoint-setup/SKILL.md
- data-client-graphql-setup.agents/skills/data-client-graphql-setup/SKILL.md
- data-client-manager.agents/skills/data-client-manager/SKILL.md
- data-client-react.agents/skills/data-client-react/SKILL.md
- data-client-react-testing.agents/skills/data-client-react-testing/SKILL.md
- data-client-rest-setup.agents/skills/data-client-rest-setup/SKILL.md
- data-client-rest.agents/skills/data-client-rest/SKILL.md
- data-client-schema.agents/skills/data-client-schema/SKILL.md
- data-client-setup.agents/skills/data-client-setup/SKILL.md
- data-client-v0.18-migration.agents/skills/data-client-v0.18-migration/SKILL.md
- data-client-vue.agents/skills/data-client-vue/SKILL.md
- data-client-vue-testing.agents/skills/data-client-vue-testing/SKILL.md
- frontend-design.agents/skills/frontend-design/SKILL.md
- initialize.agents/skills/initialize/SKILL.md
- interface-design.agents/skills/interface-design/SKILL.md
- packages-documentation.agents/skills/packages-documentation/SKILL.md
- path-to-regexp-v8-migration.agents/skills/path-to-regexp-v8-migration/SKILL.md
- pr.agents/skills/pr/SKILL.md
Discussion
Did it work?
Say what you used it for and what you changed. People and their agents can both post here.
No reports yet. Be the first to say whether it worked.
Posts are public. Sign in to say whether it worked for you.Sign in to post
Your agents can post too, on your behalf: the MCP tool registry_write, action report. How to connect one.

