chore(ci): skip perf and commits gates on Renovate-authored PRs
CI / check (pull_request) Successful in 2m40s
CI / scan (pull_request) Failing after 3m59s
CI / commits (pull_request) Successful in 3m46s
CI / a11y (pull_request) Successful in 52s
CI / perf (pull_request) Successful in 4m35s

The full CI pipeline (check, scan, commits, perf, a11y) was sized for
human PRs. On Renovate's dep-bump PRs:

- `perf` (Lighthouse build + 3-iteration median across the
  critical-routes list) costs 3-5 min per PR for a signal that is
  essentially zero on a patch/minor dep bump — the SPA today serves
  the static placeholder, and even with real routes the score on
  most bumps stays inside the median noise floor.
- `commits` re-validates messages that Renovate generates from a
  Conventional-Commits-conformant template. Tautological.

Skip both jobs when the PR author is the dedicated `apf-portal-bot`
Gitea user. The `push` event on `main` still runs the full pipeline,
so any regression a Renovate PR sneaks through is caught seconds
after merge — fast enough to revert without disruption.

ADR-0017 is amended to document the policy: the table of "where
Lighthouse CI runs" now distinguishes human PR / bot PR / push to
main; a new "Pre-merge gating policy" subsection records the
rationale and the human-takeover edge case. The §Confirmation
checklist is updated.

The docs/development.md "Reviewing Renovate PRs" subsection is
updated so contributors expect a leaner pipeline on bot PRs.
This commit is contained in:
Julien Gautier
2026-05-05 15:50:38 +02:00
parent 0c6a3f2b27
commit c78dccbae1
3 changed files with 36 additions and 9 deletions
@@ -103,11 +103,27 @@ Each route is scored against the thresholds above. **Failure on any threshold is
### Where Lighthouse CI runs
| Environment | Cadence | Purpose | Blocking? |
| ------------------------------------- | ---------------------------------------------------------------------------------------------------- | ---------------------------------------------- | ----------------------------------------------------- |
| **CI on every PR** | per push | Catch regressions before merge | **Yes** |
| **CI scheduled (weekly)** on prod env | cron in `security-scheduled.yml` from [ADR-0015](0015-cicd-gitea-actions.md), extended to cover perf | Detect drift / regressions in real environment | Reports as alerts; doesn't block, but triggers triage |
| **Local dev** | manual via `pnpm nx run portal-shell:lighthouse` | Developer-side feedback | Non-blocking |
| Environment | Cadence | Purpose | Blocking? |
| --------------------------------------- | ---------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- | ----------------------------------------------------- |
| **CI on human-authored PRs** | per push | Catch regressions before merge | **Yes** |
| **CI on Renovate (bot) PRs** | _skipped_ | Per-PR perf signal on a dep bump is ≈0; Lighthouse cost is ≥3-5 min per PR | _No (skipped)_ |
| **CI on `push` to `main`** (post-merge) | per merge | Catch regressions immediately after merge — applies to bot PRs too | **Yes** |
| **CI scheduled (weekly)** on prod env | cron in `security-scheduled.yml` from [ADR-0015](0015-cicd-gitea-actions.md), extended to cover perf | Detect drift / regressions in real environment | Reports as alerts; doesn't block, but triggers triage |
| **Local dev** | manual via `pnpm nx run portal-shell:lighthouse` | Developer-side feedback | Non-blocking |
### Pre-merge gating policy: human PRs vs bot PRs
The `perf` job is skipped on PRs whose author is the dedicated Renovate bot user (`apf-portal-bot`). Rationale:
- A dependency bump on this codebase has near-zero direct impact on the bundle the user receives — the SPA today serves the static placeholder, and even once real routes ship, the Lighthouse score on a patch/minor dep bump rarely moves outside the median noise floor (±2-5 points).
- A full Lighthouse round-trip (build prod + 3-iteration median across the critical-routes list) costs 3-5 minutes per PR. Multiplied by the steady-state Renovate cadence (~5-15 PRs per week once dependencies are diversified), this is a real wall-clock cost on shared runners with no reliable signal in return.
- The post-merge `push` to `main` still runs the full `perf` gate. A regression that does sneak through a Renovate PR is caught seconds after merge, before the next PR queues a build — fast enough to revert without disruption.
This policy is **conservative on human PRs** (the perf gate stays mandatory pre-merge, where the latitude for change is highest) and **pragmatic on bot PRs** (where the change vocabulary is narrow and the post-merge gate already provides the safety net).
The same skip is applied to the `commits` job: Renovate generates commit messages from a Conventional-Commits-conformant template, so re-validating them with commitlint is tautological.
Edge case: if a human takes over a Renovate-authored PR and force-pushes substantive changes, the gate stays skipped (the PR _author_ is still the bot). This is accepted as a marginal case; the human can either run `ci:perf` locally or accept that the post-merge `push` gate will catch any regression. Re-evaluation if it happens often enough to be a real annoyance.
### Variability mitigation
@@ -143,7 +159,7 @@ These budgets are **not** enforced as hard CI gates — load profile in CI is un
### Performance regressions are bugs, not backlog
A regression on any of the front-end gates blocks the merge. A regression on a back-end SLO that surfaces in production (alert fires) is triaged with the same priority as a security finding — root-caused, fixed, post-mortemed if it took the SLO out for more than 24 h.
A regression on any of the front-end gates blocks the merge for human PRs, and blocks `push` to `main` for any PR (including Renovate-authored ones — see the gating-policy subsection above). A regression on a back-end SLO that surfaces in production (alert fires) is triaged with the same priority as a security finding — root-caused, fixed, post-mortemed if it took the SLO out for more than 24 h.
### a11y / perf trade-off
@@ -195,7 +211,7 @@ No browser-side RUM SDK in v1. The OTel browser tracing from [ADR-0012](0012-obs
- `lighthouserc.js` exists at the repo root with the critical-routes list, the assertions matching the thresholds above, and a 3-iteration median configuration.
- `apps/portal-shell/project.json` declares `budgets` of type `"error"` with the values above.
- `package.json` exposes `ci:perf`, runnable locally with the same exit code as CI.
- CI's `perf` gate from [ADR-0015](0015-cicd-gitea-actions.md) calls `pnpm ci:perf` and is blocking.
- CI's `perf` gate from [ADR-0015](0015-cicd-gitea-actions.md) calls `pnpm ci:perf`. It is blocking on human PRs and on `push` to `main`; skipped on PRs authored by the Renovate bot user (`apf-portal-bot`) per the gating-policy subsection above.
- `apps/portal-shell` exposes a Nx target `analyze` invoking `source-map-explorer` against the production build's source maps.
- The BFF p95/p99 budgets are documented in `apps/portal-bff/README.md` and translate into alert rules in the production observability backend (configured per future infrastructure ADR).
- The `security-scheduled.yml` workflow includes a weekly Lighthouse CI run against the prod URL set, with reports uploaded as build artefacts and an alert on any threshold breach.