Skip to main content

Notifications composer (ops guide)

Questa è la pagina Compose notification del control plane: https://control.extrinsiccognition.com/p2e/notifications

Da qui mandi messaggi che appaiono dentro il launcher Playroll (e in futuro anche email). Il sistema è già live: la pagina compone l'evento, lo invia, il launcher dei destinatari lo riceve entro 60 secondi (o subito se mettono fuoco sulla finestra).

Prima di iniziare leggi sempre la sezione "Verifica prima di inviare" più sotto — un invio è irreversibile e tocca utenti reali.


Chi può aprire la pagina

Accesso gated da Cloudflare Access + crm_user_roles su Supabase. Devi avere il ruolo admin o cm. Se vedi un 403 al primo caricamento, scrivi a Marco per essere aggiunto al ruolo. Le persone autorizzate al momento sono Phil, Nico, Denise, Kame, Mattia, più gli admin EC.


Cosa sa già fare il sistema (live oggi)

  • Componi una notifica con categoria + sottotipo + tier
  • Scrivi copy in italiano e/o inglese
  • Aggiungi una CTA opzionale (esterna o deeplink in-app)
  • Targetizza i destinatari per gruppo, release track, lingua, data di creazione account, oppure passandone gli UUID specifici
  • Vedi in tempo reale quanti utenti riceveranno il messaggio
  • Conferma con friction (INVIA da digitare) prima dell'invio
  • Vedi nella history (in fondo alla pagina) le ultime 50 notifiche inviate, con conteggio destinatari

Notifiche programmatiche vs craftate

Questa è la cosa più importante da avere chiara prima di aprire la pagina.

Le 8 categorie definite dalla spec si dividono in due famiglie:

5 categorie programmatiche — non si emettono mai da questa pagina. Partono dai loro source system quando le condizioni scattano:

CategoriaSource system
payment_readinessTrigger nel Contributor Portal su cambio iban_status / documents
validation_outcomePipeline validazione upload
payout_lifecycleBackend payouts + webhook Wise
app_updateRelease pipeline (playroll-installer, CI Vincenzo)
annual_capBackend cumulative tracker (P3)

3 categorie craftate — sono quelle che vedi nel dropdown della pagina e che puoi inviare tu manualmente:

CategoriaQuando si usa
cohort_announcementComunicazioni ops (manutenzione, novità Discord, ringraziamenti)
catalogAggiunta/rimozione gioco dal catalogo Playroll
systemEscape hatch per casi non coperti dalle altre due
warning

Se hai bisogno di emettere ad-hoc una programmatica (es. backfill di un evento che era andato perso), non farlo dalla UI: fallo via SQL diretto o chiedi a Marco di scrivere uno script una-tantum. Esponendo le 5 programmatiche nel dropdown si rischierebbe troppo facilmente di mandare un payout_sent finto.


Cosa non è ancora live, anche se la UI lo lascia presagire

Sii consapevole di queste limitazioni prima di lanciare:

CosaStato
Filtro per language (it/en)Inerte oggi. La colonna playroll_users.language è NULL per tutti gli utenti finché non finiamo EC-216. Se selezioni it o en qui, il count torna 0.
Filtro per library_contains (giochi posseduti)Live (EC-203). Cerca un gioco nella sezione "Audience builder" → autocomplete giochi catalogo (min 2 caratteri). Solo giochi con mapping Steam sono filtrabili; il count destinatari diventa solo gli utenti che hanno almeno uno dei giochi selezionati nella loro libreria Steam.
Email come canaleMai inviata oggi. Per i tier T0 / T1-money/legal il sistema scrive una riga in notification_deliveries con status='queued', ma nessun worker li raccoglie ancora. In backlog (EC-194).
Throttle automatico su cohort_announcementLive (EC-214). Max 1 critical + 2 standard per finestra rolling 7 giorni. Lato UI vedi un chip critical: X/1 · standard: Y/2 nel pannello "1. Event type" quando selezioni cohort_announcement. Quando sei al limite il bottone Send si disabilita e compare la data della prossima finestra disponibile. Il backend rifiuta comunque con HTTP 429 cohort_throttle_exceeded se qualcuno tenta di bypassare la UI.
CRUD gruppi via UILive. I gruppi founders, ops, beta si popolano dalla pagina Players (vedi runbook People & Roles).
Modal bloccante T0 nel launcherLa spec PM lo prevede ma il launcher al momento mostra T0 come tutte le altre nella tab Notifiche, senza modal. Marco aprirà un ticket quando definiamo dove vive visivamente.
Drafts (salva bozza)Non implementato. Se navighi via dalla pagina perdi tutto.

I 4 tier di urgenza

TierRendering nel launcherQuando usarli
T0Modal bloccante (non ancora implementato — appare come T1 oggi)Solo per cose che richiedono azione immediata (T&C, payout bloccato). Richiedono CTA obbligatoria.
T1Banner interruttivo + tab + badge, 12s auto-dismissPagamento imminente, traguardo €100/€400, payout fallito, annuncio critico
T2Banner ambient (solo se launcher in foreground) + tab + badge, 6sDefault per la maggior parte degli annunci
T3Solo badge + entry nella tab, niente bannerCose informative che possono aspettare (es. update disponibile)

Il tier di default viene proposto in base al sottotipo. Cambialo solo se sai quello che fai.


Audience builder: come selezionare i destinatari

