Aller au contenu
KodrudderConnexionCommencer gratuitement

Dossier d'exemple, non retouché

Kodrudder, par lui-même

Ce dossier a été écrit par Kodrudder. Nous l'affichons tel quel, avec ses hypothèses et les écarts que la relecture a trouvés.

Relevé de cette génération : 177 s, coût estimé 0,54 $.

À lire d'abord

1 hypothèse marquée

Kodrudder transforme une idée en une phrase en un dossier « ready-to-dev » (cadrage, règles, parcours, architecture, SQL, API, tâches) destiné à Cursor, Claude Code, Lovable ou Bolt. Chaque information y est marquée Fait (avec URL) ou H.

Ce qui est décidé

  • Lot 1 : générer (Rapide ou Complète), discuter par section, exporter (ZIP + AGENTS.md), payer.
  • Plans : gratuit (1 projet, Rapide, BYOK) et Solo à 15 €/mois (quota mensuel, BYOK au-delà).
  • Export : AGENTS.md plus un CLAUDE.md d'une ligne qui l'importe.
  • Stripe avec Stripe Tax ; base et stockage hébergés dans l'UE ; interface FR/EN.
  • Hors lot 1 : GitHub, MCP, mises à jour par différences, agent de codage, équipes.

Ce qui reste une hypothèse à vérifier avant de coder

  • Estimation coût/durée (D-02) : tester le ratio estimé/réel sur quelques idées types.
  • Marge de Solo (15 €) : chiffrer le coût moyen d'une Complète avant de fixer les quotas.
  • TVA/TTC et sorties hors UE vers les fournisseurs IA : validation juridique (DPA, base légale).

Corrections de la relecture

Ces corrections prévalent sur le texte des sections.

  • EC-01 : RM-15 : plan free = 3 Rapides au total ; free_quota_used +1 par génération (transaction, verrou sur subscriptions), restitué si annulée sans section ou échec.
  • EC-02 : le quota Solo se décompte par utilisateur sur quota_units (hors générations remboursées ou échouées) depuis current_period_start ; plafond 30.
  • EC-03 : une génération BYOK a quota_units = 0.
  • EC-04 : un job (ou évaluation à la lecture) bascule en free après grace_ends_at ou current_period_end.
  • EC-05 : après DELETE /me, connexion refusée ; purge des données à J+30.
  • EC-06 : ajouter GET /models et users.default_model_id.
  • EC-07 : valider les questions en transaction (3 à 5, 2 options) avant insertion ; sinon relance puis failed.
  • EC-08 : ajouter users.doc_locale_default.
  • EC-09 : le SQL exporté est extrait des blocs de code de la section « Architecture et données ».

Comment utiliser ce dossier

  1. Donner à l'outil de codage AGENTS.md (ou CLAUDE.md) et cette page, puis lui faire lire le Cadrage et les règles métier.
  2. Lui fournir ensuite l'architecture, le SQL et l'API, en lui imposant les corrections ci-dessus.
  3. Exécuter les tâches du plan dans l'ordre (T-01, T-02…), une par une, en validant chaque « preuve de fin » avant la suivante.

Cadrage

14 hypothèses marquées

Résumé produit

  • Kodrudder est un SaaS qui transforme une idée en une phrase en un dossier « ready-to-dev » unique. Il est destiné à Cursor, Claude Code, Lovable et Bolt.
  • Le flux : 3 à 5 questions à deux options, puis génération (cadrage, règles, parcours, architecture, SQL, API, tâches). Chaque information est marquée « fait sourcé » ou « hypothèse ».
  • Lot 1 : générer, discuter section par section, exporter (ZIP + AGENTS.md), payer. Données hébergées dans l'UE, interface FR/EN.

Utilisateurs et résultat attendu

IDUtilisateurRésultat attendu
U-01Vibecoder non techniqueObtenir un dossier qu'un outil de codage IA peut exécuter sans qu'il écrive de spécification.
U-02DéveloppeurGagner du temps de cadrage et partir de tâches ordonnées, avec faits et hypothèses distingués.

Décisions tranchées

IDQuestionDécision retenueFait / H
D-01Quelle différence entre Rapide et Complète ?Rapide = 1 appel IA, sections condensées, 1 à 2 min. Complète = génération section par section avec vérification web.H
D-02Comment afficher coût et durée avant lancement ?Estimation en fourchette, calculée à partir de la longueur de l'idée et du modèle choisi. Elle est affichée avant confirmation.H
D-03Que se passe-t-il avec une clé d'API personnelle (BYOK) ?La clé est chiffrée côté serveur, jamais renvoyée au client. Le coût affiché est alors indicatif et facturé par le fournisseur.H
D-04Données dans l'UE et modèles IA ?Base de données et stockage hébergés dans l'UE. Les appels aux modèles sont signalés à l'utilisateur comme pouvant sortir de l'UE. Le catalogue de modèles est à valider avec le responsable produit.H
D-05Que contient l'export ?Le ZIP contient les sections en Markdown, le SQL et AGENTS.md. Il contient aussi un CLAUDE.md d'une ligne qui importe AGENTS.md. Raison : Cursor lit AGENTS.md, alors que Claude Code ne le lit que si aucun CLAUDE.md n'existe (https://codersera.com/blog/agents-md-complete-guide-2026/).Fait + H
D-06Paiement et TVA ?Abonnement Stripe, avec Stripe Tax activé. Raison : la TVA UE s'applique selon la localisation du client (https://docs.stripe.com/tax/digital-products). Le prix de 15 €/mois est TTC pour les particuliers.Fait + H
D-07Que couvre le plan gratuit ?1 projet, profondeur Rapide uniquement, BYOK autorisé.H
D-08Que couvre le plan Solo à 15 €/mois ?Projets illimités avec quota mensuel de générations. Au-delà, l'utilisateur passe en BYOK.H
D-09Langue du dossier généré ?Même langue que l'interface choisie par l'utilisateur, modifiable par projet.H
D-10Authentification ?E-mail + mot de passe et lien magique. Pas de connexion sociale dans le lot 1.H

Périmètre MVP (lot 1)

IDFonction
MVP-01Saisie de l'idée, puis 3 à 5 questions à deux options
MVP-02Génération Rapide et Complète, avec coût et durée affichés avant lancement
MVP-03Dossier en 7 sections, chaque information marquée Fait (avec URL) ou H
MVP-04Chat IA par section
MVP-05Choix du modèle et BYOK
MVP-06Export ZIP + AGENTS.md
MVP-07Comptes, abonnement gratuit/Solo, interface FR/EN, hébergement UE

Hors périmètre

IDExcluPreuve qui justifierait l'ajout H
HP-01Connexion GitHubPlus de 30 % des exports sont recopiés à la main dans un dépôt
HP-02Serveur MCPDemandes répétées d'utilisateurs de Claude Code ou Cursor
HP-03Mise à jour de la spec par différencesUtilisateurs qui régénèrent un même projet plus de 2 fois
HP-04Agent de codageHors positionnement tant que le dossier n'est pas validé
HP-05Équipe / multi-utilisateursDemandes d'accès partagé de la part d'abonnés payants

Mesures de succès (H : cibles à calibrer après 30 jours)

