docs(03): complete organization-layer phase
Phase 3 verified: 5/5 must-haves passed - SwipeService CRUD on data/swipe_file.json - 5 REST endpoints for Swipe File management - SwipeFile UI page with add/edit/delete/filter - topic_overrides in CalendarRequest + pipeline wiring - Swipe File picker in Genera Calendario form Requirements completed: SWP-01, SWP-02, SWP-03, SWP-04, UI-06
This commit is contained in:
@@ -141,16 +141,16 @@
|
||||
| IMG-02 | Phase 4 | Pending |
|
||||
| IMG-03 | Phase 4 | Pending |
|
||||
| IMG-04 | Phase 1 | Complete |
|
||||
| SWP-01 | Phase 3 | Pending |
|
||||
| SWP-02 | Phase 3 | Pending |
|
||||
| SWP-03 | Phase 3 | Pending |
|
||||
| SWP-04 | Phase 3 | Pending |
|
||||
| SWP-01 | Phase 3 | Complete |
|
||||
| SWP-02 | Phase 3 | Complete |
|
||||
| SWP-03 | Phase 3 | Complete |
|
||||
| SWP-04 | Phase 3 | Complete |
|
||||
| UI-01 | Phase 1 | Complete |
|
||||
| UI-02 | Phase 1 | Complete |
|
||||
| UI-03 | Phase 1 | Complete |
|
||||
| UI-04 | Phase 1 | Complete |
|
||||
| UI-05 | Phase 2 | Complete |
|
||||
| UI-06 | Phase 3 | Pending |
|
||||
| UI-06 | Phase 3 | Complete |
|
||||
| UI-07 | Phase 1 | Complete |
|
||||
| UI-08 | Phase 1 | Complete |
|
||||
| INF-01 | Phase 1 | Complete |
|
||||
@@ -167,4 +167,4 @@
|
||||
|
||||
---
|
||||
*Requirements defined: 2026-03-07*
|
||||
*Last updated: 2026-03-08 — Phase 2 requirements marked Complete*
|
||||
*Last updated: 2026-03-09 — Phase 3 requirements marked Complete*
|
||||
|
||||
@@ -14,7 +14,7 @@ Decimal phases appear between their surrounding integers in numeric order.
|
||||
|
||||
- [x] **Phase 1: Core Generation Pipeline** - Infrastruttura + pipeline calendario → LLM → CSV funzionante end-to-end
|
||||
- [x] **Phase 2: Prompt Control + Output Review** - Editor prompt via UI e anteprima caroselli prima dell'export
|
||||
- [ ] **Phase 3: Organization Layer** - Swipe File e gestione storico campagne per workflow sostenibile
|
||||
- [x] **Phase 3: Organization Layer** - Swipe File e gestione storico campagne per workflow sostenibile
|
||||
- [ ] **Phase 4: Enrichment** - Integrazione Unsplash, context injection da Swipe File, polish UI
|
||||
|
||||
## Phase Details
|
||||
|
||||
170
.planning/phases/03-organization-layer/03-VERIFICATION.md
Normal file
170
.planning/phases/03-organization-layer/03-VERIFICATION.md
Normal file
@@ -0,0 +1,170 @@
|
||||
---
|
||||
phase: 03-organization-layer
|
||||
verified: 2026-03-09T00:00:00Z
|
||||
status: passed
|
||||
score: 5/5 must-haves verified
|
||||
---
|
||||
|
||||
# Phase 3: Organization Layer - Verification Report
|
||||
|
||||
**Phase Goal:** Swipe File per salvare rapidamente idee e topic interessanti; integrazione topic override nel form genera calendario.
|
||||
**Verified:** 2026-03-09
|
||||
**Status:** PASSED
|
||||
**Re-verification:** No - initial verification
|
||||
|
||||
## Goal Achievement
|
||||
|
||||
### Observable Truths
|
||||
|
||||
| # | Truth | Status | Evidence |
|
||||
|---|-------|--------|----------|
|
||||
| 1 | Aggiunta idea Swipe File appare immediatamente | VERIFIED | SwipeFile.tsx: form 3 campi, addMutation.mutate(), onSuccess invalida queryKey swipe |
|
||||
| 2 | Idea persiste al riavvio container | VERIFIED | swipe_service.py _save() su swipe_file.json; docker-compose.yml volume postgenerator-data:/app/data |
|
||||
| 3 | Eliminazione con conferma aggiorna lista | VERIFIED | SwipeCard delete dialog, deleteMutation.mutate(itemId), onSuccess invalida cache |
|
||||
| 4 | Selezione topic da Swipe File come override slot specifico | VERIFIED | GenerateCalendar.tsx: griglia 13 slot, SwipePicker inline, topicOverrides state |
|
||||
| 5 | Topic override applicato in generazione prima dell LLM | VERIFIED | generation_pipeline.py righe 313-320: override controllato PRIMA della chiamata LLM |
|
||||
|
||||
**Score:** 5/5 truths verified
|
||||
|
||||
---
|
||||
|
||||
### Required Artifacts
|
||||
|
||||
| Artifact | Expected | Status | Details |
|
||||
|----------|----------|--------|---------|
|
||||
| backend/schemas/swipe.py | 4 Pydantic models | VERIFIED | 53 righe - SwipeItem/Create/Update/ListResponse con validazione |
|
||||
| backend/services/swipe_service.py | CRUD su swipe_file.json | VERIFIED | 182 righe - list/add/update/delete/mark_used con _load/_save completi |
|
||||
| backend/routers/swipe.py | 5 endpoint REST | VERIFIED | 142 righe - GET/POST/PUT/DELETE/mark-used, lazy init, ValueError->404 |
|
||||
| frontend/src/pages/SwipeFile.tsx | Form, lista, filtro, edit, delete | VERIFIED | 491 righe - tutte le feature presenti e collegate agli hook |
|
||||
| backend/schemas/calendar.py | CalendarRequest.topic_overrides | VERIFIED | Riga 91: Optional[dict[int, str]] con description esplicativa |
|
||||
| backend/services/generation_pipeline.py | Override prima LLM | VERIFIED | Righe 313-320: topic_overrides controllati PRIMA del blocco LLM |
|
||||
| frontend/src/pages/GenerateCalendar.tsx | Griglia 13 slot con picker | VERIFIED | 463 righe - Array.from(length:13), SwipePicker inline, topicOverrides nel CalendarRequest |
|
||||
| frontend/src/types.ts | 4 interfacce Swipe File + topic_overrides | VERIFIED | Righe 188-213 (SwipeItem/Create/Update/ListResponse), riga 33 (topic_overrides) |
|
||||
| frontend/src/api/hooks.ts | 5 hook Swipe File | VERIFIED | Righe 244-297 - hook con invalidazione cache; apiFetch per DELETE |
|
||||
|
||||
---
|
||||
|
||||
### Key Link Verification
|
||||
|
||||
| From | To | Via | Status | Details |
|
||||
|------|----|-----|--------|---------|
|
||||
| SwipeFile.tsx | GET /api/swipe/ | useSwipeItems() | VERIFIED | apiGet(/swipe/), dati in data.items |
|
||||
| SwipeFile.tsx | POST /api/swipe/ | useAddSwipeItem().mutate() | VERIFIED | addMutation.mutate con topic, nicchia, note |
|
||||
| SwipeFile.tsx | PUT /api/swipe/{id} | useUpdateSwipeItem().mutate() | VERIFIED | updateMutation.mutate con id e data: update |
|
||||
| SwipeFile.tsx | DELETE /api/swipe/{id} | useDeleteSwipeItem().mutate() | VERIFIED | apiFetch DELETE, deleteMutation.mutate(itemId) |
|
||||
| GenerateCalendar.tsx | POST /api/swipe/{id}/mark-used | useMarkSwipeUsed().mutate() | VERIFIED | markUsed.mutate(item.id) fire-and-forget |
|
||||
| GenerateCalendar.tsx | CalendarRequest.topic_overrides | topicOverrides->handleSubmit | VERIFIED | Object.fromEntries converte SlotOverride -> string nel payload |
|
||||
| CalendarRequest | generation_pipeline._run_generation | request.topic_overrides | VERIFIED | Override applicato per slot.indice corrispondente |
|
||||
| swipe_file.json | Docker volume | postgenerator-data:/app/data | VERIFIED | docker-compose.yml volume; SwipeService scrive in DATA_PATH/swipe_file.json |
|
||||
|
||||
---
|
||||
|
||||
### Requirements Coverage
|
||||
|
||||
| Requirement | Status | Note |
|
||||
|-------------|--------|------|
|
||||
| SWP-01: Aggiunta voce Swipe File (topic, nicchia, note) | SATISFIED | Form inline 3 campi, POST /api/swipe/ |
|
||||
| SWP-02: Eliminazione voce con conferma | SATISFIED | Dialog conferma in SwipeCard, DELETE /api/swipe/{id} |
|
||||
| SWP-03: Modifica inline voce esistente | SATISFIED | editingId state, input inline, PUT /api/swipe/{id} PATCH-like |
|
||||
| SWP-04: Filtro per nicchia | SATISFIED | uniqueNicchie derivate dagli items, chip filter con filterNicchia state |
|
||||
| UI-06: Topic override da Swipe File nel form Genera Calendario | SATISFIED | Griglia 13 slot, SwipePicker inline, mark-used automatico |
|
||||
|
||||
---
|
||||
|
||||
### Anti-Patterns Found
|
||||
|
||||
Nessun anti-pattern bloccante trovato.
|
||||
|
||||
| File | Pattern | Severita | Note |
|
||||
|------|---------|----------| -----|
|
||||
| generation_pipeline.py riga 290 | brand_name hardcoded | Avviso | Previsto - commento segnala lettura da settings in fase successiva |
|
||||
|
||||
---
|
||||
|
||||
### Human Verification Required
|
||||
|
||||
#### 1. Persistenza dati al riavvio
|
||||
|
||||
**Test:** Aggiungere 2-3 voci allo Swipe File, eseguire docker compose restart, ricaricare la pagina.
|
||||
**Expected:** Le voci sono ancora presenti.
|
||||
**Why human:** Volume Docker configurato correttamente nel codice ma verifica richiede container live.
|
||||
|
||||
#### 2. Picker Swipe File nel form Genera Calendario
|
||||
|
||||
**Test:** Con voci nello Swipe File, aprire Genera Calendario, cliccare Da Swipe File su uno slot, selezionare un idea.
|
||||
**Expected:** Topic appare nello slot con bordo amber; contatore override si aggiorna; idea compare come Usato.
|
||||
**Why human:** Comportamento picker inline (absolute positioning, z-20, chiusura automatica) richiede test visivo nel browser.
|
||||
|
||||
#### 3. Override applicato nella generazione reale
|
||||
|
||||
**Test:** Selezionare override per slot 0, avviare generazione, verificare nei log che "Topic override applicato" appaia.
|
||||
**Expected:** Log mostra "Topic override applicato | slot=0 | topic=..." e il post usa il topic scelto.
|
||||
**Why human:** Richiede API key configurata e generazione reale per verifica log end-to-end.
|
||||
|
||||
---
|
||||
|
||||
## Verifica Strutturale Dettagliata
|
||||
|
||||
### Backend
|
||||
|
||||
**SwipeService (backend/services/swipe_service.py):**
|
||||
- _load(): ritorna [] se file non esiste, gestisce JSONDecodeError e OSError
|
||||
- _save(): scrive con ensure_ascii=False, indent=2
|
||||
- list_items(): ordina per created_at descending (ISO string sort)
|
||||
- add_item(): genera id uuid4.hex[:12], timestamp ISO UTC, used=False
|
||||
- update_item(): aggiorna solo campi non-None, aggiorna updated_at; ValueError se ID non trovato
|
||||
- delete_item(): filtra per id, ValueError se non trovato
|
||||
- mark_used(): setta used=True, aggiorna updated_at, ValueError se non trovato
|
||||
|
||||
**Router (backend/routers/swipe.py):**
|
||||
- Lazy init identico al pattern PromptService (singleton module-level)
|
||||
- Tutti i ValueError mappati a HTTPException 404
|
||||
- 5 endpoint con prefix /api/swipe registrati in main.py riga 95
|
||||
|
||||
**CalendarRequest (backend/schemas/calendar.py):**
|
||||
- topic_overrides: Optional[dict[int, str]] alla riga 91
|
||||
- Pydantic converte automaticamente chiavi stringa JSON -> int
|
||||
|
||||
**GenerationPipeline._run_generation (backend/services/generation_pipeline.py):**
|
||||
- Priorita override: slot.topic -> request.topic_overrides[slot.indice] -> generazione LLM
|
||||
- Righe 313-320: check topic_overrides con log informativo quando applicato
|
||||
|
||||
### Frontend
|
||||
|
||||
**frontend/src/pages/SwipeFile.tsx:**
|
||||
- Form 3 campi, submit disabilitato se topic.trim() < 3 chars
|
||||
- SwipeCard: visualizzazione + edit inline + delete confirm in un componente
|
||||
- Filter chips da uniqueNicchie = Array.from(new Set(...))
|
||||
- Badge Usato con CheckCircle icon per item.used === true
|
||||
- relativeTime(): logica data relativa senza librerie esterne
|
||||
|
||||
**frontend/src/pages/GenerateCalendar.tsx:**
|
||||
- topicOverrides: Record<number, SlotOverride> con swipeId per tracking
|
||||
- handleSelectSwipeItem(): aggiorna state + markUsed.mutate() fire-and-forget
|
||||
- handleOpenPicker(): toggle (prev === slotIndex ? null : slotIndex)
|
||||
- handleSubmit(): converte SlotOverride -> string nel CalendarRequest payload
|
||||
|
||||
**Navigazione e routing:**
|
||||
- Sidebar.tsx: navItem to=/swipe-file, label Swipe File, icon Lightbulb
|
||||
- App.tsx: Route path=/swipe-file element={SwipeFile} registrata
|
||||
|
||||
**frontend/src/api/hooks.ts:**
|
||||
- useSwipeItems(): GET /swipe/, staleTime 30s
|
||||
- useAddSwipeItem(): POST /swipe/, invalida [swipe]
|
||||
- useUpdateSwipeItem(): PUT /swipe/{id}, invalida [swipe]
|
||||
- useDeleteSwipeItem(): apiFetch DELETE /swipe/{id}, invalida [swipe]
|
||||
- useMarkSwipeUsed(): POST /swipe/{id}/mark-used, invalida [swipe]
|
||||
|
||||
---
|
||||
|
||||
## Conclusione
|
||||
|
||||
Tutti i requisiti della Fase 3 sono implementati in modo completo e connesso.
|
||||
Non ci sono stub, placeholder o wiring mancanti.
|
||||
La persistenza e garantita dal Docker volume postgenerator-data montato su /app/data.
|
||||
Gli override topic passano correttamente: frontend topicOverrides state -> CalendarRequest.topic_overrides -> GenerationPipeline._run_generation, dove vengono applicati con priorita superiore rispetto alla generazione LLM.
|
||||
|
||||
---
|
||||
|
||||
_Verified: 2026-03-09_
|
||||
_Verifier: Claude (gsd-verifier)_
|
||||
Reference in New Issue
Block a user