App Readiness · Lockdown Lift Plan · 2026-05-18

Audit READ-ONLY condotto da Claude (sessione P-launch-readiness) il 2026-05-18 Hard gate 10 Giu 2026 · 22 giorni rimanenti · soft gate 31 Mag (13 giorni)

Scopo: identificare ogni gate tecnico + operativo tra l'app oggi (founder-gated lockdown, VITE_FOUNDER_EMAILS 3 email) e il primo paying customer reale che firma un checkout Stripe e riceve valore dal SaaS in autonomia.

Premessa metodologica: questo report è prodotto da Claude in lettura sola del repo codice (D:\Desktop\Multiverbe) + database Supabase live (yqhixdqipojvgzfadufo) + checklist business (Multiverbe-Business/ops/first-paying-customer-checklist.md). Nessuna modifica al codice, niente migration, niente deploy.


0 · Executive summary (per founder · 60 secondi)

Verdict launch-readiness 18 Mag 2026: READY-WITH-2-BLOCKERS.

L'app è funzionalmente pronta per il primo paying customer. Il 78% delle 35 task della checklist first-paying-customer-checklist.md è done; i 2 unici BLOCKING reali sono azioni founder (10 + 20 minuti), non codice da scrivere:

  1. BLOCKING-A3: verify domain Brevo (TXT su DNS multiverbe.com) → senza, le mail transazionali (signup confirm, welcome paying, invoice receipt) NON partono.
  2. BLOCKING-C2: wildcard CNAME *.sites.multiverbe.com su DNS → senza, i siti generati dal Site Builder NON sono raggiungibili dal pubblico.

Tutto il resto è polish o stato fisiologico di un MVP pre-lancio.

Counts

Categoria Findings
BLOCKING (pre-payment) 6
NICE-TO-HAVE (post-1st) 9
Risk register top 10

Top 3 founder action items immediate (oggi · 18 Mag)

  1. Verify domain Brevo (10 min) — Dashboard Brevo > Senders > Domains > multiverbe.com → copia 3 record DNS (1 TXT DKIM + 1 TXT SPF + 1 TXT verification) → incolla in provider DNS landing multiverbe.com. Sblocca BLOCKING-A3 (email confirmation, welcome, invoice receipts).
  2. DNS wildcard *.sites.multiverbe.com (20 min) — Cloudflare/Register.it CNAME → cname.vercel-dns.com + verifica nel project multiverbe-sites di Vercel. Sblocca BLOCKING-C2 + abilita SSL Let's Encrypt auto-issued post-propagazione DNS (~5 min).
  3. Switch Stripe live mode + fund test card $5 — Dashboard Stripe > Activate account (founder data + bank account CH/IT). Poi rotate test → live le 9 price IDs su Vercel env STRIPE_PRICE_*. Sblocca BLOCKING-F0 (oggi le price sono sk_test_*).

Il primo paying customer non può oggi firmare un checkout reale finché item #3 non è fatto, anche se la BillingPage e useCheckout sono già code-complete.


1 · Stato lockdown attuale (defense-in-depth a 4 layer)

L'app è protetta da 4 layer indipendenti — togliere SOLO il layer 1 (client) lascia comunque chiuso il signup; togliere SOLO il layer 2 (signup_enabled) lascia comunque i nuovi utenti banned dal trigger DB. Per APRIRE bisogna toccare tutti i layer in ordine specifico (vedi §5 checklist).

1.1 · Layer 1 · Client gate · FounderGate.tsx

File: frontend\multiverbe-app\src\components\auth\FounderGate.tsx (131 righe) Wired in: frontend\multiverbe-app\src\App.tsx:343-344 (post-auth, pre-dashboard) Trigger: !isFounderEmail(email) → mostra schermata "Stiamo costruendo" + auto-signOut in 6 secondi (setTimeout in FounderGate.tsx:20-26).

isFounderEmail() è definito in frontend\multiverbe-app\src\lib\founder.ts:15-25:

const FOUNDER_EMAILS = (
  import.meta.env.VITE_FOUNDER_EMAILS ?? 'metaverbe@gmail.com'
).split(',').map(e => e.trim().toLowerCase())

Build-time env: VITE_FOUNDER_EMAILS su Vercel project multiverbe-app Production scope. Valori attesi (per BOS audit + DB allowlist):

  • metaverbe@gmail.com (founder Gregorio)
  • loris.beraud17.7@icloud.com (Loris)
  • gregoriociurleo@gmail.com (founder personal, aggiunto 2026-05-17)

Nota: la variabile è VITE_* (esposta al client a build-time). Cambiarla richiede redeploy. La fonte di verità è la tabella public.founder_emails (allowlist server).

1.2 · Layer 2 · Signup UI flag · VITE_SIGNUP_ENABLED

File: frontend\multiverbe-app\src\components\auth\SignupPage.tsx:17 (default false) Comportamento: la pagina /?auth=signup mostra <SignupClosedPage /> (righe 19-89) con testo "Le registrazioni sono chiuse." e CTA Torna al login. Solo se VITE_SIGNUP_ENABLED === 'true' viene renderato <SignupOriginal /> (signup form vero).

Link "Primo accesso?" nella LoginPage.tsx è stato rimosso (commento riga 375-377: Build phase (2026-05-17 founder): registrazioni chiuse al pubblico). Mostra solo la stringa "Multiverbe è in fase di build. Le registrazioni apriranno presto." (riga 389).

1.3 · Layer 3 · Supabase Auth signup OFF (Dashboard founder)

Stato pending verifica: dalla migration founder_gate_build_phase_2026_05_17 (commento inline righe 7-8) il toggle "Allow new users to sign up" è stato spento manualmente in Supabase Dashboard > Authentication > Settings. Anche se il front-end manda supabase.auth.signUp(), l'API risponde 422 Signups disabled.

Non verificabile via MCP mcp__supabase__* perché non c'è un'API per leggere il flag disable_signup. Va guardato manualmente in Dashboard (founder action).

1.4 · Layer 4 · Trigger DB auto_ban_non_founder_users_trigger

Migration: 20260517183128_founder_gate_build_phase_2026_05_17 (applicata su prod Supabase 2026-05-17 18:31 UTC).

Crea 3 oggetti server-side:

  1. Tabella public.founder_emails — allowlist primary key email text (lowercase). Seed 2 founder + 1 manuale post-deploy (gregoriociurleo@gmail.com). RLS ON, policy founder_emails_self_read permette SELECT solo se email = lower(auth.jwt() ->> 'email').
  2. Function is_founder()SECURITY DEFINER STABLE SQL, ritorna bool. Usata per future RLS policy. Già un advisor warn authenticated_security_definer_function_executable raccomanda di rivalutare quando si apre signup.
  3. Trigger auto_ban_non_founder_users_trigger su auth.users AFTER INSERT FOR EACH ROW. Se la nuova email non è in founder_emails, esegue UPDATE auth.users SET banned_until = 'infinity'. SECURITY DEFINER indispensabile (la tabella auth.users è di proprietà di supabase_auth_admin).

Verifica live (eseguita oggi via MCP):

total_users        : 5
banned_users       : 2  ← non-founder + 1 unconfirmed
founder_emails     : 3
workspaces         : 5
subscriptions      : 0
sectors            : 21
users_confirmed    : 4
users_unconfirmed  : 1
sites              : 0
bookings           : 0
webhook_events     : 2
smtp_configs       : 0   ← VUOTO (founder mailbox SMTP non ancora configurato in DB)
email_accounts     : 0   ← VUOTO
tier_caps          : 15  (3 tier × 5 metric)

1.5 · Riassunto · cosa cambiare per togliere il gate

# Layer File / Risorsa Cambio
1 Vercel env VITE_SIGNUP_ENABLED (project multiverbe-app) falsetrue + redeploy
2 Vercel env VITE_FOUNDER_EMAILS RIMUOVERE (oppure lasciare come admin allowlist · vedi §5 decisione)
3 Supabase Dashboard > Auth > Settings > Allow new users sign up OFF → ON (1 click founder)
4 Supabase DB Trigger auto_ban_non_founder_users_trigger DROP TRIGGER auto_ban_non_founder_users_trigger ON auth.users
5 App code frontend\multiverbe-app\src\App.tsx:343-345 Rimuovere il blocco if (!isFounderEmail(email)) return <FounderGate/>
6 App code frontend\multiverbe-app\src\components\auth\LoginPage.tsx:375-390 Ripristinare link "Primo accesso? → /?auth=signup"

Steps 4-6 sono opzionali con dipendenza (vedi §5): se vuoi mantenere founder_emails come admin allowlist (es. per accesso a ?__dev=* harness) puoi tenere step 5 e DROP solo il trigger. Decisione founder consigliata: tenere founder_emails come admin ACL, rimuovere il banneraggio automatico, rimuovere FounderGate (il signup pubblico diventa la porta normale, gli harness __dev restano founder-only).


2 · Onboarding readiness · landing → primo pagamento (10 step)

Audit step-by-step del happy path. Per ogni step: status (✅ works · ⚠️ partial · ❌ broken) con file/riga e gap concreti.

Step 1 · Landing multiverbe.com → CTA "Inizia ora"

Stato: ⚠️ partial — source non in questo repo

  • Repo locale D:\Desktop\Multiverbe\ NON contiene il source della landing (cfr. MEMORY index project_landing_no_local_source.md).
  • La landing è hostata su Vercel project multiverbe-landing (rinominato 2026-05-11), lockdown via Dashboard, non confonderla con D:\Desktop\Metaverbe\metaverbe.com\ che è il sito agenzia.
  • Il founder ha confermato A1 done in checklist (riga 20: "live su Vercel multiverbe-landing").
  • GAP da verificare manualmente con founder: il CTA "Inizia ora" punta a https://app.multiverbe.app/?auth=signup? Se sì OK. Se ancora app.multiverbe.com, funziona via redirect 308 in frontend\multiverbe-app\vercel.json:6-19 (entrambi app.multiverbe.com e app.multiverbe.app redirectano a multiverbe.app).

