docs(planning): archivia v2.1/v2.2/v2.3 e documenta v2.4

.planning/ documentava in dettaglio cio che era vecchio e per niente cio
che e in produzione: le fasi 11-22 (v2.1 e v2.2, chiuse a giugno) erano
ancora in phases/ mentre v1.0 e v2.0 stavano gia in milestones/, e il
lavoro degli ultimi due mesi - gate OTP e ciclo di vita dei retainer, cioe
quello che gira su hub.iamcavalli.net - non aveva nessuna cartella.

- phases/{11,12,14} -> milestones/v2.1-phases/, phases/{18..22} ->
  milestones/v2.2-phases/. Ora phases/ contiene solo la milestone in
  corso, che e quello che state.cjs conta per il progresso
- v2.1-ROADMAP.md ricostruito: era l'unica milestone senza archivio,
  interrotta dal reset del 19/06 e mai chiusa formalmente
- v2.3-ROADMAP.md + v2.3-REQUIREMENTS.md: v2.3 e stata eseguita fuori dal
  ciclo GSD, non esistono PLAN/SUMMARY per fase. L'archivio E la doc
- REQUIREMENTS.md riscritto per v2.4 con il backlog reale
- phases/13 e phases/26: SUMMARY ricostruiti da commit, migration e
  STATUS.md. 26 e il primo numero libero
- research/: cancellate 4 varianti dello stesso PITFALLS e FEATURES/
  SUMMARY, superati da PROJECT.md. Diverse anti-feature erano ormai
  contraddette dai fatti (il Kanban e stato costruito in Phase 19,
  l'email in v2.3, il time tracking esiste)
- cancellati UI-RULES.md e DESIGN-SYSTEM.md (CLAUDE.md li dichiara
  superseded: impongono l'inverso della regola attuale) e HANDOFF.md,
  fermo al 13/06
- SECURITY-*.md -> security/: audit chiuso, ma i report restano la doc di
  cosa e stato ruotato e perche
- PROJECT.md/MILESTONES.md/ROADMAP.md allineati: milestone corrente v2.4,
  sessione OTP 90gg non 30, migrazioni fino alla 0016

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-08 22:38:36 +02:00
parent 187550fedf
commit f7eb7eec23
86 changed files with 1659 additions and 2859 deletions
+99
View File
@@ -0,0 +1,99 @@
# Archivio milestone v2.3 — Email & Accesso
**Fasi:** 2325 · **Aperta:** 2026-06-21 · **Shipped:** 2026-07-29 (commit `27da969`)
**Requisiti:** [v2.3-REQUIREMENTS.md](v2.3-REQUIREMENTS.md)
> **Nota di archivio.** v2.3 è stata eseguita **fuori dal ciclo GSD**: non sono mai
> esistite cartelle `phases/23`, `24`, `25` con PLAN/SUMMARY. Questo file *è*
> la documentazione della milestone — non cercare altrove.
## Obiettivo
Aggiungere uno strato email all'app: gate OTP per il portale cliente e invio del
link preventivo dall'admin, con un'unica integrazione Resend condivisa.
Il portale non doveva più essere apribile col solo link: chiunque avesse l'URL
vedeva il progetto del cliente.
## Fasi
### Phase 23 — Resend Setup ✅ 2026-07-28
**Goal:** infrastruttura email condivisa.
**Requisiti:** SEND-01, SEND-02 (poi ridotti — vedi sotto).
Consegnato: `resend@6.18.1`, `src/lib/mailer.ts` (Result tipizzato, mai un catch
silenzioso), template OTP in italiano. `RESEND_API_KEY` e `RESEND_FROM` configurate
su Coolify (production **e** preview).
**Riduzione di scope del 2026-07-28:** SEND-01/SEND-02 (invio del preventivo via
email dall'admin) spostati al backlog. Il preventivo si manda a mano; l'automazione
non serviva subito. Phase 23 si è ridotta alla sola infrastruttura Resend, che il
gate OTP usa comunque.
### Phase 24 — Schema + Whitelist Admin ✅ 2026-07-28
**Goal:** l'admin gestisce la whitelist email di ogni cliente; tabelle pronte per il gate.
**Requisiti:** OTP-01. **Dipende da:** Phase 23.
Migration `0015_otp_access.sql`, **additiva pura**, applicata a prod via SSH prima
del codice dipendente: `client_emails` (whitelist, unique case-insensitive),
`otp_codes` (hash del codice, mai il codice in chiaro), `clients.sessions_valid_from`
(revoca in blocco). Conteggi pre/post identici sulle tabelle protette —
clients 4 / projects 5 / payments 11 / phases 10.
UI: sezione "Accessi al portale" in `/admin/clients/[id]` — aggiungi/rimuovi email,
"Revoca sessioni attive". Server actions in `clients/[id]/actions.ts`.
### Phase 25 — OTP Gate + Sessione ✅ 2026-07-29
**Goal:** `/client/[token]/*` richiede verifica OTP prima di mostrare la dashboard.
**Requisiti:** OTP-02..OTP-07. **Dipende da:** Phase 24.
Consegnato: `src/lib/otp.ts` (codice 6 cifre CSPRNG, hash SHA-256 con
`NEXTAUTH_SECRET`+clientId, TTL 15 minuti, monouso, max 5 tentativi),
`src/lib/client-session.ts` (cookie HMAC per-cliente `ch_sess_<id>`, httpOnly +
secure + SameSite=lax, `path=/client`), `src/lib/client-gate.ts`, le route
`/api/client/otp/request|verify`, il componente `OtpGate`.
**Scostamento dalla spec del 21/06:** sessione **90 giorni** invece di 30 — rientro
più fluido, compensato da OTP-08 (revoca in blocco lato admin).
## Catena di dipendenze
```
Phase 23 (Resend SDK + env)
└── Phase 24 (schema additivo: client_emails + otp_codes)
└── Phase 25 (gate OTP + sessione cookie 90gg)
```
## Copertura requisiti
| Requisito | Fase | Esito |
|---|---|---|
| SEND-01, SEND-02 | 23 | ⏭ Rinviati al backlog il 2026-07-28 |
| OTP-01 | 24 | ✅ |
| OTP-02 … OTP-07 | 25 | ✅ |
| OTP-08 (revoca) | 24 | ✅ |
## Verifica in produzione (2026-07-29, `hub.iamcavalli.net`)
Gate mostrato senza cookie e **zero dati di progetto nell'HTML** (12.487 byte);
email fuori e dentro whitelist danno risposta identica e solo la seconda genera un
OTP; codice sbagliato rifiutato, corretto accettato; cookie `ch_sess_<id>` con
`Secure` + `HttpOnly` + `SameSite=lax` + `Max-Age=7776000`; rientro col cookie
mostra la dashboard; la sessione di un cliente sull'URL di un altro mostra il gate.
Nessun errore d'invio nei log del container. Dati di test rimossi, tabelle protette
invariate.
## Lezioni
Le due lezioni operative di questa milestone (il gate non va nel layout App Router;
ricreare il dominio su Resend rigenera la chiave DKIM) sono in `STATUS.md`,
sezione "Lezioni operative" — è lì che si vanno a cercare.
## Strascico alla chiusura
La whitelist è stata seedata solo con `mario@test.it` (cliente di test). Tre clienti
reali su quattro hanno whitelist vuota e finché lo è **il loro portale non è
accessibile**. Voce aperta in `STATUS.md`.