IDMesureCible
MS-01Idée → dossier exporté≥ 50 % des projets créés
MS-02Durée de génération Rapide≤ 2 min dans 90 % des cas
MS-03Conversion gratuit → Solo≥ 5 % à 60 jours
MS-04Dossier jugé « utilisable tel quel » (question post-export)≥ 70 %

Règles métier et critères d'acceptation

10 hypothèses marquées

Statuts (liste unique)

ObjetStatuts autorisés
Projetdraft → questioning → ready → generating → generated · failed
Générationqueued → running → succeeded · failed · canceled
Abonnementfree · solo_active · solo_past_due · solo_canceled
Clé BYOKvalid · invalid · revoked

Besoins fonctionnels

IDBesoinMVP
BE-01Créer un compte et se connecter (e-mail + mot de passe, lien magique)MVP-07
BE-02Saisir une idée en une phrase et créer un projetMVP-01
BE-03Répondre à 3 à 5 questions à deux optionsMVP-01
BE-04Choisir la profondeur (Rapide/Complète) et voir coût + durée avant lancementMVP-02
BE-05Lancer et suivre la génération du dossier en 7 sectionsMVP-02, 03
BE-06Voir chaque information marquée Fait (avec URL) ou HMVP-03
BE-07Discuter avec l'IA sur une section et appliquer ou rejeter sa propositionMVP-04
BE-08Choisir le modèle IA et enregistrer sa propre clé d'APIMVP-05
BE-09Exporter un ZIP contenant AGENTS.mdMVP-06
BE-10S'abonner à Solo, gérer ou résilier l'abonnementMVP-07
BE-11Basculer l'interface FR/EN et choisir la langue du dossierMVP-07
BE-12Supprimer un projet et son compte (RGPD)MVP-07

Règles métier

IDRègleValeur par défaut HCas limites
RM-01Le questionnaire compte 3 à 5 questions, chacune à exactement 2 options4 questionsGénération IA invalide (≠ 2 options, < 3 ou > 5 questions) : 1 relance auto, puis échec failed avec message ; idée vide ou < 10 caractères : refusée
RM-02Un plan free a 1 projet actif maximum, profondeur Rapide uniquement1 projetCréation d'un 2ᵉ projet refusée avec invitation à passer Solo ; suppression du projet libère le quota ; retour à free avec plusieurs projets : lecture et export gardés, génération bloquée
RM-03Le plan Solo (15 €/mois) autorise projets illimités et un quota mensuel de générations30 Rapide ou 10 Complète par mois, remis à zéro à la date anniversaireQuota épuisé : proposition de BYOK ; génération échouée non décomptée ; une Complète compte pour 3 Rapides
RM-04Coût et durée estimés sont affichés avant lancement, la confirmation explicite est obligatoireFourchette min–maxChangement de modèle ou de profondeur : recalcul avant confirmation ; en BYOK, coût marqué « indicatif, facturé par le fournisseur » ; estimation indisponible : lancement bloqué
RM-05Une seule génération queued/running par projet—Double clic ou 2 onglets : la 2ᵉ requête est refusée (409), pas de double décompte ; clé d'idempotence par requête
RM-06Génération échouée ou annulée : reprise possible sans perte des sections déjà produitesReprise à la 1ʳᵉ section manquanteTimeout fournisseur : 2 tentatives puis failed ; annulation utilisateur : état canceled, crédit restitué si aucune section produite
RM-07Toute information du dossier porte un marqueur Fait (avec URL) ou H ; un Fait sans URL est requalifié en H—URL inaccessible à la vérification (Complète) : requalifiée en H ; Rapide : sans vérification web, les faits sans URL deviennent H
RM-08Le chat est rattaché à une section ; une proposition de l'IA n'est appliquée qu'après acceptationHistorique conservé par sectionModification concurrente de la section : la version la plus récente gagne, l'ancienne est conservée 1 version ; quota de messages atteint : message d'erreur explicite
RM-09La clé BYOK est chiffrée côté serveur, jamais renvoyée au client ni journaliséeTest de validité à l'enregistrementClé invalide : invalid, génération bloquée ; clé révoquée en cours de génération : arrêt failed, aucune bascule vers la clé de la plateforme sans accord
RM-10L'export ZIP contient les 7 sections en Markdown, le SQL, AGENTS.md et un CLAUDE.md d'une ligne important AGENTS.md—Projet non generated : export refusé ; section en cours de régénération : export refusé ; noms de fichiers sûrs (pas de chemin absolu ni de ..)
RM-11L'accès à un projet est limité à son propriétaire—Accès à l'ID d'un autre utilisateur : 404 ; session expirée : redirection vers la connexion en conservant la saisie
RM-12Abonnement Stripe avec Stripe Tax ; l'état local suit les webhooks Stripe—Webhook dupliqué ou hors ordre : traitement idempotent ; paiement refusé : solo_past_due avec 7 jours de grâce puis retour à free ; résiliation : accès Solo jusqu'à la fin de période
RM-13Les données sont stockées dans l'UE ; l'utilisateur est averti que les appels aux modèles peuvent en sortirBandeau à la première générationSans acceptation de l'avertissement : génération bloquée
RM-14La suppression de compte efface projets, clés et historiques sous 30 jours ; l'abonnement actif est résilié d'abord30 joursAbonnement solo_active : résiliation demandée avant suppression

Table de décision : autorisation de lancer une génération (RM-02, 03, 04, 09, 13)

PlanProfondeurCléQuota restantEstimation confirméeAvertissement UE acceptéRésultat
freeComplète————Refusé : Solo requis
freeRapideplateforme—ouiouiAutorisé (hors 1ʳᵉ génération gratuite : quota gratuit de 3 Rapides, H)
freeRapideBYOK valid—ouiouiAutorisé
soloRapide/ComplèteplateformeouiouiouiAutorisé
soloRapide/Complèteplateformenon——Refusé : proposer BYOK
soloRapide/ComplèteBYOK validnon requisouiouiAutorisé
touttoutBYOK invalid/revoked———Refusé : corriger la clé
touttouttouttoutnontoutRefusé : confirmation requise
touttouttouttoutouinonRefusé : accepter l'avertissement
solo_past_due après grâcetoutplateforme———Traité comme free

Critères d'acceptation

