d14f95c80d
Cosa è stato verificato davvero: build e lint verdi, migration 0020
applicata a prod prima del push con la colonna riletta a conferma,
deploy atterrato (immagine taggata 44be190 = HEAD).
Cosa no, e ora è scritto: le pagine non sono state rese a runtime.
.env.local è scaduto su tre fronti — ADMIN_PASSWORD, NEXTAUTH_SECRET e
la password del DB — e l'host che dichiara (…:5432) è chiuso dal
firewall; il DB vero sta su 127.0.0.1:54321. Estrarre la password viva
dal container è bloccato dal classifier e non è stato aggirato. La nota
tecnica che dava per buone quelle credenziali diceva il falso.
Aggiunto come vedere se un deploy è atterrato: il tag dell'immagine in
docker ps è lo SHA del commit.
REQUIREMENTS: HUB-14 e HUB-15 registrati come fatti, HUB-16 apre la
verifica a mano che resta da fare, e il riallineamento di .env.local
entra fra le cose aperte.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
128 lines
12 KiB
Markdown
128 lines
12 KiB
Markdown
# Requirements: ClientHub v2.5 Audit
|
||
|
||
**Definiti:** 2026-08-16 (piano approvato) · **rivisti:** 2026-08-18 (motore)
|
||
**Core Value della milestone:** L'imprenditore paga un'analisi del suo sito e riceve un
|
||
documento che gli dice, con numeri misurati, cosa non funziona e cosa costa — non un
|
||
elenco di quaranta punti generato da un tool gratuito.
|
||
|
||
Milestone precedente: [v2.4 Post-vendita](milestones/v2.4-REQUIREMENTS.md), chiusa 2026-08-08.
|
||
|
||
Piani di riferimento (fuori dal repo, in `~/.claude/plans/`):
|
||
`dovremmo-fare-una-cosa-woolly-puddle.md` (documento, editor, template) +
|
||
`vorrei-solo-farti-capire-radiant-valley.md` (motore — sostituisce §6/§7 del primo).
|
||
|
||
## Il prodotto
|
||
|
||
Tre livelli venduti, che sono **configurazioni di un unico documento**, non tre documenti:
|
||
|
||
| Livello | Blocchi inclusi |
|
||
|---|---|
|
||
| **Radiografia** | 1, 2, 2b, 3, 4, 5, 8, 9 |
|
||
| **Prima/Dopo** | + 6 (il redesign), 6b (cosa il redesign non risolve) |
|
||
| **Rotta** | + 7 (le ottimizzazioni, con priorità e impegno in giornate) |
|
||
|
||
I blocchi non pertinenti **non esistono nel DOM**, non sono nascosti via CSS.
|
||
|
||
## Requisiti
|
||
|
||
### Motore (Phase 27)
|
||
|
||
- [x] **AUD-01**: Schema additivo per audit, finding, ottimizzazioni, rubrica, esiti, run e visite — *migration `0017_audits.sql`, in prod 2026-08-18*
|
||
- [x] **AUD-02**: La rubrica del motore (264 voci falsificabili) vive in `checklist_items`, non nel documento — *in prod 2026-08-18*
|
||
- [x] **AUD-03**: Le fonti raccolgono **rilevazioni, non stime**: PageSpeed (153 audit sul DOM renderizzato), CrUX (utenti reali), Wayback, RDAP, robots/sitemap/JSON-LD, header — *`src/lib/audit/sources/`, provato sul campo 2026-08-18, non ancora pushato*
|
||
- [x] **AUD-04**: Ogni fonte fallisce in modo **non fatale** e dice *perché*: "non ha risposto" e "ha risposto che non ci sono dati" sono informazioni diverse
|
||
- [x] **AUD-05**: Quando CrUX non ha dati di campo il documento lo **dice** ("i visitatori non sono abbastanza numerosi perché Google raccolga dati"), non lascia un buco — *`nota` in `crux.ts`; il caso "zero dati" resta da vedere su un sito vero*
|
||
- [ ] **AUD-06**: Ogni output di modello è validato con Zod, `safeParse`, fallimento duro — nessun loop di riparazione (precedente: `src/lib/proposal/schema.ts`)
|
||
- [ ] **AUD-07**: Quattro sub-agent in parallelo (checklist, visivo, storico, tecnico) più un sintetizzatore che **incrocia** le loro osservazioni in un solo finding con più evidenze indipendenti
|
||
- [ ] **AUD-08**: Massimo **10 finding**, ordinati per impatto su tre soli valori (`alto|medio|basso`); la sfumatura sta nell'ordine dentro il gruppo
|
||
- [ ] **AUD-09**: Disciplina sui numeri imposta nel prompt di sistema — un numero entra nel documento solo se misurato, e ogni numero consegnato è rintracciabile in `audit_runs.raw`
|
||
- [ ] **AUD-10**: Fan-out con tetto di concorrenza e retry con backoff sulle 429/529 di Anthropic
|
||
- [ ] **AUD-11**: Heartbeat a ogni passo su `audit_runs`; una run senza battito va in `error` e "Rilancia" riparte dall'ultimo passo completato *(un redeploy Coolify uccide un job in corso)*
|
||
|
||
### Storage immagini (Phase 28)
|
||
|
||
- [ ] **AUD-12**: Volume persistente Coolify su `/app/uploads`, lettura da `/api/uploads/[...path]` con guardia sul path traversal, whitelist MIME e limite di dimensione
|
||
- [ ] **AUD-13**: Due immagini caricate a mano per audit (hero **prima** e **dopo** del redesign, JPG ≤ 512 KB); due scritte dalla pipeline (screenshot mobile e desktop da PageSpeed)
|
||
|
||
### Editor admin (Phase 29)
|
||
|
||
- [ ] **AUD-14**: Creazione **manuale** di un audit (livello, profilo, URL, cliente/lead). L'ingresso Whop è predisposto nello schema (`origin`, `external_ref`) ma **non costruito**
|
||
- [ ] **AUD-15**: Editor a payload intero (modello: `admin/offers/actions.ts`) che **salva sempre, anche a metà** — tutti i campi di contenuto sono nullable, la validazione di completezza scatta solo alla consegna
|
||
- [ ] **AUD-16**: Riordino di finding e ottimizzazioni con `@dnd-kit/sortable`, con re-sync degli id dei figli
|
||
- [ ] **AUD-17**: Il **blocco 8 (La direzione) resta manuale, foglio bianco** — è il blocco che giustifica il prezzo; se diventa formula il cliente lo sente
|
||
- [ ] **AUD-18**: Il registro delle visite è visibile nell'editor, in ordine cronologico
|
||
|
||
### Documento pubblico (Phase 30)
|
||
|
||
- [ ] **AUD-19**: `/audit/[slug]` — pagina privata, `X-Robots-Tag: noindex, nofollow`, rate limit sul matcher di `proxy.ts`
|
||
- [ ] **AUD-20**: Il template è **congelato alla creazione** (`template_version`): migliorare il documento tocca gli audit successivi, mai quelli già consegnati
|
||
- [ ] **AUD-21**: PDF via **print CSS**, non libreria: interruzioni di pagina corrette, slider impilato in due immagini, **nessun marcatore di lavorazione sopravvissuto**
|
||
- [ ] **AUD-22**: In **scala di grigi** impatti e metriche restano distinguibili — il colore non può essere l'unico portatore di informazione
|
||
- [ ] **AUD-23**: Il documento usa **il design system dell'area admin** ("Quiet Luxury", `design-reference/DESIGN-SYSTEM.md`): token semantici, Plus Jakarta Sans per il testo, **Geist Mono per metriche, punteggi e date**, e i primitivi già esistenti (`StatusBadge` per gli impatti). *Decisione del 2026-08-18, sostituisce la deroga tipografica prevista dal piano.* Due conseguenze: i font sono già self-hostati da `next/font/google`, quindi la CSP `font-src 'self'` è soddisfatta senza lavoro; e il documento **non aggiunge debito a DEBT-01** perché nasce già a token.
|
||
- [ ] **AUD-24**: Tracciamento delle aperture (`view`) e delle stampe (`print`) via isola client + Server Action; **un admin loggato non viene contato** (altrimenti i numeri li inquiniamo noi rileggendo le bozze)
|
||
- [ ] **AUD-25**: L'IP non si salva in chiaro — SHA-256 di `ip + NEXTAUTH_SECRET`, come il digest del gate admin
|
||
|
||
## Vincoli che questa milestone tocca
|
||
|
||
- **LOCKED #5 (no file hosting)** — emendato limitatamente agli asset di audit, deroga già annotata in `CLAUDE.md`. Non estendere ad altre entità.
|
||
- **Nessun renderer headless, da nessuna parte.** Il VPS non regge Chromium (RAM), e non serve: gli audit Lighthouse arrivano già fatti sul DOM renderizzato.
|
||
|
||
## Modifiche hub (richieste 2026-08-18, in corso)
|
||
|
||
Fuori dalla milestone v2.5, che è in pausa. Piano in
|
||
`~/.claude/plans/sei-arrivato-qua-search-recursive-kettle.md`.
|
||
|
||
- [x] **HUB-01**: Via il tab Commenti dal progetto — `/admin/conversazioni` li aggrega già tutti con l'etichetta dell'entità. *Perde solo la risposta sulla singola entità, che era già confluita sul thread generale.*
|
||
- [x] **HUB-02**: Via il timer dalla lista progetti — si avvia dove c'è il contesto
|
||
- [x] **HUB-03**: Riepilogo soldi + avanzamento in testa al progetto, senza query nuove
|
||
- [x] **HUB-04**: Timer per fase e task — migration `0018`, `ON DELETE SET NULL` perché le ore sopravvivono al task
|
||
- [x] **HUB-05**: Inbox in cima alla dashboard, con da-quanto-aspetta e contesto del messaggio
|
||
- [x] **HUB-06**: Analytics per linea di prodotto (Entry/Signature/Retainer) dalla tassonomia, con l'incassato non attribuibile mostrato a parte
|
||
- [x] **HUB-07**: Timeline delle consegne con semaforo ritardo/anticipo; scadenza dedotta da offerta + durata, `projects.due_date` come override
|
||
- [x] **HUB-08**: `POST /api/webhooks/lead` — un endpoint per form del sito e bridge, con dedup sull'email
|
||
- [ ] **HUB-09**: Confermare la forma del payload Elementor con un invio **vero** — oggi è gestita in modo difensivo
|
||
- [ ] **HUB-10**: `LEAD_WEBHOOK_SECRET` su Coolify — finché manca, la route risponde 403 a tutti
|
||
- [ ] **HUB-11**: TidyCal. **[BLOCCANTE]** Niente webhook (loro FAQ): serve polling della REST API. Path e filtri stanno dietro il login → servono token o documentazione dall'utente
|
||
- [ ] **HUB-12**: Alleggerire l'hub. Senza perimetro: si definisce guardando cosa è poco usato
|
||
- [ ] **HUB-13**: Whop → progetto + audit automatico. Dipende dal motore v2.5 (AUD-06→11)
|
||
- [x] **HUB-14**: Rifiniture dall'uso reale del pannello — rinomina di un valore di tassonomia con propagazione alle fasi dei progetti, stato task "In revisione", tab pagamenti con ordine stabile e importi a mano, riordino task per trascinamento. *In prod 2026-08-20, migration `0019`.*
|
||
- [x] **HUB-15**: Portale cliente — barra di avanzamento compatta e a tutta larghezza, card offerta senza accordion, "Valore dell'offerta" con override admin (`project_offers.offer_value_override`). *In prod 2026-08-21, migration `0020`. Serviva perché la somma dei prezzi di catalogo mostrava €20.250 su offerte vendute a 7.000 e 5.500.*
|
||
- [ ] **HUB-16**: Guardare a mano HUB-14 e HUB-15 in produzione — le scritture nuove (`reorderTasks`, `updatePaymentField`, `clearPaymentOverride`, `updateOfferValueOverride`) e il drag non sono mai state **cliccate**, e dal 2026-08-21 la verifica locale contro i dati veri non è più possibile
|
||
|
||
## Backlog (ereditato, nessuno in corso)
|
||
|
||
- [ ] **SEND-01 / SEND-02** — Invio del link `/preventivo/[slug]` via email. Il mailer è già in produzione dalla v2.3: manca il pulsante e l'action. *Rinviati il 2026-07-28.*
|
||
- [ ] **PROP-03** — Stripe Payment Link sul deck pubblico del preventivo.
|
||
- [ ] **PROP-04** — Auto-provisioning cliente / progetto / fasi al passaggio del lead a "Vinto".
|
||
- [ ] **RET-06** — Canoni mensili tracciabili. **Serve una tabella nuova**: `payments` è protetta dai vincoli di Data Safety.
|
||
- [ ] **OFFER-14** — Sezioni analitiche stile Notion sull'offerta.
|
||
- [ ] **ARCH-01** — Split del modulo "compartimento stagno" in un deploy separato. *Solo se cresce.*
|
||
- [ ] **DEBT-01** — Debito design: ~40 file, ~450 occorrenze di palette raw/hex al posto dei token. Cluster in `/admin/projects/[id]` (~182), `/admin/offers/[id]/edit` (~79), `/admin/clients/[id]` (~59), `/quote/[token]` (~48, ed è rivolto al cliente), `ChatPanel` (37), `ui/dialog.tsx`. *Misurato il 2026-08-08.*
|
||
- [ ] **DEBT-02** — Tabelle legacy `service_catalog` / `offer_services` / `offer_micro_services`; `createService` / `serviceSchema` dead code.
|
||
|
||
## Rinviati esplicitamente da v2.5
|
||
|
||
- **Allegato tecnico** — seconda vista sugli stessi finding con registro da sviluppatore (selettori, file, stime). Renderebbe vera la promessa del blocco 9 *"il documento resta tuo e puoi darlo a chiunque lavorerà sul sito"*. Da valutare **dopo il primo audit consegnato**.
|
||
- **Affettare lo screenshot a pagina intera** — richiede `sharp`, da verificare su `node:20-alpine`. Non serve in fase 1: `final-screenshot` (250×498) è leggibile.
|
||
- **Ingresso via webhook Whop** — schema predisposto, costruzione in fase 2.
|
||
|
||
## Aperto, non un requisito
|
||
|
||
**`.env.local` da riallineare a Coolify.** `ADMIN_PASSWORD`, `NEXTAUTH_SECRET` **e la
|
||
password del DB** sono stale, e l'host che il file dichiara (`178.104.27.55:5432`) è chiuso
|
||
dal firewall — il DB vero è su `127.0.0.1:54321` dietro tunnel. Finché resta così **una
|
||
modifica al portale si verifica solo in produzione**: build, migration e query di controllo,
|
||
poi occhio umano sul sito. Recuperare la password viva dal container è bloccato dal
|
||
classifier dei permessi e non va aggirato: la sblocca l'utente copiando le variabili da
|
||
Coolify. Le migration non ne soffrono (`docker exec` non usa quelle credenziali).
|
||
|
||
**Whitelist del portale vuota per 3 clienti su 4.** La migration 0015 ha seedato solo
|
||
`mario@test.it`. Protocollo Estetico, Caruso Speaker e Teckell hanno whitelist vuota e
|
||
finché lo è **il loro portale non è accessibile**. Si popola da `/admin/clients/<id>` →
|
||
"Accessi al portale", poi va reinviato il link.
|
||
|
||
## Fuori scope
|
||
|
||
- Tabella utenti / multi-admin: l'auth resta una singola credenziale da env.
|
||
- File hosting per i documenti del portale cliente: restano URL esterni (LOCKED #5, non emendato per quelli).
|