A full spec from one sentence
Product, flows, business rules, data, API and development plan. Facts are sourced, assumptions flagged.
For Cursor, Claude Code, Lovable, Bolt and the rest
Kodrudder writes your project's full spec, hands it to your coding tool, then checks that the code sticks to it. No more AI guesswork.
No credit card. Your first spec in minutes.
Trademarks are named for information only. Kodrudder is not affiliated with any of these vendors.
From idea to verified code: one spec that follows your project from the first prompt to production.
Product, flows, business rules, data, API and development plan. Facts are sourced, assumptions flagged.
Kodrudder compares the spec with the code and shows what is done, partial or forgotten, file by file.
Paste a GitHub link or drop an archive: Kodrudder rebuilds the spec of what already exists.
AGENTS.md, Cursor rules, Claude Code, Windsurf, Lovable and Bolt prompts, MCP server: your AI reads the spec without copy-paste.
PRD, user stories, SQL schema, API contract, screen mockups, task list.
Read-only links, reviewers, comments on each section: everyone works on the same spec.
Send the next task to Lovable, Bolt, v0 or Replit, then tick it off when it's done.
One sentence, a few questions, and the spec writes itself in front of you. Then it goes to your tool in one click.
Describe your project in plain words. Kodrudder asks the right questions, writes everything your AI needs, then keeps the spec up to date.
One sentence is enough. Or a link to your GitHub repository.
Only the ones that change the result. For the rest, Kodrudder decides and says so.
Rules, acceptance criteria, data, API and tasks, reviewed before they reach you.

