station

S5 · Build

playbook/S5-build.md

S5 — Build (J7–11)

Objectif : passer du spec à un produit live sur domaine custom, Stripe live (vraie carte qui débite), E2E Playwright qui passe en CI. 5 jours, pas plus.

Durée

5 journées (J7–11). C'est la station la plus longue et la seule qui justifie 5 jours.

Inputs

  • 04-spec.md (3 features + AC)
  • 04-design-tokens.json
  • wireframes/ (5 écrans)
  • templates/saas-base/ (à scaffolder en v0 — voir bootstrap S5)
  • ~/.atelier.env (tokens : VERCEL_TOKEN, SUPABASE_ACCESS_TOKEN, STRIPE_SECRET_KEY, GITHUB_TOKEN, RESEND_API_KEY, POSTHOG_API_KEY)

Output (livrables verrouillés)

  • Repo GitHub privé (peut devenir public en S6)
  • URL https://{domaine-custom} qui répond 200 sur la home
  • Stripe LIVE keys configurées (pas test) ; 1 vraie transaction passée par Martin
  • Playwright E2E tests qui passent en CI (golden path : signup → onboarding → 1 feature core → upgrade)
  • README projet : pnpm dev marche en 1 commande depuis un clone fresh
  • 📱 Gate Telegram : URL live + screenshot Stripe txn + lien Playwright run

Critères de verrouillage

  1. URL live publique sur le domaine custom (DNS pointé via Vercel, SSL OK).
  2. Une vraie carte a débité via Stripe LIVE. Martin teste lui-même avec sa carte perso → refund. Screenshot du Payment Intent succeeded dans Stripe Dashboard.
  3. Playwright E2E vert sur les 3 features v1 + le golden path. Workflow GitHub Actions configuré.
  4. pnpm dev marche en fresh clone : un dev externe (ou agent) qui clone + pnpm install + remplit .env.local + pnpm dev doit voir le produit fonctionner en localhost en < 10 min.
  5. 0 secret committé au repo. .env* dans .gitignore, secrets dans GitHub Actions secrets + Vercel env vars.
  6. Posthog branché et au moins 3 events trackés (signup, activation event, upgrade click).

Stack figée (pas négociable per sprint)

Next.js 14 (app router)
  + Tailwind CSS
  + shadcn/ui (composants)
  + Lucide icons
Supabase (auth + Postgres + RLS)
Stripe (Checkout + Customer Portal + webhooks)
Resend (email transactionnel)
Vercel (hosting + preview deployments)
Playwright (E2E)
Posthog (analytics + feature flags)
GitHub Actions (CI + Playwright run)

Le template templates/saas-base/ doit pré-câbler tout ça. Pas d'install à chaque sprint.

Process

J7 — Setup repo + scaffold (1 jour)

  1. gh repo create atelier-{sprint_slug} (privé)
  2. Copier templates/saas-base/ → repo
  3. Renommer toutes les occurrences saas-base{nom S3}
  4. Connect Supabase : supabase init + supabase link --project-ref {ref} + supabase db push
  5. Connect Stripe : créer les products dans Stripe Dashboard + écrire lib/stripe.ts avec les price IDs
  6. Connect Vercel : vercel link + push env vars + premier deploy preview
  7. Custom domain : vercel domains add {domain.com} + pointer DNS

J8 — Feature 1 (1 jour)

J9 — Feature 2. J10 — Feature 3.

Chaque jour : 1 feature complète selon les AC du spec. Pas de "je commence Feature 2 avant d'avoir fini Feature 1". Définit "fini" = tous les AC cochés + tests Playwright passants pour cette feature.

J11 — Stripe live + Playwright CI + Posthog (1 jour)

Matin :

  • Activer Stripe LIVE (passage de test → live keys)
  • Tester avec ta vraie carte → refund
  • Configurer webhooks Stripe → app/api/webhooks/stripe/route.ts (signature verify obligatoire)

Après-midi :

  • Compléter la suite Playwright (golden path E2E)
  • GitHub Actions workflow e2e.yml → run on PR
  • Posthog SDK : signup event, activation event, upgrade_clicked event

Soir :

  • Smoke test full sur prod URL (par Martin lui-même OU agent via gstack browser skill)
  • Gate Telegram envoie URL + screenshot Stripe txn + lien Playwright run

Bootstrap de la template saas-base/

Le template n'existe pas encore. À scaffolder UNE FOIS, puis réutilisé pour tous les sprints. Structure cible :

