docs: enrichir ARCHITECTURE, DEVELOPMENT_PRINCIPLES, GOLDEN_DATASET, TESTS_AUTOMATISES, TEST_ENVIRONMENT
This commit is contained in:
parent
52b8e9d011
commit
f343fb4696
5 changed files with 1649 additions and 1476 deletions
|
|
@ -1,10 +1,8 @@
|
|||
# DEVELOPMENT_PRINCIPLES.md — Expria / Coach TCF Canada
|
||||
# DEVELOPMENT_PRINCIPLES.md — Expria Frontend
|
||||
|
||||
> **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.
|
||||
> Ce document définit les règles que toute session Claude Code sur le frontend doit lire et respecter, sans exception.
|
||||
> Adapté de `DEVELOPMENT_PRINCIPLES.md` du backend — les principes sont les mêmes, les règles spécifiques sont ajustées.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -13,10 +11,12 @@
|
|||
**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
|
||||
2. Lire `ARCHITECTURE.md` frontend
|
||||
3. Lire `PLANS_TARIFAIRES.md` (copie du backend)
|
||||
4. Lire `PARCOURS_UTILISATEURS.md` (section du plan concerné)
|
||||
5. Consulter les ADRs pertinents dans `docs/adr/`
|
||||
6. Annoncer : "Documents lus. Voici mon plan pour cette session."
|
||||
7. Produire un plan détaillé et attendre la validation
|
||||
|
||||
**Tu ne passes à l'implémentation que quand Hermann dit "GO".**
|
||||
|
||||
|
|
@ -24,7 +24,7 @@
|
|||
|
||||
## 1. Le cycle de travail obligatoire
|
||||
|
||||
Chaque modification, petite ou grande, suit ce cycle sans exception :
|
||||
Identique au backend :
|
||||
|
||||
```
|
||||
ÉTAPE 1 — ANALYSE
|
||||
|
|
@ -47,13 +47,15 @@ Chaque modification, petite ou grande, suit ce cycle sans exception :
|
|||
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"
|
||||
Lancer npm run typecheck
|
||||
Lancer npm run test
|
||||
Annoncer le résultat : "Typecheck OK. 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)
|
||||
Indiquer les tests manuels à rejouer (Golden Dataset)
|
||||
Proposer un message de commit
|
||||
```
|
||||
|
||||
---
|
||||
|
|
@ -63,141 +65,184 @@ Chaque modification, petite ou grande, suit ce cycle sans exception :
|
|||
### 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.
|
||||
Si une tâche nécessite de modifier plus de 3 fichiers, elle est découpée en plusieurs étapes avec validation intermédiaire.
|
||||
|
||||
### Règle C — Tests verts avant de continuer
|
||||
Après chaque étape d'implémentation :
|
||||
`npm run typecheck` doit retourner 0 erreur.
|
||||
`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) { ... }
|
||||
import { hasAccess } from '@/entities/user/lib'
|
||||
if (hasAccess(user.plan, 'exam_mode')) { ... }
|
||||
```
|
||||
|
||||
Voir ADR 005 pour le détail.
|
||||
|
||||
### Règle E — Jamais de clé privée dans le frontend
|
||||
Les variables suivantes n'existent que dans le backend :
|
||||
Seules ces variables sont autorisées dans le frontend :
|
||||
- `VITE_API_URL`
|
||||
- `VITE_SUPABASE_URL`
|
||||
- `VITE_SUPABASE_ANON_KEY` (clé publique)
|
||||
- `VITE_ENABLE_T2_LIVE` (flag)
|
||||
- `VITE_SENTRY_DSN` (si monitoring actif)
|
||||
|
||||
Les variables suivantes **n'existent jamais** dans le frontend :
|
||||
- `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 d'appel direct à Supabase pour des données métier
|
||||
Supabase côté frontend est **exclusivement** utilisé pour l'auth :
|
||||
```typescript
|
||||
// ✅ Autorisé
|
||||
supabase.auth.signInWithPassword(...)
|
||||
supabase.auth.signOut()
|
||||
supabase.auth.getSession()
|
||||
|
||||
### 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.
|
||||
// ❌ Interdit
|
||||
supabase.from('productions').select()
|
||||
supabase.from('profiles').update(...)
|
||||
```
|
||||
|
||||
### 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.
|
||||
Toute lecture/écriture de données métier passe par le backend Hono.
|
||||
|
||||
### 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.
|
||||
### Règle G — access.ts doit rester identique au backend
|
||||
`src/entities/user/access.ts` est une **copie bit-à-bit** de `expria-backend/src/lib/access.ts`.
|
||||
Toute modification se fait dans les deux dépôts, dans le même commit logique.
|
||||
Voir ADR 004.
|
||||
|
||||
### Règle H — Jamais de logique métier dans `features/`
|
||||
La logique métier (permissions, quotas, floutage, règles de validation) vit exclusivement dans `src/entities/<domaine>/lib.ts`.
|
||||
Les composants React de `features/` appellent ces fonctions, sans les réimplémenter.
|
||||
|
||||
### Règle I — 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 J — Sécurité
|
||||
- Jamais `dangerouslySetInnerHTML` sans `DOMPurify`
|
||||
- Jamais `eval`, `new Function`, `setTimeout(string, ...)`
|
||||
- Jamais de secret dans `localStorage` ou `sessionStorage`
|
||||
- Jamais de `console.log` de données utilisateur (email, JWT, payload API)
|
||||
- Toujours passer par `apiFetch` (jamais `fetch` nu)
|
||||
|
||||
Voir `SECURITY.md` pour le détail.
|
||||
|
||||
### Règle K — Pas de worktree Git
|
||||
Claude Code ne crée jamais de worktree Git.
|
||||
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)
|
||||
```
|
||||
src/
|
||||
app/ → camelCase.tsx (providers.tsx, router.tsx)
|
||||
entities/
|
||||
<domaine>/
|
||||
types.ts → interfaces et types
|
||||
lib.ts → fonctions pures métier
|
||||
api.ts → fonctions d'appel API
|
||||
access.ts → cas spécial : identique au backend
|
||||
features/
|
||||
<feature>/
|
||||
components/ → PascalCase.tsx (LoginForm.tsx, PaywallModal.tsx)
|
||||
pages/ → PascalCase.tsx (LoginPage.tsx, DashboardPage.tsx)
|
||||
hooks/ → camelCase.ts (useAuth.ts, usePlan.ts)
|
||||
lib/ → camelCase.ts (ws-client.ts, audio.ts)
|
||||
state/ → kebab-case.ts (t2-machine.ts)
|
||||
shared/
|
||||
components/ → PascalCase.tsx
|
||||
hooks/ → camelCase.ts
|
||||
lib/ → kebab-case.ts (api-client.ts, auth-client.ts)
|
||||
types/ → camelCase.ts
|
||||
config/ → camelCase.ts
|
||||
|
||||
__tests__/ → <nom-du-fichier-testé>.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) { }
|
||||
function hasAccess(plan: Plan, feature: BooleanPermission): boolean { }
|
||||
function canSimulate(plan: Plan, used: number): SimulationCheck { }
|
||||
|
||||
// Composants React : PascalCase
|
||||
function DashboardPage() { }
|
||||
function PaywallModal() { }
|
||||
|
||||
// Hooks : use + PascalCase (convention React)
|
||||
function useAuth() { }
|
||||
function usePlan() { }
|
||||
|
||||
// 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: {...} }
|
||||
const PLANS = { ... }
|
||||
```
|
||||
|
||||
### Structure d'une route Hono (backend)
|
||||
### Structure d'un composant de feature
|
||||
|
||||
```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()
|
||||
// features/dashboard/pages/DashboardPage.tsx
|
||||
|
||||
// 2. Logique métier (déléguer au controller)
|
||||
const result = await simulationController.create(body, c.get('user'))
|
||||
import { usePlan } from '../hooks/usePlan'
|
||||
import { hasAccess } from '@/entities/user/lib'
|
||||
|
||||
// 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) {
|
||||
export function DashboardPage() {
|
||||
// 1. Hooks en premier
|
||||
const { user } = useAuth()
|
||||
const perms = usePlan()
|
||||
const { plan, isLoading, error } = usePlan()
|
||||
|
||||
// 2. Handlers
|
||||
const handleClick = () => { }
|
||||
// 2. Gestion des états de chargement et d'erreur
|
||||
if (isLoading) return <Spinner />
|
||||
if (error) return <ErrorView error={error} />
|
||||
|
||||
// 3. Rendu conditionnel selon le plan
|
||||
if (!perms.dashboard) return <PaywallModal feature="dashboard" />
|
||||
// 3. Logique de permission via hasAccess
|
||||
if (!hasAccess(plan.plan, 'dashboard')) {
|
||||
return <DashboardFreeAperçu />
|
||||
}
|
||||
|
||||
// 4. JSX
|
||||
return (
|
||||
<div>...</div>
|
||||
)
|
||||
// 4. Rendu principal
|
||||
return <DashboardComplete plan={plan} />
|
||||
}
|
||||
```
|
||||
|
||||
### Structure d'un hook custom
|
||||
|
||||
```typescript
|
||||
// features/dashboard/hooks/usePlan.ts
|
||||
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { getPlanStatus } from '@/entities/user/api'
|
||||
|
||||
export function usePlan() {
|
||||
return useQuery({
|
||||
queryKey: ['plan'],
|
||||
queryFn: getPlanStatus,
|
||||
staleTime: 5 * 60 * 1000, // 5 min
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -205,147 +250,160 @@ export function NomDuComposant({ prop1, prop2 }: Props) {
|
|||
|
||||
## 4. Gestion des erreurs
|
||||
|
||||
### Backend — toujours retourner une erreur structurée
|
||||
### Côté api-client
|
||||
|
||||
```typescript
|
||||
// ✅ Format d'erreur standard
|
||||
return c.json({
|
||||
error: true,
|
||||
code: 'QUOTA_REACHED',
|
||||
message: 'Quota de simulations atteint pour ce plan',
|
||||
}, 403)
|
||||
// shared/lib/api-client.ts
|
||||
// En cas de succès : retourne le payload typé T directement (pas d'enveloppe)
|
||||
// En cas d'erreur : throw une ApiError typée
|
||||
// TanStack Query catch automatiquement et rend l'erreur disponible dans .error
|
||||
|
||||
// ❌ 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
|
||||
}
|
||||
export async function apiFetch<T>(
|
||||
path: string,
|
||||
options: RequestInit & { timeoutMs?: number } = {}
|
||||
): Promise<T> {
|
||||
// 1. Ajouter headers (Authorization Bearer, X-API-Version, Content-Type)
|
||||
// 2. Appliquer AbortController pour timeout
|
||||
// 3. Retry avec backoff exponentiel sur erreurs réseau + 5xx
|
||||
// 4. Parser la réponse :
|
||||
// - Si 2xx → retourner le JSON tel quel typé T
|
||||
// - Si 4xx/5xx → parser l'erreur backend { error: true, code, message } → throw ApiError
|
||||
}
|
||||
```
|
||||
|
||||
Le format exact des erreurs backend est documenté dans `ARCHITECTURE.md` section 5 (confirmé par audit du 2026-04-17).
|
||||
|
||||
### Côté hook (utilisation)
|
||||
|
||||
```typescript
|
||||
const mutation = useMutation({
|
||||
mutationFn: submitProduction,
|
||||
onError: (err: ApiError) => {
|
||||
switch (err.code) {
|
||||
case 'QUOTA_REACHED':
|
||||
openUpgradeModal()
|
||||
break
|
||||
case 'PLAN_INSUFFICIENT':
|
||||
openPaywall()
|
||||
break
|
||||
case 'AUTH_REQUIRED':
|
||||
redirectToLogin()
|
||||
break
|
||||
case 'VALIDATION_ERROR':
|
||||
case 'INVALID_BODY':
|
||||
toast.error('Données invalides. Vérifiez votre saisie.')
|
||||
break
|
||||
default:
|
||||
toast.error('Une erreur est survenue. Réessayez dans quelques instants.')
|
||||
}
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
> **Note** : `VALIDATION_ERROR` et `INVALID_BODY` sont deux codes backend pour la même classe d'erreur (corps invalide). Côté frontend, on les gère de la même manière. L'unification côté backend est tracée dans `TECH_DEBT.md` backend (TD-15).
|
||||
|
||||
### Messages utilisateur
|
||||
|
||||
- En français
|
||||
- Clairs et non techniques
|
||||
- Orientés action
|
||||
|
||||
```
|
||||
// ✅ Bons messages
|
||||
"Vous avez utilisé vos 5 simulations gratuites. Passez en Standard pour continuer."
|
||||
"Le mode Examen est réservé au plan Premium."
|
||||
"Une erreur est survenue. Réessayez dans quelques instants."
|
||||
|
||||
// ❌ Mauvais messages
|
||||
"Erreur 403 : quota_reached"
|
||||
"TypeError: Cannot read property 'plan' of undefined"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 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
|
||||
## 5. 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)
|
||||
- src/features/dashboard/pages/DashboardPage.tsx (ajouter affichage conditionnel)
|
||||
- src/entities/user/lib.ts (ajouter helper formatRemainingSimulations)
|
||||
|
||||
Fichiers créés :
|
||||
- src/lib/__tests__/canUserSimulate.test.ts
|
||||
- src/features/dashboard/components/RemainingSimulationsBadge.tsx
|
||||
- src/features/dashboard/components/__tests__/RemainingSimulationsBadge.test.tsx
|
||||
|
||||
Fichiers supprimés :
|
||||
- aucun
|
||||
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
|
||||
- La modification de DashboardPage peut affecter l'affichage pour les 3 plans
|
||||
→ vérifier les 3 comptes de test après implémentation
|
||||
|
||||
É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
|
||||
1. Créer le composant RemainingSimulationsBadge + son test
|
||||
2. Créer le helper formatRemainingSimulations dans entities/user/lib.ts + test
|
||||
3. Intégrer dans DashboardPage
|
||||
|
||||
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
|
||||
- src/features/dashboard/pages/DashboardPage.tsx : intégration badge ligne 42
|
||||
|
||||
Créé :
|
||||
- src/lib/__tests__/canUserSimulate.test.ts (7 tests)
|
||||
- src/features/dashboard/components/RemainingSimulationsBadge.tsx
|
||||
- src/entities/user/lib.ts : fonction formatRemainingSimulations
|
||||
+ 2 fichiers de tests (3 tests supplémentaires)
|
||||
|
||||
Tests : 41/41 passés ✅
|
||||
Typecheck : OK
|
||||
Tests : 27/27 passés ✅
|
||||
|
||||
Tests manuels à rejouer :
|
||||
- Golden Dataset Groupe 2 (B2, B7) — quota Free
|
||||
- Golden Dataset Groupe 3 (C2) — illimité Standard
|
||||
- Golden Dataset Groupe 2 (B1) — Dashboard Free avec compteur
|
||||
- Golden Dataset Groupe 3 (C1) — Dashboard Standard sans compteur
|
||||
|
||||
Message de commit proposé :
|
||||
feat(dashboard): afficher le compteur de simulations restantes (Free)
|
||||
```
|
||||
|
||||
### 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
|
||||
- Le backend doit être modifié (session différente, dépôt différent)
|
||||
- Une clé privée serait nécessaire côté frontend
|
||||
|
||||
---
|
||||
|
||||
## 7. Checklist de démarrage de session
|
||||
## 6. 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,
|
||||
[ ] Les documents de référence ont été lus
|
||||
(ARCHITECTURE.md, DEVELOPMENT_PRINCIPLES.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 ADRs pertinents ont été consultés
|
||||
(docs/adr/)
|
||||
|
||||
[ ] L'environnement de dev fonctionne
|
||||
(npm run dev, pas d'erreur au démarrage)
|
||||
|
||||
[ ] Les tests automatisés sont tous verts
|
||||
(npm run test → 0 échec)
|
||||
(npm run typecheck && 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)
|
||||
|
|
@ -353,70 +411,53 @@ Avant chaque session Claude Code, vérifier :
|
|||
|
||||
---
|
||||
|
||||
## 8. Checklist de fin de session
|
||||
|
||||
Avant de clôturer chaque session Claude Code :
|
||||
## 7. Checklist de fin de session
|
||||
|
||||
```
|
||||
[ ] Les tests automatisés sont tous verts
|
||||
(npm run test → 0 échec)
|
||||
(npm run typecheck && 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]"
|
||||
Format : "feat(<scope>): ...", "fix(<scope>): ...", "refactor(<scope>): ..."
|
||||
Scopes possibles : auth, dashboard, simulation, t2-live, billing, shared, docs
|
||||
|
||||
Exemples :
|
||||
"feat: ajout vérification quota simulations free"
|
||||
"fix: correction rapport flouté plan découverte"
|
||||
"refactor: extraction logique permissions dans access.ts"
|
||||
"feat(dashboard): affichage conditionnel selon le plan"
|
||||
"fix(auth): corriger la redirection après logout"
|
||||
"refactor(entities/user): extraire hasAccess dans un fichier dédié"
|
||||
|
||||
[ ] DEVELOPMENT_PRINCIPLES.md et ARCHITECTURE.md
|
||||
sont à jour si une décision a changé
|
||||
[ ] docs/CHANGELOG.md mis à jour avec un résumé de la session
|
||||
|
||||
[ ] ARCHITECTURE.md / SECURITY.md mis à 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
|
||||
## 8. Ce que Claude Code ne doit jamais faire
|
||||
|
||||
```
|
||||
// ✅ 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
|
||||
❌ Modifier entities/user/access.ts sans prévenir (source de vérité partagée avec le backend)
|
||||
❌ Ajouter une dépendance npm sans demander la validation
|
||||
❌ Modifier le schéma Supabase directement dans le dashboard
|
||||
❌ Appeler Supabase pour autre chose que l'auth
|
||||
❌ É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"
|
||||
❌ Utiliser dangerouslySetInnerHTML sans DOMPurify
|
||||
❌ Créer un worktree Git
|
||||
❌ Modifier le backend depuis une session frontend (dépôt différent)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. Historique
|
||||
|
||||
| Version | Date | Changements |
|
||||
|---|---|---|
|
||||
| 1.0 | 2026-04-17 | Création, adaptée de la version backend |
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue