Rilettura completa della documentazione confrontata con il codice. Dove la doc
diceva il falso l'ho corretta; dove aveva ragione lei ho corretto il codice.
Autovoto (la doc aveva ragione)
- migration m12_niente_autovoto: vincoli mvp_no_autovoto e badge_social_no_autovoto,
gli stessi che pagelle_voti ha dalla v1.0. Le righe che li violano vengono
cancellate prima dell'ALTER, altrimenti fallisce; in locale non ce n'erano.
M11 garantisce solo che il voto sia firmato con il proprio votante_id, non che il
votato sia un altro: eleggersi MVP restava a un POST di distanza.
- VotazioneMvp non mostra più il votante nell'elenco, come già faceva VotoSocial.
Test che guardavano la colonna sbagliata
- scritture.test.ts verificava che aggiornato_il si muovesse, chiamandolo "quello che
alimenta la serie di conferme". È l'opposto: la serie usa risposto_il, che il trigger
di M9 deve tenere fermo. Ora il test prova a riscriverlo e controlla che il database
abbia tenuto la prima risposta; prima passava anche senza trigger.
- destinatariSollecito() esce dalla route sollecita-presenze e diventa una funzione pura
in presenze.ts, con i suoi test — stesso trattamento di avvisiPalloniEvento.
Tre riletture in meno
- giocatori-squadra, scout-store e avatar-store usavano invalidateQueries dove il dato
scritto era già noto: ora setQueryData, come il resto dell'app. Resta scout-live, dove
il lock può averlo vinto un altro dispositivo.
Documentazione riallineata
- presenze.md, badge.md, mvp.md: i limiti su RLS aperta e route non autenticata erano
superati da M11 e DD-024;
- serie-presenze.md: il filtro è e.data < oggi, non <=, e l'evento di oggi non conta
(conterebbe come assenza per tutti); aggiunta la tabella risposto_il/aggiornato_il;
- ARCHITECTURE.md ed EFFICIENZA_CLOUD.md: una sola eccezione a setQueryData;
- DATABASE.md: i vincoli delle tre tabelle di voto;
- PROJECT_STATE.md: fermo a M9, ora arriva a M12.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Il testo della notifica viaggia ora cifrato dentro la push (aes128gcm, RFC
8188/8291) invece di essere recuperato dal service worker con una fetch al
risveglio. Era quella fetch a non chiudersi in tempo: ad app chiusa il browser
tiene vivo il worker pochi secondi, showNotification non veniva mai chiamata e
non compariva niente, mentre ad app aperta con la rete calda sembrava tutto a
posto. La POST porta anche Urgency: high, che chiede la consegna immediata
invece di far accumulare i messaggi fino al risveglio del dispositivo.
Cadono i pezzi che esistevano solo per rimediare al payload vuoto: la route
push-messaggio, la coda promemoria_push con la sua scadenza a 12 ore,
messaggioPalloniOggi() e il timeout nel worker. Tutti e tre i mittenti avevano
gia il testo pronto prima di inviare.
Il worker si aggiorna da solo all'avvio e a ogni ritorno in primo piano
(mantieniWorkerPushAggiornato): nella webapp installata quello vecchio puo
sopravvivere a lungo, e senza questo un dispositivo resterebbe fermo alla
versione che va a cercare il testo in rete.
Profilo -> Opzioni ha "Mandami una notifica di prova", visibile solo a notifiche
attive: manda una push a questo dispositivo e riporta stato HTTP, corpo della
risposta e se l'endpoint risulta davvero in push_subscriptions. Senza, "non
arriva" era cieco: ogni prova richiedeva un admin, un evento nello stato giusto
e una seconda persona, e la risposta del servizio push veniva buttata via.
inviaPush torna { stato, corpo } e logga il corpo sui rifiuti.
Dalle prove sul campo: a parita di server, iPhone installato da Home riceve ad
app chiusa. Su Android installato come webapp resta da verificare: il WebAPK e
un'app Android a se, con permesso notifiche (Android 13+) e voce batteria
distinti da quelli di Chrome. Annotato nei limiti noti.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Le tabelle della v1.0 sono nate con policy USING (true) per anon e
authenticated. M4 ha tolto il GRANT ad anon e la cosa è passata per "ora è
chiuso", ma per gli autenticati non era rimasto nessun limite. Verificato sul
database locale con un utente appena creato, senza ruolo e senza slot nella
rosa: POST su eventi_app risponde 201, DELETE risponde 200. Qualsiasi giocatore
loggato poteva svuotare il calendario o riscrivere il voto di un altro parlando
direttamente con PostgREST, saltando l'interfaccia che quei pulsanti glieli
nasconde. Il permesso viveva solo nei componenti, cioè nel posto che un
attaccante non usa.
M11 fa dire alle policy quello che l'interfaccia già fa: eventi_app agli admin,
risposte_presenze e cacche_partita alla propria riga, i tre voti al proprio
votante_id. L'admin resta incluso ovunque, perché DD-017 gli riconosce già il
diritto di agire al posto del giocatore.
turni_palloni e le tabelle scout restano aperte di proposito: nell'interfaccia
non hanno nessun gate, quindi stringerle sarebbe una funzionalità nuova e non
una messa in sicurezza. Un test lo fissa, così se il gate arriva qualcuno se ne
accorge.
L'identità è lo slot di giocatori_squadra collegato all'account, con lo stesso
EXISTS delle policy dei profili: mio_giocatore_id() di M2 era già stata rimossa
dalla migration di correzione e non va reintrodotta.
I cinque test nuovi in permessi.test.ts hanno ognuno il proprio controllo
positivo — l'admin crea l'evento, il giocatore salva la propria presenza —
perché un database che rifiuta tutto passerebbe qualsiasi test di sola
negazione. Provata con db reset da zero; non applicata in produzione.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`npm run format` ha toccato anche tre file estranei a questo lavoro:
`presenze.ts` aveva un errore prettier che `npm run lint` già segnalava, gli
altri due erano solo a capo nelle tabelle markdown. Stanno qui da soli per non
sporcare i commit di merito.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
I tre contatori (serieAllenamenti, seriePartite, serieConferme) e streak erano
zero fisso in useRosa(): la sezione Serie di presenze del profilo mostrava sempre
progresso nullo, i badge legati alla costanza erano impossibili da sbloccare e
l'obiettivo Continuita di squadra restava a 0/12.
serieConsecutiva() e serieConferme() (src/lib/presenze.ts) derivano le serie dagli
eventi passati e da risposte_presenze gia in cache, senza query aggiuntive.
Migration m9: nuova colonna risposto_il con l'istante della prima risposta, resa
immutabile da un trigger, confrontata con eventi_app.creato_il per le conferme
entro 24 ore. Serviva perche aggiornato_il registra l'ultima modifica, quindi chi
rispondeva subito e cambiava idea dopo risultava lento.
Corregge anche la barra di progresso, che misurava valore/prossimo e tornava
indietro a ogni traguardo raggiunto (2/3 = 67%, poi 3/6 = 50%).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Il gate "M4 non applicabile finché la squadra non è collegata" è superato:
il login era già l'unica via d'accesso lato app, quindi i giocatori non
ancora collegati non erano comunque impattati dal residuo di accesso anon
che M4 chiudeva. Aggiorna anche DD-018, ormai stale: l'email dei giocatori
si imposta da /admin, non solo via migration.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
M3 esisteva solo come bozza archiviata in docs/archive/migrations/: il bucket
profili-giocatore è creato dalla migration M2 insieme alla tabella. Allinea
PROJECT_STATE.md (12 -> 18 migration), DATABASE.md, CHANGELOG.md, TODO.md,
DESIGN_DECISIONS.md, test/README.md e il test di integrazione dei profili.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Aggiorna DESIGN_DECISIONS.md (DD-015 da "In valutazione" ad "Accettata",
con le conseguenze reali: fallback nascita e crapp-data.ts come solo
seed/fallback), DATABASE.md, ARCHITECTURE.md e PROJECT_STATE.md di
conseguenza.
Numero e data della tessera arrivano dal comitato dopo l'iscrizione, quindi
li scrive solo un admin (come numero/ruolo): estende il trigger di M1/M5,
aggiunge il pannello dedicato in /admin con badge e conteggio in dashboard.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Due bug architetturali, entrambi con la stessa causa: dati che vivevano
solo in localStorage, quindi visibili a un solo dispositivo.
- Il blocco "chi sta scoutando" (scout-live.ts) non funzionava mai tra
telefoni diversi: due persone potevano prendere il controllo insieme
da dispositivi diversi, sovrascrivendosi a vicenda le azioni nel
salvataggio condiviso. Ora usa scout_sessioni, tabella già presente
nello schema ma mai collegata al codice.
- La partita scoutata finita (scout-store.ts) veniva salvata solo in
localStorage: "Punti/Ace/Muri squadra" e le presenze derivate dallo
scout esistevano solo sul telefono di chi aveva chiuso la partita.
Nuova tabella scout_partite archivia la partita completa (azioni
incluse) per tutta la squadra.
useScoutMatches() mantiene la stessa firma di prima (ScoutMatch[]), ora
alimentata da una query invece che da localStorage: nessuna modifica
necessaria nei punti che la leggono (squadra, classifica, home,
dettaglio partita, rosa). Rigenerati i tipi Supabase.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Le foto caricate in Profilo vivevano solo in localStorage: ogni giocatore
vedeva la propria foto solo sul proprio dispositivo, mai quella dei
compagni nella rosa (Squadra). Ora vengono caricate in un bucket
pubblico dedicato (avatar-giocatori, migration m6) e il componente
Avatar carica l'URL pubblico con fallback su numero/iniziali se assente
o non ancora caricata.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Solo whitespace: tabelle allineate, enfasi normalizzata (* -> _), a capo
coerenti. Nessuna modifica di contenuto.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Al primo accesso l'app collega da sola l'account Google al giocatore
la cui email registrata coincide (case-insensitive), invece di far
scegliere il nome da un elenco. Senza corrispondenza compare solo un
messaggio d'errore con un pulsante per uscire e riprovare con un altro
account: nessuna scelta manuale di ripiego.
- Migration m5: colonna `email` su giocatori_squadra, seed per Ivan
Cacciari e Davide Grilli, trigger esteso per richiedere anche il
match email oltre allo slot libero.
- slotPerEmail() sostituisce slotLiberi() (rimossa, senza più
chiamanti in produzione).
Co-Authored-By: Claude Mythos <noreply@anthropic.com>
Checklist "Fine lavoro" di AGENTS.md per il lavoro dei due commit precedenti.
- DATABASE.md: profili_giocatore creata, user_roles come fonte dei permessi, e
la nuova sezione Storage per il bucket privato.
- ARCHITECTURE.md: autenticazione e ruoli tra i punti fermi, comandi dello stack
Supabase locale; gli stessi comandi in CLAUDE.md.
- CHANGELOG.md: la voce è marcata come presente su develop e non ancora in
produzione.
- PROJECT_STATE.md: i cinque passaggi per attivare login e dashboard in
produzione, in ordine, con il redirect URI di Google e la query per il primo
admin. Segnalato che dev e produzione condividono lo stesso database.
- TODO.md: la dashboard esce dal backlog; entra il profilo lato giocatore, senza
il quale la dashboard resterebbe senza dati.
- modules/profilo-giocatore.md: registrate due scelte fatte in corso d'opera —
solo Google al posto di "Google oppure Email", e "Visualizza profilo" come
scheda in linea invece di una schermata separata.
ROADMAP.md resta con la casella non spuntata: la funzionalità è su develop, non
ancora rilasciata.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Documentation:
- Add docs/README.md, the documentation index that AGENTS.md pointed to as the
first file to read but which did not exist.
- One home per piece of information: the feature list stays in ROADMAP.md,
CHANGELOG.md records only when something shipped, TODO.md only ongoing work.
Reconcile the entries that had drifted (CSI was both done and pending;
pagelle, badge social and serie were missing from the roadmap).
- Rewrite DATABASE.md as tables: add giocatori_squadra (already created by a
migration) and profili_giocatore (planned in DD-016), fix the wrong heading
levels, drop the duplicated roadmap.
- DESIGN_DECISIONS.md: move the index to the top and sort it, extract the
template into _template-dd.md.
- ARCHITECTURE.md becomes the technical reference; CLAUDE.md no longer
duplicates it.
- Add docs/EFFICIENZA_CLOUD.md with the rules previously kept in mem/,
separating what the code enforces from the goals not yet implemented.
- Fix statements the code contradicted: mutations use setQueryData rather than
invalidateQueries, and scout_sessioni and giocatori_squadra are not read by
the code yet.
- Track the v1.0 modules with no spec in docs/modules/ from TODO.md.
AI assistants:
- AGENTS.md is the single source of the rules, now including the technical
constraints only Claude Code knew about (generated files, Vite plugins, data
access) and an end-of-work checklist that applies to every assistant.
- CLAUDE.md and .cursor/rules/crapp.mdc point to AGENTS.md instead of
restating it.
- Remove the five .cursor/*.md files, which Cursor never loaded.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>