Files
postgenerator/.planning/phases/01-core-generation-pipeline/01-02-PLAN.md
Michele 3f1dbbf396 docs(01): create phase plan
Phase 01: Core Generation Pipeline
- 4 plan(s) in 3 wave(s)
- Wave 1: 01-01 (infra) + 01-02 (core services) parallel
- Wave 2: 01-03 (LLM pipeline + API routers)
- Wave 3: 01-04 (Web UI) with human-verify checkpoint
- Ready for execution
2026-03-08 01:27:25 +01:00

16 KiB

phase, plan, type, wave, depends_on, files_modified, autonomous, must_haves
phase plan type wave depends_on files_modified autonomous must_haves
01-core-generation-pipeline 02 execute 1
backend/services/__init__.py
backend/services/calendar_service.py
backend/services/format_selector.py
backend/services/prompt_service.py
backend/schemas/__init__.py
backend/schemas/calendar.py
backend/schemas/generate.py
backend/data/format_mapping.json
backend/data/prompts/system_prompt.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
backend/constants.py
true
truths artifacts key_links
CalendarService genera esattamente 13 slot con distribuzione PN corretta (4 valore, 2 storytelling, 2 news, 3 riprova, 1 coinvolgimento, 1 promo)
Ogni slot ha livello Schwartz assegnato con distribuzione corretta (L5+L4=6, L3=4, L2=2, L1=1)
FormatSelector mappa ogni combinazione tipo_contenuto x livello_schwartz a un formato narrativo
PromptService carica, lista e compila prompt .txt con sostituzione variabili
CANVA_FIELDS e' definito come costante locked e contiene tutti i nomi colonna CSV
Almeno 5 prompt base esistono come file .txt scritti IN italiano
path provides contains
backend/services/calendar_service.py CalendarService con generate_calendar() che produce 13 slot PN + Schwartz + nicchie + date class CalendarService
path provides contains
backend/services/format_selector.py FormatSelector con select_format(tipo, livello) -> formato narrativo class FormatSelector
path provides contains
backend/services/prompt_service.py PromptService con load, list, compile con variabili class PromptService
path provides contains
backend/constants.py CANVA_FIELDS, PERSUASION_DISTRIBUTION, SCHWARTZ_DISTRIBUTION costanti locked CANVA_FIELDS
path provides contains
backend/schemas/calendar.py CalendarSlot, CalendarRequest, CalendarResponse Pydantic models class CalendarSlot
path provides contains
backend/schemas/generate.py SlideContent, GeneratedPost Pydantic models per output LLM e CSV class GeneratedPost
path provides
backend/data/format_mapping.json Tabella mapping tipo_contenuto x livello_schwartz -> formato narrativo
path provides
backend/data/prompts/system_prompt.txt System prompt scritto IN italiano per generazione caroselli
from to via pattern
backend/services/calendar_service.py backend/constants.py Importa PERSUASION_DISTRIBUTION e SCHWARTZ_DISTRIBUTION from.*constants.*import
from to via pattern
backend/services/format_selector.py backend/data/format_mapping.json Carica mapping da file JSON format_mapping
from to via pattern
backend/services/prompt_service.py backend/config.py Usa PROMPTS_PATH per localizzare file .txt PROMPTS_PATH
Creare i servizi core del dominio: CalendarService (distribuzione 13 post PN + Schwartz + nicchie + date), FormatSelector (mapping tipo x livello -> formato), PromptService (carica/compila prompt .txt), e definire le costanti fondamentali (CANVA_FIELDS, distribuzioni).

Purpose: Costruire la logica di dominio pura (zero dipendenza LLM) che orchestra il calendario editoriale e prepara i prompt per la generazione. Queste sono le fondamenta su cui LLMService e CSVBuilder si appoggeranno.

Output: Servizi Python testabili indipendentemente, 5 prompt .txt in italiano, schema Pydantic per slot calendario e post generato, costante CANVA_FIELDS locked.

<execution_context> @C:\Users\miche.claude/get-shit-done/workflows/execute-plan.md @C:\Users\miche.claude/get-shit-done/templates/summary.md </execution_context>

@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/research/ARCHITECTURE.md @.planning/research/PITFALLS.md @.planning/phases/01-core-generation-pipeline/01-CONTEXT.md Task 1: Costanti di dominio, Pydantic schemas, FormatSelector 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/data/format_mapping.json 1. Creare backend/constants.py con le costanti LOCKED del progetto:
   PERSUASION_DISTRIBUTION — distribuzione per ciclo di 13 post:
   - "valore": 4
   - "storytelling": 2
   - "news": 2
   - "riprova_sociale": 3
   - "coinvolgimento": 1
   - "promozione": 1

   SCHWARTZ_DISTRIBUTION — distribuzione livelli per 13 post:
   - L5: 3 post (storytelling + news)
   - L4: 3 post (valore + storytelling)
   - L3: 4 post (valore + riprova)
   - L2: 2 post (riprova + coinvolgimento)
   - L1: 1 post (promozione)
   Nota: L5+L4=6, L3=4, L2=2, L1=1 come da requirement CAL-02.

   CANVA_FIELDS — lista ORDINATA dei nomi colonna CSV per Canva Bulk Create:
   Metadati: campagna, fase_campagna, tipo_contenuto, formato_narrativo, funzione, livello_schwartz, target_nicchia, data_pub_suggerita
   Slide (8 slide x 3 campi = 24):
     cover_title, cover_subtitle, cover_image_keyword
     s2_headline, s2_body, s2_image_keyword
     s3_headline, s3_body, s3_image_keyword
     s4_headline, s4_body, s4_image_keyword
     s5_headline, s5_body, s5_image_keyword
     s6_headline, s6_body, s6_image_keyword
     s7_headline, s7_body, s7_image_keyword
     cta_text, cta_subtext, cta_image_keyword
   Extra: caption_instagram
   Totale: 8 metadati + 24 slide + 1 caption = 33 colonne

   FORMATI_NARRATIVI — lista dei 7 formati: PAS, AIDA, BAB, Listicle, Storytelling, Dato_Implicazione, Obiezione_Risposta

   FUNZIONI_CONTENUTO — le 4 funzioni: Intrattenere, Educare, Persuadere, Convertire

   FASI_CAMPAGNA — le 4 fasi: Attira, Cattura, Coinvolgi, Converti

   NICCHIE_DEFAULT — lista default: ["generico", "dentisti", "avvocati", "ecommerce", "local_business", "agenzie"]

   POST_PER_CICLO = 13

   Nota: usare _image_keyword (non _image_url) per le colonne immagine — l'URL verra' solo con Unsplash in Phase 4. Per ora il CSV contiene keyword testuali.

2. Creare backend/schemas/__init__.py (vuoto).

3. Creare backend/schemas/calendar.py con Pydantic models:
   - CalendarSlot: indice (int), tipo_contenuto (str), livello_schwartz (str), formato_narrativo (str), funzione (str), fase_campagna (str), target_nicchia (str), data_pub_suggerita (str, formato YYYY-MM-DD), topic (Optional[str], default None — verra' generato dall'LLM o overridden dall'utente)
   - CalendarRequest: obiettivo_campagna (str), settimane (int, default 2), nicchie (Optional[list[str]]), frequenza_post (int, default 3 — post a settimana), data_inizio (Optional[str])
   - CalendarResponse: campagna (str), slots (list[CalendarSlot]), totale_post (int)

4. Creare backend/schemas/generate.py con Pydantic models:
   - SlideContent: headline (str), body (str), image_keyword (str)
   - GeneratedPost: cover_title (str), cover_subtitle (str), cover_image_keyword (str), slides (list[SlideContent] — 6 slide centrali s2-s7), cta_text (str), cta_subtext (str), cta_image_keyword (str), caption_instagram (str)
   - GenerateRequest: slot (CalendarSlot), obiettivo_campagna (str), brand_name (Optional[str]), tono (Optional[str])
   - PostResult: slot_index (int), status (Literal["success", "failed", "pending"]), post (Optional[GeneratedPost]), error (Optional[str])
   - GenerateResponse: campagna (str), results (list[PostResult]), total (int), success_count (int), failed_count (int)

5. Creare backend/data/format_mapping.json:
   Matrice tipo_contenuto x livello_schwartz -> formato_narrativo.
   Struttura: { "valore": { "L5": "Listicle", "L4": "PAS", "L3": "PAS", "L2": "Obiezione_Risposta", "L1": "AIDA" }, ... }
   Coprire tutte le 6 combinazioni tipo x 5 livelli con i 7 formati disponibili, scegliendo il formato piu' efficace per ogni combinazione.

6. Creare backend/services/__init__.py (vuoto).

7. Creare backend/services/format_selector.py:
   - class FormatSelector con __init__ che carica format_mapping.json
   - Metodo select_format(tipo_contenuto: str, livello_schwartz: str) -> str che ritorna il formato narrativo
   - Fallback a "PAS" se combinazione non trovata
   - Metodo get_mapping() -> dict per esporre la tabella completa
- backend/constants.py: CANVA_FIELDS ha esattamente 33 elementi, PERSUASION_DISTRIBUTION somma a 13, SCHWARTZ_DISTRIBUTION somma a 13 - backend/schemas/calendar.py: CalendarSlot importabile, CalendarRequest ha campo obiettivo_campagna - backend/schemas/generate.py: GeneratedPost ha slides come list[SlideContent], PostResult ha campo status - backend/data/format_mapping.json: contiene tutte le 6 chiavi tipo_contenuto, ciascuna con 5 livelli - backend/services/format_selector.py: FormatSelector ha metodo select_format Costanti di dominio locked (CANVA_FIELDS, PERSUASION_DISTRIBUTION, SCHWARTZ_DISTRIBUTION). Pydantic schemas per calendario e generazione definiti. FormatSelector carica mapping da JSON e mappa tipo x livello -> formato. Tutto il dominio ha tipi espliciti, nessuna stringa magica. Task 2: CalendarService, PromptService, e prompt .txt in italiano backend/services/calendar_service.py backend/services/prompt_service.py backend/data/prompts/system_prompt.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 1. Creare backend/services/calendar_service.py: - class CalendarService - Metodo generate_calendar(request: CalendarRequest) -> CalendarResponse: a. Genera 13 slot con distribuzione PERSUASION_DISTRIBUTION (import da constants) b. Assegna livelli Schwartz secondo la logica naturale: - valore: L4 (2), L3 (2) — educare chi e' consapevole del problema/soluzioni - storytelling: L5 (2) — attirare inconsapevoli con storie - news: L5 (1), L4 (1) — intrattenere/educare - riprova_sociale: L3 (2), L2 (1) — persuadere chi conosce soluzioni/prodotto - coinvolgimento: L2 (1) — interagire con chi conosce il prodotto - promozione: L1 (1) — convertire chi e' pronto Verifica che totali siano: L5=3, L4=3, L3=4, L2=2, L1=1 c. Assegna funzioni contenuto: Intrattenere (storytelling, news, coinvolgimento), Educare (valore), Persuadere (riprova_sociale), Convertire (promozione) d. Distribuisce nelle 4 fasi campagna: Attira (L5), Cattura (L4+L3), Coinvolgi (L3+L2), Converti (L1+L2) e. Ordina gli slot nella sequenza campagna (Attira -> Cattura -> Coinvolgi -> Converti) f. Calcola date_pub_suggerita dalla data_inizio con la frequenza specificata (default: 3 post/settimana, lun-mer-ven) g. Assegna nicchie con rotazione: 50% generico, 50% verticali in rotazione dalla lista nicchie - Usa FormatSelector internamente per assegnare formato_narrativo a ogni slot - Metodo statico _distribute_niches(slots, nicchie) per la logica rotazione
2. Creare backend/services/prompt_service.py:
   - class PromptService(__init__ riceve prompts_dir: Path)
   - list_prompts() -> list[str]: elenca tutti i .txt nella directory
   - load_prompt(name: str) -> str: carica contenuto file .txt
   - compile_prompt(name: str, variables: dict[str, str]) -> str: carica e sostituisce {{variabile}} con valori dal dict
     - Usa doppia graffa {{variabile}} come delimitatore (Pitfall 7)
     - Solleva ValueError se una variabile nel template non ha corrispondenza nel dict
   - save_prompt(name: str, content: str) -> None: salva contenuto (per Phase 2 editor)
   - get_required_variables(name: str) -> list[str]: parse il template e ritorna lista variabili {{...}} trovate

3. Creare i 5 prompt base + system prompt, TUTTI scritti IN italiano (Pitfall 8):

   backend/data/prompts/system_prompt.txt:
   - Ruolo: esperto di content marketing B2B per PMI italiane
   - Tono: diretto, provocatorio ma costruttivo, usa il "tu"
   - Target: imprenditori e manager italiani
   - Regole: "cosa fare" mai "come farlo", benefici concreti, evitare jargon
   - Lingua: italiano naturale, NON tradotto dall'inglese
   - Output: JSON strutturato con i campi specificati nello schema

   backend/data/prompts/pas_valore.txt (formato PAS per post valore):
   - Sezioni: SYSTEM (ref system_prompt), USER, OUTPUT_SCHEMA
   - Variabili: {{obiettivo_campagna}}, {{target_nicchia}}, {{livello_schwartz}}, {{topic}}, {{brand_name}}
   - Istruzioni PAS: Problema -> Agitazione -> Soluzione
   - Output schema JSON esplicito con campi GeneratedPost

   backend/data/prompts/listicle_valore.txt (formato Listicle per post valore):
   - Variabili: stesse di PAS
   - Istruzioni Listicle: lista numerata di consigli pratici
   - Enfasi su valore educativo

   backend/data/prompts/bab_storytelling.txt (formato BAB per storytelling):
   - Variabili: stesse + enfasi emotiva
   - Istruzioni BAB: Before -> After -> Bridge
   - Tono narrativo, storia di trasformazione

   backend/data/prompts/aida_promozione.txt (formato AIDA per promo):
   - Variabili: stesse + {{call_to_action}}
   - Istruzioni AIDA: Attenzione -> Interesse -> Desiderio -> Azione
   - Focus su conversione

   backend/data/prompts/dato_news.txt (formato Dato+Implicazione per news):
   - Variabili: stesse
   - Istruzioni: dato/statistica -> cosa significa -> come agire
   - Focus su urgenza informata

   TUTTI i prompt devono:
   - Essere scritti interamente in italiano (istruzioni E esempi)
   - Usare {{variabile}} per tutti i parametri dinamici
   - Specificare l'output JSON schema esplicito con i campi di GeneratedPost
   - Includere istruzioni sul tono (tu, diretto, concreto)
   - Specificare il numero di slide e la struttura (cover, 6 slide centrali, CTA)
- CalendarService.generate_calendar() con CalendarRequest(obiettivo_campagna="test", settimane=2) produce CalendarResponse con esattamente 13 slot - Distribuzione PN: contare tipi -> 4 valore, 2 storytelling, 2 news, 3 riprova, 1 coinvolgimento, 1 promo - Distribuzione Schwartz: contare livelli -> L5=3, L4=3, L3=4, L2=2, L1=1 - PromptService.list_prompts() ritorna almeno 6 file (system + 5 base) - PromptService.compile_prompt("pas_valore", {"obiettivo_campagna": "test", ...}) sostituisce tutte le variabili senza errori - Tutti i prompt .txt contengono SOLO testo italiano, nessuna istruzione in inglese - Nessun prompt contiene numeri hardcoded per slide count — usano {{num_slides}} o la struttura e' definita nell'output schema CalendarService genera 13 slot con distribuzione PN e Schwartz corretta, assegna fasi campagna, calcola date, ruota nicchie. PromptService carica e compila prompt con variabili {{...}}. 5 prompt base + system prompt scritti IN italiano, con output JSON schema esplicito. Nessun valore hardcoded nei template. 1. CANVA_FIELDS ha 33 elementi e corrisponde allo schema in PROJECT.md (+ caption_instagram) 2. CalendarService produce 13 slot con distribuzione verificabile contando tipi e livelli 3. Ogni prompt .txt usa solo {{variabile}} e non contiene numeri/strutture hardcoded 4. FormatSelector mappa tutte le 30 combinazioni (6 tipi x 5 livelli) 5. Pydantic schemas validano correttamente un esempio di GeneratedPost

<success_criteria>

  • CalendarService genera calendario con distribuzione PN e Schwartz corretta
  • FormatSelector mappa ogni combinazione tipo x livello a un formato
  • PromptService carica, lista, compila prompt con sostituzione variabili
  • 5 prompt base + system prompt esistono come file .txt in italiano
  • CANVA_FIELDS locked come costante con tutti i nomi colonna
  • Pydantic schemas per calendario, generazione, e risultati definiti </success_criteria>
After completion, create `.planning/phases/01-core-generation-pipeline/01-02-SUMMARY.md`