fix/crew-event-link — la crew creata da un evento non era collegata
Data: 2 agosto 2026 Priorità: 1 — Bug
Il sintomo, in tre travestimenti
L’utente ha segnalato, in sequenza:
- “Se dall’evento ne creo una crew collegata non si crea!”
- “Ah no si crea ma non riesco a vederla. E se clicco sull’invito dà errore”
- “E non posso cercarla, ma se cerco un’altra cosa mi esce”
Sembravano tre bug. Erano una causa sola più due indipendenti.
Causa
CrewService.createCrew accetta eventId da sempre e lo inserisce in
crews.event_id. Nessuno poteva passarglielo:
| Livello | eventId |
|---|---|
create_crew_screen._submitCrew | non lo passava |
CrewNotifier.createCrew | parametro assente |
CrewRepository.createCrew | parametro assente |
CrewService.createCrew | lo accetta e lo scrive — mai ricevuto |
Tutto il resto della catena funzionava: la rotta legge ?eventId=, il wizard
carica l’evento e precompila, lo stato lo conserva attraverso copyWith, e la
UI mostra perfino una sequenza di passi diversa quando è valorizzato
(_stepsLinked vs _stepsStandalone). La chiamata di invio lo buttava via.
Verificato in produzione: 0 delle 8 crew più recenti aveva un
event_id. Il vibe "Market" su quattro di esse dimostra che il precaricamento
partiva davvero — la categoria dell’evento veniva ereditata — e che quindi il
collegamento si perdeva a valle, non a monte.
Perché sembrava che il bug fosse cambiato
Il vincolo crew_must_have_anchor sul database pretende che una crew sia
ancorata a un evento, un locale o una zona.
- Con
event_idperso, una crew nata da un evento non aveva ancora → l’insert veniva rifiutato → “non si crea”. - Qualcuno ha risolto aggiungendo la zona corrente come ripiego
(
areaFallback, vedi il commento increw_notifier.dart). L’insert è passato — con il collegamento all’evento sempre mancante → “si crea ma non la vedo”, perché la pagina evento elenca le crew conwhere event_id = ….
Il sintomo si è spostato, la causa no. Il vincolo del database stava segnalando il bug vero e la correzione l’ha zittito.
Correzione
eventId (e venueId, finora ugualmente irraggiungibile) aggiunti a
CrewRepository.createCrew e CrewNotifier.createCrew;
create_crew_screen._submitCrew passa wizardState.linkedEventId.
Bug 2 — rotte inesistenti nelle card condivise
Introdotto il 31 luglio con feature-chat-sharing.
SharedContent.route puntava:
- le crew a
/crew/:id— rotta che non esiste inapp_router.dart; - i profili a
/profile/:id— la rotta vera è/user/:userId;/profileè una shell branch con figli fissi (settings,my-crews…), quindi/profile/<uuid>non combacia con niente.
GoRouter lancia un’eccezione su una rotta sconosciuta, quindi la card dava errore sotto il dito. Non è un link morto: è un crash.
Le crew ora non restituiscono nessuna rotta, perché davvero non c’è dove
mandarle: si aprono con showSquadDetailSheet, che vuole un oggetto Squad e
non un id. Il pulsante “Unisciti” dell’invito continua a funzionare, che è
l’azione che conta.
Quella modifica ne ha scoperta un’altra:
_opentrattava “nessuna rotta” come “allora è un posto” e apriva Google Maps cercando il titolo. Una crew non è un posto, e “Quelli di Grazzano” digitato su Maps non è una risposta utile. Ora soloSharedKind.placefinisce sulle mappe.
Bug 3 — la ricerca crew cercava nel campo sbagliato
search_screen.dart cercava le crew con ilike su vibe_tag, mentre gli
eventi cercano title e i locali name. Digitare il nome di una crew non
trovava niente; digitare il suo genere l’avrebbe trovata.
Ora or(title.ilike, vibe_tag.ilike), con il termine ripulito da , ( )
— sono i caratteri con cui PostgREST delimita un filtro or, e lasciarli
dentro trasforma una ricerca tipo “rock, techno” in un filtro malformato che
fa fallire l’intera query.
Verificato in produzione: il filtro vecchio restituisce 0 righe per la crew creata quella sera, il nuovo 1.
Bug 4 — la chat della crew non si creava
Segnalato subito dopo: “ok però non mi si crea la chat”.
Causa: colonna inesistente
squad_detail_sheet._openSquadChat creava la chat di gruppo, poi salvava l’id
con update crews set chat_id = …. crews.chat_id non esisteva. PostgREST
rifiutava la scrittura, scattava il catch, l’utente vedeva “impossibile
aprire la chat” e non entrava mai.
E siccome l’id non poteva essere salvato, Squad.chatId restava null per
sempre: ogni tocco creava un’altra chat.
Prova schiacciante: la crew “Andiamo al Milk?” ha cinque chat di gruppo create fra le 06:02:07 e le 06:02:10 del 22 aprile. “PiselloPalliamo”, “Sborra”, “House Nation” e “cassino” ne hanno due ciascuna.
La colonna da sola non sarebbe bastata
Le policy su crews consentono l’UPDATE solo al creatore
(Creators can update own crews). Per qualsiasi altro membro l’update non
combacia con nessuna riga — e PostgREST considera un update a zero righe un
successo, quindi non c’è nemmeno un errore da intercettare. Il collegamento
non sarebbe mai stato scritto e il tocco successivo avrebbe creato un’altra
chat, in silenzio.
Due meccanismi indipendenti (RLS + corsa fra tocchi) producevano lo stesso duplicato. Per questo la correzione è una funzione atomica, non tre chiamate dal client.
get_or_create_crew_chat(p_crew_id)
SECURITY DEFINER, search_path = ''. Fa tutto sotto un FOR UPDATE sulla
riga della crew, così il secondo chiamante aspetta e poi trova la chat che ha
appena scritto il primo.
Poiché SECURITY DEFINER scavalca RLS, i controlli dentro la funzione SONO
l’autorizzazione: senza di essi la funzione regalerebbe a qualsiasi account
loggato l’ingresso in qualsiasi conversazione di crew.
- autenticato;
is_approved()— il gate della beta;- membro della crew.
Aggiunge inoltre tutti i membri della crew come partecipanti. Il client
vecchio chiamava createGroupChat(participantIds: []), quindi la chat
conteneva una persona sola e il resto della crew non poteva vederla.
Collaudata in transazione annullata: estraneo respinto, due chiamate → una
sola chat con lo stesso id, tutti i membri dentro, crews.chat_id collegato.
Ripristino
3 crew ricollegate alla chat che avevano già. Scelta per numero di messaggi prima, data poi: i duplicati non sono intercambiabili, la conversazione finiva sempre nell’ultima chat creata, quindi prendere la più vecchia avrebbe attaccato la crew a una stanza vuota orfanando il thread vero.
Pulizia (autorizzata dall’utente, 2 ago 2026)
Il primo ripristino pretendeva chats.created_by = crews.creator_id e aveva
collegato solo 3 crew. Condizione sbagliata: i duplicati li generavano
soprattutto i membri non creatori — cioè proprio il caso che RLS scartava
in silenzio. “Andiamo al Milk?” aveva sette chat, tutte create da un
account che non aveva creato la crew.
Ripristino allargato al solo titolo, con la condizione un solo crew per quel titolo verificata nella query invece che data per scontata.
Poi cancellazione, con tre condizioni di cui la terza è quella che conta: una chat con anche un solo messaggio non viene mai toccata. Non è stato cancellato niente di detto da nessuno; sono sparite stanze vuote che nessuno poteva più raggiungere.
Risultato verificato in produzione:
| chat vuote rimosse | 12 |
| crew con esattamente una chat collegata | 7 su 7 |
| messaggi ancora presenti | 47 |
| chat senza partecipanti | 0 |
| righe partecipante orfane | 0 |
Le conversazioni con contenuto sono tutte sopravvissute: PiselloPalliamo 3 messaggi, Andiamo al Milk? 1, House Nation 1, Sborra 1.
Cosa non è stato fatto
Nessun test di regressione. CrewRepository costruisce CrewService()
direttamente e CrewService legge Supabase.instance.client in un
inizializzatore di campo: non esiste un punto dove iniettare un finto.
Aggiungerlo vale la pena — questa classe di bug (un parametro che esiste in
fondo allo stack e nessuno glielo passa) è invisibile a flutter analyze
ed è già successa due volte.
Le crew già esistenti restano orfane. Non è possibile indovinare a quale evento andassero collegate. Vanno ricollegate a mano o ricreate.
Related
fix-crew-creation-v2 — descrive la selezione dell’evento collegato come funzionante: lo era nella UI, non nel salvataggio feature-chat-sharing — origine delle rotte sbagliate feature-crew-chat — crew e chat