templates/saas-base/
├── README.md                  # comment cloner pour un nouveau sprint
├── package.json
├── pnpm-lock.yaml
├── tsconfig.json
├── tailwind.config.ts
├── next.config.js
├── .env.example               # toutes les vars listées
├── .gitignore
├── components/
│   └── ui/                    # shadcn components
├── lib/
│   ├── supabase.ts
│   ├── stripe.ts
│   ├── resend.ts
│   ├── posthog.ts
│   └── utils.ts
├── app/
│   ├── layout.tsx
│   ├── page.tsx               # landing
│   ├── (auth)/login/page.tsx
│   ├── (auth)/signup/page.tsx
│   ├── (auth)/callback/route.ts
│   ├── (dashboard)/app/page.tsx
│   ├── (dashboard)/settings/page.tsx
│   ├── pricing/page.tsx
│   └── api/
│       ├── webhooks/stripe/route.ts
│       └── webhooks/resend/route.ts
├── supabase/
│   ├── config.toml
│   └── migrations/
│       └── 00_init.sql        # users, subscriptions, audit_log
├── tests/
│   └── e2e/
│       ├── auth.spec.ts
│       ├── stripe.spec.ts
│       └── golden-path.spec.ts
├── .github/workflows/
│   ├── ci.yml
│   └── e2e.yml
└── scripts/
    ├── bootstrap-sprint.sh    # clone + rename + env setup
    └── deploy-prod.sh         # vercel --prod + db push

Scaffold-le UNE FOIS, ensuite réutilisable. C'est ce qui rend Atelier vendable plus tard : un solopreneur qui achète Atelier hérite de cette template + du playbook.

Prompt canonique (Builder agent v1)

Tu es S5 Builder d'Atelier. Tu reçois startup.json après S4 (spec + tokens + wireframes).

Tu travailles dans un dossier {runs}/build/ qui contient un clone fresh de templates/saas-base/ déjà renommé pour ce sprint.

Tu produis dans l'ordre :

J7 — Setup
- Vérifier que pnpm install + pnpm dev marche
- Connecter Supabase + push migrations (tu lis supabase/migrations/00_init.sql et tu adaptes pour les features du spec)
- Créer les Stripe products + récupérer price IDs
- vercel deploy preview + custom domain

J8-J10 — Une feature par jour
- Lis features_v1[i] du startup.json
- Code la feature en suivant les AC
- Écris Playwright spec pour cette feature
- Commit + push + vérifier que le preview Vercel marche
- Mets à jour startup.json.s5_build avec les progress

J11 — Stripe live + CI + Posthog
- Swap test keys → live keys (DEMANDE confirmation humaine via gate Telegram)
- Smoke test avec gstack browser : signup → activate → upgrade → checkout
- Run Playwright en CI
- Branche Posthog
- Écris BUILD_READY.txt avec URL prod + screenshot Stripe txn

Tu signales tout problème > 30 min de stuck. Tu ne t'auto-overscope pas — si une AC est tordue, tu reviens au spec S4 et tu demandes clarification.

Stack à utiliser STRICTEMENT (pas de substitution) :
{stack list}

State input :
<input>
{startup_json_after_s4}
</input>

Anti-patterns

  • Démarrer avec un template "from scratch" au lieu de saas-base/ → tu vas perdre 2 jours à recâbler Stripe/Supabase. Le template est obligatoire.
  • "Je vais ajouter une 4ème feature parce que c'est facile" → STOP. Le scope est figé S4. Note dans OUT_OF_SCOPE_DISCOVERED.md, on verra en sprint suivant.
  • Skip Playwright "pas le temps" → c'est le seul garde-fou contre la régression. Sans, tu shippes un produit qui sera cassé à J20.
  • Stripe test pendant S6 → non. Stripe live doit être prouvé en S5. Si Stripe test marche mais live échoue, c'est en S5 qu'on découvre.
  • Secrets dans le repo → audit obligatoire avant push. git secrets ou check manuel des .env* files.
  • Pas de webhook signature verify sur les routes Stripe → faille de sécurité. Le template doit l'inclure par défaut.

Lien S6

L'URL prod live + le Stripe LIVE + le repo sont les inputs canoniques de S6. La landing (qui est app/page.tsx du repo) est déjà coded en S5 — S6 va l'affiner avec du copy Hormozi-style et ajouter le tracking de conversion.

Update startup.json (après S5)

{
  "current_station": "S6",
  "s5_build": {
    "completed_at": "<iso8601>",
    "telegram_gate_approved_at": "<iso8601>",
    "repo_url": "https://github.com/{owner}/atelier-{slug}",
    "live_url": "https://{domain}.com",
    "custom_domain_pointed": true,
    "stripe_live": true,
    "supabase_project_ref": "...",
    "e2e_passing": true,
    "playwright_run_url": "https://github.com/.../actions/runs/...",
    "posthog_project_id": "..."
  }
}