# Sistema a Trittici di Agenti (Esecutore → Revisore → Controllore) — NIS2 Agile > **Obiettivo**: eseguire lavori a rischio (es. rollout UI Bootstrap Italia, correzioni di conformità) con **qualità garantita da una catena a 3 agenti** per ogni unità di lavoro: **uno fa, uno controlla, uno ricontrolla** in modo avversariale. Almeno **5 trittici** (qui 6). > **Principi del progetto incorporati**: *fonti certe* (niente invenzioni, tutto citato/verificato), **verifica sul path reale** (login/render vero, mai CLI/"a memoria"), **zero backend** per la UI, **produzione intatta** (worktree / pagine `-bi`), **incrementale** (un'ondata per volta). > Tipo: design realizzativo · v1 · 2026-06-11. --- ## 1. Il protocollo "trittico" (3 agenti per unità di lavoro) ``` ┌─────────────┐ artefatti ┌─────────────┐ difetti? ┌──────────────┐ IN ──▶│ A1 ESECUTORE│ ─────────────────▶ │ A2 REVISORE │ ──── sì ───────▶│ (rework A1) │ │ "fa" │ │ "controlla" │ ──── no ───┐ └──────────────┘ └─────────────┘ └─────────────┘ ▼ ┌──────────────────────┐ PASS/FAIL │ A3 CONTROLLORE │ ───────────▶ GATE │ "ricontrolla" (avvers)│ └──────────────────────┘ ``` | Ruolo | Cosa fa | Mentalità | Output | |---|---|---|---| | **A1 — Esecutore** | Implementa l'unità (es. converte N pagine a BI), self-host asset, scrive gli artefatti | costruttiva, segue gli acceptance criteria | file modificati + self-report (cosa, dove, come verificato) | | **A2 — Revisore** | Verifica l'output di A1 contro i **criteri di accettazione** (conformità BI, WCAG, parità funzionale, zero backend) | critica ma collaborativa | verdetto + **lista difetti** (file:riga, severità) | | **A3 — Controllore** | Re-review **indipendente e avversariale**: cerca ciò che A2 ha mancato, **verifica sul path reale** (la pagina si serve/renderizza? login funziona? niente regressioni?), conferma che i difetti siano reali e chiusi | "rompila": prova a smontare il lavoro | **GATE PASS/FAIL** + motivazione | **Regole della catena** - A2 e A3 sono **read-only** (non modificano): producono rilievi. Il rework lo fa A1 (loop A1↔A2 fino a "0 difetti bloccanti"), poi A3 dà il sigillo. - **A3 ≠ A2**: lente diversa e adversariale, deve esercitare il *path reale* (curl della pagina servita via bind-mount, login vero), non test sintetici (lezione delle 3 nottate DB). - **Gate FAIL** → l'unità torna ad A1 con i rilievi di A3; non si procede all'ondata successiva. - **Fonti certe**: A1/A2/A3 citano sempre la fonte (BI docs, WCAG, file:riga); marcano `[da verificare]` ciò che non confermano. --- ## 2. Criteri di accettazione comuni (Definition of Done per ogni trittico UI) 1. **Conformità BI**: usa Bootstrap Italia v2.18.1 **self-hostato** (`public/vendor/bootstrap-italia/`), componenti/markup ufficiali, Blu Italia #0066CC, Titillium. 2. **Accessibilità WCAG 2.1 AA**: contrasti ≥ soglie, label associate, focus visibile, `aria-*`/alt, navigazione tastiera. 3. **Parità funzionale**: stessa logica (`api.js`/`common.js`), stessi `id`/flussi → **zero modifiche backend**; la pagina fa esattamente quel che faceva prima. 4. **Nessuna regressione**: la pagina si serve **200**, JS senza errori console, login/azioni reali OK (verifica sul path fpm reale). 5. **Produzione intatta** fino al gate: lavoro su pagine `-bi` / worktree; swap solo dopo PASS. --- ## 3. I 6 trittici (unità di lavoro — rollout Opzione B) | # | Trittico | A1 Esecutore produce | A2/A3 verificano | |---|---|---|---| | **T1** | **Base & self-host** | `public/vendor/bootstrap-italia/` (CSS/JS/font self-hostati v2.18.1) + token AGID confermati (da AgileHub V2 o BI ufficiale) + pagina di smoke | asset serviti 200, font caricati, `loadFonts` ok, nessun riferimento CDN residuo | | **T2** | **Pagine pubbliche** | login, register, onboarding, forgot/reset, landing → BI (riusando il pilota `login-bi.html`) | render BI, WCAG, login reale 200, redirect corretti | | **T3** | **Sidebar/Topbar (componente)** | `common.js` `loadSidebar()`/topbar come componenti BI (header slim+main, sidebar) | si propaga a tutte le app-page senza romperle; voci/permessi intatti | | **T4** | **App-pages onda A** | dashboard, assessment, risks, incidents, policies → BI | parità funzionale (grafici/form/azioni), WCAG, nessuna regressione | | **T5** | **App-pages onda B** | supply-chain, training, assets, reports, settings, acn-gap, normative, whistleblowing, kb, companies, cross-analysis | idem T4 | | **T6** | **WCAG & cross-check finale** | report contrasti/accessibilità su tutte le pagine + fix residui + rimozione pagine `-bi`/CDN | audit AA completo, 0 hardcoded color fuori token, suite verde end-to-end | > ≥5 trittici richiesti → qui **6**. (Lo stesso schema è riusabile per l'**Asse A conformità**: es. T-AI = AI Act/GDPR nel registro; vedi `PROGETTO_REVISIONE_NIS2.md`.) --- ## 4. Orchestrazione "in background" **Esecuzione**: i trittici girano **in pipeline** (un'unità entra in A1 mentre la precedente è già ad A2/A3 — niente barriere inutili), con **concorrenza limitata** e **gate** tra ondate dove serve l'ordine (T1 prima di tutto; T3 prima di T4/T5). Due modalità: - **(a) Workflow deterministico** (consigliato): un unico script orchestratore (`Workflow`) che fa `pipeline(unità, A1, A2, A3)` con i gate. Vedi skeleton §6. È ripetibile, journaled, con resume. - **(b) Agenti in background manuali**: lanciare i 3 agenti per trittico via `Agent(run_in_background:true)`, leggere i report, gateare a mano. Più controllo, meno automazione. **Stato condiviso & isolamento** - Lavoro in **worktree isolato** (es. `/tmp/nis2-ui-bi`, branch `ui-bi`) → produzione `main` intatta. - Ogni trittico scrive un **report** in `docs/revisione/Tn-report.md` (artefatti + verdetti A2/A3) → tracciabilità e input per il trittico successivo. - I **token AGID** (output di T1) sono la single-source-of-truth per T2–T6. **Sequenza dei gate** ``` T1 ─PASS─▶ T2 ─┐ ├─PASS─▶ T3 ─PASS─▶ T4 ─┐ │ ├─PASS─▶ T6 (finale) └────────────────▶ T5 ──┘ ``` (T2 e T5 possono partire in parallelo dopo T1; T4 richiede T3 per la sidebar.) --- ## 5. Ruoli ↔ agenti specializzati - A2/A3 dei trittici UI usano lente **UI/accessibilità**; per i contenuti normativi si innestano gli agenti già creati **`nis2-expert`** e **`iso-27001-expert`** (`.claude/agents/`) come A2/A3 dell'Asse A. - Finché il harness non carica gli agenti nativi nella sessione, si usano agenti `general-purpose` con le istruzioni-persona iniettate (come fatto in R1/R2). --- ## 6. Skeleton orchestratore (Workflow) — pronto da adattare ```js export const meta = { name: 'trittici-ui-bi', description: 'Rollout Bootstrap Italia a trittici Esecutore→Revisore→Controllore', phases: [{title:'T1 Base'},{title:'T2 Pubbliche'},{title:'T3 Sidebar'}, {title:'T4 App-A'},{title:'T5 App-B'},{title:'T6 WCAG'}], } const VERDICT = { type:'object', properties:{ pass:{type:'boolean'}, difetti:{type:'array', items:{type:'string'}}, note:{type:'string'} }, required:['pass','difetti'] } async function trittico(unit, phase) { // A1 fa const fatto = await agent(`ESECUTORE ${unit.titolo}: ${unit.task}. Zero backend; pagine -bi; report cosa/dove/come verificato.`, {phase, label:`A1:${unit.id}`}) // A2 controlla let rev = await agent(`REVISORE ${unit.titolo}: verifica contro i criteri (BI self-host, WCAG AA, parità funzionale, no backend). Output difetti. Contesto:\n${fatto}`, {phase, label:`A2:${unit.id}`, schema:VERDICT}) // loop rework finché 0 difetti bloccanti (max 2 giri) let giro=0 while(rev && !rev.pass && giro++<2){ await agent(`ESECUTORE ${unit.titolo}: correggi questi difetti:\n${(rev.difetti||[]).join('\n')}`, {phase, label:`A1-fix${giro}:${unit.id}`}) rev = await agent(`REVISORE ${unit.titolo}: ri-verifica.`, {phase, label:`A2-r${giro}:${unit.id}`, schema:VERDICT}) } // A3 ricontrolla (avversariale, sul path reale) const gate = await agent(`CONTROLLORE AVVERSARIALE ${unit.titolo}: re-review indipendente. ESERCITA IL PATH REALE (curl della pagina servita, login vero, niente test sintetici). Conferma 0 regressioni e difetti chiusi. PASS solo se tutto verde.`, {phase, label:`A3:${unit.id}`, schema:VERDICT}) return { unit:unit.id, rev, gate } } phase('T1 Base') const t1 = await trittico({id:'T1', titolo:'Base & self-host BI', task:'self-host bootstrap-italia 2.18.1 + token AGID + smoke'}, 'T1 Base') if(!t1.gate?.pass){ log('T1 FAIL — stop'); return { stop:'T1', t1 } } // T2 e T5 in parallelo dopo T1; T3→T4 in sequenza const UNITS = [ {id:'T2', titolo:'Pagine pubbliche', task:'login/register/onboarding/forgot/reset/landing → BI', phase:'T2 Pubbliche'}, {id:'T3', titolo:'Sidebar/Topbar', task:'common.js loadSidebar/topbar come componenti BI', phase:'T3 Sidebar'}, {id:'T5', titolo:'App-pages onda B', task:'supply-chain/training/assets/reports/settings/... → BI', phase:'T5 App-B'}, ] const par = await parallel(UNITS.map(u => () => trittico(u, u.phase))) const t3 = par.find(x=>x?.unit==='T3') const t4 = t3?.gate?.pass ? await trittico({id:'T4', titolo:'App-pages onda A', task:'dashboard/assessment/risks/incidents/policies → BI'}, 'T4 App-A') : null phase('T6 WCAG') const t6 = await trittico({id:'T6', titolo:'WCAG & cross-check', task:'audit AA completo + rimozione -bi/CDN + suite verde'}, 'T6 WCAG') return { t1, par, t4, t6 } ``` --- ## 7. Come si lancia (governance) - **Costo**: ~3 agenti × 6 trittici (+ rework) = **~20–30 agenti**. È orchestrazione multi-agente esplicita → si lancia **solo su tua conferma** (`Workflow`), perché consuma molti token. - **Avvio**: `Workflow({name:'trittici-ui-bi'})` dopo aver materializzato lo script da §6 (con i task reali) e creato il worktree. - **Monitoraggio**: `/workflows` per la progress; ogni gate FAIL ferma l'ondata. - **Reversibile**: tutto su worktree/pagine `-bi`; il `main` resta verde finché non approvi lo swap. --- ## 8. Riferimenti - Progetto di revisione: `docs/PROGETTO_REVISIONE_NIS2.md` · Gap UI: `docs/VERIFICA_GAP_UI_AGID_BOOTSTRAP_ITALIA.md` - Agenti esperti: `.claude/agents/nis2-expert.md`, `.claude/agents/iso-27001-expert.md` - Pilota: `public/login-bi.html` · Bootstrap Italia v2.18.1 (italia.github.io/bootstrap-italia)