IDÉtant donnéQuandAlorsBERM
CA-01Une idée vide ou de 5 caractèresL'utilisateur valideErreur « idée trop courte », aucun projet crééBE-02RM-01
CA-02Une idée valideLe projet passe en questioning3 à 5 questions sont affichées, chacune avec exactement 2 optionsBE-03RM-01
CA-03L'IA renvoie 2 fois un questionnaire invalideLa relance échoueProjet failed, message d'erreur, possibilité de réessayerBE-03RM-01
CA-04Un compte free avec 1 projetIl crée un 2ᵉ projetRefus (403) avec lien vers l'abonnement SoloBE-02RM-02
CA-05Un compte freeIl sélectionne la profondeur ComplèteL'option est désactivée et renvoie à SoloBE-04RM-02
CA-06Un compte Solo sans quota restant, sans BYOKIl lance une générationRefus avec proposition d'ajouter une clé BYOKBE-05RM-03
CA-07Un projet readyL'utilisateur change de modèleCoût et durée sont recalculés avant confirmationBE-04, 08RM-04
CA-08Une clé BYOK valid sélectionnéeL'estimation s'afficheElle porte la mention « indicatif, facturé par le fournisseur »BE-04RM-04
CA-09Une estimation non confirméeLe lancement est tenté via l'APIRefus 422, aucune génération crééeBE-04RM-04
CA-10Une génération runningUne 2ᵉ requête de lancement arriveRéponse 409, un seul décompte de quotaBE-05RM-05
CA-11Une génération échouée après 3 sectionsL'utilisateur relanceReprise à la 4ᵉ section, sections 1 à 3 conservéesBE-05RM-06
CA-12Une génération annulée avant toute sectionAnnulation confirméeStatut canceled, crédit restituéBE-05RM-06
CA-13Un dossier généréL'utilisateur le consulteAucune information sans marqueur Fait/H ; chaque Fait a une URLBE-06RM-07
CA-14Un Fait sans URL dans la sortie IALe dossier est enregistréIl est stocké comme HBE-06RM-07
CA-15Une section ouverte dans le chatL'IA propose une modificationLa section ne change qu'après clic sur « Appliquer » ; « Rejeter » la laisse intacteBE-07RM-08
CA-16Deux modifications concurrentes sur une sectionElles sont enregistréesLa plus récente est conservée, la version précédente est restaurableBE-07RM-08
CA-17Une clé API invalide saisieL'utilisateur l'enregistreStatut invalid, génération bloquée avec messageBE-08RM-09
CA-18Une clé BYOK enregistréeToute réponse API et tout journal sont inspectésLa clé en clair n'y apparaît jamaisBE-08RM-09
CA-19Une clé révoquée en cours de générationLe fournisseur renvoie une erreur d'authentificationGénération failed, aucune bascule vers la clé de la plateformeBE-05, 08RM-09
CA-20Un projet generatedL'utilisateur exporteLe ZIP contient 7 sections .md, le SQL, AGENTS.md et un CLAUDE.md important AGENTS.mdBE-09RM-10
CA-21Un projet generatingL'export est demandéRefus avec message expliciteBE-09RM-10
CA-22Un utilisateur AIl appelle l'ID d'un projet de BRéponse 404BE-02RM-11
CA-23Un abonnement solo_activeLe webhook de paiement refusé arriveStatut solo_past_due, accès Solo conservé 7 jours puis retour à freeBE-10RM-12
CA-24Un même webhook Stripe reçu 2 foisLe second est traitéAucun effet supplémentaire sur l'étatBE-10RM-12
CA-25Un abonné qui résilieLa résiliation est confirméeStatut solo_canceled, accès Solo jusqu'à la fin de période puis freeBE-10RM-12
CA-26Un utilisateur n'ayant jamais lancé de générationIl en lance uneL'avertissement UE est affiché ; sans acceptation, lancement refuséBE-05RM-13
CA-27Un utilisateur FR change la langue en ENIl actualise l'interfaceTous les libellés passent en anglais ; la langue du dossier reste celle du projetBE-11—
CA-28Un compte solo_activeIl demande la suppression du compteLa suppression est refusée tant que l'abonnement n'est pas résiliéBE-12RM-14
CA-29Un compte sans abonnement actifIl confirme la suppressionLes projets, clés et historiques sont purgés sous 30 joursBE-12RM-14
CA-30Un compte free ayant épuisé son quota gratuit de 3 RapidesIl lance une 4ᵉ génération sans BYOKRefus avec invitation à passer Solo ou à ajouter une clé BYOKBE-05RM-02, RM-03

Questions ouvertes (H à valider)

IDQuestionHypothèse retenue
Q-01Quota mensuel Solo exact et quota gratuit de 3 Rapides30 Rapide ou 10 Complète ; 3 Rapides gratuites (RM-03, RM-02)
Q-02Délai de grâce après impayé7 jours (RM-12)
Q-03Durée de rétention après suppression30 jours (RM-14)

Parcours et écrans

7 hypothèses marquées

Parcours principaux

P-01 — Vibecoder / développeur : de l'idée au dossier exporté (U-01, U-02)

  1. S'inscrire ou se connecter (e-mail + mot de passe, ou lien magique).
  2. Saisir l'idée en une phrase et valider (≥ 10 caractères, RM-01).
  3. Répondre aux 3 à 5 questions à deux options, une par une.
  4. Choisir la profondeur (Rapide / Complète) et le modèle ; lire coût et durée estimés.
  5. Accepter l'avertissement UE (1ʳᵉ génération), puis confirmer le lancement.
  6. Suivre la progression section par section ; annuler possible.
  7. Lire le dossier : sections 1 à 7, marqueurs Fait (URL) / H.
  8. Discuter d'une section avec l'IA, puis « Appliquer » ou « Rejeter ».
  9. Exporter le ZIP (AGENTS.md inclus).

P-02 — Clé BYOK et choix du modèle (U-02)

  1. Ouvrir Réglages > Clés d'API.
  2. Choisir le fournisseur, coller la clé, enregistrer.
  3. Voir le résultat du test (valid / invalid) ; révoquer possible.
  4. Sélectionner le modèle au moment du lancement ; voir la mention « indicatif ».

P-03 — Abonnement Solo (U-01, U-02)

  1. Atteindre une limite (2ᵉ projet, Complète, quota) ou ouvrir Abonnement.
  2. Consulter la comparaison Gratuit / Solo (15 €/mois).
  3. Payer via Stripe Checkout, puis retour sur l'app avec statut mis à jour.
  4. Gérer ou résilier via le portail Stripe.

P-04 — Suppression de compte (RGPD)

  1. Réglages > Compte > Supprimer.
  2. Si solo_active : résilier d'abord (RM-14).
  3. Confirmer en saisissant « SUPPRIMER » ; message de purge sous 30 jours.

Écrans

