# DEVELOPMENT_PRINCIPLES.md — Expria / Coach TCF Canada > **Document de référence — Version 1.0** > Ce document définit les règles que Claude Code doit lire et respecter > à chaque session de développement, sans exception. > Il est conçu pour un projet maintenu par un fondateur non-technique > assisté par l'IA. --- ## INSTRUCTION OBLIGATOIRE POUR CLAUDE CODE **Avant d'écrire la moindre ligne de code, tu dois :** 1. Lire ce fichier en entier 2. Lire ARCHITECTURE.md 3. Lire PLANS_TARIFAIRES.md 4. Annoncer : "Documents lus. Voici mon plan pour cette session." 5. Produire un plan détaillé et attendre la validation **Tu ne passes à l'implémentation que quand Hermann dit "GO".** --- ## 1. Le cycle de travail obligatoire Chaque modification, petite ou grande, suit ce cycle sans exception : ``` ÉTAPE 1 — ANALYSE Lire les fichiers concernés Identifier tous les fichiers qui seront impactés Identifier les risques de régression ÉTAPE 2 — PLAN Produire un plan détaillé : — Fichiers modifiés (liste exhaustive) — Fichiers créés (liste exhaustive) — Fichiers supprimés (liste exhaustive) — Risques identifiés — Étapes dans l'ordre Attendre la validation de Hermann ÉTAPE 3 — IMPLÉMENTATION Exécuter le plan validé Ne pas s'écarter du plan sans signaler le changement Maximum 3 fichiers touchés par étape ÉTAPE 4 — VÉRIFICATION Lancer npm run test (backend) Annoncer le résultat : "X tests passés, 0 échecs" Si un test échoue : STOP — corriger avant de continuer ÉTAPE 5 — RÉSUMÉ Lister exactement ce qui a été modifié Indiquer les tests manuels à rejouer (référence au Golden Dataset) ``` --- ## 2. Règles absolues — ne jamais enfreindre ### Règle A — Plan avant code Claude Code ne commence jamais à coder sans plan validé. Même pour une modification "simple" d'une ligne. La simplicité apparente est trompeuse dans un projet avec des permissions par plan. ### Règle B — Maximum 3 fichiers par étape Si une tâche nécessite de modifier plus de 3 fichiers, elle est découpée en plusieurs étapes avec validation intermédiaire. Chaque étape est testée avant de passer à la suivante. ### Règle C — Tests verts avant de continuer Après chaque étape d'implémentation : `npm run test` doit retourner 0 échec. Si un test échoue, on corrige avant de passer à l'étape suivante. On ne livre jamais une étape avec des tests rouges. ### Règle D — Jamais de logique de plan en dur dans le code Toutes les vérifications de permissions lisent depuis `lib/access.ts`. Exemple interdit : ```typescript // ❌ JAMAIS if (user.plan === 'premium') { ... } if (user.plan !== 'free') { ... } ``` Exemple correct : ```typescript // ✅ TOUJOURS const perms = getPlanPermissions(user.plan) if (perms.exam_mode) { ... } ``` ### Règle E — Jamais de clé privée dans le frontend Les variables suivantes n'existent que dans le backend : - `SUPABASE_SERVICE_ROLE_KEY` - `GEMINI_API_KEY` - `DEEPSEEK_API_KEY` - `STRIPE_SECRET_KEY` - `STRIPE_WEBHOOK_SECRET` Le frontend n'a accès qu'à : - `VITE_SUPABASE_ANON_KEY` (clé publique Supabase) - `VITE_API_URL` (URL du backend) ### Règle F — Jamais de modification de la base de données sans migration SQL Toute modification du schéma Supabase passe par un fichier de migration dans `supabase/migrations/`. Jamais de modification directe dans le dashboard Supabase sans fichier de migration correspondant. ### Règle G — Jamais de suppression de données utilisateur Aucune opération `DELETE` sur les tables `productions` ou `profiles` sauf dans les scripts de test explicitement balisés. En cas de doute : archiver, ne pas supprimer. ### Règle H — Signaler tout écart par rapport au plan Si pendant l'implémentation Claude Code réalise que le plan doit être modifié, il STOP, signale le changement, explique pourquoi, et attend une nouvelle validation. Il ne prend jamais de décision architecturale de sa propre initiative. ### Règle I — Pas de worktree Git Claude Code ne crée jamais de worktree Git (`git worktree add`). Toutes les modifications se font directement dans le dossier du projet principal. --- ## 3. Structure du code — conventions ### Nommage des fichiers ``` frontend/src/ pages/ → PascalCase.tsx (Dashboard.tsx, Simulation.tsx) components/ → PascalCase.tsx (RapportCard.tsx, PaywallModal.tsx) hooks/ → camelCase.ts (useAuth.ts, usePlan.ts) api/ → camelCase.ts (simulations.ts, corrections.ts) lib/ → camelCase.ts (access.ts, supabase.ts) types/ → camelCase.ts (plans.ts, simulation.ts) backend/src/ routes/ → camelCase.ts (simulations.ts, stripe.ts) controllers/ → camelCase.ts (simulationController.ts) middleware/ → camelCase.ts (auth.ts, plan.ts) lib/ → camelCase.ts (access.ts, deepseek.ts) __tests__/ → camelCase.test.ts (canUserSimulate.test.ts) ``` ### Nommage des variables et fonctions ```typescript // Variables : camelCase const userPlan = 'premium' const simulationsUsed = 5 // Fonctions : camelCase, verbe en premier function getUserPlan(userId: string) { } function canUserSimulate(profile: Profile) { } function updateUserPlan(userId: string, plan: Plan) { } // Types et interfaces : PascalCase type Plan = 'free' | 'standard' | 'premium' interface UserProfile { } interface SimulationResult { } // Constantes globales : SCREAMING_SNAKE_CASE const MAX_FREE_SIMULATIONS = 5 const PLANS = { free: {...}, standard: {...}, premium: {...} } ``` ### Structure d'une route Hono (backend) ```typescript // Pattern obligatoire pour toutes les routes app.post('/simulations', authMiddleware, planMiddleware('simulation'), async (c) => { // 1. Récupérer et valider les données entrantes const body = await c.req.json() // 2. Logique métier (déléguer au controller) const result = await simulationController.create(body, c.get('user')) // 3. Retourner la réponse return c.json(result, 201) }) ``` ### Structure d'un composant React (frontend) ```typescript // Pattern obligatoire pour tous les composants interface Props { // Toujours typer les props } export function NomDuComposant({ prop1, prop2 }: Props) { // 1. Hooks en premier const { user } = useAuth() const perms = usePlan() // 2. Handlers const handleClick = () => { } // 3. Rendu conditionnel selon le plan if (!perms.dashboard) return // 4. JSX return (
...
) } ``` --- ## 4. Gestion des erreurs ### Backend — toujours retourner une erreur structurée ```typescript // ✅ Format d'erreur standard return c.json({ error: true, code: 'QUOTA_REACHED', message: 'Quota de simulations atteint pour ce plan', }, 403) // ❌ Jamais return c.json({ error: 'error' }, 500) throw new Error('Something went wrong') ``` ### Codes d'erreur standardisés ``` AUTH_REQUIRED → 401 Pas de JWT ou JWT invalide PLAN_INSUFFICIENT → 403 Feature non disponible pour ce plan QUOTA_REACHED → 403 Quota de simulations épuisé INVALID_PLAN → 400 Valeur de plan inconnue SIMULATION_NOT_FOUND → 404 Simulation inexistante ou non accessible STRIPE_WEBHOOK_INVALID → 400 Signature webhook invalide INTERNAL_ERROR → 500 Erreur serveur inattendue ``` ### Frontend — toujours gérer les erreurs API ```typescript // ✅ Pattern obligatoire pour tout appel API try { const result = await api.simulations.create(data) // succès } catch (error) { if (error.code === 'QUOTA_REACHED') { // afficher modal upgrade } else if (error.code === 'PLAN_INSUFFICIENT') { // afficher paywall } else { // afficher message d'erreur générique } } ``` --- ## 5. Sécurité — vérifications obligatoires ### Toute route backend qui touche à des données utilisateur doit : ```typescript // 1. Vérifier l'authentification app.use(authMiddleware) // vérifie le JWT Supabase // 2. Vérifier le plan si nécessaire app.use(planMiddleware('exam_mode')) // vérifie la permission // 3. Vérifier que la ressource appartient à l'utilisateur const simulation = await db.getSimulation(id) if (simulation.user_id !== currentUser.id) { return c.json({ error: true, code: 'AUTH_REQUIRED' }, 401) } ``` ### La vérification des permissions se fait TOUJOURS côté backend Le frontend peut masquer des boutons selon le plan — c'est de l'UX. Mais la vérification réelle se fait dans le middleware backend. Un utilisateur malveillant peut appeler l'API directement sans passer par le frontend. --- ## 6. Communication avec Hermann ### Format du plan (avant implémentation) ``` 📋 PLAN — [nom de la tâche] Fichiers modifiés : - src/routes/simulations.ts (ajouter la vérification de quota) - src/lib/access.ts (ajouter la fonction canUserSimulate) Fichiers créés : - src/lib/__tests__/canUserSimulate.test.ts Fichiers supprimés : - aucun Risques identifiés : - La modification de access.ts peut affecter toutes les routes qui utilisent getPlanPermissions → vérifier après modification Étapes : 1. Créer la fonction canUserSimulate dans access.ts 2. Écrire le test canUserSimulate.test.ts 3. Ajouter la vérification dans la route POST /simulations En attente de validation avant de commencer. ``` ### Format du résumé (après implémentation) ``` ✅ RÉSUMÉ — [nom de la tâche] Modifié : - src/routes/simulations.ts : ajout vérification quota ligne 42 - src/lib/access.ts : ajout fonction canUserSimulate Créé : - src/lib/__tests__/canUserSimulate.test.ts (7 tests) Tests : 41/41 passés ✅ Tests manuels à rejouer : - Golden Dataset Groupe 2 (B2, B7) — quota Free - Golden Dataset Groupe 3 (C2) — illimité Standard ``` ### Quand Claude Code doit s'arrêter et demander - Un test automatisé échoue après modification - La tâche nécessite de modifier plus de 3 fichiers par étape - Une décision architecturale non documentée est requise - Une ambiguïté sur le comportement attendu selon un plan tarifaire - Une clé privée serait nécessaire côté frontend --- ## 7. Checklist de démarrage de session Avant chaque session Claude Code, vérifier : ``` [ ] Les 4 documents de référence ont été lus (DEVELOPMENT_PRINCIPLES.md, ARCHITECTURE.md, PLANS_TARIFAIRES.md, PARCOURS_UTILISATEURS.md) [ ] L'environnement de test est dans l'état attendu (voir TEST_ENVIRONMENT.md — script de réinitialisation) [ ] Les tests automatisés sont tous verts (npm run test → 0 échec) [ ] Un commit Git propre existe avant de commencer (état de repli en cas de régression) [ ] La tâche de la session est clairement définie (une seule tâche par session) ``` --- ## 8. Checklist de fin de session Avant de clôturer chaque session Claude Code : ``` [ ] Les tests automatisés sont tous verts (npm run test → 0 échec) [ ] Le résumé de session a été produit (fichiers modifiés, tests passés) [ ] Les tests manuels du Golden Dataset ont été rejoués (groupes concernés par les modifications) [ ] Un commit Git a été fait avec un message clair Format : "feat: [description]" ou "fix: [description]" Exemples : "feat: ajout vérification quota simulations free" "fix: correction rapport flouté plan découverte" "refactor: extraction logique permissions dans access.ts" [ ] DEVELOPMENT_PRINCIPLES.md et ARCHITECTURE.md sont à jour si une décision a changé ``` --- ## 9. Messages d'erreur utilisateur — ton et format Les messages affichés à l'utilisateur doivent être : - En français - Clairs et non techniques - Orientés action (que faire ensuite) - Non condescendants ``` // ✅ Bons messages "Vous avez utilisé vos 5 simulations gratuites. Passez en Standard pour continuer votre préparation." "Le mode Examen est réservé au plan Premium. Passez en Premium pour vous entraîner en conditions réelles." "Une erreur est survenue. Veuillez réessayer dans quelques instants." // ❌ Mauvais messages "Erreur 403 : quota_reached" "Vous n'êtes pas autorisé à effectuer cette action." "Internal server error" ``` --- ## 10. Ce que Claude Code ne doit jamais faire ``` ❌ Modifier lib/access.ts sans signaler que TOUTES les permissions sont impactées ❌ Ajouter une dépendance npm sans demander la validation ❌ Modifier le schéma Supabase directement dans le dashboard ❌ Écrire une logique de plan en dur (if plan === 'premium') ❌ Exposer une clé privée dans le frontend ❌ Supprimer des données utilisateur (productions, profiles) ❌ Modifier plus de 3 fichiers sans validation intermédiaire ❌ Passer à l'étape suivante si un test est rouge ❌ Prendre une décision architecturale sans la documenter ❌ Coder sans plan validé, même pour "juste une petite modification" ```