diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 618b98ef8..ab1195c95 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -246,6 +246,23 @@ jobs: - name: DB call-graph gate (no sync DB-touching methods, no raw transactions) run: cd server && npm run db:call-graph -- --sync --tx + - name: File size check + # A source file may hold no more lines than scripts/size-baseline.json + # allows (1000 for a file without an entry, long lines counted + # wrapped), so a big service is split by concern instead of growing. + run: cd server && npm run lint:size + + - name: Import boundaries and cycles + # No new load-time cycle, no new reach into another domain's + # internals, src/db stays below src/nest. Today's violations sit in + # scripts/import-boundaries-baseline.json and may only shrink. + run: cd server && npm run lint:boundaries + + - name: Open request schemas may only shrink + # Needs the shared build above. Counts open shapes (records of + # unknown, passthrough, any) in shared's request schemas. + run: cd shared && npm run contracts:open + client-quality: name: Client Types & Lint needs: changes @@ -272,7 +289,9 @@ jobs: run: cd client && npm run typecheck - name: Lint - run: cd client && npm run lint:check + # Fails on any ESLint error, like lint:check did, and on any rule + # whose warning count rises above scripts/eslint-baseline.json. + run: cd client && npm run lint:warnings - name: Page pattern check run: cd client && npm run lint:pages @@ -284,11 +303,34 @@ jobs: run: cd client && npm run lint:rtl - name: File size check - # A source file may hold no more lines than scripts/size-baseline.json - # allows (1000 for a file without an entry), so a big file is split by - # concern instead of growing (scripts/size-lint.mjs). + # A source file or stylesheet may hold no more lines than + # scripts/size-baseline.json allows (1000 without an entry, 2000 for a + # test, long lines counted wrapped), so a big file is split by concern + # instead of growing (scripts/size-lint.mjs). run: cd client && npm run lint:size + - name: Formatting check + # Every file outside scripts/format-baseline.json must be Prettier + # formatted; the list only shrinks. + run: cd client && npm run lint:format + + - name: Layering check + # Imports go downwards: components never import pages/ or mobile/. + run: cd client && npm run lint:layers + + - name: Offline data path check + # No view file calls src/api/ directly beyond + # scripts/offline-baseline.json; writes go through repo/ and the + # mutation queue. + run: cd client && npm run lint:offline + + - name: Skipped and focused tests check + run: cd client && npm run lint:skips + + - name: i18n keys check + # Every translation key src/ names must exist in shared en. + run: cd client && npm run lint:i18n-keys + plugin-sdk-quality: name: Plugin SDK Types needs: changes diff --git a/CLAUDE.md b/CLAUDE.md index 32e3cf399..a15100de3 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -35,7 +35,14 @@ cd client && npm run test # client vitest run cd client && npm run e2e # Playwright (CI runs the public and app projects; the screenshot and help-media ones are local) cd client && npm run lint:pages # enforce the Page pattern (CI gate) cd client && npm run lint:rtl # physical left/right sides may only shrink per file (CI gate) -cd client && npm run lint:size # no source file past 1000 lines, longer ones only shrink (CI gate) +cd client && npm run lint:size # no source file past 1000 lines, longer ones only shrink (CI gate; server has the same) +cd client && npm run lint:warnings # eslint errors fail, warnings per rule may only shrink (CI gate) +cd client && npm run lint:format # Prettier outside the baseline list (CI gate) +cd client && npm run lint:layers # components never import pages/ or mobile/ (CI gate) +cd client && npm run lint:offline # views may not import src/api/ beyond the baseline (CI gate) +cd client && npm run lint:skips # no .only; skipped tests may only shrink (CI gate) +cd client && npm run lint:i18n-keys # every key the client names exists in en (CI gate) +cd server && npm run lint:boundaries # no new import cycles or cross-domain reach-ins (CI gate) cd client && npm run theme:lint # flag styling that bypasses appearance tokens (local only) npm run typecheck --workspace=server # tsc --noEmit (also client/shared; server also has typecheck:tests) ``` @@ -110,7 +117,7 @@ These principles come out of a full-repo audit and shape all new code: ## Quality gates in CI -`.github/workflows/test.yml` runs, per package: typecheck (server also `typecheck:tests`), `lint:check`, `lint:pages`, `lint:rtl`, `lint:size`, `check:plugin-facts`, `i18n:parity:strict`, the S3 contract suite, the browser end-to-end flows (Chromium and WebKit), a client build with `check:gl-split`, coverage for all four packages, then a SonarCloud scan. It also builds the Docker image and boots it until `/api/health/ready` answers (`docker-image`), and lints and renders the Helm chart against the Kubernetes schemas and runs `docker compose config` on every compose file (`deploy-manifests`). `lint-prettier.yml` additionally runs `lint` + `format:check` on `shared/`. `plugin-sdk` has no lint script and is outside the eslint gates. **Not in CI**: `theme:lint` and the screenshot and help-media Playwright projects; run them locally. There are no git hooks; nothing runs before a commit except you. Other workflows build Docker images, scan them, and publish `trek-plugin-sdk` on `plugin-sdk-v*` tags. +`.github/workflows/test.yml` runs, per package: typecheck (server also `typecheck:tests`), lint (client through `lint:warnings`, which also holds the warning count per rule), `lint:pages`, `lint:rtl`, `lint:size` (client and server), the server's `lint:boundaries`, the client's `lint:format`, `lint:layers`, `lint:offline`, `lint:skips` and `lint:i18n-keys`, shared's `contracts:open`, `check:plugin-facts`, `i18n:parity:strict` (with the untranslated-strings ratchet), the S3 contract suite, the browser end-to-end flows (Chromium and WebKit), a client build with `check:gl-split`, coverage for all four packages, then a SonarCloud scan. It also builds the Docker image and boots it until `/api/health/ready` answers (`docker-image`), and lints and renders the Helm chart against the Kubernetes schemas and runs `docker compose config` on every compose file (`deploy-manifests`). `lint-prettier.yml` additionally runs `lint` + `format:check` on `shared/`. `plugin-sdk` has no lint script and is outside the eslint gates. **Not in CI**: `theme:lint` and the screenshot and help-media Playwright projects; run them locally. There are no git hooks; nothing runs before a commit except you. Other workflows build Docker images, scan them, and publish `trek-plugin-sdk` on `plugin-sdk-v*` tags. - **`ci-ok` is the required check.** The branch ruleset requires `ci-ok` (plus `lint` and `check-target` from their own workflows), and `ci-ok` needs every job above except the scan. It fails if any of them failed or was cancelled and passes if they passed or were skipped. A new gate job counts only once it is listed in `ci-ok`'s `needs:`. - **What runs is decided by `changes`, not by `on.paths`**: `scripts/ci/changed-areas.mjs` maps the changed files to the `code`, `image` and `deploy` areas (tests: `npm run test:ci-scripts`). A path filter on the workflow would leave `ci-ok` unreported on a docs-only PR and block it. Add a new top-level file the build reads to the right area there. diff --git a/client/scripts/size-baseline.json b/client/scripts/size-baseline.json index 88d00cfe6..e62581dfe 100644 --- a/client/scripts/size-baseline.json +++ b/client/scripts/size-baseline.json @@ -2,7 +2,7 @@ "e2e/help/fixtures.ts": 2291, "src/api/client.ts": 1916, "src/components/Admin/AdminPluginsPanel.test.tsx": 2704, - "src/components/Admin/AdminPluginsPanel.tsx": 2431, + "src/components/Admin/AdminPluginsPanel.tsx": 2433, "src/components/Budget/CostsPanel.test.tsx": 2854, "src/components/Budget/CostsPanel.tsx": 2366, "src/components/Journey/JourneyDetailPageEntryEditor.tsx": 1014, @@ -12,8 +12,8 @@ "src/components/Map/RouteCalculator.ts": 1196, "src/components/PDF/TripPDF.tsx": 1097, "src/components/Planner/DayDetailPanel.test.tsx": 2333, - "src/components/Planner/DayPlanSidebar.test.tsx": 5400, - "src/components/Planner/DayPlanSidebar.tsx": 3319, + "src/components/Planner/DayPlanSidebar.test.tsx": 5403, + "src/components/Planner/DayPlanSidebar.tsx": 3312, "src/components/Planner/PlaceFormModal.test.tsx": 2864, "src/components/Planner/PlaceFormModal.tsx": 1669, "src/components/Planner/PlaceInspector.tsx": 1357, @@ -23,7 +23,7 @@ "src/components/Studio/bookTemplates.data.ts": 1309, "src/components/Studio/countryShapes.ts": 2595, "src/index.css": 1636, - "src/mobile/screens/admin/MAdminPluginsPanel.tsx": 2262, + "src/mobile/screens/admin/MAdminPluginsPanel.tsx": 2264, "src/mobile/screens/journey/MJourneyEntrySheet.tsx": 1122, "src/mobile/screens/trip/sheets/MTransportFormSheet.tsx": 1194, "src/mobile/screens/trip/tabs/MCostsTab.tsx": 1095, @@ -34,6 +34,6 @@ "src/pages/TripPlannerPage.wiring.test.tsx": 2049, "src/pages/atlas/useAtlas.ts": 1288, "src/pages/tripPlanner/useTripPlanner.roadtrip.test.tsx": 3531, - "src/pages/tripPlanner/useTripPlanner.test.tsx": 3307, + "src/pages/tripPlanner/useTripPlanner.test.tsx": 3309, "src/styles/studio.css": 2087 } diff --git a/server/scripts/size-baseline.json b/server/scripts/size-baseline.json index d652438fe..14cf0dcd4 100644 --- a/server/scripts/size-baseline.json +++ b/server/scripts/size-baseline.json @@ -1,15 +1,15 @@ { "scripts/db-call-graph.mjs": 1036, - "scripts/generate-entities.ts": 2186, + "scripts/generate-entities.ts": 2259, "src/db/dialect/sql-functions.ts": 1007, "src/db/repositories/DayAssignments.repository.ts": 1030, "src/db/repositories/Places.repository.ts": 1483, "src/db/repositories/Reservations.repository.ts": 1806, - "src/db/repositories/Trips.repository.ts": 1145, - "src/db/repositories/Users.repository.ts": 1679, + "src/db/repositories/Trips.repository.ts": 1164, + "src/db/repositories/Users.repository.ts": 1710, "src/nest/atlas/atlas-geo.ts": 1005, "src/nest/atlas/atlas.service.ts": 1182, - "src/nest/auth/auth.service.ts": 1161, + "src/nest/auth/auth.service.ts": 1166, "src/nest/budget/budget.service.ts": 1703, "src/nest/collections/collections.service.ts": 1488, "src/nest/doc-sync/doc-sync.service.ts": 1060,