Send the spec to your tool. Compare it with the code whenever you want to know where you stand.
Your idea becomes a clear plan the AI follows, instead of ten rounds fixing what it guessed.
Rules, acceptance criteria and tasks your tool takes one by one, without losing the thread between sessions.
One source of truth, reviewed, shared and checked against the code at every step.
This is the spec produced for “a booking app for fitness coaches”. Browse it the way your AI will read it.
Booking for fitness coaches
Sourced factAssumption (H)
Read this first
Produit. Application web de réservation pour coachs sportifs indépendants : le coach publie disponibilités et tarifs, le client réserve et paie en ligne sans compte, puis reçoit un SMS de rappel J-1. Objectif : moins de no-shows et moins de relances manuelles.
| Écart | Ce qui prévaut sur le texte des sections |
|---|---|
| EC-01 | Webhook tardif sur EXPIREE : PAYE puis REMBOURSE dans une seule transaction (RM-04) |
| EC-02 | rappel_sms créé à la confirmation si début > 24 h ; passé à annule à toute annulation |
| EC-03 | D-05 : 1 rappel + 1 SMS d'annulation coach éventuel, contenu transactionnel |
| EC-04 | Jeton de gestion = 256 bits partout (RM-13 aligné) |
| EC-05 | Blocage de date refusé si réservation EN_ATTENTE_PAIEMENT ou CONFIRMEE ce jour |
| EC-06 | Mobile 06/07 uniquement (RM-11 aligné) ; validation API avant insertion, 400 et non 500 |
| EC-07 | Ajouter GET /api/coach/seances, /disponibilites, /dates-bloquees |
| EC-08 | E-03 lit le statut via API-15 par jeton |
Business rules and acceptance criteria
4 marked assumptions
| Statut de réservation | Sens |
|---|---|
EN_ATTENTE_PAIEMENT | Créneau verrouillé temporairement, paiement non confirmé |
CONFIRMEE | Paiement reçu, séance planifiée |
ANNULEE_CLIENT | Annulée par le client |
ANNULEE_COACH | Annulée par le coach |
EXPIREE | Paiement non finalisé avant expiration du verrou |
TERMINEE | Heure de fin de séance dépassée |
| Statut de paiement | Sens |
|---|---|
AUCUN | Aucun paiement initié |
PAYE | Capturé |
REMBOURSE | Remboursement intégral émis |
ECHEC_REMBOURSEMENT | Remboursement refusé ou en erreur, à traiter manuellement |
| ID | Besoin | MVP |
|---|---|---|
| BE-01 | Le coach crée un compte et se connecte (email + mot de passe) | MVP-01 |
| BE-02 | Le coach active ses paiements via l'onboarding Stripe Connect | MVP-01 |
| BE-03 | Le coach définit ses séances : durée, prix, lieu | MVP-02 |
| BE-04 | Le coach définit des disponibilités hebdomadaires et bloque des dates | MVP-03 |
| BE-05 | Le client consulte la page publique d'un coach et ses créneaux libres | MVP-04 |
| BE-06 | Le client réserve sans compte (nom, email, mobile) | MVP-04 |
| BE-07 | Un créneau ne peut être réservé que par un seul client | MVP-05 |
| BE-08 | Le client paie en ligne en une fois | MVP-05 |
| BE-09 | Le client reçoit un email de confirmation avec lien de gestion | MVP-06 |
| BE-10 | Le client reçoit un SMS de rappel 24 h avant la séance | MVP-06 |
| BE-11 | Le client annule via son lien, selon le délai | MVP-07 |
| BE-12 | Le coach annule une séance et le client est remboursé automatiquement | MVP-07 |
| BE-13 | Le coach consulte réservations et paiements | MVP-08 |
Valeurs par défaut en H. Toutes les heures sont en Europe/Paris (D-09).
| ID | Règle | Défaut H | Cas limites |
|---|---|---|---|
| RM-01 | Un créneau = (coach, début, durée). Il est réservable s'il est dans une disponibilité, hors date bloquée et sans réservation EN_ATTENTE_PAIEMENT ou CONFIRMEE chevauchante | Pas de chevauchement, pas de pause tampon | Deux séances de durées différentes qui se chevauchent : la seconde est refusée. Créneau passé : non réservable |
| RM-02 | Délai minimal de réservation avant le début | 2 h | Créneau à moins de 2 h : non proposé |
| RM-03 | Horizon maximal de réservation | 60 jours | Au-delà : non proposé |
| RM-04 | Verrou de paiement : la création passe la réservation en EN_ATTENTE_PAIEMENT et verrouille le créneau | 15 min. Expiration → EXPIREE, créneau libéré | Paiement confirmé après expiration : remboursement automatique et information du client. Abandon sur Stripe : EXPIREE à l'échéance |
| RM-05 | Concurrence : le verrou est atomique (contrainte d'unicité en base), la première écriture gagne | n/a | Deux requêtes simultanées : une seule réussit, l'autre reçoit « créneau indisponible » |
| RM-06 | Paiement : montant = prix de la séance, EUR, intégral. Passage à CONFIRMEE uniquement sur webhook Stripe signé et idempotent | Aucune commission (D-10) | Webhook en doublon : sans effet. Échec de carte : réservation reste EN_ATTENTE_PAIEMENT jusqu'à expiration. Montant reçu ≠ attendu : réservation non confirmée |
| RM-07 | Annulation client (D-07) via lien de gestion | Plus de 24 h avant le début : ANNULEE_CLIENT + remboursement intégral. À 24 h pile ou moins : annulation refusée | Séance déjà TERMINEE ou annulée : refus. Lien utilisé deux fois : idempotent |
| RM-08 | Annulation coach : toujours possible avant le début, remboursement intégral automatique | Notification email + SMS au client | Annulation avec remboursement en échec : ANNULEE_COACH, paiement ECHEC_REMBOURSEMENT, alerte au coach |
| RM-09 | Frais de remboursement : le client reçoit le montant intégral payé. Le surcoût éventuel des frais Stripe sur remboursement est à vérifier | Absorbé par le coach H | Coach sans solde Connect suffisant : remboursement à traiter manuellement |
| RM-10 | Rappel SMS : 1 SMS 24 h avant le début, uniquement pour une réservation CONFIRMEE (D-05, D-06). Contenu transactionnel sans offre promotionnelle | Envoi à début − 24 h | Réservation faite à moins de 24 h du début : pas de rappel, la confirmation par email suffit. Annulée avant l'envoi : aucun SMS. Mobile invalide ou échec Twilio : 1 nouvel essai à +10 min, puis echec journalisé, sans blocage de la réservation. Jamais d'envoi en doublon pour une même réservation |
| RM-11 | Validation du mobile : format français (+33 / 0X) normalisé E.164 ; email valide | Mobile et email obligatoires | Format invalide : réservation refusée avec message explicite |
| RM-12 | Modification de disponibilités : sans effet sur les réservations CONFIRMEE existantes. Un blocage de date sur une réservation existante est refusé tant que le coach ne l'annule pas (RM-08) | n/a | Suppression d'une séance du catalogue : les réservations existantes sont conservées |
| RM-13 | Droits : un coach ne voit et ne modifie que ses données. Le lien de gestion client contient un jeton aléatoire non devinable, limité à une réservation | Jeton ≥ 128 bits | Jeton invalide ou inconnu : 404 sans fuite d'information |
| RM-14 | Paiement activé : une séance n'est réservable que si l'onboarding Connect du coach est complet | n/a | Compte Connect restreint ou désactivé après coup : nouvelles réservations bloquées, existantes conservées |
| RM-15 | Passage à TERMINEE : automatique après l'heure de fin | n/a | Aucun remboursement possible après TERMINEE |
| Initiateur | Statut courant | Délai avant le début | Résultat | Remboursement |
|---|---|---|---|---|
| Client | CONFIRMEE | > 24 h | ANNULEE_CLIENT | Intégral |
| Client | CONFIRMEE | ≤ 24 h | Refusé | Aucun |
| Client | EN_ATTENTE_PAIEMENT | tout | EXPIREE (libération du créneau) | Aucun paiement à rembourser |
| Client | TERMINEE, ANNULEE_*, EXPIREE | tout | Refusé | Aucun |
| Coach | CONFIRMEE | tout (avant début) | ANNULEE_COACH | Intégral automatique |
| Coach | EN_ATTENTE_PAIEMENT | tout | EXPIREE | Aucun paiement à rembourser |
| Coach | TERMINEE, ANNULEE_*, EXPIREE | tout | Refusé | Aucun |
| ID | Étant donné | Quand | Alors | BE | RM |
|---|---|---|---|---|---|
| CA-01 | Un coach a une disponibilité le lundi 10:00–12:00 et une séance de 60 min | Un client ouvre sa page publique | Les créneaux 10:00 et 11:00 sont proposés | BE-05 | RM-01 |
| CA-02 | Une date est bloquée par le coach | Un client consulte cette date | Aucun créneau n'est proposé | BE-04 | RM-01 |
| CA-03 | Un créneau dans 1 h | Un client consulte la page | Le créneau n'est pas proposé | BE-05 | RM-02 |
| CA-04 | Un créneau dans 61 jours | Un client consulte la page | Le créneau n'est pas proposé | BE-05 | RM-03 |
| CA-05 | Un créneau libre | Un client soumet nom, email et mobile valides | Réservation EN_ATTENTE_PAIEMENT, redirection vers Stripe Checkout, créneau verrouillé | BE-06, BE-08 | RM-04 |
| CA-06 | Un créneau libre | Deux requêtes de réservation arrivent simultanément | Une seule réussit, l'autre reçoit « créneau indisponible » | BE-07 | RM-05 |
| CA-07 | Une réservation EN_ATTENTE_PAIEMENT depuis 16 min | Le job d'expiration s'exécute | Statut EXPIREE, créneau à nouveau réservable | BE-07 | RM-04 |
| CA-08 | Une réservation EN_ATTENTE_PAIEMENT | Un webhook Stripe signé de paiement réussi arrive | Statut CONFIRMEE, paiement PAYE, email de confirmation envoyé | BE-08, BE-09 | RM-06 |
| CA-09 | Le même webhook déjà traité | Il est reçu à nouveau | Aucun changement d'état, aucun second email | BE-08 | RM-06 |
| CA-10 | Un webhook avec signature invalide | Il est reçu | Rejeté (400), aucun changement d'état | BE-08 | RM-06 |
| CA-11 | Une réservation EXPIREE | Un webhook de paiement réussi arrive | Paiement REMBOURSE automatiquement, réservation reste EXPIREE | BE-08 | RM-04 |
| CA-12 | Un paiement dont le montant ≠ prix attendu | Le webhook arrive | La réservation n'est pas confirmée et l'anomalie est journalisée | BE-08 | RM-06 |
| CA-13 | Une réservation CONFIRMEE à plus de 24 h | Le client annule via son lien | ANNULEE_CLIENT, paiement REMBOURSE intégral, créneau libéré | BE-11 | RM-07 |
| CA-14 | Une réservation CONFIRMEE à 24 h ou moins | Le client tente d'annuler | Refus avec message explicite, statut inchangé | BE-11 | RM-07 |
| CA-15 | Une réservation ANNULEE_CLIENT | Le client rouvre son lien d'annulation | Aucun second remboursement, message « déjà annulée » | BE-11 | RM-07 |
| CA-16 | Une réservation CONFIRMEE à 2 h | Le coach l'annule | ANNULEE_COACH, remboursement intégral, email et SMS au client | BE-12 | RM-08 |
| CA-17 | Le remboursement Stripe échoue | Le coach annule | ANNULEE_COACH, paiement ECHEC_REMBOURSEMENT, alerte visible au coach | BE-12 | RM-08 |
| CA-18 | Une réservation CONFIRMEE début à T | L'horloge atteint T − 24 h | Un SMS de rappel est envoyé, une seule fois | BE-10 | RM-10 |
| CA-19 | Une réservation créée à moins de 24 h du début | Elle passe en CONFIRMEE | Aucun SMS de rappel, email de confirmation envoyé | BE-10 | RM-10 |
| CA-20 | Une réservation annulée avant T − 24 h | L'horloge atteint T − 24 h | Aucun SMS | BE-10 | RM-10 |
| CA-21 | Twilio renvoie une erreur | Le rappel est envoyé | Un nouvel essai à +10 min, puis échec journalisé, réservation inchangée | BE-10 | RM-10 |
| CA-22 | Un SMS de rappel généré | Son contenu est inspecté | Il ne contient aucune offre promotionnelle (modèle fixe testé) | BE-10 | RM-10 |
| CA-23 | Un mobile « 12345 » | Le client soumet le formulaire | Refus avec message de format, aucune réservation créée | BE-06 | RM-11 |
| CA-24 | Un mobile « 06 12 34 56 78 » | Le client soumet le formulaire | Enregistré en +33612345678 | BE-06 | RM-11 |
| CA-25 | Une réservation CONFIRMEE sur une date | Le coach bloque cette date | Refus tant que la réservation n'est pas annulée | BE-04 | RM-12 |
| CA-26 | Deux coachs A et B | A demande une réservation de B | 403 ou 404, aucune donnée de B exposée | BE-13 | RM-13 |
| CA-27 | Un jeton de gestion inconnu | Le lien est ouvert | 404 sans information sur l'existence d'une réservation | BE-11 | RM-13 |
| CA-28 | Un coach sans onboarding Connect complet | Un client consulte sa page | Aucune séance réservable | BE-02 | RM-14 |
| CA-29 | Une réservation CONFIRMEE dont l'heure de fin est dépassée | Le job de clôture s'exécute | Statut TERMINEE, annulation refusée | BE-13 | RM-15 |
| CA-30 | Un coach connecté avec 3 réservations | Il ouvre son tableau de bord | Les 3 réservations s'affichent avec statut de réservation et de paiement | BE-13 | RM-13 |
| CA-31 | Un email déjà utilisé par un coach | Un second compte est créé avec cet email | Refus avec message d'erreur | BE-01 | — |
| CA-32 | Un coach crée une séance de 60 min à 50 € | Il l'enregistre | Elle apparaît sur sa page publique avec durée, prix et lieu | BE-03 | RM-14 |
| ID | Question | Hypothèse H |
|---|---|---|
| Q-01 | Qui supporte les frais Stripe non restitués lors d'un remboursement ? | Le coach (RM-09), à vérifier dans la tarification Stripe Connect |
| Q-02 | Faut-il un délai de grâce après l'heure de début pour une séance non honorée par le coach ? | Non, hors MVP |
Architecture and data
4 marked assumptions
| Couche | Choix | Raison | Alternative écartée |
|---|---|---|---|
| Front + back | Next.js (TypeScript), un seul dépôt | Pages publiques rapides, API et front dans un déploiement unique H | SPA + API séparée : surcoût pour un MVP |
| Base | PostgreSQL | Contraintes d'exclusion (RM-05), transactions, jobs sur SQL | SQLite/NoSQL : pas d'exclusion de chevauchement fiable |
| Paiement | Stripe Checkout + Connect Express (D-02, D-03) | Pas de données carte à gérer | Paiement sur place : no-shows |
| SMS | Twilio (D-04) | Fournisseur retenu en section 1 | Brevo SMS : non évalué |
| Resend H | API simple pour confirmations | SMTP maison | |
| Jobs | Table job_planifie + worker cron (1 min) H | Pas de broker à exploiter, planification en SQL | Redis/BullMQ : composant en plus |
| Hébergement | PaaS EU + Postgres managé, sauvegarde quotidienne H | Données en UE, exploitation légère | Kubernetes : disproportionné |
evenement_stripe), confirme ou rembourse (RM-06).CREATE EXTENSION IF NOT EXISTS btree_gist; -- requis pour l'exclusion (RM-05)
CREATE EXTENSION IF NOT EXISTS pgcrypto;
CREATE TABLE coach (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
email text NOT NULL,
mot_de_passe_hash text NOT NULL,
nom_affiche text NOT NULL,
slug text NOT NULL, -- lien public partageable (MVP-04)
stripe_account_id text,
connect_complet boolean NOT NULL DEFAULT false, -- RM-14
cree_le timestamptz NOT NULL DEFAULT now(),
CONSTRAINT coach_slug_format CHECK (slug ~ '^[a-z0-9-]{3,50}$')
);
-- CA-31 : un email = un compte (insensible à la casse)
CREATE UNIQUE INDEX coach_email_uniq ON coach (lower(email));
CREATE UNIQUE INDEX coach_slug_uniq ON coach (slug);
CREATE TABLE seance_type (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
coach_id uuid NOT NULL REFERENCES coach(id) ON DELETE CASCADE,
titre text NOT NULL,
duree_minutes integer NOT NULL CHECK (duree_minutes BETWEEN 15 AND 240),
prix_cents integer NOT NULL CHECK (prix_cents > 0), -- RM-06 : EUR, intégral
lieu text NOT NULL,
actif boolean NOT NULL DEFAULT true, -- RM-12 : désactivation sans suppression
cree_le timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX seance_type_coach_idx ON seance_type (coach_id) WHERE actif;
-- Disponibilités hebdomadaires récurrentes (heure locale Europe/Paris, D-09)
CREATE TABLE disponibilite (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
coach_id uuid NOT NULL REFERENCES coach(id) ON DELETE CASCADE,
jour_semaine smallint NOT NULL CHECK (jour_semaine BETWEEN 1 AND 7), -- 1 = lundi
heure_debut time NOT NULL,
heure_fin time NOT NULL,
CONSTRAINT disponibilite_plage CHECK (heure_fin > heure_debut)
);
CREATE INDEX disponibilite_coach_idx ON disponibilite (coach_id, jour_semaine);
CREATE TABLE date_bloquee (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
coach_id uuid NOT NULL REFERENCES coach(id) ON DELETE CASCADE,
jour date NOT NULL,
UNIQUE (coach_id, jour)
);
-- RM-12 (refus du blocage si réservation CONFIRMEE ce jour) : contrôle applicatif, CA-25.
CREATE TABLE reservation (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
coach_id uuid NOT NULL REFERENCES coach(id),
seance_type_id uuid NOT NULL REFERENCES seance_type(id),
client_nom text NOT NULL,
client_email text NOT NULL,
client_mobile_e164 text NOT NULL,
debut timestamptz NOT NULL,
fin timestamptz NOT NULL,
prix_cents integer NOT NULL CHECK (prix_cents > 0), -- copie figée du prix
statut text NOT NULL DEFAULT 'EN_ATTENTE_PAIEMENT',
statut_paiement text NOT NULL DEFAULT 'AUCUN',
verrou_expire_le timestamptz, -- RM-04 : créée + 15 min
jeton_gestion text NOT NULL DEFAULT encode(gen_random_bytes(32), 'hex'), -- RM-13 : 256 bits
stripe_session_id text,
stripe_payment_intent_id text,
cree_le timestamptz NOT NULL DEFAULT now(),
mis_a_jour_le timestamptz NOT NULL DEFAULT now(),
annulee_le timestamptz,
CONSTRAINT reservation_statut_valide CHECK (statut IN
('EN_ATTENTE_PAIEMENT','CONFIRMEE','ANNULEE_CLIENT','ANNULEE_COACH','EXPIREE','TERMINEE')),
CONSTRAINT reservation_paiement_valide CHECK (statut_paiement IN
('AUCUN','PAYE','REMBOURSE','ECHEC_REMBOURSEMENT')),
CONSTRAINT reservation_plage CHECK (fin > debut),
-- RM-11 : mobile français normalisé E.164
CONSTRAINT reservation_mobile_e164 CHECK (client_mobile_e164 ~ '^\+33[67][0-9]{8}$'),
CONSTRAINT reservation_email_format CHECK (client_email ~ '^[^@\s]+@[^@\s]+\.[^@\s]+$'),
-- RM-04 : un verrou est obligatoire tant que le paiement est en attente
CONSTRAINT reservation_verrou_requis CHECK (statut <> 'EN_ATTENTE_PAIEMENT' OR verrou_expire_le IS NOT NULL),
-- RM-06 : CONFIRMEE implique un paiement capturé
CONSTRAINT reservation_confirmee_payee CHECK (statut <> 'CONFIRMEE' OR statut_paiement = 'PAYE'),
-- RM-01 + RM-05 : pas de chevauchement pour un même coach tant que la réservation
-- est EN_ATTENTE_PAIEMENT ou CONFIRMEE ; la première écriture gagne (CA-06).
CONSTRAINT reservation_pas_de_chevauchement EXCLUDE USING gist (
coach_id WITH =,
tstzrange(debut, fin, '[)') WITH &&
) WHERE (statut IN ('EN_ATTENTE_PAIEMENT','CONFIRMEE'))
);
CREATE UNIQUE INDEX reservation_jeton_uniq ON reservation (jeton_gestion); -- RM-13
CREATE UNIQUE INDEX reservation_session_uniq ON reservation (stripe_session_id)
WHERE stripe_session_id IS NOT NULL;
CREATE INDEX reservation_coach_debut_idx ON reservation (coach_id, debut DESC); -- tableau de bord (BE-13)
CREATE INDEX reservation_expiration_idx ON reservation (verrou_expire_le)
WHERE statut = 'EN_ATTENTE_PAIEMENT'; -- job RM-04
CREATE INDEX reservation_cloture_idx ON reservation (fin)
WHERE statut = 'CONFIRMEE'; -- job RM-15
-- Idempotence des webhooks Stripe (RM-06, CA-09)
CREATE TABLE evenement_stripe (
id_evenement text PRIMARY KEY, -- evt_... : doublon = INSERT en conflit, sans effet
type text NOT NULL,
recu_le timestamptz NOT NULL DEFAULT now(),
traite_le timestamptz
);
-- Rappels SMS : au plus un par réservation (RM-10, CA-18)
CREATE TABLE rappel_sms (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
reservation_id uuid NOT NULL UNIQUE REFERENCES reservation(id) ON DELETE CASCADE, -- jamais de doublon
envoi_prevu_le timestamptz NOT NULL, -- début - 24 h
statut text NOT NULL DEFAULT 'a_envoyer'
CHECK (statut IN ('a_envoyer','envoye','echec','annule')),
tentatives smallint NOT NULL DEFAULT 0 CHECK (tentatives BETWEEN 0 AND 2), -- 1 essai + 1 retry à +10 min
twilio_sid text,
derniere_erreur text,
envoye_le timestamptz
);
CREATE INDEX rappel_sms_a_envoyer_idx ON rappel_sms (envoi_prevu_le) WHERE statut = 'a_envoyer';
-- Journal des anomalies (RM-06 montant ≠ attendu, ECHEC_REMBOURSEMENT, échec SMS)
CREATE TABLE journal_anomalie (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
reservation_id uuid REFERENCES reservation(id) ON DELETE SET NULL,
type text NOT NULL,
detail jsonb NOT NULL DEFAULT '{}',
cree_le timestamptz NOT NULL DEFAULT now()
);
| Règle | Mécanisme |
|---|---|
| RM-01, RM-05 | EXCLUDE USING gist partiel sur EN_ATTENTE_PAIEMENT et CONFIRMEE |
| RM-04 | verrou_expire_le obligatoire, index dédié pour le job d'expiration |
| RM-06 | evenement_stripe (idempotence) et CONFIRMEE impossible sans PAYE |
| RM-10 | UNIQUE (reservation_id) sur rappel_sms |
| RM-11 | CHECK E.164 et email |
| RM-13 | Jeton 256 bits, index unique |
RM-02, RM-03 (délai min. 2 h, horizon 60 j), RM-07 et RM-08 (table de décision d'annulation), RM-12 (blocage de date) et RM-14 (connect_complet). Le job d'expiration libère le créneau en passant le statut à EXPIREE, ce qui sort la ligne de la contrainte d'exclusion.
API, integrations and security
5 marked assumptions
Conventions : JSON sur HTTPS ; erreurs { "code", "message" } ; heures ISO 8601 en UTC, affichées en Europe/Paris (D-09). Session coach par cookie HttpOnly, Secure, SameSite=Lax H. Accès client par jeton de gestion (RM-13), sans compte.
| ID | Méthode et chemin | Rôle autorisé | Entrée | Sortie | Erreurs | RM / CA |
|---|---|---|---|---|---|---|
| API-01 | POST /api/coach/inscription | Public | email, mot de passe, nom_affiche | 201 {id, slug} | 400 format ; 409 email déjà utilisé | BE-01 ; CA-31 |
| API-02 | POST /api/coach/connexion | Public | email, mot de passe | 200 + cookie de session | 401 identifiants erronés ; 429 | BE-01 |
| API-03 | POST /api/coach/connect/onboarding | Coach | — | 200 {url} (lien Stripe Connect) | 401 ; 502 Stripe | BE-02 ; RM-14, CA-28 |
| API-04 | GET /api/coach/connect/statut | Coach | — | 200 {connect_complet} | 401 | RM-14 ; CA-28 |
| API-05 | POST /api/coach/seances | Coach | titre, duree_minutes, prix_cents, lieu | 201 séance | 400 validation ; 401 | BE-03 ; RM-06, CA-32 |
| API-06 | PATCH /api/coach/seances/{id} | Coach propriétaire | champs modifiés ou actif=false | 200 séance | 400 ; 401 ; 404 | RM-12, RM-13 |
| API-07 | PUT /api/coach/disponibilites | Coach | liste {jour_semaine, heure_debut, heure_fin} | 200 liste | 400 plage invalide ; 401 | BE-04 ; RM-01, CA-01 |
| API-08 | POST /api/coach/dates-bloquees | Coach | jour | 201 | 401 ; 409 réservation CONFIRMEE ce jour | RM-12 ; CA-25, CA-02 |
| API-09 | DELETE /api/coach/dates-bloquees/{id} | Coach propriétaire | — | 204 | 401 ; 404 | RM-01 |
| API-10 | GET /api/coach/reservations | Coach | filtres statut, du, au | 200 liste (statut, statut_paiement, alerte) | 401 | BE-13 ; RM-13, CA-30, CA-26 |
| API-11 | POST /api/coach/reservations/{id}/annuler | Coach propriétaire | — | 200 {statut, statut_paiement} | 401 ; 404 ; 409 statut non annulable ou séance terminée | BE-12 ; RM-08, RM-15, CA-16, CA-17 |
| API-12 | GET /api/public/coachs/{slug} | Public | — | 200 coach + séances actives (vide si connect_complet faux) | 404 | BE-05 ; RM-14, CA-28, CA-32 |
| API-13 | GET /api/public/coachs/{slug}/creneaux | Public | seance_type_id, du, au | 200 liste de débuts libres | 400 hors horizon ; 404 | BE-05 ; RM-01 à RM-03, CA-01 à CA-04 |
| API-14 | POST /api/public/reservations | Public | seance_type_id, debut, nom, email, mobile | 201 {reservation_id, jeton_gestion, checkout_url} | 400 mobile/email invalide ; 409 créneau indisponible ; 422 coach sans Connect, créneau hors règles | BE-06, BE-07, BE-08 ; RM-04, RM-05, RM-11, RM-14, CA-05, CA-06, CA-23, CA-24 |
| API-15 | GET /api/public/reservations/{jeton} | Détenteur du jeton | — | 200 détail, annulable (booléen), règle d'annulation | 404 jeton inconnu, sans fuite | BE-11 ; RM-13, CA-27, CA-15 |
| API-16 | POST /api/public/reservations/{jeton}/annuler | Détenteur du jeton | — | 200 {statut, statut_paiement} | 404 ; 409 refus (≤ 24 h, terminée, déjà annulée) | BE-11 ; RM-07, RM-15, CA-13, CA-14, CA-15 |
| API-17 | POST /api/webhooks/stripe | Stripe (signature) | événement signé | 200 ; doublon : 200 sans effet | 400 signature invalide | BE-08, BE-09 ; RM-04, RM-06, CA-08 à CA-12 |
| JOB-01 | Worker, chaque minute | Interne | — | Réservations EN_ATTENTE_PAIEMENT échues → EXPIREE | — | RM-04 ; CA-07 |
| JOB-02 | Worker, chaque minute | Interne | — | Envoi des rappel_sms échus (réservation CONFIRMEE), retry à +10 min | — | BE-10 ; RM-10, CA-18 à CA-22 |
| JOB-03 | Worker, chaque minute | Interne | — | CONFIRMEE passée de fin → TERMINEE | — | RM-15 ; CA-29 |
Comportement structurant :
INSERT dans reservation (la contrainte reservation_pas_de_chevauchement tranche la concurrence, violation → 409), puis création de la session Checkout avec stripe_session_id. Si Stripe échoue, la ligne passe en EXPIREE.INSERT dans evenement_stripe ; conflit sur id_evenement → sans effet. Montant ≠ prix_cents → pas de confirmation, ligne dans journal_anomalie. Réservation déjà EXPIREE → remboursement automatique, statut_paiement = REMBOURSE.ECHEC_REMBOURSEMENT et anomalie journalisée.account.updated (reçu par API-17) met à jour connect_complet.| Service | Usage | Point de défaillance | Comportement de repli |
|---|---|---|---|
| Stripe Checkout | Paiement de la réservation | Indisponibilité à la création de session | Réservation EXPIREE, 502 au client, créneau libéré |
| Stripe webhooks | Confirmation, account.updated | Webhook perdu ou en retard | Stripe rejoue ; si le verrou expire avant, paiement tardif remboursé (CA-11) |
| Stripe Connect / remboursements | Onboarding, remboursements | Refus ou solde insuffisant (RM-09) | ECHEC_REMBOURSEMENT, alerte au coach, traitement manuel |
| Twilio | SMS de rappel et d'annulation coach | Erreur API, mobile invalide | 1 retry à +10 min, puis echec journalisé ; réservation inchangée (CA-21) |
| Resend H | Email de confirmation et lien de gestion | Erreur API | Retry par le worker ; lien de gestion aussi affiché sur E-03 |
| PostgreSQL | Données, verrou, jobs | Indisponibilité | 503 ; aucun état partiel grâce aux transactions |
mot_de_passe_hash), limitation des tentatives sur API-02, session expirante H.coach_id de la session ; accès hors périmètre → 404 (CA-26). Jeton de gestion à 256 bits, 404 uniforme si inconnu (CA-27).Development plan
1 marked assumption
Hypothèses de plan H : un seul dépôt Next.js + PostgreSQL ; Stripe, Twilio et Resend sont isolés derrière des interfaces simulables (mocks en test) ; tests : Vitest (unitaire et intégration sur Postgres de test) et Playwright (bout en bout). La tranche verticale prioritaire est P-01 (réserver et payer) : elle est livrée dès le jalon J3. Chaque tâche suppose les corrections EC-xx intégrées (marquées « corrige EC-xx »).
| Écart | Décision appliquée dans le plan |
|---|---|
| EC-01 | Webhook tardif sur EXPIREE : PAYE puis REMBOURSE dans une même transaction (T-12) |
| EC-02 | Insertion de rappel_sms à la confirmation (T-11) ; passage à annule aux annulations (T-15, T-17) |
| EC-03 | SMS d'annulation coach conservé ; D-05 devient « 1 rappel + 1 SMS d'annulation coach éventuel », contenu transactionnel (T-17) |
| EC-04 | Jeton = 256 bits partout ; RM-13 aligné (T-02) |
| EC-05 | Blocage de date refusé si réservation EN_ATTENTE_PAIEMENT ou CONFIRMEE ce jour ; en plus, la confirmation Stripe d'une réservation sur date bloquée n'est pas empêchée car le créneau est verrouillé avant (T-08, T-12) |
| EC-06 | Mobile = 06/07 uniquement ; RM-11 aligné ; validation API avant insertion, 400 et non 500 (T-09) |
| EC-07 | Ajout des GET coach séances, disponibilités, dates bloquées (T-06, T-07, T-08) |
| EC-08 | E-03 utilise API-15 par jeton (T-13) |
| ID | Objectif | Modules ou fichiers | Dépend de | CA | Preuve de fin | Piège à éviter |
|---|---|---|---|---|---|---|
| T-01 | Initialiser Next.js (TS), lint, Vitest, Playwright, variables d'environnement typées, CI | package.json, src/lib/env.ts, .github/workflows/ci.yml | — | — | npm run lint && npm test verts en CI | Ne jamais committer de clé ; échouer au démarrage si une variable manque |
| T-02 | Migrations : schéma §4 complet, avec jeton 256 bits (corrige EC-04) et CHECK mobile ^\+33[67][0-9]{8}$ | db/migrations/001_init.sql | T-01 | CA-06, CA-31 | Test d'intégration : insertion chevauchante rejetée par EXCLUDE ; email en double rejeté | Activer btree_gist avant la contrainte ; tester la contrainte sur base réelle, pas en mémoire |
| T-03 | Adaptateurs simulables Stripe, Twilio, Resend, Horloge (interfaces et mocks) | src/adapters/*.ts | T-01 | — | Tests unitaires avec mocks ; horloge injectable | Sans horloge injectable, les tests 24 h et 15 min sont impossibles |
| ID | Objectif | Modules ou fichiers | Dépend de | CA | Preuve de fin | Piège à éviter |
|---|---|---|---|---|---|---|
| T-04 | API-01/02 : inscription, connexion, session cookie HttpOnly, hash argon2id, limitation des tentatives | src/app/api/coach/inscription, connexion, src/lib/auth.ts | T-02 | CA-31 | Test : second compte avec le même email (casse différente) → 409 | Comparer l'email en lower() ; ne pas révéler si l'email existe à la connexion |
| T-05 | API-03/04 : onboarding Connect Express, statut ; account.updated met à jour connect_complet (traité dans T-12) | src/app/api/coach/connect/* | T-04, T-03 | CA-28 | Test mock Stripe : lien renvoyé ; statut reflète connect_complet | Ne jamais déduire « complet » du retour navigateur, seulement du webhook |
| T-06 | Écrans E-05 et E-06 (inscription/connexion, onboarding) | src/app/(coach)/connexion, onboarding | T-04, T-05 | CA-31 | Playwright : création de compte puis affichage du statut Connect | Messages d'erreur liés aux champs (AM-03) |
| ID | Objectif | Modules ou fichiers | Dépend de | CA | Preuve de fin | Piège à éviter |
|---|---|---|---|---|---|---|
| T-07 | API-05/06 + GET /api/coach/seances (corrige EC-07) : création, modification, désactivation (actif=false) | src/app/api/coach/seances | T-04 | CA-32 | Test : séance 60 min à 5000 cents créée, relue par GET ; accès à la séance d'un autre coach → 404 | Filtrer toujours par coach_id de session ; prix en centimes entiers |
| T-08 | API-07/08/09 + GET /api/coach/disponibilites et GET /api/coach/dates-bloquees (corrige EC-07) ; blocage refusé si réservation EN_ATTENTE_PAIEMENT ou CONFIRMEE ce jour (corrige EC-05) | src/app/api/coach/disponibilites, dates-bloquees | T-04 | CA-02, CA-25 | Test : blocage sur jour avec réservation CONFIRMEE → 409 ; idem avec EN_ATTENTE_PAIEMENT | Calculer « le jour » en Europe/Paris, pas en UTC |
| T-09 | Module de calcul des créneaux : disponibilités, dates bloquées, durée, chevauchements, délai min 2 h, horizon 60 j | src/domain/creneaux.ts | T-02, T-03 | CA-01, CA-02, CA-03, CA-04 | Tests unitaires CA-01 à CA-04 avec horloge figée | Passage heure d'été/hiver : tester un lundi de changement d'heure |
| T-10 | Écran E-07 (séances, disponibilités, dates bloquées, lien public copiable) | src/app/(coach)/seances | T-07, T-08 | CA-02, CA-25, CA-32 | Playwright : création séance et disponibilité, blocage refusé affiché | Afficher l'erreur 409 en texte clair (AM-04) |
| ID | Objectif | Modules ou fichiers | Dépend de | CA | Preuve de fin | Piège à éviter |
|---|---|---|---|---|---|---|
| T-11 | API-12/13 : page publique et créneaux ; séances masquées si connect_complet faux | src/app/api/public/coachs/[slug] | T-09, T-05 | CA-01 à CA-04, CA-28, CA-32 | Tests API : coach sans Connect → liste de séances vide | Ne pas exposer d'email ni d'id interne coach |
| T-12 | API-14 : validation entrées (email, mobile 06/07 normalisé E.164, 400 avant insertion, corrige EC-06), INSERT réservation avec verrou 15 min, session Checkout ; violation d'exclusion → 409 ; échec Stripe → EXPIREE | src/app/api/public/reservations, src/domain/reservation.ts, src/lib/telephone.ts | T-11, T-03 | CA-05, CA-06, CA-23, CA-24 | Test concurrence : 2 requêtes parallèles → 1 × 201, 1 × 409 ; « 12345 » → 400 ; « 06 12 34 56 78 » → +33612345678 | Une transaction ; ne pas pré-vérifier la disponibilité puis insérer (course) : la contrainte tranche |
| T-13 | API-17 webhook Stripe : signature, idempotence evenement_stripe, CONFIRMEE si montant égal, anomalie sinon ; à la confirmation, insertion de rappel_sms si début − now > 24 h (corrige EC-02) ; webhook tardif sur EXPIREE : PAYE puis REMBOURSE dans la même transaction (corrige EC-01) ; account.updated → connect_complet | src/app/api/webhooks/stripe, src/domain/paiement.ts | T-12 | CA-08 à CA-12, CA-19 | Tests : doublon sans effet, signature invalide → 400, montant ≠ → pas de confirmation et ligne journal_anomalie, paiement tardif → EXPIREE + REMBOURSE | Lire le corps brut pour la signature ; INSERT dans evenement_stripe et traitement dans la même transaction |
| T-14 | JOB-01 : worker d'expiration des verrous (cron 1 min) | src/worker/expiration.ts | T-12 | CA-07 | Test avec horloge : réservation à 16 min → EXPIREE, créneau à nouveau réservable | Ne toucher que EN_ATTENTE_PAIEMENT ; requête idempotente |
| T-15 | Écrans E-01, E-02, E-03 (E-03 lit API-15 par jeton, corrige EC-08), compte à rebours, états d'erreur | src/app/c/[slug], reserver, retour | T-11, T-12, T-13 | CA-05, CA-06, CA-23, CA-24, CA-07 | Playwright : parcours P-01 complet avec Stripe simulé, du créneau à « confirmée » | Page de retour : statut « en cours de validation » tant que le webhook n'est pas reçu |
| ID | Objectif | Modules ou fichiers | Dépend de | CA | Preuve de fin | Piège à éviter |
|---|---|---|---|---|---|---|
| T-16 | Email de confirmation avec lien de gestion (envoi après confirmation, retry par le worker) | src/adapters/email.ts, src/domain/notifications.ts | T-13 | CA-08, CA-09 | Test : 1 email à la confirmation, 0 sur webhook doublon | Envoyer après commit de la transaction, pas dedans |
| T-17 | JOB-02 : envoi des rappels SMS, 1 retry à +10 min puis echec ; modèle de message fixe sans promotion ; jamais de doublon | src/worker/rappels.ts, src/domain/sms.ts | T-13, T-03 | CA-18 à CA-22 | Tests : 1 SMS à T − 24 h ; erreur Twilio → retry puis echec, réservation inchangée ; contenu = modèle fixe | UNIQUE(reservation_id) + mise à jour atomique du statut avant envoi pour éviter le double envoi |
| T-18 | API-15/16 : détail et annulation client par jeton, table de décision ; remboursement intégral ; rappel_sms.statut='annule' à l'annulation (corrige EC-02) ; 404 uniforme si jeton inconnu | src/app/api/public/reservations/[jeton] | T-13 | CA-13 à CA-15, CA-27 | Tests : > 24 h → ANNULEE_CLIENT + REMBOURSE ; ≤ 24 h → 409 ; 2ᵉ appel idempotent sans second remboursement ; jeton inconnu → 404 | Clé d'idempotence Stripe sur le remboursement ; frontière exacte à 24 h pile = refus |
| T-19 | Écran E-04 (gestion client, confirmation d'annulation avec issue financière, AM-06) | src/app/g/[jeton] | T-18 | CA-13 à CA-15, CA-27 | Playwright : annulation depuis le lien, puis réouverture → « déjà annulée » | Boîte de dialogue accessible au clavier |
| ID | Objectif | Modules ou fichiers | Dépend de | CA | Preuve de fin | Piège à éviter |
|---|---|---|---|---|---|---|
| T-20 | API-10/11 : liste des réservations et annulation coach, remboursement automatique ; ECHEC_REMBOURSEMENT + anomalie + alerte ; rappel_sms → annule ; email + SMS d'annulation coach au client, contenu transactionnel, SMS documenté dans D-05 (corrige EC-03) | src/app/api/coach/reservations | T-18, T-17 | CA-16, CA-17, CA-26, CA-30 | Tests : annulation à 2 h → ANNULEE_COACH + REMBOURSE + email et SMS ; échec Stripe → ECHEC_REMBOURSEMENT ; réservation d'un autre coach → 404 | Ne pas bloquer l'annulation si le remboursement échoue ; ne pas rembourser une réservation EN_ATTENTE_PAIEMENT (→ EXPIREE) |
| T-21 | JOB-03 : clôture CONFIRMEE → TERMINEE ; refus d'annulation ensuite | src/worker/cloture.ts | T-18, T-20 | CA-29 | Test horloge : après fin → TERMINEE, annulation → 409 | Comparer sur fin, pas sur debut |
| T-22 | Écran E-08 (tableau de bord, alerte ECHEC_REMBOURSEMENT, annulation avec confirmation) | src/app/(coach)/tableau-de-bord | T-20, T-21 | CA-16, CA-17, CA-26, CA-29, CA-30 | Playwright : 3 réservations affichées avec statuts en texte ; annulation et alerte visibles | Statuts en texte, pas seulement en couleur (AM-04) |
| ID | Objectif | Modules ou fichiers | Dépend de | CA | Preuve de fin | Piège à éviter |
|---|---|---|---|---|---|---|
| T-23 | Limitation de débit (API-02, 13, 14, 16), en-têtes de sécurité, journaux sans mobile ni email en clair | src/middleware.ts, src/lib/log.ts | T-12, T-18 | CA-26, CA-27 | Test : rafale sur API-14 → 429 ; inspection des logs sans donnée personnelle | Limiter aussi par IP sur les routes publiques |
| T-24 | Tests de bout en bout sur les parcours P-01 à P-04 et mention RGPD en E-02 | e2e/*.spec.ts, E-02 | T-15, T-19, T-22 | CA-05 à CA-30 | npx playwright test vert en CI | Mocks Stripe/Twilio : ne jamais appeler les services réels en CI |
| T-25 | README : installation, variables d'environnement, lancement des workers, limites connues et hypothèses à vérifier (frais de remboursement Connect, solde insuffisant, qualification juridique des SMS) | README.md | T-24 | — | Installation à froid réussie en suivant le README | Lister les limites telles quelles, sans les masquer |
| CA | Tâche(s) |
|---|---|
| CA-01 | T-09, T-11 |
| CA-02 | T-08, T-09, T-10 |
| CA-03 | T-09, T-11 |
| CA-04 | T-09, T-11 |
| CA-05 | T-12, T-15, T-24 |
| CA-06 | T-02, T-12, T-15 |
| CA-07 | T-14, T-15 |
| CA-08 | T-13, T-16 |
| CA-09 | T-13, T-16 |
| CA-10 | T-13 |
| CA-11 | T-13 |
| CA-12 | T-13 |
| CA-13 | T-18, T-19 |
| CA-14 | T-18, T-19 |
| CA-15 | T-18, T-19 |
| CA-16 | T-20, T-22 |
| CA-17 | T-20, T-22 |
| CA-18 | T-17 |
| CA-19 | T-13, T-17 |
| CA-20 | T-17, T-18 |
| CA-21 | T-17 |
| CA-22 | T-17 |
| CA-23 | T-12, T-15 |
| CA-24 | T-12, T-15 |
| CA-25 | T-08, T-10 |
| CA-26 | T-20, T-22, T-23 |
| CA-27 | T-18, T-19, T-23 |
| CA-28 | T-05, T-11 |
| CA-29 | T-21, T-22 |
| CA-30 | T-20, T-22 |
| CA-31 | T-02, T-04, T-06 |
| CA-32 | T-07, T-10, T-11 |
Les 32 CA sont couverts. Les T-13 et T-17 s'appuient sur rappel_sms créé à la confirmation (EC-02) ; l'ordre T-13 → T-17 → T-18 → T-20 respecte les dépendances.
Independent review
| ID | Gravité | Où | Problème | Correction |
|---|---|---|---|---|
| EC-01 | Haute | §4 reservation_confirmee_payee vs RM-06 / CA-08 | Contrainte CONFIRMEE ⇒ PAYE. Mais RM-07/RM-08 passent en ANNULEE_* avec REMBOURSE : cohérent. En revanche TERMINEE n'a pas de contrainte, et surtout un remboursement automatique sur EXPIREE (RM-04/CA-11) est incohérent avec RM-06 « réservation expirée = créneau libéré » : le client payé tardivement ne voit pas son créneau garanti. Ce cas est traité, mais le paiement PAYE transitoire avant REMBOURSE n'est pas défini. | Préciser en RM-04 : webhook tardif → paiement PAYE puis REMBOURSE dans la même transaction. |
| EC-02 | Haute | RM-10 / §4 rappel_sms / JOB-02 | rappel_sms n'est jamais créé ni annulé dans les parcours : aucune règle ni API ne dit qui insère la ligne (à la confirmation, API-17) ni qui passe annule (annulations RM-07/RM-08). CA-19 (< 24 h : pas de rappel) suppose de ne pas insérer. | Ajouter à API-17 : à la confirmation, insérer rappel_sms si début − now() > 24 h. Ajouter à API-11/API-16 : passer rappel_sms.statut = 'annule'. |
| EC-03 | Moyenne | RM-08 / §5 intégrations | RM-08 prévoit « email + SMS au client » à l'annulation coach, mais aucun SMS d'annulation n'est dans D-05 (1 SMS par réservation), RM-10, ni les CA (CA-16 le cite). Coût et cadre D-06 non traités. | Mettre à jour D-05 : « 1 rappel + 1 SMS d'annulation coach éventuel » et garder son contenu transactionnel, ou retirer le SMS de RM-08/CA-16. |
| EC-04 | Moyenne | RM-13 vs §4 | RM-13 dit « jeton ≥ 128 bits », §4/§5 en fournissent 256 bits. Écart mineur de formulation. | Aligner RM-13 sur « 256 bits » (ou « ≥ 128 » partout). |
| EC-05 | Moyenne | RM-12 / CA-25 vs API-08 | RM-12 : blocage refusé si réservation CONFIRMEE ; la réservation EN_ATTENTE_PAIEMENT ce jour n'est pas couverte et peut devenir CONFIRMEE sur une date bloquée. | Étendre à EN_ATTENTE_PAIEMENT ou filtrer les créneaux/confirmations sur dates bloquées. |
| EC-06 | Moyenne | §4 CHECK reservation_mobile_e164 vs RM-11 | Regex \+33[67]… n'accepte que les mobiles 06/07 ; RM-11 dit « +33 / 0X » (tout format français). CA-24 passe, mais un 01–05, 08–09 est accepté par RM-11 et rejeté en base (500 au lieu de 400). | Aligner RM-11 sur « mobile français 06/07 » et valider côté API avant insertion. |
| EC-07 | Faible | §3 E-01/E-07 vs API | Aucun endpoint de lecture pour E-07 (liste séances, disponibilités, dates bloquées du coach) ni de suppression de disponibilité/séance ; GET coach manquants. | Ajouter GET /api/coach/seances, GET /api/coach/disponibilites, GET /api/coach/dates-bloquees. |
| EC-08 | Faible | §3 E-03 vs API | E-03 affiche le statut « en attente du webhook » mais API-15 est la seule lecture et est présentée pour E-04. | Indiquer que E-03 utilise API-15 par jeton. |
One spec on us, no credit card. Your own AI keys accepted from the free plan.
To judge on the evidence, with your own idea.
To build your products, on your own.
For a team sharing the same spec.
A studio that writes your software project's spec, makes it readable by your AI coding tool, then checks that the code follows it. You keep one reference document, from the first prompt to production.
It plans the task at hand. Kodrudder describes the whole product: business rules, data, edge cases, acceptance criteria. And the spec doesn't vanish at the end of the session: it's the reference for every new task.
Yes if you use Cursor, Claude Code, Codex, Windsurf, Copilot, Gemini, Lovable, Bolt, v0 or Replit. The spec exports in the format each one expects, and an MCP server lets your tool read it directly.
Yes. Give a GitHub link, public or private, or drop an archive. Kodrudder rebuilds the spec of what exists, flags weak spots and prepares the context files for your tool.
Your idea, your code and your spec are only used to produce your spec. We don't sell them and don't use them to train models. With your own AI key, you also choose the provider.
Yes. Your first spec needs no account and no credit card. Create a free account to keep it.
No credit card.