Handoff — Pianeta Web migliorative · sessione fresca (2026-06-10)
Prompt pronto da incollare in una sessione Claude nuova (repo
D:\Desktop\Multiverbe). Aggiorna il handoff del 2026-06-07: M1 e M2(b) sono fatte e live; restano M2(a) · M3 · M4.
Sei un senior staff full-stack + design engineer (livello big-tech) che alza la qualità del Pianeta Web di MULTIVERBE (il SaaS che dà a una PMI CH/UE tutta la presenza digitale, coordinata da un'AI centrale; il "BOS"/bos.multiverbe.com è lo strumento interno, NON il prodotto).
Standard di lavoro (Engineering Charter — memory operating_charter, non negoziabile)
Un task è "fatto" SOLO se: verificato dal vivo con metodo esplicito · piccolo e coeso (no refactor misti a feature) · zero hardcoded customer-facing (ogni stringa vale per QUALSIASI tenant; cornice via i18n, contenuto via DB) · sicuro multi-tenant · advisor Supabase 0 errori · niente regressioni · fix alla radice · merge SOLO a CI verde (squash + --admin è il pattern del repo). UI/copy in italiano. Plan-first per lavori non banali; proponi la scelta migliore con trade-off; verifica lo stato reale (DB/prod/CI) prima di agire; chiedi prima solo per schema-prod, pricing/canon, sicurezza, hard-to-revert, e scelte di identità di marca (logo). Cost-mode: verifica in LOCALE prima di pushare (PR nasce verde), batcha i commit, 1 PR per unità coerente, non ri-triggerare la CI a vuoto; i gate non si toccano. Per ogni scelta di stack/libreria valida sul web l'ultimo livello 2026.
⚠️ LEZIONE CRITICA di questa sessione — "merged ≠ live"
Il deploy dei siti era rotto da #338 (2026-06-07): l'Ignored Build Step Vercel di multiverbe-sites (git diff HEAD^ HEAD --quiet -- sites-runtime) gira dalla Root Directory del progetto (sites-runtime/), quindi il pathspec sites-runtime non matchava nulla → exit 0 → OGNI deploy siti cancellato. La PR #346 (M1 i18n) è rimasta mergiata ma non live per 3 giorni. Fix in #350: ignoreCommand in-repo in sites-runtime/vercel.json con pathspec repo-relative :/sites-runtime (:/ = dalla radice repo, vale da qualunque cwd). CONSEGUENZA OPERATIVA: dopo OGNI merge su sites-runtime, verifica che il deploy prod abbia davvero buildato (Vercel MCP get_project multiverbe-sites → latestDeployment.readyState deve diventare BUILDING→READY, NON CANCELED) e che la modifica sia live (curl prod). ⚠️ DA FARE: verificare se multiverbe-app ha lo stesso bug (-- frontend/multiverbe-app dalla sua Root Directory); sembra deployare, quindi forse ha Root Directory diversa — controlla frontend/multiverbe-app/vercel.json e l'impostazione dashboard; se affetto, stesso fix :/frontend/multiverbe-app.
Verità architetturale (tienila)
Nessun codice per-sito. UNA runtime condivisa (sites-runtime, Next.js 16 + React 19, Vercel project multiverbe-sites, region fra1) serve tutti i *.multiverbe.app via routing per subdomain (sites-runtime/proxy.ts) + ISR. Il sito = DATI in Supabase (sites/site_subdomains/site_pages/site_blocks/site_block_translations/site_assets/site_constitutions). Modificare i dati = UPDATE righe + /api/revalidate (REVALIDATE_SECRET). Progetto Supabase yqhixdqipojvgzfadufo (eu-central-1). Unico sito pubblicato = trattoria-bellinzona-demo (tier business, tema CHIARO bg #FAF5EC / testo #241C17; locale it/de/fr/en presenti). Le route [subdomain]/[locale]/… sono dinamiche (ƒ).
Base già fatta e VERIFICATA LIVE (NON rifarla)
- #307 watermark "Powered by Multiverbe" (logo brand + wordmark EN monolingua, micro-press Apple, UTM, gating per-tier Starter/Pro mostrano · Business no). Asset raster
sites-runtime/public/brand/multiverbe-mark.png. - #311 CTA sticky mobile hero si ritrae sul footer.
- #321 footer/cookie/legali page-locale-aware (middleware inoltra
x-multiverbe-locale) + cornice Events/Menu localizzata viasites-runtime/lib/sections/i18n.ts. - #346 (M1, QUESTA SESSIONE, LIVE) — chiusura i18n di tutta la cornice sezioni:
lib/sections/i18n.tsesteso (it/de/fr/en) + helperweekdayLong/weekdayShort/galleryCategory;localethreadato daSectionRenderer→ dispatcher → varianti → primitive per Booking, Hero, Gallery, Contact, ServicesPricing, Legal (+ 2 aria-label Menu).buildWeekScheduleresta dati puri; giorni/"Chiuso"/"oggi" risolti al render. Verificato SSR/de: Öffnungszeiten/Geschlossen/Montag/Einführung/Karte anzeigen, zero IT residuo. - #350 (deploy fix, LIVE) — vedi lezione sopra.
- #351 (M2b, LIVE) — rimosse 7 dipendenze morte da
sites-runtime:@react-three/fiber,@react-three/drei,three,mapbox-gl,@vercel/og,zod,@types/mapbox-gl(−1105 righe lockfile). Restano usate:gsap+lenis(SmoothScroll),framer-motion(Hero/Menu),@supabase/supabase-js,clsx,next,react,react-dom,sharp,tailwind-merge. Nota: la mappa Contact usa un iframe OpenStreetMap (Contact/_MapEmbed.tsx), non mapbox;README.md"Fase 7 Mapbox" +.env.example NEXT_PUBLIC_MAPBOX_ACCESS_TOKENsono ora reference stale (pulizia opzionale).
DA FARE — in quest'ordine
⚡ M2(a) — Smooth-scroll anchor (Lenis)
I CTA puntano a #prenota/#menu ma lo scroll è il jump nativo: Lenis (sites-runtime/components/effects/SmoothScroll.tsx) non è agganciato agli hash. Oggi SmoothScroll crea Lenis guidato dal ticker GSAP, MA fa return early sotto prefers-reduced-motion (niente Lenis) e non intercetta i click sugli anchor.
Cosa fare: in SmoothScroll, aggancia i click su a[href^="#"] allo lenis.scrollTo(target); rispetta prefers-reduced-motion (niente smooth → lascia il jump nativo); mantieni il fallback no-JS (l'href resta); aggiorna l'hash (history.pushState) + sposta il focus sul target (tabindex=-1 + focus({preventScroll:true})) per a11y; gestisci solo left-click senza modificatori; offset per eventuale header sticky (controlla se c'è un header fisso nel layout [subdomain] o se le sezioni hanno scroll-margin-top). Valida sul web l'API Lenis 2026 (npx opensrc path lenis --cwd sites-runtime; versione installata lenis ^1.3.5: firma scrollTo, opzioni offset/onComplete).
Done: smooth-scroll verificato con e senza reduced-motion (verifica interattiva in browser — vedi metodo), next build verde, nessuna regressione. Una PR a sé.
⚡ M3 — Consolida la i18n del runtime (architetturale · PLAN-FIRST)
Oggi la i18n è sparsa: lib/sections/i18n.ts (cornice sezioni, nuova) · switch cookieLabel in components/layout/SiteFooter.tsx · stringhe in components/Compliance/CookieBanner.tsx · locale in proxy.ts · (lato app) booking/_lib/i18n.ts.
Obiettivo: un meccanismo unico, tipizzato, moderno in lib/i18n/. PLAN-FIRST: presenta al founder la scelta con trade-off + raccomandazione → next-intl (App Router) vs dizionario tipizzato in-house. VINCOLO: il runtime ha già il proprio routing locale (subdomain + [locale] + x-multiverbe-locale) → se next-intl, usane solo il core (getTranslations/useTranslations) SENZA il suo middleware di routing (eviti conflitti con proxy.ts). Confine: le label di cornice/UI stanno nel codice (i18n tipizzata); il contenuto del tenant resta data-driven in site_block_translations (non spostarlo). M1/M2 possono confluire. Valida next-intl 2026 sul web (versione corrente, compatibilità Next 16/React 19, se serve ancora un plugin). Done: una sola fonte di verità i18n, tipizzata, tsc verde, render SSR identico nelle 4 lingue.
🎨 M4 — Logo SVG ufficiale + watermark background-adaptive (FOUNDER-GATED)
Il watermark usa un raster (sfera nera + occhio ciano): su tenant a tema scuro la sfera si fonde col fondo. Canon canon/12-design-system-orbi-logos.md §"Logo di Multiverbe": non esiste un SVG pulito; colori #0EA5E9 / #22D3EE / #4ADE80; servono master SVG + 3 varianti + light/dark. Task aperto: execution/logo-ufficiale-task-2026-06-06.md. Il logo è identità di marca = territorio founder (come i pianeti 3D): non ridisegnare unilateralmente.
Cosa fare: (1) proponi 2-3 mark monocromo inline SVG currentColor (riproduzione fedele della sfera-occhio, NON redesign; riferimento frontend/multiverbe-app/src/components/auth/SolarSystemPattern.tsx) leggibili su QUALSIASI fondo; (2) ottieni l'OK del founder; (3) wiralo in sites-runtime/components/layout/SiteFooter.tsx (inline SVG che eredita currentColor al posto dell'<img> raster), mantenendo press Apple + gating per-tier + UTM + raster come fallback. Verifica leggibilità su CHIARO e SCURO (per lo scuro: flippa temporaneamente i design_tokens del demo a un set scuro in DB, poi ripristina; oppure pagina di prova locale con fondo scuro). Done: watermark nitido su tenant chiaro E scuro, verificato a vivo; founder ha approvato il mark.
Metodo di verifica LOCALE (sites-runtime) — memory reference_live_verify_behind_auth_chrome_mcp §sites-runtime
I siti sono pubblici → verificabili in locale. sites-runtime usa npm (non pnpm): npm ci nel worktree funziona (~minuti, già fatto in questa sessione → node_modules presente). Girano npx tsc --noEmit, node scripts/check-cwv-budget.mjs, npm run build. Per renderizzare un tenant: build prod + npm run start (porta 3001) + host *.localtest.me → curl http://trattoria-bellinzona-demo.localtest.me:3001/de. Env reale a build-time via .env.local (gitignored): NEXT_PUBLIC_SUPABASE_URL=https://yqhixdqipojvgzfadufo.supabase.co + NEXT_PUBLIC_SUPABASE_ANON_KEY=<anon, Supabase MCP get_publishable_keys>. ⚠️ output:'standalone' impedisce al middleware di girare sotto next start → per il test SSR commenta temporaneamente output:'standalone' in next.config.mjs, builda, testa, ripristina via git checkout next.config.mjs (verifica git diff). Stringhe i18n = SSR → ground truth via curl + grep; per l'interattività (smooth-scroll M2a) usa Playwright o Chrome/Claude-Preview MCP contro next start. Pulizia: rimuovi .env.local; MAI Remove-Item *.png alla radice.
CI / merge (cost-mode)
Sul PR (solo sites-runtime/**) girano security-audit (Semgrep + Trivy + TruffleHog + gitleaks + npm/pip audit) + Vercel multiverbe-sites (= npm run build: CWV prebuild + next build + typecheck) = il gate type/build. frontend-ci/planet-system-ci/e2e-smoke NON triggerano su sites-runtime. Dopo il fix #350, sul PR il check "Vercel – multiverbe-sites" mostra "Deployment has completed" (builda) quando tocchi sites-runtime — NON più "Canceled". Merge: gh pr merge <n> --squash --admin a check verdi (mergeStateStatus BLOCKED = branch protection, si supera con --admin). ⚠️ Il branch del worktree traccia origin/master (upstream misconfigurato) → pusha SEMPRE esplicito: git push -u origin <branch>, mai un bare git push. Messaggi multi-riga via git commit -F <file> e gh pr ... --body-file <file>. Chiudi il commit con Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> e il body PR con la riga Claude Code. Dopo il merge su sites-runtime: verifica il go-live (Vercel MCP get_project → readyState BUILDING→READY + curl prod).
Primo passo
Inizia da M2(a) (autonomo). Leggi SmoothScroll.tsx + il layout [subdomain] (header sticky? scroll-margin?), valida l'API Lenis 1.3 via opensrc, implementa, verifica in browser con e senza reduced-motion, build verde, PR squash. Poi M3 (plan-first, fermati per la scelta libreria) → M4 (founder-gated sul mark).
Pending bookkeeping (non perdere)
- Aggiorna BOS: marca M1 + M2(b) come done/live nello stato Pianeta Web; il fix #350 (deploy siti) è una nota infra rilevante. (Repo
Multiverbe-Business, push su origin = deploy bos.multiverbe.com.) - Verifica
multiverbe-appper lo stesso bug ignoreCommand (vedi lezione critica). - Cleanup opzionale reference stale Mapbox (
README.mdFase 7 +.env.example).
✅ STATO ESECUZIONE — aggiornato 2026-06-10 (sessione frosty-mirzakhani)
- M1 (#346) + M2(b) (#351): confermati LIVE in prod.
- M2(a) — smooth-scroll anchor (Lenis): ✅ PR #356 MERGED + LIVE. Listener delegato sul
documentsua[href^="#"]→lenis.scrollTo; Lenis 1.3 sottrae da sé loscroll-margin-topdel target (.mv-section=5.5rem) → stesso offset del jump nativo (una sola fonte di verità); reduced-motion → jump nativo (l'effect esce prima); a11y focus sul target +history.pushState; bail sudefaultPrevented(coesiste con lo scroll-spy del Menu). Verificato a vivo sul dominio prodtrattoria-bellinzona-demo.multiverbe.appcon Chrome: normal=smooth (53 frame distinti) + offset esatto + focus + hash; reduced=istantaneo. Deploy prod confermato buildato (NON canceled → fix #350 regge). - Pending "verifica multiverbe-app stesso bug ignoreCommand": ✅ RISOLTO —
frontend/multiverbe-app/vercel.jsonha giàignoreCommand ... -- :/frontend/multiverbe-app(repo-relative, fix #355). Sul PR #356 il checkVercel – multiverbe-appha mostrato correttamente "Canceled by Ignored Build Step" (PR tocca solosites-runtime). - M3 — i18n del runtime: DECISO dal founder 2026-06-10 → dizionario tipizzato in-house in
lib/i18n/(NON next-intl). Motivo: label statiche di cornice (contenuto vero data-driven nel DB), zero nuove dipendenze (cost-mode + leanness post-M2b), estende il pattern esistente. next-intl validato compatibile Next16/React19 ma scartato (overkill, +1 dep). Blast radius misurato: 22 file importano@/lib/sections/i18n;SiteLocalein 14 file (def.lib/data/fetch-page.ts); le label dibuild-nav.ts/SiteFooter/CookieBannersono locali. Approccio low-churn:lib/i18n/SSoT (locales + messages + index t()+formatter) e re-export attraverso i path esistenti per non toccare i 22 file. Da implementare (refactor architetturale → meglio in sessione fresca dedicata; verificare SSR identico it/de/fr/en con curl+diff). - M4 — logo SVG watermark background-adaptive: PENDING, founder-gated sul mark (proporre 2-3 mark monocromo
currentColor, OK founder, poi wirare inSiteFooter.tsx). - Cleanup Mapbox: ✅ PR #363 MERGED 2026-06-12 (
bae8ee0) — rimosse le 3 reference stale post-#351: README roadmap Fase 7,NEXT_PUBLIC_MAPBOX_ACCESS_TOKENin.env.example, bullet commento_MapEmbed.tsx. Zero codice eseguibile. - M3 + M4 → attività suggerita (chip) creata per sessione fresca con brief completo (decisione i18n + gate founder logo + lezioni di verifica). Niente altro in coda su questo stream.