Content Studio + App — Backlog di bonifica (TUTTE le criticità, una a una)

Direttiva founder 2026-06-02: "TUTTE LE CRITICHE DEVONO ASSOLUTAMENTE ESSERE RISOLTE", in una sessione dedicata che bonifica l'app di ogni problema (legale, strutturale, costi, prodotto) e rende reale il routing sovrano così "dati in Europa" diventa vendibile. Fonte: spec content-studio-spec-2026-06-02.md + 3 critiche avversariali (legale CH/EU, CFO, prodotto), verificate contro codice e DB yqhixdqipojvgzfadufo il 2026-06-02. Legenda owner: S3 = sessione Content · S2 = sessione Sales · X = cross-cutting app-wide · F = richiede azione/infra founder · DEC = bloccato da una delle 7 decisioni. Legenda stato: ☐ aperto · ◐ in corso · ☑ risolto.


A. LEGALE / COMPLIANCE (Critica 1) — priorità massima, è la liability penale CHF 250k

ID Problema Gravità Owner Stato
L1 pseudonymizePii() non è chiamato da nessuna parte nel codebase (codice morto). Nessun trattamento verso provider US è oggi pseudonimizzato. Serve gate centralizzato fail-closed nel provider-registry, applicato a TUTTI gli endpoint. Gravissima X (S3 PR#140: cablato nel content endpoint via api/content/_lib/pseudonymize.ts; gate app-wide + NER nomi propri aperti)
L2 api/sales/generate-outreach.ts LIVE: JWT non verificato (IDOR) + invia nome/azienda/ruolo/città di decisori reali a Mistral senza pseudonimizzazione né base legale (manca LIA). Fix: verifyUser + Legitimate Interest Assessment + pseudonimizzazione. Gravissima S2
L3 Prompt grezzo scritto in content_generations.prompt (anche dal mio generate-text.ts): PII in chiaro nel DB + verso provider. Fix: solo versione pseudonimizzata/hash; trigger che rifiuta prompt non marcato. Alta S3 (S3 PR#140: prompt pseudonimizzato prima di store/provider, metadata.pii_pseudonymized; trigger DB = follow-up)
L4 Riuso di audit_log_gdpr_art9 (registro dati sanitari) per attestazioni contenuti: finalità sbagliata, inquina il registro Art.9. Fix: tabella content_audit_log dedicata append-only. Grave S3/X
L5 legal_acceptances.document_type bloccato a 4 valori sia DB sia tipo TS: voice_clone_consent e content_studio_tos non registrabili. Finché aperto, nessun cloning ha base legale. Fix: estendere entrambi + test gate. Grave X
L6 Routing sovrano EU = 0 righe ma posizionato come feature vendibile. Vendere "dati in Europa / a norma" ora = pubblicità potenzialmente ingannevole + responsabilità verso cliente. Fix: (a) bloccare in prod l'endpoint consumer Google hard-fail; (b) costruire il routing EU reale; (c) non usare il claim finché non testato. Grave X / F (S3 PR#140: (a) fatto — guardia hard-fail consumer in prod, 503 sovereign_routing_required. (b) routing Vertex EU reale = SOV1, aperto. (c) claim non vendibile finché aperto)
L7 modality (telemetria proposta) usa valori voice/cgi non allineati al CHECK reale (audio sì). Fix: allineare vocabolario prima della migration. Media S3 (S3 PR#140: trovato e fixato bug VIVO collegato — generation_type: 'social_text' non ammesso dal CHECK → era insert-fail garantito; corretto a 'text'. Vocab modality per la migration resta)
L8 ToS promette indennizzi upstream incerti ("le garanzie del fornitore si estendono a te"). Fix: "ti giriamo le garanzie ove esistenti e nei loro limiti, senza garanzia di Multiverbe". Far passare da legale + humanizer. Media F/X
L9 Notifica 30gg sub-processor (revFADP art.9) senza registro versionato accettato per-workspace. Fix: versionare la sub-processor list in legal_acceptances. Media X
L10 EXIF non strippato di default (GPS = dato personale) + nessun rilevamento volti. Fix: EXIF-strip server-side obbligatorio prima del dispatch; regola difensiva "foto utente → solo Vertex EU" finché manca face-detection. Media X

B. SOVRANITÀ REALE — "vendere dati in Europa" (il cuore della direttiva)

ID Problema Owner Stato
SOV1 Migrare generate-text.ts da endpoint consumer Google (generativelanguage.googleapis.com) a Vertex AI EU con service account. È il P0 che protegge moat + liability. S3 + F (GCP)
SOV2 Migrare la generazione immagine (generate-social-media, oggi Gemini consumer) a FLUX Azure EU o Imagen Vertex EU. X + F (Azure/GCP)
SOV3 Costruire il gateway/provider-registry fail-closed (api/content/_lib/provider-registry.ts): region + flag legali + pseudonimizzazione obbligatoria pre-dispatch. Unico punto di verità. X
SOV4 Forzare EU Data Boundary su Azure per FLUX (ticket Microsoft; default è "global"). F
SOV5 Firmare DPA con Google/Microsoft/Mistral/fal.ai + versionare sub-processor + TIA per ogni extra-UE. F
SOV6 Flag "modalità sovranità totale" per-tenant + telemetria sovereign/provider_region → registro nDSG automatico. X
SOV7 Solo DOPO SOV1-SOV6 testati: sbloccare il claim marketing "dati in Europa / a norma". F/DEC#5

C. COSTI / PRICING (Critica 2) — proteggono il margine

ID Problema Owner Stato
C1 Video contati in secondi (Pro 40s / Business 120s), non in clip. Neutralizza l'errore Veo (15s = CHF 1.98 non 1.06). DEC#2 + X
C2 Derivazione multi-ratio del visual = 0.25 unità/ratio (il re-frame rigenera, non è gratis). Testo per N canali resta gratis. Video: un solo ratio per generazione. X
C3 Upscale = 0.5 unità foto nel counter images_count (oggi manca il counter). X
C4 Separare cap testo vs media (ai_text_generations vs ai_media_generations): oggi il testo satura ai_generations. X
C5 Hard-cap sui media pesanti già in Fase A (non Fase B). CGI fuori dal mensile finché COGS non confermato <$6. DEC#3 + X
C6 Backstop COST_CAP_CHF=50 è globale (tutti i pianeti) e soft: il solo Content Business lo satura. Hard-cap media da subito + budget hard in modalità Auto. X
C7 Lock-in Veo (unico video sovrano EU, prezzo volatile): floor di margine sui pacchetti video che si ricalcola se Google muove il listino. X/F
C8 Rollover quota max 2x (un churner non accumula 200 foto per bruciarle prima di disdire). X
C9 A/B reale qualità voce IT (Voxtral vs ElevenLabs) prima di mettere Voxtral default. F/X

D. PRODOTTO / STRUTTURA (Critica 3) — completezza dell'esperienza

ID Problema Sev. Owner Stato
P1 Riconciliare social_posts (publish) con content_items (concept) invece di biforcare. Migrazione/decisione scritta PRIMA della migration. P0 bloccante X
P2 Immagini live (generate-social-media) transizionate al gateway sovrano (= SOV2). P0 X
P3 Agganciare l'intelligence già viva (analyze-market-trends + analyze-competitors) come 4° slot "perché ora" del context-assembly. P0 S3/X
P4 Editing video post-generazione (trim, cover frame, on/off sottotitoli, musica): gratis client-side; ri-gen = 1 unità. P1 X
P5 Onestà UX: derivazione ratio sul video non è gratis (un solo ratio per generazione, gli altri costano). P1 X
P6 "Correggi questo" (FLUX Kontext edit mirato) vs "Rigenera tutto". + micro-ragione di scarto che nutre il brand. P1 X
P7 Piano mensile: approva-in-blocco settimanale (non 30 push singoli). P1 X
P8 Brand auto-learning: ogni approvazione/scarto/edit scrive segnale in content_brand_memory → il brand si costruisce anche per chi salta l'onboarding. Antidoto allo slop. P1 X
P9 Stato "nessun canale connesso" nel flusso crea (gate soft + fallback download manuale). P1 S3/X
P10 Smontare i controlli tecnici già spediti in ContentCreatorPage (selettore ratio, style, checkbox "doppio passaggio Gemini", prompt visivo): contraddicono i "3 tap". P2 S3
P11 Preset per scopo: max 4-5 per settore, ordinati per rilevanza temporale + "Altro: dimmi cosa vuoi" (riusa sector-detection). P2 S3
P12 Lingua: aggiungere fr (Romandia) + trattare la lingua come dimensione di derivazione (1 concept → varianti lingua). P2 X
P13 Gate review differenziato: video = player full-screen, approva dopo la visione (play loggato = bonus attestazione Art.50(4)). P2 S3
P14 Ritorno metriche nel pianeta dopo l'handoff (read-only da Marketing) → chiude il ciclo crea→pubblica→impara, nutre il brand. P2 X

Ordine di esecuzione consigliato (il più severo, dalle critiche)

  1. Bloccare in prod l'endpoint Gemini consumer (hard-fail) — L6/SOV1.
  2. Gate pseudonimizzazione centralizzato fail-closed prima di ogni dispatch, su TUTTI gli endpoint — L1.
  3. Fix generate-outreach.ts (verifyUser + LIA + pseudonimizzazione): è live — L2.
  4. Smettere di scrivere prompt grezzo nel ledger — L3.
  5. Decidere la riconciliazione social_posts/content_items prima della migration — P1.
  6. Separare l'audit contenuti da audit_log_gdpr_art9 — L4. Sbloccare document_type (DB+TS) — L5.
  7. Costruire il routing EU reale (Vertex/Azure) + provider-registry — SOV1-SOV3. Solo dopo, vendere la sovranità — SOV7.
  8. Correzioni costo (cap video in secondi, separare cap testo/media, hard-cap media Fase A) — C1/C4/C5.
  9. Completezza prodotto P3→P14 in ordine di severità.

Dipendenze dalle 7 decisioni founder (spec §10)

  • DEC#2 (quota video in secondi) → sblocca C1.
  • DEC#3 (CGI inclusa/add-on) → sblocca C5.
  • DEC#5 (quando vendere la sovranità) → governa SOV7 e i claim marketing.
  • DEC#6 (chi fixa generate-outreach.ts) → L2.
  • Le altre (DEC#1 Content da Pro, DEC#4 quota voce, DEC#7 Higgsfield) non bloccano la bonifica tecnica.

Stato dopo la sessione di bonifica · 2026-06-02 · PR #145

Le tabelle sopra sono lo stato as-found. Questa sezione è la verità post-sessione. Repo: Multiverbe · PR https://github.com/metaverbe-hub/Multiverbe/pull/145 (branch claude/hungry-chandrasekhar-4e05b6). Nota importante: il codice merged di PR #140 non conteneva L1/L3/L6/L7 (il backlog li dava per fatti — era drift). Verificati sul codice reale e chiusi qui. generation_type:'social_text' violava il CHECK reale → era un insert-fail vivo, ora text.

Legenda: ☑ chiuso · ◐ codice/schema pronto, resta applicazione o follow-up · 🔒 bloccato-da-founder (infra/legale).

ID Stato Dettaglio + ref
L1 Gate PII centralizzato fail-closed nel gateway + consolidato app-wide (un solo gate canonico api/_lib/pii.ts, copie inline rimosse, dead code frontend eliminato). La critica "nulla è pseudonimizzato" era un falso-negativo da name-search: sole-ask/sole-crm/sector-deduction già pseudonimizzavano. Follow-up: NER nomi propri liberi.
L2 generate-outreach hardened: verifyUser (era JWT decode-senza-verify → IDOR), route Mistral FR sovrana, LIA scritta (ops/lia-cold-outreach-2026-06-02.md), audit log con base giuridica. In prod-ready appena confermi ZDR+DPA Mistral (infra B1).
L3 Ledger salva il prompt pseudonimizzato + prompt_sha256, mai grezzo. Trigger DB che rifiuta prompt non marcato = follow-up opzionale.
L4 content_audit_log dedicata append-only (trigger anti-UPDATE/DELETE) in 2026-06-02a. Applicare via Dashboard. NON riusa il registro Art.9.
L5 document_type CHECK esteso (2026-06-02a) + union TS (compliance.ts): content_studio_tos, voice_clone_consent, sub_processor_list. Applicare migration.
L6 Endpoint consumer Google rimosso; blocklist backend vietati asserita; dispatch fail-closed 503, mai downgrade silenzioso.
L7 generation_type 'social_text''text' (bug vivo: violava il CHECK reale).
L8 🔒 ToS clausola indennizzi da riscrivere (legale + humanizer). Vedi infra checklist F.
L9 sub_processor_list sbloccato (migration). Flusso accettazione versionata = follow-up.
L10 EXIF-strip + "foto utente → solo Vertex EU": si applica quando arriva l'adapter immagini (SOV2). Regola definita, enforcement con SOV2.
SOV1 ◐🔒 Adapter Vertex EU (Gemini + Claude) + auth service-account + routing: codice completo, fail-closed. Attesa provisioning GCP (infra A).
SOV2 ◐🔒 Registry pronto (azure-flux-eu / vertex-imagen-eu). Manca adapter immagini + migrazione generate-social-media. Dipende anche da infra A/B2.
SOV3 provider-registry fail-closed + routing per modalità/tier costruito.
SOV4 🔒 Ticket EU Data Boundary Azure (infra B2).
SOV5 🔒 DPA Google/Microsoft/Mistral/fal (infra C).
SOV6 Telemetria residenza scritta + vista sovereignty_register_v (migration). Flag "sovranità totale" per-tenant = follow-up piccolo.
SOV7 🔒 Claim "dati in Europa" sbloccabile solo dopo SOV1-6 testati (infra E).
C1 Video in secondi (40/120) in tier_caps (2026-06-02c).
C2 Modello unità ratio (0.25) definito; enforcement all'endpoint media (quando arriva).
C3 Upscale 0.5 unità: idem, forward-looking.
C4 Cap testo vs media separati (ai_text_generations soft vs ai_media_generations hard).
C5 Media hard-capped già in Fase A (ALWAYS_HARD_METRICS). CGI fuori MVP (addendum).
C6 Hard media + fail-closed su errore per i contenuti a franchi (mai costo illimitato).
C7 🔒 Floor margine Veo da ricalcolare se Google muove il listino (processo ops).
C8 Rollover 2x: content_credit_ledger lo supporta; cron di rollover = follow-up.
C9 🔒 A/B voce IT (infra B3).
P1 Decisione riconciliazione scritta (database/migrations/README-content-reconciliation-2026-06-02.md): content_items=concept, social_posts=publish.
P2 ◐🔒 = SOV2.
P3 Agganciare trend/competitor come slot "perché ora": context-assembly = follow-up (code-doable, Fase B1).
P4 Editing video post-gen: frontend, quando arriva il video (Fase B1).
P5 Onestà UX ratio video non gratis: documentato; copy UI con il video.
P6 "Correggi questo" (FLUX Kontext): richiede adapter immagini (Fase B1).
P7 Approva-in-blocco settimanale: schema content_plans pronto; UI Fase B2.
P8 Brand auto-learning: content_brand_memory esiste; scrittura segnale su approve/scarto = follow-up (code-doable).
P9 Stato "nessun canale connesso": frontend (Fase B1).
P10 Smontare controlli tecnici ContentCreatorPage: frontend + skill design + preview (Fase B1).
P11 Preset per scopo: content-seeds esiste; ordinamento per rilevanza temporale = follow-up.
P12 Lingua fr aggiunta (system prompt + route). "Lingua come dimensione di derivazione" = follow-up.
P13 Gate review video full-screen: frontend, con il video.
P14 Ritorno metriche nel pianeta: frontend + integrazione Marketing.
R1 Output platform-ready (caption+hashtag+ratio+cta+alt+orario) come pacchetto JSON.
R2 Routing qualità per tier (Business long-form → Claude su Vertex EU).
R3 Durata video selezionabile + score: con il motore video (Fase B1).
R4 Prompt scrivibile dall'utente: il backend accetta prompt libero; toggle "proposte vs scrivi il tuo" = UI follow-up.
R5 Ring: content_credit_ledger + cap effettivo (cap+crediti) lato lib. Componente UI ring (stile Apple) = frontend follow-up.
R6 Preview→commit: content_assets.stage + conteggio solo final. Endpoint preview economico = con il motore media.
R7 CGI rimossa dall'MVP (non in caps/registry).
Knowledge Layer conoscenza esperta (api/_lib/content-knowledge): playbook 8 piattaforme + framework + psicologia colori → lente esperta a ogni generazione. Graduazione a pgvector = follow-up.

Sintesi: tutte le criticità legali bloccanti (L1-L7) sono chiuse o pronte-da-applicare; la sovranità è reale e fail-closed (manca solo il provisioning founder); i costi sono protetti (cap media hard da subito); il Knowledge layer e l'output platform-ready danno al pianeta la "mente da social media manager". Il resto è frontend/Fase B1-B2 o bloccato-da-founder, tutto tracciato qui sopra con la ragione.


Stato dopo la sessione Pianeta Content (frontend) · 2026-06-03 · PR #146

Sessione dedicata "Pianeta Content perfetto": lato UX/derivazione/strategia, in parallelo a #145 (backend gateway/knowledge). Branch claude/romantic-wu-ca63e3, off master. Consuma il contratto #145 con degrado grazioso (503 → manuale). Niente fork: riusa social_posts + useContentStudio.

Legenda: ☑ chiuso · ◐ parziale (resta backend/infra o follow-up).

ID Stato Dettaglio (PR #146)
R1 Format-derivation frontend: lib/content-studio/channels.ts (8 piattaforme × formati, ratio/pixel/safe-zone 2026) + derive.ts (1 concept → N pacchetti platform-ready: caption alla finestra di canale, hashtag tagliati, formato per scopo, alt, orario). SafeZoneFrame disegna la zona di sicurezza.
R4 Prompt libero ("Altro: dimmi cosa vuoi") + preset + "Altre così" (varianti). Controllo sempre all'utente.
R5 Ring stile Apple Activity (QuotaRing + useContentQuota): anello per metrica, bonus crediti, quanto manca, top-up. Resta: applicare migration 2026-06-02c + usage media reale (quando arriva il motore).
C4 ☑(UI) Ring separa testo (illimitato) dai media (anelli) lato UI.
P5 Onestà costo visual per ratio mostrata su ogni card (video 1 ratio/gen, immagine ricomposta).
P9 Canali oltre Meta+LinkedIn = copia/scarica; degrado 503 → scrivi a mano, adatto io ai social.
P10 Controlli tecnici smontati: ContentCreatorPage shell slim, niente ratio/style/doppio-passaggio/prompt-visivo. Tono/lingua in "Tono e dettagli".
P11 Preset settore (≤5 + Altro), ordinati per rilevanza temporale (presets.ts, riusa content-seeds).
P12 Lingua fr nel selettore avanzato; "lingua come dimensione di derivazione" = follow-up.
P13 Gate review a 3 tap (guarda prima di approvare); player video full-screen arriva col motore video (Fase B1).
Le tre vie + evento/offerta Richiesta founder: goals.ts (farmi conoscere/dare valore/vendere + evento + offerta) + StepScopo. Mappa a funnel/objective/framework/CTA/KPI. Cadenza teaser→promemoria→ultima-chiamata.
Best-time + handoff Marketing Richiesta founder: best-time.ts (piattaforma × settore × località CH, con "perché" + personalizzazione futura) + scheduling.ts (contratto handoff) + toggle "programma nel giorno migliore" (auto/su conferma).
Knowledge visibile rationale.ts: lente esperta resa visibile ("perché formato/colore/orario/framework"). Complementare al backend #145.
R3 Durata selezionabile + score lunghezza-qualità-performance (video-score.ts puro+testato) + VideoDurationPicker (slider, score live, anteprima→commit). Render video reale = gateway (Fase B1).
P14 Ritorno metriche: tab "Risultati" + MetricsReturnPanel (legge social_posts.metrics_*, top performer = "Ha funzionato"). Chiude crea→pubblica→impara.
P3 "Perché ora" in StepScopo via analyze-market-trends (edge function riusata) come proposte proattive. Graceful se non keyato.
P8 Brand auto-learning: useBrandSignal (approve/reject) + migration additiva content_brand_signals (append-only, RLS workspace; content_brand_memory è un profilo → tabella dedicata). Best-effort fino all'apply via Dashboard.
R6 Preview→commit: framing UI fatto ("l'anteprima non consuma"); endpoint anteprima economica col motore media (content_assets.stage già a prod).
P4/P6 Editing post-gen / "correggi questo" (FLUX Kontext): col motore media (gateway).

Validazione: 0 errori tsc (CI Linux verde); logica pura testata (vitest + smoke tsx). tsc/vitest non eseguibili sul worktree Windows (pnpm/postcss) → gate = CI.

✅ IN PRODUZIONE (2026-06-03): PR #146 mergiata in master (cab4493) → deploy Vercel prod. Additiva, degrada con grazia sul backend attuale.

📌 Reminder BOS / sessioni future (direttiva founder 2026-06-03): il Pianeta Content non è finito, va migliorato ancora (livello Higgsfield + occhio business). Prossimi passi quando il gateway sovrano è provisionato: render video Veo + immagini FLUX, editing post-gen (P4), "correggi questo" FLUX Kontext (P6), endpoint anteprima→commit (R6). Inoltre: applicare migration database/migrations/2026-06-03_content_brand_signals.sql via Dashboard (accende P8), QA visivo reale, polish UX continuo, e — quando i dati ci sono — far imparare davvero il best-time dall'audience del cliente.


Stato dopo la sessione extension (backend compliance) · 2026-06-03 · PR #147

Sessione dedicata "big tech 100% compliant + routing EU reale", stacked su #145. Prima ho rivisto #145 a fondo: merge-ready (la review avversariale automatica dava falsi positivi perché leggeva il working tree pre-#145; ogni file critico verificato a mano). Poi ho chiuso gli item code-doable rimasti, dai più gravi. Branch claude/sovereign-bonifica-extend. Logica critica validata via Node contro i moduli reali.

Legenda: ☑ chiuso · ◐ codice pronto, resta innesto founder/Fase B · 🔒 bloccato-da-founder.

ID Stato Dettaglio (PR #147)
L1 NER nomi propri chiuso (era il follow-up aperto). redactProperNames() nel gate canonico (api/_lib/pii.ts) + mirror Deno. Euristica conservativa ML-free; si applica solo prima di un hop NON sovrano (piiLevel:'full'), sulla rotta EU i nomi restano (residenza). Commit 859b996.
L6 Esteso a TUTTI i path. Le 8 ai-copilot-* (edge) usavano Gemini consumer: nuovo _shared/sovereign.ts (forbidden-host + gate PII + adapter Vertex EU reale RS256 + fail-closed). ai-client.ts ora Vertex→fail-closed→fallback dev. Commit 3cb8f71. ◐ attivazione = founder accende SOVEREIGN_ENFORCE/provisiona Vertex.
L10 ☑/◐ EXIF-strip lossless JPEG/PNG (api/_lib/exif-strip.ts) + regola ROUTE_USER_PHOTOS_TO=sovereign-eu-only. Test 13/13. ◐ innesto nell'adapter immagini SOV2. Commit bf24f16.
SOV6 Flag per-tenant forceSovereign (vince su ogni opt-in caller) nel registry + dispatch. Commit a97e208.
L9 ☑/◐ Lista sub-processor versionata (src/lib/sub-processors.ts, SUB_PROCESSOR_LIST_VERSION) = fonte di verità per trasparenza + accettazione (legal_acceptances). ◐ schermata UI accettazione (Fase B). Commit 73fc969.
L8 ◐🔒 Clausola ToS garanzie riscritta (no indennizzi upstream incerti), via humanizer: docs/content-studio-tos-garanzie-DRAFT.md. 🔒 sign-off legale. Commit 5fb6dad.
SOV1/2/4/5 🔒 Runbook founder ESATTO: docs/sovereign-routing-provisioning.md (GCP Vertex EU service account, Mistral ZDR+DPA, Azure EU + ticket EU Data Boundary, switch SOVEREIGN_ENFORCE, matrice env→feature, checklist DPA, validazione). Codice pronto, manca solo l'innesto infra. Commit 5fb6dad.
SOV7 🔒 Claim "dati in Europa" sbloccabile dopo provisioning + 4 test verdi del runbook §8.

⚠️ Decisione founder (impatto prod): mergeare #145 + #147 porta testo Content, outreach e le 8 copilot a fail-closed 503 in prod finché Vertex EU non è provisionato (compliance voluta). SOVEREIGN_ENFORCE=false = escape hatch documentato. Sequenza nel runbook.

Sintesi: il blocco endpoint consumer è ora su tutti i path (api + edge); il gate PII è completo (strutturato + NER) e centralizzato; EXIF, sub-processor versionati, flag sovranità per-tenant chiusi; il routing EU reale è codice pronto + runbook, manca solo l'infra founder. Nessun item legale lasciato muto.



Stato dopo la sessione Fase B1 · 2026-06-03 · PR #155

Sessione dedicata "avanza il Pianeta Content a B1, qualità big-tech". Anti-drift: #145 e #147 erano ancora APERTE (mai mergiate) e #145 confliggeva con master. Riconciliate in feat/content-b1 (PR #155, supersede #145/#147). CI verde (Typecheck+build pass). Validazione api/: esbuild --bundle (95/95) + smoke logici.

ID Stato Dettaglio (PR #155)
Integrazione #145+#147 su master, conflitti risolti preservando l'identità SSoT #144 (buildSoleIdentity). Fix prod-safety: 38 import → ESM .js//index.js (api/ è ESM NodeNext, estensionless = ERR_MODULE_NOT_FOUND a runtime; esbuild non lo vede).
SOV2 / P2(motore) Motore immagini sovrano: imagen-adapter.ts (Imagen 4 :predict Vertex EU, SynthID nativo) + dispatchImage (PII gate, breaker, retry, sha256, costo) + POST /api/content/generate-image (hard-cap media, preview→commit R6, EXIF-strip L10, content_assets con provenance), routing per tier (Pro→Fast, Business→Ultra).
L10 EXIF-strip innestato nel flusso immagini (storage.ts strippa prima di persistere; SynthID vive nei pixel → sopravvive).
C2 / C3 Costo per-immagine cablato in dispatchImage; derivazione ratio/upscale come unità = quando arriva l'editing.
Provenance C2PA ☑/◐ content-provenance.ts: manifest C2PA (c2pa.created + digitalSourceType IPTC, do-not-train, hash-binding, ref SynthID) + label visibile, salvati su content_assets. ◐ firma crittografica = C2PA_SIGNING_CERT founder (manifest emesso unsigned, fail-closed).
P8 approve.ts scrive segnale 'approve' server-side in content_brand_signals (fail-soft). Applicare migration 2026-06-03_content_brand_signals.sql via Dashboard per accenderlo.
SOV1/2 (runtime) 🔒 Vertex EU: env GCP aggiunti dal founder, mai verificati a runtime. Testabile dopo deploy di #155 (runbook §8). Senza, fail-closed 503.
P3 Worker durabile: urgenza bassa per B1 (immagini veloci in-request); serve per il video. Prossimo.
P4 Context "perché ora" (trends/competitor 4° slot): prossimo.
P6 UX 3-tap + ring + wiring useGenerateMedia/api/content/generate-image: prossimo (frontend + design).
P7 Swap gemini-3-flash (verificare model-id Vertex) + valutare Mistral Large 3.

Sintesi B1: il pianeta ha ora un motore immagini sovrano end-to-end (concept → immagine EU, hard-capped, con provenance C2PA+SynthID e label Art.50) sopra il gateway integrato e prod-safe.

Update fine sessione — landati anche in #155: P4 (slot "perché ora": market-context.ts + body.marketSignals in generate-text, 4° slot) e P7 (Gemini 3 Flash default + env-override GCP_VERTEX_GEMINI_MODEL; leva Mistral Large 3 via MISTRAL_MODEL). Totale: integrazione + P1 + P2 + P4 + P5 + P7 su #155, CI verde.

Rinviati con razionale:

  • P3 worker durabile → valore reale col motore video (modalità pesante, oggi OFF per CH); le immagini sono veloci in-request e un worker contraddirebbe la UX preview→commit. ai_generation_jobs polimorfico è già pronto. Costruirlo CON il video.
  • P6 UX immaginiturno dedicato: useGenerateMedia è definito ma non ancora cablato in UI (ContentCreatorPage genera solo testo). Serve build frontend reale (genera immagine nel 3-tap + ring usage reale da content_assets + passa marketSignals) con skill multiverbe-design + QA Playwright.

Resta inoltre: provisioning Vertex (founder) per il test runtime reale.



Stato dopo la sessione Video Engine · 2026-06-04 · PR #171 / #173 / #176 / #177

Sessione dedicata "Pianeta Content oltre testo+immagini": motore video DACH-safe + architettura performante, qualità big-tech, tutto fail-closed finché un provider video non è provisionato. Anti-drift verificato sul codice + prod: /api/content/sovereign-status LIVE conferma testo+immagini sovrani già in prod; migrazioni 2026-06-02b/c applicate (verificato via MCP) → substrato video (ai_generation_jobs polimorfico, content_assets, tier_caps.video_seconds 40/120, content_credit_ledger) già pronto, nessuna migration necessaria.

Legenda: ☑ chiuso · ◐ codice pronto, resta provisioning/innesto · 🔒 bloccato-da-founder/infra.

ID Stato Dettaglio
P3 worker Worker durabile Inngest content-video-render (claim→submit→poll step.sleep→finalize→complete) su ai_generation_jobs (api/_lib/content-jobs.ts). Idempotency per-job + retry/backoff Inngest + cost-cap abort. Sovranità: l'evento porta solo jobId+workspace_id; la richiesta (prompt) vive in Supabase EU → PII mai a Inngest (US). PR #171 (CI verde).
P2/SOV video dispatchVideo submit/poll nel gateway + VIDEO_ROUTES quality-tier + adapter Firefly/Synthesia/Runway (api/_lib/sovereign/adapters/). Fail-closed senza env. PR #173.
C1/C2/C7 ☑/◐ Video in secondi già nei caps; estimateVideoCostChf per-provider (◐ riverificare prima di claim margine, C7). Derivazione ratio video = 1 ratio/gen (architettura).
R3 ☑(BE) Durata selezionabile cablata in storyboard/commit (durationSeconds 3-120). Score UI = video-score.ts frontend (PR #146).
R6 (la killer) Preview→commit: preview = storyboard dal motore TESTO sovrano (gratis, fuori dal ring); commit accoda il worker e brucia video_seconds solo lì → scarti gratis. POST /api/content/video-preview + generate-video + GET /video-status. PR #176.
Quality tier (top models) Decisione founder "top dei modelli + modo per incorporarli": Sovrano (Firefly/Synthesia EU, default + unico in forceSovereign) · Qualità Mondiale (Runway Gen-4.5, opt-in allowNonSovereign+consenso esplicito, etichettato fuori-UE). PR #173/#176.
Provenance video finalizeVideoAsset costruisce C2PA + label Art.50 + sha256 sul video (riusa content-provenance.ts).
P6a Quality Score Post Quality Score 0-100 + 1 suggerimento (stile Buffer), puro/offline, POST /api/content/quality-score. PR #177 (indipendente, base master).
Veo 🔒 Tenuto available:false ma la base "Pre-GA" è superata (ora GA+EU-region+indemnified, già su Vertex EU). Vale valutarne lo sblocco (allowlist Google + CH-billing). Adapter Veo da scrivere (predictLongRunning).
P4 Editing post-gen (trim/cover/sottotitoli) = client-side frontend; provenance video già fatta.
P5 Repurposing 1-clic = sessione dedicata (pesante, dipende da provider + trascrizione).
P6 b-e Loop metriche→contenuto · distillazione brand DNA (job Sole content_brand_signalscontent_brand_memory) · sector-tuning profondo · Knowledge→pgvector (2026-07-15a).

🔒 BLOCCATO da infra/founder (sessione "sblocco infra" parallela): env provider video (RUNWAY_API_KEY / ADOBE_FIREFLY_CLIENT_ID+SECRET + DPA Adobe per residenza EU / SYNTHESIA_API_KEY) + INNGEST_EVENT_KEY per accodare. Bit schema-volatili degli adapter = override-via-env (fix a provisioning senza code change). Wiring frontend (ContentCreatorPage → endpoint video + ring reale + badge quality-score) = sessione design+Playwright.


Questo backlog è la SSoT della sessione di bonifica dedicata. Ogni item chiuso → spuntare lo stato e linkare il commit/PR.