PMS Sessione D · Strangler retrofit reale · Scoping doc

Status: READ-ONLY scoping · NO execution Owner execution: Joint (Claude code + founder UAT) Stima: 8-12h distribuite in 2-3 sessioni Pre-requisite: founder disponibile per UAT 30-45 min tra fasi Rollback: feature flag ?ff=pms.web.enabled:off (5 sec) Action item BOS: pms-strangler-real-retrofit-web-booking-2026-05-31


Contesto · cosa è già fatto (Sessioni A/B/C)

Le 3 sessioni precedenti del PMS retrofit sono mergiate a master via PR:

Sessione PR Cosa fatto
A · npm→pnpm conversion PR #57 frontend/multiverbe-app ora workspace member pnpm-workspace.yaml
B · planet-core + testkit PR #56 @multiverbe/planet-core registry/loader/event-bus/feature-flags · @multiverbe/planet-testkit 18 contract test
C · sector-adapters + GDPR Art 9 PR #60 @multiverbe/sector-adapters 7 settori · DB migration audit_log_gdpr_art9 + patient_pseudo_map (medici)

6 packages esistono come scaffold:

  • @multiverbe/contracts v0.1.0 — defineModule, PlanetContext, PlanetManifest, EventMap
  • @multiverbe/planet-core v0.1.0 — registry + loader + event-bus + feature-flags + telemetry + healthcheck
  • @multiverbe/planet-testkit v0.1.0 — contract suite vitest runner
  • @multiverbe/planet-web v0.1.0 — scaffold conforme (NON wirato)
  • @multiverbe/planet-booking v0.1.0 — scaffold conforme (NON wirato)
  • @multiverbe/sector-adapters v0.1.0 — 7 settori · 4 adapter completi (ristorazione/parrucchieri/studi-medici/hotel) + 3 skeleton

Stato reale: i 2 scaffold planet-web + planet-booking esistono ma il dashboard frontend NON li monta. Il routing avviene tutto via PlanetScene + PlanetPanel con state Zustand (selectedPlanet + activeSatellite). Nessun bridge tra l'app esistente e i package PMS.


Scope reale Sessione D

Cosa serve fare (in ordine logica esecuzione)

Fase 1 · Bootstrap registry nel frontend (1.5h)

File: frontend/multiverbe-app/src/main.tsx

Oggi: monta solo React + AuthProvider + App.

Da fare:

  1. Import createRegistry, registerAdapter da @multiverbe/planet-core
  2. Lazy-import module da @multiverbe/planet-web + @multiverbe/planet-booking
  3. Crea istanza PlanetContext con:
    • workspaceId da Zustand store (useWorkspace hook)
    • supabase client esistente
    • featureFlags.isOn(key) reading URL ?ff=key:on/off + localStorage fallback
    • telemetry no-op stub (Sentry wrapper next phase)
    • logger console wrapper
    • eventBus localmente Map-based (cross-planet broadcast)
  4. Register entrambi i moduli + init()
  5. Espone registry via React Context (PMSContext) sotto AuthProvider

Output verificabile: console.log in dev mode mostra "planet-web ok · planet-booking ok · contract tests pass".

Fase 2 · Feature flag URL handler (30 min)

File: nuovo frontend/multiverbe-app/src/lib/pms/feature-flags.ts

Implementa:

  • parseFFFromURL()?ff=pms.web.enabled:off,pms.booking.enabled:onMap<string, boolean>
  • isFFOn(key) → URL > localStorage > manifest default
  • setFF(key, value) → localStorage write + emit event

Wire in main.tsx bootstrap.

Fase 3 · Bridge Web planet (3h)

File: frontend/multiverbe-app/src/components/layout/PlanetPanel.tsx

Oggi: switch case routing web.site → SiteBuilderLauncher, web.analytics → AnalyticsPage, ecc.

Da fare nel SatelliteContent (function inside PlanetPanel):

if (destination === 'web') {
  const webModule = useRegistryModule('planet-web');
  if (webModule && featureFlags.isOn('pms.web.enabled')) {
    // PMS-managed mount
    const Mount = webModule.mount({ slot: { name: satelliteId || 'default' } });
    return <Mount />;
  }
  // FALLBACK: existing component chain (current code path)
  switch (satelliteId) {
    case 'site': return <SiteBuilderLauncher />;
    case 'analytics': return <AnalyticsPage />;
    // ... resto invariato ...
  }
}

Pattern: PMS-first con fallback istantaneo a legacy. Quando il mount() PMS è incompleto, ritorna null → fallback automatico al switch case esistente.

Per ora il mount() PMS è un placeholder div (WebPlaceholderComponent in planet-web/src/index.ts:109). Sessione D NON sostituisce il mount() con il vero codice — quello è Sessione E future.

Sessione D fa solo il bridge: feature flag → either PMS path or legacy.

Fase 4 · Bridge Booking planet (1.5h)

Stesso pattern di Fase 3 ma per destination === 'booking'.

Fase 5 · Contract test integration (1h)

File: frontend/multiverbe-app/src/test/pms-bootstrap.test.ts (nuovo)

Test integration che verifica:

  • Registry bootstrap senza errori
  • module.init() ritorna Result.ok
  • Feature flag URL parsing
  • Healthcheck pass per entrambi i planet

Run: pnpm --filter @multiverbe/dashboard test

Fase 6 · UAT + smoke E2E (1-2h founder)

