Phase 1 executed (4 plans, 3 waves), verified, gap fixed. 36 requirements marked Complete in traceability table. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
15 KiB
phase, verified, status, score, gaps, human_verification
| phase | verified | status | score | gaps | human_verification | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 01-core-generation-pipeline | 2026-03-08T01:41:51Z | gaps_found | 3/5 must-haves verified |
|
|
Phase 1: Core Generation Pipeline - Verification Report
Phase Goal: L'utente puo generare un calendario di 13 caroselli completi e scaricare un CSV valido per Canva Bulk Create con un click dalla Web UI deployata su VPS. Verified: 2026-03-08T01:41:51Z Status: gaps_found Re-verification: No - initial verification
Goal Achievement
Observable Truths
| # | Truth | Status | Evidence |
|---|---|---|---|
| 1 | La Web UI si carica su https://lab.mlhub.it/postgenerator/ | ? HUMAN_NEEDED | App non deployata. Dockerfile, docker-compose, SPAStaticFiles, subpath /postgenerator/ tutti cablati. |
| 2 | L'utente vede progress indicator e badge PN/Schwartz in OutputReview | FAILED | ProgressIndicator funziona (polling 2s). Gap critico: result.slot undefined in PostCard - badge e Riprova non funzionanti. |
| 3 | CSV scaricabile con caratteri italiani e header Canva corretti | VERIFIED code / HUMAN_NEEDED Excel | CSVBuilder usa utf-8-sig BOM. CANVA_FIELDS 33 colonne locked. Verifica Excel = human needed. |
| 4 | CSV con distribuzione PN corretta e livelli Schwartz assegnati | VERIFIED distribution | Assert a load-time in constants.py. CalendarService produce 13 slot ordinati per funnel. |
| 5 | Post falliti marcati come errore senza bloccare il batch | VERIFIED | try/except individuale per slot. PostResult status=failed con error. PostCard mostra card rossa. |
Score: 3/5 truths verified
Required Artifacts
| Artifact | Status | Details |
|---|---|---|
backend/main.py |
VERIFIED | 104 righe. SPAStaticFiles, lifespan, 4 router inclusi prima di SPA |
backend/config.py |
VERIFIED | 54 righe. DATA_PATH, PROMPTS_PATH, OUTPUTS_PATH, CAMPAIGNS_PATH, CONFIG_PATH |
backend/constants.py |
VERIFIED | 195 righe. CANVA_FIELDS(33), PERSUASION_DISTRIBUTION, SCHWARTZ_DISTRIBUTION con assert |
backend/services/calendar_service.py |
VERIFIED | 297 righe. _build_raw_slots, _sort_by_funnel, _distribute_niches, _generate_dates |
backend/services/llm_service.py |
VERIFIED | 285 righe. Retry loop, RateLimitError con retry-after, backoff esponenziale |
backend/services/csv_builder.py |
VERIFIED | 182 righe. utf-8-sig, BOM manuale, mapping 33 colonne CANVA_FIELDS |
backend/services/generation_pipeline.py |
VERIFIED | 572 righe. asyncio.create_task, try/except individuale per slot, persistenza JSON |
backend/services/prompt_service.py |
VERIFIED | 170 righe. compile_prompt, ValueError per variabili mancanti, prompt_exists |
backend/routers/generate.py |
VERIFIED | 247 righe. POST /bulk (202), GET /job/{id}/status, GET /job/{id}, POST /single |
backend/routers/export.py |
VERIFIED | 151 righe. GET FileResponse + POST con edits inline |
backend/routers/settings.py |
VERIFIED | 164 righe. api_key mascherata, merge preserva chiave esistente |
backend/routers/calendar.py |
VERIFIED | 60 righe. POST /api/calendar/generate thin router |
backend/data/prompts/system_prompt.txt |
VERIFIED | 34 righe. Italiano nativo, tono + regole + struttura 8 slide |
backend/data/prompts/pas_valore.txt |
VERIFIED | 76 righe. Variabili template, schema JSON output |
backend/data/prompts/topic_generator.txt |
VERIFIED | File presente con variabili per slot calendario |
backend/data/prompts/bab_storytelling.txt |
VERIFIED | File presente |
backend/data/prompts/listicle_valore.txt |
VERIFIED | File presente |
backend/data/prompts/aida_promozione.txt |
VERIFIED | File presente (con variabile call_to_action) |
backend/data/prompts/dato_news.txt |
VERIFIED | File presente |
frontend/src/api/client.ts |
VERIFIED | 110 righe. API_BASE=/postgenerator/api, tutti gli helper presenti |
frontend/src/api/hooks.ts |
VERIFIED | 187 righe. 11 hooks TanStack Query completi |
frontend/src/types.ts |
VERIFIED | 161 righe. Tutti gli interface TypeScript |
frontend/src/App.tsx |
VERIFIED | 38 righe. 5 route + BrowserRouter basename=/postgenerator |
frontend/src/pages/GenerateCalendar.tsx |
VERIFIED | 247 righe. Form + polling flow + navigate a OutputReview |
frontend/src/components/ProgressIndicator.tsx |
VERIFIED | 154 righe. useJobStatus, barra progresso, PostStatusRow |
frontend/src/pages/OutputReview.tsx |
PARTIAL | 168 righe. localResults, CSV download - manca merge slot |
frontend/src/components/PostCard.tsx |
PARTIAL | 180 righe. result.slot sempre undefined - badge e Riprova non funzionanti |
frontend/src/components/SlideViewer.tsx |
VERIFIED | 371 righe. Cover + 6 slide + CTA, EditableField, keyboard nav |
Dockerfile |
VERIFIED | 42 righe. node:22-slim + python:3.12-slim, --root-path /postgenerator in CMD |
docker-compose.yml |
VERIFIED | proxy_net external, postgenerator-data volume, NO porte esposte |
Key Link Verification
| From | To | Via | Status | Details |
|---|---|---|---|---|
| GenerateCalendar.tsx | POST /api/generate/bulk | useGenerateCalendar mutation | WIRED | apiPost /generate/bulk -> job_id |
| ProgressIndicator.tsx | GET /api/generate/job/{id}/status | useJobStatus hook | WIRED | refetchInterval 2s se status=running |
| OutputReview.tsx | GET /api/generate/job/{id} | useJobResults hook | WIRED | apiGet con jobId dal URL param |
| OutputReview.tsx | POST /api/export/{id}/csv | useDownloadEditedCsv | WIRED | apiDownload POST con localResults + campagna |
| PostCard.tsx | result.slot CalendarSlot | direct field access | NOT_WIRED | result.slot sempre undefined - merge non avviene |
| GenerationPipeline | LLMService per ogni slot | asyncio.to_thread | WIRED | Per-item isolation, try/except individuale |
| LLMService | anthropic.Anthropic client | _load_api_key() | WIRED | Carica da settings.json o env ANTHROPIC_API_KEY |
| CSVBuilder | CANVA_FIELDS 33 colonne | from constants.py | WIRED | DictWriter fieldnames=CANVA_FIELDS |
| main.py | SPAStaticFiles | montato dopo tutti i router | WIRED | Ordine corretto nel codice |
| Dockerfile CMD | Uvicorn --root-path /postgenerator | solo in CMD | WIRED | Mai nel costruttore FastAPI() |
Requirements Coverage
| Requirement | Status | Note |
|---|---|---|
| INF-01 Backend FastAPI | VERIFIED | main.py + 4 router |
| INF-02 Frontend React + Tailwind | VERIFIED | Vite + Tailwind v4 + BrowserRouter |
| INF-03 Single container Docker multi-stage | VERIFIED | Dockerfile 2 stage |
| INF-04 Deploy VPS /postgenerator/ | HUMAN_NEEDED | Non ancora deployato |
| INF-05 Config via .env | VERIFIED | .env.example, config.py da env |
| INF-06 File-based storage | VERIFIED | prompts/, outputs/, data/ |
| CAL-01 13 post distribuzione PN | VERIFIED | Constants + assert + CalendarService |
| CAL-02 Livelli Schwartz corretti | VERIFIED | SCHWARTZ_DISTRIBUTION + assert |
| CAL-03 Rotazione nicchie 50/50 | VERIFIED | _distribute_niches |
| CAL-04 Fasi campagna ordinate | VERIFIED | _sort_by_funnel |
| CAL-05 Date di pubblicazione | VERIFIED | _generate_dates con lun/mer/ven |
| CAL-06 Topic generation LLM | VERIFIED | LLMService.generate_topic + topic_generator.txt |
| CAL-07 Override topic manuale | VERIFIED | CalendarSlot.topic Optional |
| FMT-01 Mapping tipo x livello -> formato | VERIFIED | FormatSelector + format_mapping.json |
| FMT-02 Mapping configurabile JSON | VERIFIED | backend/data/format_mapping.json |
| LLM-01 8 slide in JSON strutturato | VERIFIED | GeneratedPost schema Pydantic |
| LLM-02 Validazione JSON | VERIFIED | model_validate_json in LLMService |
| LLM-03 Retry con istruzione correttiva | VERIFIED | validation_retry_done loop |
| LLM-04 Rate limiting e backoff | VERIFIED | RateLimitError + retry-after + backoff esponenziale |
| LLM-05 Per-item error isolation | VERIFIED | try/except individuale per slot |
| LLM-06 Provider LLM configurabile | VERIFIED | LLM_MODEL env var, Settings.llm_model |
| PRM-01 Prompt in file .txt | VERIFIED | 7 prompt .txt in backend/data/prompts/ |
| PRM-02 Prompt Manager | VERIFIED | PromptService.compile_prompt |
| PRM-03 5 prompt base per MVP | VERIFIED | 5 prompt narrativi presenti |
| PRM-04 System prompt in italiano | VERIFIED | system_prompt.txt in italiano nativo |
| CSV-01 33 colonne Canva Bulk Create | VERIFIED | CANVA_FIELDS 33 elementi locked |
| CSV-02 Encoding utf-8-sig BOM | VERIFIED | encoding=utf-8-sig + BOM manuale |
| CSV-03 Campi metadato inclusi | VERIFIED | 8 metadati: campagna, fase, tipo, formato, funzione, livello, nicchia, data |
| CSV-04 Download CSV dalla Web UI | VERIFIED | useDownloadEditedCsv + triggerDownload |
| IMG-01 Keyword immagine per slide | VERIFIED | cover_image_keyword, slide.image_keyword, cta_image_keyword |
| IMG-04 Fallback keyword testuale | VERIFIED | CANVA_FIELDS usa _image_keyword non URL |
| UI-01 Dashboard con stato | VERIFIED | Dashboard.tsx con banner API key |
| UI-02 Form Genera Calendario | VERIFIED | GenerateCalendar.tsx completo |
| UI-03 Form Genera Singolo Post | VERIFIED | GenerateSingle.tsx |
| UI-04 Output Review con anteprima slide | PARTIAL | SlideViewer funzionale, badge mancanti per slot-merge gap |
| UI-07 Pagina Impostazioni | VERIFIED | Settings.tsx completo |
| UI-08 Progress indicator | VERIFIED | ProgressIndicator.tsx con polling 2s |
Anti-Patterns Found
| File | Line | Pattern | Severity | Impact |
|---|---|---|---|---|
frontend/src/components/PostCard.tsx |
42 | result.slot sempre undefined | BLOCKER | Badge PN, Schwartz, metadata non visibili; Riprova non funziona |
frontend/src/pages/OutputReview.tsx |
135-146 | Raw results a PostCard senza merge slot | BLOCKER | Radice del problema: CalendarSlot mai aggiunto ai PostResult |
Human Verification Required
1. Accesso Web UI su VPS
Test: Aprire https://lab.mlhub.it/postgenerator/ dopo vps-lab-deploy Expected: Pagina si carica, sidebar con navigazione, Dashboard mostra banner API key Why human: App non ancora deployata sul VPS
2. Flusso generazione completo
Test: Configurare API key, inserire obiettivo campagna (min 10 caratteri), cliccare Genera Calendario Expected: ProgressIndicator con polling 2s, lista post con icone individuali, navigazione automatica a OutputReview al completamento Why human: Richiede API key Anthropic reale e deployment VPS
3. Badge nella OutputReview (gap noto)
Test: Verificare PostCard con badge PN colorati e badge livello Schwartz Expected: Ogni card mostra tipo (es. Valore, Storytelling) e livello (es. L3) con colori distinti Why human: Il gap slot-merge causera badge mancanti - serve verifica visuale della gravita
4. CSV in Excel con caratteri italiani
Test: Scaricare il CSV e aprire in Microsoft Excel su Windows Expected: Caratteri italiani intatti, 33 colonne, header corretti Why human: Compatibilita Excel richiede test runtime reale
Gaps Summary
Il phase goal e quasi completamente implementato. L unico gap bloccante nel codice e la mancata propagazione dei dati CalendarSlot ai PostResult nel frontend.
Gap Critico: slot-merge mancante in OutputReview
Il backend PostResult contiene solo slot_index (intero), non l oggetto CalendarSlot completo. La GenerateResponse include i results ma non il calendar. Il file types.ts dichiara PostResult.slot?: CalendarSlot con il commento "viene aggiunto dal frontend dopo merge con CalendarSlot", ma questo merge non avviene mai in OutputReview.tsx.
In PostCard.tsx riga 42: const slot = result.slot e sempre undefined.
Conseguenze:
- BadgePN tipo={slot.tipo_contenuto} non renderizzato (slot undefined)
- BadgeSchwartz livello={slot.livello_schwartz} non renderizzato
- Metadati secondari (formato, nicchia, data) non visibili nella card
- handleRetry() ritorna immediatamente (if (!slot) return)
Fix raccomandato: Aggiungere calendar: Optional[CalendarResponse] alla GenerateResponse del backend (il calendario e gia salvato nel JSON del job), poi nel frontend aggiornare il useEffect di OutputReview.tsx:
// In OutputReview.tsx useEffect:
const slotMap = Object.fromEntries(
(jobData.calendar?.slots ?? []).map(s => [s.indice, s])
)
setLocalResults(jobData.results.map(r => ({ ...r, slot: slotMap[r.slot_index] })))
Tutto il resto e implementato correttamente: pipeline LLM con retry e rate limiting, CalendarService con distribuzione locked, CSVBuilder con utf-8-sig BOM, 7 prompt italiani con schema JSON, form UI completo, polling ProgressIndicator, SlideViewer con edit inline, Docker multi-stage build, subpath /postgenerator/ configurato ovunque.
Verified: 2026-03-08T01:41:51Z Verifier: Claude Sonnet 4.6 (gsd-verifier)