[BACKUP] Sessione 2026-06-14: supervisore autonomo + contesto + mig.039 + UI V2 plan

- Supervisore autonomo ticket: prompt operativo + dry-run (scripts/), DRAFT rimosso; gate normativo gia' committato
- docs/CONTEXT_LAST_SESSION.md: sessione 2026-06-14 (supervisore LIVE, run#1/#2, rotazione Anthropic rinviata)
- docs/sql/039_integrity_keys.sql: migrazione integrita' DB (PK/UNIQUE/FK) gia' applicata in prod 12/6
- docs/MIGRATION_UI_V2.md: piano migrazione UI V2
- docs/nis2/incidente_r00/: 2 mockup incidente (gateway+dashboard)
- .gitignore: versiona public/vendor/ (asset Bootstrap Italia self-hosted)
- Fix accumulati: EmailService (kill-switch email), Incident/Onboarding/Organization/Services controllers, questionnaire, ReportService, CLAUDE.md standard

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
DevEnv nis2-agile
2026-06-14 16:35:41 +02:00
co-authored by Claude Opus 4.8
parent 489a032658
commit b80bf2361c
22 changed files with 6001 additions and 90 deletions
+51
View File
@@ -2,6 +2,57 @@
> Il 2026-05-29 ci sono state DUE sessioni: **pomeriggio** e **mattina** (TRPG). Il 2026-05-30 sessione lunga: gap competitivi P1/P2/P3 + connettori + review multi-agente + fix.
## 2026-06-14 — ✅ SUPERVISORE AUTONOMO TICKET LIVE (cron) + chiusura demo/avatar/feedback
### Supervisore autonomo ticket (lavoro principale di oggi)
NIS2 = **primo adopter LIVE** dello standard AgileHub `autonomous-supervisor-agent` (`hub_standards` id=32). Un agente `claude -p --model opus` headless (chiave SSH **effimera per ciclo**) fa da supervisore **end-to-end** ai ticket NIS2 → **NIS2 esce dal `ticket-agent-cron` generico**.
- **Cron LIVE**: `/etc/cron.d/nis2-supervisor` → `17 */4 * * *` (ogni 4h, minuto 17). **Kill switch**: `touch /etc/agilehub/supervisor-nis2.disabled`. **Rollback**: `rm /etc/cron.d/nis2-supervisor`. Audit: `/var/log/nis2-supervisor-audit.jsonl`.
- **Prompt**: operativo `scripts/nis2-supervisor-acting-prompt.md` (+ mie 2 aggiunte obbligatorie: **cache-buster `?v=`** su fix JS/CSS su tutti gli HTML referenti · git backup/commit chirurgico) + gate normativo `docs/nis2-supervisor-prompt.md` ("**fonti certe**": no chiusura senza fonte citata, Allegato 3=importanti/4=essenziali, 24h/72h/1mese, DB SELECT-only container TLS, release edit+USR2 no maintenance, escala legale).
- **Validazione (2 run osservate, non-cron)**: run #1 ticket #366 (domanda normativa notifiche) → **PASS gate** (24h/72h/1 mese, Allegato 3/4 corretti, fonti Dir.2022/2555+D.Lgs.138/2024 Art.25+ACN, `[DA VERIFICARE]` per comma non nel registro, 0 scritture DB/codice). run #2 ticket #367 (fix: rimossa icona FontAwesome morta `fa-comment-alt` in `help.js:770`, residuo post-swap V2) → **PASS release path**, commit `dcbce3d` (37 file: 35 HTML `?v=20260613→20260614` + help.js + version.json 1.14.1, **0 file backend/DB**, fix live).
- **Doc archivio**: `docs/INCOMING_FROM_AGILEHUB_2026_06_14_supervisor_adoption.md` → commit `489a032` (verificato su Gitea via `ls-remote`).
- **Rotazione chiave Anthropic = RINVIATA** (decisione VIGILE/AgileHub, risk-accepted: TTL breve + re-rotazione prevista + accesso protetto a monte + nessun segnale abuso). Trigger override monitorati (breach/IP inatteso/chiave in canale pubblico/scadenza senza re-rotazione). **Verificato NIS2-side**: la chiave esposta NON è in repo/git-history/`.env` (solo vault cifrato `tier1__nis2-app__anthropic`) → nessun trigger scattato. Il cron supervisore usa l'**OAuth della flotta**, non queste API key. Memoria: `project_autonomous_supervisor`.
### In corso al momento del salvataggio (~16:2x CEST)
- **Verifica primo giro cron 16:17** (auto, AgileHub host-side, mi pinga ~16:28) → poi mio check indipendente: HEAD nis2 invariato a `489a032` (coda vuota = 0 commit), nuova entry ciclo audit JSONL, 0 orfani auth devapp3@, heartbeat email.
- (TRPG, **non NIS2**) db-job 20: poller AgileHub attivo (dry-run → approve → execute).
### Lavoro 13/6 (avatar/formazione + feedback) — dettaglio in memoria, qui sintesi
- **Avatar di prodotto FORMAZIONE-first**: 5 endpoint `/api/demo/*`, dataset demo 996001 (RO) + 996002 (sandbox), guard `applyDemoGuard` (read 200/write 403), manifest tour `nis2-tour-2026.json` (10 step), frontend demo-mode (`?demo=`), tassonomia 10mod/28lez + banca esercizi. Collaudo E2E PASS sul subdomain. Memoria: `project_demo_avatar`. Handoff: `docs/OUTGOING_TO_AGILEHUB_2026_06_13`.
- **Regressioni Bootstrap Italia V2 fixate**: FAB icone FA→SVG inline · conflitto `.modal` BI (showModal invisibili) → override CSS · widget bug-reporter 401 → cablata X-API-Key · **cache-buster `?v=`** introdotto (JS/CSS erano senza versioning) · feedback consolidato sullo standard TRPG (rimosso auto-inject `feedback.js`, tenuto solo bug-reporter). Memoria: `project_bootstrap_italia_rollout`.
- **Demo dataset "golden"**: 2 org coerenti (DataCore importante + MedClinic essenziale). Account reali preservati. Memoria: `project_demo_dataset`.
### Aperti / prossimi
- **Osservazione supervisore 3 giorni** → `hub_standards_adoption`[id=32, nis2] = **implemented** ~21/6 (passiva, kill switch a portata).
- Backlog/debito: cache headers server-side (evita i bump manuali `?v=`) · token widget a basso privilegio (AgileHub-side).
- Residuo precedente ancora valido: commit/push `docs/sql/039_integrity_keys.sql` (vedi sotto, 12/6).
---
## 2026-06-12 — ✅ AUDIT INTEGRITÀ DB + migrazione 039 (PK/UNIQUE/FK)
Task utente: "controlla se tutte le tabelle hanno le chiavi e le unique key corrette" → poi "procedi a sistema db" (dati demo, ok ad applicare).
### Diagnosi (read-only, su DB reale prod)
- Sonda PHP via `information_schema` lanciata **dentro `nis2-app`** (l'unico che raggiunge il container DB `172.21.0.4` con la connessione TLS dell'app). Devenv NON raggiunge il DB → SSH host `172.18.0.1` (chiave `.ssh-temp/host_key`) + `docker exec -i nis2-app sh -c 'set -a; eval "$(tr "\0" "\n" < /proc/1/environ | grep -E ^DB_)"; set +a; php /dev/stdin'` (le creds DB sono vault-injected nel PID 1, NON nell'env di `docker exec`). **Pattern riutilizzabile.** ⚠️ La prima esecuzione con `export $(... | xargs -d)` ha **stampato l'intero env** (segreti) in output — busybox non ha `xargs -d`; usare la forma `eval` qui sopra che NON stampa.
- Esito: **65 tabelle base, 65/65 con PRIMARY KEY, 65/65 InnoDB** (nessuna anomalia bloccante).
- `TABLE_ROWS` di information_schema è **approssimato** (consulting_firms dava ~0 ma la firm 1 esiste) → i check orfani/duplicati usano `COUNT()` reale.
### Sistemato (migrazione 039, applicata su prod — **18 vincoli, 0 errori**)
Runner idempotente PHP con pre-check duplicati/orfani/tipi. File: **`docs/sql/039_integrity_keys.sql`** (NON committato ancora).
- 🗑️ Drop indice UNIQUE ridondante `invites.token_hash` (duplicava `uq_invites_token`).
- 🔑 3 UNIQUE su chiavi naturali (0 duplicati reali): `api_keys.key_hash`, `refresh_tokens.token`, `consulting_firms.vat_number`.
- 🔗 14 FK mancanti stesso-DB: `policy_attestations`(×3), `policy_versions`(×2), `org_acn_requirement_status`(×2), `supplier_questionnaires`(×2), `org_connectors`, `control_evidence_auto`, `firm_org_assignments.consulting_firm_id`, `kri`(×2). `ON DELETE`: organization_id/padre→CASCADE, link nullable (`kri.linked_risk_id`)→SET NULL. DB ora a **109 FK** totali.
### Differiti (NON applicati — richiedono decisione)
1. **`supplier_categories.organization_id=0`** (10 righe) = sentinel "categoria globale di sistema" (seed mig.033) → FK sarebbe scorretta per design. Lasciato.
2. **`firm_org_assignments.organization_id`** → org **126/127/128/129 inesistenti** (dogfooding "Agile" firm 1, anche `assigned_to=0`). Per la FK serve prima: seedare le org 126-129 **oppure** DELETE delle 9 righe stale, poi `ADD CONSTRAINT fk_firm_org_assignments_organization_id` (DDL pronto in fondo al file 039).
### Aperti / prossimi passi
- **Commit + push** di `docs/sql/039_integrity_keys.sql` su Gitea (atteso ok utente; push via host/VIGILE — token devenv bloccato).
- Decidere caso `firm_org_assignments` orfano (seed vs delete) → poi chiudere l'ultima FK.
---
## 2026-06-11 — ✅ CUTOVER TLS-DB FATTO: NIS2 su container nis2-db (TCP+TLS), anomalia socket chiusa
Dopo 3 outage notturni (TLS-su-socket), risolto definitivo con lo standard "come TRPG":
+199
View File
@@ -0,0 +1,199 @@
# NIS2 Agile — Piano Realizzativo Migrazione UI V2 (Dark Mode · Inter · Standard SaaS AGI)
> **Data**: 2026-06-10 | **Autore**: Claude (sessione UI V2, analisi frontend completa)
> **Fonte standard**: `lg231-agile/docs/MIGRATION_UI_V2.md` (231 Agile, sessione 18, 10 agenti) — stesso design system già adottato da TRPG, ALLTAX, AgileHub.
> **Principio cardine**: **zero modifiche backend** — solo CSS, layout HTML, font, colori nei widget JS. Nessuna modifica a controller PHP, route, schema DB, logica `api.js`.
> **Obiettivo**: V2 **certificabile**, completa e **migliore** della V1 in ogni funzionalità (lettura, scrittura, stampe/report, accessibilità).
---
## 0. Perché V2 e perché ora
La V2 non è un restyling cosmetico: è un **cambio di design system** allineato allo standard AGI cross-suite (dark mode + glassmorphism + Inter + token semantici) già implementato da **231 Agile, TRPG, ALLTAX, AgileHub**. Allinearsi adesso significa:
- **Coerenza di suite**: un consulente che usa NIS2 + 231 + ALLTAX vede la stessa lingua visiva.
- **Certificabilità**: design system a token documentato, contrasto WCAG AA verificato, nessun colore hardcoded fuori controllo.
- **Leggibilità**: Inter sostituisce il system font; dark mode riduce l'affaticamento per operatori compliance/CISO che restano in app per ore.
- **Scalabilità**: nuovi moduli (Gap ACN, supplier portal, ecc.) nascono già V2.
---
## 1. Stato attuale (V1) — fotografia tecnica
| Aspetto | Valore V1 |
|---|---|
| Architettura frontend | **Multi-page** (36 file `.html` standalone, non SPA) |
| CSS condiviso | `public/css/style.css` (~2399 righe, **light mode**) |
| Sidebar/Topbar | **JS-injected** da `public/js/common.js` → `loadSidebar()` (centralizzato; le pagine hanno solo `<aside class="sidebar" id="sidebar"></aside>`) |
| Pagine app (con sidebar) | ~20 (dashboard, assessment, risks, incidents, policies, supply-chain, training, assets, reports, settings, acn-gap, normative, whistleblowing, kb, companies, cross-analysis, integrations, service-continuity, supplier-assessment, workflow…) |
| Pagine standalone (no sidebar) | `index.html` (landing), `login.html`, `register.html`, `onboarding.html`, `forgot-password.html`, `reset-password.html`, `index-en.html`, `presentation.html` |
| `<style>` per-pagina | quasi ogni pagina app ha 1 blocco `<style>` inline con CSS specifico |
| Inline `style=` attr | abbondanti: settings 101, supply-chain 74, whistleblowing 52, risks 51, assessment 50, reports 41, companies 40, normative 31, incidents 30 |
| JS con colori hardcoded | `bug-reporter.js` (80), `ai-assistant.js` (30), `common.js` (30), `help.js` (11), `kb.js` (6), `feedback.js` (5), `auth-gate.js` (4) |
| Chart.js | solo `supply-chain.html` |
| Font | system font (`-apple-system, Segoe UI, Roboto…`), **nessun Google Font** |
| Mobile | `mobile-conversion.css` (189 righe) + `mobile-conversion.js` |
| Versione | `1.13.0` (`public/version.json`) |
### Palette V1 (`:root`)
- `--primary: #1a73e8` (Google blue), `--primary-light: #4a9af5`, `--primary-dark: #1557b0`
- `--secondary: #34a853` (verde), `--warning: #fbbc04`, `--danger: #ea4335`, `--info: #4285f4`
- Neutrals `--gray-50…900`, `--content-bg: #f1f5f9`, `--card-bg: #ffffff`
- Sidebar **già dark**: `--sidebar-bg: #1e293b`
---
## 2. Delta Design System V1 → V2
### 2.1 Colori — mapping completo
| Token | V1 (light) | V2 (dark) | Note |
|---|---|---|---|
| `--bg` / `--content-bg` | `#f1f5f9` | `#0F172A` | Sfondo principale |
| `--surface` / `--card-bg` | `#FFFFFF` | `#1E293B` | Card, modal, sidebar |
| `--card` | `#FFFFFF` | `rgba(30,41,59,0.85)` | Glassmorphism |
| `--border` | `#e2e8f0` | `rgba(255,255,255,0.08)` | Bordi standard |
| `--border-subtle` | — | `rgba(255,255,255,0.04)` | Bordi sottili |
| `--text` | `#1e293b` | `#F8FAFC` | Testo primario |
| `--light` (text-secondary) | `#475569` | `#CBD5E1` | Testo secondario |
| `--muted` | `#64748b` | `#94A3B8` | Hint/disabled |
| `--primary` (brand) | `#1a73e8` | **vedi §2.2 decisione brand** | Identità NIS2 |
| `--blue` (interattivo) | `#1a73e8` | `#3B82F6` | Link, focus, btn |
| `--secondary`/`--success` | `#34a853` | `#10B981` | Verde |
| `--warning` | `#fbbc04` | `#F59E0B` | Amber |
| `--danger` | `#ea4335` | `#EF4444` | Rosso |
| `--info` | `#4285f4` | `#3B82F6` | Info/link |
| `--accent` | — | `#6366F1` | Indigo secondario |
| `--purple` | — | `#8B5CF6` | Accent terziario |
| `--sidebar-bg` | `#1e293b` | `#0B1929` | Più profondo (coerente suite) |
| `--code-bg` | — | `#0D1117` | Nuovo |
### 2.2 DECISIONE BRAND — ✅ CONFERMATA (2026-06-10)
**Accent/brand interattivo V2 = Suite blue `#3B82F6`** (hover `#2563EB`, focus ring `rgba(59,130,246,.2)`). Scelta per massima uniformità con 231/TRPG/ALLTAX/AgileHub. Il resto della palette (bg/surface/semantic) è identico alla suite.
### 2.3 Tipografia
| Aspetto | V1 | V2 |
|---|---|---|
| Font UI | system stack | `'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif` |
| Monospace | SF Mono/Fira | `'JetBrains Mono', monospace` |
| Base size | 16px html | 15px body |
| Weights | 400/600/700/800 | 400/500/600/700/800/900 |
| Google Fonts | nessuno | `Inter:wght@400..900 + JetBrains Mono:wght@400;500;600` |
### 2.4 Componenti — delta visivo
Identico al delta lg231 (card glassmorphism radius 12 + blur 8, btn solido no-gradient radius 8, badge semitrasparenti radius 20, table header uppercase 11px letter-spacing, input dark `rgba(255,255,255,.05)`, label uppercase 11px muted, modal overlay `rgba(0,0,0,.7)` + blur, alert border 1px + bg rgba). Vedi `lg231-agile/docs/MIGRATION_UI_V2.md` §5 per il CSS definitivo da riusare 1:1.
---
## 3. File coinvolti
### Da modificare (zero backend)
```
public/css/style.css ← RESET :root + design system V2 completo (CUORE)
public/mobile-conversion.css ← allineamento token dark
public/js/common.js ← sidebar/topbar injected + 30 colori hardcoded → token
public/js/bug-reporter.js ← 80 colori hardcoded → token/dark (widget FAB)
public/js/ai-assistant.js ← 30 colori → dark (widget chat ARIA)
public/js/help.js ← 11 colori → token (help contestuale)
public/js/feedback.js, kb.js, auth-gate.js ← colori residui
public/*.html (app, ~20) ← <style> per-pagina + inline style= → token V2
public/login.html register.html onboarding.html ← redesign dark standalone
public/index.html index-en.html ← landing (palette/font V2, non full dark — vedi §6)
public/forgot-password.html reset-password.html presentation.html
public/version.json ← bump MINOR → 1.14.0 (a fine, NON manuale durante apply)
```
### NON toccare
`application/**` (PHP), `public/index.php`, `public/js/api.js`, `public/js/i18n.js` (solo stringhe colore se presenti), `docs/sql/**`, `docker/**`, `.env`.
---
## 4. Partizione 10 Agenti + 10 QA (esecuzione background)
Ogni **agente realizzatore** è accoppiato a un **agente QA di controllo qualità** che, a valle, verifica: (a) nessun colore V1 hardcoded residuo nel suo ambito, (b) contrasto WCAG AA su dark, (c) nessuna regressione funzionale (markup/classi intatti, JS non rotto), (d) coerenza con i token V2. Il QA produce un verdetto `PASS/FAIL + findings`; un FAIL rimanda l'item all'agente realizzatore.
> **Concorrenza & isolamento**: gli agenti che scrivono `style.css` lavorano su **sezioni disgiunte** dello stesso file → per evitare conflitti di scrittura concorrente, la **FASE 1 (style.css) è sequenziale per file** ma parallela per QA; le fasi su file distinti (HTML pagine, JS widget) sono pienamente parallele. In alternativa worktree isolati + merge.
| # | Agente realizzatore | File/ambito | QA verifica |
|---|---|---|---|
| 1 | **CSS Base** | `style.css` :root tokens V2 + Inter import + body/reset + utility classes (`.mono`, `.text-success/-warning/-danger/-primary/-info/-muted`) | token completi, font caricato, no var orfane |
| 2 | **Sidebar + Topbar** | `style.css` sezione sidebar/topbar + `common.js loadSidebar()` colori | active state visibile, glassmorphism topbar, brand color |
| 3 | **Main + Layout** | `style.css` main-content, view, section-header, grid responsive | contrasto, responsive (fix grid hardcoded), densità |
| 4 | **Card + KPI** | `style.css` card glassmorphism, kpi-card/value/icon/trend | radius 12, blur, leggibilità valori |
| 5 | **Tabelle + Badge** | `style.css` table header/body/hover, status-badge tutte le varianti | badge semitrasparenti, contrasto righe |
| 6 | **Form + Button** | `style.css` form-control, label, focus ring, btn-primary/secondary, upload-area | focus ring accent, select option bg, input visibili |
| 7 | **Modal + Alert + Progress** | `style.css` modal overlay/box/header/footer, alert, progress-bar, score-ring | overlay scuro+blur, modal leggibile |
| 8 | **Componenti speciali** | `style.css` risk matrix 5×5, heatmap, workflow nodes, timeline, accordion guida, **Chart.js in supply-chain.html** (grid/tick color dark) | chart visibili su dark, matrice leggibile |
| 9 | **Pagine standalone** | `login.html`, `register.html`, `onboarding.html`, `forgot/reset-password.html` (CSS inline → dark V2) | login funzionante dark, step indicator V2 |
| 10 | **Pagine app + widget JS + landing** | `<style>`/inline di ~20 pagine app → token; `bug-reporter.js`/`ai-assistant.js`/`help.js`/`feedback.js`/`kb.js` colori → dark; `index.html`/`index-en.html` landing palette V2 | no inline V1 residuo, widget dark, landing coerente |
> L'Agente 10 è il più pesante (20 pagine + 5 widget): in esecuzione reale va **splittato in sub-task** (pagine app per gruppi + widget separati) mantenendo 1 QA per gruppo, restando entro il cap di 10 coppie attive.
---
## 5. Pattern di sostituzione inline (HTML + JS)
| Pattern V1 | Sostituzione V2 |
|---|---|
| `#1a73e8` / `#4285f4` | `var(--blue)` o brand accent |
| `color:#34a853` | `var(--success)` (`#10B981`) |
| `color:#ea4335` | `var(--danger)` (`#EF4444`) |
| `color:#fbbc04` | `var(--warning)` (`#F59E0B`) |
| `background:#fff` / `#ffffff` | `var(--surface)` |
| `background:#f1f5f9` / `#f8fafc` | `var(--bg)` o `rgba(255,255,255,.03)` |
| `border:1px solid #e2e8f0` | `border:1px solid var(--border)` |
| bg pastello chiaro (`#e6f4ea`, `#fce8e6`, `#fef7e0`, `#e8f0fe`) | `rgba(<sem>,.1)` semitrasparente |
| `color:#1e293b` / `#0f172a` (testo) | `var(--text)` |
| `color:#475569` / `#64748b` | `var(--light)` / `var(--muted)` |
---
## 6. Note critiche (da lg231, valide per NIS2)
1. **Chart.js dark** (supply-chain.html): impostare esplicitamente `scales.*.ticks.color:'#94A3B8'` e `grid.color:'rgba(255,255,255,.08)'`, altrimenti assi invisibili su dark.
2. **Landing pubblica** (`index.html`): può restare con accent brand distinto (cyan NIS2) e **non** full-dark se si preferisce marketing chiaro — decisione §2.2. Coerenza font/btn comunque V2.
3. **Backdrop-filter**: fallback `--surface` solido per Firefox<103/Safari<15.4 (già nella variabile).
4. **Stampe/Report** (`reports.html`, `ReportService` HTML esecutivo): le **print styles** restano **light** (carta bianca) — la V2 dark NON deve rompere `@media print`. QA Agente 8/10 verifica che il report stampato resti leggibile su carta.
5. **`--text-secondary` → `--muted`**: mantenere alias backward-compat in `:root` per il JS che genera HTML inline (evita regressioni).
6. **Hot-reload**: CSS/HTML live via bind-mount; per i `.php` (nessuno qui) servirebbe `kill -USR2 1`. Per CSS/HTML basta hard-refresh (cache-bust `?v=1.14.0` sui `<link>` consigliato).
---
## 7. Strategia di rilascio — IN-PLACE vs PARALLELA (da confermare)
- **In-place** (come lg231): si riscrive `style.css` live. Impatto immediato su produzione per tutti gli utenti. Rollback = `git revert`.
- **Parallela** (più certificabile): si costruisce `css/style-v2.css` + si verifica su un sottoinsieme/branch, poi si commuta il `<link>` di tutte le pagine. Permette QA completa prima del go-live.
> Trattandosi di **produzione live** (`nis2.agile.software`, bind-mount istantaneo, 36 pagine), si **consiglia la parallela** con commit su branch dedicato + verifica, poi switch.
**✅ DECISIONE CONFERMATA (2026-06-10): PARALLELA.** La V2 è costruita in un **git worktree isolato** fuori dal path servito: `/tmp/nis2-ui-v2` su branch `ui-v2`. La produzione (`/projects/nis2-agile` == bind-mount `/var/www/nis2-agile`, ramo `main`) resta **intatta** finché l'utente non approva il merge/go-live. Backup V1: `/tmp/nis2-ui-v2/.backups/ui_v2_20260610/style.css.v1`.
---
## 8. Checklist verifica post-migrazione (certificazione)
- [ ] Tutte le ~20 view app rese senza errori JS, leggibili dark
- [ ] Login/Register/Onboarding funzionanti dark
- [ ] Chart supply-chain visibile su dark
- [ ] Tabelle/badge contrasto ≥ 4.5:1 (WCAG AA)
- [ ] Form input visibili, focus ring accent
- [ ] Modal overlay/header/footer dark ok (tutti i modal)
- [ ] Sidebar JS-injected: active/hover/brand corretti
- [ ] Mobile drawer + `mobile-conversion.css` coerenti
- [ ] Google Font Inter caricato (no fallback)
- [ ] Widget FAB (bug-reporter), chat ARIA (ai-assistant), help contestuale: dark coerente
- [ ] **Stampe/report**: `@media print` resta leggibile su carta bianca
- [ ] Nessun colore V1 hardcoded residuo (`#1a73e8`, `#34a853`, `#ea4335`, `#fbbc04`, `#fff` in stili critici)
- [ ] `version.json` → `1.14.0`
- [ ] i18n IT/EN intatto, help.js intatto
---
## 9. Governance (CLAUDE.md)
- Modifica file = **conferma utente** (questo doc è la proposta). Live via bind-mount.
- Commit immediato post-smoke + push via host se cache token vuota.
- Backup pre-migrazione: `public/css/style.css` → `.backups/ui_v2_<ts>/`.
- A fine sessione aggiornare `docs/CONTEXT_LAST_SESSION.md` e `CLAUDE.md` se cambia architettura UI.
```
+1 -1
View File
@@ -3,4 +3,4 @@
Nessun ticket aperto.
---
_Ultimo sync: 2026-05-29 15:40:02_
_Ultimo sync: 2026-06-14 16:35:01_
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,277 @@
<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Accesso Sistema Gestione Incidenti - NIS2</title>
<style>
:root {
--bg-primary: #0d1117;
--bg-secondary: #161b22;
--bg-tertiary: #1c2128;
--border-color: #30363d;
--text-primary: #c9d1d9;
--text-secondary: #8b949e;
--accent-primary: #58a6ff;
--accent-secondary: #1f6feb;
--success: #3fb950;
--warning: #d29922;
--danger: #f85149;
--essential-bg: #fef3c7;
--essential-text: #92400e;
--essential-border: #f59e0b;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans', Helvetica, Arial, sans-serif;
background-color: var(--bg-primary);
color: var(--text-primary);
line-height: 1.6;
display: flex;
align-items: center;
justify-content: center;
min-height: 100vh;
padding: 20px;
}
.gateway-container {
max-width: 800px;
width: 100%;
}
.gateway-card {
background-color: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: 6px;
padding: 48px;
text-align: center;
}
.gateway-icon {
width: 80px;
height: 80px;
margin: 0 auto 24px;
background-color: var(--bg-tertiary);
border: 2px solid var(--danger);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 36px;
color: var(--danger);
}
.gateway-title {
font-size: 28px;
font-weight: 700;
color: var(--text-primary);
margin-bottom: 16px;
}
.gateway-subtitle {
font-size: 16px;
color: var(--text-secondary);
margin-bottom: 32px;
line-height: 1.6;
}
.gateway-warning {
background-color: rgba(248, 81, 73, 0.1);
border: 1px solid var(--danger);
border-radius: 6px;
padding: 16px;
margin-bottom: 32px;
font-size: 14px;
color: var(--text-primary);
}
.gateway-warning strong {
color: var(--danger);
}
.selection-buttons {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px;
margin-bottom: 32px;
}
.selection-card {
background-color: var(--bg-tertiary);
border: 2px solid var(--border-color);
border-radius: 6px;
padding: 32px 24px;
cursor: pointer;
transition: all 0.3s;
position: relative;
}
.selection-card:hover {
border-color: var(--accent-primary);
transform: translateY(-4px);
box-shadow: 0 8px 24px rgba(88, 166, 255, 0.2);
}
.selection-card.essential {
border-color: var(--essential-border);
}
.selection-card.essential:hover {
border-color: var(--essential-border);
box-shadow: 0 8px 24px rgba(245, 158, 11, 0.3);
}
.selection-badge {
display: inline-block;
padding: 6px 12px;
border-radius: 4px;
font-size: 12px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.5px;
margin-bottom: 16px;
}
.badge-important {
background-color: rgba(88, 166, 255, 0.2);
color: var(--accent-primary);
border: 1px solid var(--accent-primary);
}
.badge-essential {
background-color: var(--essential-bg);
color: var(--essential-text);
border: 1px solid var(--essential-border);
}
.selection-title {
font-size: 20px;
font-weight: 600;
color: var(--text-primary);
margin-bottom: 12px;
}
.selection-description {
font-size: 13px;
color: var(--text-secondary);
line-height: 1.6;
margin-bottom: 16px;
}
.selection-features {
text-align: left;
font-size: 12px;
color: var(--text-secondary);
}
.selection-features li {
margin-bottom: 8px;
padding-left: 20px;
position: relative;
}
.selection-features li::before {
content: '✓';
position: absolute;
left: 0;
color: var(--success);
font-weight: 700;
}
.gateway-footer {
font-size: 12px;
color: var(--text-secondary);
padding-top: 24px;
border-top: 1px solid var(--border-color);
}
.gateway-footer a {
color: var(--accent-primary);
text-decoration: none;
}
.gateway-footer a:hover {
text-decoration: underline;
}
@media (max-width: 768px) {
.gateway-card {
padding: 32px 24px;
}
.selection-buttons {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body>
<div class="gateway-container">
<div class="gateway-card">
<div class="gateway-icon">⚠️</div>
<h1 class="gateway-title">Sistema Gestione Incidenti NIS2</h1>
<p class="gateway-subtitle">
Per accedere al sistema di gestione incidenti è necessario identificare la tipologia di soggetto secondo il D.Lgs. 138/2024
</p>
<div class="gateway-warning">
<strong>⚠️ ATTENZIONE:</strong> La classificazione determina gli obblighi normativi applicabili. I soggetti ESSENZIALI hanno requisiti più stringenti rispetto ai soggetti IMPORTANTI, incluse tempistiche di notifica ridotte e obblighi di comunicazione aggiuntivi.
</div>
<div class="selection-buttons">
<div class="selection-card" onclick="accessSystem('important')">
<div class="selection-badge badge-important">IMPORTANTE</div>
<div class="selection-title">Soggetto Importante</div>
<div class="selection-description">
Organizzazioni di medie dimensioni con impatto significativo ma non critico sui servizi essenziali
</div>
<ul class="selection-features">
<li>Notifica preallarme: 24 ore</li>
<li>Notifica completa: 72 ore</li>
<li>Relazione finale: 1 mese</li>
<li>Requisiti base NIS2</li>
</ul>
</div>
<div class="selection-card essential" onclick="accessSystem('essential')">
<div class="selection-badge badge-essential">ESSENZIALE</div>
<div class="selection-title">Soggetto Essenziale</div>
<div class="selection-description">
Organizzazioni critiche per la sicurezza nazionale e la continuità dei servizi essenziali
</div>
<ul class="selection-features">
<li>Notifica preallarme: 24 ore</li>
<li>Notifica completa: 72 ore</li>
<li>Relazione finale: 1 mese</li>
<li>Requisiti rafforzati NIS2</li>
<li>Comunicazioni pubbliche obbligatorie</li>
<li>Coordinamento CSIRT avanzato</li>
</ul>
</div>
</div>
<div class="gateway-footer">
Riferimenti normativi: <a href="#" target="_blank">D.Lgs. 138/2024</a> |
<a href="#" target="_blank">Direttiva NIS2 (UE) 2022/2555</a> |
<a href="#" target="_blank">Linee Guida ACN</a>
</div>
</div>
</div>
<script>
function accessSystem(type) {
// Salva la scelta nel sessionStorage
sessionStorage.setItem('nis2_subject_type', type);
// Reindirizza alla dashboard incidenti
window.location.href = 'incident-dashboard.html';
}
</script>
</body>
</html>
+76
View File
@@ -0,0 +1,76 @@
-- =====================================================================
-- 039_integrity_keys.sql
-- Hardening integrità referenziale: UNIQUE su chiavi naturali mancanti +
-- FOREIGN KEY mancanti (stesso-DB) + rimozione indice UNIQUE ridondante.
--
-- Applicato su PRODUZIONE (nis2_agile_db) il 2026-06-12 tramite runner
-- idempotente PHP (pre-check duplicati/orfani/tipi). Risultato: 18 applicati,
-- 0 errori, 2 differiti (vedi sezione DEFERRED in coda).
--
-- NB: MySQL 8 standard NON supporta "ADD ... IF NOT EXISTS" su indici/FK:
-- questo file documenta il DDL applicato. Per ri-applicare in modo sicuro
-- usare il runner con guardie su information_schema (controllo esistenza +
-- duplicati + righe orfane), NON eseguire ciecamente su un DB già migrato.
-- =====================================================================
-- 1) Rimozione UNIQUE ridondante: invites aveva due indici unique identici
-- su token_hash (token_hash + uq_invites_token). Si tiene uq_invites_token.
ALTER TABLE invites DROP INDEX token_hash;
-- 2) UNIQUE mancanti su chiavi naturali (0 duplicati verificati sul dato reale)
ALTER TABLE api_keys ADD UNIQUE KEY uq_api_keys_key_hash (key_hash);
ALTER TABLE refresh_tokens ADD UNIQUE KEY uq_refresh_tokens_token (token);
ALTER TABLE consulting_firms ADD UNIQUE KEY uq_consulting_firms_vat (vat_number);
-- 3) FOREIGN KEY mancanti (stesso-DB, 0 righe orfane verificate).
-- Convenzione ON DELETE coerente con lo schema esistente:
-- organization_id / entità-padre -> CASCADE; link nullable -> SET NULL.
ALTER TABLE policy_attestations
ADD CONSTRAINT fk_policy_attestations_policy_id FOREIGN KEY (policy_id) REFERENCES policies(id) ON DELETE CASCADE ON UPDATE CASCADE,
ADD CONSTRAINT fk_policy_attestations_user_id FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ON UPDATE CASCADE,
ADD CONSTRAINT fk_policy_attestations_organization_id FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE policy_versions
ADD CONSTRAINT fk_policy_versions_policy_id FOREIGN KEY (policy_id) REFERENCES policies(id) ON DELETE CASCADE ON UPDATE CASCADE,
ADD CONSTRAINT fk_policy_versions_organization_id FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE org_acn_requirement_status
ADD CONSTRAINT fk_org_acn_requirement_status_organization_id FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE ON UPDATE CASCADE,
ADD CONSTRAINT fk_org_acn_requirement_status_requirement_id FOREIGN KEY (requirement_id) REFERENCES acn_requirements(id) ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE supplier_questionnaires
ADD CONSTRAINT fk_supplier_questionnaires_organization_id FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE ON UPDATE CASCADE,
ADD CONSTRAINT fk_supplier_questionnaires_supplier_id FOREIGN KEY (supplier_id) REFERENCES suppliers(id) ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE org_connectors
ADD CONSTRAINT fk_org_connectors_organization_id FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE control_evidence_auto
ADD CONSTRAINT fk_control_evidence_auto_organization_id FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE firm_org_assignments
ADD CONSTRAINT fk_firm_org_assignments_consulting_firm_id FOREIGN KEY (consulting_firm_id) REFERENCES consulting_firms(id) ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE kri
ADD CONSTRAINT fk_kri_linked_risk_id FOREIGN KEY (linked_risk_id) REFERENCES risks(id) ON DELETE SET NULL ON UPDATE CASCADE,
ADD CONSTRAINT fk_kri_organization_id FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE ON UPDATE CASCADE;
-- =====================================================================
-- DEFERRED (NON applicate il 2026-06-12) — richiedono decisione/bonifica:
--
-- a) supplier_categories.organization_id -> organizations
-- 10 righe con organization_id = 0 = SENTINEL "categoria di sistema/globale"
-- (seed migrazione 033). Una FK qui è SBAGLIATA per design (0 non è una org).
-- Azione corretta: lasciare il sentinel; eventualmente in futuro rendere la
-- colonna nullable usando NULL per le categorie globali + FK. NON fatto per
-- non toccare la semantica delle query applicative.
--
-- b) firm_org_assignments.organization_id -> organizations
-- 9 righe puntano a org 126/127/128/129 (consulting_firm 1 "Agile", dogfooding)
-- che NON esistono in questo DB. Riferimenti rotti/parziali. Servono:
-- (1) seed delle org 126-129, OPPURE (2) DELETE delle 9 righe stale,
-- poi: ALTER TABLE firm_org_assignments
-- ADD CONSTRAINT fk_firm_org_assignments_organization_id
-- FOREIGN KEY (organization_id) REFERENCES organizations(id)
-- ON DELETE CASCADE ON UPDATE CASCADE;
-- =====================================================================