Joint session:

  1. Founder login app · navigate planet Web → satellite Site → verifica mount placeholder
  2. URL ?ff=pms.web.enabled:off · refresh → verifica fallback a SiteBuilderLauncher legacy
  3. Stesso pattern pianeta Booking
  4. Run contract tests pass

Fase 7 · Commit + push (30 min)

Commit message structurato. Push a feature branch feat/pms-sessione-d-bridge. UAT founder. Merge.


Rischi identificati · mitigation

R1 · Collision con design system attivo (HIGH risk)

Issue: founder sta attivamente modificando App.tsx, PlanetPanel.tsx, sta aggiungendo GlassSheetOverlay.tsx (v4.6). Touchare gli stessi file = merge conflict garantito.

Mitigation:

  • ESEGUI Sessione D SOLO quando founder PAUSA il design work
  • Joint scheduled session 4h continuative
  • Lavora su branch dedicato feat/pms-sessione-d-bridge (no master)
  • Bridge logic concentrato in 1-2 file (main.tsx + PlanetPanel.tsx 2 location switch case)

R2 · React Context overhead bootstrap (MID risk)

Issue: aggiungere PMSContext + registry init in main.tsx ritarda il first paint.

Mitigation:

  • Lazy-init: registry created at first useRegistryModule() call, NOT at bootstrap
  • defer planet module imports via dynamic import()
  • Measure bundle: planet-core + 2 manifests = ~5KB gzipped budget

R3 · Feature flag URL ?ff= conflict con altri URL params (LOW)

Issue: parse del query string ?ff= potrebbe collidere con altri params usati (?auth=, ?__dev=, ?siteId=, ?t=).

Mitigation: namespace dedicato ?ff= · multi-value separato da , · case-insensitive · validation in parseFFFromURL().

R4 · Contract tests fail al primo runtime (MID)

Issue: i contract test pre-esistenti su planet-web/planet-booking testano in isolation con fake context. Quando integrati nel main.tsx con context reale (Supabase + workspace), potrebbero fallire per ragioni di env (anon key, RLS, ecc.).

Mitigation:

  • Eseguire contract test in pms-bootstrap.test.ts con mock Supabase
  • Init test isolato dal context reale
  • Fail-safe: se module.init() fail, log warning ma NON crash bootstrap (app continua con fallback)

R5 · Vercel build fail per workspace dependencies (LOW)

Issue: frontend/multiverbe-app/package.json deve dichiarare @multiverbe/planet-web come workspace:* dep. Se manca, Vercel build fail dipende dalla pnpm-lock.

Mitigation: aggiungere in dependencies:

"@multiverbe/planet-web": "workspace:*",
"@multiverbe/planet-booking": "workspace:*",
"@multiverbe/planet-core": "workspace:*",
"@multiverbe/contracts": "workspace:*"

Run pnpm install per rigen lockfile · commit lockfile.


Out-of-scope Sessione D (post-MVP)

NON facciamo in Sessione D:

  • ❌ Implementare il vero mount() di planet-web con sites-runtime real wiring (Sessione E)
  • ❌ Migrare event-bus a Sentry/PostHog telemetry (Sessione F)
  • ❌ State migrations runtime su switch versione manifest (Sessione G)
  • ❌ Cross-planet event subscription (es. web:site:publishedcrm:contact:created) (Sessione H)
  • ❌ Server-side PMS (edge function executor) (Sessione I post-launch)

Sessione D è ESCLUSIVAMENTE bridge + feature flag. Tutto il vero codice business resta in frontend/multiverbe-app/src/components/* + src/features/booking/* come oggi.


Acceptance criteria

Sessione D considerata "done" quando:

  • [ ] pnpm install da root passa zero errori (workspace + lockfile coerenti)
  • [ ] pnpm --filter @multiverbe/dashboard test pms-bootstrap.test.ts pass
  • [ ] pnpm --filter @multiverbe/planet-web test (contract suite) pass
  • [ ] pnpm --filter @multiverbe/planet-booking test pass
  • [ ] App login + navigate planet Web (default ?ff=pms.web.enabled:on) → mostra placeholder PMS div
  • [ ] App login + navigate planet Web con ?ff=pms.web.enabled:off → mostra SiteBuilderLauncher legacy (fallback)
  • [ ] Idem pianeta Booking
  • [ ] Vercel build production pass post-merge
  • [ ] Browser console: zero errors related to PMS bootstrap

Decision needed dal founder · quando eseguire

Sessione D è pronta da eseguire MA collide con il tuo lavoro design attivo (v4.5/v4.6 glass redesign). Opzioni:

  1. Defer post-lift launch (24 Mag+) — più sicuro, no merge conflict. Dopo primo paying.
  2. Window dedicato 4h continuative pre-launch — solo se design v4.x è stabile e tu sei disponibile UAT.
  3. Mai — accettare che planet-web + planet-booking scaffold restino bridged solo via tests, mai mount runtime. Costo: il PMS resta architettura "future" non "today" → quando vorrai aggiungere planet HR (Q3) o Finanza (2027), dovrai fare bridge allora.

Raccomandazione: opzione 1 (defer post-lift). Il PMS retrofit non sblocca primo paying, e il design v4.x è ancora in active iteration.


Last update: 2026-05-18 · scoping post-N1 immobiliari seed Owner: Gregorio (decisione timing) · Claude (execution quando autorizzato)