Deliverable formazione-first (DESIGN_AVATAR_FORMAZIONE / product-demo-complete):
- docs/mappa-logica/01..22_*.md: tutte le viste app V2 mappate (formato canonico standard §2),
selettori VERIFICATI sul DOM reale, endpoint verificati su api.js/script, gate ruolo dai controller
- docs/mappa-logica/SELETTORI_DEMO.md: registry canonico (22 viste, 131 visualizzazione + 89 azioni)
+ worklist consolidata 132 id stabili da aggiungere (modifiche chirurgiche future)
- docs/formazione/TASSONOMIA_CORSO_NIS2.{md,json}: 10 moduli / 28 lezioni / ~7h45, prerequisiti+durate,
esame con soglia 80%, sandbox 'prova tu' org 996002
- fix kb.js: ingest scope=ORG ora invia organization_id (prima 422 KB_ORG_REQUIRED) — bug trovato da A2
Prodotto con 4 trittici di agenti (A1 esecutore -> A2 revisore -> A3 controllore avversariale):
A2: ~14 correzioni (cross-ref, gate ruolo, comportamenti); A3 PASS: 38 selettori campionati 0 rotti,
18 endpoint reali, 0 violazioni normative (Allegati 3/4, IS-4, '1 mese', PII.* interna), 9 claim verificati.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
45 lines
4.9 KiB
Markdown
45 lines
4.9 KiB
Markdown
# MAPPA-LOGICA · Knowledge Base
|
||
|
||
⟦ INIZIO · U-KB · Videata "Knowledge Base" ⟧
|
||
File: public/kb.html · URL: /kb.html · Scopo: caricamento e gestione dei documenti (procedure, policy, normative, training material) che l'AI usa per rispondere con citazioni precise (pipeline RAG su Qdrant), con visibilità a 3 livelli: SYSTEM (vendor), FIRM (studio + condivisioni esplicite), ORG (singola organizzazione cliente) · Profili: lettura/ricerca per tutti gli utenti autenticati (la visibilità applica il filtro authz a 3 livelli); l'upload (ingest) è riservato ai ruoli super_admin, org_admin, compliance_manager e consultant (employee/auditor read-only, 403 KB_FORBIDDEN). Per scope: SYSTEM solo super_admin; FIRM solo utenti con consulting_firm_id (consulenti di studio); ORG richiede ruolo org_admin/compliance_manager sull'organizzazione destinataria (super_admin bypassa) e `organization_id` valorizzato lato backend. La UI mostra/nasconde le radio di scope client-side (`setupScopeForUser`), ma l'enforcement è nel backend (KnowledgeBaseController::ingest).
|
||
|
||
• U-KB.1 — Header pagina + "Carica documento"
|
||
Mostra: titolo "Knowledge Base"; paragrafo introduttivo sul modello di visibilità a 3 livelli (SYSTEM/FIRM/ORG).
|
||
Azioni: bottone "Carica documento" → toggle del form di upload (U-KB.2); all'apertura chiama `setupScopeForUser()` che mostra/nasconde le opzioni di scope in base a ruolo e firm.
|
||
Dati da: GET /api/auth/me (per role e consulting_firm_id, determina gli scope disponibili).
|
||
Confine: `#btn-kb-upload` (riga 34).
|
||
|
||
• U-KB.2 — Form di upload/indicizzazione documento (nascosto di default)
|
||
Mostra: campo "Titolo"; select "Tipo" (Procedura interna / Linea guida / Normativa / Training); textarea "Contenuto (testo libero, 50–50.000 caratteri)"; radio "Visibilità": "Sistema (super_admin)" (visibile solo a super_admin), "Studio + condivisioni" (visibile solo a utenti firm), "Solo organizzazione corrente" (default, sempre visibile); status di indicizzazione ("Indicizzazione in corso..." / "Documento indicizzato (SCOPE, N chunk)" / messaggi di errore con validazione: titolo obbligatorio, testo minimo 50 caratteri).
|
||
Azioni: "Indicizza documento" → POST ingest (embed Voyage + upsert Qdrant + tracking MySQL), poi svuota i campi e ricarica la lista; "Annulla" → nasconde il form; selezione radio "FIRM" → mostra il blocco condivisione (U-KB.3).
|
||
Dati da: POST /api/knowledgebase/ingest (body: title, text, entity_type, scope, shared_with_orgs[]).
|
||
Confine: `#kb-form-card` (riga 50); `#kb-title` (55), `#kb-entity-type` (59), `#kb-text` (70), radio `input[name="kb-scope"]` con label `[data-scope-opt="SYSTEM|FIRM|ORG"]` (righe 77-84), `#btn-kb-submit` (102), `#btn-kb-cancel` (103), `#kb-status` (104).
|
||
|
||
• U-KB.3 — Blocco condivisione FIRM (visibile solo con scope=FIRM)
|
||
Mostra: multi-select "Organizzazioni clienti destinatarie (Ctrl/Cmd per selezione multipla, opzionale)" popolata con le organizzazioni dello studio (nome + P.IVA); nota: "Lascia vuoto per condividere solo con i collaboratori dello studio".
|
||
Azioni: selezione multipla delle org destinatarie → inviate come shared_with_orgs nell'ingest.
|
||
Dati da: GET /api/knowledgebase/firmOrgs (risposta: data.organizations[]).
|
||
Confine: `#kb-share-block` (riga 89), `#kb-shared-with` (riga 93).
|
||
|
||
• U-KB.4 — Lista "Documenti caricati visibili"
|
||
Mostra: per ogni documento visibile all'utente: titolo, badge scope colorato (SYSTEM blu / FIRM ambra / ORG verde), tipo entità, numero chunk, data di creazione; "Nessun documento visibile" se vuota. La visibilità applica il filtro authz a 3 livelli lato backend.
|
||
Azioni: bottone "Elimina" per documento → conferma → DELETE (rimuove documento + chunk da Qdrant) e ricarica la lista.
|
||
Dati da: GET /api/knowledgebase/list (risposta: data.documents[]); DELETE /api/knowledgebase/{id}.
|
||
Confine: card `.kb-doc-list` (riga 110); contenitore lista `#kb-doc-list` (riga 115); bottoni elimina dinamici con attributo `[data-del-id="<id>"]` (kb.js riga 110).
|
||
|
||
• U-KB.5 — "Test search semantica"
|
||
Mostra: campo query (placeholder "Es: come gestire un incidente NIS2?"); risultati: per ogni hit [N] titolo, badge scope, score di similarità, estratto del contenuto (primi 300 caratteri); "Nessun risultato" se vuoto.
|
||
Azioni: bottone "Cerca" → POST search semantica (top 5 risultati) nel perimetro di visibilità dell'utente.
|
||
Dati da: POST /api/knowledgebase/search (body: {query, top_k: 5}; risposta: data.results[]).
|
||
Confine: `#kb-search-query` (riga 127), `#btn-kb-search` (riga 128), `#kb-search-results` (riga 133).
|
||
|
||
⟦ FINE · U-KB ⟧
|
||
|
||
## ID STABILI MANCANTI (da aggiungere)
|
||
| Elemento | Riga | Id proposto |
|
||
|---|---|---|
|
||
| Card lista documenti (wrapper) | 110 | `nis2-kb-doc-list-card` |
|
||
| Card test search (wrapper) | 122 | `nis2-kb-search-card` |
|
||
| Paragrafo intro visibilità | 42 | `nis2-kb-intro` |
|
||
| Bottone "Elimina" documento (dinamico) | kb.js 110 | già presente `data-del-id` (selettore stabile, ok) |
|