--- phase: 20-knowledge-base-cliente plan: 01 type: execute wave: 1 depends_on: [] files_modified: - src/db/migrations/0009_client_transcripts.sql autonomous: false requirements: - KB-01 must_haves: truths: - "Il file 0009_client_transcripts.sql esiste con DDL completo e idempotente" - "La tabella client_transcripts è presente nel database di produzione" - "Il codice schema-dipendente dei piani successivi non viene pushato prima che la migration sia applicata" artifacts: - path: "src/db/migrations/0009_client_transcripts.sql" provides: "DDL CREATE TABLE IF NOT EXISTS client_transcripts con tutti i campi D-01/D-02" contains: "CREATE TABLE IF NOT EXISTS client_transcripts" key_links: - from: "src/db/migrations/0009_client_transcripts.sql" to: "database produzione" via: "SSH tunnel + psql/docker exec" pattern: "CREATE TABLE IF NOT EXISTS client_transcripts" --- Scrivere il file SQL della migration manuale per la tabella `client_transcripts` e applicarlo al database di produzione prima di pushare qualsiasi codice schema-dipendente. Purpose: Il progetto ha `drizzle-kit generate` rotto (meta-snapshot fuori sync). Ogni schema change richiede SQL a mano applicato a prod via SSH PRIMA del codice dipendente — invariante bloccante LOCKED in CLAUDE.md. Output: `src/db/migrations/0009_client_transcripts.sql` applicato a prod. Il checkpoint umano sblocca i piani 20-02 e 20-03. @$HOME/.claude/get-shit-done/workflows/execute-plan.md @$HOME/.claude/get-shit-done/templates/summary.md @.planning/ROADMAP.md @.planning/phases/20-knowledge-base-cliente/20-CONTEXT.md Da 0005_phase_10_crm_leads_activities_reminders.sql: ```sql CREATE TABLE IF NOT EXISTS "activities" ( "id" text PRIMARY KEY NOT NULL, "lead_id" text NOT NULL, ... FOREIGN KEY ("lead_id") REFERENCES "leads"("id") ON DELETE cascade ); CREATE INDEX IF NOT EXISTS "activities_lead_id" ON "activities"("lead_id"); ``` Da 0008_offer_tier_schema.sql (header): ```sql -- Phase 12: Offer Editor — additive schema ... -- All statements additive/idempotent. No drops/truncates (Data Safety LOCKED). ``` Task 1: Scrivere src/db/migrations/0009_client_transcripts.sql src/db/migrations/0009_client_transcripts.sql - src/db/migrations/0008_offer_tier_schema.sql (pattern header + CREATE TABLE IF NOT EXISTS) - src/db/migrations/0005_phase_10_crm_leads_activities_reminders.sql (pattern FK + index per tabelle CRM) - .planning/phases/20-knowledge-base-cliente/20-CONTEXT.md (D-01 e D-02 — campi esatti) Creare il file `src/db/migrations/0009_client_transcripts.sql` con il seguente contenuto esatto: ```sql -- Phase 20: Knowledge Base Cliente — tabella transcript datati per lead/cliente -- Additive only. No drops/truncates (Data Safety LOCKED). -- Applicare a prod via SSH tunnel PRIMA di pushare il codice dipendente (D-03). CREATE TABLE IF NOT EXISTS client_transcripts ( id text PRIMARY KEY NOT NULL, lead_id text REFERENCES leads(id) ON DELETE CASCADE, client_id text REFERENCES clients(id) ON DELETE CASCADE, title text, content text NOT NULL, call_date date NOT NULL, created_at timestamp with time zone NOT NULL DEFAULT now() ); CREATE INDEX IF NOT EXISTS client_transcripts_lead_id_idx ON client_transcripts (lead_id); CREATE INDEX IF NOT EXISTS client_transcripts_client_id_idx ON client_transcripts (client_id); CREATE INDEX IF NOT EXISTS client_transcripts_call_date_idx ON client_transcripts (call_date DESC); ``` Note sui campi (da D-01/D-02): - `id`: text PK NOT NULL — nanoid inserito dall'app, nessun DEFAULT SQL (pattern progetto) - `lead_id`: nullable FK → leads(id) ON DELETE CASCADE (D-01) - `client_id`: nullable FK → clients(id) ON DELETE CASCADE (D-01) - `title`: text nullable (D-02) — titolo libero opzionale - `content`: text NOT NULL (D-02) — testo grezzo illimitato - `call_date`: date NOT NULL (D-02) — giorno della call, non timestamp - `created_at`: timestamp with time zone NOT NULL DEFAULT now() (D-02) Gli indici su lead_id, client_id e call_date ottimizzano le query per lead (Phase 20) e future query per client_id (Phase 21+). grep -c "CREATE TABLE IF NOT EXISTS client_transcripts" /Users/simonecavalli/Vault/IAMCAVALLI/src/db/migrations/0009_client_transcripts.sql - File `src/db/migrations/0009_client_transcripts.sql` esiste - Contiene `CREATE TABLE IF NOT EXISTS client_transcripts` - Contiene `lead_id text REFERENCES leads(id) ON DELETE CASCADE` - Contiene `client_id text REFERENCES clients(id) ON DELETE CASCADE` - Contiene `content text NOT NULL` - Contiene `call_date date NOT NULL` - Contiene `created_at timestamp with time zone NOT NULL DEFAULT now()` - Contiene 3 `CREATE INDEX IF NOT EXISTS` (lead_id_idx, client_id_idx, call_date_idx) - Nessun DROP o TRUNCATE nel file File SQL completo e idempotente scritto, pronto per applicazione a prod. Task 2: [BLOCKING] Applicare migration 0009 al database di produzione via SSH - src/db/migrations/0009_client_transcripts.sql (verificare il contenuto prima di applicare) La migration va applicata a prod via SSH tunnel PRIMA di eseguire i piani 20-02 e 20-03. Il database di produzione è accessibile solo tramite tunnel SSH. **Passi:** 1. Aprire un terminale locale e avviare il tunnel SSH: ```bash ssh -L 54321:localhost:54321 root@178.104.27.55 ``` Lasciare questo terminale aperto per tutta la durata. 2. In un altro terminale, applicare la migration con psql (il DATABASE_URL del progetto punta a 127.0.0.1:54321): ```bash cd /Users/simonecavalli/Vault/IAMCAVALLI psql "$(grep DATABASE_URL .env.local | cut -d= -f2-)" -f src/db/migrations/0009_client_transcripts.sql ``` Oppure, se il DATABASE_URL non funziona direttamente, applicare via docker exec sul server: ```bash # Sul server (nella sessione SSH aperta al punto 1): docker exec -i psql -U -d < /path/to/0009_client_transcripts.sql ``` (Il nome del container e le credenziali sono in `.env.local` o nei secret Coolify.) 3. Verificare che la tabella esista: ```bash psql "$(grep DATABASE_URL .env.local | cut -d= -f2-)" -c "\d client_transcripts" ``` Deve mostrare le colonne: id, lead_id, client_id, title, content, call_date, created_at. 4. Chiudere il tunnel SSH solo dopo aver verificato il punto 3. Il file SQL 0009_client_transcripts.sql è stato scritto da Claude nel Task 1. Questo task richiede solo che tu applichi la migration a prod — nessun codice da scrivere. Eseguire in locale (con tunnel attivo): ```bash psql "$(grep DATABASE_URL .env.local | cut -d= -f2-)" -c "SELECT column_name, data_type, is_nullable FROM information_schema.columns WHERE table_name = 'client_transcripts' ORDER BY ordinal_position;" ``` Risultato atteso: 7 righe con le colonne id, lead_id, client_id, title, content, call_date, created_at. Digita "migration applicata" dopo aver verificato che \d client_transcripts mostra le 7 colonne attese. - La tabella `client_transcripts` esiste nel database di produzione - `\d client_transcripts` mostra 7 colonne: id (text), lead_id (text, nullable), client_id (text, nullable), title (text, nullable), content (text, NOT NULL), call_date (date, NOT NULL), created_at (timestamptz, NOT NULL) - 3 indici presenti: client_transcripts_lead_id_idx, client_transcripts_client_id_idx, client_transcripts_call_date_idx Database di produzione aggiornato con la tabella client_transcripts. I piani 20-02 e 20-03 sono sbloccati. ## Trust Boundaries | Boundary | Descrizione | |----------|-------------| | developer → database prod | SQL applicato manualmente via SSH tunnel autenticato | ## STRIDE Threat Register | Threat ID | Category | Component | Disposition | Mitigation Plan | |-----------|----------|-----------|-------------|-----------------| | T-20-01 | Tampering | 0009_client_transcripts.sql | mitigate | Revisione manuale del contenuto prima dell'applicazione (Task 2 step 1); file in version control | | T-20-02 | Repudiation | Applicazione migration prod | accept | L'operazione è tracciata nel git commit di questo piano; nessun audit log applicativo necessario per DDL | | T-20-03 | Denial of Service | DROP/TRUNCATE accidentale | mitigate | Il file usa solo CREATE TABLE IF NOT EXISTS + CREATE INDEX IF NOT EXISTS; nessuna istruzione distruttiva presente | ```bash # Verificare che il file SQL esista e contenga i campi obbligatori grep -E "CREATE TABLE IF NOT EXISTS client_transcripts|content.*text NOT NULL|call_date.*date NOT NULL|lead_id.*REFERENCES leads|client_id.*REFERENCES clients" \ /Users/simonecavalli/Vault/IAMCAVALLI/src/db/migrations/0009_client_transcripts.sql # Verificare assenza di istruzioni distruttive grep -i "drop\|truncate" /Users/simonecavalli/Vault/IAMCAVALLI/src/db/migrations/0009_client_transcripts.sql | wc -l # Atteso: 0 ``` - `src/db/migrations/0009_client_transcripts.sql` esiste con DDL completo e idempotente (KB-01) - La tabella `client_transcripts` è presente nel database di produzione con le 7 colonne di D-02 - Nessuna istruzione distruttiva nel file SQL - Il checkpoint umano è stato completato e segnalato con "migration applicata" Dopo il completamento del Task 2, creare `.planning/phases/20-knowledge-base-cliente/20-01-SUMMARY.md` con: - Migration file creato: `src/db/migrations/0009_client_transcripts.sql` - Migration applicata a prod: sì/no - Colonne verificate: lista delle 7 colonne confermate