> ## Documentation Index
> Fetch the complete documentation index at: https://docs.hr-easy.nlead.ch/llms.txt
> Use this file to discover all available pages before exploring further.

# The quality gate

> One command that mirrors CI, the guards it runs, and the change-aware fast path.

## One command

```bash theme={null}
npm run gate
```

Runs, in order: `format:check` → `check:all` → `test:scripts` → `lint` → `typecheck` →
`test` → `build`, and reports **all** failures at the end so you fix everything in one
pass.

It runs on the host Node toolchain — **no Docker and no Postgres** — because unit and
component tests mock the database. That means it works in Docker-less web sessions too.
Every step must exit 0.

```bash theme={null}
npm run gate:fast   # format, guards, lint, typecheck — what the pre-push hook runs
```

## The guards

`npm run check:all` runs every guard:

| Guard                        | Enforces                                                          |
| ---------------------------- | ----------------------------------------------------------------- |
| `check:encryption-coverage`  | Sensitive fields go through application-layer encryption.         |
| `check:i18n-parity`          | de / en / fr share a full nested key tree.                        |
| `check:component-sizes`      | The component-size ratchet.                                       |
| `check:audit-coverage`       | State-changing routes write an audit entry.                       |
| `check:findmany-bounds`      | Every `findMany` in an API route is bounded.                      |
| `check:unlogged-catches`     | No catch block swallows its error.                                |
| `check:ai-model-ids`         | No hardcoded `claude-*` string outside the central model config.  |
| `check:openapi-drift`        | Every committed portal-contract artifact matches the Zod schemas. |
| `check:compose-secrets`      | No secrets in compose files.                                      |
| `check:deploy-secrets`       | Deploy workflows reference secrets correctly.                     |
| `check:regression-tests`     | Service / API-route changes ship with a test change.              |
| `check:ui-conventions`       | Shared page-chrome components are used, not re-implemented.       |
| `check:dashboard-page-gates` | Dashboard pages carry their permission gates.                     |
| `check:date-input-bounds`    | Date inputs are bounded.                                          |
| `check:stable-intl-mock`     | Component tests use the stable intl mock.                         |

Each guard exists because a specific class of defect reached review more than once.

## Git hooks

Installed on `npm install`:

| Hook       | Runs                                                       |
| ---------- | ---------------------------------------------------------- |
| pre-commit | Prettier on staged files, plus the schema-migration guard. |
| pre-push   | `npm run gate:fast`.                                       |

Bypass with `--no-verify` when you have a reason. The hooks are a safety net, not the
gate.

## CI is change-aware

CI classifies the PR's changed files:

```bash theme={null}
git diff --name-only origin/develop...HEAD | bash scripts/classify-pr-paths.sh
```

| Output                | Effect                                                                                                     |
| --------------------- | ---------------------------------------------------------------------------------------------------------- |
| `docs_only=true`      | Only the static leg runs — typecheck, tests, build and the E2E smoke are skipped. \~3 min instead of \~15. |
| `prisma_changed=true` | The Postgres migrations and drift job runs.                                                                |

A separate **Quality Gate** job verifies that every skip was sanctioned by that
classification and **fails closed** otherwise, so the fast path can never weaken the
gate for a code change.

The classifier fails closed on an empty file list, too: misclassifying code as docs
would silently skip the gate, while misclassifying docs as code merely costs runner
minutes.

### Working with the fast path

* **Docs-only pushes may run `gate:fast` instead of the full gate.** A diff the
  classifier reports as `docs_only=true` cannot affect tests or the build, so running
  them locally proves nothing. This is the **only** sanctioned exception.
* **Do not dilute a docs-only PR.** One code file forfeits the fast path for the whole
  PR. Ship documentation changes separately when they are not coupled to code — but a
  genuinely coupled change (a new rule plus the guard that enforces it) stays in one PR.
* **Do not drive-by edit `prisma/`** in unrelated PRs; any file under it pulls in the
  Postgres leg.
* When extending the docs-only pattern list, change `scripts/classify-pr-paths.sh` and
  its test file **together**. Misclassifying code as docs is the failure mode those
  tests exist to prevent.

## Before every commit

```bash theme={null}
npm run format
npm run lint
npm run typecheck
npm run test -- --run
npm run check:i18n-parity
```

The pre-commit hook already formats staged files; this is the belt-and-braces version
for when hooks are bypassed.

<Warning>
  Never skip the gate because "the change is tiny". CI mirrors it exactly — running it locally means
  fixing problems quietly instead of in a PR thread.
</Warning>