IDButÉléments clésÉtats à gérerCA
EC-01Connexion / inscriptionChamps e-mail et mot de passe, lien magique, sélecteur FR/ENErreur d'identifiants, lien envoyé, lien expiré, chargementCA-27
EC-02Liste des projetsCartes (nom, statut, date), bouton « Nouveau projet », jauge de quotaVide (onboarding), chargement, erreur réseau, limite free atteinte (CTA Solo)CA-04
EC-03Saisie de l'idéeChamp texte avec compteur, bouton « Continuer »Idée trop courte, envoi en cours, échec IA avec réessaiCA-01
EC-04QuestionnaireUne question par vue, 2 options (boutons radio), progression « 2/4 », retourChargement des questions, questionnaire invalide → failed + réessayer, reprise après rechargementCA-02, CA-03
EC-05LancementChoix Rapide/Complète, sélecteur de modèle, estimation coût/durée, case d'avertissement UE, bouton « Lancer »Complète désactivée en free (renvoi Solo), estimation en recalcul/indisponible (lancement bloqué), clé invalid, quota épuisé (proposer BYOK), mention « indicatif » en BYOKCA-05, 06, 07, 08, 09, 26, 30
EC-06Suivi de générationListe des 7 sections avec statut, durée écoulée, bouton « Annuler »queued, running, failed avec « Reprendre », canceled, 409 si double lancement, clé révoquéeCA-10, 11, 12, 19
EC-07Dossier (lecture)Sommaire des 7 sections, rendu Markdown/tableaux, badges Fait/H avec URL cliquable, bouton « Exporter »Section en régénération, export refusé hors generatedCA-13, 14, 20, 21
EC-08Chat de sectionPanneau latéral, historique, proposition IA en aperçu, boutons « Appliquer » / « Rejeter »Envoi en cours, quota de messages atteint, erreur IA, conflit de version (restaurer la précédente)CA-15, 16
EC-09Réglages : clés et modèleListe des clés avec statut, ajout, révocation, modèle par défautClé masquée après saisie, test en cours, invalid/revokedCA-17, 18
EC-10AbonnementPlan actuel, comparatif, bouton Stripe, date de fin de périodefree, solo_active, solo_past_due (bandeau 7 jours), solo_canceled, retour de paiement en attenteCA-23, 24, 25
EC-11Réglages : compteLangue de l'interface, langue du dossier par défaut, suppressionSuppression bloquée si solo_active, confirmation par saisieCA-27, 28, 29
EC-12Erreurs transversesPages 404 (projet d'autrui), session expiréeRedirection vers EC-01 avec saisie conservéeCA-22

Règles d'accessibilité et mobile

  1. Clavier : tout parcours P-01 réalisable sans souris ; ordre de focus logique, focus visible, aucun piège (WCAG 2.2, https://www.w3.org/WAI/WCAG22/quickref/).
  2. Erreurs et états : message textuel associé au champ, annoncé aux lecteurs d'écran ; la couleur ne porte jamais seule l'information, y compris pour Fait / H (texte ou icône + libellé).
  3. Progression asynchrone : le suivi de génération (EC-06) annonce les changements de statut via une région live (H : aria-live="polite").
  4. Cibles tactiles : ≥ 44 × 44 px H pour options du questionnaire et boutons de confirmation.
  5. Mobile first : EC-03 à EC-06 utilisables à 360 px de large ; le chat (EC-08) s'ouvre en plein écran sur mobile, en panneau latéral sur desktop H.
  6. Actions destructrices et coûteuses : confirmation explicite (lancement, annulation, suppression) ; libellés FR/EN sans texte codé en dur dans les images.

Limite : ces règles sont des exigences de conception ; la conformité WCAG reste à tester sur le rendu final (H : niveau AA visé).

Architecture et données

2 hypothèses marquées

Stack retenue

CoucheChoixRaisonAlternative écartée
Front + APINext.js (TypeScript), i18n FR/ENUn seul dépôt, SSR, adapté aux outils de codage IA HSPA React + API séparée : deux déploiements pour un MVP
Base de donnéesPostgreSQL managé, région UERM-13, transactions et contraintes pour RM-02/05NoSQL : invariants multi-tables difficiles à garantir
AuthentificationAuth intégrée (e-mail + lien magique), sessions serveurD-10Connexion sociale : hors lot 1
GénérationWorker asynchrone + file en base (generations)Génération de 1 à plusieurs minutes, reprise RM-06Appel synchrone : timeouts HTTP
Appels IACouche d'abstraction multi-fournisseursBE-08 : choix du modèle et BYOKFournisseur unique : pas de choix de modèle
Secrets BYOKChiffrement applicatif AES-256-GCM, clé maître en KMS UERM-09Stockage en clair : interdit
PaiementStripe Billing + Stripe Tax, webhooksD-06, RM-12Paiement maison : conformité TVA
ExportZIP généré à la volée côté serveurRM-10, pas de stockage de fichiersStockage objet : inutile au lot 1

Composants

  • Web : interface FR/EN, parcours idée → questions → estimation → dossier → export.
  • API : authentification, projets, génération, chat, clés, export, webhooks Stripe.
  • Worker : exécute les générations (queued → running), enregistre les sections, applique RM-06/07.
  • Connecteur IA : appelle les modèles choisis, plateforme ou BYOK.
  • Facturation : synchronise l'abonnement local depuis les webhooks (idempotents).
  • Moteur de quotas : calcule le décompte (Complète = 3 Rapides, RM-03).

Modèle de données (PostgreSQL)

CREATE EXTENSION IF NOT EXISTS pgcrypto;

CREATE TABLE users (
  id            uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  email         text NOT NULL,
  password_hash text,                                   -- NULL si lien magique uniquement
  ui_locale     text NOT NULL DEFAULT 'fr' CHECK (ui_locale IN ('fr','en')),   -- BE-11
  eu_notice_accepted_at timestamptz,                    -- RM-13 : NULL = génération bloquée
  deletion_requested_at timestamptz,                    -- RM-14
  created_at    timestamptz NOT NULL DEFAULT now()
);
CREATE UNIQUE INDEX users_email_uq ON users (lower(email));

CREATE TABLE subscriptions (
  id                     uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  user_id                uuid NOT NULL UNIQUE REFERENCES users(id) ON DELETE CASCADE,
  status                 text NOT NULL DEFAULT 'free'
    CHECK (status IN ('free','solo_active','solo_past_due','solo_canceled')),
  stripe_customer_id     text UNIQUE,
  stripe_subscription_id text UNIQUE,
  current_period_start   timestamptz,                   -- RM-03 : date anniversaire
  current_period_end     timestamptz,                   -- RM-12 : accès jusqu'à la fin de période
  grace_ends_at          timestamptz,                   -- RM-12 : 7 jours après impayé
  free_quota_used        integer NOT NULL DEFAULT 0
    CHECK (free_quota_used BETWEEN 0 AND 3),            -- RM-02 : 3 Rapides gratuites (H)
  updated_at             timestamptz NOT NULL DEFAULT now()
);

-- RM-12 : idempotence des webhooks Stripe (CA-24)
CREATE TABLE stripe_events (
  event_id     text PRIMARY KEY,
  type         text NOT NULL,
  processed_at timestamptz NOT NULL DEFAULT now()
);

CREATE TABLE ai_models (
  id          text PRIMARY KEY,                         -- ex. identifiant catalogue
  provider    text NOT NULL,
  label       text NOT NULL,
  input_price_per_mtok  numeric(10,4) NOT NULL CHECK (input_price_per_mtok >= 0),
  output_price_per_mtok numeric(10,4) NOT NULL CHECK (output_price_per_mtok >= 0),
  active      boolean NOT NULL DEFAULT true
);

CREATE TABLE byok_keys (
  id          uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  user_id     uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE,
  provider    text NOT NULL,
  key_cipher  bytea NOT NULL,                           -- RM-09 : chiffré, jamais renvoyé ni journalisé
  key_last4   text NOT NULL,                            -- seul élément affichable
  status      text NOT NULL DEFAULT 'valid'
    CHECK (status IN ('valid','invalid','revoked')),
  created_at  timestamptz NOT NULL DEFAULT now(),
  UNIQUE (user_id, provider)
);

CREATE TABLE projects (
  id          uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  owner_id    uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE,   -- RM-11
  name        text NOT NULL,
  idea        text NOT NULL CHECK (char_length(btrim(idea)) >= 10),   -- RM-01 : idée >= 10 car.
  doc_locale  text NOT NULL DEFAULT 'fr' CHECK (doc_locale IN ('fr','en')),  -- D-09
  status      text NOT NULL DEFAULT 'draft'
    CHECK (status IN ('draft','questioning','ready','generating','generated','failed')),
  created_at  timestamptz NOT NULL DEFAULT now(),
  updated_at  timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX projects_owner_idx ON projects (owner_id, created_at DESC);

CREATE TABLE questions (
  id          uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  project_id  uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
  position    smallint NOT NULL CHECK (position BETWEEN 1 AND 5),    -- RM-01 : 3 à 5 questions
  prompt      text NOT NULL,
  option_a    text NOT NULL,                            -- RM-01 : exactement 2 options (deux colonnes NOT NULL)
  option_b    text NOT NULL,
  answer      text CHECK (answer IN ('a','b')),
  UNIQUE (project_id, position),
  CHECK (option_a <> option_b)
);
-- Le minimum de 3 questions est vérifié par l'application avant le passage en `questioning`.

CREATE TABLE generations (
  id               uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  project_id       uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
  depth            text NOT NULL CHECK (depth IN ('rapide','complete')),
  model_id         text NOT NULL REFERENCES ai_models(id),
  key_source       text NOT NULL CHECK (key_source IN ('platform','byok')),
  byok_key_id      uuid REFERENCES byok_keys(id) ON DELETE SET NULL,
  status           text NOT NULL DEFAULT 'queued'
    CHECK (status IN ('queued','running','succeeded','failed','canceled')),
  idempotency_key  text NOT NULL,                       -- RM-05
  estimate_min_eur numeric(8,2) NOT NULL,               -- RM-04 : estimation obligatoire
  estimate_max_eur numeric(8,2) NOT NULL,
  estimate_min_sec integer NOT NULL,
  estimate_max_sec integer NOT NULL,
  estimate_confirmed_at timestamptz NOT NULL,           -- RM-04 : pas de génération sans confirmation (CA-09)
  quota_units      smallint NOT NULL CHECK (quota_units IN (0,1,3)),     -- RM-03 : Rapide=1, Complète=3
  quota_refunded   boolean NOT NULL DEFAULT false,      -- RM-06 : crédit restitué
  attempts         smallint NOT NULL DEFAULT 0 CHECK (attempts <= 2),    -- RM-06 : 2 tentatives
  error_message    text,
  created_at       timestamptz NOT NULL DEFAULT now(),
  finished_at      timestamptz,
  CHECK (key_source = 'platform' OR byok_key_id IS NOT NULL),
  CHECK (estimate_min_eur <= estimate_max_eur AND estimate_min_sec <= estimate_max_sec),
  UNIQUE (project_id, idempotency_key)
);
-- RM-05 : une seule génération queued/running par projet (CA-10 -> 409)
CREATE UNIQUE INDEX generations_one_active_uq
  ON generations (project_id) WHERE status IN ('queued','running');
-- RM-03 : décompte mensuel par utilisateur via project -> owner
CREATE INDEX generations_project_created_idx ON generations (project_id, created_at DESC);

CREATE TABLE sections (
  id          uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  project_id  uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
  position    smallint NOT NULL CHECK (position BETWEEN 1 AND 7),    -- 7 sections du dossier
  slug        text NOT NULL,
  title       text NOT NULL,
  content_md  text NOT NULL,
  version     integer NOT NULL DEFAULT 1,               -- RM-08 : version courante
  generation_id uuid REFERENCES generations(id) ON DELETE SET NULL,  -- RM-06 : reprise à la 1re manquante
  updated_at  timestamptz NOT NULL DEFAULT now(),
  UNIQUE (project_id, position),
  UNIQUE (project_id, slug)
);

-- RM-08 : version précédente restaurable (CA-16)
CREATE TABLE section_versions (
  id          uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  section_id  uuid NOT NULL REFERENCES sections(id) ON DELETE CASCADE,
  version     integer NOT NULL,
  content_md  text NOT NULL,
  created_at  timestamptz NOT NULL DEFAULT now(),
  UNIQUE (section_id, version)
);

-- RM-07 : toute information porte un marqueur ; un Fait exige une URL (CA-13, CA-14)
CREATE TABLE claims (
  id          uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  section_id  uuid NOT NULL REFERENCES sections(id) ON DELETE CASCADE,
  text        text NOT NULL,
  kind        text NOT NULL CHECK (kind IN ('fact','hypothesis')),
  source_url  text,
  CHECK (kind = 'hypothesis' OR (source_url IS NOT NULL AND source_url ~ '^https?://'))
);
CREATE INDEX claims_section_idx ON claims (section_id);

CREATE TABLE chat_messages (
  id          uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  section_id  uuid NOT NULL REFERENCES sections(id) ON DELETE CASCADE,   -- RM-08 : chat par section
  role        text NOT NULL CHECK (role IN ('user','assistant')),
  content     text NOT NULL,
  proposal_md text,                                     -- proposition de l'IA
  proposal_status text CHECK (proposal_status IN ('pending','applied','rejected')),  -- appliquée seulement après acceptation
  created_at  timestamptz NOT NULL DEFAULT now(),
  CHECK (proposal_md IS NOT NULL OR proposal_status IS NULL)
);
CREATE INDEX chat_messages_section_idx ON chat_messages (section_id, created_at);

-- RM-02 : 1 projet actif maximum en free : contrôle applicatif en transaction
-- (verrou sur subscriptions) car la règle dépend du plan, non exprimable en contrainte simple.

Notes de cohérence

  • Statuts alignés sur la section 2 : projet, génération, abonnement, clé BYOK.
  • Hypothèses H : quota gratuit de 3 Rapides et grâce de 7 jours (Q-01, Q-02) ; dates anniversaire dérivées de current_period_start.
  • Les règles RM-02 (limite de projets), RM-13 (avertissement UE) et RM-14 (purge sous 30 jours via deletion_requested_at et un job planifié) sont appliquées côté application ; les contraintes SQL couvrent les autres invariants.

API, intégrations et sécurité

9 hypothèses marquées

API (REST JSON, préfixe /api/v1, session cookie, CSRF actif)

Rôles : anon (non connecté), owner (propriétaire du projet), user (connecté). Tout accès à un projet d'autrui renvoie 404 (RM-11, CA-22). Erreurs communes : 401 (session expirée), 404, 429 (limite de débit, H).

MéthodeCheminRôleEntréeSortieErreursRM / CA
POST/auth/signupanonemail, password, ui_locale201 user409 e-mail pris, 422RM-11 · CA-27
POST/auth/login · /auth/magic-linkanonemail, password ou email200 session / 202 lien envoyé401, 410 lien expiréRM-11
GET/projectsuser—liste + quota restant401RM-02, 03
POST/projectsuseridea, doc_locale201 project draft422 idée < 10 car., 403 free avec 1 projetRM-01, 02 · CA-01, 04
POST/projects/{id}/questionsowner—201 3 à 5 questions, projet questioning502 (2 échecs IA → failed), 409 déjà généréesRM-01 · CA-02, 03
PUT/projects/{id}/answersowner[{question_id, answer a/b}]200 projet ready422 réponses incomplètesRM-01 · CA-02
POST/projects/{id}/estimateownerdepth, model_id, key_source200 min/max € et s, drapeau indicative (BYOK)403 Complète en free, 422 estimation indisponibleRM-02, 04 · CA-05, 07, 08
PUT/me/eu-noticeuseraccepted=true204 (pose eu_notice_accepted_at)—RM-13 · CA-26
POST/projects/{id}/generationsownerdepth, model_id, key_source, byok_key_id?, header Idempotency-Key, confirmed_estimate202 generation queued409 génération active, 422 non confirmée, 403 refus table de décision (plan, quota, clé invalid/revoked, avertissement UE non accepté)RM-02 à 05, 09, 13 · CA-06, 09, 10, 26, 30
GET/projects/{id}/generations/{gid}owner—statut, sections produites, erreur—RM-06 · CA-11
POST/projects/{id}/generations/{gid}/cancelowner—200 canceled, crédit restitué si 0 section409 déjà terminéeRM-06 · CA-12
POST/projects/{id}/generations/{gid}/resumeownerIdempotency-Key202 reprise à la 1ʳᵉ section manquante409, 403 quota/cléRM-05, 06 · CA-11
GET/projects/{id} · /sections/{sid}owner—projet, sections, claims (fact+URL / hypothesis)404RM-07, 11 · CA-13, 22
GET/sections/{sid}/chatowner—historique404RM-08
POST/sections/{sid}/chatownermessage201 réponse + proposal_md (pending)429 quota de messages, 502 erreur IARM-08 · CA-15
POST/chat-messages/{mid}/apply · /rejectownerbase_version200 section v+1 (ancienne dans section_versions) / 200 inchangée409 version périmée (restauration proposée)RM-08 · CA-15, 16
POST/sections/{sid}/restoreownerversion200404RM-08 · CA-16
GET/me/keysuser—liste (provider, key_last4, status)—RM-09 · CA-18
POST/me/keysuserprovider, key201 valid ou invalid (test à l'enregistrement)422RM-09 · CA-17, 18
DELETE/me/keys/{kid}user—204 (revoked)404RM-09
GET/projects/{id}/exportowner—ZIP : 7 sections .md, SQL, AGENTS.md, CLAUDE.md409 projet non generated ou section en régénérationRM-10 · CA-20, 21
POST/billing/checkout · /billing/portaluser—URL Stripe409 déjà SoloRM-12 · CA-25
POST/webhooks/stripeStripe (signature)événement200400 signature invalide ; doublon : 200 sans effetRM-12 · CA-23, 24
PATCH/meuserui_locale200422BE-11 · CA-27
DELETE/meuserconfirmation « SUPPRIMER »202 purge sous 30 jours409 solo_active (résilier d'abord)RM-14 · CA-28, 29
DELETE/projects/{id}owner—204404RM-02

Intégrations externes

ServiceUsagePoint de défaillanceComportement de repli
Fournisseurs IA (plateforme ou BYOK)Questions, sections, chatTimeout, 429, erreur d'authentification2 tentatives puis failed (RM-06) ; clé révoquée : failed sans bascule vers la clé plateforme (RM-09, CA-19) ; reprise possible
Stripe Billing + TaxAbonnement Solo, TVAIndisponibilité, webhooks dupliqués ou hors ordreIdempotence via stripe_events ; état local conservé ; solo_past_due : grâce 7 j H puis free ; Checkout indisponible : message et réessai
Vérification d'URL des Faits (Complète)Contrôle d'accessibilitéSite lent ou inaccessibleFait requalifié en H (RM-07) ; jamais bloquant
E-mail transactionnel (UE, H)Lien magiqueÉchec d'envoiBouton « Renvoyer » ; connexion par mot de passe toujours possible
KMS UEClé maître de chiffrement BYOKIndisponibleEnregistrement et usage des clés BYOK refusés ; génération plateforme non affectée

Sécurité et données personnelles

  1. Authentification : mots de passe hachés (argon2id, H), cookie HttpOnly/Secure/SameSite=Lax, lien magique à usage unique expirant sous 15 min H, limitation de débit sur la connexion.
  2. Autorisation : chaque requête filtre par owner_id (RM-11) ; refus renvoyé en 404 pour ne pas révéler l'existence du projet.
  3. Clés BYOK : AES-256-GCM, clé maître en KMS UE ; la clé en clair n'apparaît ni dans les réponses (seul key_last4) ni dans les journaux (masquage systématique, test CA-18) ; l'appel au fournisseur se fait côté serveur uniquement (RM-09).
  4. Entrées et sorties : validation de schéma sur chaque endpoint ; contenu IA rendu en Markdown assaini (pas de HTML brut) ; noms de fichiers du ZIP normalisés, sans .. ni chemin absolu (RM-10) ; secrets Stripe vérifiés par signature de webhook.
  5. Protection contre les abus : Idempotency-Key obligatoire pour lancer ou reprendre ; index unique des générations actives (RM-05) ; plafond de messages de chat et de débit par utilisateur H.
  6. RGPD : données stockées dans l'UE ; avertissement avant la 1ʳᵉ génération sur la sortie possible des données vers les fournisseurs IA (RM-13) ; suppression du compte et purge de projets, clés et historiques sous 30 jours (RM-14) ; journaux sans idée de projet ni contenu de dossier H.

Limite : les valeurs marquées H sont à confirmer ; aucune mesure de charge n'a été réalisée.

Plan de développement

4 hypothèses marquées

Conventions : TS strict, Next.js, PostgreSQL. Chaque tâche livre du code + tests. Ordre = dépendances. Les corrections « corrige EC-xx » sont intégrées dans les tâches (dont ajout de RM-15 : plan free = 3 Rapides au total, free_quota_used +1 par génération, en transaction avec verrou sur subscriptions, restitué si annulée sans section ou échec ; BYOK = quota_units = 0).

Jalon J1 — Socle, comptes, projets, questionnaire (MVP-01, 07 partiel)

IDObjectifModules / fichiersDépend deCAPreuve de finPiège à éviter
T-01Initialiser Next.js TS, lint, i18n FR/EN, CI, config envapp/, i18n/, .github/—CA-27pnpm build && pnpm test verts ; bascule FR/EN testéeLibellés en dur : tout passe par les clés i18n
T-02Migrations SQL complètes de §4 + seed ai_models ; corrige EC-06/08 : ajouter users.default_model_id (FK ai_models) et users.doc_locale_default (fr/en)db/migrations/001_init.sql, db/seed.sqlT-01CA-13, 14, 16pnpm db:migrate sur base vide ; insertion Fait sans URL rejetée par CHECKOublier l'index unique partiel generations_one_active_uq
T-03Auth e-mail + mot de passe (argon2id), lien magique (15 min, usage unique), sessions HttpOnly/Secure/SameSite, CSRF, rate limit ; connexion refusée si deletion_requested_at non nul (corrige EC-05)api/auth/*, lib/session.tsT-02CA-22, 27Tests : login OK/KO, lien expiré 410, compte en suppression refuséFuite d'existence d'un e-mail dans les messages d'erreur
T-04Garde d'autorisation requireOwner (filtre owner_id, 404 si autrui)lib/authz.tsT-03CA-22Test : user A sur projet de B → 404Renvoyer 403 au lieu de 404
T-05POST/GET/DELETE /projects : idée ≥ 10 car. ; limite 1 projet free en transaction avec verrou subscriptionsapi/projects/*T-04CA-01, 04Tests : idée de 5 car. → 422 ; 2ᵉ projet free → 403 ; suppression libère le quotaCourse : deux créations simultanées dépassant le quota
T-06Abstraction fournisseurs IA (interface complete(), timeouts, 2 tentatives, erreurs typées auth/429/timeout) + mock déterministelib/ai/*T-01CA-03, 19Tests avec mock : timeout → 2 essais puis erreur typéeJournaliser le prompt ou la clé
T-07POST /projects/{id}/questions ; corrige EC-07 : validation en transaction (3 à 5 questions, 2 options distinctes) avant insertion ; relance auto 1 fois, puis projet failed ; PUT /answers → readyapi/projects/[id]/questions, lib/questionnaire.tsT-05, T-06CA-02, 03Test : mock invalide ×2 → failed, rien inséré ; mock valide → questioningInsertion partielle de questions après échec de validation
T-08UI EC-01 à EC-04 (connexion, liste, idée, questionnaire), états vide/erreur/chargement, accessibilité clavier, cibles ≥ 44 pxapp/(auth), app/projects/*T-03, T-05, T-07CA-01, 02, 03, 27Test Playwright : P-01 étapes 1 à 3 au clavier, à 360 pxPerdre la saisie à l'expiration de session

Jalon J2 — Clés BYOK, modèles, estimation (MVP-05, 02 partiel)

IDObjectifModules / fichiersDépend deCAPreuve de finPiège à éviter
T-09Chiffrement AES-256-GCM, clé maître via KMS UE (abstraction + stub local), masquage des logslib/crypto.ts, lib/logger.tsT-01CA-18Test unitaire aller-retour ; test log sans clé en clairIV réutilisé
T-10GET/POST/DELETE /me/keys : test de validité à l'enregistrement (valid/invalid), revoked à la suppression ; réponses sans clé (seul key_last4)api/me/keysT-04, T-06, T-09CA-17, 18Tests : clé invalide → invalid ; réponse/log inspectés sans cléRenvoyer la clé dans l'écho du POST
T-11GET /models (catalogue actif) et PATCH /me (ui_locale, default_model_id, doc_locale_default) — corrige EC-06/08api/models, api/meT-02, T-04CA-07, 27Tests : modèle inactif refusé ; défaut persistéAccepter un model_id inexistant
T-12POST /projects/{id}/estimate : fourchette € et s selon profondeur × modèle ; drapeau indicative en BYOK ; 403 Complète en free ; 422 si indisponiblelib/estimate.ts, api/.../estimateT-02, T-04CA-05, 07, 08Tests : changement de modèle → recalcul ; BYOK → indicative=trueEstimation min > max
T-13UI EC-09 (clés/modèle par défaut)app/settings/keysT-10, T-11CA-17, 18Test E2E : ajout clé invalide → statut visible, clé masquéeAfficher la clé après saisie

Jalon J3 — Facturation, quotas, autorisation de lancement (MVP-07, 02)

IDObjectifModules / fichiersDépend deCAPreuve de finPiège à éviter
T-14Stripe Checkout/Portal + POST /webhooks/stripe : signature vérifiée, idempotence stripe_events, transitions solo_active/solo_past_due (grâce 7 j)/solo_canceledapi/billing/*, api/webhooks/stripeT-03CA-23, 24, 25Tests : webhook rejoué ×2 sans effet ; hors ordre toléré ; signature KO → 400Traiter l'événement avant d'écrire stripe_events
T-15Job d'expiration (corrige EC-04) : status = 'free' si grace_ends_at ou current_period_end dépassé ; évaluation aussi à la lecture du planjobs/expire-subscriptions.ts, lib/plan.tsT-14CA-23, 25Test avec horloge simulée : J+8 → free ; résilié → Solo jusqu'à fin de périodeFuseaux : comparer en UTC
T-16Moteur de quotas (corrige EC-01/02/03) : requête de décompte Solo = somme quota_units des générations de l'utilisateur (jointure projects.owner_id) avec created_at >= current_period_start, quota_refunded = false, status <> 'failed', plafond 30 ; index adapté ; RM-15 pour free (3 Rapides, free_quota_used en transaction + verrou) ; BYOK → quota_units = 0 ; restitution sur annulation sans section ou écheclib/quota.ts, migration 002_quota_index.sqlT-02, T-14CA-06, 12, 30Tests : Complète = 3 ; 4ᵉ free refusée ; échec non décompté ; BYOK = 0Course sur deux lancements : verrou obligatoire
T-17PUT /me/eu-notice + fonction pure canLaunch() = table de décision (plan, profondeur, clé, quota restant, estimation confirmée, avertissement UE) ; « quota gratuit restant > 0 » (corrige EC-01)lib/launch-policy.ts, api/me/eu-noticeT-10, T-12, T-15, T-16CA-05, 06, 08, 09, 17, 26, 30Test paramétré : chaque ligne de la table de décisionOrdre des refus incohérent avec la table
T-18UI EC-10 (abonnement) et bandeaux solo_past_dueapp/billingT-14, T-15CA-23, 25Test E2E avec Stripe en mode testBandeau absent hors grâce

Jalon J4 — Génération asynchrone et dossier (MVP-02, 03)

IDObjectifModules / fichiersDépend deCAPreuve de finPiège à éviter
T-19POST /generations : Idempotency-Key obligatoire, confirmed_estimate, canLaunch, insertion queued ; 409 sur index unique ; 422 non confirmée ; projet → generatingapi/.../generationsT-12, T-16, T-17CA-09, 10, 26, 30Tests : double POST concurrent → un seul 202 + un 409, un seul décompteDécompter avant l'insertion réussie
T-20Worker : prend les queued, produit les 7 sections dans l'ordre, enregistre section + claims ; Fait sans URL → H ; Rapide : sans vérification web ; Complète : vérification d'URL, inaccessible → Hworker/*, lib/claims.tsT-06, T-19CA-13, 14Tests mock : sortie avec Fait sans URL stocké hypothesis ; aucun claim sans marqueurVérification d'URL bloquante (ne doit pas l'être)
T-21Reprise, échec, annulation : 2 tentatives puis failed ; resume à la 1ʳᵉ section manquante ; cancel avec crédit restitué si 0 section ; clé révoquée → failed sans bascule plateformeworker/resume.ts, api/.../cancel, .../resumeT-20CA-11, 12, 19Tests : échec après 3 sections → reprise section 4 ; annulation à 0 section → quota_refundedRégénérer les sections 1 à 3
T-22GET projet/génération/sections + UI EC-05 (lancement), EC-06 (suivi, région aria-live), EC-07 (lecture, badges Fait/H en texte + icône)app/projects/[id]/*T-19, T-20, T-21CA-05 à 13, 26E2E : P-01 étapes 4 à 7 en Rapide avec mock ; bannière UE bloquanteCouleur seule pour Fait/H

Jalon J5 — Chat, export, RGPD (MVP-04, 06, 07)

IDObjectifModules / fichiersDépend deCAPreuve de finPiège à éviter
T-23Chat par section : GET/POST /sections/{sid}/chat, proposition pending, apply (section v+1, ancienne dans section_versions, base_version → 409 si périmée), reject, restore ; plafond de messages → 429api/sections/*, api/chat-messages/*T-20CA-15, 16Tests : section inchangée avant « Appliquer » ; deux apply concurrents → plus récente gagne, ancienne restaurableAppliquer sans transaction
T-24UI EC-08 (panneau latéral desktop, plein écran mobile)app/projects/[id]/chatT-23CA-15, 16E2E : appliquer/rejeter ; conflit → restauration proposéePerte de l'historique au rechargement
T-25GET /export ZIP : 7 sections .md, schema.sql extrait des blocs de code de la section « Architecture et données » (corrige EC-09), AGENTS.md, CLAUDE.md d'une ligne ; noms normalisés ; 409 si non generated ou section en régénérationapi/.../export, lib/zip.tsT-20CA-20, 21Test : contenu du ZIP vérifié ; aucun .. ni chemin absoluExtraire plusieurs blocs SQL dans le mauvais ordre
T-26Suppression de compte (corrige EC-05) : DELETE /me avec « SUPPRIMER » ; 409 si solo_active ; pose deletion_requested_at, invalide les sessions ; job de purge à J+30 supprimant users (cascade) et données liées hors cascade (stripe_events rattachés, journaux)api/me, jobs/purge-accounts.tsT-03, T-14CA-28, 29Tests : refus solo_active ; connexion refusée après demande ; purge à J+30 avec horloge simuléeOublier les tables hors cascade
T-27UI EC-11, EC-12 (compte, 404, session expirée avec saisie conservée) ; durcissement : Markdown assaini, rate limits, en-têtes de sécuritéapp/settings/account, middleware.tsT-26, T-23CA-22, 27, 28, 29E2E : suppression bloquée puis confirmée ; test XSS dans le Markdown IAHTML brut rendu depuis la sortie IA
T-28Parcours E2E complets P-01 à P-04 + passe accessibilité claviere2e/*T-08 à T-27CA-01 à CA-30pnpm e2e vert en CIDépendre de vrais fournisseurs IA en CI (utiliser le mock)

Couverture des critères d'acceptation

CATâche(s)CATâche(s)
CA-01T-05, T-08, T-28CA-16T-02, T-23, T-24
CA-02T-07, T-08CA-17T-10, T-13, T-17
CA-03T-06, T-07, T-08CA-18T-09, T-10, T-13
CA-04T-05CA-19T-06, T-21
CA-05T-12, T-17, T-22CA-20T-25
CA-06T-16, T-17CA-21T-25
CA-07T-11, T-12, T-22CA-22T-03, T-04, T-27
CA-08T-12, T-17CA-23T-14, T-15, T-18
CA-09T-17, T-19CA-24T-14
CA-10T-19CA-25T-14, T-15, T-18
CA-11T-21CA-26T-17, T-19, T-22
CA-12T-16, T-21CA-27T-01, T-03, T-08, T-11, T-27
CA-13T-02, T-20, T-22CA-28T-26, T-27
CA-14T-02, T-20CA-29T-26, T-27
CA-15T-23, T-24CA-30T-16, T-17, T-19

Écarts corrigés : EC-01 (T-16, T-17), EC-02 (T-16), EC-03 (T-16), EC-04 (T-15), EC-05 (T-03, T-26), EC-06 (T-02, T-11), EC-07 (T-07), EC-08 (T-02, T-11), EC-09 (T-25).

Relecture indépendante

Écarts

IDGravitéOùProblèmeCorrection
EC-01Haute§2 RM-02/CA-30 vs §4 SQL, §5 APILe quota gratuit de 3 Rapides est une règle (table de décision, CA-30) mais n'a aucune règle propre : free_quota_used n'est jamais incrémenté ni décrit côté API. Les Rapides « gratuites » contredisent aussi RM-03 (quota mensuel Solo) et la mention « 1ʳᵉ génération gratuite » ambiguë.Ajouter RM-15 : « plan free = 3 Rapides au total ; free_quota_used +1 à chaque génération (en transaction, avec verrou sur subscriptions), restitué si annulée sans section ou échec » ; remplacer la mention ambiguë de la table de décision par « quota gratuit restant > 0 ».
EC-02Haute§2 RM-03 vs §4 SQLLe quota mensuel Solo (30 Rapide / 10 Complète) n'a ni compteur ni requête : seul un index generations(project_id, created_at) existe, alors que le décompte est par utilisateur.Ajouter l'index/vue de décompte : somme de quota_units des générations de l'utilisateur (jointure projects.owner_id) où created_at >= current_period_start, quota_refunded = false, status <> 'failed'. Plafond : 30 unités (RM-03 : 10 Complètes × 3).
EC-03Moyenne§2 RM-03 vs §4 quota_unitsquota_units IN (0,1,3) : 0 n'est pas défini (BYOK ?). Or RM-03 dit « quota épuisé : proposer BYOK », donc BYOK ne décompte pas, mais la règle n'est écrite nulle part.Ajouter à RM-03 : « une génération BYOK a quota_units = 0 ».
EC-04Moyenne§2 RM-12 vs §4 subscriptions / §5solo_past_due « traité comme free après grâce » : aucun job ni règle ne bascule status vers free à grace_ends_at (idem solo_canceled à current_period_end).Ajouter un job planifié (ou évaluation à la lecture) : si grace_ends_at ou current_period_end dépassé → status = 'free'. Le noter en §4 composants.
EC-05Moyenne§2 RM-14 / CA-29 vs §4La purge sous 30 jours est citée (job planifié) mais deletion_requested_at n'est pas lié à une règle de blocage de connexion ni à une résiliation Stripe ; ON DELETE CASCADE ne couvre pas stripe_events ni les journaux.Préciser : après DELETE /me, session invalidée et connexion refusée ; le job de purge supprime users (cascade) à J+30.
EC-06Moyenne§3 EC-05 / §5Aucun endpoint pour afficher le catalogue de modèles (ai_models) ni pour le modèle par défaut (EC-09 « modèle par défaut »), et pas de colonne default_model_id.Ajouter GET /models et users.default_model_id (ou retirer « modèle par défaut » d'EC-09).
EC-07Moyenne§5 POST /questions vs RM-01Le minimum de 3 questions et l'exactement 2 options ne sont vérifiés qu'« par l'application » : pas de test de passage en questioning dans l'API ni de rollback.Préciser : validation en transaction avant insertion ; si invalide → relance, puis failed.
EC-08Faible§1 D-09 vs EC-11La langue du dossier « par défaut » (EC-11) n'existe pas en base (users n'a que ui_locale).Ajouter users.doc_locale_default ou supprimer ce réglage d'EC-11.
EC-09Faible§5 export vs RM-10GET /export doit exporter « le SQL » : ce SQL est la section 4 du dossier, pas un fichier distinct stocké.Préciser que le SQL est extrait des blocs de code de la section « Architecture et données ».

Hypothèses à vérifier en premier

  1. Estimation fiable du coût/durée avant lancement (D-02) — risque : fourchette fausse, lancement bloqué (RM-04) ou coût sous-estimé ; vérifier sur quelques idées types le ratio estimé/réel.
  2. Marge économique de Solo à 15 €/mois (30 Rapides ou 10 Complètes, modèles plateforme) — risque : coût IA supérieur au prix ; chiffrer le coût moyen d'une Complète avec vérification web avant de fixer les quotas.
  3. TVA/Stripe Tax et prix « TTC » pour particuliers (D-06) et sortie de données hors UE vers les fournisseurs IA (D-04/RM-13) — risque juridique RGPD/TVA ; faire valider par un juriste (DPA des fournisseurs, base légale, mention TTC vs HT).

Sources web consultées

20 liens vers des sources