Azione: founder verifica CTA destination + test mobile.

Step 2 · Signup app.multiverbe.app/?auth=signup

File entry: src\components\auth\SignupPage.tsx<SignupOriginal /> solo se VITE_SIGNUP_ENABLED=true.

Stato: ✅ code-complete, gated da env flag

  • Form email + password + checkbox terms (righe 245-310).
  • Validation: required + minLength={6} (riga 269).
  • Submit → useAuth().register(email, password)AuthContext.tsx:95-115supabase.auth.signUp({ email, password }).
  • Risposta: se data.user && !data.session mostra needsConfirmation screen "Apri la mail" (righe 129-200). Se session presente, login automatico.
  • Link OAuth Google/Apple presenti nella LoginPage (LoginPage.tsx:298-371), MA OAuth provider va abilitato in Supabase Dashboard > Auth > Providers (founder action).

GAP / decisioni:

  • OAuth Google/Apple richiede setup separato (Google Cloud OAuth client + Apple Developer team ID). Vedi Multiverbe-Business/ops/google-api-setup-kit-2026-05-18.md. Non bloccante: passwordless signup funziona da solo.
  • Quando si toglie il lockdown, ricordare di ripristinare il link "Primo accesso?" in LoginPage.tsx:378-390 (oggi mostra solo testo "Multiverbe è in fase di build").

Step 3 · Email confirmation (Brevo SMTP)

Stato: ❌ BLOCKING — Brevo domain verify pending

  • Supabase di default invia l'email di conferma tramite il proprio SMTP fallback (noreply@mail.app.supabase.io). Quello funziona per testing, NON è production-grade (alta delivery rate solo se cliente in whitelist).
  • L'integrazione Brevo c'è già lato Vercel functions (api\_lib\brevo.ts 108 righe
    • api\booking\_lib\brevo.ts), MA la funzione sendBrevoEmail parte solo se process.env.BREVO_API_KEY è settato + il sender domain è verified su Brevo.
  • Lo stato smtp_configs in DB live = 0 righe (verificato via SQL). I template Brevo per signup confirm NON sono ancora collegati a Supabase Auth (servirebbe configurare Supabase Auth Email Templates con SMTP custom Brevo).
  • Checklist riga 22 (A3): "Email confirmation Brevo SMTP attiva (bloccante: verify domain Brevo TXT)".

GAP concreti:

  1. Brevo domain verify multiverbe.com con TXT DKIM + SPF + verification → founder DNS task.
  2. Supabase Auth SMTP custom (Dashboard > Authentication > Email > SMTP Settings) → host smtp-relay.brevo.com:587 + Brevo SMTP user + password.
  3. Email template "Confirm signup" in Supabase → personalizzare HTML allineato a wrapMultiverbeEmail() (api\_lib\brevo.ts:94-107) per consistenza brand.

Step 4 · Login post-confirm + creazione workspace

Stato: ✅ works (trigger handle_new_user attivo)

  • Function handle_new_user() è ON in auth.users AFTER INSERT (verificato via information_schema.triggers).
  • Crea: 1 row workspaces (default settings) + 1 row team_members (role=owner).
  • La logica è in canon (Multiverbe-Business/canon/05-stack.md riga "Trigger automatici") + verificata in checklist A5 done.
  • Live: 5 workspace per 5 users → 1:1 corrispondenza confermata.

Step 5 · Onboarding wizard SetupWizardV2

Stato: ✅ code-complete

  • Wired in App.tsx:347-369 con feature flag USE_ONBOARDING_V2 = import.meta.env.VITE_ONBOARDING_V2 === 'true'.
  • Default OFF (legacy SetupWizard attivo). Founder action pre-launch: settare VITE_ONBOARDING_V2=true su Vercel.
  • 4 step orchestrator (SetupWizardV2.tsx 310 righe):
    • Step 1 Macro (5 cards · 15 sec)
    • Step 2 Sector (3-5 cards per macro · 10-30 sec)
    • Step 2.5 AI Chat fallback (Sonnet 4.6 · max 4 turni · Step2AIChat.tsx)
    • Step 3 Workspace (5 sotto-domande · 60 sec)
    • Step 4 Tour (60 sec preview)
  • Salvataggio finale: workspaces UPDATE con sector_id, sector_macro_id, business_type, active_modules, onboarding_completed=true (righe 142-159).
  • Fallback graceful pre-migration B: drop campi V2 e salva tutto in default_settings.onboarding_v2 jsonb (righe 162-176).

21 settori live in DB (verificato via SQL): 4 hospitality (ristorante, bar, hotel, bnb) · 4 salute (parrucchiere, estetica, gym, studio-medico) · 4 servizi-pro (legale, commercialista, tecnico, consulente) · 4 vendita (ecommerce, boutique, concept-store, wholesale) · 5 eventi (wedding, fotografo, video, comunicazione, web-agency).

Settori top-7 pre-MVP (cfr. memory project_sectors_top_7_pre_mvp.md): ristorante, hotel, parrucchiere, gym, studio-medico, immobiliari (MISSING — non in DB · canon 11-sectors-deep.md riga 47 marker TODO), studi professionali (studio-legale + studio-commercialista + studio-tecnico ok).

