# QUALITY_GATES.md — executable frontend gates The single command that runs **every** gate, locally and in CI: ```bash npm run verify ``` `verify` chains: `format:check` → `lint` → `build` (`tsc -b && vite build`) → `test` (`vitest run`) → `governance`. Governance also runs the repository gates: Terraform import-plan and release-plan checkers, isolation tests, Terraform formatting and validation, the HCP run guard, CloudFront verify, and workflow shell checks. A task is not done until this is green. ## Gate matrix | Gate | Command / rule source | Enforced by | Scope | | ----------------------------------- | ----------------------------------------------------------------------------------------------------------- | ---------------------- | -------------------------------------- | | Formatting | `npm run format:check` (Prettier) | `verify` + lint-staged | Whole repo | | Lint, zero warnings | `npm run lint` → `eslint . --max-warnings=0` | `verify` + CI | Governed TS/TSX (`eslint.config.js`) | | Type-check + production build | `npm run build` → `tsc -b && vite build` | `verify` + CI | Whole app | | Unit tests | `npm test` → `vitest run` | `verify` + CI | `src/test/**`, `config/**/*.test.ts` | | Conditional rendering (no `: null`) | `no-restricted-syntax` in `eslint.config.js` | lint | Governed TSX | | Boolean-only JSX `&&` | `seahaven/no-non-boolean-jsx-and` (type-aware) in `eslint-rules/` | lint | Governed TSX | | Shared `Text` typography | `no-restricted-syntax` (raw `p`/`h1`–`h6`) + `seahaven/no-vp-error-outside-text` | lint | Governed TSX | | Hooks correctness | `eslint-plugin-react-hooks` recommended (incl. `exhaustive-deps`) under zero-warnings | lint | Governed TS/TSX | | Godfile ratchet (file length) | `scripts/governance-check.mjs` + `scripts/governance-baseline.json` | `governance` | `src/**`, `config/**` (non-test) | | Changed-file maintainability | `scripts/governance-check.mjs` → ESLint (`complexity`, `max-lines-per-function`, `max-params`, `max-depth`) | `governance` | Changed TS/TSX vs base ref | | Terraform import-plan contract | `npm run test:terraform-import-plan` → `scripts/test-terraform-import-plan-check.py` | `governance` + CI | Synthetic plan JSON + canonical maps | | Terraform release-plan contract | `npm run test:terraform-release-plan` → `scripts/test-terraform-release-plan-check.py` | `governance` + CI | Synthetic plan JSON + 15 fixtures | | Terraform isolation gate contract | `npm run test:terraform-isolation` → `scripts/check-terraform-isolation.test.mjs` | `governance` + CI | Changed-file classifier | | Terraform formatting/validation | `npm run test:terraform` → `scripts/terraform-validate.mjs` | `governance` + CI | `terraform/live/dev` | | HCP run guard | `npm run test:hcp-run-guard` → `scripts/test-hcp-run-guard.py` | `governance` + CI | Workspace invariants + apply reconcile | | CloudFront release verify | `npm run test:cloudfront-release-verify` → `scripts/test-verify-cloudfront-release.sh` | `governance` + CI | Stubbed aws/curl | | GitHub workflow shell | `npm run test:github-workflows` → `scripts/check-github-workflows.sh` | `governance` + CI | `bash -n` + actionlint | | Terraform/app change isolation | `terraform-isolation.yaml` job `terraform-isolation` → `scripts/check-terraform-isolation.mjs` | CI (PR) | Changed files of the PR | ## No-false-pass guarantees - **`--max-warnings=0`** — a warning is a failure. There is no "warning-only" backlog; rules ship green (see AGENTS.md → How to add a convention). - **Type-aware rules fail closed** — `seahaven/no-non-boolean-jsx-and` reports when type services are unavailable rather than silently claiming safety. - **Godfile ratchet is monotonic** — any new file over the cap, new baseline entry, global cap increase, per-file cap increase, or growth beyond a frozen legacy cap fails. Only cap reductions and entry removals are allowed. - **Changed-file maintainability fails closed without a valid base** — in CI the base ref is derived from `GITHUB_BASE_REF` (PR) or `github.event.before` (push). An absent or unresolvable base is a failure, not a pass. - **Terraform gates never touch live state** — `terraform init -backend=false -lockfile=readonly` and `validate` run offline; the plan checker is tested against synthetic plan JSON. Real import and controlled-update plans from HCP are migration evidence reviewed by a human before an approved apply (`terraform/README.md`). - **The isolation gate re-evaluates on label changes** — the `terraform-isolation-override` label is the only way to merge a mixed Terraform/application PR. `.github/workflows/terraform-isolation.yaml` runs `terraform-isolation` on `labeled` and `unlabeled` as well as the default pull-request types, so adding or removing the label re-checks the current labels without starting a new Frontend checks run. Removing the label fails a mixed PR that had previously passed with the override. ## Where the gates run - **Locally:** `npm run verify`. `lint-staged` (via Husky) re-runs ESLint + Prettier on staged files at commit; commitlint enforces Conventional Commits. - **CI ([`.github/workflows/ci.yaml`](.github/workflows/ci.yaml)):** the org reusable workflow (`ci-typescript-frontend.yaml`, Node 24) runs format/lint/build/tests, **and** a repo-owned `governance` job runs `npm run verify` (with Terraform 1.16.0 installed) so the maintainability ratchets and repository gates are guaranteed from this repository regardless of the reusable workflow. - **Terraform isolation ([`.github/workflows/terraform-isolation.yaml`](.github/workflows/terraform-isolation.yaml)):** on pull requests, fails when Terraform infrastructure and application code change together. Label add/remove re-runs only this workflow. ## Toolchain pin Node ≥ 22.22.1 (CI uses Node 24); npm 11.16.0 via `packageManager` (use `corepack npm …` if your default `npm` is older). The lockfile is `package-lock.json` v3; install with `npm ci`. Governance also needs `terraform` (CI: 1.16.0; `versions.tf` accepts `>= 1.14.0, < 2.0.0`) and `python3` (3.10+) on `PATH`.