Files
clienthub/STATUS.md
T
simone 0aad9caf46 docs: STATUS.md unico documento narrativo, STATE.md a digest
STATUS.md e .planning/STATE.md raccontavano la stessa storia in due posti
con date diverse, e STATE.md si contraddiceva: frontmatter fermo a
"milestone v2.3 / executing" quando v2.3 e shipped, l'anteprima admin data
per "NON committata" mentre e il commit 187550f deployato l'8 agosto,
Session Continuity ferma al 29/07 e la tabella Performance Metrics spezzata
a meta.

Il template GSD dice esplicitamente che STATE.md deve stare sotto le 100
righe ("a DIGEST, not an archive"): ne aveva 177, quasi tutte narrativa.

- STATUS.md assorbe la narrativa e diventa l'unico posto dove si racconta
  il progetto. Nuova sezione "Lezioni operative" per le trappole in cui si
  ricasca: il gate OTP non va nel layout App Router (il payload RSC
  trapela), ricreare il dominio Resend rigenera la chiave DKIM, .env.local
  non e allineato a produzione dal 28/07, Playwright non funziona contro
  npm run dev
- STATE.md sceso a 98 righe, con i campi che state.cjs legge davvero.
  Frontmatter corretto a v2.4, blocchi gia risolti (DKIM, env Coolify)
  rimossi, nulla risulta piu "non committato"
- il debito design era sottostimato: non 11 pagine ma ~40 file e ~450
  occorrenze. Esclusi perche legittimi AdminSidebar (eccezione brand),
  mailer.ts (HTML email) e i colori di stato di StatusBadge

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 22:38:49 +02:00

8.9 KiB
Raw Blame History

ClientHub (IAMCAVALLI) — Status

Ultimo aggiornamento: 2026-08-08

Questo è l'unico documento narrativo del progetto: a che punto siamo, cosa manca, cosa abbiamo imparato. .planning/STATE.md è il digest che leggono i comandi /gsd-* — non raddoppia questo file, ci rimanda.

Stato attuale

In produzione su hub.iamcavalli.net (Coolify/Hetzner, deploy automatico su push a main via Gitea). Build verde, npm audit pulito.

Milestone v2.4 "Post-vendita" — Phase 13 e Phase 26 consegnate e in produzione. Nessun lavoro in sospeso non committato.

Milestone Fasi Stato
v2.4 Post-vendita 13, 26 🔨 in corso — consegnato tutto ciò che era pianificato
v2.3 Email & Accesso 2325 shipped 2026-07-29, verificata E2E
v2.2 Sales Loop 1822 shipped 2026-06-20
v2.1 Offer Studio + CRM 11, 12, 14 chiusa per reset 2026-06-19
v1.0 + v2.0 110 shipped giugno 2026

Fatto

v2.4 — Post-vendita

  • [Phase 13, prod 2026-08-01] Ciclo di vita dei servizi ricorrenti. project_offers.status (attivo/sospeso/cessato) + end_date (migr. 0016). Prima un retainer non poteva finire e il forecast lo sommava a ogni mese in eterno. Comandi Sospendi/Riattiva/Cessa nella tab Offerte; il cliente vede stato, "attivo dal" e canone mensile. Dettaglio: .planning/phases/13-…/13-SUMMARY.md
  • [Phase 26, prod 2026-08-08] Anteprima admin del portale + toggle password. ?preview=1 più una sessione Auth.js valida apre il portale di un cliente in sola lettura, senza passare dal gate OTP. Deviazione consapevole dal vincolo LOCKED #4, annotata in CLAUDE.md. Dettaglio: .planning/phases/26-…/26-SUMMARY.md

v2.3 — Email & Accesso

  • Gate OTP sul portale cliente: whitelist email per cliente (client_emails), codice a 6 cifre via Resend, sessione firmata 90 giorni, revoca in blocco dall'admin (clients.sessions_valid_from). Migr. 0015. Verificato E2E in produzione. Archivio: v2.3-ROADMAP.md

Prima di v2.3

  • [2026-07] Audit di sicurezza chiuso: 4 vulnerabilità risolte e deployate, slug clienti ruotati a 12 char CSPRNG, INTERNAL_SECRET e ADMIN_PASSWORD configurati su Coolify. Report in .planning/security/.
  • Design system "Quiet Luxury": dashboard, liste (Clienti/Offerte/Catalogo/ Preventivi/Progetti), Conversazioni, Impostazioni, Pipeline+Kanban, dettaglio Lead e portale cliente base sono a token semantici e dual-theme.
  • Tassonomie centralizzate in Impostazioni (modello Notion, pool persistenti src/lib/taxonomy.ts).
  • Lead → Cliente: clients.email/phone + leads.archived (migr. 0011). convertLeadToClient riusa createClientCore, porta i transcript, archivia il lead mantenendo "won".
  • Offerta → Fasi/Task: importOfferIntoProject crea fasi raggruppando i servizi del tier per services.fase.
  • Offerte (modello + UI): offer_macros.offer_type ('una_tantum'|'retainer') + toggle "Modalità" nell'editor (migr. 0012). Tab Offerte a 2 step.