GAP: il settore immobiliari previsto in canon NON è nel DB seed. È in top-7 pre-MVP. Decisione founder: ship MVP senza (il fallback AI chat copre il gap) OR seed inline pre-deploy.

Step 6 · Dashboard primo accesso

File entry: App.tsx:377-440<PlanetScene /> (v2-3d 2026-05-17 root experience).

Stato: ✅ works

  • PlanetScene.tsx (171 righe iniziali read) = NucleoSphere 3D fullbleed + glass nav
    • PlanetPanel side overlay quando un pianeta è selezionato + SatelliteCard carousel.
  • Layout legacy MAIND (KPI/Crescita/Finanza/Opera) rimosso dal route principale (decisione founder 2026-05-17, righe commento 17-29).
  • KPI "—" placeholder è ok per primo accesso (workspace vuoto, nessun deal/booking/contatto).

GAP NICE-TO-HAVE:

  • D6 Mobile responsive pixel-level audit (checklist riga 63) scheduled w4 26 Mag-1 Giu.
  • Empty state copywriting su pianeti vuoti — verifica founder. KPISection/OperaSection componenti legacy esistono ancora come dead-code (cfr. memory project_repo_rename_2026_05_11

Step 7 · Site Builder flow

Entry: click "Pianeta Web" in DesktopSidebar.tsx:70-75setShowSiteBuilder(true)App.tsx:415-423 renderizza <SiteBuilderWizard />.

Stato: ✅ code-complete (B1+B2 done in checklist), ⚠️ smoke pending API keys

  • 7 step wizard:
    1. Step1SectorConfirm.tsx
    2. Step2TemplateChoice.tsx
    3. Step3BrandIntake.tsx
    4. Step4PhotoUpload.tsx
    5. Step5ContentSeeds.tsx
    6. Step6GeneratePreview.tsx (background job + polling)
    7. Step7Publish.tsx
  • Pipeline AI 6-stage funzionante: Opus 4.7 + Kimi K2.6 + Replicate Flux + Sonnet polish.
  • Persistence: localStorage (draft) + DB upsert su sites.brand_dna._intake (SiteBuilderWizard.tsx:55-62).
  • Tier caps via useSiteBuilderCap (Starter 1 sito / Pro 3 / Business 10).

GAP / dipendenze:

  • MOONSHOT_API_KEY (Kimi K2.6 per Stage 3 Copywriter) — fallback Sonnet se mancante.
  • REPLICATE_API_TOKEN (Flux dev/pro per Stage 4 Visual) — bloccante: no fallback, Stage 4 fallisce silenziosamente.
  • QSTASH_TOKEN + signing keys (Upstash) — per scheduling worker pipeline.
  • Verifica founder: env presenti su Vercel project multiverbe-app Production.

Step 8 · Booking widget setup

Entry: pianeta CRM/Booking → BookingDashboard.tsx + BookingSettings.tsx in features\booking\dashboard\.

Stato: ✅ code-complete (E1+E2+E3+E4 100% in checklist riga 101)

  • 8 API endpoint: api\booking\{cancel,confirm,create,prepay,services,slots-available,update,widget-create}.ts.
  • Widget pubblico embeddable: features\booking\widget\BookingWidget.tsx (route /widget/booking/:slug in App.tsx:294-306, NO auth).
  • 4 settori coperti: ristorante (tavoli) · parrucchiere (slot) · gym (lezioni) · studio professionale (appuntamenti) · hotel (camere) — checklist riga 67.
  • Email confirmation cliente via api\booking\_lib\brevo.ts (helper dedicato) — dipende da Brevo verify (vedi BLOCKING-A3).

GAP: notification ristoratore via push PWA pending service worker (checklist riga 69 "E3 · push PWA pending service worker"). Per MVP basta email.

Step 9 · Stripe Checkout (BillingPage + useCheckout)

Stato: ⚠️ code-complete, ma Stripe in TEST MODE oggi

  • useCheckout.ts (72 righe) → POST /api/stripe/create-checkout-session.
  • api\stripe\create-checkout-session.ts (194 righe):
    • Body: { tier, billingPeriod, includeSetupFee, workspaceId }.
    • Auth: Supabase JWT in header X-Supabase-Auth (riga 88-102).
    • Workspace ownership check via team_members (RLS-safe · righe 109-125).
    • Recurring + optional setup fee line items (righe 148-153).
    • success_url: ${appBaseUrl}/billing?session_id={CHECKOUT_SESSION_ID}&status=success.
    • TRIAL OFF di default (riga 161-164 commento): // subscription_data: { trial_period_days: 10 }. Canon dice "NO trial v1" → corretto.

BLOCKING-F0: il founder oggi ha Stripe in test mode (cfr. memory project_stripe_mcp_live_mode_default.md). I 9 STRIPE_PRICE_* env su Vercel sono price_test_*. Per accettare €/CHF reali serve:

  1. Stripe Dashboard > Activate account (founder info + bank IBAN CH/IT).
  2. Re-seed 9 price live mode con scripts/seed-stripe-prices.ts + sk_live_*.
  3. Update Vercel env STRIPE_PRICE_{STARTER,PRO,BUSINESS}_{MONTHLY,ANNUAL} + 3 setup fees.
  4. Update STRIPE_SECRET_KEY da sk_test_* a sk_live_* (Production scope).
  5. Update STRIPE_WEBHOOK_SECRET (Stripe live mode dashboard webhook).

Step 10 · Stripe webhook → subscriptions table + tier_caps

File: api\stripe\webhook.ts (440 righe · audit completo)

Stato: ✅ code-complete

  • Idempotency table webhook_events (stripe_event_id PK, righe 103-128).
  • Signature verification obbligatoria (righe 88-101) — return 400 se manca/invalida.
  • 9 event handlers:
    • checkout.session.completed (righe 203-247) — update workspaces.stripe_customer_id + set subscriptions.setup_fee_paid=true + welcome email Brevo.
    • customer.subscription.{created,updated} (upsertSubscription righe 249-300).
    • customer.subscription.deleted (onSubscriptionDeleted righe 302-331) — status=canceled + cancellation email.
    • customer.subscription.trial_will_end (righe 333-353) — pre-renewal email.
    • invoice.payment_succeeded (righe 355-391) — status=active + invoice receipt email.
    • invoice.payment_failed (righe 393-423) — status=past_due + dunning email.
    • customer.{created,updated} (righe 425-439).
  • DLQ implicito via webhook_events.error text (riga 142-146 update on catch).

GAP critici:

  1. Tier caps NON enforced dal webhook checkout.session.completed (checklist riga 77 dice F4 done "schema applicato" ma il webhook non scrive in usage_counters né triggera check_tier_cap). Il tier viene salvato in subscriptions.plan_tier, ma l'enforcement runtime dipende dall'app stessa chiamare check_tier_cap().
  2. Email Brevo gracefully skip se BREVO_API_KEY missing (api\_lib\brevo.ts:43-45). Significa che oggi (key non in Vercel) il cliente paga MA non riceve welcome email → percezione "checkout silenzioso" → support ticket.

3 · Pre-launch BLOCKING vs NICE-TO-HAVE

3.1 · BLOCKING (deve essere fixato per accettare 1° pagamento)

ID Item File / Risorsa Stima Owner
B1 Brevo domain verify TXT su DNS multiverbe.com DNS provider (Register.it/Cloudflare) + Brevo Dashboard 10m Founder
B2 DNS wildcard *.sites.multiverbe.com DNS provider + Vercel project multiverbe-sites (canon: *.multiverbe.app) 20m Founder
B3 Stripe live mode + 9 price IDs Stripe Dashboard + scripts/seed-stripe-prices.ts + Vercel env 1h Founder
B4 Stripe webhook live endpoint registrato Stripe Dashboard > Webhooks > Add app.multiverbe.app/api/stripe/webhook + 9 event subscribe + reveal STRIPE_WEBHOOK_SECRET → Vercel env 15m Founder
B5 Supabase Auth SMTP custom Brevo Supabase Dashboard > Auth > Email > SMTP 10m Founder
B6 Replicate API key in Vercel (Site Builder) Founder signup + REPLICATE_API_TOKEN Vercel env 15m Founder

Nota: tutti BLOCKING sono founder actions, zero codice da scrivere. Il code path è già completo. Eccezione: B4 richiede 1 redeploy post-env update.

3.2 · NICE-TO-HAVE (non bloccano il primo pagamento)

ID Item Note Stima
N1 Settore immobiliari seed in DB top-7 pre-MVP · AI fallback chat copre gap 30m
N2 VITE_ONBOARDING_V2=true Vercel env Senza, default è legacy SetupWizard (uno solo wizard step) 5m
N3 OAuth Google + Apple provider in Supabase Auth Passwordless email funziona da solo 1h
N4 D6 mobile responsive pixel-level audit Checklist scheduled w4 4-6h
N5 Empty state copy su pianeti vuoti (KPI/CRM/Sales) primo accesso Polish UX 2h
N6 Push notification PWA service worker (notify ristoratore booking) Email fallback ok per MVP 4h
N7 Tier caps enforcement runtime (chiamare check_tier_cap da app) Limit AI gen / cold email / contacts 6h
N8 Klaro cookie banner verify su app subdomain Già wired sites-runtime · verify app.multiverbe.app 30m
N9 Sentry alert rules per webhook_events.error IS NOT NULL Cfr. §7 monitoring 1h

4 · 7-day execution plan (Mar 19 Mag → Lun 25 Mag 2026)

Convenzione: F = founder action · C = Claude code/audit · J = joint (founder+Claude in stessa sessione · smoke test, monitoring).

Giorno 1 · Martedì 19 Mag (focus DNS + Brevo)

Time Owner Task Output verificabile
09:00 F DNS Brevo verify TXT (DKIM + SPF + verification) su multiverbe.com Brevo Dashboard > Senders > Domains: status verde
09:30 F DNS wildcard CNAME *.sites.multiverbe.comvercel-dns.com dig *.sites.multiverbe.com answer entry
10:00 F Supabase Dashboard > Auth > Email > SMTP custom (Brevo) Test "Send confirmation email" da Supabase
11:00 C Audit Brevo templates HTML vs wrapMultiverbeEmail() consistency Diff report inline (no commit)
14:00 F Stripe Dashboard > Activate account (founder info + IBAN) Account status: active
15:00 J Test signup founder-non-allowlist mail (Mailosaur o personale) Confirmation email Brevo arriva entro 5 sec
17:00 C Update Multiverbe-Business/ops/first-paying-customer-checklist.md A3+B2 done Progress 78% → 86%

Goal fine-giornata: confirmation email Brevo funziona + DNS sites-runtime up.

Giorno 2 · Mercoledì 20 Mag (focus Stripe live mode)

Time Owner Task Output verificabile
09:00 F Stripe live mode webhook endpoint + reveal STRIPE_WEBHOOK_SECRET Vercel env updated · redeploy ok
09:30 C Re-seed 9 prices live via scripts/seed-stripe-prices.ts (sk_live_*) 9 price_live_* IDs
10:30 F Update 9 Vercel env STRIPE_PRICE_* + STRIPE_SECRET_KEY=sk_live_* Vercel env diff
11:00 F Redeploy production Deploy ok
14:00 J E2E smoke test: signup foo@example.com → onboard → checkout Starter monthly carta 4242 4242 4242 4242 subscriptions row insert + welcome email arriva
16:00 C Verify webhook_events table: 4 eventi (checkout + customer + sub.created + invoice.paid) tutti processed_at NOT NULL DB query screenshot
17:00 C Update checklist F0 (stripe live) + F1-F5 verified Progress 86% → 92%

Goal fine-giornata: 1 pagamento di test reale (carta Stripe real money 1 CHF, refund subito). Welcome email + invoice receipt arrivano.

Giorno 3 · Giovedì 21 Mag (focus polish onboarding + Replicate)

Time Owner Task Output verificabile
09:00 F Replicate signup + $20 credit + API token su Vercel REPLICATE_API_TOKEN Vercel env
09:30 F Vercel env VITE_ONBOARDING_V2=true + redeploy Deploy ok
10:00 J Site Builder smoke test: ristorante demo cliente → 6-stage pipeline → publish Sito nomeristorante.sites.multiverbe.com live
12:00 C Seed settore immobiliari in DB (migration inline read-only audit + propose) Migration draft (NO apply, founder sign-off)
14:00 C Audit kpi_defaults per pianeti vuoti — empty state copywriting Diff proposal (no commit)
16:00 F Founder approva migration immobiliari + Claude applica via MCP Settore visibile in onboarding wizard
17:00 C Stress test webhook idempotency (replay 1 evento Stripe da Dashboard) Log "already processed, skipping"

Goal fine-giornata: pipeline Site Builder funzionante E2E con Replicate + 22 settori in onboarding.

Giorno 4 · Venerdì 22 Mag (focus mobile audit + UAT pre-friend)

Time Owner Task Output verificabile
09:00 C D6 mobile responsive pixel-level audit · iPhone 15 + Pixel 8 Chrome DevTools Audit report coverage/d6-mobile-audit-2026-05-22.md
11:00 C Fix critical mobile bugs (tap target 44px · safe area · canvas fullbleed) PR draft no merge
14:00 F UAT con 1 amico ristoratore Lugano (founder accompagna in persona) UAT note + 5 bug minori identificati
16:00 C Triage UAT bug → fix immediate vs post-launch Priority list
17:00 J Daily standup status: progress %, blockers, decisione "open pubblico domenica?" Decision log entry

Goal fine-giornata: 1 utente reale ha completato signup→checkout→site publish→booking widget visible.

Giorno 5 · Sabato 23 Mag (buffer + content + waitlist)

Time Owner Task Output verificabile
10:00 F Loris cold outreach 30 min — pre-launch teaser "domani apriamo" 30 touch
11:00 C Final UI polish (loading states, error messages, toast copy) Diff inline
14:00 C Sentry config (DSN già setup) — alert rules: 3 alert rules attive
- webhook_events.error IS NOT NULL (Slack)
- auth.users insert rate >10/min (anomaly)
- API /api/stripe/* 5xx rate >1%
16:00 F Cookie banner (Klaro) verify su app.multiverbe.app Klaro corner pill visible · accept/reject ok

Giorno 6 · Domenica 24 Mag (final smoke + lockdown lift go/no-go decision)

Time Owner Task Output verificabile
09:00 J Full E2E rehearsal: signup nuovo account (gmail) → onboarding 4-step → site builder publish → booking widget embed → Stripe checkout Starter annual 4242 → portal cancellation Tutti i passi ok in <30 min
11:00 J Decision meeting: GO / NO-GO lockdown lift Decision log + ticket
14:00 F (if GO) Vercel env updates + Supabase Dashboard toggle (vedi §5 checklist) Lockdown lift checklist done
16:00 J Lockdown LIFT in produzione · smoke test 1° utente esterno Status: "LIVE"

Giorno 7 · Lunedì 25 Mag (post-lift monitoring + Loris activation)

Time Owner Task Output verificabile
09:00 J /security-weekly check Audit log clean
10:00 F Loris activation: campaign "Multiverbe è aperto" 60 touch · LinkedIn + DM IG 60 touch first 24h
11:00 C Monitor Sentry + Vercel runtime logs · cluster errors top 10 Triage report
14:00 C First paying customer playbook: founder-led onboarding call template Multiverbe-Business/ops/first-cust-onboard-call.md
17:00 J Daily retrospective: cosa funziona, cosa rompiamo, priority W22 (25-31 Mag) Decisions log

Goal fine-settimana 25 Mag: lockdown lifted da 24 ore, prime 3-5 signup pubblici tracked, almeno 1 customer in trial (no carta) o paying confermato.


5 · Lockdown lift checklist (ordered per dependency)

Prerequisiti (devono essere TUTTI ✅ prima del lift):

  • [ ] B1 Brevo domain verify done
  • [ ] B2 DNS wildcard sites-runtime done
  • [ ] B3 Stripe live + 9 prices live mode + env updated + redeploy ok
  • [ ] B4 Stripe webhook live endpoint + secret in Vercel
  • [ ] B5 Supabase Auth SMTP custom Brevo
  • [ ] B6 Replicate token in Vercel
  • [ ] E2E smoke test pagamento real money 1 CHF ok (con refund subito)
  • [ ] Founder GO decision logged in Multiverbe-Business/execution/decisions-log.md

Click-by-click sequence (founder lift) · ~10 minuti

  1. Vercel (project multiverbe-app) > Settings > Environment Variables > Production:

    • Set VITE_SIGNUP_ENABLED = true
    • Set VITE_ONBOARDING_V2 = true
    • Tenere VITE_FOUNDER_EMAILS come admin ACL (per harness ?__dev=* accessibili in prod solo founder). NON svuotare la var.
  2. Supabase Dashboard (project yqhixdqipojvgzfadufo) > Authentication > Settings:

    • Toggle "Allow new users to sign up" → ON
  3. Supabase Dashboard > SQL Editor:

    -- Remove the auto-ban trigger (founder_emails table + is_founder() restano per future ACL)
    DROP TRIGGER IF EXISTS auto_ban_non_founder_users_trigger ON auth.users;
    
    -- Optional: unban any test users created during lockdown
    UPDATE auth.users SET banned_until = NULL WHERE banned_until = 'infinity'::timestamptz;
    
  4. Vercel > Deployments > Redeploy production (no cache) → ~2 min build.

  5. Code edit + commit (Claude task, founder approve):

    • frontend\multiverbe-app\src\App.tsx:343-345 — rimuovere blocco if (!isFounderEmail(email)) return <FounderGate email={email}/> + import cleanup.
    • frontend\multiverbe-app\src\components\auth\LoginPage.tsx:378-390 — ripristinare link "Primo accesso? → /?auth=signup" come da commento "Quando lanciamo".
  6. Smoke test post-lift (5 min):

    • Open incognito → app.multiverbe.app → mostra LoginPage senza "Multiverbe è in fase di build"
    • Click "Primo accesso?" → SignupPage con form vero (non SignupClosedPage)
    • Signup email reale (Mailosaur o domain proprio) → conferma email arriva
    • Login → SetupWizardV2 step 1 macro
    • Complete onboarding → PlanetScene 3D · NO FounderGate
    • Click Pianeta Web → SiteBuilderWizard apre
    • Click Billing → PricingTable mostra 3 tier · click "Inizia" → redirect Stripe Checkout reale

6 · Risk register · top 10 prime 24h post-lift

Format: Impact (H/M/L) · Likelihood (H/M/L) · Score = I×L.

# Rischio I L Score Mitigation
1 Brevo email dropped (SPF/DKIM misconfig) → user non riceve confirmation H M 6 Smoke test post-DNS, monitorare Brevo Dashboard > Statistics > Bounce rate <2%
2 Stripe webhook signature fail in live mode (different secret) H M 6 Verify STRIPE_WEBHOOK_SECRET matches Dashboard live mode, replay 1 evento test
3 RLS leak cross-tenant (auth.uid() not wrapped in policy) H L 3 Audit pg_policies via skill multiverbe-supabase-rls-patterns · query <50ms
4 Replicate budget burn ($20 credit) — Stage 4 senza fallback M M 4 Cap budget Replicate Dashboard a $20 hard · alert email
5 auto_ban_non_founder_users_trigger rimosso ma founder_emails tab visibile via RPC is_founder() → enumeration L L 1 REVOKE EXECUTE già fatto in migration _lockdown_function_permissions
6 Bot signup spam su /auth=signup (no captcha) M H 6 Supabase Auth rate limiting native + cap users insert/IP/hour
7 DNS wildcard sites-runtime propaga in 24h ma SSL Let's Encrypt fail H L 3 Vercel handle auto-retry · monitor vercel inspect <domain>
8 Sentry off → errori invisibili primo customer M M 4 Daily check Sentry 9:00 (founder) + alert Slack su 5xx >1%
9 Settore "immobiliari" mancante → cliente Lugano agente real estate quita onboarding M M 4 AI fallback chat copre · seed immobiliari Giorno 3
10 Workspace creazione fallisce silenziosamente (trigger handle_new_user race con RLS) H L 3 Già hardened in migration _team_members_rls_fix_2026_05_13a · monitor logs

7 · Linea critica giornaliera (calendario cron 7 gg)

Daily 9:00 (10 min) · Founder check-in

- Vercel > Deployments > status (last 24h)
- Vercel > Runtime Logs > grep "ERROR" last 24h
- Supabase > Logs Explorer > severity=error last 24h
- Sentry > Issues > unresolved sorted by Last Seen
- Stripe Dashboard > Payments > last 24h (anomaly check)
- Brevo > Statistics > Bounce rate · Spam complaint rate

Daily 18:00 (5 min) · Deploy production verifica

- Vercel > Deployments > Production: last commit deployed?
- app.multiverbe.app → manual visit, login founder, dashboard load <3 sec?
- /api/stripe/webhook OPTIONS 405 (livelock test)

Sentry alert rules raccomandati

# Critical (PagerDuty)
- name: "Stripe webhook 5xx > 1%"
  filter: "transaction:/api/stripe/* AND http.status:5xx"
  threshold: ">1% of events in 5min window"

- name: "Webhook events error backlog"
  filter: "metric:webhook_events.error_count > 5 in 15min"

# Warning (Slack #multiverbe-alerts)
- name: "Signup rate spike"
  filter: "transaction:auth.signUp count > 20/min"
  reason: "bot signup attack"

- name: "Auth.users insert without team_members"
  filter: "metric:orphan_users > 0"
  reason: "handle_new_user trigger failed"

# Info (digest email daily)
- name: "Replicate cost burn"
  filter: "metric:replicate_cost_eur > 5/day"

- name: "AI generation jobs > 100/day"
  filter: "metric:ai_generation_jobs.count > 100"

Status.json bump finale (Mother orchestrator pattern)

Post lockdown lift, bump Multiverbe-Business/source/_meta/status.json:

{
  "phase": "live",
  "lockdown": "lifted",
  "lockdown_lifted_at": "2026-05-24T16:00:00Z",
  "first_paying_target": "2026-06-10",
  "current_paying_customers": 0,
  "trial_active": 0,
  "next_review": "2026-05-25T09:00:00Z"
}

8 · Verifica cross-check vs first-paying-customer-checklist.md

Riconciliazione delle 35 task della checklist business vs questo audit tecnico:

Cat. Done in checklist Verificato da questo audit Note differenza
A 4/5 4/5 (A3 blocking confermato) Allineato
B 2/5 2/5 (B3+B4+B5 deferred ok) Allineato
C 4/6 4/6 (C2+C4 blocking confermato) Allineato
D 5/6 5/6 (D6 mobile audit w4) Allineato
E 4/4 4/4 Allineato (E3 push PWA = nice-to-have)
F 5/5 4/5 (manca F0 live mode!) DRIFT: checklist non considera F0 test→live switch
G 4/4 4/4 Allineato
TOT 28/35 = 80% 27/35 = 77% F0 live mode aggiunto come blocking nuovo (vedi B3-B4)

Raccomandazione: aggiungere riga F0 "Stripe live mode + 9 prices live" alla checklist business. Oggi è implicita ("Stripe Checkout funzionante" F1 done) ma il founder potrebbe non realizzare che sk_test_* non accetta carte reali.


9 · Closing notes

  • Il codice è 78% production-ready. Le 6 BLOCKING sono al 100% founder action (DNS · Dashboard toggle · API keys signup · Stripe activate). Zero codice da scrivere per togliere il lockdown.
  • Il design system (Osservatorio v1.1 · PR #46-#49) + auth-v2-awwwards (PR #50) + sphere-logo 3D (PR #51) sono già "bar incredibile" per il primo customer (cfr. memory project_stream_c_v2_orbi_logos.md + feedback_design_streams_mother_pattern.md).
  • Il founder può lift il lockdown Domenica 24 Mag, prendendo 17 giorni di margin prima del hard gate 10 Giu (vs scenario "lift il 9 Giu" che lascerebbe 1 giorno per qualunque drift).
  • Il rate "+11.5%/giorno medio ultimi 5 giorni" (checklist riga 117) è già 15× sopra il pace richiesto (+0.77%/giorno). Margin di sicurezza enorme — può essere usato per Loris cold outreach W21-W23 con app già LIVE = conversion rate atteso ↑↑↑.

Owner audit: Claude (session P-launch-readiness · D:\Desktop\Multiverbe) Founder review: Gregorio · richiesta entro 18 Mag sera Next refresh: Mercoledì 20 Mag post Giorno 2 (Stripe live mode test) Skill chain usata: multiverbe-stripe-checkout-edges · multiverbe-supabase-rls-patterns · multiverbe-security-ch · multiverbe-ch-compliance