Sole identity — backlog di follow-up (post PR #144)

Stato: 2026-06-02. Origine: sessione "Qualità di TUTTI i system prompt". Identità del Sole validata (ops/sole-identity-2026-06-02.md) e implementata (buildSoleIdentity). Questo doc è il brief autorevole per la sessione fresca: tutto ciò che NON è entrato in PR #144, rankizzato, con scope + file + acceptance criteria. Decisione founder: i 4 workstream sotto sono tutti in scope; questa sessione ha chiuso il P0; il resto si esegue da qui.

Già fatto (PR #144 · NON ri-fare)

  • SSoT frontend/multiverbe-app/api/_lib/sole-identity.ts (buildSoleIdentity · voce "noi" · language-aware it/de/fr/en + fallback mondo · role-aware · per-settore via ai_brain_seed · 3 surface cockpit/outbound/content). Port edge Deno supabase/functions/_shared/sole-identity.ts.
  • Tutti i system prompt sopra l'identità: analyze-planet-data, sole-crm-suggestions (fallback localizzati it/de/fr/en), sector-deduction (22 settori, language-aware), generate-outreach (Mistral), generate-text (Gemini), site-builder system-prompt, edge context-builder (routing Gemini/Groq invariato).
  • Onboarding popola entity_profile + setta workspaces.primary_language dalla lingua UI.
  • Overlay reso reale: il cockpit ora rende goals → priorità localizzate + personality → tono (prima erano caricati ma inutilizzati). Guardrail test surface×locale.
  • Loop personalità chiuso (commit db901e9): l'onboarding (Step3) cattura il tono → personality.tone_key; buildSoleIdentity lo rende localizzato (it/de/fr/en) su tutte le surface. → item 1.1 + 1.2 sotto sono FATTI.
  • Contract-test matrix (commit 34b1ed9): guard 5 lingue × 3 surface + ruoli + overlay + litmus "freelance in Svezia" (zero residui IT/CH/PMI), eseguita localmente verde → parte statica del workstream 2.1 FATTA (resta solo l'LLM-judge, gated API key).

📌 Stato repo 2026-06-03 (per la sessione fresca)

  • #142 (memoria + sole-ask) È MERGIATA in master (80b76ef), insieme a #140 Content, #141 Sales Boss-View, #143. Quindi api/_lib/memory.ts · pii.ts · memory-embed.ts · api/ai/sole-ask.ts · _inngest/functions/memory-sync.ts sono su master → cablare sole-ask a buildSoleIdentity non è più bloccato.
  • PR #144 (questa: SSoT identità) ora CONFLITTA con master su 2 soli file: api/content/generate-text.ts (toccato anche da #140 Content) e vitest.config.ts (io e #142 abbiamo aggiunto lo stesso include api/** → merge banale). → rebase piccolo prima del merge.
  • Nuove direttive founder 2026-06-03 (vedi prompt sessione fresca): pianeti dinamici per tier+settore (no "hai 4 pianeti" hardcoded), revisione tassonomia settori/artisti freelance, chat proattiva a card (2 opzioni o 1 risposta), proattività bounded/token-efficient.

🆕 Direttive founder 2026-06-03 → sessione fresca (T1-T4)

T1-T4 ESEGUITI 2026-06-04 — PR #174 (3 commit atomici + fix CI lint/test). Modello pianeti a due assi (accesso=tier, salienza=settore · resolver api/_lib/workspace-planets.ts), tassonomia "Creativi & Artisti" (6 macro / 28 settori · migration 2026-06-04a), sole-ask su buildSoleIdentity + card proattive (componente SoleChat in HomePage), proattività bounded. Record completo: sole-v2-pianeti-tassonomia-2026-06-04.md. Residui non bloccanti: foregrounding salient/dormant nella Home, QA madrelingua de/fr sulle nuove label, eval LLM-judge sulle card.

Dettaglio operativo nel prompt di kickoff (chat 2026-06-03). Sintesi durevole:

  • T1 · Pianeti DINAMICI per tier+settore (IMPORTANTISSIMO): no "hai 4 pianeti" hardcoded (analyze-planet-data.ts PLANET_KEYS + procedura). Creare SSoT resolveWorkspacePlanets({tier, sectorId, activeModules}) (canon 02/03/11 · fonti: subscriptions.plan_tier via tier-caps.ts, workspaces.active_modules, sectors) → usarlo in buildSoleIdentity + analyze-planet-data + ogni "hai X pianeti".
  • T2 · Tassonomia settori/artisti freelance + ruoli: macro creativa rivista (designer, produttore musicale, cantante, studio di registrazione…), togliere il taglio "solo fotografo/eventi", ampliare per il mondo business. Tocca sectors (DB additivo · CHIEDI se destructive), sectors-fallback.ts, sector-deduction.ts, canon 11. Proporre la tassonomia al founder PRIMA.
  • T3 · Chat proattiva a CARD (FONDAMENTALE): sole-ask (prima portarlo su buildSoleIdentity · è ancora hardcoded "PMI svizzere/titolare/amica esperta") ritorna suggestions strutturati: 2 opzioni o 1 risposta personalizzata, rese come card al posto della tastiera. Frontend chat semplice.
  • T4 · Proattività bounded + token-efficient: ≤2-3 suggerimenti, concisi, maxTokens stretti, niente over-suggesting.

✅ RISOLTO — la CI ora gira su #144 (era un conflitto, non un setting)

  • Causa reale: PR #144 era in CONFLITTO → GitHub non poteva creare il merge-commit di test → i workflow pull_request non partivano (le Actions sono attive: enabled: all, girano sulle altre PR).
  • Fix applicato (sessione 2026-06-03): mergiato master nel branch + risolti 2 conflitti (generate-text auth #140 + identità · vitest.config) + chiuso il gate lint (helper pick() sui lookup i18n, senza alzare il cap) + import test senza .ts. → Frontend gate PASS (lint+vitest+build), mergeStateStatus CLEAN, MERGEABLE.
  • Lezione: PR con solo check Vercel e nessun Actions = quasi sempre un conflitto. Risolvi il conflitto, non le impostazioni.

🔴 Workstream 1 — Overlay + onboarding (residuo dopo il fix)

1.1 ✅ FATTO (commit db901e9) — Onboarding cattura il tono

  • Perché: oggi l'overlay rende il tono solo se entity_profile.personality è popolato, ma l'onboarding non lo chiede → resta vuoto. Il render è forward-compatible: appena l'onboarding scrive il tono, il Sole lo usa.
  • Scope: micro-step (o campo nello Step3) con 3-4 opzioni di registro (es. diretto / caloroso / professionale / informale) oppure una frase libera breve. Salvare in entity_profile.personality = { tone: <valore> }.
  • File: src/components/onboarding/Step3Workspace.tsx (+ types.ts), SetupWizardV2.tsx (handleStep3Complete + handleFinalComplete), src/lib/entity-profile.ts (aggiungere param personality).
  • Acceptance: nuovo onboarding scrive personality.tone; il cockpit mostra "Il nostro tono: …" nella lingua impostata. Test onboarding aggiornato.
  • Effort: ~0.5g. Nota UX: decidere opzioni + copy (design, non bolt-on).

1.2 ✅ FATTO (commit db901e9) — Tono su content + outbound

  • Perché: la voce dell'azienda (tono) conta anche quando il Sole scrive contenuti/outreach, non solo nel cockpit.
  • File: api/_lib/sole-identity.ts (rendere personality/tono anche per surface content+outbound).
  • Acceptance: content/outbound includono il tono quando presente; test esteso. Effort: ~1h.

1.3 Scala di maturità (bloccata su decisione founder) · P2/P3

  • Stato: il founder sta passando da nucleare/solare/galattico/cosmico a un modello stellare dwarf/subgiant/giant/supergiant/hypergiant. Il CHECK del DB è ancora sui valori vecchi; entity-profile.ts per ora NON scrive maturity_level (NULL).
  • Scope (dopo lock founder): migration additiva del CHECK su entity_profile.maturity_level (ALTER → CHIEDERE al founder); default a onboarding = livello più basso; render della maturità nell'overlay cockpit.
  • Acceptance: onboarding scrive il livello iniziale; il Sole può citare la fase dell'azienda. Dep: scala fissata dal founder.

🟠 Workstream 2 — Eval + localizzazione pro

2.1 Eval harness sull'output REALE del modello · P2 (il vero "big-tech")

  • ✅ Parte statica FATTA (commit 34b1ed9): contract-test matrix sul prompt assemblato (settore×lingua×surface×ruolo×overlay + litmus Svezia). Resta sotto solo l'eval sull'output del modello (LLM-judge), che costa API.
  • Perché: il contract-test verifica il prompt; l'LLM-judge verifica cosa il Sole produce. "Qualità assoluta" va misurata su entrambi.
  • Scope: fixtures (settore × locale × surface) → chiamata modello → giudice-LLM su: lingua corretta, zero slop/em-dash, voce "noi", nessun numero inventato, registro di settore. Scorecard + soglia.
  • File: nuovo scripts/eval-sole-prompts.ts o api/_lib/__eval/ (gated, NON in CI di default: costa API).
  • Dep: ANTHROPIC_API_KEY. Acceptance: eval runnabile che produce una scorecard per settore×locale con pass/fail. Effort: 1-2g. Nota: decidere cadenza (manuale pre-release vs nightly).

2.2 Catalogo i18n UNICO Node + edge · P2

  • Perché: la prosa localizzata è scritta a mano nel modulo Node e RI-scritta nel port Deno (doppia manutenzione). Aggiungere una lingua = editare 2 posti.
  • Scope: estrarre tutta la prosa localizzata (pack identità, template fallback CRM, pain-labels, role-labels) in un JSON condiviso (es. api/_lib/sole-i18n.json) importato sia da Node sia da Deno (entrambi possono importare JSON).
  • File: api/_lib/sole-identity.ts, supabase/functions/_shared/sole-identity.ts, nuovo JSON, sole-crm-suggestions.ts (catalogo CRM), analyze-planet-data.ts (catalogo rule-based).
  • Acceptance: una sola fonte; aggiungere una lingua = editare il JSON; Node ed edge leggono gli stessi dati; test verdi. Effort: ~1g. Rischio: regressione → farlo DOPO 2.1 (l'eval cattura il drift).

2.3 QA madrelingua de/fr · P2 (pre-clienti non-IT)

  • Scope: revisione madrelingua di tutta la prosa de/fr (identità, template, label). Dep: persona madrelingua. Acceptance: sign-off. Nota: bloccante prima di esporre il prodotto a clienti DE/FR.

2.4 Humanizer su tutta la copy utente · P1 (rapido)

  • Scope: passare anthropic-skills:humanizer su tutta la copy che l'utente legge (template fallback sole-crm-suggestions + analyze-planet-data it/de/fr/en + titoli suggerimenti). Regola progetto feedback_humanizer_always_on_text.
  • Acceptance: copy humanizer-clean. Effort: ~2h.

🟡 Workstream 3 — Igiene dati + compliance + perf

3.1 Un solo locale canonico · P2

  • Problema: 3 colonne locale (workspaces.primary_language, workspaces.language, entity_profile.language). buildSoleIdentity fa coalesce, ma è drift-prone.
  • Scope: canonico = workspaces.primary_language; deprecare le altre (derivate o droppate). Migration (ALTER → CHIEDERE). Acceptance: una sola fonte di verità per la lingua. Effort: 0.5g + migration.

3.2 Dedup campi identità · P2

  • Problema: nome/settore/paese duplicati in workspaces E entity_profile.
  • Scope: entity_profile tiene SOLO l'overlay (goals/personality/maturity); nome/settore/paese si leggono da workspaces. Aggiornare entity-profile.ts (non scrivere i duplicati) + loadSoleIdentity (già workspace-first). Acceptance: nessuna duplicazione scritta; identità invariata. Effort: 0.5g.

3.3 Compliance · nome operatore → Anthropic (US) · P1 (responsabilità penale CHF 250k)

  • Problema: il nome di battesimo dell'operatore viene inviato ad Anthropic (US, fuori EU Data Boundary) e NON è documentato in privacy/DPA/sub-processor.
  • Scope: aggiungere in docs/legal/privacy-policy.md, docs/legal/dpa-template.md, docs/legal/sub-processors-list.md la menzione esplicita: "il nome di battesimo dell'utente operatore è trattato da Anthropic (sub-processor, US) per personalizzare le risposte del Sole; i dati dei clienti/contatti restano pseudonimizzati". Dep: review fiduciario (PTEK). Acceptance: i 3 doc lo riportano. Effort: ~2h + review.
  • Testo pronto (IT): «Per rivolgersi all'utente per nome, Multiverbe trasmette ad Anthropic (sub-processor con sede negli Stati Uniti) il solo nome di battesimo dell'utente che opera il prodotto. Nessun cognome, e-mail o dato di clienti/contatti terzi viene trasmesso in chiaro: questi ultimi sono sempre pseudonimizzati prima dell'invio a qualunque modello.»

3.4 Perf · cache degli identity load · P2

  • Problema: ogni chiamata agente fa +query (entity_profile + sectors + team_members + admin-lookup nome); nel cron fan-out è per-workspace.
  • Scope: memoizzare loadSoleIdentity per (workspaceId,userId) nell'arco di una richiesta/processo. Acceptance: nessun doppio load nella stessa richiesta. Effort: ~2h.

🔵 Workstream 4 — sole-ask + consolidamento cervelli

4.1 sole-ask sull'identità (dopo merge PR #142) · P1, dep esterna

  • Scope: in api/ai/sole-ask.ts sostituire SYSTEM_PROMPT_BASE con buildSoleIdentity({ workspaceId, userId, operatorFirstName }) (il first name si estrae da getUseruser_metadata), mantenendo le sezioni dati (memoria/pianeti/oggi). recallMemories è già presente.
  • Dep: PR #142 merged. Acceptance: sole-ask parla con l'identità, nella lingua del workspace, si rivolge all'operatore per nome. Effort: ~1h. Swap di ~2 righe.

4.2 Consolidamento cervelli edge → Anthropic + memoria · P3 (post-gate)

  • Problema: l'edge ai-copilot ha ora la VOCE giusta ma gira su Gemini/Groq + tabelle legacy (brand_intelligence/dashboard_config/ai_goals), mentre l'api Sole gira su Anthropic + entity_memory. Due cervelli con conoscenza diversa sotto lo stesso volto.
  • Scope: migrare le edge function ai-copilot-* sul routing canonico (llm-client/agent-core) + entity_memory, validando la parità (piano in ops/proposta-consolidamento-cervelli-2026-05-30.md). Effort: multi-giorno. Esplicitamente post-gate.

Note operative

  • CI: frontend-ci (vitest + lint) NON è partita sulla PR #144 (anomalia GitHub Actions · zero run sul branch, mentre PR sorelle l'hanno avuta). Il gate equivalente tsc && vite build è verde via Vercel. Verificare/ri-innescare prima del merge.
  • Voce "noi" + "tu Marco": il Sole parla come l'azienda ("abbiamo") e si rivolge a Marco che fa parte dell'azienda. Validare con un test UX reale; rifinire se suona straniante.
  • 5 workspace reali: non hanno entity_profile e restano primary_language='it' → beneficiano solo ri-onboardando. Backfill a basso valore (account di test founder).

Sequenza consigliata verso il gate 22 Giu

  1. P1 rapidi: 2.4 humanizer · 3.3 compliance (carta) · 1.1 onboarding tono · 4.1 sole-ask (appena #142 è merged).
  2. P2 qualità: 2.1 eval harness → 2.2 catalogo i18n → 1.2 tono content/outbound → 3.4 perf.
  3. P2 dati: 3.1 locale canonico · 3.2 dedup (insieme, una migration).
  4. Post-gate: 2.3 QA madrelingua de/fr (pre-clienti non-IT) · 1.3 maturità (dopo lock scala) · 4.2 consolidamento cervelli.