From 03bf10e6abb6c2552268d789f8bc7da5f40bb6c6 Mon Sep 17 00:00:00 2001 From: William Phetsinorath Date: Wed, 9 Sep 2026 14:47:44 +0200 Subject: [PATCH 1/8] docs: record recurring agent rules in AGENTS.md Signed-off-by: William Phetsinorath Change-Id: I9c480eac0f9c39d41e8befaa9827cb246a6a6964 --- AGENTS.md | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index 8370661976..49062e7049 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -6,7 +6,8 @@ pnpm monorepo. Node >= 26, pnpm v11.8 - `apps/client` : Vue 3 + Vite + vue-dsfr (French gov design system), Pinia, UnoCSS - `apps/server` : Fastify 4 + Prisma 6 (PostgreSQL), contract-first API via @ts-rest -- `apps/server-nestjs` : NestJS rewrite (in progress) +- `apps/server-nestjs` : NestJS rewrite (in progress) — the only modifiable + backend target; `apps/server` is frozen (read-only reference) - `plugins/*` : argocd, gitlab, harbor, keycloak, kubernetes, nexus, sonarqube, vault - `packages/shared` : API contracts (@ts-rest), types, permissions (BigInt bitmasks) - `packages/hooks` : plugin hook system (core of plugin architecture) @@ -26,6 +27,10 @@ Resource-based organization in `apps/server/src/resources/`. Each resource follo API contracts defined in `@cpn-console/shared`, shared with client via @ts-rest. Auth: Keycloak + Fastify session. Permissions: BigInt bitmasks (`ProjectAuthorized`, `AdminAuthorized`). +Services receive configuration via injection +(`@Inject(xxxConfigFactory.KEY)` + `ConfigType`), +never `process.env`. + ## Plugin / Hook system Hook lifecycle: `pre` -> `main` -> `post` (sequential steps, parallel plugin execution). On failure: `revert`. @@ -33,11 +38,18 @@ Plugins are statically imported in `apps/server/src/plugins.ts`, then external p Each plugin: `index.ts` (Plugin interface), `infos.ts` (metadata/config), `functions.ts` (hook handlers). Plugins use TS module augmentation to extend `ProjectStore` and `Config` interfaces. +server-nestjs parity: every `eventEmitter.emitAsync('.')` must +have a matching `@OnEvent` consumer bridging to `capturePluginResult`, or +Keycloak/GitLab group syncs silently stop at cutover. + ## Database (Prisma) Multi-file schema in `apps/server/src/prisma/schema/*.prisma` (project, user, token, admin, topography). Migrations: standard Prisma Migrate. Major version data migrations in `migrations/v9/`. +`deleteMany`/`updateMany` with an `undefined` filter value matches ALL rows — +always filter by concrete id. `ProjectRole`/`Repository` foreign keys do not cascade. + ## Environment config - Files: `.env`, `.env.docker`, `.env.integ` in `apps/client/`, `apps/server/`, `apps/server-nestjs/` @@ -50,6 +62,10 @@ Migrations: standard Prisma Migrate. Major version data migrations in `migration - **Vitest**: unit tests everywhere (server, client, packages, plugins) — colocated `*.spec.ts` files - **Playwright**: E2E in `playwright/` (Chromium + Firefox, parallel) - Commands: `pnpm test` (all unit), `pnpm playwright:test` +- Deterministic tests: a faker draw must never be able to cross a branch + threshold (pin the draw window), otherwise CI flakes. +- server-nestjs unit specs use `mockDeep` for Prisma/config and no + describe-scope calls. ## Code quality @@ -64,6 +80,9 @@ Migrations: standard Prisma Migrate. Major version data migrations in `migration - Server: extends shared base, uses `ts-patch`/`tspc` for path transform in emitted JS - Client: does NOT extend shared base, uses `Bundler` module resolution - server-nestjs: standalone config with `emitDecoratorMetadata` + `experimentalDecorators` +- No `as` casts (including `as any`) to narrow an unknown or optional value — + use `if` type guards; include the offending identifier in the error message. +- Helpers return new objects; do not mutate inputs. ## Main commands @@ -83,3 +102,7 @@ Migrations: standard Prisma Migrate. Major version data migrations in `migration - Template env files use `-example` suffix (not `.example`) - `ci/scripts/init-env.sh` copies `*-example` to active equivalents (non-destructive) +- Fix at the shared source all callers route through, not a guard duplicated in + every caller. +- Never report done without running the gates: `pnpm lint` plus the targeted + vitest specs. From 931b8fbca8643fbf3106e0e95d486cd5f72e7890 Mon Sep 17 00:00:00 2001 From: William Phetsinorath Date: Wed, 9 Sep 2026 15:01:33 +0200 Subject: [PATCH 2/8] docs: record recurring agent rules in AGENTS.md Signed-off-by: William Phetsinorath Change-Id: I85e3e2b1a4d99e70a644510b3e57aca26a6a6964 --- AGENTS.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 49062e7049..6e8c58c026 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -104,5 +104,5 @@ always filter by concrete id. `ProjectRole`/`Repository` foreign keys do not cas - `ci/scripts/init-env.sh` copies `*-example` to active equivalents (non-destructive) - Fix at the shared source all callers route through, not a guard duplicated in every caller. -- Never report done without running the gates: `pnpm lint` plus the targeted - vitest specs. +- Before reporting done, format: `pnpm format`, then run the gates — `pnpm lint` + plus the targeted vitest specs. From 8e6d778855a37c368e96bc768c04ffbb8c9e8007 Mon Sep 17 00:00:00 2001 From: William Phetsinorath Date: Wed, 9 Sep 2026 15:02:55 +0200 Subject: [PATCH 3/8] docs: record recurring agent rules in AGENTS.md Signed-off-by: William Phetsinorath Change-Id: I3c95cf7ef8cd05942569058deca9270e6a6a6964 --- AGENTS.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 6e8c58c026..e8712ecb66 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -64,8 +64,8 @@ always filter by concrete id. `ProjectRole`/`Repository` foreign keys do not cas - Commands: `pnpm test` (all unit), `pnpm playwright:test` - Deterministic tests: a faker draw must never be able to cross a branch threshold (pin the draw window), otherwise CI flakes. -- server-nestjs unit specs use `mockDeep` for Prisma/config and no - describe-scope calls. +- Tests: always prefer `mockDeep` for mocks (type safety over plain + `vi.fn()`/hand-rolled mocks); no describe-scope calls. ## Code quality From 5d54320c17911f09c95808a04170b01363fab83b Mon Sep 17 00:00:00 2001 From: William Phetsinorath Date: Wed, 9 Sep 2026 15:03:38 +0200 Subject: [PATCH 4/8] docs: record recurring agent rules in AGENTS.md Signed-off-by: William Phetsinorath Change-Id: I450a91dab443a0459818630fcb0a95756a6a6964 --- AGENTS.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index e8712ecb66..5d039e07aa 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -80,8 +80,10 @@ always filter by concrete id. `ProjectRole`/`Repository` foreign keys do not cas - Server: extends shared base, uses `ts-patch`/`tspc` for path transform in emitted JS - Client: does NOT extend shared base, uses `Bundler` module resolution - server-nestjs: standalone config with `emitDecoratorMetadata` + `experimentalDecorators` -- No `as` casts (including `as any`) to narrow an unknown or optional value — - use `if` type guards; include the offending identifier in the error message. +- Strict type safety: no `as` casts (including `as any`), no `any`, and no + unchecked escapes of `unknown` — anything that breaks the type-checking + chain. Narrow with `if` type guards (include the offending identifier in the + error message); validate external data at the boundary instead of casting it. - Helpers return new objects; do not mutate inputs. ## Main commands From 2f0712d2e0d0b771133bb5d2d651b46f144ea2db Mon Sep 17 00:00:00 2001 From: William Phetsinorath Date: Wed, 9 Sep 2026 15:05:22 +0200 Subject: [PATCH 5/8] docs: record recurring agent rules in AGENTS.md Signed-off-by: William Phetsinorath Change-Id: Id53b931a40350908b49812c1a036b4686a6a6964 --- AGENTS.md | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 5d039e07aa..7e577a4a23 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -27,7 +27,7 @@ Resource-based organization in `apps/server/src/resources/`. Each resource follo API contracts defined in `@cpn-console/shared`, shared with client via @ts-rest. Auth: Keycloak + Fastify session. Permissions: BigInt bitmasks (`ProjectAuthorized`, `AdminAuthorized`). -Services receive configuration via injection +server-nestjs services receive configuration via injection (`@Inject(xxxConfigFactory.KEY)` + `ConfigType`), never `process.env`. @@ -53,9 +53,10 @@ always filter by concrete id. `ProjectRole`/`Repository` foreign keys do not cas ## Environment config - Files: `.env`, `.env.docker`, `.env.integ` in `apps/client/`, `apps/server/`, `apps/server-nestjs/` -- Templates: `*-example` suffix (git-tracked), active files gitignored +- Templates: `*-example` suffix (not `.example`; git-tracked), active files gitignored - Override chain (weakest to strongest): `.env` < `.env.docker` (if DOCKER=true) < `.env.integ` (if INTEGRATION=true) < explicit env vars - Server loading: `apps/server/src/utils/env.ts` | Client: `apps/client/vite.config.ts` +- `ci/scripts/init-env.sh` copies `*-example` to active equivalents (non-destructive) ## Testing @@ -64,7 +65,7 @@ always filter by concrete id. `ProjectRole`/`Repository` foreign keys do not cas - Commands: `pnpm test` (all unit), `pnpm playwright:test` - Deterministic tests: a faker draw must never be able to cross a branch threshold (pin the draw window), otherwise CI flakes. -- Tests: always prefer `mockDeep` for mocks (type safety over plain +- Always prefer `mockDeep` for mocks (type safety over plain `vi.fn()`/hand-rolled mocks); no describe-scope calls. ## Code quality @@ -100,10 +101,6 @@ always filter by concrete id. `ProjectRole`/`Repository` foreign keys do not cas - Release Please for automated versioning, changelogs, npm publish, Docker images, Helm chart updates - PR template: `.github/PULL_REQUEST_TEMPLATE.md` -## Conventions - -- Template env files use `-example` suffix (not `.example`) -- `ci/scripts/init-env.sh` copies `*-example` to active equivalents (non-destructive) - Fix at the shared source all callers route through, not a guard duplicated in every caller. - Before reporting done, format: `pnpm format`, then run the gates — `pnpm lint` From ac4ee3eacdef41fd3c7873c1f42e9fc3375a5851 Mon Sep 17 00:00:00 2001 From: William Phetsinorath Date: Wed, 9 Sep 2026 15:08:10 +0200 Subject: [PATCH 6/8] docs: record recurring agent rules in AGENTS.md Signed-off-by: William Phetsinorath Change-Id: Ic02f826cbe8ea0ff2df315df7e156e726a6a6964 --- AGENTS.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 7e577a4a23..be84c5ffc9 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -20,6 +20,7 @@ pnpm monorepo. Node >= 26, pnpm v11.8 ## Server architecture (Fastify) Resource-based organization in `apps/server/src/resources/`. Each resource follows a 3-file pattern: + - `router.ts` : route handlers (auth, permissions, delegates to business) - `business.ts` : business logic, orchestrates queries + hook calls - `queries.ts` : Prisma database queries @@ -74,6 +75,10 @@ always filter by concrete id. `ProjectRole`/`Repository` foreign keys do not cas - Stylelint for CSS/Vue in client - Husky hooks: pre-commit (lint-staged), commit-msg (commitlint), pre-push (unit tests) - Conventional commits enforced: `feat`, `fix`, `chore`, `docs`, `refactor`, `revert`, `build` +- Fix at the shared source all callers route through, not a guard duplicated in + every caller. +- Before reporting done, format: `pnpm format`, then run the gates — `pnpm lint` + plus the targeted vitest specs. ## TypeScript @@ -100,8 +105,3 @@ always filter by concrete id. `ProjectRole`/`Repository` foreign keys do not cas - Branches: `main` (protected) + `hotfix/*` - Release Please for automated versioning, changelogs, npm publish, Docker images, Helm chart updates - PR template: `.github/PULL_REQUEST_TEMPLATE.md` - -- Fix at the shared source all callers route through, not a guard duplicated in - every caller. -- Before reporting done, format: `pnpm format`, then run the gates — `pnpm lint` - plus the targeted vitest specs. From ed43c179c1affa702f39556d9a7ad38cc29ef906 Mon Sep 17 00:00:00 2001 From: William Phetsinorath Date: Wed, 9 Sep 2026 15:10:54 +0200 Subject: [PATCH 7/8] docs: record recurring agent rules in AGENTS.md Signed-off-by: William Phetsinorath Change-Id: Id11ed3c81d6e602a54063366e046bfe56a6a6964 --- AGENTS.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index be84c5ffc9..b001b164ca 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -89,7 +89,8 @@ always filter by concrete id. `ProjectRole`/`Repository` foreign keys do not cas - Strict type safety: no `as` casts (including `as any`), no `any`, and no unchecked escapes of `unknown` — anything that breaks the type-checking chain. Narrow with `if` type guards (include the offending identifier in the - error message); validate external data at the boundary instead of casting it. + error message). Parse, don't validate: parse external data at the input + boundary into precise types, so nothing downstream re-checks. - Helpers return new objects; do not mutate inputs. ## Main commands From 3d717d168fbddcb9e09c3393e7a9e8560bd34336 Mon Sep 17 00:00:00 2001 From: William Phetsinorath Date: Wed, 9 Sep 2026 15:13:22 +0200 Subject: [PATCH 8/8] docs: record recurring agent rules in AGENTS.md Signed-off-by: William Phetsinorath Change-Id: I76071d82f3862b1134697ea37073a2e46a6a6964 --- AGENTS.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index b001b164ca..51812e86a6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -89,8 +89,10 @@ always filter by concrete id. `ProjectRole`/`Repository` foreign keys do not cas - Strict type safety: no `as` casts (including `as any`), no `any`, and no unchecked escapes of `unknown` — anything that breaks the type-checking chain. Narrow with `if` type guards (include the offending identifier in the - error message). Parse, don't validate: parse external data at the input - boundary into precise types, so nothing downstream re-checks. + error message). Parse, don't validate: at the input boundary, turn external + data into the most precise type it allows — the returned type is the proof — + so nothing downstream re-checks; a check-and-throw that returns nothing + preserves nothing. - Helpers return new objects; do not mutate inputs. ## Main commands