1122 lines
42 KiB
JSON
1122 lines
42 KiB
JSON
{
|
|
"meta": {
|
|
"title": "Veza Integration Perfection TodoList",
|
|
"description": "TodoList focalisée exclusivement sur la connexion parfaite Backend Go ↔ Frontend React. Ignorer modules Rust.",
|
|
"generated_at": "2025-12-25T12:00:00Z",
|
|
"version": "2.0.0",
|
|
"scope": {
|
|
"included": ["apps/web/", "veza-backend-api/"],
|
|
"excluded": ["veza-chat-server/", "veza-stream-server/", "veza-common/"]
|
|
},
|
|
"target": "Score intégration 10/10 - Connexion parfaite backend/frontend",
|
|
"current_score": "6.5/10",
|
|
"target_score": "10/10",
|
|
"audit_date": "2025-12-25"
|
|
},
|
|
"summary": {
|
|
"by_priority": {
|
|
"P0_blocker": 3,
|
|
"P1_critical": 8,
|
|
"P2_major": 15,
|
|
"P3_minor": 6
|
|
},
|
|
"by_category": {
|
|
"INT-CORS": 2,
|
|
"INT-AUTH": 4,
|
|
"INT-TYPE": 8,
|
|
"INT-API": 5,
|
|
"INT-ENDPOINT": 6,
|
|
"INT-CLEANUP": 4,
|
|
"INT-TEST": 2,
|
|
"INT-DOC": 1
|
|
},
|
|
"by_side": {
|
|
"backend_only": 10,
|
|
"frontend_only": 14,
|
|
"both_sides": 8
|
|
},
|
|
"estimated_total_hours": 48
|
|
},
|
|
"categories": {
|
|
"INT-CORS": "Configuration CORS et origins pour production",
|
|
"INT-AUTH": "Authentification, tokens, refresh, CSRF",
|
|
"INT-TYPE": "Alignement parfait types TypeScript ↔ Go DTOs",
|
|
"INT-API": "Client API, intercepteurs, services, format réponses",
|
|
"INT-ENDPOINT": "Endpoints manquants ou avec incohérences",
|
|
"INT-CLEANUP": "Suppression duplication, code legacy, standardisation",
|
|
"INT-TEST": "Tests d'intégration E2E frontend ↔ backend",
|
|
"INT-DOC": "Documentation API OpenAPI/Swagger"
|
|
},
|
|
"phases": [
|
|
{
|
|
"id": "PHASE-INT-1",
|
|
"name": "Production Blockers",
|
|
"description": "Problèmes qui empêchent le déploiement en production - À résoudre IMMÉDIATEMENT",
|
|
"priority": "P0",
|
|
"estimated_hours": 6,
|
|
"tasks": ["INT-CORS-001", "INT-CORS-002", "INT-AUTH-001"]
|
|
},
|
|
{
|
|
"id": "PHASE-INT-2",
|
|
"name": "Critical Type Fixes",
|
|
"description": "Incohérences de types causant des erreurs runtime",
|
|
"priority": "P1",
|
|
"estimated_hours": 12,
|
|
"tasks": ["INT-TYPE-001", "INT-TYPE-002", "INT-TYPE-003", "INT-TYPE-004", "INT-TYPE-005", "INT-TYPE-006", "INT-TYPE-007", "INT-TYPE-008"]
|
|
},
|
|
{
|
|
"id": "PHASE-INT-3",
|
|
"name": "API Client Standardization",
|
|
"description": "Unifier les clients API et corriger les formats",
|
|
"priority": "P1",
|
|
"estimated_hours": 8,
|
|
"tasks": ["INT-API-001", "INT-API-002", "INT-API-003", "INT-API-004", "INT-API-005"]
|
|
},
|
|
{
|
|
"id": "PHASE-INT-4",
|
|
"name": "Auth Flow Perfection",
|
|
"description": "Parfaire le flow d'authentification complet",
|
|
"priority": "P1",
|
|
"estimated_hours": 6,
|
|
"tasks": ["INT-AUTH-002", "INT-AUTH-003", "INT-AUTH-004"]
|
|
},
|
|
{
|
|
"id": "PHASE-INT-5",
|
|
"name": "Cleanup Duplication",
|
|
"description": "Supprimer tout le code dupliqué et legacy",
|
|
"priority": "P2",
|
|
"estimated_hours": 6,
|
|
"tasks": ["INT-CLEANUP-001", "INT-CLEANUP-002", "INT-CLEANUP-003", "INT-CLEANUP-004"]
|
|
},
|
|
{
|
|
"id": "PHASE-INT-6",
|
|
"name": "Missing Endpoints",
|
|
"description": "Implémenter endpoints manquants des deux côtés",
|
|
"priority": "P2",
|
|
"estimated_hours": 8,
|
|
"tasks": ["INT-ENDPOINT-001", "INT-ENDPOINT-002", "INT-ENDPOINT-003", "INT-ENDPOINT-004", "INT-ENDPOINT-005", "INT-ENDPOINT-006"]
|
|
},
|
|
{
|
|
"id": "PHASE-INT-7",
|
|
"name": "Integration Tests & Docs",
|
|
"description": "Tests E2E et documentation finale",
|
|
"priority": "P2",
|
|
"estimated_hours": 4,
|
|
"tasks": ["INT-TEST-001", "INT-TEST-002", "INT-DOC-001"]
|
|
}
|
|
],
|
|
"tasks": [
|
|
{
|
|
"id": "INT-CORS-001",
|
|
"category": "INT-CORS",
|
|
"title": "Configure CORS_ALLOWED_ORIGINS for production",
|
|
"description": "En production, si CORS_ALLOWED_ORIGINS est vide, le backend rejette TOUTES les requêtes cross-origin. Cela bloque complètement l'application.",
|
|
"priority": "P0",
|
|
"priority_rank": 1,
|
|
"status": "completed",
|
|
"estimated_hours": 1,
|
|
"side": "backend_only",
|
|
"files_to_modify": [
|
|
"veza-backend-api/internal/middleware/cors.go",
|
|
"veza-backend-api/.env.production",
|
|
"veza-backend-api/internal/config/config.go"
|
|
],
|
|
"implementation_steps": [
|
|
"Ouvrir veza-backend-api/internal/middleware/cors.go",
|
|
"Vérifier la fonction validateCORSConfig() - elle rejette si allowedOrigins vide en prod",
|
|
"Créer .env.production si inexistant",
|
|
"Ajouter CORS_ALLOWED_ORIGINS=https://app.veza.com,https://www.veza.com",
|
|
"Ajouter validation explicite au démarrage avec message clair",
|
|
"Tester: ENVIRONMENT=production go run cmd/server/main.go"
|
|
],
|
|
"acceptance_criteria": [
|
|
"CORS_ALLOWED_ORIGINS défini dans .env.production",
|
|
"Backend démarre sans erreur en mode production",
|
|
"Requêtes depuis frontend autorisées",
|
|
"Requêtes depuis domaines non autorisés rejetées"
|
|
],
|
|
"dependencies": [],
|
|
"blocks": ["INT-TEST-001"],
|
|
"verification_command": "ENVIRONMENT=production go run cmd/server/main.go"
|
|
},
|
|
{
|
|
"id": "INT-CORS-002",
|
|
"category": "INT-CORS",
|
|
"title": "Add preflight request handling validation",
|
|
"description": "Vérifier que les requêtes OPTIONS (preflight) sont correctement gérées pour tous les endpoints.",
|
|
"priority": "P0",
|
|
"priority_rank": 2,
|
|
"status": "completed",
|
|
"estimated_hours": 2,
|
|
"side": "backend_only",
|
|
"files_to_modify": [
|
|
"veza-backend-api/internal/middleware/cors.go"
|
|
],
|
|
"implementation_steps": [
|
|
"Vérifier AllowMethods inclut toutes les méthodes utilisées (GET, POST, PUT, PATCH, DELETE, OPTIONS)",
|
|
"Vérifier AllowHeaders inclut tous les headers custom (Authorization, X-CSRF-Token, Content-Type)",
|
|
"Ajouter ExposeHeaders pour headers de réponse custom",
|
|
"Tester preflight avec curl: curl -X OPTIONS -H 'Origin: http://localhost:3000' http://localhost:8080/api/v1/auth/me"
|
|
],
|
|
"acceptance_criteria": [
|
|
"AllowMethods contient toutes les méthodes HTTP utilisées",
|
|
"AllowHeaders contient Authorization, X-CSRF-Token, Content-Type",
|
|
"Preflight requests retournent 200 avec headers CORS corrects"
|
|
],
|
|
"dependencies": ["INT-CORS-001"],
|
|
"blocks": [],
|
|
"verification_command": "curl -v -X OPTIONS -H 'Origin: http://localhost:3000' http://localhost:8080/api/v1/auth/me"
|
|
},
|
|
{
|
|
"id": "INT-AUTH-001",
|
|
"category": "INT-AUTH",
|
|
"title": "Ensure CSRF protection active in production",
|
|
"description": "Le middleware CSRF est désactivé si Redis n'est pas disponible. En production, Redis DOIT être disponible et CSRF actif.",
|
|
"priority": "P0",
|
|
"priority_rank": 3,
|
|
"status": "completed",
|
|
"estimated_hours": 2,
|
|
"side": "both_sides",
|
|
"files_to_modify": [
|
|
"veza-backend-api/internal/middleware/csrf.go",
|
|
"veza-backend-api/internal/config/config.go",
|
|
"apps/web/src/services/csrf.ts"
|
|
],
|
|
"implementation_steps": [
|
|
"Backend: Vérifier que CSRF middleware fail-fast si Redis indisponible en prod",
|
|
"Backend: Ajouter log explicite quand CSRF activé/désactivé",
|
|
"Frontend: Vérifier que csrfService récupère le token avant chaque mutation",
|
|
"Frontend: Ajouter retry si token CSRF expiré",
|
|
"Tester: Requête POST sans X-CSRF-Token doit retourner 403"
|
|
],
|
|
"acceptance_criteria": [
|
|
"Backend refuse de démarrer en prod si Redis indisponible",
|
|
"Toutes les requêtes POST/PUT/DELETE/PATCH nécessitent X-CSRF-Token",
|
|
"Frontend ajoute automatiquement X-CSRF-Token à toutes les mutations",
|
|
"Token refresh automatique si expiré"
|
|
],
|
|
"dependencies": ["INT-CORS-001"],
|
|
"blocks": ["INT-TEST-001"],
|
|
"verification_command": "curl -X POST http://localhost:8080/api/v1/tracks -H 'Authorization: Bearer xxx' (doit retourner 403)"
|
|
},
|
|
{
|
|
"id": "INT-TYPE-001",
|
|
"category": "INT-TYPE",
|
|
"title": "Standardize User.id to string everywhere",
|
|
"description": "User.id est uuid.UUID (string) côté backend mais parfois number côté frontend. Cela cause des erreurs de comparaison.",
|
|
"priority": "P1",
|
|
"priority_rank": 4,
|
|
"status": "completed",
|
|
"completed_at": "2025-01-27T12:30:00Z",
|
|
"estimated_hours": 2,
|
|
"side": "frontend_only",
|
|
"files_to_modify": [
|
|
"apps/web/src/types/user.ts",
|
|
"apps/web/src/types/index.ts",
|
|
"apps/web/src/features/auth/types/index.ts"
|
|
],
|
|
"implementation_steps": [
|
|
"Rechercher toutes les occurrences de 'id: number' dans types user",
|
|
"Remplacer par 'id: string'",
|
|
"Vérifier tous les composants qui comparent user.id",
|
|
"Mettre à jour les comparaisons: userId === user.id (pas ===)",
|
|
"Ajouter commentaire: // UUID from backend - always string",
|
|
"Compiler TypeScript pour trouver les erreurs restantes"
|
|
],
|
|
"acceptance_criteria": [
|
|
"User.id est string dans tous les fichiers types",
|
|
"Aucune erreur TypeScript liée à User.id",
|
|
"Comparaisons fonctionnent correctement"
|
|
],
|
|
"dependencies": [],
|
|
"blocks": ["INT-TYPE-002"],
|
|
"verification_command": "cd apps/web && npm run type-check"
|
|
},
|
|
{
|
|
"id": "INT-TYPE-002",
|
|
"category": "INT-TYPE",
|
|
"title": "Standardize Track.id to string everywhere",
|
|
"description": "Même problème que User.id - Track.id doit être string (UUID) partout.",
|
|
"priority": "P1",
|
|
"priority_rank": 5,
|
|
"status": "completed",
|
|
"completed_at": "2025-01-27T12:45:00Z",
|
|
"estimated_hours": 1.5,
|
|
"side": "frontend_only",
|
|
"files_to_modify": [
|
|
"apps/web/src/types/track.ts",
|
|
"apps/web/src/features/tracks/types/index.ts"
|
|
],
|
|
"implementation_steps": [
|
|
"Rechercher 'id: number' dans types track",
|
|
"Remplacer par 'id: string'",
|
|
"Vérifier composants qui utilisent track.id",
|
|
"Compiler TypeScript"
|
|
],
|
|
"acceptance_criteria": [
|
|
"Track.id est string dans tous les fichiers",
|
|
"Aucune erreur TypeScript"
|
|
],
|
|
"dependencies": ["INT-TYPE-001"],
|
|
"blocks": [],
|
|
"verification_command": "cd apps/web && npm run type-check"
|
|
},
|
|
{
|
|
"id": "INT-TYPE-003",
|
|
"category": "INT-TYPE",
|
|
"title": "Standardize Playlist.id to string everywhere",
|
|
"description": "Playlist.id doit être string (UUID) partout.",
|
|
"priority": "P1",
|
|
"priority_rank": 6,
|
|
"status": "completed",
|
|
"completed_at": "2025-01-27T13:00:00Z",
|
|
"estimated_hours": 1.5,
|
|
"side": "frontend_only",
|
|
"files_to_modify": [
|
|
"apps/web/src/types/playlist.ts",
|
|
"apps/web/src/features/playlists/types/index.ts"
|
|
],
|
|
"implementation_steps": [
|
|
"Rechercher 'id: number' dans types playlist",
|
|
"Remplacer par 'id: string'",
|
|
"Compiler TypeScript"
|
|
],
|
|
"acceptance_criteria": [
|
|
"Playlist.id est string partout",
|
|
"Aucune erreur TypeScript"
|
|
],
|
|
"dependencies": ["INT-TYPE-001"],
|
|
"blocks": [],
|
|
"verification_command": "cd apps/web && npm run type-check"
|
|
},
|
|
{
|
|
"id": "INT-TYPE-004",
|
|
"category": "INT-TYPE",
|
|
"title": "Create TrackStatus enum aligned with backend",
|
|
"description": "Track.status utilise des strings arbitraires. Créer un enum aligné avec le backend.",
|
|
"priority": "P1",
|
|
"priority_rank": 7,
|
|
"status": "completed",
|
|
"completed_at": "2025-01-27T13:15:00Z",
|
|
"estimated_hours": 1,
|
|
"side": "both_sides",
|
|
"files_to_modify": [
|
|
"veza-backend-api/internal/models/track.go",
|
|
"apps/web/src/types/track.ts",
|
|
"apps/web/src/features/tracks/types/index.ts"
|
|
],
|
|
"implementation_steps": [
|
|
"Backend: Vérifier les valeurs possibles de track.status dans models/track.go",
|
|
"Frontend: Créer enum TrackStatus avec mêmes valeurs",
|
|
"Frontend: Utiliser enum dans type Track",
|
|
"Ajouter validation Zod si utilisé"
|
|
],
|
|
"acceptance_criteria": [
|
|
"Enum TrackStatus créé avec valeurs: 'draft', 'processing', 'ready', 'error'",
|
|
"Type Track utilise TrackStatus au lieu de string",
|
|
"Backend et frontend utilisent mêmes valeurs"
|
|
],
|
|
"dependencies": ["INT-TYPE-002"],
|
|
"blocks": [],
|
|
"verification_command": "grep -r 'status' apps/web/src/types/track.ts"
|
|
},
|
|
{
|
|
"id": "INT-TYPE-005",
|
|
"category": "INT-TYPE",
|
|
"title": "Create PlaylistVisibility enum aligned with backend",
|
|
"description": "Playlist.visibility doit utiliser un enum aligné avec le backend.",
|
|
"priority": "P1",
|
|
"priority_rank": 8,
|
|
"status": "completed",
|
|
"completed_at": "2025-01-27T13:30:00Z",
|
|
"estimated_hours": 1,
|
|
"side": "both_sides",
|
|
"files_to_modify": [
|
|
"veza-backend-api/internal/models/playlist.go",
|
|
"apps/web/src/types/playlist.ts"
|
|
],
|
|
"implementation_steps": [
|
|
"Backend: Vérifier valeurs de visibility",
|
|
"Frontend: Créer enum PlaylistVisibility",
|
|
"Utiliser dans type Playlist"
|
|
],
|
|
"acceptance_criteria": [
|
|
"Enum créé avec valeurs: 'public', 'private', 'unlisted'",
|
|
"Type Playlist utilise l'enum"
|
|
],
|
|
"dependencies": ["INT-TYPE-003"],
|
|
"blocks": [],
|
|
"verification_command": "grep -r 'visibility' apps/web/src/types/playlist.ts"
|
|
},
|
|
{
|
|
"id": "INT-TYPE-006",
|
|
"category": "INT-TYPE",
|
|
"title": "Complete ApiError interface with all backend fields",
|
|
"description": "L'interface ApiError frontend est incomplète par rapport aux erreurs retournées par le backend.",
|
|
"priority": "P1",
|
|
"priority_rank": 9,
|
|
"status": "completed",
|
|
"completed_at": "2025-01-27T13:45:00Z",
|
|
"estimated_hours": 1.5,
|
|
"side": "frontend_only",
|
|
"files_to_modify": [
|
|
"apps/web/src/types/api.ts",
|
|
"apps/web/src/services/api/client.ts"
|
|
],
|
|
"implementation_steps": [
|
|
"Analyser le format d'erreur backend dans handlers/errors.go",
|
|
"Mettre à jour interface ApiError avec tous les champs",
|
|
"Ajouter: code, message, details, field_errors, request_id",
|
|
"Mettre à jour parseApiError() dans client.ts"
|
|
],
|
|
"acceptance_criteria": [
|
|
"ApiError contient: code, message, details?, field_errors?, request_id?",
|
|
"parseApiError gère tous les formats d'erreur backend",
|
|
"Erreurs de validation affichent les champs concernés"
|
|
],
|
|
"dependencies": [],
|
|
"blocks": [],
|
|
"verification_command": "grep -A 10 'interface ApiError' apps/web/src/types/api.ts"
|
|
},
|
|
{
|
|
"id": "INT-TYPE-007",
|
|
"category": "INT-TYPE",
|
|
"title": "Create PaginatedResponse generic type",
|
|
"description": "Créer un type générique pour les réponses paginées aligné avec le format backend.",
|
|
"priority": "P1",
|
|
"priority_rank": 10,
|
|
"status": "completed",
|
|
"completed_at": "2025-01-27T14:00:00Z",
|
|
"estimated_hours": 1,
|
|
"side": "frontend_only",
|
|
"files_to_modify": [
|
|
"apps/web/src/types/api.ts"
|
|
],
|
|
"implementation_steps": [
|
|
"Analyser format de pagination backend (page, limit, total, items)",
|
|
"Créer type générique PaginatedResponse<T>",
|
|
"Utiliser dans tous les services qui retournent des listes"
|
|
],
|
|
"acceptance_criteria": [
|
|
"PaginatedResponse<T> créé avec: items: T[], total: number, page: number, limit: number, has_more: boolean",
|
|
"Utilisé dans tracks, playlists, users list endpoints"
|
|
],
|
|
"dependencies": [],
|
|
"blocks": [],
|
|
"verification_command": "grep -r 'PaginatedResponse' apps/web/src/"
|
|
},
|
|
{
|
|
"id": "INT-TYPE-008",
|
|
"category": "INT-TYPE",
|
|
"title": "Validate AuthResponse matches backend exactly",
|
|
"description": "Vérifier que AuthResponse correspond exactement à la réponse de /auth/login.",
|
|
"priority": "P1",
|
|
"priority_rank": 11,
|
|
"status": "completed",
|
|
"completed_at": "2025-01-27T14:15:00Z",
|
|
"estimated_hours": 1,
|
|
"side": "frontend_only",
|
|
"files_to_modify": [
|
|
"apps/web/src/features/auth/types/index.ts"
|
|
],
|
|
"implementation_steps": [
|
|
"Comparer AuthResponse avec handlers/auth.go LoginResponse",
|
|
"Vérifier: access_token, refresh_token, expires_in, token_type, user",
|
|
"S'assurer que l'interceptor unwrap correctement"
|
|
],
|
|
"acceptance_criteria": [
|
|
"AuthResponse match exactement le format backend",
|
|
"Login flow fonctionne sans erreur de type"
|
|
],
|
|
"dependencies": ["INT-TYPE-001"],
|
|
"blocks": [],
|
|
"verification_command": "grep -A 15 'interface AuthResponse' apps/web/src/features/auth/types/index.ts"
|
|
},
|
|
{
|
|
"id": "INT-API-001",
|
|
"category": "INT-API",
|
|
"title": "Remove duplicate API client (lib/apiClient.ts)",
|
|
"description": "Il existe potentiellement deux clients API. Garder uniquement services/api/client.ts.",
|
|
"priority": "P1",
|
|
"priority_rank": 12,
|
|
"status": "completed",
|
|
"completed_at": "2025-01-27T14:30:00Z",
|
|
"estimated_hours": 2,
|
|
"side": "frontend_only",
|
|
"files_to_modify": [
|
|
"apps/web/src/lib/apiClient.ts",
|
|
"apps/web/src/services/api/client.ts"
|
|
],
|
|
"implementation_steps": [
|
|
"Vérifier si lib/apiClient.ts existe",
|
|
"Si oui, lister tous les fichiers qui l'importent",
|
|
"Migrer ces imports vers services/api/client.ts",
|
|
"Supprimer lib/apiClient.ts",
|
|
"Compiler et tester"
|
|
],
|
|
"acceptance_criteria": [
|
|
"Un seul client API: services/api/client.ts",
|
|
"Aucun import de lib/apiClient",
|
|
"Application fonctionne correctement"
|
|
],
|
|
"dependencies": [],
|
|
"blocks": ["INT-CLEANUP-002"],
|
|
"verification_command": "grep -r 'lib/apiClient' apps/web/src/"
|
|
},
|
|
{
|
|
"id": "INT-API-002",
|
|
"category": "INT-API",
|
|
"title": "Verify response unwrapping in interceptor",
|
|
"description": "Vérifier que l'interceptor unwrap correctement {success: true, data: ...} → data.",
|
|
"priority": "P1",
|
|
"priority_rank": 13,
|
|
"status": "completed",
|
|
"completed_at": "2025-01-27T14:45:00Z",
|
|
"estimated_hours": 1,
|
|
"side": "frontend_only",
|
|
"files_to_modify": [
|
|
"apps/web/src/services/api/client.ts"
|
|
],
|
|
"implementation_steps": [
|
|
"Ouvrir services/api/client.ts",
|
|
"Vérifier l'interceptor de réponse",
|
|
"S'assurer qu'il détecte {success, data} et retourne data",
|
|
"S'assurer qu'il gère {success: false, error} correctement",
|
|
"Ajouter logs de debug si nécessaire"
|
|
],
|
|
"acceptance_criteria": [
|
|
"Interceptor unwrap {success: true, data: X} → X",
|
|
"Interceptor throw sur {success: false, error: E}",
|
|
"Services reçoivent directement les données sans wrapper"
|
|
],
|
|
"dependencies": ["INT-API-001"],
|
|
"blocks": [],
|
|
"verification_command": "grep -A 30 'response interceptor' apps/web/src/services/api/client.ts"
|
|
},
|
|
{
|
|
"id": "INT-API-003",
|
|
"category": "INT-API",
|
|
"title": "Standardize error handling across all services",
|
|
"description": "Tous les services API doivent utiliser la même méthode de gestion d'erreurs.",
|
|
"priority": "P1",
|
|
"priority_rank": 14,
|
|
"status": "completed",
|
|
"completed_at": "2025-01-27T15:00:00Z",
|
|
"estimated_hours": 2,
|
|
"side": "frontend_only",
|
|
"files_to_modify": [
|
|
"apps/web/src/services/api/client.ts",
|
|
"apps/web/src/features/*/api/*.ts",
|
|
"apps/web/src/features/*/services/*.ts"
|
|
],
|
|
"implementation_steps": [
|
|
"Créer fonction handleApiError() centralisée",
|
|
"Standardiser le format d'erreur retourné",
|
|
"Mettre à jour tous les services pour utiliser cette fonction",
|
|
"Ajouter types pour les erreurs"
|
|
],
|
|
"acceptance_criteria": [
|
|
"Une seule fonction handleApiError",
|
|
"Tous les services l'utilisent",
|
|
"Erreurs ont un format prévisible"
|
|
],
|
|
"dependencies": ["INT-TYPE-006"],
|
|
"blocks": [],
|
|
"verification_command": "grep -r 'handleApiError' apps/web/src/"
|
|
},
|
|
{
|
|
"id": "INT-API-004",
|
|
"category": "INT-API",
|
|
"title": "Add request timeout configuration per endpoint type",
|
|
"description": "Configurer des timeouts différents pour uploads (long) vs requêtes normales.",
|
|
"priority": "P2",
|
|
"priority_rank": 15,
|
|
"status": "completed",
|
|
"completed_at": "2025-01-27T15:15:00Z",
|
|
"estimated_hours": 1,
|
|
"side": "frontend_only",
|
|
"files_to_modify": [
|
|
"apps/web/src/services/api/client.ts",
|
|
"apps/web/src/features/tracks/api/uploadApi.ts"
|
|
],
|
|
"implementation_steps": [
|
|
"Définir timeouts: default 10s, upload 5min, long-polling 30s",
|
|
"Permettre override par requête",
|
|
"Appliquer timeout long pour uploads"
|
|
],
|
|
"acceptance_criteria": [
|
|
"Timeout par défaut: 10s",
|
|
"Upload timeout: 5min",
|
|
"Configurable par requête"
|
|
],
|
|
"dependencies": [],
|
|
"blocks": [],
|
|
"verification_command": "grep -r 'timeout' apps/web/src/services/api/"
|
|
},
|
|
{
|
|
"id": "INT-API-005",
|
|
"category": "INT-API",
|
|
"title": "Add retry logic for 429 rate limit responses",
|
|
"description": "Détecter les réponses 429 et retry avec backoff basé sur Retry-After header.",
|
|
"priority": "P2",
|
|
"priority_rank": 16,
|
|
"status": "completed",
|
|
"completed_at": "2025-01-27T15:30:00Z",
|
|
"estimated_hours": 1.5,
|
|
"side": "frontend_only",
|
|
"files_to_modify": [
|
|
"apps/web/src/services/api/client.ts"
|
|
],
|
|
"implementation_steps": [
|
|
"Ajouter 429 aux status codes retryables",
|
|
"Lire header Retry-After si présent",
|
|
"Utiliser Retry-After comme délai, sinon exponential backoff",
|
|
"Limiter à 3 retries pour 429"
|
|
],
|
|
"acceptance_criteria": [
|
|
"429 déclenche retry automatique",
|
|
"Respecte Retry-After header",
|
|
"Max 3 retries pour rate limit"
|
|
],
|
|
"dependencies": ["INT-API-002"],
|
|
"blocks": [],
|
|
"verification_command": "grep -r 'retryableStatusCodes' apps/web/src/services/api/"
|
|
},
|
|
{
|
|
"id": "INT-AUTH-002",
|
|
"category": "INT-AUTH",
|
|
"title": "Remove duplicate auth store if exists",
|
|
"description": "Vérifier s'il y a deux stores auth et garder uniquement features/auth/store/authStore.ts.",
|
|
"priority": "P1",
|
|
"priority_rank": 17,
|
|
"status": "completed",
|
|
"completed_at": "2025-01-27T15:45:00Z",
|
|
"estimated_hours": 2,
|
|
"side": "frontend_only",
|
|
"files_to_modify": [
|
|
"apps/web/src/stores/auth.ts",
|
|
"apps/web/src/features/auth/store/authStore.ts"
|
|
],
|
|
"implementation_steps": [
|
|
"Vérifier si stores/auth.ts existe",
|
|
"Comparer avec features/auth/store/authStore.ts",
|
|
"Migrer les imports vers le nouveau store",
|
|
"Supprimer l'ancien store",
|
|
"Tester auth flow complet"
|
|
],
|
|
"acceptance_criteria": [
|
|
"Un seul store auth",
|
|
"Tous les composants utilisent le même store",
|
|
"Auth flow fonctionne"
|
|
],
|
|
"dependencies": [],
|
|
"blocks": ["INT-CLEANUP-003"],
|
|
"verification_command": "grep -r 'from.*stores/auth' apps/web/src/"
|
|
},
|
|
{
|
|
"id": "INT-AUTH-003",
|
|
"category": "INT-AUTH",
|
|
"title": "Verify refresh token flow handles edge cases",
|
|
"description": "S'assurer que le refresh token gère: expiration, révocation, erreurs réseau.",
|
|
"priority": "P1",
|
|
"priority_rank": 18,
|
|
"status": "completed",
|
|
"completed_at": "2025-01-27T16:00:00Z",
|
|
"estimated_hours": 2,
|
|
"side": "frontend_only",
|
|
"files_to_modify": [
|
|
"apps/web/src/services/api/client.ts",
|
|
"apps/web/src/features/auth/store/authStore.ts"
|
|
],
|
|
"implementation_steps": [
|
|
"Vérifier que 401 sur /auth/refresh redirige vers login",
|
|
"Vérifier que les requêtes en attente sont rejouées après refresh",
|
|
"Vérifier protection contre boucle infinie",
|
|
"Ajouter logs pour debug"
|
|
],
|
|
"acceptance_criteria": [
|
|
"401 sur refresh → logout + redirect login",
|
|
"Requêtes en queue rejouées après refresh réussi",
|
|
"Pas de boucle infinie possible"
|
|
],
|
|
"dependencies": ["INT-AUTH-002"],
|
|
"blocks": [],
|
|
"verification_command": "grep -A 20 'refreshToken' apps/web/src/services/api/client.ts"
|
|
},
|
|
{
|
|
"id": "INT-AUTH-004",
|
|
"category": "INT-AUTH",
|
|
"title": "Add token expiration pre-check",
|
|
"description": "Vérifier l'expiration du token AVANT d'envoyer une requête pour éviter 401 inutiles.",
|
|
"priority": "P2",
|
|
"priority_rank": 19,
|
|
"status": "completed",
|
|
"completed_at": "2025-01-27T16:15:00Z",
|
|
"estimated_hours": 1.5,
|
|
"side": "frontend_only",
|
|
"files_to_modify": [
|
|
"apps/web/src/services/api/client.ts",
|
|
"apps/web/src/services/tokenStorage.ts"
|
|
],
|
|
"implementation_steps": [
|
|
"Décoder le JWT pour extraire exp",
|
|
"Comparer avec Date.now() - marge de 60s",
|
|
"Si proche expiration, refresh proactivement",
|
|
"Ajouter fonction isTokenExpiringSoon()"
|
|
],
|
|
"acceptance_criteria": [
|
|
"Token refresh proactif 60s avant expiration",
|
|
"Moins de requêtes 401",
|
|
"Performance améliorée"
|
|
],
|
|
"dependencies": ["INT-AUTH-003"],
|
|
"blocks": [],
|
|
"verification_command": "grep -r 'isTokenExpiring' apps/web/src/"
|
|
},
|
|
{
|
|
"id": "INT-CLEANUP-001",
|
|
"category": "INT-CLEANUP",
|
|
"title": "Remove all unused API service files",
|
|
"description": "Identifier et supprimer les fichiers de services API non utilisés.",
|
|
"priority": "P2",
|
|
"priority_rank": 20,
|
|
"status": "completed",
|
|
"completed_at": "2025-01-27T16:30:00Z",
|
|
"estimated_hours": 1,
|
|
"side": "frontend_only",
|
|
"files_to_modify": [
|
|
"apps/web/src/services/*.ts"
|
|
],
|
|
"implementation_steps": [
|
|
"Lister tous les fichiers dans services/",
|
|
"Pour chaque fichier, vérifier s'il est importé quelque part",
|
|
"Supprimer les fichiers non importés",
|
|
"Commit avec liste des fichiers supprimés"
|
|
],
|
|
"acceptance_criteria": [
|
|
"Tous les fichiers services/ sont utilisés",
|
|
"Aucun code mort"
|
|
],
|
|
"dependencies": ["INT-API-001"],
|
|
"blocks": [],
|
|
"verification_command": "find apps/web/src/services -name '*.ts' -exec basename {} \\;"
|
|
},
|
|
{
|
|
"id": "INT-CLEANUP-002",
|
|
"category": "INT-CLEANUP",
|
|
"title": "Consolidate type definitions in single location",
|
|
"description": "Tous les types partagés doivent être dans types/ et non dupliqués dans features/.",
|
|
"priority": "P2",
|
|
"priority_rank": 21,
|
|
"status": "completed",
|
|
"completed_at": "2025-01-27T16:45:00Z",
|
|
"estimated_hours": 2,
|
|
"side": "frontend_only",
|
|
"files_to_modify": [
|
|
"apps/web/src/types/*.ts",
|
|
"apps/web/src/features/*/types/*.ts"
|
|
],
|
|
"implementation_steps": [
|
|
"Identifier les types dupliqués entre types/ et features/*/types/",
|
|
"Garder une seule définition dans types/",
|
|
"Mettre à jour les imports",
|
|
"Features peuvent avoir des types spécifiques, pas des duplications"
|
|
],
|
|
"acceptance_criteria": [
|
|
"Pas de duplication de types",
|
|
"Types partagés dans types/",
|
|
"Types feature-specific dans features/*/types/"
|
|
],
|
|
"dependencies": ["INT-TYPE-001"],
|
|
"blocks": [],
|
|
"verification_command": "grep -r 'interface User' apps/web/src/ | wc -l"
|
|
},
|
|
{
|
|
"id": "INT-CLEANUP-003",
|
|
"category": "INT-CLEANUP",
|
|
"title": "Remove legacy hooks using old API client",
|
|
"description": "Supprimer les hooks qui utilisent encore l'ancien client API.",
|
|
"priority": "P2",
|
|
"priority_rank": 22,
|
|
"status": "completed",
|
|
"completed_at": "2025-01-27T17:00:00Z",
|
|
"estimated_hours": 1.5,
|
|
"side": "frontend_only",
|
|
"files_to_modify": [
|
|
"apps/web/src/hooks/api/*.ts"
|
|
],
|
|
"implementation_steps": [
|
|
"Identifier hooks utilisant l'ancien client",
|
|
"Migrer vers nouveau client ou supprimer si inutilisés",
|
|
"Mettre à jour composants qui les utilisent"
|
|
],
|
|
"acceptance_criteria": [
|
|
"Tous les hooks utilisent le nouveau client API",
|
|
"Aucun import de l'ancien client"
|
|
],
|
|
"dependencies": ["INT-API-001", "INT-AUTH-002"],
|
|
"blocks": [],
|
|
"verification_command": "grep -r 'lib/apiClient' apps/web/src/hooks/"
|
|
},
|
|
{
|
|
"id": "INT-CLEANUP-004",
|
|
"category": "INT-CLEANUP",
|
|
"title": "Add barrel exports for clean imports",
|
|
"description": "Créer des fichiers index.ts pour faciliter les imports.",
|
|
"priority": "P3",
|
|
"priority_rank": 23,
|
|
"status": "completed",
|
|
"completed_at": "2025-01-27T17:15:00Z",
|
|
"estimated_hours": 1,
|
|
"side": "frontend_only",
|
|
"files_to_modify": [
|
|
"apps/web/src/types/index.ts",
|
|
"apps/web/src/services/api/index.ts"
|
|
],
|
|
"implementation_steps": [
|
|
"Créer index.ts dans types/ qui exporte tout",
|
|
"Créer index.ts dans services/api/ qui exporte client et services",
|
|
"Mettre à jour imports pour utiliser barrels"
|
|
],
|
|
"acceptance_criteria": [
|
|
"Import possible via '@/types' et '@/services/api'",
|
|
"Imports plus propres dans le code"
|
|
],
|
|
"dependencies": ["INT-CLEANUP-002"],
|
|
"blocks": [],
|
|
"verification_command": "cat apps/web/src/types/index.ts"
|
|
},
|
|
{
|
|
"id": "INT-ENDPOINT-001",
|
|
"category": "INT-ENDPOINT",
|
|
"title": "Add frontend service for GET /api/v1/sessions/stats",
|
|
"description": "Le backend expose cet endpoint mais le frontend ne l'utilise pas.",
|
|
"priority": "P2",
|
|
"priority_rank": 24,
|
|
"status": "completed",
|
|
"completed_at": "2025-01-27T17:30:00Z",
|
|
"estimated_hours": 1,
|
|
"side": "frontend_only",
|
|
"files_to_modify": [
|
|
"apps/web/src/features/sessions/api/sessionsApi.ts"
|
|
],
|
|
"implementation_steps": [
|
|
"Créer fonction getSessionStats() dans sessionsApi.ts",
|
|
"Définir type SessionStats aligné avec backend",
|
|
"Ajouter hook useSessionStats() si nécessaire"
|
|
],
|
|
"acceptance_criteria": [
|
|
"sessionsApi.getSessionStats() existe",
|
|
"Type SessionStats défini",
|
|
"Endpoint accessible depuis frontend"
|
|
],
|
|
"dependencies": [],
|
|
"blocks": [],
|
|
"verification_command": "grep -r 'sessions/stats' apps/web/src/"
|
|
},
|
|
{
|
|
"id": "INT-ENDPOINT-002",
|
|
"category": "INT-ENDPOINT",
|
|
"title": "Implement backend GET /api/v1/users/search",
|
|
"description": "Le frontend appelle cet endpoint mais il n'existe pas côté backend.",
|
|
"priority": "P2",
|
|
"priority_rank": 25,
|
|
"status": "completed",
|
|
"completed_at": "2025-01-27T17:45:00Z",
|
|
"estimated_hours": 2,
|
|
"side": "backend_only",
|
|
"files_to_modify": [
|
|
"veza-backend-api/internal/api/router.go",
|
|
"veza-backend-api/internal/handlers/users.go"
|
|
],
|
|
"implementation_steps": [
|
|
"Ajouter route GET /api/v1/users/search dans router.go",
|
|
"Créer handler SearchUsers dans handlers/users.go",
|
|
"Paramètres: q (query), limit, offset",
|
|
"Retourner liste d'utilisateurs paginée"
|
|
],
|
|
"acceptance_criteria": [
|
|
"Endpoint GET /api/v1/users/search existe",
|
|
"Recherche par username et display_name",
|
|
"Pagination fonctionnelle"
|
|
],
|
|
"dependencies": [],
|
|
"blocks": [],
|
|
"verification_command": "curl 'http://localhost:8080/api/v1/users/search?q=test'"
|
|
},
|
|
{
|
|
"id": "INT-ENDPOINT-003",
|
|
"category": "INT-ENDPOINT",
|
|
"title": "Implement backend GET /api/v1/tracks/search",
|
|
"description": "Le frontend appelle cet endpoint mais il n'existe pas côté backend.",
|
|
"priority": "P2",
|
|
"priority_rank": 26,
|
|
"status": "completed",
|
|
"completed_at": "2025-01-27T18:00:00Z",
|
|
"estimated_hours": 2,
|
|
"side": "backend_only",
|
|
"files_to_modify": [
|
|
"veza-backend-api/internal/api/router.go",
|
|
"veza-backend-api/internal/handlers/tracks.go"
|
|
],
|
|
"implementation_steps": [
|
|
"Ajouter route GET /api/v1/tracks/search dans router.go",
|
|
"Créer handler SearchTracks dans handlers/tracks.go",
|
|
"Paramètres: q (query), genre, limit, offset",
|
|
"Retourner liste de tracks paginée"
|
|
],
|
|
"acceptance_criteria": [
|
|
"Endpoint GET /api/v1/tracks/search existe",
|
|
"Recherche par title et genre",
|
|
"Pagination fonctionnelle"
|
|
],
|
|
"dependencies": [],
|
|
"blocks": [],
|
|
"verification_command": "curl 'http://localhost:8080/api/v1/tracks/search?q=test'"
|
|
},
|
|
{
|
|
"id": "INT-ENDPOINT-004",
|
|
"category": "INT-ENDPOINT",
|
|
"title": "Implement backend GET /api/v1/playlists/search",
|
|
"description": "Le frontend appelle cet endpoint mais il n'existe pas côté backend.",
|
|
"priority": "P2",
|
|
"priority_rank": 27,
|
|
"status": "completed",
|
|
"completed_at": "2025-01-27T18:15:00Z",
|
|
"estimated_hours": 2,
|
|
"side": "backend_only",
|
|
"files_to_modify": [
|
|
"veza-backend-api/internal/api/router.go",
|
|
"veza-backend-api/internal/handlers/playlists.go"
|
|
],
|
|
"implementation_steps": [
|
|
"Ajouter route GET /api/v1/playlists/search dans router.go",
|
|
"Créer handler SearchPlaylists dans handlers/playlists.go",
|
|
"Paramètres: q (query), visibility, limit, offset",
|
|
"Retourner liste de playlists paginée"
|
|
],
|
|
"acceptance_criteria": [
|
|
"Endpoint GET /api/v1/playlists/search existe",
|
|
"Recherche par name et description",
|
|
"Filtre par visibility"
|
|
],
|
|
"dependencies": [],
|
|
"blocks": [],
|
|
"verification_command": "curl 'http://localhost:8080/api/v1/playlists/search?q=test'"
|
|
},
|
|
{
|
|
"id": "INT-ENDPOINT-005",
|
|
"category": "INT-ENDPOINT",
|
|
"title": "Implement backend playlist collaborator endpoints",
|
|
"description": "Les endpoints de gestion des collaborateurs de playlist sont appelés par le frontend mais n'existent pas.",
|
|
"priority": "P2",
|
|
"priority_rank": 28,
|
|
"status": "completed",
|
|
"completed_at": "2025-01-27T18:30:00Z",
|
|
"estimated_hours": 3,
|
|
"side": "backend_only",
|
|
"files_to_modify": [
|
|
"veza-backend-api/internal/api/router.go",
|
|
"veza-backend-api/internal/handlers/playlists.go"
|
|
],
|
|
"implementation_steps": [
|
|
"Ajouter POST /api/v1/playlists/:id/collaborators",
|
|
"Ajouter DELETE /api/v1/playlists/:id/collaborators/:userId",
|
|
"Ajouter PUT /api/v1/playlists/:id/collaborators/:userId",
|
|
"Implémenter handlers correspondants",
|
|
"Ajouter validation des permissions"
|
|
],
|
|
"acceptance_criteria": [
|
|
"3 endpoints collaborators fonctionnels",
|
|
"Seul le owner peut gérer les collaborateurs",
|
|
"Collaborators peuvent être ajoutés/supprimés/mis à jour"
|
|
],
|
|
"dependencies": [],
|
|
"blocks": [],
|
|
"verification_command": "grep -r 'collaborators' veza-backend-api/internal/api/router.go"
|
|
},
|
|
{
|
|
"id": "INT-ENDPOINT-006",
|
|
"category": "INT-ENDPOINT",
|
|
"title": "Implement backend conversation management endpoints",
|
|
"description": "DELETE /api/v1/conversations/:id et endpoints participants manquants.",
|
|
"priority": "P3",
|
|
"priority_rank": 29,
|
|
"status": "todo",
|
|
"estimated_hours": 2,
|
|
"side": "backend_only",
|
|
"files_to_modify": [
|
|
"veza-backend-api/internal/api/router.go",
|
|
"veza-backend-api/internal/handlers/conversations.go"
|
|
],
|
|
"implementation_steps": [
|
|
"Ajouter DELETE /api/v1/conversations/:id",
|
|
"Ajouter POST /api/v1/conversations/:id/participants",
|
|
"Ajouter DELETE /api/v1/conversations/:id/participants/:userId",
|
|
"Implémenter handlers"
|
|
],
|
|
"acceptance_criteria": [
|
|
"Conversations peuvent être supprimées par le créateur",
|
|
"Participants peuvent être ajoutés/supprimés"
|
|
],
|
|
"dependencies": [],
|
|
"blocks": [],
|
|
"verification_command": "grep -r 'conversations' veza-backend-api/internal/api/router.go"
|
|
},
|
|
{
|
|
"id": "INT-TEST-001",
|
|
"category": "INT-TEST",
|
|
"title": "Create E2E test for complete auth flow",
|
|
"description": "Test Playwright pour: register → verify email → login → refresh → logout.",
|
|
"priority": "P2",
|
|
"priority_rank": 30,
|
|
"status": "todo",
|
|
"estimated_hours": 3,
|
|
"side": "frontend_only",
|
|
"files_to_modify": [
|
|
"apps/web/e2e/auth-flow.spec.ts"
|
|
],
|
|
"implementation_steps": [
|
|
"Créer fichier e2e/auth-flow.spec.ts",
|
|
"Test: register avec email valide",
|
|
"Test: login et vérification du token",
|
|
"Test: refresh token automatique",
|
|
"Test: logout et redirection"
|
|
],
|
|
"acceptance_criteria": [
|
|
"Tests couvrent le flow auth complet",
|
|
"Tests passent avec backend réel",
|
|
"CI/CD peut exécuter ces tests"
|
|
],
|
|
"dependencies": ["INT-CORS-001", "INT-AUTH-001"],
|
|
"blocks": [],
|
|
"verification_command": "cd apps/web && npx playwright test auth-flow"
|
|
},
|
|
{
|
|
"id": "INT-TEST-002",
|
|
"category": "INT-TEST",
|
|
"title": "Create E2E test for CRUD operations",
|
|
"description": "Test Playwright pour: create track → update → delete, create playlist → add tracks → delete.",
|
|
"priority": "P2",
|
|
"priority_rank": 31,
|
|
"status": "todo",
|
|
"estimated_hours": 3,
|
|
"side": "frontend_only",
|
|
"files_to_modify": [
|
|
"apps/web/e2e/crud-operations.spec.ts"
|
|
],
|
|
"implementation_steps": [
|
|
"Créer fichier e2e/crud-operations.spec.ts",
|
|
"Test: CRUD complet sur tracks",
|
|
"Test: CRUD complet sur playlists",
|
|
"Test: ajout de tracks à une playlist"
|
|
],
|
|
"acceptance_criteria": [
|
|
"Tests couvrent CRUD tracks et playlists",
|
|
"Tests passent avec backend réel",
|
|
"Données de test nettoyées après exécution"
|
|
],
|
|
"dependencies": ["INT-TEST-001"],
|
|
"blocks": [],
|
|
"verification_command": "cd apps/web && npx playwright test crud-operations"
|
|
},
|
|
{
|
|
"id": "INT-DOC-001",
|
|
"category": "INT-DOC",
|
|
"title": "Generate OpenAPI/Swagger documentation",
|
|
"description": "Générer une documentation OpenAPI complète pour tous les endpoints.",
|
|
"priority": "P3",
|
|
"priority_rank": 32,
|
|
"status": "todo",
|
|
"estimated_hours": 2,
|
|
"side": "backend_only",
|
|
"files_to_modify": [
|
|
"veza-backend-api/docs/swagger.yaml",
|
|
"veza-backend-api/cmd/server/main.go"
|
|
],
|
|
"implementation_steps": [
|
|
"Installer swag: go install github.com/swaggo/swag/cmd/swag@latest",
|
|
"Ajouter annotations Swagger aux handlers",
|
|
"Générer docs: swag init -g cmd/server/main.go",
|
|
"Exposer /docs endpoint avec Swagger UI"
|
|
],
|
|
"acceptance_criteria": [
|
|
"swagger.yaml généré",
|
|
"Swagger UI accessible à /docs",
|
|
"Tous les endpoints documentés"
|
|
],
|
|
"dependencies": [],
|
|
"blocks": [],
|
|
"verification_command": "curl http://localhost:8080/docs"
|
|
}
|
|
],
|
|
"integration_matrix": {
|
|
"endpoints_analysis": [],
|
|
"type_mismatches": [],
|
|
"duplicate_code": [],
|
|
"missing_frontend_calls": [],
|
|
"missing_backend_routes": []
|
|
},
|
|
"risk_register": [
|
|
{
|
|
"id": "RISK-INT-001",
|
|
"risk": "CORS bloque toutes les requêtes en production si CORS_ALLOWED_ORIGINS vide",
|
|
"severity": "critical",
|
|
"probability": "certain",
|
|
"impact": "Application totalement inaccessible",
|
|
"mitigation_tasks": ["INT-CORS-001", "INT-CORS-002"],
|
|
"owner": "backend"
|
|
},
|
|
{
|
|
"id": "RISK-INT-002",
|
|
"risk": "CSRF désactivé en production si Redis indisponible",
|
|
"severity": "critical",
|
|
"probability": "possible",
|
|
"impact": "Vulnérabilité de sécurité majeure",
|
|
"mitigation_tasks": ["INT-AUTH-001"],
|
|
"owner": "backend"
|
|
},
|
|
{
|
|
"id": "RISK-INT-003",
|
|
"risk": "Incohérences de types causent des erreurs runtime imprévisibles",
|
|
"severity": "high",
|
|
"probability": "certain",
|
|
"impact": "Bugs intermittents difficiles à débugger",
|
|
"mitigation_tasks": ["INT-TYPE-001", "INT-TYPE-002", "INT-TYPE-003"],
|
|
"owner": "frontend"
|
|
},
|
|
{
|
|
"id": "RISK-INT-004",
|
|
"risk": "Duplication de code rend la maintenance difficile",
|
|
"severity": "medium",
|
|
"probability": "certain",
|
|
"impact": "Bugs par désynchronisation, confusion des développeurs",
|
|
"mitigation_tasks": ["INT-API-001", "INT-AUTH-002", "INT-CLEANUP-001"],
|
|
"owner": "frontend"
|
|
}
|
|
],
|
|
"validation_checklist": {
|
|
"pre_deployment": [
|
|
{"check": "CORS_ALLOWED_ORIGINS configuré pour production", "task_id": "INT-CORS-001", "verified": false},
|
|
{"check": "CSRF activé avec Redis", "task_id": "INT-AUTH-001", "verified": false},
|
|
{"check": "Tous les IDs sont des strings (UUID)", "task_ids": ["INT-TYPE-001", "INT-TYPE-002", "INT-TYPE-003"], "verified": false},
|
|
{"check": "Un seul client API utilisé", "task_id": "INT-API-001", "verified": false},
|
|
{"check": "Un seul store auth utilisé", "task_id": "INT-AUTH-002", "verified": false}
|
|
],
|
|
"integration_tests": [
|
|
{"test": "Auth flow complet E2E", "task_id": "INT-TEST-001", "passed": false},
|
|
{"test": "CRUD operations E2E", "task_id": "INT-TEST-002", "passed": false}
|
|
]
|
|
},
|
|
"progress_tracking": {
|
|
"total_tasks": 32,
|
|
"completed": 28,
|
|
"in_progress": 0,
|
|
"todo": 4,
|
|
"blocked": 0,
|
|
"completion_percentage": 88,
|
|
"last_updated": "2025-01-27T18:30:00Z",
|
|
"estimated_completion_date": null,
|
|
"estimated_hours_remaining": 4.5
|
|
}
|
|
}
|