fix(perf): enforce route budgets and ratchet coverage thresholds
CI / check (push) Successful in 31s
CI / tests-integration (push) Successful in 2m4s
CI / tests-unit (push) Successful in 2m16s
CI / tests-imaging-browsers (push) Successful in 1m30s
CI / tests-quality (push) Successful in 54s
CI / tests-ui (push) Successful in 3m1s
CI / preflight (push) Skipped
CI / deploy (push) Successful in 3m24s

Allow mode: enforce in the report config, fail the command on over-budget and
unavailable routes, and make the markdown wording follow the configured mode.
Recalibrate the budgets against the webpack build (baseline x1.15, rounded up to
the next 10 KiB) now that the old Turbopack numbers no longer apply, so the
Docker image build gate blocks genuine bundle regressions instead of only
warning. Raise the Vitest coverage thresholds to the measured floor
(37.17/30.1/31.79/38.17 -> 36/29/30/37).
This commit is contained in:
openhands committed 2026-10-16 16:57:08 +02:00
1 parent e70b96e3a7
commit 3a296551f6
5 files changed
+100 -33

No files matched your search

+13 -11
View File
@@ -1,4 +1,4 @@
# Informational route JavaScript budgets
# Route JavaScript budgets (enforced)
Run after the existing production build; no second build or server is needed:
@@ -28,20 +28,22 @@ The definition follows the data exposed by the installed Next 16.3.8 build and t
## Initial limits
The first limits are **baseline bytes × 1.15, rounded upward to the next 10 KiB (10,240 bytes)** independently for raw and gzip. They are provisional size alerts, not validated speed targets. Baseline: local production build `build-TfctsWXpff2fKS`, Next 16.3.4 **Turbopack**; its source commit was not inferred.
The limits are **baseline bytes × 1.15, rounded upward to the next 10 KiB (10,240 bytes)** independently for raw and gzip. They are size guardrails, not validated speed targets.
The production build now runs webpack, so the numbers it reports are not directly comparable to the baseline below. Re-measured on the current webpack build the routes land at `/me` 786138/247738, `/news` 781601/245672, `/events` 782011/245923, `/search` 783262/246578, `/admin/catalog` 1172089/370843, `/admin/studio/furni` 1374128/440546 (raw/gzip). All remain inside the limits below, but `/admin/studio/furni` sits at ~98% of its gzip limit, so the next dependency added to that route will trip it. Recalibrate the table and `scripts/performance-budgets.json` together if the intent is to reset the baseline on webpack.
Baselines were last recalibrated against a local webpack production build (Next 16.4.1, `next build --webpack`) and cross-checked against the report the Docker image build emits: `/me` 621460/195411, `/news` 616712/193199, `/events` 617121/193450, `/search` 618536/194216, `/admin/catalog` 1049094/330369, `/admin/studio/furni` 1397566/447765 (raw/gzip). The earlier Turbopack numbers (previous revision) were **not** comparable and were dropped.
The image build compiles the app, then runs `scripts/performance-report.mjs` in the same `RUN` layer (`Dockerfile:48-53`). In `mode: enforce` a failing route therefore fails the image build, which blocks both the PR `preflight` job and the `deploy` job. Recalibrate the table and `scripts/performance-budgets.json` **together** in the same commit whenever the envelope legitimately grows (new route dependency, Next upgrade).
| Route | Baseline raw bytes | Baseline gzip bytes | Raw limit | Gzip limit |
| --- | ---: | ---: | ---: | ---: |
| `/me` | 767156 | 238571 | 890880 | 276480 |
| `/news` | 765367 | 237599 | 880640 | 276480 |
| `/events` | 765851 | 237964 | 890880 | 276480 |
| `/search` | 765851 | 237964 | 890880 | 276480 |
| `/admin/catalog` | 1654898 | 492619 | 1904640 | 573440 |
| `/admin/studio/furni` | 1241312 | 391541 | 1433600 | 450560 |
| `/me` | 621460 | 195411 | 716800 | 225280 |
| `/news` | 616712 | 193199 | 716800 | 225280 |
| `/events` | 617121 | 193450 | 716800 | 225280 |
| `/search` | 618536 | 194216 | 716800 | 225280 |
| `/admin/catalog` | 1049094 | 330369 | 1208320 | 389120 |
| `/admin/studio/furni` | 1397566 | 447765 | 1607680 | 522240 |
Configured limits are positive integer bytes; `null` explicitly means observe-only. `scripts/performance-budgets.json` remains `mode: informational`. Exceeding a limit produces `over-budget` and a warning, with exit code 0. Missing production `BUILD_ID`, unsupported manifests, missing routes or missing referenced assets produce `unavailable` with a reason and **no partial/zero total**, also exit code 0. Malformed budget configuration or an unwritable output directory fails the command. This keeps initial CI reporting non-blocking while preventing invalid configuration from quietly disabling limits.
Configured limits are positive integer bytes; `null` explicitly means observe-only. `scripts/performance-budgets.json` runs `mode: enforce`: an `over-budget` route prints `Performance budgets enforced: <route> (over-budget)` and exits non-zero. Missing production `BUILD_ID`, unsupported manifests, missing routes or missing referenced assets produce `unavailable` with a reason and **no partial/zero total**; under `enforce` an `unavailable` route also fails the command, so a bundler change that breaks measurement cannot pass quietly. Malformed budget configuration or an unwritable output directory always fails the command. Set `mode` back to `informational` only while recalibrating, never as a permanent state.
Synthetic tests cover shared-chunk deduplication, exact byte/gzip calculations, route bootstrap selection, missing data, safe parsing and CLI exit behavior. Run `pnpm exec vitest run --coverage.enabled=false scripts/performance-report.test.mjs`.
Synthetic tests cover shared-chunk deduplication, exact byte/gzip calculations, route bootstrap selection, missing data, safe parsing and CLI exit behavior in both modes. Run `pnpm exec vitest run --coverage.enabled=false scripts/performance-report.test.mjs`.