Quality gates
Every change reaches main through a pull request, and a pull request can only merge when the automated checks pass. This page lists the checks, how to run them locally, and the steps from branch to merge.
Pull request flow
- Branch from an up-to-date
main. Direct pushes tomainare blocked. - Commit with the local hooks enabled (see below). Add a changelog fragment.
- Push and open a pull request against
main. - Wait for the required checks:
CI GateandGitLeaks Secret Scanning. - Squash-merge. You can enable auto-merge, for example
gh pr merge --auto --squash, and it merges when the checks go green.
Merging to main runs the gates once more before the change is released.
The gates
The CI workflow (.github/workflows/ci.yml) runs on every pull request to main.
Quality Gates job (always runs):
| Gate | Command |
|---|---|
| Typecheck (API, web, worker) | pnpm typecheck |
| Lint | pnpm lint |
| Formatting | pnpm format:check |
| FE/BE contract drift | pnpm contracts:check |
| API unit tests | pnpm test:api:unit |
| Scheduling engine tests | pnpm -F @aetech/scheduling-engine run test |
| CI script tests | pnpm test:gate |
| Analytics emit sites | pnpm check:analytics-emit-sites |
| Widget i18n catalog drift | pnpm check:wp-i18n |
| WordPress plugin zip version | pnpm check:wp-zip |
Database Tests job: starts a fresh test database, runs pnpm db:test:reset, then pnpm test:api:int and pnpm test:api:e2e. It is skipped when a pull request only touches documentation, OpenSpec files, CI config other than ci.yml, root scripts or dependency manifests.
CI Gate passes only when both jobs passed or were legitimately skipped. It is the check that branch protection requires.
Contract drift
pnpm contracts:check runs pnpm contracts:regen and fails if apps/api/openapi.json or apps/web/lib/api/generated/openapi.d.ts changed. If it fails, run pnpm contracts:regen and commit both files. See Contracts.
Opt-in suites
These workflows do not run on every pull request. Add the run-heavy-ci label to a pull request to run them, or start them by hand:
- QA Tests (Playwright): the browser suite across all projects. See Testing.
- Lighthouse CI and the performance gate.
Security scans (dependency scan, secret scan, Semgrep) run on pull requests, on pushes or on a schedule. Their results are in each run's job summary and artifacts. Code Scanning (the GitHub Security tab) is not available on this repository.
Run the gates locally
The Git hooks (installed by pnpm install through Husky) run most gates for you:
| Hook | Runs |
|---|---|
pre-commit | lint-staged (ESLint and Prettier on staged files), then a typecheck of the touched apps |
pre-push | pnpm typecheck, pnpm lint, pnpm format:check, pnpm contracts:check and more |
To run the checks by hand before you push:
pnpm typecheck
pnpm lint
pnpm format:check
pnpm test:api:unit
pnpm ci:local runs a similar sequence in one command; pnpm ci:local:quick skips the unit tests. Run pnpm test:api:int too when you change database code; it needs the test database from Testing.
lint-staged only formats the files you stage, but the pre-push hook checks the whole repository. If the push fails on formatting, run pnpm format:fix, commit and push again.
Changelog fragments
Every change adds one new file under openspec/changelog.d/ instead of editing openspec/changelog.md. Two open pull requests then never conflict on the changelog.
-
Name the file
YYYY-MM-DD-short-slug.md. -
Its content is one changelog block:
## [2026-08-10] - fix: short summary of the change (#123)
What changed and why. -
pnpm changelog:assemblefolds the pending fragments intoopenspec/changelog.mdat release time.
See openspec/changelog.d/README.md for the details.