Da fare

  • Whitelist portale: dei 4 clienti solo uno ha email autorizzate. Chi ha la whitelist vuota non entra nel proprio portale — si popola da /admin/clients/<id> → "Accessi al portale", poi va reinviato il link.
  • Fasi/Task dall'offerta funzionano solo se i servizi hanno il campo Fase valorizzato nel Catalogo (altrimenti finiscono in "Generale").
  • Debito design (DEBT-01): ~40 file, ~450 occorrenze di palette Tailwind raw e hex literal invece dei token semantici. I cluster: /admin/projects/[id] e i suoi tab (~182), /admin/offers/[id]/edit (~79), /admin/clients/[id] (~59), tutto /quote/[token] (~48, ed è rivolto al cliente), ChatPanel (37), più ui/dialog.tsx che propaga il look vecchio a ogni modale. Esclusi perché legittimi: AdminSidebar (eccezione brand documentata), src/lib/mailer.ts (HTML email), i colori di stato di StatusBadge (sanzionati dal design system, hanno già le varianti dark:).
  • Micro legacy "Mantenimento" senza tier: valutare se rimuoverlo/normalizzarlo.
  • Backlog: canoni mensili tracciabili (RET-06 — serve una tabella nuova), PROP-03 (Stripe sul deck), PROP-04 (auto-provisioning al "Vinto"), SEND-01/02 (invio preventivo via email — il mailer è già pronto). Elenco completo in .planning/REQUIREMENTS.md.

Lezioni operative

Cose imparate a caro prezzo. Non sono documentazione di feature: sono trappole in cui si ricasca.

  • Il gate OTP non va nel layout. Prima implementazione: gate in client/[token]/layout.tsx che rendeva <OtpGate/> al posto di {children}. Non protegge nulla. Nell'App Router il segmento page è renderizzato in parallelo al layout: la dashboard spariva a schermo ma fasi, task e pagamenti restavano leggibili nel payload RSC dell'HTML (46.907 byte → 17.594 dopo il fix). Il gate sta in cima alla page, prima di ogni query, via getClientGate(). Ogni nuova route sotto /client/[token]/ deve fare lo stesso.
  • Ricreare il dominio su Resend rigenera la chiave DKIM. Se il dominio torna failed, non fidarsi di valori DKIM annotati in passato: rileggerli da GET /domains e confrontarli con dig +short TXT resend._domainkey.iamcavalli.net @8.8.8.8.
  • .env.local non è allineato a produzione. ADMIN_PASSWORD e NEXTAUTH_SECRET sono stati ruotati il 2026-07-28 solo su Coolify. Per costruire firme o hash validi in prod vanno letti da Coolify, non da .env.local.
  • L'API Coolify rifiuta is_build_time con 422 sul POST a /api/v1/applications/<uuid>/envs: mandare solo key, value, is_preview.
  • Playwright non funziona contro npm run dev: la CSP blocca eval e i client component non si idratano. Serve il build di produzione.

Note tecniche

  • Il DB di .env.local È la produzione (178.104.27.55). Non esiste un database di sviluppo separato: qualunque cosa si esegua in locale scrive su dati reali. Verificare i conteggi delle tabelle protette prima e dopo ogni prova.
  • Migrazioni: SQL scritto a mano in src/db/migrations/ (drizzle-kit generate è rotto — vanno tenuti in sync schema.ts e l'SQL). Si applicano da locale via SSH + docker exec, senza tunnel — procedura completa in CLAUDE.md. Il tunnel ssh -f -N -L 54321:localhost:54321 serve solo per puntare il tooling locale (es. npx tsx) al DB di prod.
  • Ordine di deploy con schema: applicare la migrazione a prod prima del push (il deploy fa girare subito il codice nuovo).
  • Coolify API: credenziali in ~/.coolify.env (formato export VAR=…, va sorgentato). App uuid xsksow44g4kcoo8wocsgkscc.
  • overrides in package.json forzano postcss >= 8.5.18 e sharp >= 0.35.0: le versioni che Next si porta dietro hanno CVE high e non c'è fix upstream. Se un aggiornamento di Next rompe qualcosa, è il primo posto dove guardare.
  • offer_micros non ha created_at (no "tier più vecchio" affidabile).
  • Debito tecnico non bloccante: tabelle legacy service_catalog / offer_services / offer_micro_services come deadweight; createService / serviceSchema dead code in src/app/admin/catalog/actions.ts.

File chiave

File Scopo
src/proxy.ts Middleware (Next 16 lo chiama proxy, non middleware)
src/lib/client-gate.ts Gate OTP — va chiamato in cima a ogni page sotto /client/[token]/
src/lib/otp.ts, client-session.ts Codici OTP e cookie di sessione 90gg
src/lib/mailer.ts Unico punto di invio email (Resend)
src/lib/forecast-queries.ts Forecast 12 mesi — rispetta stato e end_date dei retainer
src/lib/taxonomy.ts Pool tassonomie (Impostazioni)
src/app/admin/projects/project-actions.ts importOfferIntoProject, setProjectOfferLifecycle, piani pagamento
src/components/admin/tabs/OffersTab.tsx Tab Offerte + comandi ciclo di vita
src/lib/admin-queries.ts / client-view.ts I due layer separati: admin vs proiezioni client-safe
src/db/migrations/ 0011 (email/phone), 0012 (offer_type), 0015 (OTP), 0016 (ciclo di vita)

Dove sta il resto

Cosa Dove
Regole per Claude, procedure deploy/DB, vincoli LOCKED CLAUDE.md
Digest di stato per i comandi /gsd-* .planning/STATE.md
Requisiti e backlog della milestone corrente .planning/REQUIREMENTS.md
Roadmap e storico milestone .planning/ROADMAP.md, .planning/MILESTONES.md
Archivi delle milestone chiuse .planning/milestones/
Fasi della milestone in corso .planning/phases/
Report dell'audit di sicurezza (chiuso) .planning/security/
Design system e mock per pagina design-reference/