Imported from tpicaud/cityborn (
.agents/AGENTS.md). Install upstream withnpx skills add tpicaud/cityborn --skill .agents. Copyright stays with the author.
Cityborn — guide pour agents
Monorepo pnpm/turbo, TypeScript partout. Deux règles au-dessus de tout : type-safe (jamais any, jamais affaiblir un type pour le compilateur) et bonnes pratiques d'architecture.
Ce fichier ne contient que le contexte transverse à tout le monorepo. Les conventions propres à un domaine vivent dans des skills chargés à la demande — voir Skills.
Vue d'ensemble
| Package / app | Rôle |
|---|---|
apps/backend |
NestJS + Prisma. API exposée via contrats ts-rest. |
apps/frontend, apps/back-office |
Next.js (App Router). |
apps/mobile |
Expo / React Native. |
packages/api |
Source de vérité des contrats : ts-rest et WebSocket + schémas zod, types qui transitent par l'API, ErrorCode + messages FR, map zod FR. |
packages/client |
Code partagé front + mobile qui ne transite pas par l'API, rangé par domaine et exposé en sous-chemins (./api, ./ws, ./auth, ./session, ./game, ./platform). Agnostique de Next, Expo et du rendu. |
packages/core |
Code partagé backend + client (front/mobile). Aujourd'hui la logique de jeu (src/game) ; a vocation à s'étoffer. Dépend de @cityborn/api. |
packages/design-system |
Composants UI partagés. |
Où mettre du code / un type partagé :
- transite par l'API →
@cityborn/api - partagé backend + front/mobile →
@cityborn/core - partagé front + mobile uniquement →
@cityborn/client - sinon local à l'app
Ne jamais dupliquer un type qui existe déjà dans un package.
Principes
- Le typage prime : un typage clair est une part majeure de la DX. Corriger en amont (narrowing, generics, zod) plutôt qu'un cast.
- Identifiants métier : utiliser les types brandés de
@cityborn/api. Valider une fois à l'entrée du domaine puis propager le type brandé ; réserverunknownaux sources réellement non typées et les schémas UUID aux identifiants dont ce format est garanti. Le branding conserve un format JSON/OpenAPIstring. - Si l'architecture touchée par une tâche est mauvaise : le signaler en fin de réponse avec une piste, sans implémenter le refacto ni dévier de la tâche demandée.
- Les instructions explicites de l'utilisateur priment sur les préférences de workflow de ce guide et des skills, sans lever les garde-fous de sécurité ni élargir le périmètre demandé.
- Avancer de façon autonome pour les actions réversibles et dans le périmètre demandé. Poser une question uniquement si une information manquante change matériellement le résultat ou si une autorisation listée ci-dessous est nécessaire.
Style de code
- Aucun commentaire dans le code, JSDoc compris. Le naming, les types et le découpage portent l'intention. Seule tolérance : le bloc
@deprecated/@deprecatedSinceposé par le skilldeprecate. Un pourquoi que le code ne peut pas porter (workaround, contrainte externe ou réglementaire) va dans le message de commit, la PR oudocs/— jamais en commentaire. - Naming précis : le nom reflète exactement la chose.
const service = new RateLimitService(redisService); // ❌ trop générique const rateLimitService = new RateLimitService(redisService); // ✅ - Éviter
as: un cast casse l'inférence et masque des erreurs. - Objets typés : quand un type nommé décrit l'objet créé, préférer
const objet: Type = { ... }. Réserversatisfies Typeaux cas où conserver le type inféré de l'expression est utile ; évitersatisfies Parameters<typeof méthode>[0]si un type nommé existe. - Variables locales : annoter explicitement chaque
constetletdès qu'un type approprié peut être nommé, y compris pour le résultat d'une méthode et les données de test. Ne laisser le type implicite que lorsqu'aucune annotation explicite pertinente n'est possible. - Éviter
else: early return ; ternaire seulement si vraiment nécessaire. import type { … }obligatoire pour les types (forcé par BiomeuseImportType).- Nouveaux fichiers : inspecter les fichiers voisins et suivre le précédent dominant. Préférer étendre un fichier existant quand sa responsabilité reste cohérente. Demander uniquement si plusieurs emplacements correspondent à des responsabilités différentes et que le choix affecte l'architecture publique.
Frontières du monorepo
packages/apiest la source de vérité des contrats. Toute évolution d'un contrat existant doit rester rétrocompatible (check:api-compaten CI). Un breaking change = bump de version d'API, jamais une modif silencieuse.- Modifier un contrat
@cityborn/api(route, event WS, schéma zod, type, enum) → skillapi-contract-change. - Déprécier / nettoyer un élément déprécié → skills
deprecate/check-and-remove-deprecated.
Configuration d'environnement
- Chaque application possède ses schémas Zod et expose une configuration typée en camelCase. Le code applicatif consomme cette configuration ; réserver les accès directs à
process.envaux modules de configuration et aux points d'entrée techniques des frameworks. - Valider chaque contexte à sa phase d'utilisation : démarrage NestJS, commande Prisma, développement et build pour les variables publiques Next.js, démarrage du serveur pour ses variables privées, prébuild pour la configuration native Expo, puis Metro ou EAS pour sa configuration client. Le typecheck reste indépendant de la configuration applicative.
- Conserver des accès littéraux aux variables publiques Next.js et Expo afin que leurs bundlers puissent les injecter.
- Tout ajout, suppression ou renommage met à jour dans le même lot le schéma, ses consommateurs, le
.env.examplede l'application et la configuration Turbo concernée.
Commandes
| Commande | Usage |
|---|---|
pnpm typecheck |
typecheck du monorepo (CI, doit toujours passer) |
pnpm format / pnpm format:check |
Biome (lint + format) |
pnpm check:api-compat |
rétrocompat des contrats API (CI) |
pnpm dev:web / pnpm dev:mobile |
stack web / mobile + backend + packages |
pnpm --dir apps/backend test |
tests backend unitaires (Jest) |
pnpm db:test:start / pnpm db:test:stop |
démarrer (et attendre les healthchecks) / arrêter uniquement Postgres + Redis du profil test |
pnpm test:int / pnpm test:e2e |
tests backend d'intégration / e2e sur la stack dédiée ; après pnpm install puis pnpm db:test:start |
pnpm --dir apps/backend test:all / pnpm --dir apps/backend test:cov |
tous les projets Jest / avec couverture |
pnpm --dir packages/api test |
tests de compatibilité OpenAPI |
pnpm db:start / db:migrate / db:reset |
DB locale (Docker + Prisma) ; lance aussi Redis (localhost:6379) + RedisInsight (localhost:5540) |
./scripts/setup-worktree.sh [chemin] [--skip-install] |
prépare un worktree : copie les .env du checkout principal puis pnpm install (commande /setup-worktree) |
./scripts/cleanup-worktree.sh [chemin] |
supprime un worktree dont tout le travail est publié et remet le checkout principal sur un main à jour ; lançable depuis le worktree à supprimer, sans argument (skill manuel cleanup-worktree) |
Stratégie de vérification
- Pendant l'implémentation, lancer d'abord les tests et vérifications ciblés sur les packages modifiés.
- Exécuter les contrôles transverses explicitement requis par un skill une seule fois avant le compte rendu final.
- Ne pas répéter un contrôle déjà réussi sans nouveau changement pertinent ou échec qui le justifie.
- Pour une modification réversible et de faible impact, ne pas ajouter un test qui ne ferait que reproduire l'implémentation. Tester les comportements et invariants significatifs.
Commits & PR
- Commit : message court, en anglais, une seule ligne (pas de corps) ; format Conventional Commits (
feat:,fix:,chore:,refactor:, …) ; pas de signature. - PR : publiée en draft ; titre au format
#<num_issue> - <titre_issue>.
Demander avant d'agir
- Ajout d'une dépendance npm.
- Breaking change sur un contrat OpenAPI de
packages/api. - Ajout d'une ligne dans
packages/api/tools/compat/err-ignore.txt(bypass decheck:api-compat).
Garde-fous
- Ne jamais modifier une migration Prisma déjà appliquée/mergée : toujours en créer une nouvelle (
pnpm db:migrate). - Ne pas contourner Biome ni le typecheck (
// biome-ignore,@ts-ignorede confort interdits). - Ne pas toucher aux
overridesdepnpm-workspace.yaml(la plupart corrigent des CVE). - Ne jamais lancer le front ni le mobile pour tester l'UI — demander un test manuel.
Skills
Les conventions propres à un domaine sont découvertes automatiquement depuis les descriptions de .agents/skills/*/SKILL.md.
Maintenir ce guide
Tout changement qui modifie une convention ou une décision d'archi doit mettre à jour, dans le même lot, soit ce fichier (si transverse), soit le SKILL.md concerné (si propre à un domaine) — jamais les deux, jamais en double. Le guide reflète l'état réel du code.