Due modalità:

  • Everyone → tutti i 124 utenti registrati. Usa solo per cose che servono davvero a tutti (es. T&C re-acceptance). Se sbagli a digitare una T1/T2 e selezioni Everyone, 124 persone ricevono un'idiozia.
  • Custom filter → combinazione AND di:
    • Groups: clic sui gruppi seedati (founders, ops, beta). Per aggiungere/togliere membri vai sulla pagina Players (runbook People & Roles).
    • App track: stable, test, dev. Selezionando test mandi solo agli 8 utenti attualmente sul test track. stable → 0 oggi (nessuno ancora).
    • Language: it / en. Inerte oggi (vedi sopra). Usa altri filtri.
    • Created after: filtra per data di registrazione.
    • User IDs: incolla uno o più UUID, uno per riga. Modalità chirurgica — usala per mandare a una persona sola (test, ack mirato).
    • Library contains (Steam): cerca un gioco (es. "Elden Ring") e aggiungilo come pill. Selezionandone più di uno il filtro è OR (utente deve avere ≥1 dei giochi). Solo giochi con mapping Steam sono filtrabili (gli altri non compaiono nell'autocomplete). Tipico per catalog.new_game_added — manda solo a chi già possiede quel gioco.

Il conteggio destinatari si aggiorna entro mezzo secondo dopo ogni modifica. Se vedi 0 recipients il bottone Send è disabilitato.


Verifica prima di inviare

Prima di cliccare Send leggi questa checklist:

  1. Categoria + sottotipo + tier sono coerenti. Es. non usare cohort_announcement con tier T0 — il T0 esiste per modal blocking, non per gli annunci comunità.
  2. La copy italiana c'è e non ha errori. Per money/legal (payment_readiness, payout_lifecycle, annual_cap) chiedi review a Guido prima.
  3. Il count destinatari è quello che ti aspetti. Se mandi a founders ma il gruppo è vuoto vedrai 0 — niente arriverà. Devi prima popolare il gruppo (sotto).
  4. CTA target: se hai messo un URL esterno, aprilo a parte e verifica che funzioni. Se è un deeplink, sappi che oggi non c'è un router deeplink nel launcher (è in arrivo); rendi il CTA external_url o none.
  5. Throttle cohort: enforced automaticamente. Se sei al limite il chip diventa rosso e Send si disabilita — non serve più contare a mano. La data della prossima finestra disponibile è mostrata accanto al chip.
warning

Solo dopo questi 5 check clicca Send. Confermi digitando INVIA nel modal.


Cosa succede dopo l'invio

  1. Il server inserisce 1 riga in notification_event (l'evento canonico)
  2. Inserisce N righe in notification_state, una per ogni destinatario (sono queste che il launcher legge)
  3. Per i tier che hanno email come canale (T0, T1-money/legal), inserisce anche N righe in notification_deliveries con status='queued'. Le email non vengono ancora inviate (EC-194); le righe restano nel queue.
  4. La pagina rimanda alla history view che ora mostra il tuo evento in cima.
  5. Sui launcher dei destinatari:
    • Entro 60 secondi (polling timer) la notifica appare nella tab Notifiche.
    • Oppure immediatamente se l'utente clicca sulla finestra del launcher (trigger on focus).
    • Se ha abilitato il sound, sente un chime al primo arrivo (solo T1/T2).

L'evento e le righe state restano nel DB per sempre (oggi non c'è retention job). Il client mark-read è server-side e persiste tra reinstall.


Popolare un gruppo

Vai a /p2e/people/players, cerca l'utente per username/discord, clic sulla riga → modal di edit → tick il gruppo (founders, ops, beta) → Save. Il count nel composer si aggiorna entro mezzo secondo dalla prossima ricarica.

Per i dettagli (creare nuovi gruppi, bulk add, regole RBAC) vedi il runbook People & Roles.


Esempi pratici

Esempio 1: annuncio manutenzione a tutti

Category: cohort_announcement
Subtype: critical
Tier: T1
Title IT: Manutenzione domenica
Body IT: Domenica 18 maggio 8:00-10:00 la pipeline di validazione
sarà ferma per manutenzione. Le sessioni registrate in
quella finestra non perderanno l'audio ma saranno
validate al riavvio.
Audience: Everyone
CTA: Nessuna

Esempio 2: ringraziamento ai founders (sul test track)

Category: cohort_announcement
Subtype: standard
Tier: T2
Title IT: Grazie per i bug report
Body IT: Le segnalazioni di questa settimana hanno permesso di
sistemare 4 bug critici. Continua a usare F12 in app
per aprire il pannello bug.
Audience: Custom filter → Group: founders, App track: test
CTA: Nessuna

Esempio 3: messaggio mirato a un singolo utente (test ack)

Category: system
Subtype: custom
Tier: T2
Title IT: Verifica sound notification
Body IT: Se vedi questa notifica e senti il chime, il sistema
sound funziona.
Audience: Custom filter → User IDs: <singolo-uuid>
CTA: Nessuna

Cosa fare se qualcosa non torna

  • "0 recipients" ma sono certo che il filtro è giusto: probabilmente stai filtrando per language (vedi sopra: NULL su tutti) o per un gruppo vuoto.
  • L'utente non vede la notifica nel launcher: chiedi che riapra il launcher o ci clicchi sopra. Polling timer 60s, on-focus immediato.
  • Vedo un errore 500 nel network tab dopo Send: controlla la console del browser per il messaggio, e poi scrivi a Marco col contenuto della risposta. Il backend logga sempre il fallimento.
  • Voglio "annullare" una notifica appena inviata: non c'è UI per farlo. Si può cancellare via SQL (delete from notification_event where id='<id>' fa cascade sulle state rows), ma se l'utente ha già fatto focus sul launcher l'avrà già vista. Misura sette volte, taglia una.

Riferimenti