--- phase: 01-core-generation-pipeline plan: 02 subsystem: domain-logic tags: [python, pydantic, calendar, prompts, constants, domain-model] # Dependency graph requires: - phase: 01-core-generation-pipeline (plan 01) provides: backend/config.py con PROMPTS_PATH, struttura directory base provides: - CalendarService con generate_calendar() — 13 slot PN+Schwartz+fasi+nicchie+date - FormatSelector con select_format() — matrice 30 combinazioni tipo x livello - PromptService con load/list/compile — gestione prompt .txt con variabili - CANVA_FIELDS locked (33 colonne) — schema CSV Canva definitivo - 7 prompt .txt in italiano — system_prompt + topic_generator + 5 formati narrativi - Pydantic schemas — CalendarSlot, GeneratedPost, TopicResult, PostResult affects: - 01-03 (LLMService userà PromptService e CalendarService) - 01-04 (CSVBuilder userà CANVA_FIELDS e GeneratedPost schema) # Tech tracking tech-stack: added: [] patterns: - "Costanti LOCKED con assert a load-time per prevenire drift accidentale" - "Pydantic Field con constraints espliciti (max_length, ge, min_length)" - "Distribuzione PN/Schwartz verificata a doppio controllo: constants + calendar_service" - "Prompt template con {{variabile}} doppia graffa (distingue da f-string e Jinja)" key-files: created: - backend/constants.py - backend/schemas/__init__.py - backend/schemas/calendar.py - backend/schemas/generate.py - backend/services/__init__.py - backend/services/format_selector.py - backend/services/calendar_service.py - backend/services/prompt_service.py - backend/data/format_mapping.json - backend/data/prompts/system_prompt.txt - backend/data/prompts/topic_generator.txt - backend/data/prompts/pas_valore.txt - backend/data/prompts/listicle_valore.txt - backend/data/prompts/bab_storytelling.txt - backend/data/prompts/aida_promozione.txt - backend/data/prompts/dato_news.txt modified: - .gitignore (fix: backend/data/ non più ignorato interamente) key-decisions: - "CANVA_FIELDS usa _image_keyword (non _image_url): keyword testuali ora, URL Unsplash in Phase 4" - "Distribuzione L3: 4 slot totali — 2 da valore (Cattura) + 2 da riprova_sociale (Coinvolgi)" - "Nicchie: 50% generico (slot pari), 50% verticali in rotazione (slot dispari)" - "PromptService ValueError per variabili mancanti: fail esplicito, non silenzioso" patterns-established: - "Servizi puri: zero dipendenze HTTP, zero I/O async — testabili con pytest sincrono" - "FormatSelector iniettabile in CalendarService (dependency injection per test)" - "Prompt .txt scritti in italiano nativo — non tradotti da inglese" # Metrics duration: 9min completed: 2026-03-08 --- # Phase 1 Plan 02: Servizi core di dominio — CalendarService, FormatSelector, PromptService, 7 prompt italiani **CalendarService genera 13 slot PN+Schwartz con distribuzione locked, FormatSelector mappa 30 combinazioni tipo x livello, PromptService compila 7 prompt .txt scritti in italiano con sostituzione variabili {{...}}** ## Performance - **Duration:** 9 min - **Started:** 2026-03-08T00:51:34Z - **Completed:** 2026-03-08T01:00:34Z - **Tasks:** 2 - **Files modified:** 16 ## Accomplishments - CalendarService produce esattamente 13 slot con distribuzione PN corretta (4v+2s+2n+3r+1c+1p) e Schwartz corretta (L5=3,L4=3,L3=4,L2=2,L1=1), ordinati per fase funnel, con rotazione nicchie e calcolo date - CANVA_FIELDS locked come costante con assert a load-time — 33 colonne definitive per CSV Canva Bulk Create - FormatSelector mappa tutte le 30 combinazioni (6 tipi x 5 livelli) da format_mapping.json con fallback "PAS" - PromptService carica/compila/lista prompt .txt con sostituzione {{variabile}} e ValueError esplicito per variabili mancanti - 7 prompt .txt scritti interamente in italiano: system_prompt, topic_generator, pas_valore, listicle_valore, bab_storytelling, aida_promozione, dato_news — tutti con schema output JSON esplicito - Pydantic schemas completi: CalendarSlot, CalendarRequest, CalendarResponse, SlideContent, GeneratedPost, TopicResult, GenerateRequest, PostResult, GenerateResponse ## Task Commits Ogni task è stato committato atomicamente: 1. **Task 1: Costanti, schemas, FormatSelector** - `209d896` (feat) 2. **Task 2: CalendarService, PromptService, prompt .txt** - `ef9b947` (feat) **Plan metadata:** (questo commit) (docs: complete plan) ## Files Created/Modified - `backend/constants.py` — CANVA_FIELDS (33), PERSUASION_DISTRIBUTION, SCHWARTZ_DISTRIBUTION, FORMATI_NARRATIVI, FUNZIONI_CONTENUTO, FASI_CAMPAGNA, NICCHIE_DEFAULT, POST_PER_CICLO - `backend/schemas/calendar.py` — CalendarSlot, CalendarRequest, CalendarResponse con field constraints - `backend/schemas/generate.py` — SlideContent, GeneratedPost, TopicResult, GenerateRequest, PostResult, GenerateResponse - `backend/data/format_mapping.json` — matrice 6 tipi x 5 livelli -> formato narrativo - `backend/services/format_selector.py` — FormatSelector: carica JSON, select_format con fallback, get_mapping - `backend/services/calendar_service.py` — CalendarService: generate_calendar, ordinamento funnel, rotazione nicchie, calcolo date - `backend/services/prompt_service.py` — PromptService: list_prompts, load_prompt, compile_prompt, save_prompt, get_required_variables - `backend/data/prompts/system_prompt.txt` — esperto content marketing B2B italiano, regole tono, struttura 8 slide - `backend/data/prompts/topic_generator.txt` — genera topic per slot con guida livelli L1-L5 - `backend/data/prompts/pas_valore.txt` — formato PAS per post valore, schema JSON GeneratedPost - `backend/data/prompts/listicle_valore.txt` — formato Listicle con 6 punti numerati - `backend/data/prompts/bab_storytelling.txt` — formato BAB: Before→After→Bridge narrativo - `backend/data/prompts/aida_promozione.txt` — formato AIDA per conversione, variabile call_to_action - `backend/data/prompts/dato_news.txt` — Dato+Implicazione per news di settore - `.gitignore` — fix: backend/data/ ignorato troppo aggressivamente (Regola 1 - Bug) ## Decisions Made - **CANVA_FIELDS usa _image_keyword**: Le colonne immagine contengono keyword testuali, non URL. L'integrazione Unsplash arriva in Phase 4. Mantiene il CSV utile anche senza API Unsplash. - **Distribuzione L3 split tra fasi**: I 4 slot L3 sono divisi tra "Cattura" (valore) e "Coinvolgi" (riprova_sociale). La logica è che chi conosce la soluzione (L3) ma non ancora il prodotto va nutrito sia con educazione che con prova sociale. - **Nicchie 50/50**: Slot pari = generico, slot dispari = verticale in rotazione. Garantisce copertura del pubblico generico senza ignorare le nicchie specifiche. - **PromptService fail esplicito**: ValueError per variabili mancanti invece di lasciare {{variabile}} nel testo. Un prompt con variabili non sostituite manderebbe al LLM istruzioni rotte. ## Deviations from Plan ### Auto-fixed Issues **1. [Regola 1 - Bug] Fix .gitignore: backend/data/ era ignorato troppo aggressivamente** - **Trovato durante:** Task 1 (staging dei file) - **Problema:** Il pattern `data/` in .gitignore matchava anche `backend/data/`, impedendo di committare `format_mapping.json` e i file prompt .txt che sono file sorgente, non dati runtime - **Fix:** Cambiato `data/` a `/data/` (solo root) e aggiunto le sottocartelle runtime specifiche (`backend/data/outputs/`, `backend/data/campaigns/`, `backend/data/config/`) - **Files modificati:** `.gitignore` - **Verifica:** `git check-ignore -v backend/data/format_mapping.json` → non ignorato - **Committato in:** `209d896` (parte del Task 1 commit) --- **Totale deviazioni:** 1 auto-fixed (1 bug) **Impatto sul piano:** Necessaria per committare file sorgente. Nessuno scope creep. ## Issues Encountered Nessun problema durante l'implementazione — piano eseguito secondo le specifiche. ## User Setup Required Nessuno — nessun servizio esterno richiede configurazione per questi file di dominio puro. ## Next Phase Readiness - CalendarService e PromptService pronti per essere usati da LLMService (Plan 03) - CANVA_FIELDS locked — CSVBuilder (Plan 04) può iniziare a usarlo - 7 prompt in italiano pronti — LLMService userà PromptService.compile_prompt() per prepararli - Tutti i servizi sono puri (zero async, zero HTTP) — testabili indipendentemente - Nessun blocco: Plan 03 può proseguire --- *Phase: 01-core-generation-pipeline* *Completed: 2026-03-08*