422 lines
13 KiB
Markdown
422 lines
13 KiB
Markdown
# 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.
|
|
|
|
---
|
|
|
|
## 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 <PaywallModal feature="dashboard" />
|
|
|
|
// 4. JSX
|
|
return (
|
|
<div>...</div>
|
|
)
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 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"
|
|
```
|