Tutte le modifiche rilevanti all'applicazione sono documentate in questo file.
Il formato segue Keep a Changelog.
Fix — F81 (1.4.1) non riconosceva un indicatore testuale in prossimità DOM immediata
checkF81_RequiredColorOnly(src/lib/wcag-techniques/checks/static/forms.ts) — riconosce ora anche un indicatore testuale espresso come testo diretto del genitore o del fratello DOM immediato (es. un asterisco in uno<span>adiacente al campo), non solo vialabel/aria-label/aria-describedby. Chiudeva un backlog aperto dal 2026-07-25 (f81-indicatore-solo-prossimita-dom-non-rilevato) dopo 3 round di fix falliti sulla stessa classe di bug (contaminazione cross-campo): la generalizzazione via.text()ricorsivo suparent()/prev()/next()faceva "filtrare" l'indicatore di un campo in un campo fratello senza indicatore proprio. Il 4° tentativo applica in modo uniforme la correzione già parziale del round 2 (nodi di testo diretti, mai.text()ricorsivo) sia al genitore sia ai fratelli — il leak residuo del round 2 veniva esattamente dall'incoerenza tra i due. Nuovo limite residuo, più stretto e onesto: un indicatore annidato un livello più in profondità nel fratello (es.<span><em>*</em></span>) resta non rilevato — documentato in/help-valutazione.
Fix — /cookie-policy reindirizzava al login
auth.config.ts—isPublicPath()non includeva/cookie-policy: una pagina puramente informativa GDPR/legale, senza logica di sessione propria, veniva quindi trattata come area riservata e reindirizzata a/loginper chiunque non fosse autenticato. Aggiuntopath === '/cookie-policy'alla whitelist.
Fix — "0 progetti" silenzioso su Home con sessione scaduta
DashboardClient.tsx— Il pattern fetch difensivo esteso in 1.97.0 (fetch('/api/dashboard')con fallback silenzioso) assorbiva anche il caso 401 (sessione JWT stale dopo rebuild container, vedi 1.96.4), mostrando "Nessun progetto presente" senza alcun indizio che la causa fosse una sessione scaduta e non un portfolio vuoto. Aggiunto rilevamento esplicito dir.status === 401con banner "Sessione scaduta" (pulsante "Accedi" verso/login) al posto dello stato vuoto silenzioso.
Osservabilità — Sentry Uptime Monitoring (chiude uptime-monitoring-sentry / uptime-monitoring-esterno-mancante)
- Monitor Sentry Uptime — Configurato un monitor esterno (
valida-app-main-health, progettojavascript-nextjs) suhttps://valida-app.xyz/api/health, interval 10 minuti, regioni EU+US. Il piano Sentry free/Developer consente al massimo 1 Uptime Monitor totale (non 1 per progetto come pianificato originariamente): un secondo monitor dedicato al renderer-service è stato creato e ha superato il test iniziale, ma la sua attivazione ha fallito ("Unable to update monitor") perché richiede budget pay-as-you-go, quindi è stato eliminato. Copertura equivalente ottenuta con due assertion in Verification sull'unico monitor — Status Code (2xx, default) + JSON Path$.services.rendererequalsok— che sfrutta il campo già presente nel body di/api/health. Alert rule 694602 ("Send a notification for high priority issues") collegata. Documentazione completa inSENTRY_UPTIME_SETUP.mdesecond-brain/superpowers/specs+plans/2026-08-06-uptime-monitoring-sentry*. Test end-to-end (downtime simulato → alert → recovery) ancora da eseguire.
Fix — ON CONFLICT arbiter su indice parziale dom_hash (cross-section-issues)
src/app/api/cross-section-issues/route.ts— La clausolaON CONFLICT (valutazione_id, criterion_id, dom_hash) DO NOTHINGnon ripeteva il predicatoWHERE dom_hash IS NOT NULLdell'indice unico parziale definito insrc/db/migrate.ts, causando"no unique or exclusion constraint matching the ON CONFLICT specification"(SentryJAVASCRIPT-NEXTJS-A). Aggiunto il predicato mancante alla clausola; nuovo test di integrazione (on-conflict.integration.test.ts, esegue query reali contro Postgres) copre la regressione.
Fix — Estensione pattern fetch difensivo a più viste
DashboardClient.tsx,ValutazioneClient.tsx,DettaglioClient.tsx,PagineClient.tsx— Dopo un restart con cookie di sessione stale, le API rispondono con un oggetto errore invece dell'array/oggetto atteso, causando crash su.filter()/.map()o accesso a proprietà mancanti. Estende il pattern difensivo già introdotto in 1.96.4 (fetch(url).then(r => r.ok ? r.json() : fallback)) ai fetch di risultati/componenti in queste viste, e aversions?.ibm/versions?.verapdfinLibraryVersionsBlock.
Fix — Build rotta su HEAD (commit ae25c7b incompleto)
src/lib/utils/portfolio.ts+src/__tests__/portfolio.test.ts— Il commitae25c7bintroduceva l'import di@/lib/utils/portfolioinDashboardClient.tsxma i due file non erano stati aggiunti al commit (rimanevano untracked nel working tree).HEADera quindi rotto:tsc --noEmitfalliva conTS2307: Cannot find module '@/lib/utils/portfolio'. Commit di follow-upd142afaaggiunge i file mancanti; build e test suite (1548 test) tornano verdi.
Dashboard — lastRoundDate per ordinamento portfolio Home
- API (
/api/dashboard) — Aggiunto campolastRoundDateal payload di ogni progetto (data dell'ultimo round, formatoYYYY-MM-DD), consumato daselectRecentProjects()per mostrare in Home solo gli ultimi 5 progetti con almeno una valutazione, ordinati per data più recente.
Accessibilità — Target touch 44×44px su .btn (WCAG 2.5.5)
src/app/globals.css— La regola globalebutton, select, [role="button"] { min-height: 2.75rem }non copriva<a>/<Link>renderizzati con classe.btn. Estesa la regola a.btn;.btn--smresta esplicitamente più piccolo (min-height: unset) per le varianti compatte volute.
Fix — Crash "Si è verificato un errore imprevisto" dopo rebuild container
DashboardClient.tsx,AiChatbotSidebar.tsx— Dopodocker compose up --build -d, sessioni JWT/cookie stale facevano restituire alle API/api/dashboard,/api/dashboard/workflow,/api/dashboard/live,/api/chatbot/sessionsun oggetto errore 401{"error":"Non autenticato"}invece degli array attesi. Il client chiamava.filter()sull'oggetto →TypeError: u.filter is not a function→ error boundary rootglobal-error.tsx. Fix difensivo:Array.isArray(data)su/api/dashboarde/api/chatbot/sessions;typeof data === 'object' && data !== null(con log) su/api/dashboard/workflowe/api/dashboard/live. L'errore viene loggato in console invece di far crashare l'app. Causa radice probabile:AUTH_SECRET=dev-secret-not-for-production+ JWT senzamaxAgeesplicito; da valutare headerx-build-idper rilevare mismatch client/server su deploy.
Estensione Chrome — Visibilità capacità degradate (capability gap) nei report (chiude capabilita-degradate-visibilita-report)
- Backend (
persistence.ts) —buildProvenance()ora ritornaEvaluationProvenanceanche per statusN/DquandocapabilityGapsnon è vuoto (in precedenza ritornavanullincondizionato per N/D). Questo permette di tracciare il motivo del degrado (es.real-hover,viewport-resizenon disponibili nell'estensione) fino al database. - Label map (
capability-labels.ts) — Nuova mappaCAPABILITY_LABELS: Record<DriverCapability, string>per tradurre le 5 capability (real-hover,viewport-resize,multi-navigation,cross-browser,screenshot) in italiano leggibile. - Confidence UI (
provenance.ts) — Nuovo tiercapability-degradatoindescribeConfidence()che appare solo quandoconfidence === null(status N/D) ecapabilityGapsè popolato. Badge "Capacità degradata" (warning), tooltip con label umane. - Dettaglio criterio (
DettaglioClient.tsx) — Badge "Capacità degradata" nella colonna trail con tooltip dedicato: "Capacità mancante: Hover reale non disponibile (estensione)... — l'esito N/D è un degrado, non una non-valutazione". - Triage (
triage/route.ts,TriageClient.tsx) — Query allargata per includere N/D conprovenance->capabilityGaps IS NOT NULL(ordinati persortOrder, non confidence). Badge identico a Dettaglio. - Export Excel (
excel/route.ts) — Colonna Note arricchita con nota"[Capacità degradata] Hover reale non disponibile (estensione); Ridimensionamento viewport non disponibile (estensione)"— nessuna nuova colonna per mantenere compatibilità foglio standard aziendale. - Test: 1542 test verdi, TypeScript clean, build Next.js riuscito.
- Documentazione: spec
2026-08-05-capabilita-degradate-visibilita-report-design.md(status → fatto), piano2026-08-05-capabilita-degradate-visibilita-report.md, riga decisione inMOC-Valutazione.md.
Estensione Chrome / Guida — Aggiornamenti per capability gap
- Pagina Estensione (
EstensioneClient.tsx) — Aggiornata la descrizione dei limiti: specificato chereflow(1.4.10) ehover-focus(1.4.13) degradano a N/D concapabilityGapesplicito, non "restituiscono N/D" generico. Aggiunto riferimento al badge "Capacità degradata" visibile in Dettaglio/Triage/Excel. - Guida Valutazione (
LimitazioniSection.tsx) — Paragrafo limiti estensione affinato: "entrambe restituiscono un esito esplicitamente degradato con capability gap" invece di "esito esplicitamente degradato". Nota che il gap è ora visibile in UI (badge Dettaglio/Triage) ed export Excel.
UI/UX — Home: Portfolio mostra ultimi 5 progetti con valutazione + Project nav deduplicata
- Home (
DashboardClient.tsx) — Il blocco "Portfolio" ora mostra solo gli ultimi 5 progetti che hanno ricevuto almeno un round di valutazione, ordinati per data dell'ultima valutazione (più recente prima). Progetti senza round restano accessibili da/progettima non affollano la home. - Project page (
ProgettoClient.tsx) — Rimosso il tab "Dashboard" dalla navigazione a pill: restano solo Riepilogo / Storico / Panoramica./progetti/[id]/dashboardrimane accessibile via URL diretto ma non linkato (contenuto duplicato di Panoramica). - API (
/api/dashboard) — AggiuntolastRoundDateal payload per supportare l'ordinamento.
Fix — Testo residuo non allineato dopo l'attivazione dei 19 agenti (estensione-agentic-19-agenti)
chrome-extension/popup.html: la checkbox "Esegui agenti comportamentali" dichiarava ancora(7 — form/auth)mentrepopup.jsinvoca giàvalidaRunAgenticProbes()senza argomenti (tutti e 19 gli agenti, dal Task 5 del piano v1.96.0) — testo fuorviante nell'unico punto dove l'auditor decide se abilitare la fase agentica. Corretto in(19).src/app/estensione/EstensioneClient.tsx: titolo pagina fermo a "Valida v1.4" (ilmanifest.jsonera già a 1.5.0 dal merge precedente) e step 4 delle istruzioni d'uso ancora "le 7 sonde form/autenticazione (WCAG 3.3.1, 3.3.3, 3.3.4, 3.3.7, 3.3.8, 2.2.1, 4.1.2)" — aggiornato a "tutti e 19 gli agenti" con link alla sezione "Fase agentica" della guida.src/app/help-valutazione/components/AutomaticoSection.tsx: refuso preesistente e scorrelato trovato durante la verifica — "otto pattern di interazione" per state-reveal a riga 145, maREGISTERED_AGENTS(src/lib/state-reveal/index.ts) ne registra 7 (modal, accordion, tab, dropdown, lazy-load, carousel, wizard), coerente con "sette pattern" già usato correttamente più sotto nello stesso file (riga 182, cookie banner escluso perché non è un pattern di reveal ma un dismiss one-off separato). Corretto a "sette".chrome-extension/manifest.json→ 1.5.1 (patch: comportamento osservabile invariato, solo testo).
Estensione Chrome — attivazione completa dei 19 agenti agentici (chiude estensione-agentic-19-agenti)
- Attivati tutti i 19 agenti agentici da estensione Chrome (prima solo 7, cluster form/auth) — derivazione automatica da
AGENT_REGISTRY, nessuna lista hardcoded residua né inpopup.jsné insrc/lib/agentic-engine/extension-support.ts. descriptionesportata dall'interfacciaAgent— fonte unica per il testo iniettato nel prompt Claude, non più copiato a mano nella whitelist server-side.EXTENSION_AGENTS/EXTENSION_AGENT_IDSderivati daAGENT_REGISTRY/ALL_AGENT_IDS—parseAgenticEvidence()continua a fare fail-closed validation, ma contro l'insieme reale di 19 agenti invece di una whitelist di 7 mantenuta a mano.browser-entry.tsriscritto con lookup table (BROWSER_AGENT_REGISTRY) al posto di 19 branchifconcriterionCoderipetuto — leggecriterionCodedirettamente dall'agente importato;validaRunAgenticProbes()ha ora default a tutti gli agenti supportati ed espone.AGENT_IDS.popup.jsnon mantiene piùAGENTIC_LOTTO1_IDS— chiamavalidaRunAgenticProbes()senza argomenti, usa il default esposto dal bundle generato.- Inclusi
reflow(1.4.10) ehover-focus(1.4.13) — degradano a N/D concapabilityGapesplicito quando eseguiti da estensione (capacità fisica non disponibile in un content script), mai un esito approssimato in silenzio. capabilityGapend-to-end fino aEvaluationProvenanceper il percorso estensione resta esplicitamente fuori scope — gap preesistente, tracciato per un topic futuro separato.
Fix
- Corretto testo fuorviante in
LimitazioniSection.tsx(/help-valutazione) che dichiarava la copertura completa dei 19 agenti prima che fosse effettivamente vera ("Sette tutti e 19..." → "tutti e 19").
Testing
- Test suite verde lungo tutti i 6 task di implementazione (description su Agent, AGENT_REGISTRY esportato, EXTENSION_AGENTS derivato, browser-entry.ts lookup table, popup.js senza lista hardcoded, testo LimitazioniSection + copertura test
extensionAgenticInterpret).
Spec: second-brain/superpowers/specs/2026-08-04-estensione-agentic-19-agenti-design.md. Piano: second-brain/superpowers/plans/2026-08-04-estensione-agentic-19-agenti.md.
Bugfix — Motore deterministico EN 301549 (4.2.x) + fix Best Practice (BP.*)
- Nuovo motore deterministico per i criteri EN 301549 Cap.4 (Functional Performance Statements, 4.2.1–4.2.8) in
src/lib/en301549-fps.ts— i criteri vengono ora derivati automaticamente aggregando (worst-case) gli esiti dei criteri WCAG 2.2 già valutati e mappati. Risolve il bug per cui i 4.2.x restavano bloccati aDa verificare/N/Dda settimane perché nessun motore automatico li processava (solo lo schema dati esisteva, Sezione 1 dello spec originale, ma la Sezione 2 "engine layer" non era mai stata scritta). Il mapping FPS→WCAG (8 righe, validato con l'utente) è tracciabile viasourceCodes[]nel risultato derivato. - Aggancio automatico in
applyEvaluations()(src/lib/evaluation-pipeline/persistence.ts) — la derivazione FPS è ora eseguita ad ogni scrittura di risultati WCAG, garantendo auto-consistenza indipendentemente da quale motore ha scritto per ultimo (statico, IBM, Claude, Gemini, estensione Chrome, coerenza cross-pagina). Sostituito il vecchio placeholder N/D piatto per gli EN-only che produceva sempreN/Dindipendentemente dallo stato WCAG reale. - Esclusione 4.2.x e BP. dal prompt Claude* (
src/lib/ai-engine/index.ts) — i criteri derivati e i Best Practice (derivati da IBM) non finiscono più nel prompt AI, riducendo di 13 criteri su 69 i token in input. - Esito "Superato" esplicito per Best Practice (BP.*) (
src/lib/ibm-engine/index.ts) — quando IBM non trova raccomandazioni per una categoria BP.* le cui regole sono note (mappate inIBM_RULE_TO_BP), ora genera un esitoSuperatocon nota[IBM Best Practice] Nessuna raccomandazione rilevatainvece di restare assente (che risultava in un N/D fuorviante via placeholder). Funzione purabuildBestPracticeFindings()estratta ed esportata per testabilità. - Script di backfill one-off (
scripts/backfill-en301549-fps.ts) — ricalcola i 4.2.x per tutti i 58 round esistenti con risultati WCAG applicabili: 463 criteri aggiornati, rispetta le correzioni manuali esistenti (stesso guardforcedella pipeline live). I BP.* toccati solo per round conbest_practices_enabled=1(0 a oggi, loggato esplicitamente). - Nessuna correzione manuale sovrascritta — il guard
forcepreserva eventuali override dell'esperto (status ≠ N/D/Da verificare). - Test: 8 test mapping/aggregazione (
en301549-fps.test.ts), 2 test integrazione (persistence-fps.test.ts), 4 test BP (ibm-engine-bp.test.ts), 1 test query filtrata (get-all-criteria.test.ts). Tutti i 63 test dei moduli toccati passano;tsc --noEmitpulito. - Documentazione: spec
2026-08-04-en301549-fps-engine-design.md(status → fatto), riga decisione inMOC-Valutazione.md, piano2026-08-04-en301549-fps-engine.md.
Spec: second-brain/superpowers/specs/2026-08-04-en301549-fps-engine-design.md. Piano: second-brain/superpowers/plans/2026-08-04-en301549-fps-engine.md.
UX — Filtro per stato nella vista di sola lettura "Dettaglio" (chiude filtro-stato-vista-dettaglio-readonly)
- Nuovo filtro per stato (Superato/Non superato/Da verificare/N/D/Tutti) nella vista "Dettaglio" di sola lettura (
/progetti/[id]/valutazioni/[roundId]/dettaglio/[componentId]) — estende la feature già presente nella vista di editing (ComponenteClient.tsx) anche alla pagina di consultazione per revisori/PM/RTD. - Componente condiviso
StatusFilterChips(src/components/evaluation/StatusFilterChips.tsx) — estratto dal markup esistente inComponenteClient.tsx, riusabile tra entrambe le viste con stessa accessibilità (role="group",aria-label,aria-pressed, toggle-off al riclick, annuncioaria-live="polite"). - Logica di filtro pura centralizzata in
src/lib/result-filter.ts(ResultFilter,filterResultsByStatus) — usata da entrambe le viste e dal componente condiviso, con test unitari dedicati (4+3 test). - Applicazione mirata: il filtro agisce solo sul tab "Criteri WCAG 2.2" (sostituisce la griglia di 6 stat-card statiche con la riga di chip); il tab "EN 301 549 esclusivi" (
En301549Panel) resta invariato, riceve sempreresultsnon filtrato. - Conformità (%) non interattiva — chip
score-chip--metain coda alla riga, mostra la percentuale calcolata sempre suresultsnon filtrato (non sul sottoinsieme filtrato), con tooltip esplicativo. - Chip "Tutti" = reset + conteggio — sostituisce la card "Criteri totali", click imposta filtro
'all'. - Stato vuoto + screen reader — messaggio "Nessun criterio con stato «X» — torna a «Tutti» per vedere l'elenco completo." + annuncio
sr-onlyconaria-live="polite". - Nessuna nuova CSS — riuso completo di
.score-chip,.score-chip--all/pass/fail/check/na/meta,.is-activegià inglobals.css. - Persistenza solo in-page (
useState), nessun URL/localStorage. - Test: 4 test per
filterResultsByStatus, 3 percountByStatus;tsc --noEmitpulito; suite completa 179/183 file passati (5 fallimenti pre-esistenti inrenderer-servicenon correlati). - Documentazione: spec
2026-08-03-filtro-stato-vista-dettaglio-readonly-design.md, piano2026-08-03-filtro-stato-vista-dettaglio-readonly.md, riga decisione inMOC-Valutazione.md.
Spec: second-brain/superpowers/specs/2026-08-03-filtro-stato-vista-dettaglio-readonly-design.md. Piano: second-brain/superpowers/plans/2026-08-03-filtro-stato-vista-dettaglio-readonly.md.
Architettura — PageDriver: migrazione completa di tutti i 19 agenti agentic-engine
- Interfaccia
PageDriverestesa con 9 nuovi metodi (getUrl,focus,type,fill,boundingBox,setViewportSize,screenshot,evaluateHandle,evaluateHandleOn,moveAway) + 2 nuoveDriverCapability(viewport-resize,screenshot) — mappatura completa per ogni metodo/agente documentata intypes.ts. - Split
collectEvidence()/run(): contratto condiviso (runViaCollectEvidence) per tutti i 19 agenti — centralizza try/catch +interpretWithClaude, mai più 19 blocchi quasi identici. - Migrazione completa 19 agenti su
PageDriver:aria-state(pilota, Piano 1),no-trap,skip-nav,label-in-name,on-focus-input,pause-stop-hide,focus-order,error-identification,accessible-auth,form-validation,keyboard,error-prevention,redundant-entry,timing-adjustable,resize-text,focus-visible,live-region,reflow,hover-focus. Ogni agente ha test di parità suPlaywrightDriver(Chromium live) eBrowserDriver(jsdom). - Capability gap system:
reflow-agent(1.4.10) degrada concapabilityGap=viewport-resizequando eseguito via BrowserDriver;hover-focus-agent(1.4.13) conreal-hover. Propagazione →EvaluationProvenance.capabilityGapsinbuildProvenance()+toEvaluationResult(). AgentContext.pagerimosso (campo deprecato dalla migrazione incrementale): tutti i 19 agenti e 23 file test aggiornati adriversolo.browser-entry.tscompleto: tutte le 19 sonde agenti registrate invalidaRunAgenticProbes, compilato vianpm run build:extension.
Architettura — PageDriver: chiusura Piano 2, migrazione state-reveal e rimozione porting manuali
- Migrazione completa 8 pattern
state-revealsuPageDriver:selector-path.ts/capture.ts(libreria condivisa) +accordion/dropdown/tab/modal/carousel/lazy-load/wizard-reveal-agent. Ogni pattern ha test di parità suPlaywrightDriver(Chromium live) eBrowserDriver(jsdom). - Screenshot capability-aware:
capture.tsusadriver.supports('screenshot')— sempreundefinedsuBrowserDriver(nessuna API di cattura schermo disponibile a un content script), mai un errore: l'HTML catturato resta valido comunque. state-reveal/browser-entry.ts(nuovo): esponewindow.validaRunStateReveal, compilato vianpm run build:extensioninsieme al bundle agentic già esistente.- Rimozione definitiva dei porting manuali ES5:
chrome-extension/agentic-probes.js(949 righe) echrome-extension/state-reveal-probes.js(657 righe) eliminati —popup.jsinietta ora sologenerated/agentic-probes.js/generated/state-reveal-probes.js.download/route.tssemplificato: rimosso il fallbackisGeneratedProbesComplete()introdotto dafix/extension-generated-probes-placeholder, non più necessario perché il bundle generato è ora sempre completo per costruzione. - Chiude definitivamente
codegen-sync-agentic-probes-estensionee l'intero pianoestensione-pagedriver-architettura(Piano 1 + Piano 2).
Testing
- 1547 test totali (fase agentic-engine): 1542 passati (5 fallimenti pre-esistenti non correlati in seed-data e JSDOM Tab nav). Nuovi test: page-driver (6+11), agentic-runner (3), 15 file di test agenti (~65 test di parità), capability-gap (5 test).
npm run build:extensionconfermato (bundle 9.6kb, nessun SDK Anthropic/Node trascinato). - 11 file / 63 test state-reveal (selector-path, capture, 7 agenti reveal, orchestratore
runStateReveal). Suite completa: 1496/1501 test passati (5 fallimenti pre-esistenti non correlati, verificati congit stash).npm run typecheckpulito,npm run build:extensiongenera entrambi i bundle (77.9kb + 22.3kb), nessun SDK Anthropic trascinato.
Documentazione — guida alla valutazione allineata al Piano 2
- Pagina
/estensione: numero agenti comportamentali aggiornato da 7 a 19, versione visualizzatav1.3→v1.4(coerente conchrome-extension/manifest.json). - Guida alla valutazione (
/help-valutazione) — sezione "Copertura di stati dinamici": rimosso il riferimento a "un'implementazione indipendente specifica per il content script" per i patternstate-reveal— ora dichiara esplicitamente lo stesso codice TypeScript condiviso viaPageDriver. - Sezione "Limitazioni" — card "Estensione Valida": rimosso il conteggio obsoleto "7 agenti / 12 residui (~6 portabili)", sostituito con "tutti e 19 gli agenti" e i due capability gap reali (
reflow→viewport-resize,hover-focus→real-hover) che degradano esplicitamente invece di essere assenti. - Backlog second-brain: aggiunta voce
rendered-context-collectors-porting-manuale-non-centralizzato(i 3 collector RenderedContext — focus styles/target size/text spacing — restano un porting manuale ES5 non centralizzato, mai stato nello scope del piano PageDriver); ripristinate 3 voci (pdf-ocg-bookmark-collassati,dashboard-osservabilita-sentry-sonarqube,sentry-blind-spot-processi-esterni) rimosse per errore da un commit non correlato del 2 agosto; chiuse 3 voci superate dal Piano 2 (estensione-agentico-lotto2,codegen-sync-agentic-probes-estensione,estensione-consolidare-persistenza-con-pipeline-principale).
Architettura — PageDriver: eliminata duplicazione comportamentale tra pipeline web ed estensione Chrome
- Interfaccia
PageDriverunificata con due implementazioni (PlaywrightDriverserver,BrowserDriverestensione) che sostituisce la duplicazione manuale ES5 (agentic-probes.js,state-reveal-probes.js) — stessa logica TypeScript compilata per entrambi i target via esbuild, niente più drift silenzioso tra pipeline web ed estensione. - Capability system per funzionalità non universalmente disponibili:
real-hover(movimento puntatore OS),multi-navigation(page.goto),cross-browser(Firefox/WebKit) — dichiarate esplicitamente con degradazione tracciata (capabilityGapinAgentResulteEvaluationProvenance), mai approssimazione silenziosa. - Safety contract ("mai submit reale") responsabilità strutturale di
BrowserDriver.runSession()— intercetta automaticamente eventosubmit+HTMLFormElement.prototype.submitper tutta la sessione agenti, zero rischio per sessioni autenticate reali. - Migrazione pilota
aria-state-agent(criterio 4.1.2) suPageDriver— stesso identico esito verificato su PlaywrightDriver (Chromium live) e BrowserDriver (jsdom), pattern validato per gli altri 18 agenti + 8 pattern state-reveal (lavoro successivo). - Build toolchain esbuild (
npm run build:extension) generachrome-extension/generated/agentic-probes.jsda un'unica entry point TypeScript — file generati mai editati a mano, check di freschezza inpack.shblocca build se sorgenti sono più recenti del bundle (chiudecodegen-sync-agentic-probes-estensione). - Persistenza estensione consolidata su
applyEvaluations(stessa pipeline web) — eliminaextension-persistence.tsduplicato e più povero, risolve bug noto:passing_evidencenon persistita (chiudeestensione-consolidare-persistenza-con-pipeline-principale),confidence/browserDivergence/capabilityGapsora arrivano automaticamente anche all'estensione. - ace.js versioning:
- Bug fix: costante
ACE_ENGINE_VERSIONhardcoded a4.0.24inpopup.js(server a~4.0.29) → ora letta da nuovo endpointGET /api/extension/ace-version(riusaaceEngineVersion()esistente). - Avviso staleness:
ace-version.jsonmarker scritto dasetup.she/api/extension/download, confrontato all'avvio del popup con versione server — avviso non bloccante se divergono. - Marker
ace-version.jsonincluso nello zip scaricato da/api/extension/download(tutti e 3 i code path: cache locale, CDN fetch, placeholder CDN unreachable).
- Bug fix: costante
- Schema: campo additivo
capabilityGaps?: string[]suEvaluationProvenance(jsonb, nessuna migrazione) — traccia capacità mancanti che hanno degradato un esito (es.real-hovervia estensione).
Affidabilità — capabilityGaps come nuovo segnale di qualità
- Campo
capabilityGapsuAgentResultecapabilityGapssuEvaluationProvenancetracciano esplicitamente quando un esito è stato degradato per capacità mancante nel contesto di esecuzione (es.real-hovervia estensione). - Nuova voce backlog registrata:
capabilita-degradate-visibilita-report— UI per visualizzare questi segnali (stesso punto dibrowserDivergence), deliberatamente fuori scope qui.
Refactor — Consolidamento persistenza estensione su pipeline principale
src/app/api/extension/submit/route.tsusa oraapplyEvaluations(stessa funzione della pipeline web Inngest) invece disaveEvaluationsduplicato e più povero.- Risolve bug noto:
passing_evidencenon persistita dall'estensione (ora scritta correttamente viaapplyEvaluations). - Miglioramenti futuri alla pipeline principale (confidence, placeholder N/D EN-only, batch upsert, accumulo
browserDivergence/capabilityGaps) arrivano automaticamente all'estensione. - File rimossi:
src/lib/extension/extension-persistence.tse test associato. - Bonus fix:
extensionAgenticInterpret.tsaggiornato allo stesso pattern.
Build — Prima toolchain per l'estensione Chrome
npm run build:extensioncompila via esbuild l'entry pointsrc/lib/agentic-engine/browser-entry.ts→chrome-extension/generated/agentic-probes.js(IIFE, browser platform).pack.shguadagna check di freschezza: fallisce segenerated/agentic-probes.jsè più vecchio dei sorgenti condivisi (src/lib/agentic-engine/,src/lib/page-driver/) — un porting dimenticato diventa errore di build bloccante, non drift silenzioso.esbuildpromosso a devDependency esplicita (era solo transitiva via Vitest)..gitignoreaggiornato conchrome-extension/generated/.
Fix — ace.js versioning
- Bug fix: costante
ACE_ENGINE_VERSION = '4.0.24'hardcoded inchrome-extension/popup.js(package.json a~4.0.29) — ogni submit dichiarava versione ACE sbagliata anche quandoace.jsscaricato era aggiornato. - Nuovo endpoint
GET /api/extension/ace-versionesponeaceEngineVersion()(già esistente insrc/lib/extension/ace-version.ts) con autenticazione estensione standard. popup.jsrisolve la versione all'avvio via fetch (fallback aundefined, mai blocca il popup).setup.she/api/extension/downloadscrivonoace-version.jsonmarker accanto adace.jsnello zip.checkAceVersionStaleness()inpopup.jsconfronta marker locale vs server all'avvio, mostra avviso non bloccante se divergono (class="feedback warn"coerente con design system esistente).
Testing
- 1482 test passati (suite completa), 5 fallimenti pre-esistenti in
renderer-service/non correlati. - Nuovi test:
page-driver(6+5 test),aria-state-agent(3 test parità driver),ace-version/route.test.ts(3 test),download/route.test.ts(nuovo testace-version.jsonin zip). - Fix round su test batch-upsert (
route.test.ts,revealed-states.test.ts): indici batch a blocchi da 12 invece di indici fissi; mock wrapping inrevealed-states.test.tsper la veraapplyEvaluations. - Fix CSS: classe
.warninginesistente →.feedback.warn(esistente in design system). tsc --noEmitpulito, nessuna regressione oltre i 5 fallimenti pre-esistentirenderer-service/(dipendenzaexpressmancante, non correlato).
Fix — Raccomandazioni IBM Equal Access senza dettaglio + badge mancante sulla nota estensione (segnalazione esperti)
- Le "Raccomandazioni" IBM Equal Access mostravano solo il messaggio grezzo (es.
"1) Heading element has no descriptive content"), senza rule ID, riga sorgente o snippet HTML — a differenza del blocco "Problemi rilevati", che li include sempre.recLinesinevaluateWithIbm(e il suo omologomapIbmResultsnel path estensione Chrome) ora include[ruleId]+ snippet, stesso stile già usato perproblemLinesebestPracticeFindings. - Un criterio con solo finding di livello
RECOMMENDATION(nessuna violazione/potenziale) restava senza alcun dettaglio consultabile: le note citavano un conteggio ("49 problemi rilevati") ma il campoproblemseranull, quindi il blocco "Problemi rilevati" spariva del tutto dalla UI (DettaglioClient.tsxlo renderizza solo se non vuoto). Aggiunto un fallback: quando violazioni+potenziali sono entrambi vuoti,problemLinesusa le raccomandazioni come dettaglio — le raccomandazioni pure restano comunque nel camporecommendationscome prima, senza duplicazione nel caso comune (violazioni/potenziali presenti). - Le note IBM originate dal path estensione Chrome (prefisso
[IBM ACE – Browser]) non ricevevano alcun badge colorato informatNotes/formatters.tsx, a differenza di Claude/Gemini/Agentico/Tecniche WCAG/IBM (path server) — cadevano nel fallback "nessun badge". Aggiunta una entry dedicata inSOURCE_BADGE_LABELSper il prefisso[IBM ACE …]. - Root cause verificata su dati reali del DB locale (stessa pipeline) prima del fix, e sul motore IBM live dopo il fix (stessa pagina reale, stesso criterio) — non solo su unit test.
- Test: 1 nuovo test per il badge
[IBM ACE – Browser]informatters-notes.test.ts. Suite esistente (84 test suibm-engine,extension/submit/route.test.ts,formatters-notes.test.ts) verde,tsc --noEmitpulito. Riguarda solo i dati generati dalle prossime valutazioni IBM — le righe già in DB restano col vecchio formato finché non vengono ri-eseguite.
Affidabilità — Divergenza cross-browser come segnale di confidence (chiude divergenza-cross-browser-confidence)
- **Nuovo segnale di affidabilità: la divergenza reale tra browser reali (chromium/firefox/webkit) abbassa la confidence a 0.4 e genera un badge dedicato "Browser discordanti" nei pannelli Dettaglio/Triage — non un'altra nota testuale persa nel rumore, ma un cap numerico identico a quello già usato per l'arbitraggio AI (Claude vs Gemini), con lo stesso pattern di persistenza cross-fase.
- Merge deterministico (
mergeBrowserEvaluations) normalizza le chiavi browser/viewport (normalizeBrowserKey, modulo condiviso) e calcolabrowserDivergencesolo tra browser reali distinti — niente falsi positivi in scan combinati multi-browser + responsive. - Confidence cap 0.4 in
computeConfidence(stesso diarbitrated), nessuna doppia penalità se un criterio è sia arbitrato sia browser-divergente. Accumulo cross-fase viaaccumulateBrowserDivergence(union con provenance già persistita) — sopravvive al passaggio fase tecnica → Claude/Gemini sia nella pipeline Inngest async sia nel percorso sincrono dev/fallback. Fix mirato inmergeAiResultsPreservingRevealedStatesper il percorso sincrono (merge AI sostituiva l'intero oggetto perdendo il campo), e fix inline intechnical-phase.tsper preservare il campo attraverso i merge IBM/best-practice. - UI dedicata:
describeConfidenceguadagna tierbrowser-divergentecon priorità suarbitrato(stesso valore 0.4, label diversa). Badge "Browser discordanti (40%)" con tooltip elenco browser inDettaglioClienteTriageClient. Ordinamento coda triage per confidence crescente mette i criteri divergenti in cima. - Persistenza additiva:
EvaluationProvenance.browserDivergence?: string[]suvalutazione_results(jsonb, nessuna migrazione). Campo omonimo suCriterionEvaluationin memoria — nessun remapping tra livelli. - Test regressione: 7 test per
mergeBrowserEvaluations(incluso false-positive combined mode), 6 peraccumulateBrowserDivergence, 3 perdescribeConfidencetier, 2 per fix sync path inmergeAiResultsPreservingRevealedStates, plus IBM merge preservation regression. Suite completa 179 file / 1481 test,tsc --noEmitpulito. - MOC: riga decisione in
MOC-Attendibilita-Knowledge-Base.md. Seed Ipotizzato ripulito (divergenza-cross-browser-confidencerimosso). Spec/piano statusfatto.
Spec: second-brain/superpowers/specs/2026-07-31-divergenza-cross-browser-confidence-design.md. Piano: second-brain/superpowers/plans/2026-07-31-divergenza-cross-browser-confidence.md.
Sicurezza — Rimossi token live da .claude/settings.json, chiude secrets-live-esposti-claude-settings-json
- Token API live (Render, 2× SonarQube, 1× TestSprite) incorporati in chiaro in 41 regole di permesso Bash di
.claude/settings.json(tracciato in git, non gitignored — visibile a chiunque avesse accesso in lettura al repo). Finiti lì perché sessioni precedenti hanno salvato il comando esatto eseguito (token incluso) come regola di permesso, invece di un pattern generico. Trovato verificando il gate gitleaks introdotto il 2026-07-23 (bug, priorità alta). - Render: token revocato (non serve più — migrato a Hostinger,
hostinger-migration-fase1). Rimosse 12 regole Bash conAuthorization: Bearerin chiaro, 11 regolemcp__render__*, 4 regole WebFetch/curl residue, e il server MCPrenderda~/.claude.json. - SonarQube: token ruotato su dashboard locale, nuovo valore solo in
.env.local(gitignored). 27 regole con token in chiaro rimosse — sostituite da una singola regola a match esattoBash(./sonar-scan.sh)(lo script legge giàSONAR_TOKENda env var). - TestSprite: chiave API ruotata — scoperta e inclusa nello scope solo durante questa sessione (non menzionata nella nota di backlog originale). 2 regole con chiave in chiaro rimosse, registrazione MCP locale aggiornata.
- Finding di una review di sicurezza post-commit (non pre-esistente — introdotto e corretto nella stessa sessione): la generalizzazione inizialmente scelta per le regole SonarQube usava un wildcard a metà stringa (
Bash(SONAR_TOKEN=* ./sonar-scan.sh)) — un wildcard che non è l'ultimo carattere del pattern matcha l'intera stringa successiva, permettendo di eseguire comandi concatenati arbitrari dopo il prefisso. Stessa classe di escape già corretta in questo repo il 2026-07-23 (gitleaks version *,python3 *rimosso interamente). Corretto con match esatto e rimozione secca delle query curl senza generalizzazione sicura possibile. - Nessuna riscrittura della history git (decisione esplicita): i valori storici restano leggibili nei commit passati ma sono inerti dopo revoca/rotazione.
.gitleaks-baseline.jsonrigenerato — le entry per l'HEAD attuale spariscono per assenza di match; restano 14 entry per 2 commit storici di.claude/settings.json(history non riscritta, coerente con la decisione sopra).- Promemoria anti-ricorrenza aggiunto a
CLAUDE.md(sezione Convenzioni di sviluppo): regole di permesso Bash con segreti inline vanno generalizzate omettendo il segreto (mai con un wildcard a metà stringa) o rimosse se nessuna generalizzazione sicura è possibile.
Spec: second-brain/superpowers/specs/2026-07-31-secrets-live-esposti-claude-settings-json-design.md. Piano: second-brain/superpowers/plans/2026-07-31-secrets-live-esposti-claude-settings-json.md.
Hardening — Guardrail di coerenza technique-map.ts ↔ codice dei check + 3 bugfix
TECHNIQUE_MAP(wcag-techniques) poteva disallinearsi silenziosamente dal codice che emette davvero i risultati, senza alcun segnale automatico (hardening): terza volta in una settimana che questa classe di bug viene trovata solo per audit manuale (dopo H58_PARTS/G161/G83_PROX il 24 luglio e F44/F85 il 25 luglio). Aggiunto un guardrail a 3 invarianti intechnique-map-coherence.ts— nessuntechniqueIdemesso assente dalla mappa (invariante A), iltypedichiarato deve comparire tra itypeeffettivamente emessi per quell'id (invariante B — non "ogni emissione deve coincidere", formula verificata contro ~20 pattern legittimi già in produzione), ogni tecnicastatic/rendereddichiarata è emessa oppure allowlistata con motivazione (invariante C) — più una guardia inversa che rileva quando un'entry allowlist diventa obsoleta. L'estrazione è statica (regex sui sorgentichecks/), non richiede eseguire i check con fixture.technique-map-gap-allowlist.tsdocumenta le 87 coppie (criterio, tecnica) dichiarate ma non emesse, classificate come coperte da un altro motore (14), duplicato dichiarativo con una variante già implementata (28), o gap reale non implementato in questo lavoro (45).- 3 bug reali trovati e corretti durante l'audit: dead code
checkFocusAppearance/collectFocusAppearance(C45/F110b, calcolava un risultato sempre scartato, residuo della rimozione del criterio AAA 2.4.13 del 27 luglio);F103_FORM(errore form senza live region, 3.3.1) mai dichiarato inTECHNIQUE_MAPpur essendo già emesso dacheckARIA22_F103— risultato scartato silenziosamente;H44_PH(placeholder come unica etichetta di un campo, 3.3.2) dichiaratofailurema emesso sempresufficient— un campo con solo placeholder non faceva mai scattare "Non superato", falso negativo silenzioso in produzione, quarta istanza della stessa classe di bug dopo H58_PARTS/G161/G83_PROX. - Un finding della review finale whole-branch, corretto prima del merge:
findTypeMismatchescostruiva una mappa keyed sutechniqueIdche, in presenza di due dichiarazioni dello stesso id contypediverso (es. sotto criteri diversi), manteneva silenziosamente solo l'ultima — esattamente la classe di bug che il guardrail esiste per intercettare. Nessun id nel repository attiva oggi questo caso, ma la funzione ora itera su ogni dichiarazione anziché deduplicare per id.
Verifica: suite completa 1446/1448 test verdi (i 2 falliti sono un bug pre-esistente e indipendente in scripts/lib/seed-ipotizzato.test.mjs, non correlato a questo lavoro — confermato identico su main prima del merge), tsc --noEmit ed eslint puliti. Sviluppato via subagent-driven-development (7 task + review finale whole-branch con 1 fix round). Chiude wcag-techniques-consistenza-guardrail-technique-map-vs-codice. Spec second-brain/superpowers/specs/2026-07-29-wcag-techniques-consistenza-guardrail-design.md, piano second-brain/superpowers/plans/2026-07-29-wcag-techniques-consistenza-guardrail.md.
Hardening — Istrumentazione Sentry per renderer-service (chiude parzialmente il blind spot sui processi esterni)
renderer-service(worker Playwright su Hostinger) senza alcuna osservabilità Sentry, unico processo esterno del sistema a non riportare errori (hardening): un crash Firefox-specific in v1.87.4/v1.87.5 era passato inosservato per circa un mese perché i fallimenti parziali del rendering multi-browser (arrayfailuresdiPromise.allSettled) finivano solo inconsole.warn, mai come errore tracciato. Aggiunta@sentry/node@10.64.0con DSN dedicato (SENTRY_DSN_RENDERER, separato da quello dell'app principale) e auto-instrumentation Express: 10catchespliciti su/render,/ibm-scan,/agentic-run,/capture-element-snippets,/capture-dynamic-state-snippets,/pdf-remediation/reading-order,agid-export.ts,snippets.ts,pdf-remediate.ts; piùcaptureMessagea livellowarningper i fallimenti parziali multi-browser precedentemente visibili solo via log.- Due deviazioni dal design emerse in implementazione: il campo dell'integrazione HTTP per sopprimere il body si chiama
maxIncomingRequestBodySizenella versione installata (nonmaxRequestBodySizecome da doc Sentry di riferimento); i test HTTP-level richiedono una guardiaif (process.env.VITEST !== 'true')attorno adapp.listen()inserver.tsper evitareEADDRINUSEquando più suite importano./servernello stesso processo Vitest. - Residuo esplicito, non chiudibile da codice: creazione manuale del progetto Sentry dedicato (DSN, alert rule) sul dashboard — precondizione per lo smoke test end-to-end reale. Chiude solo la parte
renderer-servicedel blind spot: estensione Chrome e sidecar veraPDF restano fuori scope.
Verifica: tsc --noEmit pulito su entrambi i tsconfig (app + renderer-service), eslint 0 errori, suite completa 171 file / 1418 test verdi, build Docker pulita, /health confermato healthy, log puliti senza SENTRY_DSN_RENDERER configurata. Sviluppato via subagent-driven-development (9 task + review finale whole-branch). Spec second-brain/superpowers/specs/2026-07-28-sentry-renderer-service-instrumentation-design.md.
Evolutiva — Fase 2 migrazione Hostinger: motore Agentic e snippet stati dinamici delegati al renderer-service
- Motore Agentic (19 agenti Playwright) inutilizzabile in produzione dopo lo slimming dell'immagine
valida-app(evolutiva): lanciava sempre Chromium in-process, mai raggiungibile sul VPS Hostinger senza binari Playwright — degradava a N/D per ogni round conagenticEnabled=true. Estratto versorenderer-servicecon nuovo endpointPOST /agentic-run(stesso pattern "delega HTTP quandoWORKER_URLè settato" già usato da IBM engine/PDF-remediation/reading-order/AGID-export): il worker esegue lancio browser + N agenti + chiusura in un'unica chiamata, screenshot ritornati come base64 grezzi (nessuna credenziale storage lato worker) e caricati su MinIO/R2 lato app. Avvolto inwithHeavyOpLock('agentic', ...), sia il percorso worker sia il fallback locale, per rispettare il guardrail "una sola operazione pesante alla volta" del VPS. captureDynamicStateSnippets(snippet focus/hover per Gemini Vision, criteri 2.4.7/1.4.13/2.5.5) con lo stesso gap, mai delegata (evolutiva): sorella dicaptureElementSnippetsnello stesso file, che aveva già ricevuto la delega in un fix round precedente — degradava già onestamente (array vuoti + log distinto), ma non funzionava mai in produzione. Estratta con lo stesso pattern versorenderer-service— nuovo endpointPOST /capture-dynamic-state-snippets, avvolta inwithHeavyOpLock('capture-dynamic-state-snippets', ...)su entrambi i rami (delega e fallback locale). La review finale whole-branch ha trovato che le due funzioni ora condividono lo stesso lock ma venivano lanciate concorrentemente nello stessoPromise.allda due chiamanti (technical-phase.ts,visual-phase.ts) — corretto sequenziandole nei chiamanti, così la seconda non rischia più di attendere l'interoMAX_WAIT_MS(120s) e degradare di nuovo a risultato vuoto.
Verifica: suite completa 168 file / 1405 test verdi, tsc --noEmit pulito (app + renderer-service). Spec second-brain/superpowers/specs/2026-07-08-hostinger-migration-design.md §12/§13, piani 2026-07-28-agentic-engine-estrazione-worker-fase2.md e 2026-07-28-capture-dynamic-state-snippets-estrazione-worker-fase2.md.
Refactor + Fix — 2 voci di backlog chiuse: criteri AAA sprecati in technique-map, checkLanguageOfParts esteso ai form
technique-map.tsmappava 3 criteri di livello AAA mai valutabili (refactor): 2.4.9 (Scopo del link, solo link), 2.4.12 (Focus non oscurato, avanzato) e 2.4.13 (Aspetto del focus) sono fuori dai 56 criteri A/AA ufficiali (data/wcag22-criteria.json) —runTechniqueEnginecalcolava comunque inizializzazione/aggregazione/instradamento verso AI per questi 3 codici ad ogni valutazione web, risultati mai persistiti (nessunidcorrispondente inwcag_criteria). Verificato prima del fix che nessun consumer reale dipende dai 3 codici, poi rimosse le entry daTECHNIQUE_MAP. Aggiunto un test guardrail che confrontaTECHNIQUE_MAPcon l'elenco ufficiale in entrambe le direzioni, per prevenire un futuro disallineamento. Scoperta laterale fuori scope: 2.4.12 dichiarava la tecnicaC43_AA, mai emessa da alcun check (soloC43) — non riceveva mai un risultato reale nemmeno prima di questo fix, terza istanza della stessa classe di bug già tracciata in backlog.checkLanguageOfParts(3.1.2) non coprivaoption/selectné altri controlli di form (fix): il selettore per il testo straniero senzalangera limitato ap, li, td, th, dd, blockquote, span, div— un blocco in script non-Latino dentro un<option>,<button>,<label>o<legend>non veniva rilevato, anche se identico per struttura a un<p>/<div>già coperto. Esteso il selettore ai 5 tag. Il gap più difficile — parole inglesi in alfabeto latino dentro contenuto italiano (il caso realmente segnalato: "All" in una<option>popolata via AJAX) — resta un limite accettato del checker statico, non implementato: richiederebbe un dizionario/allowlist per non generare falsi positivi su prestiti legittimi (email, PDF, login). Guida/help-valutazioneaggiornata di conseguenza.
Verifica: TDD per entrambi i fix (test RED→GREEN prima dell'implementazione), suite completa 1331/1331 test verdi, tsc --noEmit pulito. Spec dedicati in second-brain/superpowers/specs/2026-07-27-technique-map-criteri-aaa-calcolo-sprecato-design.md e second-brain/superpowers/specs/2026-07-27-checker-lingua-parti-non-copre-select-option-e-inglese-latino-design.md.
Fix — 2 tecniche WCAG senza ramo di fallimento (F69, F101)
- F69 (1.4.4, Ridimensionamento del testo) non veniva mai eseguita fuori dalla pipeline con rendering (
fix):checkViewportZoomaveva già un ramofailfunzionante ma viveva inrendered-checks.tspur non usando mairenderedContext(legge solo<meta name="viewport">) — nell'harness statico (es. golden dataset) non girava mai, nonostantetechnique-map.tsla dichiarasse correttamentemode: 'static'. Spostata inchecks/static/structure.ts, registrata inrunAllStaticChecks. - F101 (2.5.2, Annullamento del puntatore) restava sempre "Da verificare", mai "Non superato" (
fix):checkF101rilevava azioni sumousedown/pointerdown/touchstartma produceva solooutcome: 'warning', nonostante il criterio fosse dichiaratotype: 'failure'. Aggiunto un pattern ad alta confidenza (submit/rimozione/navigazione/checkout eseguiti sul down-event invece che sull'up-event) che promuove il risultato afail; gli handler ambigui restanowarningcome prima. Il pattern è stato rivisto durante la review finale per eliminare 5 falsi positivi reali su codice legittimo (classList.remove(), la parola "delete" dentro una stringa qualsiasi,checkout/purchasecome substring di un identificatore più lungo). - 9 tecniche esaminate e chiuse come "nessun intervento": F99 (2.1.4), F103 (4.1.3), H96 (1.2.1), C22 (1.4.5), C8/C21 (1.4.12), G19 (2.3.1), G84 (3.3.3), H100_A (3.3.8) — per ciascuna la scelta di non aggiungere un ramo
failè motivata caso per caso (assenza di una tecnica "sufficient" non è mai un fallimento WCAG di per sé, oppure il criterio richiede intrinsecamente un motore diverso dal solo HTML statico). Dettaglio insecond-brain/superpowers/specs/2026-07-25-wcag-techniques-check-senza-esito-fallimento-design.md§2. - Bugfix collaterale —
mergeByTopic()perdeva la nota descrittiva dello spec quando il file di piano associato non ne aveva una propria: la sovrascrittura "il piano vince" (voluta per lo status) si applicava per errore anche anote, azzerando la colonna Note dell'indice generato per il topic appena chiuso. Aggiunto un fallback dedicato, con test di regressione.
Verifica: suite completa 152 file / 1323 test verdi, tsc --noEmit pulito, eslint pulito. Sviluppato in worktree isolato con subagent-driven-development: 3 task con review per-task (Task 3 con 2 round di fix), review finale sull'intero branch con 1 fix wave sulla regex F101 (approvata dall'utente prima di procedere). Guida /help-valutazione aggiornata con i 2 limiti espliciti (F99, F103).
Feature — 7 tecniche WCAG dichiarate ma mai implementate + bugfix id orfano F44
- 7 criteri con una tecnica dichiarata in
technique-map.tssenza alcuna funzione implementante nel codice (feat): 1.2.3 (G78), 1.2.5 (G78_V), 1.3.2 (G57), 1.4.1 (F81), 1.4.10 (C32), 2.4.3 (F44), 3.3.7 (G221) — una violazione di questi criteri non intercettata da altri motori passava silenziosamente. Implementate tutte e 7: G78/G78_V condividono un'unica funzione (checkG78_AudioDescriptionTrack,track kind="descriptions"su video); C32 rileva@medianel CSS incorporato; G57 rileva la proprietà CSSordercome proxy di riordino visivo; F81 rileva campi obbligatori indicati solo dal colore (unica altra tecnicafailureoltre F44); G221 è un'euristica proxy per l'inserimento ridondante (mai oltrewarning/cannot-tell, limite strutturale dichiarato — la vera verifica richiede confronto tra step di un form multi-pagina, non possibile da un singolo snapshot statico). - Bugfix — 2.4.3/F44 era orfano, non assente (
fix):checkTabindexPositive(interactions.ts) implementava già la logica di F44 ma taggava il risultato con id'F85', mai dichiarato intechnique-map.ts—buildTechniqueToCriteriaMap()lo scartava silenziosamente, 2.4.3 non riceveva mai questo segnale nonostante il codice funzionante esistesse. Rinominato l'id. - Correzioni emerse in review durante l'implementazione: G57, la regex di rilevamento
ordermatchava falsamente dentroborder:(proprietà CSS comunissima) — corretta con ancoraggio a word boundary. F81, il rilevamento CSS solo-colore inizialmente non correlava la regola alla specifica coppia campo↔selettore (una classe.requiredsu un elemento non correlato poteva salvare un campo genuinamente privo di indicatore) e non ignorava i commenti CSS — corretti; dopo 3 round della stessa classe di bug su un'estensione della ricerca per prossimità DOM, la generalizzazione a testo di genitore/fratelli è stata rimossa: F81 riconosce ora solo label/aria-label/aria-describedby, limite noto e documentato in/help-valutazione. - Gap di piano trovato nella review finale sull'intero branch: 4 delle 7 tecniche (G78/G78_V/G57/G221) aggiungono un
passche rimuove il criterio dalla coda di verifica AI. G221 (3.3.7, nessuna tecnica rendered/ai con cui interferire) corretto acannot-tellcon confidence 0.5, coerente con il proprio limite già dichiarato; G57 lasciato invariato per non regredire il caso comune in cui il rendering (sempre disponibile nella pipeline reale) verifica già correttamente 1.3.2.
Verifica: suite completa 1287/1287 test verdi (nessuna regressione, F1 golden-dataset invariato a 0.7654), tsc --noEmit pulito, eslint pulito. Sviluppato in worktree isolato con subagent-driven-development: 7 task con review per-task, poi review finale sull'intero branch con 3 round di fix (ciascuno confermato con l'utente prima di procedere).
Fix — navigazione tra criteri: la pagina non si riposizionava sull'accordion appena aperto
- Aprendo un nuovo criterio nell'accordion di valutazione, la vista non si riposizionava sulla nuova intestazione e il focus da tastiera andava perso (
fix): nessuna gestione di scroll/focus esisteva inListResultsView.tsx— l'intestazione appena aperta poteva finire coperta dalla TopBar sticky (64px) o restare fuori dal viewport, causando salti di layout e perdita di contesto durante la valutazione sequenziale dei criteri. Segnalato dai colleghi durante l'uso reale (osservazioni-utenti-2026-07-20.md§1). - Fix:
scroll-padding-top: 4rem+scroll-behavior: smoothsuhtml(globals.css) — stessa tecnica già raccomandata dal motore di remediation del progetto per il criterio 2.4.11 (operable.ts, tecnica WCAG C43), applicata qui alla propria interfaccia. InListResultsView.tsx, unuseEffectsul criterio espanso richiamafocus({ preventScroll: true })escrollIntoView({ block: 'start' })sul bottone.crit-rowcorrispondente.scroll-behavior: smoothè già neutralizzato perprefers-reduced-motionda una regola preesistente.
Verifica: nessuna infrastruttura di test component-level nel progetto (vitest gira in environment: node) — verificato dal vivo con script Playwright ad-hoc contro il container Docker ricostruito: intestazione riposizionata esattamente sotto la TopBar (~64px), focus conservato sul bottone appena aperto, zero errori console, comportamento istantaneo con prefers-reduced-motion. 1261/1261 test verdi, tsc --noEmit pulito.
Fix — 6 bug UI segnalati (widget flottante, dialog, formattazione, technical note, pulsante morto)
- Widget "Assistente AI" copriva e intercettava i click su contenuto reale (
fix):.cbot-launcher(position: fixed,z-index: 50) non aveva safe-area riservata — nascondeva sia l'ultima riga del portfolio in Home sia i link del footer (Changelog/Cookie Policy). Alzatobottomdel launcher (24→80px) epadding-bottomdel<main>(16→24,layout.tsx). - Modale "Elimina progetto" illeggibile (
fix): la regoladialog { background: transparent; border: none; padding: 0 }inglobals.cssera fuori da ogni@layer— per la CSS Cascade Layers spec le regole non-layered vincono sempre su quelle layered, quindi sovrastava le utility Tailwind (bg-white,border,p-6) applicate allo stesso<dialog>. Spostata in@layer base(pattern standard Tailwind v4 per estendere Preflight). - Grassetti resi a blocco in "Come eseguire un audit" (estensione) (
fix):display:flexsu un<li>con testo inline misto a<strong>trasformava ogni run di testo e ogni<strong>in flex-item separati, rompendo il flusso di lettura. Avvolto il testo di ogni voce in uno<span>aggiuntivo — solo numero e blocco testo restano flex-item. - Technical note del criterio 2.4.2 (divergenza cross-browser) troncata (
fix):extractJson()(json-parser.ts) e la logica di parsing inDettaglioClient.tsxusavano una regex non-greedy che si fermava al primo fenceinvece dell'ultimo — quando `technicalNote` conteneva un blocco di codice d'esempio annidato (html...), il JSON risultava troncato/non parsabile e il contenuto oltre il blocco annidato andava perso. Sostituita con `lastIndexOf('')` (regex greedy scartata: SonarQube segnalava rischio di backtracking super-lineare). Aggiunto test di regressione. - Rimosso
.home-fabin Home (fix): pulsante "Apri strumenti rapidi" senza alcunonClickda quando è stato introdotto (255ef55, 2026-06-03) — mai più toccato, nessuna spec lo documenta, rimasto invisibile finché il widget AI lo copriva (sopra). Un controllo visibile che non fa nulla al click è peggiore di uno assente, specialmente per screen reader. - "All" in inglese tra le option di una select popolata via AJAX: non un bug lato Valida (nessuna option hardcoded nel codice). Documentato invece un gap reale del checker WCAG 3.1.2 "Lingua delle parti" (
checkLanguageOfParts, non scansionaoption/select, non rileva parole inglesi in alfabeto latino) nella guida/help-valutazione(sezione Limitazioni) e in backlog — nessuna euristica introdotta per evitare falsi positivi affrettati su prestiti linguistici legittimi.
Verifica: bug su widget/dialog/formattazione verificati con Playwright sulla CSS compilata reale dell'app (nessun login necessario). 1229/1229 test verdi, tsc --noEmit pulito.
Fix — canvas/pdfjs-dist, allowlist Bash, screenshot Gemini Vision su dialog reali
- Rendering PDF→immagine falliva con
"Image or Canvas expected"(fix):pdfjs-distsenza unCanvasFactoryesplicito crea le canvas ausiliarie per gli inline image XObject con@napi-rs/canvas, incompatibile col pacchettocanvasusato per la canvas principale. Aggiunto unNodeCanvasFactorycustom inrender-pages.tsche usacanvasovunque — confermato con test di integrazione reale su un PDF con immagini. Chiudecanvas-pdfjs-rendering-pdf-immagine-rotto, bug preesistente già segnalato in v1.87.0. - Allowlist Bash di
.claude/settings.local.jsontroppo permissiva (fix): i pattern con wildcard finale ("gitleaks version *","python3 *") matchano l'intera stringa di comando dopo il prefisso, non solo gli argomenti — un comando concatenato con;/&&/backtick sarebbe stato auto-approvato senza prompt. Ristrettogitleaks version *a match esatto, rimosso interamentepython3 *. - Gemini Vision non vedeva mai un dialog reale aperto di default al load (
fix):hideDistractingOverlays()(estratta da un blocco inline inrenderer/index.ts) nascondeva qualunque overlayfixed/stickya piena copertura schermo o con keywordmodal|popupnell'attributo prima dello screenshot passato a Gemini Vision — colpendo anche un dialog semanticamente reale (role="dialog"/aria-modal="true"), non solo cookie banner cosmetici. Ora esclude gli elementi con semantica dialog, stessa definizione già usata damodal-reveal-agent.ts. Chiudegemini-vision-nasconde-modali-attivamente.
Verifica: 1224/1224 test verdi, tsc --noEmit pulito. Ciascun fix accompagnato dal proprio test di regressione (verificato fallire contro il codice pre-fix).
Feature — gate CI per secret committati per errore + fix di robustezza state-reveal
- Nuovo job
secrets-scanin CI (.github/workflows/ci.yml): gitleaks via Docker (non l'Action ufficiale, licenza a pagamento su repo privati oltre la prova gratuita) sull'intera history del repo..gitleaks-baseline.jsonsopprime i 32 finding già noti al momento dell'introduzione del gate (generato con--redact: nessun valore reale in chiaro, solo fingerprint/metadati) — un secret nuovo, su qualunque file/riga/commit non già in baseline, fa comunque fallire il job. AggiuntoSECURITY.mdcon policy di disclosure minima. - Scoperto durante la verifica:
.claude/settings.json(tracciato) conteneva ancora un token Render e 2 token SonarQube live, incorporati in regole di permesso Bash salvate da sessioni precedenti. Rotazione rimandata su richiesta esplicita — tracciata come voce di backlog priorità alta invece di sparire silenziosamente nella baseline. - Fix
H2(Tecniche WCAG) forzava sempre'pass'su 1.1.1 anche in pagine senza alcun link con immagine, mascherando<img>senzaalt: ora ritorna'not-applicable'in quel caso, coerente col pattern già usato da H24/H46/ARIA6_SVG. - Fix
dropdown-reveal-agent(server): mancava il controllo di visibilità post-click già presente nei gemelli modal/accordion e nell'estensione Chrome — un dropdown che non si apriva davvero per un bug JS della pagina sotto test veniva comunque catturato come "rivelato". - Fix lazy-load (estensione + server): il marker transiente usato per il tracking scroll finiva nell'
outerHTMLcatturato invece di essere rimosso prima; aggiunto anche un log esplicito quando si raggiunge il capMAX_LAZY_SCROLLSsenza aver trovato il fondo reale della pagina (stesso pattern già in uso perMAX_IBM_RESCANS). - Dependabot npm settimanale (
.github/dependabot.yml) — chiude il gap di dependency-scanning (48 vulnerabilità npm audit note, 1 critical).
Verifica: 1218/1218 test verdi, tsc --noEmit pulito. Ciascun fix accompagnato dal proprio test di regressione (verificato fallire contro il codice pre-fix).
Fix — dialog nativi non centrati, documentazione Piano 3
- Modale nativa (
showModal()) renderizzata in alto a sinistra invece che centrata (es. conferma eliminazione progetto inProgettiClient.tsx): Tailwind v4 Preflight azzeramarginsu tutti gli elementi, incluso<dialog>, sovrascrivendo ilmargin: autonativo dei browser che centra i dialog aperti conshowModal(). Fix root-cause insrc/app/globals.css(regoladialog {}esistente) — corregge in un colpo solo tutti i dialog nativishowModal()-based dell'app. - Documentazione aggiornata per Piano 3 (v1.91.0): la guida alla valutazione (
/help-valutazione, sezioni Automatico e Limitazioni) e la pagina/estensioneora descrivono la copertura di stati dinamici disponibile anche dall'estensione Chrome (checkbox "Rivela stati dinamici").
Verifica: fix del dialog isolato e verificato via riproduzione live (Playwright + getComputedStyle) in una sessione precedente; nessun dialog dell'app imposta un margin in conflitto (verificato via grep). Solo modifiche CSS/documentazione — nessun impatto sulla logica applicativa.
Feature — Copertura di stati dinamici, Piano 3: porting estensione Chrome
- Completa gli 8 pattern dello spec originale su entrambe le architetture (Piano 1/2 li avevano portati solo lato pipeline web): i 7
RevealAgent(modale, accordion, tab, dropdown, carousel, wizard, lazy-load) + il dismiss cookie-banner sono ora portati anche nell'estensione Chrome "Valida", in un nuovochrome-extension/state-reveal-probes.js— controlled-duplication port (stesso pattern collaudato diagentic-probes.js: ES5 vanilla, nessun import/export, iniettato in MAIN world), non un riuso diretto del codice server-side (Playwright-based, non eseguibile in un content script del browser reale dell'auditor). - Divergenza architetturale migliorativa: mentre la pipeline Playwright rigioca IBM Equal Access su un secondo
page.goto()indipendente seguendo una recipe di interazione (da cui il follow-up notostate-reveal-selettori-non-univoci-replay-ibm, un selettore bare-tag può matchare l'elemento sbagliato in ri-navigazione), l'estensione rivaluta IBM sul posto, nello stesso DOM appena mutato, senza mai ri-navigare — il bug non può manifestarsi in questo porting per costruzione. Stesso principio "riferimento DOM live, mai un selettore ricostruito" già applicato atab-reveal-agent.tslato server, qui generalizzato a tutti e 7 i pattern. - Nuova checkbox opt-out "Rivela stati dinamici" in
popup.html(checked di default, stessa convenzione della checkbox agentica esistente) — l'utente può disattivarla, ma la funzionalità gira di default; iniezione in MAIN world sia diace.js(istanza dedicata, distinta da quella ISOLATED usata per l'audit statico) sia distate-reveal-probes.js. POST /api/extension/submitesteso per valutarerevealedStates[]: ogni stato inviato dall'estensione viene rivalutato con gli stessi motori della baseline (tecnica statica + motore statico + gli issue IBM già scansionati client-side + Claude seevalModelo prevede), fuso con la baseline tramitecollapseByWorstStatus/mergeAiResultsPreservingRevealedStates(estratte in un nuovo modulo leggeromerge-revealed-state.ts, senza le dipendenze pesanti ditechnical-phase.ts) — un "Superato" da uno stato rivelato non maschera mai una violazione nota della baseline per lo stesso criterio.- Fix di visibilità post-click sul dropdown applicato SOLO in questo porting: a differenza del gemello server (
dropdown-reveal-agent.ts, che non ha ancora questo controllo — gap tracciato separatamente), l'estensione verifica che il click abbia davvero reso visibile il menu prima di catturarlo come "rivelato". - Quattro bug reali trovati e corretti durante l'implementazione, presenti verbatim nel codice fornito dal piano stesso (non deviazioni dell'implementatore): (1) il carousel accettava
<a>come trigger "avanti" senza alcuna guardia di navigazione, in violazione del vincolo "mai una seconda navigazione" — corretto con un controllowindow.location.hrefprima/dopo ogni click, che preserva gli stati già catturati nei container precedenti; (2) il matching testo del wizard concatenavatextContent||aria-label||valuecon OR prima del test regex, perdendo i pulsanti solo-icona conaria-label— corretto testando le tre fonti indipendentemente; (3) la marcatura "già visto" del lazy-load copriva solo l'elemento di frontiera, non i discendenti — contenuto annidato veniva ri-catturato come "nuovo" a ogni livello, corretto marcando l'intero sottoalbero; (4) nel submit route,evaluateHtml()e l'output non filtrato del motore tecniche applicati a frammenti HTML nudi producevano falsi "Non superato" garantiti per criteri page-level (lingua pagina, titolo, skip-link, orientamento, navigazione/identificazione coerente) che avrebbero mascherato un "Superato" corretto della baseline su quasi ogni submit reale — corretto allineando al comportamento già corretto dievaluateRevealedStates()lato pipeline web (rimossoevaluateHtmlsui frammenti, aggiunto lo stesso filtro di esclusione page-level). - Verifica end-to-end live con estensione Chrome non pacchettizzata caricata in un browser reale via Playwright, dev server connesso ai servizi Docker in esecuzione, fixture con tutti i 7 pattern:
revealedStatesCountcorretto (7 con la checkbox attiva, 0 disattivata), nessuna navigazione né submit reale durante l'intero audit, persistenza confermata con query diretta al database (provenance.revealedStateintatto invalutazione_results).
Verifica: 12 task via subagent-driven-development in un worktree isolato, ognuno con review dedicata + fix dove necessario (4 bug plan-mandated corretti su segnalazione, sempre con conferma esplicita prima del fix). 1204/1204 test verdi (oltre ai 2 fallimenti pre-esistenti non correlati) · tsc --noEmit pulito. Chiude lo spec [[2026-07-19-copertura-stati-dinamici-oltre-load-design]] a fatto — tutti gli 8 pattern coperti su entrambe le architetture (Piano 1+2 lato pipeline web, Piano 3 lato estensione Chrome). Piano: second-brain/superpowers/plans/2026-07-21-copertura-stati-dinamici-oltre-load-piano-3.md (topic copertura-stati-dinamici-oltre-load-piano-3).
Fix — estensione Chrome: seconda pagina impossibile su URL invariata (wizard/SPA multi-step)
- Un esperto segnalava che l'estensione non riusciva ad aggiungere una seconda pagina al round quando la URL era identica a una già inviata — caso reale: wizard di reset password a 6 step la cui URL resta invariata su tutti gli step. Causa:
POST /api/extension/submitfaceva match del componente solo su(roundId, url)— trovando la URL già presente, riusava sempre il primo componente fondendoci dentro i nuovi risultati, ignorando anche un nome distinto digitato nel campo "Nome componente" (mai usato per rinominare un esistente — no clobber). - Fix a basso attrito: nuovo campo opt-in
saveAsNewPagenel payload di submit + nuova checkbox nel popup "Salva come pagina/stato distinto" — default invariato (zero click in più sul caso comune di ri-audit sulla stessa pagina), da attivare solo quando la stessa URL rappresenta uno stato logico diverso da tracciare separatamente. - Verificato che la piattaforma web non era affetta: l'aggiunta manuale pagina e la copia pagine tra round non fanno mai questo matching per URL.
Verifica: 2 nuovi test in route.test.ts (TDD), suite completa 1143/1145 verde (i 2 falliti sono il seed pianificazione pre-esistente, non correlato — confermato via git stash), tsc --noEmit pulito. Bump estensione 1.1.4 → 1.1.5.
Documentazione — audit interno /a11y di help-valutazione
- La pagina che spiega come Valida valuta l'accessibilità dei siti terzi presenta essa stessa violazioni WCAG 2.2 AA: audit su richiesta (skill
/a11y) dei 22 file sorgente disrc/app/help-valutazione/, in parte verificato live con Playwright controvalida-app.xyz. 1 CRITICA (il TOC non sposta mai il focus da tastiera dopo la navigazione), 4 HIGH (contrasto insufficiente su più badge,role="listbox"senza il comportamento tastiera del pattern, regioni scrollabili irraggiungibili da tastiera, smooth-scroll che ignoraprefers-reduced-motion), 4 MEDIUM, 3 LOW. - Nessun fix applicato in questo rilascio: solo tracciamento. Dettaglio completo in
second-brain/superpowers/specs/2026-07-21-help-valutazione-a11y-audit.md(status: ipotizzato), in attesa di pianificazione.
Verifica: nessuna modifica al codice applicativo in questo rilascio — solo documentazione e bump di versione.
Feature — Copertura di stati dinamici, Piano 2: tab, dropdown, carousel, wizard, lazy-load, cookie banner
- Completa la copertura degli 8 pattern di interazione previsti dallo spec originale (v1.88.0 ne copriva 2 di 8): il meccanismo
state-revealintrodotto per modali e accordion viene esteso a tab non selezionati, dropdown/menu chiusi, carousel/slider, wizard multi-step e contenuto caricato al lazy-load/scroll, più un cookie banner accettato automaticamente una sola volta prima di qualunque cattura (non unRevealAgentcome gli altri sette — sblocca l'intera pagina per tutti i motori, non solo per lo screenshot Gemini). - Estensione infrastrutturale per pattern sequenziali: carousel, wizard e lazy-load non si raggiungono con un solo click come modali/accordion/tab/dropdown, ma con una sequenza di passi (N click "avanti", N scroll) —
RevealedStateguadagnainteractionPath(la sequenza cumulativa da rigiocare) eaction: 'click' | 'scroll', e IBM Equal Access rigioca l'intero percorso sul proprio caricamento pagina indipendente, non solo l'ultimo passo. - Carousel e wizard non ripristinano la pagina al termine (a differenza di modali/accordion/tab/dropdown/lazy-load): sono stati sequenziali, non alternativi — tornare indietro vanificherebbe lo scopo di attraversarli. Per questo sono registrati per ultimi nell'ordine di attivazione degli agenti, cosicché ogni altro meccanismo di scoperta trovi ancora la pagina intatta.
- Guardie di sicurezza sul wizard: un passo "avanti" non viene mai attivato dentro un form classificato finanziario/legale/di modifica dati sensibili (stessa classificazione già usata per il criterio 3.3.4), né se il testo del pulsante suggerisce invio/pagamento/conferma; se un click causa una navigazione reale della pagina, l'esplorazione si interrompe immediatamente senza catturare nulla dalla nuova pagina — mai un secondo caricamento indipendente.
- Tre bug reali trovati e corretti durante l'implementazione: (1) l'agente accordion (già in produzione da v1.88.0) poteva intercettare anche i trigger dropdown per selettore troppo generico — corretto escludendo esplicitamente
aria-haspopup; (2) il ripristino della selezione originale nei tab si basava su un selettore CSS ricostruito, che falliva silenziosamente su tab senza id o con id da neutralizzare — corretto catturando un riferimento diretto all'elemento; (3) contenuto lazy-load aggiunto direttamente come figlio diretto della pagina (non dentro un contenitore dedicato) veniva scartato silenziosamente dal rilevamento — corretto includendo la pagina stessa nel confronto.
Verifica: 12 task via subagent-driven-development in un worktree isolato, ognuno con review dedicata + fix dove necessario, più review finale whole-branch (4 correzioni: guardia wizard resa "fail-closed" su errore invece di procedere per default, rimosso il tag <a> dai candidati del cookie banner per evitare una navigazione reale silenziosa su un falso positivo, due precisazioni di documentazione). Tre limiti noti tracciati come backlog dedicato per un giro successivo, non bloccanti: un selettore di fallback non sempre univoco nel replay IBM (eredità di v1.88.0, non solo di questo rilascio), un controllo di visibilità mancante sul dropdown, un test di regressione che non copre lo specifico caso corretto sui tab. Suite 133/133 file, 1143/1143 test verdi su main dopo il merge · tsc --noEmit pulito. Piano: second-brain/superpowers/plans/2026-07-20-copertura-stati-dinamici-oltre-load-piano-2.md (topic copertura-stati-dinamici-oltre-load-piano-2).
Feature — Verifica round-trip dei suggerimenti di remediation (sinergia con il golden dataset)
- Nessuna garanzia che un suggerimento di correzione risolva davvero la violazione secondo i motori di Valida (
feature): la libreria curata di remediation (23 correzioni, riferimenti a tecniche W3C) offrivacodeExamples.goodcorretti secondo la documentazione ufficiale, ma nessuno verificava se i motori automatici li riconoscessero davvero come "Superato". Nuovo moduloverifyLibraryRoundtrip()— riusarunEngines()del golden dataset senza duplicarne la logica — esegue ogni esempio conforme sui due motori statici e lo considera verificato se almeno uno dei due lo riconosce Superato: non serve l'unanimità, i due motori hanno coperture complementari. - Esposto nella Scheda di affidabilità esistente: nuovo campo
remediationRoundtripinGET /api/knowledge/reliability, nuova sezione nel pannelloKnowledge Base → Insightscon badge Verificato/Non verificato/N-D per ogni voce — stesso endpoint, stesso pannello, nessuna nuova route. - Scope v1: solo criteri verificabili staticamente. I criteri che richiedono stili calcolati (contrasto 1.4.3/1.4.11, focus non ostruito 2.4.11, dimensione target 2.5.8, focus visibile 2.4.7) sono dichiarati esplicitamente non verificabili con questo motore (badge N/D), non marcati come suggerimenti errati.
- Due bug reali scoperti nei motori stessi, non nella libreria di remediation: la tecnica
H2di Tecniche WCAG ("Link con testo e immagine") ritorna sempre "pass" anche quando la pagina non contiene alcun link con immagine — combinato con la tecnica di fallimento F65 ("alt mancante") dichiarata ma mai implementata, un<img>senzaaltviene marcato erroneamente "Superato" dal solo motore Tecniche WCAG per il criterio 1.1.1 (il motore statico principale lo rileva correttamente). Non corretto in questo rilascio — tracciato come voce di backlog dedicata, priorità alta.
Verifica: 4 task via subagent-driven-development (1 con round di fix sul design — ripristinato il controllo su entrambi i motori dopo un tentativo di scorciatoia su un solo motore — 1 con node_modules ritracciato per errore, corretto in review) + fix da review finale whole-branch (2.4.7 spostato tra i criteri non verificabili staticamente). Suite 127/127 file, 1118/1118 test verdi · tsc --noEmit pulito. Piano: second-brain/superpowers/plans/2026-07-20-remediation-roundtrip-verification.md (topic remediation-roundtrip-verification).
Fix — 4 bug da QA live su un round di valutazione
- Titolo pagina troncato a metà parola nella nota del criterio 2.4.2 (
fix):buildTechniqueSummary()tagliava ogni messaggio di tecnica a 80 caratteri senza alcun indicatore di troncamento — per i messaggi "Superato" che riportano il titolo pagina (H25_Q) era l'unico punto in cui il testo compariva (nessuna passing evidence salvata per quel caso), quindi il taglio produceva una perdita reale di informazione. Cap portato a 200 caratteri, con…quando il testo viene effettivamente troncato. - Criteri EN 301549 esclusivi (4.2.x) sempre "N/D", su tutti i progetti, non solo nel round segnalato (
fix): le righe erano seminate correttamente nel DB e la vista Wizard le includeva già, maprincipleOrdernella vista Lista — quella di default per tutti tranne i delegate — elencava solo i 4 principi WCAG POUR; i criteri con principioEN301549finivano in un gruppo calcolato ma mai iterato in fase di render, quindi mai visibili né editabili in nessun progetto. Aggiunta la sezione dedicata "EN 301 549 — Criteri esclusivi" alla vista Lista, verificata in browser con form completo ed editabile. - Il validator W3C riportava quasi sempre "riga 0" per il criterio 4.1.1 (
fix): il Nu Html Checker valorizzafirstLine/firstColumnsolo per i messaggi che coprono un range multi-riga; i messaggi puntuali (la maggioranza) hanno sololastLine/lastColumn, e il codice ripiegava direttamente su 0 quandofirstLinemancava. Ora il fallback usalastLine/lastColumnprima di 0. - Enumerazione "(1) ... (2) ... (3) ..." nelle note (es. criterio 2.1.1) non riconosciuta come lista (
fix): il parser delle note riconosceva solo lo stile "1) ..." (parentesi finale), non "(1) ..." (parentesi completa) — pattern usato da note generate da Claude quando spiega quali evidenze mancano per una valutazione definitiva. Aggiunto un riconoscimento dedicato, attivo solo su una sequenza stretta 1,2,3… per non scambiare riferimenti come "(vedi 2.4.3)" per un elenco.
Verifica: suite 126/126 file, 1107/1107 test verdi (test-first su tutti e 4 i fix) · tsc --noEmit pulito · next build pulito · fix EN 301549 verificato anche live in browser (Playwright, dopo rebuild immagine Docker).
Feature — Copertura di stati dinamici oltre il caricamento iniziale (modali, accordion)
- Ogni motore valutava la pagina solo così com'era al primo caricamento (
feature): un contenuto rivelato solo dopo un'interazione (una modale che si apre al click, un pannello accordion chiuso) non veniva mai raggiunto — una violazione reale al suo interno risultava "Superato" per assenza di verifica, non perché davvero conforme. Nato rispondendo a un dubbio degli esperti ("i risultati includono anche le modali normalmente nascoste al caricamento?"), poi generalizzato: l'indagine ha trovato lo stesso gap strutturale su 7 pattern di interazione oltre le modali (tab, accordion, dropdown, carousel, wizard, lazy-load, cookie banner). - Nuovo meccanismo
state-reveal: durante il rendering Playwright, la pagina viene esplorata per due pattern pilota — modali nascoste al caricamento (role="dialog"/aria-modal="true") e pannelli accordion chiusi (aria-expanded="false"). Ogni trigger trovato viene attivato nella stessa sessione di pagina già usata per il rendering (mai un secondo fetch/goto indipendente), il contenuto rivelato viene valutato da IBM Equal Access (rigioca il click prima dello scan) e dal motore Tecniche WCAG (modalità frammento, con esclusione dei criteri page-level — lingua pagina, titolo pagina, bypass blocks, orientamento, coerenza cross-pagina — che genererebbero falsi positivi su un sottoalbero isolato), poi la pagina viene ripristinata prima di proseguire. Ogni esito riporta inprovenance.revealedStatequale trigger l'ha rivelato (estensione del campoprovenancejsonb già esistente, nessuna migration). - Limiti noti e accettati in questa prima iterazione: altri 6 pattern di interazione (tab, dropdown/menu, carousel, wizard multi-step, lazy-load/scroll, cookie banner) restano fuori scope, in roadmap per un piano successivo. L'analisi Claude AI e Gemini Vision sul contenuto rivelato funziona solo nel percorso di valutazione sincrono (
/api/evaluate, dev/fallback) — nella pipeline asincrona di produzione (Inngest) il contenuto rivelato riceve oggi solo IBM + Tecniche WCAG, non ancora l'analisi AI, perché quella pipeline separa i tre step in file diversi (ai-phase.ts,visual-phase.ts) non ancora collegati a questo meccanismo. QuandoRENDERER_URLè impostato (rendering delegato al microservizio remoto, usato in Docker/produzione), lo state-reveal non gira sul percorso remoto. - Tre bug reali trovati e corretti durante l'implementazione (nessuno presente nel design originale): (1) Claude poteva scattare durante lo step tecnico asincrono di Inngest, pensato esplicitamente senza AI — corretto con gating esplicito su fase ed
evalMode; (2) un bug di merge silenzioso poteva far vincere un esito "Superato" proveniente da uno stato rivelato su un "Non superato" di baseline per lo stesso criterio, mascherando esattamente il tipo di falso negativo che questa feature esiste per eliminare — corretto con un merge che preserva sempre lo stato peggiore tra quelli in conflitto; (3) l'agente di rilevamento modali poteva aprire pannelli accordion (selettore trigger troppo ampio) e abbandonarli aperti senza ripristino, contaminando la cattura della pagina di base — corretto verificando la semantica del target prima del click, non dopo.
Verifica: 11 task via subagent-driven-development, ognuno con review dedicata + fix dove necessario, più review finale whole-branch (2 problemi Critical/Important trovati e risolti sulle interazioni tra task). Suite 124/124 file, 1100/1100 test verdi su main dopo il merge · tsc pulito.
Fix — Causa reale del crash Firefox nella matrice multi-browser: $HOME inesistente per l'utente non-root
- Il rendering Firefox andava in timeout dopo 180s su ogni round multi-browser, in ogni viewport (
fix): la visibilità aggiunta in 1.87.4 ha mostrato per la prima volta l'errore reale —browserType.launch: Timeout 180000ms exceeded, con stderr pieno diunable to create directory '/home/nextjs/.cache/dconf': Permission deniedeUnable to revert mtime: .../firefox/fonts. Causa: nelDockerfilel'utentenextjsviene creato conuseradd --systemsenza--create-home—/home/nextjsnon esiste mai sul filesystem pur essendo il valore di$HOMEnella passwd entry, e/homeèroot:rootmode 755, quindinextjsnon può crearla nemmeno al volo. Chromium e WebKit tollerano un$HOMEnon scrivibile; Firefox scrive cache GTK/dconf e stato di profilo sotto$HOMEall'avvio e resta bloccato a ritentare finché Playwright non va in timeout — per questo Firefox risultava assente da ogni scan (v1.87.4) mentre Chromium/WebKit funzionavano regolarmente. - Fix: aggiunto
--create-homeallauseraddnelDockerfile. Riprodotto e verificato isolatamente: stesso hang esatto ricreando l'immagine base Playwright +useraddidentico senza--create-home(identico stderr, identico path/ms-playwright/firefox-*/firefox); con--create-homeil lancio riesce in ~250ms invece di andare in timeout.
Verifica: rebuild locale immagine runner completata · lancio Firefox isolato (repro Docker minimale) passa prima/dopo il fix.
Fix — Divergenza cross-browser: nota grezza duplicata nel pannello Note + Firefox mai visibile nella matrice multi-browser
- Il blocco
[Analisi Divergenza Cross-Browser]torna a comparire nel pannello "Note" generico, JSON grezzo compreso (fix): il 26/6 (b04571b)formatNotes()era stata corretta per non mostrare più questo blocco nel pannello "Note" — resta comunque visibile, formattato, nel pannello dedicato "AI Divergence Expert". Due giorni dopo (027de78) quella correzione era stata revertita per errore insieme a un fix di tipografia non collegato ("matching the pre-change behaviour"), senza che nessun test se ne accorgesse —formatNotes()non aveva copertura. Ripristinato lo strip e aggiunto un test di regressione che riproduce esattamente il caso reale (fallback quando il parsing JSON di Claude fallisce): un bullet per ogni riga del JSON grezzo nel pannello Note, duplicato rispetto al pannello già stilizzato sotto. - Firefox risultava assente da ogni scan multi-browser/responsive in produzione, in silenzio, da almeno un mese (
fix): verificato sul Postgres di produzione chebrowser_resultsnon contiene mai la chiavefirefoxin nessun round campionato dal 25/6 a oggi, nonostante sia configurato. Causa:renderPageMulti()usaPromise.allSettlede scarta le combinazioni browser/viewport il cui rendering fallisce, senza loggare nulla (commento nel codice: "silently omitted") — riducendo silenziosamente la matrice cross-browser a solo Chromium+WebKit.renderPageMulti()(sia nel percorso inline sia nel microserviziorenderer-service) ora ritorna anche l'elenco dei fallimenti con il motivo del rigetto;technical-phase.tsli registra come warning inexecution_logs. Non è ancora una correzione della causa ultima del crash di Firefox — finché il fallimento era invisibile non c'era un errore reale da diagnosticare; questa modifica serve a farlo emergere al prossimo round multi-browser, per poi intervenire sulla causa vera.
Verifica: tsc pulito (app + renderer-service) · suite 1052/1052 · lint pulito sui file toccati.
Fix — Estensione Chrome: 413 persistente su pagine molto ripetitive + Passing Evidence mai salvata
- Il filtro dei
PASS(v1.87.2) non bastava sulle pagine con moltissime violazioni reali (fix): su un listato lunghissimo (~13.000 risultati IBM ACE, quasi tutti violazioni vere, nonPASS) l'estensione continuava a sfondare i 5 MB — il solo arrayibmResultspesava 10+ MB. Causa: la stessa regola può fallire su migliaia di elementi quasi identici (es. ogni card di un catalogo), e inviarne ogni istanza è puro spreco.popup.jsora raggruppa i risultati per regola+esito e ne invia al massimo 15 campioni per gruppo, riportando il conteggio vero a parte (ibmRuleCounts) così stato, priorità e note dei criteri restano accurati (non più basati sul campione troncato). Aggiunto anche un log diagnostico in Console che scompone il payload per byte, per non dover reinterpretare a mano il tab Payload di DevTools a ogni pagina che fallisce. Bump estensione 1.1.2 → 1.1.4. - Le prove di conformità (Passing Evidence) non venivano mai salvate per i criteri "Superato" via estensione (
fix): il motore statico calcola l'evidenza per ogni criterio superato (elementi verificati, metodo), già persistita correttamente dalla pipeline web — ma il percorso di salvataggio dell'estensione non includeva affatto quella colonna nella query di scrittura, scartando silenziosamente un dato già calcolato in memoria. Riproducibile su qualunque criterio "Superato" via estensione (osservato su 1.3.2 e 1.3.5), non specifico a un criterio. I dati già salvati prima di questo fix restano senza evidenza: serve rifare l'audit da estensione sulla pagina interessata per ripopolarli. - Rinominata anche la cartella wrapper dello ZIP scaricabile (
a11y-snapshot/→valida-extension/, filevalida-extension.zip), residuo del brand precedente.
Verifica: tsc pulito · suite 1045/1045.
Fix — Estensione Chrome: 413 "Payload troppo grande" su pagine PA voluminose
popup.jsinviava anche i risultatiPASSdi IBM ACE (fix): su pagine ricche di elementi (portali PA media-rich) l'estensione andava in 413 nonostante il limite di 5 MB fosse stato dimensionato apposta per farle entrare comodamente (v1.86 §9g). Causa:runPageAudit()inviava l'intero reportace.js, incluse le entryPASS— scartate comunque server-side damapIbmResults(stesso pattern del motore IBM lato server). Sulle pagine con molti elementi iPASSsono la stragrande maggioranza del report: puro peso morto nel payload.- Fix: i
PASSvengono ora filtrati client-side prima dell'invio; il conteggio originale è inviato a parte (ibmResultsTotal) così il messaggio "N regole verificate" resta accurato. Il limite dei 5 MB non è stato toccato — il bug era nel payload sprecato, non nella soglia. - Bump versione estensione
manifest.json1.1.1 → 1.1.2.
Verifica: tsc pulito · suite 1041/1041.
Fix — Estensione Chrome: icone disallineate dal brand e ZIP di download incompleto
- Icone ferme a un rebrand precedente (
fix): l'icona della toolbar (manifest.json→icons/icon16/48/128.png) e il logo dell'header del popup (valida-icon.svg) erano rimasti al design del 2026-05-10, mai aggiornati dopo l'ultimo rebrand del 2026-06-20 (unico usato dal resto del prodotto,public/favicon.svg).generate-icons.jsdisegnava inoltre una "A" placeholder scollegata da qualunque SVG reale — ogni futura rigenerazione avrebbe ripetuto la stessa deriva. Oravalida-icon.svgè allineato al design ufficiale egenerate-icons.jsrasterizza da quel file viasharp; rimossoicons/icon.svg, un terzo design orfano del 2026-03-24 mai referenziato. GET /api/extension/downloaddistribuiva un pacchetto incompleto (fix): la whitelist di file inclusi nello ZIP non contenevavalida-icon.svg(icona rotta nell'header del popup per chi installa l'estensione via download) nérendered-context-collectors.jseagentic-probes.js(iniettati dapopup.jsviachrome.scripting.executeScript, quindi funzionalmente assenti dal pacchetto scaricato). Aggiunti tutti e tre, con test di regressione sul contenuto dello ZIP.- Bump versione estensione
manifest.json1.1.0 → 1.1.1.
Verifica: tsc pulito · suite 1036/1036.
PDF — arricchimento vision delle proposte di remediation (§9-ter.3.1)
- Analisi visiva Gemini per gli alt text (
feat):POST /api/pdf-remediationora renderizza fino a 10 pagine del PDF (PDF_VISION_MAX_PAGES) e le invia a Gemini Vision insieme al testo estratto, per correggere gli alt text proposti "alla cieca" da Claude con testi basati sul contenuto visivo reale delle figure. Claude (proposte testuali di metadati/heading/lingua) resta invariato — riusa il pattern già collaudato inreading-order/route.tse nel motore webgemini-engine. - Rilievi visivi informativi (
feat): contrasto insufficiente e tabelle con struttura complessa rilevati visivamente vengono mostrati come note in sola lettura nello strumento di remediation (visualFindings) — non applicabili automaticamente via pikepdf, richiedono intervento manuale sul documento sorgente. - Degradazione automatica (
feat): se la chiave Gemini non è configurata o la chiamata vision fallisce, il flusso prosegue esattamente come prima (solo proposte Claude testuali) — nessun impatto sul comportamento esistente.
Nota operativa: la verifica end-to-end reale è risultata bloccata da un bug preesistente non introdotto da questo lavoro — il rendering PDF→immagine (canvas+pdfjs-dist, usato anche dall'endpoint reading-order già in produzione) fallisce con "Image or Canvas expected" in questo ambiente, riprodotto identico su main pre-esistente. Da investigare separatamente.
Verifica: tsc pulito · suite 1031/1031 · spec e piano in docs/superpowers/{specs,plans}/2026-07-18-vision-pdf-remediation-*.
Estensione Chrome — chiavi API per-valutatore (§9-quater P7)
- Chiavi personali per-valutatore (
feat): la chiave API statica condivisa dell'estensione Chrome (app_settings.extension_api_key) è sostituita da chiavi personali hashate SHA-256 (nuova tabellaextension_api_keys), una attiva per utente — rigenerare revoca automaticamente la precedente. Self-service da/estensione("La mia chiave API"), pannello admin separato per elenco/revoca di tutti gli utenti (/api/extension-keys/admin*, non un unico endpoint con branching di ruolo, per sicurezza strutturale).validateExtensionApiKeypassa da confronto a tempo costante su un segreto condiviso a lookup per hash. - Attribuzione multi-valutatore (
feat):components.submitted_byviene popolato ad ogni submit da estensione con l'utente proprietario della chiave usata, aggiornato a ogni invio successivo (non è un campo no-clobber) — abilita l'attribuzione dei componenti negli audit condotti da più valutatori. - Cutover (
fix): rimossa la UI della vecchia chiave condivisa da Impostazioni → Generale; nessuna migrazione dati necessaria, chi ha ancora la vecchia chiave configurata deve generarne una personale da/estensione. - Accessibilità: la nuova UI (self-service + tabella admin) è stata verificata con la skill
/a11y— corretti focus management dopo generazione/revoca chiave, annunci screen reader per copia riuscita, stato di elaborazione (aria-busy) sui pulsanti.
Verifica: tsc pulito · suite 1003/1003 · next build ok. Spec e piano in docs/superpowers/{specs,plans}/2026-07-16-estensione-chiavi-api-per-valutatore-*.
Attendibilità — confidence e provenienza per esito (§3.1)
- Confidence e provenienza persistite (
feat): nuovi campiconfidence(0–1) eprovenance(JSON: motori coinvolti, arbitrato, automatico) suvalutazione_results, calcolati inapplyEvaluationsdalla policy di merge già esistente (stessa formula della confidence KB:0.5 + 0.1 × motori concordi, cap 1.0; arbitrato → 0.4; override manuale → 1.0). Badge fascia/percentuale inDettaglioClient.tsx(Alta/Media/Bassa fiducia, Arbitrato, Verificato manualmente), fallback identico al comportamento precedente per le righe storiche pre-migrazione — nessuna migrazione distruttiva, nessun backfill. - Fix di un gap reale: l'arbitraggio Claude/Gemini non girava mai in produzione (
fix): la funzionearbitrate()(Claude vs Gemini → "Da verificare" quando in disaccordo) veniva eseguita solo nel percorso sincrono di sviluppo/fallback; la pipeline Inngest asincrona reale (usata in produzione) non confrontava mai i due modelli — ogni fase sovrascriveva la precedente in sequenza, senza mai produrre il badge "arbitrato" introdotto in v1.84.0.visual-phase.tsora legge lo stato già persistito prima di applicare i fallimenti di Gemini Vision e arbitra il conflitto (arbitrateAgainstPersisted()), colmando il gap nella direzione "era Superato, Gemini dice fallito". - Coda di triage (
feat): nuova tab "Triage" nel round di valutazione (GET /api/results/triage) — elenco piatto pagina×criterio ordinato per confidence crescente, per portare in cima gli esiti meno affidabili o arbitrati e prioritizzare la revisione esperta.
Trovati e corretti in corso d'opera: un helper SQL esportato per errore da un file route.ts di Next.js rompeva silenziosamente npm run build (non intercettato da tsc/vitest, solo dalla build stessa) — spostato nel modulo di persistenza (persistence.ts); il badge dell'override manuale mostrava "Alta fiducia (100%)" invece di "Verificato manualmente".
Residuo aperto (non un difetto, fedele alla formula scelta): nella pipeline asincrona ogni fase calcola la confidence guardando solo i motori della propria chiamata, quindi un criterio toccato dall'IA può fermarsi a una confidence più bassa di quanto meriterebbe anche quando i motori tecnici erano concordi, mentre il percorso sincrono di sviluppo (lista motori fusa) converge più vicino a 1.0 per lo stesso criterio — loggato come voce "Ipotizzato" confidence-accumulo-cross-fase per un giro successivo.
Verifica: tsc pulito · suite 970/970 · next build ok. Spec e piano in docs/superpowers/{specs,plans}/2026-07-16-confidence-provenance-per-esito-*.
Fix — valutazione PDF falliva per documenti su MinIO
handlePdfEvaluationnon seguiva la migrazione a MinIO (fix): dopo la migrazione dello storage PDF a MinIO (v1.x, prefixpdfs/),src/lib/evaluation-pipeline/pdf-evaluation.tsera l'unico punto rimasto a leggerecomponent.filePathconfs.existsSync/fs.readFileSync— un controllo sul filesystem locale che fallisce sempre per una chiave MinIO. Ogni valutazione PDF innescata dalla pipeline Inngest per un documento caricato dopo la migrazione terminava conError: File PDF non trovato: pdfs/...e falliva senza retry (404 è tra i codici non ritentabili), segnalato in produzione da Sentry (JAVASCRIPT-NEXTJS-3). Sostituito conresolvePdfBuffer()(già usato da tutte le routepdf-remediation/), che gestisce sia le chiavi MinIO sia i path legacy. Nuovi test inpdf-evaluation.test.tscoprono sia il caso di risoluzione riuscita sia il 404 quando la chiave non risolve e non c'è unurldi fallback.
Verifica: tsc pulito · suite 944/944 · eslint 0 errori.
Onda quick-win — confidence badge, igiene KB, fix WCAG/contrasto (§16)
- Badge provenance: distingui esiti arbitrati (
feat):detectArbitration()inprovenance.tslegge il marker[Arbitraggio AIgià scritto innotesquando Claude e Gemini divergono (technical-phase.ts) ma finora mai letto da nessuna UI. InDettaglioClient.tsxil badge diventabadge--warningcon label "arbitrato" (invece del conteggio motori) e tooltip esteso quando l'esito è stato arbitrato — nessuna migrazione DB, solo lettura di dati già persistiti (engine_sources,notes). - Fix falso positivo 1.3.1 in
wcag-mapper.ts(fix): il criterio aggregava senza filtro messaggio gli errori di 6 checker condivisi con altri criteri. Un link "salta al contenuto" mancante (competenza di 2.4.1) e gli errori del checkeraria(competenza di 4.1.2, già catturati indipendentemente da quel criterio) contaminavano erroneamente 1.3.1. Audit completo dei 6 checker, filtro esteso allo stesso pattern già usato da 2.4.1, fixture golden dataset (2.4.1-no-skip-link) estesa con un secondo ground truth per bloccare la regressione. Scoperto dal golden dataset (§3.2), mai corretto prima. - Cron di decadimento temporale KB (
feat):applyTimeDecay()(half-life 90/180gg, soft-delete sotto 0.3 di confidence) era completamente implementata ma senza alcun chiamante — il decadimento non avveniva mai in produzione. Nuova funzione InngestkbTimeDecay, trigger{ cron: '0 3 * * *' }(primo cron del progetto, gli altri 3 job esistenti sono event-triggered), registrata insrc/app/api/inngest/route.ts. - Fix contrasto
text-warm-500/600(fix): token usati su testo in 81 file sottosrc/;warm-500era 2.82:1 su bianco (falliva anche la soglia AA-large di 3:1),warm-6004.18:1 (falliva AA-normale, 4.5:1). Scuriti centralmente a#7E7154/#7A7162(4.80:1/4.81:1 su bianco, 4.55:1/4.57:1 su ivory) — effetto noto e accettato: i due token diventano visivamente quasi indistinguibili tra loro. Trovato dalla skill/a11ydurante la sessione golden-dataset del 14/07, mai corretto prima. - Disambigua title
/admin/knowledge-base(fix): dichiaravatitle: 'Knowledge Base Avanzata'ma la pagina renderizza<h1>Chatbot</h1>— facilmente confuso con la vera Knowledge Base esperti (/kb). Rinominato a "Chatbot — Fonti documentali".
Copertura: chiude 5 voci "Ipotizzato" indipendenti di docs/stato-evolutive.md, nessuna migrazione DB, nessuna dipendenza tra i task. Skill /a11y eseguita su DettaglioClient.tsx (nuovo stato badge) e sui token di contrasto aggiornati — nessun problema nuovo trovato (contrasto badge ~5.97:1, testo warm ≥4.80:1). Smoke reale in browser del badge e verifica del cron nella Inngest Dev UI (:8288) restano un passo manuale residuo — stack Docker non attivo durante l'implementazione.
Verifica: tsc pulito · suite 914/914 · eslint 0 errori (67 warning pre-esistenti, invariati) · next build ok. Spec e piano in docs/superpowers/{specs,plans}/2026-07-14-quickwin-confidence-kb-a11y-*.
Attendibilità — golden dataset e precision/recall per motore (§3.2)
- Golden dataset di verità nota (
feat): nuovo modulosrc/lib/golden-dataset/— 24 fixture HTML su 12 criteri pilota (1.1.1, 1.2.2, 1.3.1, 1.3.5, 2.4.1, 2.4.2, 2.4.4, 2.4.6, 3.1.1, 3.3.2, 4.1.1, 4.1.2), ciascuno con una pagina in violazione e una gemella conforme, verità nota in sidecar.expected.jsonaccanto a ogni HTML (scartato un manifest TS unico, per non ricreare un file monstre — §4.1).loader.tsvalida l'integrità dell'accoppiamento html/json e fallisce esplicitamente su file orfani. - Motore di calcolo precision/recall/F1 (
feat):runner.tsesegueevaluateHtml()(motore statico) erunTechniqueEngine()in modalitàstatic(Tecniche WCAG) su ogni fixture;metrics.tsclassifica ogni esito come TP/FP/FN/TN (positivo =Non superato, l'evento che conta per uno strumento di audit) e aggrega a tre livelli — per (motore, criterio), per motore, e complessivo.computeReliabilityReport()(index.ts) è la funzione pubblica unica, usata sia dal test CI sia dalla route API, nessuna duplicazione di logica. - Endpoint
GET /api/knowledge/reliability(feat): stesso pattern di autenticazione di/api/knowledge(admin/auditor). Calcolo dal vivo a ogni richiesta, nessuna tabella DB né cache — i due motori in scope sono deterministici e girano in millisecondi su 24 fixture. - Pannello "Scheda di affidabilità" in
/kb/insights(feat): nuovoReliabilityPanel.tsx, tabella criterio × motore con badge F1 colorato (verde ≥90%, giallo ≥70%, rosso sotto) sempre accompagnato dal valore numerico in testo — mai il colore come unico segnale (WCAG 1.4.1). Skill/a11yeseguita sul pannello prima del merge. - Test CI non bloccante (
feat):reliability.test.tsverifica l'integrità del dataset, l'assenza diNaNnelle metriche, stampa una tabella leggibile in console e asserisce solo un floor aggregato permissivo (F1 medio ≥ 0.4) — gate informativo, non blocca la build per la regressione di un singolo criterio mentre il dataset è ancora piccolo. - Fix Critical da review finale whole-branch (
fix): l'endpoint legge le fixture da disco a runtime (readdirSync/readFileSync, nonimport) — il file tracing di Next.js per l'outputstandalone(quello usato nell'immagine Docker) non le vede automaticamente. SenzaoutputFileTracingIncludesinnext.config.ts, la cartellasrc/lib/golden-dataset/fixtures/restava fuori dal build di produzione e l'endpoint rispondeva500solo in Docker/Render, mai in locale (npm run dev) — il tipo di regressione più difficile da notare prima del rilascio.
Copertura v1, esplicitamente dichiarata: solo i due motori statici e deterministici (motore statico Cheerio, Tecniche WCAG in modalità static). IBM Equal Access (richiede un URL live raggiungibile), Claude AI (costo/chiave per chiamata) e il motore agentico (richiede un contesto Playwright) restano fuori scope — stesso harness, iterazioni successive. Persistenza storica delle metriche e gate CI bloccante anch'essi rimandati.
Verifica: tsc pulito · suite 904/904 (+39 rispetto al baseline pre-feature) · eslint 0 errori (67 warning pre-esistenti, invariati) · next build ok. 8 task con review dedicata + review finale whole-branch (1 Critical trovato e corretto, il fix outputFileTracingIncludes sopra). Spec e piano in docs/superpowers/{specs,plans}/2026-07-13-golden-dataset-precision-recall-*.
Corretto
- Estensione Chrome — pagina
/estensionedisallineata da Onda 3: la pagina era ferma a "Valida v1.0" (solo IBM Equal Access), senza menzionare il motore statico, il validatore W3C, i 7 agenti comportamentali né il campo "Nome componente" introdotti dalle release precedenti. Aggiornata a v1.1 con i passaggi e i permessi correnti, e un link diretto alla sezione limitazioni della guida. - Warning CJS di Vite nei test (
chore):vitest@2.1.9innesca unrequire('vite')interno che ri-triggera l'auto-warning di deprecazione di Vite su se stesso a ogninpm test, indipendentemente dal codice del progetto (nessunvitest.config.tsin questo repo). Silenziato conVITE_CJS_IGNORE_WARNING=1sugli scripttest/test:watch— il flag ufficiale documentato da Vite per questo scenario, non una soppressione ad hoc. La causa radice è risolvibile solo con un upgrade major di vitest (attuale 2.1.9 vs stabile 4.1.10), rimandato: il problema è puramente cosmetico nell'output. - ESLint scansionava i worktree manuali (
fix):.worktrees/**(il percorso usato dal fallback manuale disuperpowers:using-git-worktrees) non era escluso dalla configurazione ESLint come già lo era.claude/**— un worktree duplica l'intero albero incluso il proprionode_modules, producendo 31812 falsi problemi invece di 0 errori reali.
Estensione Chrome "Valida" — motore agentico lotto 1 + completamento parità motori (Onda 3)
Con questa release la valutazione avviata dall'estensione copre 7 dei 19 agenti comportamentali e tutti i motori server-side che la pipeline web esegue senza dipendere da un browser Playwright proprio (statico, W3C, effort). Il gap residuo rispetto alla piattaforma web si restringe ora al motore agentico lotto 2 (~6 agenti ancora portabili) e a un piccolo insieme di capacità con limite strutturale del browser (nessun engineering lo colma).
Gamba A — Motore agentico, lotto 1 (form/auth, 7 agenti)
- Sonde comportamentali in content-script (
feat): nuovochrome-extension/agentic-probes.js, un modulo-funzione per agente riscritto in JS classico (nessuna API Playwright) per i 7 criteri del cluster form/autenticazione — 3.3.3 (form-validation, dati invalidi tipizzati + qualità del messaggio d'errore), 3.3.1 (error-identification, stessa sonda del form-validation con lente diversa), 3.3.4 (error-prevention, classificazione form critici legali/finanziari/modifica-dati + meccanismi G98/G99/G168), 3.3.7 (redundant-entry, campi duplicati + coperturaautocomplete/H100), 3.3.8 (accessible-auth, classificazione CAPTCHA + probe paste-block + alternative cognitive-free), 2.2.1 (timing-adjustable, meta-refresh F40/F41 + probe countdown a 2,5s), 4.1.2 (aria-state, toggle con ripristino al secondo click). Le evidenze grezze (RawEvidence[], stesso formato del motore web) sono interpretate dopo il submit dalla nuova funzione InngestextensionAgenticInterpret(src/inngest/functions/extensionAgenticInterpret.ts, eventoextension/agentic-interpret), che riusa invariatointerpretWithClaude— nessuna duplicazione di logica AI, solo delle sonde — e applica gli esiti con engine sourceagentic-extensionrispettando la skip-logic anti-overwrite esistente. Le evidenze grezze sono persistite nella nuova tabellaextension_agentic_evidence(id/audit_round_id/component_id/agent_id/criterion_code/evidencejsonb/statuspending→interpreted|failed/created_at/interpreted_at) — l'evento porta solo{ roundId, componentId }, la funzione legge le righe pending, così i retry Inngest restano idempotenti senza limiti di payload sull'evento. - Politica "mai submit reale" (decisione di sicurezza vincolante): le sonde girano nella sessione autenticata reale dell'utente (non in una sessione anonima Playwright) — un submit vero potrebbe creare record, protocollare pratiche o inviare email su sistemi di produzione dell'ente. Interceptor
submitin capture-phase suldocument, installato prima di ogni sonda e rimosso infinally(preventDefault()+stopPropagation()); le sonde usano soloreportValidity()/requestSubmit(), maiform.submit(). Un fix Critical emerso in review (task 8, commit0e603dc) ha chiuso un bypass reale: un handlerchangedella pagina che chiamavaform.submit()imperativamente scavalcava l'interceptor (che intercetta solo l'eventosubmit, non la chiamata al metodo) — la sonda ora neutralizza temporaneamenteHTMLFormElement.prototype.submitdurante la finestra di sonda e lo ripristina infinally, riprodotto con test comportamentale RED→GREEN prima del fix. - Iniezione in
world: 'MAIN'(vincolo binding di review, task 11): sia il fileagentic-probes.jssia la chiamata avalidaRunAgenticProbesdevono essere iniettati conchrome.scripting.executeScript({ world: 'MAIN' }), entrambi nello stesso mondo — i mondi isolati (default dell'estensione) non condividono i prototipi JS con la pagina: senzaworld: 'MAIN'la neutralizzazione diform.submitsarebbe silenziosamente inerte in produzione, un fallimento invisibile ai test (jsdom è single-world, resta verde comunque). Ordine di iniezione nel popup:ace.js→ screenshot (P1, Onda 2/B) → RenderedContext (P3, Onda 2/B) → sonde agentiche per ultime, perché mutano temporaneamente la pagina e lo screenshot deve restare pulito. - Guard sui form critici per l'agente aria-state (2 fix Critical/Important in review, task 10): l'agente 4.1.2 salta gli elementi dentro form classificati critici da 3.3.4 (niente toggle/click su pulsanti tipo "Elimina account"). Il guard iniziale riusava
MAX_FORMS=3(limite di campionamento dell'evidenza) come limite di sicurezza — un form critico oltre il terzo restava fuori dall'esclusione e il click reale su un pulsante distruttivo era riproducibile; corretto con un exclusion set uncapped (l'evidence sampling resta cappato, solo l'esclusione no). Un secondo fix ha chiuso un fail-open: il guard confrontava selettori CSS round-trip e falliva aperto (form incluso, non escluso) su unidnon valido come identificatore CSS (es.form#2fa-elimina, dovequerySelectorAlllancia) — riprodotto con click reale sul pulsante "Elimina account"; corretto facendo consumare al guard gli elementi DOM già raccolti, mai un round-trip tramite stringa di selettore. - Single-flight guard cross-sessione:
btnAudit.disablednon basta a impedire due esecuzioni concorrenti delle sonde se il popup viene chiuso e riaperto a metà run (il disabled si perde con la chiusura del popup, ma la pagina resta mutata dalla sonda precedente); nuovo flag in-pagewindow.__validaAgenticProbesRunning, che sopravvive alla chiusura/riapertura del popup perché vive sulla pagina, non sull'estensione. - Popup: nuova checkbox «Esegui agenti comportamentali (7 — form/auth)», default attiva; payload esteso
agenticEvidence: [{ agentId, criterionCode, evidence }].manifest.json→ 1.1.0,pack.shverificato includereagentic-probes.jsnella ZIP distribuita.
Gamba B — Completamento parità motori server-side nel submit da estensione
- Motore statico, 13 checker Cheerio (
feat):evaluateHtml(html, url)estratto daevaluateUrl(refactor behavior-preserving,a11y-engine/index.ts) —evaluateUrldiventa fetch +evaluateHtml, la pipeline web non cambia comportamento./api/extension/submitchiama oraevaluateHtml(rawHtml, url)invece di lasciare il motore statico completamente assente:evaluateUrl(url)avrebbe rifatto un fetch server-side dell'URL, colpendo la pagina di login sulle pagine dietro autenticazione — proprio il caso d'uso primario dell'estensione. Engine sourcestatic-extension. Lo step Claude riceve ora la base fusa statico+tecniche (non solo tecniche), a parità con la pipeline web (fix di un drift di parametro trovato in review, task 2). - W3C validator (
feat): gated sul flag per-round esistente (round.w3cEnabled, stesso gate della pipeline web), consourceCodeora persistito sull'upsert — prima la colonna Excel del report per submit da estensione restava vuota per i criteri W3C. Un secondo fix di review ha convertito loUPDATEdiretto inCOALESCE(EXCLUDED.source_code, source_code)sull'upsert: senza, un submit successivo privo di W3C avrebbe azzerato (null-clobber) il codice sorgente già salvato da un submit precedente. - Effort (
feat):saveEffortScores(auditRoundId, componentId)chiamato dopo il salvataggio delle valutazioni, stesso punto della pipeline web, best-effort (un fallimento non blocca il submit).
Residuo dichiarato (matrice di parità, §7 della spec)
- Agentico: 7/19 coperti da questa onda. ~6 agenti del lotto 2 restano portabili in linea di principio (via DOM/
dispatchEvent) e sono la priorità della prossima onda. 4-5 agenti non sono portabili nemmeno in teoria, limite strutturale del browser, non di sforzo ingegneristico:focus-order/skip-nav(un evento sintetico non sposta il focus reale — serve un vero Tab da tastiera via CDP),hover-focus(il vero:hoverCSS segue la posizione reale del cursore, non eventi sintetici),reflow(richiede un vero resize del viewport). Uno sblocco futuro viachrome.debugger/CDP è tecnicamente possibile ma è una permission invasiva — decisione di prodotto rimandata. - Gemini Vision, screenshot di evidenza per-elemento (
enrichEvaluations) e AX-Tree snapshot restano fuori scope: richiedono rispettivamente snippet per-elemento dal content script, un crop server-side dallo screenshot P1 (idea a costo medio per il lotto 2), e l'accessibility API di CDP/Playwright.
Verifica: tsc pulito · suite 865/865 (+61 dal baseline pre-onda 804/804 di v1.81.0) · eslint 0 errori (67 warning pre-esistenti, invariati) · next build ok. Spec e piano in docs/superpowers/{specs,plans}/2026-07-10-extension-agentic-parity-*. 13 task, ognuno con review dedicata, 3 fughe di sicurezza reali trovate e chiuse durante l'implementazione (mai esposte su main): bypass form.submit() dell'interceptor (task 8), guard aria-state fail-open su form critici oltre il terzo/su id non-CSS-ident (task 10), sonda accessible-auth priva dell'interceptor sul proprio evento paste (trovata dalla final whole-branch review, non da un review per-task).
Smoke test reale eseguito (post-merge, controller diretto, non delegato a subagent): container ricostruito dal branch e ridistribuito su Docker locale; sonde eseguite in un browser Chromium reale (non jsdom) via iniezione diretta dello script su una pagina di test con form/CAPTCHA/countdown/toggle ARIA — tutti e 7 gli agenti hanno prodotto evidenza realistica (inclusi i messaggi di validazione HTML5 nativi del browser in italiano); canary di sicurezza superati: nessuna navigazione avvenuta, HTMLFormElement.prototype.submit ripristinato all'originale, valori campo e stato ARIA ripristinati. Payload reale inviato a /api/extension/submit su stack Docker completo (Postgres/Inngest/W3C sidecar): motore statico e W3C confermati funzionanti con dati reali (3.1.1 rilevato su <html> senza lang, sourceCode W3C persistito su 6 criteri via il fix COALESCE), effort_static popolato; le 7 righe di evidenza agentica sono state persistite pending, l'evento Inngest consumato e le 7 interpretazioni Claude fallite con grazia per credito API esaurito (stesso limite noto da Onda 2/B) — nessuna riga rimasta bloccata, nessun crash, percorso di degradazione verificato in produzione. Non verificato (residuo dichiarato, nessun tool disponibile per caricare un'estensione MV3 reale in questa sessione): il caricamento effettivo tramite chrome.scripting con semantica world: 'MAIN' dal popup reale, e il rendering dei nuovi esiti nella UI risultati autenticata — entrambi da controllare al primo utilizzo reale dell'estensione pacchettizzata.
Estensione Chrome "Valida" — parità con la piattaforma web (Onda 2/B)
- Coerenza cross-pagina raggiungibile anche da round popolati da estensione (
feat):/api/extension/submitchiama oraextractPageSnapshot()(nav fingerprint, help position, landmark map) per ogni pagina ricevuta — prima le pagine da estensione restavano invisibili all'analisi di coerenza cross-pagina, indipendentemente da come/quando veniva lanciata. L'evento Inngest che ricalcola la coerenza è stato rinominato inround/consistency-recomputee viene ora emesso dopo ogni pagina "assestata" (web e estensione) — lo stesso trigger era di fatto un bug preesistente mai cablato da nessuna parte del codice. Un round popolato solo da pagine da estensione produce quindi un'analisi di coerenza completa (3.2.3 link di navigazione, 3.2.4 etichette incoerenti, 3.2.6 posizione aiuto), visibile nel pannello "Coerenza Cross-Pagina" della pagina round. - Guardia round completato su submit da estensione (
fix):/api/extension/submitverificava solo l'esistenza del round, non il suostatus, a differenza di/api/evaluate//api/evaluate/asyncche già bloccano con403. Ora replica la stessa guardia: submit su roundcompletata→403con messaggio esplicito, mostrato nel popup. - Avviso non bloccante di mismatch dominio (
feat): il popup confronta il dominio della tab attiva con l'URL del progetto selezionato (ora esposto da/api/extension/projects) e mostra un avviso non bloccante quando non coincidono, per intercettare submit accidentali sul progetto sbagliato. - Campo
extensionOnlysui componenti + blocco ri-valutazione automatica (feat): nuova colonnacomponents.extensionOnly, valorizzata sui componenti creati da estensione (pagine raggiungibili solo con la sessione autenticata del browser dell'estensione, non da Playwright). La piattaforma ora esclude questi componenti dalla ri-valutazione automatica ("Auto" disabilitato con tooltip esplicativo, esclusi dall'isEvaluabledievaluateAllcon nota riassuntiva sul numero saltato) e li segnala con badge "Da estensione" nella lista componenti — risolve il bug per cui la piattaforma tentava comunque di raggiungere pagine autenticate aggiunte solo da estensione, bloccandosi sull'autenticazione.
Estensione Chrome — P1 screenshot di evidenza
- Screenshot dell'intera tab al submit (
feat):chrome.tabs.captureVisibleTabcattura lo screenshot al momento dell'audit, caricato su MinIO (uploadExtensionScreenshot, pathscreenshots/round-{id}/extension/component-{id}.jpg) e linkato al componente (screenshot_key/screenshot_at). Best-effort: un fallimento di cattura o upload non blocca mai il submit. Le pagine dietro login — il caso d'uso primario dell'estensione — non sono più le uniche prive di evidenza visiva.
Estensione Chrome — P2 analisi Claude AI
- Step Claude AI sul
rawHtmlinviato dall'estensione (feat): lo step "Claude" (evaluateWithAi+ injection KB) viene eseguito rispettando l'evalModeeffettivo del round (tecnica-claude/tecnica-claude-gemini), dopo il salvataggio di IBM/tecniche statiche per non farne sovrascrivere lo snapshot.
Estensione Chrome — P3 RenderedContext parziale dal browser reale
- Collettori RenderedContext portati in content-script (
feat): 3 dei 4 criteri rendered originariamente coperti dal renderer Playwright sono ora disponibili anche da estensione — 2.4.7 (focus visibile), 2.5.8 (target size), 1.4.12 (text spacing overflow) — tramiterendered-context-collectors.js, riscrittura in JS classico (nessuna API Playwright, sologetComputedStyle) dei collettori dicollect-rendered-context.ts, iniettato viachrome.scripting.executeScriptinsieme adace.jsprima dello screenshot. Il popup invia il risultato sottorenderedContext, consumato darunTechniqueEngine(..., { renderedContext }). 1.4.13 (contenuto aggiuntivo su hover/focus) resta esplicitamente fuori scope: è l'unico dei 4 criteri che richiede una vera interazione hover, non esprimibile con un passaggio DOM iniettato. renderedTechniqueScope— il RenderedContext parziale non influenza più criteri fuori scope (fix):runTechniqueEngineapplicava ilRenderedContextin modo tutto-o-niente, eseguendo tutti i ~24 check rendered anche con un contesto parziale come quello dell'estensione — i check sui campi assenti (es. contrasto in hover) restituivanocannot-tell, potendo retrocedere aDa verificareanche criteriSuperatomai nello scope di questa onda. Nuova opzionerenderedTechniqueScopeinTechniqueEngineOptionsfiltra i risultati rendered ai solitechniqueIdche l'estensione raccoglie davvero (C15/G149→ 2.4.7,C42/F110→ 2.5.8,C36/F104→ 1.4.12), passata solo da/api/extension/submit; la pipeline web non la usa mai (comportamento identico a prima). Solo i 3 criteri sopra sono influenzati dalRenderedContextda estensione.
Verifica: tsc pulito · suite 804/804 · eslint 0 errori · next build ok. Smoke test reale in browser su tutte le funzionalità sopra: coerenza cross-pagina rilevata correttamente su due pagine con menu di navigazione incoerente (link mancante 3.2.3 + etichetta diversa 3.2.4); guardia round completato → 403 visibile nel popup; screenshot recuperabile via /api/screenshots per componenti da estensione (~30 KB ciascuno); step Claude eseguito con chiamata reale (errore di credito API catturato e mostrato per esteso, non silenzioso — la verifica con credito disponibile resta da fare); RenderedContext su pagina di test con focus-outline rimosso, target sotto 24px e testo che trabocca con spaziatura aumentata — tutti e tre rilevati correttamente. Spec e piano in docs/superpowers/{specs,plans}/2026-07-09-extension-p1-p3-consistency-*. Residuo noto: motore agentico (19 agenti comportamentali) non ancora portato all'estensione — 14-15/19 agenti sono riscrivibili via DOM/dispatchEvent, 4-5 (focus-order/skip-nav, hover-focus, reflow) non sono portabili nemmeno in teoria per limite strutturale del browser; priorità immediata della prossima onda (§9-quater P8 del documento di analisi).
Osservabilità — Sentry error tracking (Onda 2/A)
- Sentry EU, solo errori (
feat):@sentry/nextjsinstallato e wired su server/edge/client (sentry.{server,edge,client}.config.ts+src/instrumentation.ts, opzioni centralizzate insrc/lib/sentry-options.ts), regione EU,tracesSampleRate: 0(nessun tracing/performance, solo cattura errori). Copre i tre punti di crash dell'app: server (auto-instrumentation sui route handler), client (src/app/global-error.tsx, error boundary root) e pipeline Inngest (onFailure→Sentry.captureExceptioncon contesto{ function, componentId }). - PII scrubbing (
feat):src/lib/sentry-scrub.ts(Web Crypto, nonBuffer— eseguibile anche su Edge/client) hasha l'email utente e rimuove IP/cookie/authorization/body dagli eventi prima dell'invio, viabeforeSend.
Sicurezza — CSP script-src enforcing con nonce per-request (Onda 2/A)
- Nonce-based script-src (
feat):script-srcpromosso da Report-Only a enforcing, con nonce generato per ogni richiesta insrc/middleware.ts(Web Crypto, Edge runtime) e costruito dabuildEnforcingCsp()(src/lib/security/csp.ts); propagato come headerx-nonce(letto insrc/app/layout.tsx, che forza il rendering dinamico per la propagazione) e come header di rispostaContent-Security-Policy. Fallback'strict-dynamic' https: 'unsafe-inline'mantenuto per i browser senza supporto al nonce; nessuna libreria del bundle ha mostrato bisogno reale di'unsafe-eval'in produzione, non aggiunto alla direttiva enforcing. - Hardening auth (
fix): durante l'implementazione è emerso un bug latente inauth.config.ts— con il wrapper middleware custom richiesto per generare il nonce, NextAuth v5 non esegue più il redirect automatico quandoauthorized()ritorna il booleanofalsesu una pagina protetta da anonimo (lo fa solo seauthorized()ritorna direttamente unaResponse), servendo200invece del redirect a/login. Corretto ritornando unNextResponse.redirectesplicito, byte-per-byte identico al comportamento nativo di NextAuth (stessonextUrl.clone(),callbackUrlassoluto,307), verificato per parità contro una baseline pre-fix su 4 classi di rotta. Il bug non è mai stato esposto in produzione (introdotto e corretto nello stesso branch, mai mergiato sumain), ma chiude un gap di sicurezza reale prima del merge.
Verifica: tsc pulito · suite 787/787 · eslint 0 errori · next build ok. Verifica browser reale su build di produzione (il dev server usa eval() per l'HMR di webpack e non è rappresentativo per il controllo CSP): /login pubblica + login reale su /progetti, /progetti/[id]/dashboard e /kb/insights (entrambe fortemente recharts) — zero violazioni script-src nuove, idratazione corretta ovunque, grafici renderizzati. Spec e piano in docs/superpowers/{specs,plans}/2026-07-08-sentry-csp-enforcing-*. Residuo noto: style-src resta in Report-Only (§5.1 del documento di analisi); delivery Sentry a un progetto reale non verificata (nessun DSN disponibile in questo ambiente).
Remediation RAG & Accessibilità
- Fallback RAG Remediation (
feat): introdotto il fallback RAG nell'endpoint per i criteri senza entry manuale curata; aggiunto un trigger UI (pulsante) per attivare la spiegazione RAG; esposti gli errori dell'endpoint AI in UI. Aggiunto trigger lato admin per l'ingestione del corpus RAG. Hardening perparsePersisted. - A11y Card "Come correggere" (
fix): rimosso problema id duplicati su contesti RAG usandouseId()peraria-labelledbynellaRemediationCard; migliorato il contrasto del bordo del trigger RAG (border-leaf-700); correzioni accessibilità finali sulla card.
Knowledge Base — Import/Export JSON (Onda 1/C)
- Export
GET /api/knowledge/export(admin/auditor): dump JSON di tutta la KB ({ schemaVersion: 1, exportedAt, count, entries }), scaricabile come allegato; filtri opzionali (criterion/scope/type/active). Ometteid/created_by(instance-specific), preserva lo stato appreso (confidence/decay/occurrence/reviewerRole). - Import
POST /api/knowledge/import(admin-only): merge per chiave naturale (criterion_code::knowledge_type::original_status::corrected_status::content_type::scope) — inserisce le entry nuove, salta le esistenti (o le aggiorna con?mode=overwrite), con dedup intra-batch.created_bysempre null (mai dal payload), solo colonne whitelisted, guard body 5 MB (letto prima del parse), invalidazione cache KB. Risposta{ inserted, updated, skipped, errors }. - UI: pulsanti «Esporta KB» (admin/auditor) e «Importa KB» (admin) nel pannello
/kb. Abilita backup, portabilità tra istanze e seed di KB curate per nuovi clienti.
Provenienza — versioning prompt/modello sugli snapshot (Onda 1/B)
ai_model+ai_prompt_versionsuevaluation_snapshots: ogni snapshot di criterio prodotto da un'AI registra ora il modello e la versione del prompt che l'hanno generato, rendendo il verdetto riproducibile e difendibile (rilevante in PA). Valorizzati dalle fasi Claude (ai-phase), Gemini (visual-phase) e Claude-PDF (pdf-evaluation) tramiteapplyEvaluations; upsert conCOALESCE(EXCLUDED, esistente)così l'ultima AI che tocca il criterio vince e una fase non-AI (null) non azzera il valore. Le versioni prompt sono costanti per-engine (AI_PROMPT_VERSION,GEMINI_PROMPT_VERSION) da bumpare a mano al cambio del template. Migrazione idempotente; criteri solo-tecnici restanonull. UI ed eventuale backfill storico non inclusi.
Osservabilità — tracing per-step Inngest + rollout logger completo (Onda 1/A)
- Tracing per-step della pipeline: nuovo wrapper
tracedStep()(src/lib/evaluation-pipeline/step-trace.ts) attorno a ognistep.run()dievaluationPipeline(mark-running, technical/claude/visual/agentic-eval, mark-completed) econsistencyAnalysis. Registra per ogni step nome, durata (ms), esito ed errore inexecution_logs(categoriaSYSTEM,detailsJSON,executionIdstabile sui replay Inngest via step memoizzatotrace-init) più una riga sul logger strutturato. Scrittura best-effort: un errore di logging non può mai far fallire uno step reale, e l'errore originale dello step viene rilanciato invariato. Le tracce compaiono nella exec-log UI per componente (useExecLogs); la function di consistency (per-round, senzacomponentId) traccia solo su logger. - Rollout logger completo: migrati tutti i
console.*residui disrc/lib(11 file) esrc/app/api(12 file) al logger strutturato (logger.childconauditRoundId/componentId/requestIddove disponibili, chiavi evento snake_case,Errornormalizzati). Regola ESLintno-consolepromossa dawarnaerrorsusrc/libesrc/app/apiper prevenire regressioni (il sinklogger.tsresta esente). - Fix a11y: i blocchi di codice scrollabili (
<pre>) delle card «Come correggere» ora hannorole="group"+aria-label(regione scrollabile nominata, focalizzabile da tastiera per lo scroll — WCAG 2.1.1).
Motore agentic — lotto 2: 3 nuovi agenti (19 totali) e supporto multi-browser
- Agente
error-prevention(WCAG 3.3.4 — Prevenzione errori): classifica i form come critici — legali (dichiarazioni, consensi, autocertificazioni, istanze, contratti), finanziari (IBAN, carte di credito, pagamenti, bonifici) o di modifica dati (cancellazioni, disdette, revoche) — e verifica la presenza di almeno un meccanismo di prevenzione richiesto da G98/G99/G168: step di revisione/riepilogo, checkbox di conferma esplicita («confermo», «dichiaro di aver letto») o possibilità di annullare/modificare. Esito N/D sulle pagine senza form critici. - Agente
redundant-entry(WCAG 3.3.7 — Inserimento ridondante): rileva campi con la stessa etichetta ripetuti nella pagina (le conferme di sicurezza come «conferma password» sono riconosciute come eccezioni ammesse dal criterio) e campi dato-personale (nome, email, telefono, indirizzo, codice fiscale…) privi di tokenautocomplete(H100), il principale meccanismo anti-ridondanza. - Agente
timing-adjustable(WCAG 2.2.1 — Regolazione tempi di esecuzione): rilevameta http-equiv="refresh"con ritardo tra 0 e 20 ore (F40/F41) e countdown di sessione (role="timer", testi con orari vicino a «sessione/scade»), con probe comportamentale: il testo dei candidati viene riletto dopo 2,5 secondi — se cambia, il timer è attivo e serve un meccanismo di estensione. - Multi-browser opt-in: nuova variabile d'ambiente
AGENTIC_BROWSERS(csv dichromium/firefox/webkit, defaultchromium— comportamento invariato se assente). Gli agenti girano su ogni engine configurato e gli esiti vengono fusi per criterio con logica worst-case; status divergenti producono una nota[Divergenza browser]nell'evidenza. Gli engine non installati nell'ambiente vengono saltati con warning nel log di esecuzione (in Docker resta operativo il solo Chromium finché l'immagine non include i browser Playwright). - Guida in linea aggiornata: sezione «Form e comportamento» a 6 agenti, nuova area «Interazione visiva e tempo» a 4 agenti, conteggi a 19.
Verifica: tsc pulito · suite 628/628 · eslint 0 errori · next build ok. Spec in docs/superpowers/specs/2026-07-04-agenti-lotto2-multibrowser-design.md.
Motore agentic — 2 nuovi agenti (16 totali) e limiti configurabili
- Agente
form-validation(WCAG 3.3.3 — Suggerimento per errori): compila i campi form con dati invalidi tipizzati (email malformata, URL errata, numero fuori range, pattern non conforme; i campirequiredvengono lasciati vuoti), sottomette e valuta la qualità del messaggio di errore — un buon messaggio identifica il campo e suggerisce la correzione (formato atteso o esempio) viaaria-errormessage,aria-describedby,validationMessageo testo adiacente. Complementare aerror-identification(3.3.1, submit a vuoto), la cui descrizione criterio è stata ristretta al solo 3.3.1. - Agente
accessible-auth(WCAG 3.3.8 — Autenticazione accessibile): sulle pagine con form di login verifica il supporto ai password manager (autocompletecredenziali, probe comportamentale del blocco-incolla sul campo password), classifica gli eventuali CAPTCHA (reCAPTCHA/hCaptcha cognitivi, Cloudflare Turnstile no, captcha immagine) e rileva le alternative senza test cognitivo (SPID, CIE, CNS, OAuth, passkey, codice via email/SMS). Esito N/D sulle pagine senza autenticazione. - Limiti di campionamento configurabili: nuova env
AGENTIC_MAX_ELEMENTS(sample-limits.ts) che può solo alzare i cap per-agente (mai abbassarli, tetto 200); applicata ai nuovi agenti e ano-trap(5),error-identification(3),hover-focus(8). - Il fallback
agentic_active_agents(setting assente) ora deriva dal registry (ALL_AGENT_IDS) invece di una lista hardcoded di 4 agenti; guida in linea aggiornata (sezione «Form e comportamento» a 4 agenti, conteggi a 16). - Nota: la proposta «agente 2.5.7 dragging» del documento di analisi non è stata implementata perché il criterio è già coperto dal motore WCAG Techniques rendered (G215/F108 su
dragListeners).
Analisi di coerenza cross-pagina (3.2.3 / 3.2.4 / 3.2.6) completata
helpPositionora estratto dall'HTML (euristica contatti/aiuto/assistenza/supporto/FAQ/help con posizione header/nav/main/aside/footer): prima era semprenulle il check 3.2.6 non scattava mai.- Check ordine relativo dei link di navigazione (G61): oltre alla presenza, viene confrontato l'ordine dei link comuni tra le pagine del round (3.2.3).
- Nuovo check etichette incoerenti (F31): stesso
hrefcon testi diversi tra le pagine → issue 3.2.4; usa il nuovo campo retro-compatibileNavFingerprint.links(righe storiche senza il campo vengono ignorate). - Persistenza degli esiti:
POST /api/consistency/[roundId]/analyze(e la funzione Inngestconsistency-analysis) ora scrivono per ogni criterio coinvolto una valutazioneDa verificare(mai un fallimento automatico) su tutti i componenti del round, con evidenza aggregata eengine_sources: ['consistency']; la skip-logic esistente protegge i risultati già rivisti dall'esperto.
Verifica: tsc pulito · suite 604/604 · eslint 0 errori · next build ok. Spec e piano in docs/superpowers/{specs,plans}/2026-07-04-nuovi-agenti-*.
Manutenzione — scomposizione file «monstre», secondo lotto (§4.1)
Refactor interno behavior-preserving (nessun cambiamento funzionale, UI o API): scomposti i due file rimasti fuori scope dalla Fase 3, entrambi oltre le 1.000 righe.
src/lib/renderer/collect-rendered-context.ts1690 → 134 righe: estratti nove moduli fratelli per famiglia WCAG (rendered-context-shared,-focus,-target-link,-spacing-hover,-drag-motion,-dyslexia-flashing,-interaction,-contrast,-reflow-sequence) con un barrel che preserva invariata l'unica API pubblica (collectRenderedContext). Le quattro copie della formula di contrasto con soglie leggermente divergenti (0.03928 / 0.04045) sono state spostate verbatim, senza deduplicarle, per non alterare un comportamento storicamente differenziato.HelpValutazioneClient.tsx2680 → 163 righe: estratti tipi, utility, due hook (useWcagCriteriaCatalog,useActiveSection), quattro primitivi presentazionali (AccordionItem,CodeBlock,Badge,AgentEntry) e tredici sotto-componenti di sezione (dodici statici, uno dinamico con la ricerca nel catalogo criteri).
Verifica: tsc pulito · suite 563/563 · eslint 0 errori · smoke reale in browser (login, scroll-spy TOC, accordion, ricerca/selezione criteri) · review finale.
Manutenzione — scomposizione file «monstre» (§4.1 / Fase 3)
Refactor interno behavior-preserving (nessun cambiamento funzionale, UI o API): scomposti i tre file sorgente ad alto churn oltre le 1.000 righe, che concentravano troppa responsabilità e rallentavano ogni intervento d'area.
src/lib/ai-engine/index.ts1133 → 293 righe: estratti sette moduli fratelli (usage,client,html-context,response-parser,prompts,arbitration,divergence) con un barrel che preserva invariata l'API pubblica (evaluateWithAi,evaluatePdfWithAi,mergeEvaluations,arbitrate,analyzeBrowserDivergence,EntityContext).QualitaClient.tsx1400 → 531 righe: estratti tipi, utility,SortableHeader, tre hook (useReadabilityData,useIssueTracking,useRewrite) e cinque pannelli presentazionali.ComponenteClient.tsx1882 → 616 righe: estratti tipi, utility, sei sotto-componenti (log esecuzione, matrice cross-browser, divergence expert, confronto visivo, wizard, lista) e l'hookuseExecLogs.
Verifica: tsc pulito · suite 563/563 · eslint 0 errori · next build 96/96 route. Riprende e completa una Fase 3 precedentemente annullata.
Estensione Chrome — quick win 9f–9h (§9-quater)
Portati a terra i tre quick win residui dell'estensione «Valida», il canale per le pagine dietro autenticazione dove Playwright non arriva.
Riproducibilità del motore (9f)
- Pin di
ace.jsalla versione diaccessibility-checkerinstallata sul server (nuovo modulosrc/lib/extension/ace-version.ts; usato da/api/extension/downloade dasetup.sh), con warning se la versione non è risolvibile. La versione motore viaggia ora nel payload di submit e viene registrata comeibm-extension@<versione>inengine_sources.pack.shfallisce seace.jsmanca (prima produceva silenziosamente una ZIP inutilizzabile).
Sicurezza dell'endpoint di submit (9g)
- Rate-limit per IP (20 richieste/minuto →
429conRetry-After) e guard sulla dimensione del body (max 5 MB →413) su/api/extension/submit, prima privo di entrambi (rawHtmlillimitato).
UX post-submit nel popup (9h)
- Il submit restituisce
projectId/created/componentName; il popup mostra il pulsante «Apri in Valida» che apre direttamente la pagina del componente, un campo opzionale «Nome componente» (usato solo alla creazione, senza sovrascrivere componenti esistenti) e l'esito «Nuovo componente creato / Componente aggiornato».
Backlog Fasi 0–2 — Attendibilità, sicurezza e quick win
Smaltita buona parte del backlog di analisi (§14): igiene di release, quick win UI/API e quick win verticali sui motori. Merge di 11 interventi.
Attendibilità e trasparenza degli esiti
- Badge motori/provenienza nel dettaglio criterio: esposto
engine_sources(già inevaluation_snapshots) tramite/api/resultse mostrato con un badge inDettaglioClient. - Export Excel con citazione della tecnica WCAG e dei motori che hanno deciso l'esito —
src/lib/export/technique-citation.ts.
Sicurezza
- Endpoint di raccolta report CSP
/api/csp-report+ direttivareport-uriinnext.config.ts, così la fase Report-Only produce finalmente dati. Endpoint pubblico (i browser inviano i report senza sessione) con guard sulla dimensione del body.
Motori di valutazione
- PDF remediation (pikepdf): applicazione reale di heading (
/H1..6) e language tag per elemento — prima dichiarati nelle proposte ma non applicati (scripts/pdf_remediate.py). - Agentic: screenshot di evidenza per ogni elemento fallito (caricati su MinIO), visibili nel dettaglio criterio.
- Leggibilità: riscrittura assistita per frase in Plain Language AGID —
/api/readability/rewrite-sentence+ bottone "Proponi riscrittura" nella pagina Qualità. - PDF: limite pagine configurabile (
PDF_MAX_PAGES) con avviso non silenzioso oltre il limite (prima troncamento muto a 50 pagine).
Igiene e strumenti
- Route dedicata
/kb/insights. - Logger strutturato nei punti caldi della pipeline + regola ESLint
no-console(warn) susrc/libesrc/app/api. - Guard CI di coerenza
package.json↔ CHANGELOG (scripts/check-version.mjs) per prevenire i disallineamenti di versione. - Roadmap in
CLAUDE.mdaggiornata; rimappatura porta 3002 spostata indocker-compose.override.ymllocale.
Export AgID — Correzione PDF, font e sintesi dichiarazione
Tre correzioni agli export dell'Allegato 2 AgID e della Guida alla compilazione.
PDF che scaricava un Word (risolto)
L'interfaccia dichiarava formato PDF ma veniva scaricato un DOCX. Causa: l'opzione bootstrap di LibreOffice era passata come --env:UserInstallation (doppio trattino); soffice richiede il trattino singolo -env: e con il doppio trattino restituiva Error in option, facendo fallire ogni conversione e attivando sempre il fallback DOCX.
src/app/api/export/agid-report/route.tsesrc/app/api/export/agid-dichiarazione/route.ts— corretto--env:→-env:nella chiamatasoffice. La conversione PDF ora va a buon fine e viene scaricato un vero PDF.
Font Garamond nei testi compilati (risolto)
I testi inseriti nell'Allegato 2 (note e stati per criterio) comparivano in Garamond invece del Calibri del resto del documento. Causa: i segnaposto {n_...}/{s_...} del template non hanno formattazione di run esplicita ed ereditavano lo stile Normale, definito in Garamond.
templates_doc/AGID-Modello-Autovalutazione-Template.docx— stile Normale portato da Garamond a Calibri, coerente con il testo fisso del modello.Dockerfile— aggiuntofonts-crosextra-carlito(font metrico-compatibile con Calibri) così che LibreOffice renda correttamente il Calibri nel PDF.
Sintesi troppo stringata nella Guida alla compilazione (migliorato)
Il campo AgID «sezioni, contenuti e funzioni non accessibili» ammette fino a 2000 caratteri: la sintesi AI ora sfrutta lo spazio disponibile e, per ogni problema, richiama il punto di controllo WCAG coinvolto (codice + titolo del criterio).
src/lib/reports-ai/index.ts— riscritto il prompt disummarizeEvidencesForDichiarazione(testo più completo, riferimento esplicito a codice e titolo del criterio, cap a 2000 caratteri con taglio a fine frase). Esportata la costanteDICHIARAZIONE_FIELD_MAX_CHARS.src/app/api/export/agid-dichiarazione/route.ts— evidenze più ricche (3 campioni per criterio), fallback che preserva i riferimenti al criterio entro il limite di 2000 caratteri, e celle di testo lungo che ora rendono correttamente elenchi e paragrafi multilinea.
Motore AI — Valutazioni deterministiche
Impostato temperature: 0 su tutti i motori AI (Claude e agentic interpreter) per eliminare la variabilità stocastica tra round successivi sulla stessa pagina.
Problema risolto
Round successivi sulla stessa pagina con configurazione identica (Tecnica + Claude + Gemini, stessi browser) producevano verdetti significativamente diversi. L'analisi comparativa su 3 round di produzione (66 → 69 → 72) ha evidenziato oltre 25 criteri con status diverso tra un round e l'altro — inclusi flip critici come 1.1.1, 4.1.1, 2.5.3 — senza alcuna modifica alla pagina né alla configurazione. La causa primaria era l'assenza del parametro temperature nelle chiamate Claude API: il valore di default (1.0) introduce massima variabilità stocastica nelle risposte.
Correzione
src/lib/ai-engine/index.ts— aggiuntotemperature: 0nel body della richiesta API incallClaude(): a parità di HTML e prompt, Claude produce ora sempre lo stesso verdettosrc/lib/agentic-engine/interpreter.ts— aggiuntotemperature: 0ininterpretWithClaude(): stessa garanzia per il motore agentic (14 agenti Playwright)src/lib/gemini-engine/index.ts— abbassatotemperatureda0.1a0in entrambe le funzioni di chiamata Gemini Vision (singola e multi-immagine): elimina la residua variabilità nel motore visivo
Con temperature: 0 la valutazione AI diventa deterministicamente replicabile: round ripetuti sulla stessa pagina non modificata produrranno risultati identici, rendendo i delta tra round indicativi di cambiamenti reali nella pagina o nel sito.
Accessibilità WCAG 2.2 AA e UI
Interventi di conformità WCAG 2.2 AA sull'interfaccia dell'applicazione e correzioni alle note di valutazione nel dettaglio di criterio.
Accessibilità (WCAG 2.2 AA)
- Focus ring — opacità sufficiente (WCAG 2.4.11): elevata l'opacità del
box-shadowdi focus su tutti gli schemi cromatici (navy0.55,green0.75,tomato-error0.75) per soddisfare il requisito di contrasto minimo 3:1 rispetto allo sfondo adiacente - Focus ring chatbot: elevata l'opacità del focus ring dell'input chatbot da 0.10 a 0.55 — il precedente valore era insufficiente per ogni standard di contrasto
- Rimosso anti-pattern
focus:ring-0: sostituito confocus:ring-2 focus:ring-navy-700sul<select>inProgettiClient— rimozione della soppressione del focus che rendeva l'elemento non operabile da tastiera - Modal nativi
<dialog>— 4 componenti migrati:delete-project(ProgettiClient),delete-round(ValutazioniClient),RevisionModal(RtdClient) eDeclarationModalconvertiti da implementazioni customdiv-based al nativo<dialog>HTML5 — gestione focus trap, Escape key, aria-modal e role correttamente delegata al browser - Lifecycle dialog robusto: stabilito il pattern
dialog.close()→onSuccess()per garantire che la chiusura del modal avvenga prima di qualsiasi aggiornamento di stato (elimina race condition); aggiunto guardonCancelper intercettare la chiusura via Escape senza sopprimere l'evento - Focus management SPA: introdotto componente
FocusResetnel layout radice che, ad ogni navigazione Next.js, sposta il focus sul tag<main>(tabIndex={-1}) — evita la perdita di posizione del focus tra le rotte in applicazioni single-page
UI — Note di valutazione
- Pannello Analisi Divergenza Cross-Browser: il pannello AI Divergence Expert nel dettaglio di criterio ora esegue il rendering completo delle note tecniche tramite
ReactMarkdowncon componenti custom allineati al design system (text-sm text-warm-700,bg-warm-100 text-[#962819]percode, headingtext-xs font-semibold) — i contenuti erano in precedenza illeggibili (testo grezzo o markdown non interpretato) - Contenuto note ripristinato: rimossa la logica di stripping del blocco
[Analisi Divergenza Cross-Browser]informatNotesche tronca il contenuto della nota all'altezza del marcatore; il testo completo fluisce ora nel pipeline di formattazione standard con badge sorgente, riferimenti di riga e lista strutturata
UI — Tab EN 301 549
- Accordion EN 301 549 non si aprivano: nel tab EN 301 549 esclusivi del dettaglio componente, il corpo di ogni accordion non veniva mai visualizzato. La causa era una condizione errata che subordinava il rendering al fatto che il criterio avesse
problemsorecommendationsvalorizzati — campi sempre nulli per i criteri esclusivi (4.2.x), che vengono valutati manualmente. Lo stato si aggiornava correttamente (il chevron ruotava), ma nessun contenuto compariva - Soluzione: rimossa la guard sul contenuto; il pannello si apre sempre al click e mostra i dettagli se presenti oppure "Nessun dettaglio registrato per questo criterio." come empty state esplicito
- Miglioramenti accessibilità correlati: i trigger accordion convertiti da
div[role=button]a<button>nativo; aggiunte coppieid/aria-controlsper la relazione semantica tra trigger e pannello
Hotfix — CSP che bloccava il rendering dell'intera applicazione
La CSP in enforcement introdotta in 1.68.0 conteneva default-src 'self'. In assenza di una direttiva script-src esplicita, gli script ricadono su default-src, e 'self' non consente gli script inline: gli script inline di bootstrap/idratazione di Next.js venivano bloccati, lasciando tutte le pagine vuote.
Correzione
- CSP enforcement: rimosso
default-srcdalla policy in enforcement, che ora contiene solo le direttive realmente sicure da bloccare subito (frame-ancestors 'none',object-src 'none',base-uri 'self'). Le direttive che governano il rendering della pagina (default-src/script-src/style-src…) restano inContent-Security-Policy-Report-Onlyfinché le violazioni non saranno verificate. La protezione anti-clickjacking resta attiva - Verifica: il fix è stato validato con un caricamento reale della pagina in browser (idratazione React confermata, zero violazioni CSP), non solo a livello di build
Sicurezza, affidabilità operativa, performance e brand
Insieme di interventi infrastrutturali (quick-win dall'analisi di codebase), nuovo brand favicon e rifiniture sulla rilevazione degli acronimi nel motore di leggibilità.
Sicurezza
- Content Security Policy: introdotti header CSP con strategia a doppio binario — direttive critiche in enforcement (es.
frame-ancestorsanti-clickjacking) e il resto inContent-Security-Policy-Report-Onlyper osservare le violazioni senza rompere l'app prima dell'enforcement completo
Affidabilità operativa
- Health check readiness: l'endpoint
/api/healthverifica ora anche la raggiungibilità di MinIO (object storage) e Inngest (orchestrazione job), oltre al database — readiness reale dei servizi dipendenti - CI build verification: la pipeline di CI esegue ora anche il build di produzione Next.js, intercettando in anticipo gli errori che sfuggono a type-check e lint
Performance
- Rendering multi-browser limitato: introdotta concorrenza controllata (
p-limit) surenderPageMultiper evitare la saturazione di risorse quando si renderizzano più browser in parallelo — soglia configurabile viaRENDER_CONCURRENCY
Brand
- Nuovo favicon Valida: installato il set completo di icone (favicon, Apple touch, icone PWA) dal design handoff ufficiale, con metadata Next.js configurati per il supporto completo
Leggibilità (AGID)
- Rilevazione acronimi per-blocco: il rilevatore di acronimi non spiegati analizza ora i blocchi di testo foglia (h1, p, li…) e salta i blocchi prevalentemente in maiuscolo (titoli/heading stilizzati ≥60% parole maiuscole), eliminando i falsi positivi su titoli e nomi propri (es. "ALPE D'ATTIA") spezzati da apostrofi e punteggiatura
Qualità dei contenuti e leggibilità (AGID)
Nuovo livello di analisi della qualità dei contenuti secondo le linee guida AGID / Designers Italia e diagnostica strutturale della leggibilità.
Qualità dei contenuti (AGID)
- Regole di linguaggio AGID — motore deterministico (senza costo AI): dizionario burocratese → alternativa di Plain Language (~50 voci, es. "al fine di" → "per", "istanza" → "domanda"), inglesismi → italiano (~23 voci, es. "form" → "modulo", "download" → "scarica"), rilevazione della forma passiva, dei link con testo generico ("clicca qui", "leggi di più") e degli acronimi non spiegati al primo uso; punteggio di linguaggio 0–100
- Diagnostica strutturale per-frase: frasi troppo lunghe (oltre 25 parole), lessico complesso, tempo di lettura stimato e media parole/frase — feedback azionabile che affianca l'indice Gulpease
- Analisi AI di leggibilità arricchita con i principi AGID (frasi brevi, forma attiva, dare del "tu", niente burocratese o inglesismi evitabili, acronimi spiegati, link descrittivi)
- Pagina "Qualità dei Contenuti": nuovi pannelli Linguaggio chiaro (AGID) e Struttura del testo nel dettaglio di ogni pagina, con termine → alternativa suggerita, frase di contesto evidenziata e collegamento alle linee guida ufficiali
Affidabilità
- Parsing Gemini Vision: gli elementi non validi (nulli o non-oggetto) nella risposta del modello vengono ora scartati singolarmente invece di far fallire l'intera analisi visiva
Build, qualità del codice e tooling
Ottimizzazione dell'infrastruttura Docker e consolidamento della qualità del codice tramite analisi SonarQube. Nessuna modifica funzionale per l'utente finale.
Build / infrastruttura
- veraPDF nativo arm64: il sidecar di validazione PDF/UA non usa più l'immagine
verapdf/restsolo-amd64 (che girava in emulazione Rosetta/qemu su Apple Silicon). Nuovo build multi-stage (verapdf-service/) che copia gli artefatti Java arch-indipendenti su una base Temurin 11 multi-arch — esecuzione nativa, avvio e validazione più rapidi
Qualità del codice (SonarQube)
- Affidabilità: risolti 4 bug di reliability (ordinamento con compare function, operatori ternari a rami identici) — rating Reliability portato ad A
- Hardening ReDoS: irrobustite le espressioni regolari su input non fidato (CSS di pagine analizzate) per eliminare backtracking super-lineare in
structure,interactions - Sicurezza: nome della directory temporanea della Dichiarazione AGID generato con
crypto.randomBytesinvece diMath.random - Pulizia: rimosse type-assertion ridondanti e introdotto optional chaining dove equivalente (~50 code smell); rimossi i
.DS_Storetracciati - Security hotspots: 81 rivisti e chiusi (fix reali + triage motivato di falsi positivi: riferimenti EN 301 549 scambiati per IP, http su rete interna, random non crittografico)
- Coverage: aggiunto
@vitest/coverage-v8per la generazione del reportlcov - Quality Gate verde: impostata la baseline "New Code" così il gate valuta solo il codice nuovo (suite di 503 test verde, type-check e lint puliti)
Sicurezza — autorizzazione per-oggetto (IDOR) e SSRF
Completamento della messa in sicurezza delle API: ogni rotta che riceve un identificativo di risorsa (progetto, round, componente) verifica ora che il chiamante possa effettivamente accedervi, oltre alla semplice autenticazione. Nessuna modifica funzionale per gli utenti autorizzati.
Autorizzazione a livello di oggetto
- Esportazioni (Excel, DOCX, PDF, Dichiarazione e Allegato 2 AGID): verifica di accesso al round prima della generazione — impedisce l'esportazione di report di enti terzi tramite ID
- Componenti e risultati: mappa strutturale, analisi nomi accessibili, risultati aggregati, stima effort AI e leggibilità ora vincolati alla visibilità del chiamante
- Bonifica PDF: aggiunta autenticazione per-handler (lettura e creazione task)
- Import Excel: l'import in un progetto esistente richiede l'accesso al progetto
- AX-Tree (snapshot, cronologia, divergenze cross-browser) e confronto round: vincolati al round/progetto
- Dichiarazione AGID per round: un RTD può completarla solo per i round del proprio ente
- Notifiche (valutatore, RTD): vincolate a progetto/round
- Knowledge Base (modifica/eliminazione voci): riservata a ruoli Auditor/Admin
- Centralizzazione nel modulo
assertCanAccess{Project,Round,Component}con regola fail-closed
SSRF
- Nuovo
safe-fetch: ogni redirect viene ri-validato contro la lista di indirizzi privati/riservati, chiudendo il bypass via reindirizzamento (adottato in crawler, recupero titoli e fetch secondari di sitemap/robots) - Validazione URL con risoluzione DNS degli indirizzi effettivi (anti DNS-rebinding)
Qualità
- Esclusione dei worktree interni dalla suite di test (rimossi i duplicati che gonfiavano il conteggio)
- type-check e lint puliti, suite di test verde
Sicurezza, affidabilità e tooling
Consolidamento tecnico a partire dall'analisi di codebase: autorizzazione per-oggetto, hardening SSRF, qualità del codice e pipeline di CI. Nessuna modifica funzionale per l'utente finale.
Sicurezza
- Autorizzazione a livello di oggetto (IDOR): nuovo helper centralizzato
assertCanAccess{Project,Round,Component}con regola di visibilità fail-closed (nessuna sessione ⇒ nessun dato), applicato alla rotta/api/components; predicato puro coperto da test - SSRF: la validazione URL ora risolve il DNS e blocca gli indirizzi privati/riservati effettivi (protezione anti DNS-rebinding) su renderer, IBM engine, crawler, snippet e
fetch-title - Estensione Chrome: confronto della API key a tempo costante (
timingSafeEqual), helper condiviso al posto di tre implementazioni duplicate - Rate limiting esteso agli endpoint costosi (crawler, chatbot, bonifica PDF) con helper riusabile
- Security headers HTTP su tutte le risposte (
X-Content-Type-Options,X-Frame-Options,Referrer-Policy,Permissions-Policy, HSTS) - Parsing difensivo dell'output AI (guardie su item nulli/non-oggetto, coercizione dei campi)
Qualità e operatività
- CI GitHub Actions: type-check, test e lint bloccante a ogni push/PR
- ESLint (flat config + regole
jsx-a11y/ Next / react-hooks): adozione completa con 0 errori; rimozione di codice morto (funzioni, variabili e import inutilizzati in ~20 file) - Logger strutturato JSON con contesto (
auditRoundId/componentId) per la diagnosi in produzione - Prompt caching sul system prompt del chatbot (riduzione costi e latenza)
- Listino prezzi modelli AI aggiornato alla tariffazione Claude corrente; allineamento della versione in
package.json - README di progetto e diagramma ER dello schema database
Valida Design System — adozione completa interfaccia
Migrazione integrale di tutte le pagine e componenti dal markup Tailwind custom al Valida Design System A11y Edition — token CSS, classi semantiche, paletta navy/leaf/ivory con contrasto WCAG AA garantito.
Layout e pagine
- Login (
LoginClient): split-screenlogin-shell/login-brand/login-form-col; classifield,input-wrap,btn--accent btn--lg btn--block; brand panel con dark theme accessibile - Home / Dashboard (
DashboardClient): layouthome-summary+home-grid/home-aside; sezionihome-panelper valutazioni attive e feedback;portfolio-rowconConformityBar+ punteggipp-val; collassabilehome-libs - Nuova valutazione (
NuovaValutazioneClient):SelectCardseSettingCardsDS; pannellinote-panelcon toggle - Nuovo progetto (
NuovoProgettoClient):fieldset/fieldset--agid,radio/dot,config-card,action-bar; estratto helpergetUrlPlaceholderper ridurre complessità ciclomatica - Panoramica round (
ValutazioneClient):audit-head+meta-pills, tablink-basedconusePathname,conf-grid(3 pannelli),coherencepanel,effort-row,table-wrappagine conConformityBar - Consumi (
ConsumiClient): grafici a barre CSS-only al posto di Recharts;filter-bar, tabelle breakdown DS; log denso; fix linter (Readonly props, chiavi stabili, nessunrole=img) - Chatbot (
ChatbotClient): layoutcbot-*completo — thread ivory, header navy, pulsante send leaf; brand markValidaMarkSVGin header, avatar, stato vuoto e launcher; animazione typing dots - Storico / Confronta (
StoricoClient/ConfrontaClient):vcheckcheckbox DS, barretrend-row/tbar, tabelletbl-*, dialog modale DS;ConfrontaClientricostruito conKpiCardsestratti,cmp-head/cmp-summary/cmp-section; rimosso breadcrumb ridondante
Componenti
LibraryCard/LibraryVersionsBlock: ricostruiti con classilib-item,libs-card-head,libs-gridProjectSubNav: tab DS.tabs.tabs--pill; aggiunto tab Riepilogo →/progetti/[id]; rimossi Tailwind underline-tabsBrowserMatrix: allineamento DS; classebstatus--divaggiunta aglobals.csscon contrasto migliorato
Token e CSS globali
- Palette CSS vars: aggiunta scala completa (
--navy-900,--leaf-500, ecc.) a:rootinglobals.css— fix critico che rendeva trasparentibtn--primary, barre conformità e icone pannello - Testo: scala tipografica portata a 16 px base (
text-sm → 16px) per edizione accessibilità - Animazioni: aggiunte
@keyframes spin/pulse; fixvc-check color: transparentsu stato non selezionato (contrasto WCAG 1.4.11) - Token colore: sostituzione sistematica di tutti i token
blue-*Tailwind connavy/leafDS — FAB, chatbot, cookie policy, ruoli, admin, pagine componenti
Criteri esclusivi UNI EN 301 549 — visibilità FPS
- Pannello round summary (
ValutazioneClient): nuovo pannello espandibile "Criteri esclusivi — Functional Performance Statements (Cap. 4)" sotto le score card; mostra stato worst-case per criterio 4.2.1–4.2.8 con breakdown per componente espandibile; badge rosso sull'header se presenti criteri non superati - Tab dettaglio componente (
DettaglioClient): nuovo tab "EN 301 549 esclusivi" accanto a "Criteri WCAG 2.2"; mostra i criteri 4.2.x per il componente con stato, problemi e raccomandazioni;●rosso sul tab se presenti non-conformità - Bug fix:
DettaglioClientscartava silenziosamente i criteri conprinciple='EN301549'perché non inclusi nelprincipleOrderarray — corretto - Utility:
src/lib/en301549-utils.tsconfilterEnOnly,worstCaseStatus,groupByCode— 12 test vitest
W3C Validator — display avanzato + fix WCAG H58 BCP47
W3C Validator — pannello grouped per consultazione
W3cGroupedPanel(nuovo componente): nella vista consultazione (/dettaglio/) gli issues del validatore W3C sono ora raggruppati per gravità di impatto sull'accessibilità — ARIA non validi (alto), ID duplicati (alto), Lingua non valida (alto), Struttura heading (medio), Elementi sconosciuti (medio), Elementi obsoleti (basso), Avvisi (info). I gruppi ad alta priorità si aprono automaticamente; tutti gli altri partono collassatigroup-utils.ts: nuove utility purecategorizeW3cIssues(issues)eformatPosition(firstLine, firstColumn)— 16 unit test copertura completa;formatPositionomette "riga 0" quandofirstLine === 0W3cValidatorPanel(fix): rimosso il limite fisso a 20 issues — compare il bottone "Mostra tutti gli altri N →"; corretta la visualizzazione della posizione (non mostra più "riga 0" per issues senza riga specifica)- Integrazione nel
DettaglioClientper i criteri 4.1.1, 4.1.2 e 1.3.1
Fix WCAG H58 — validazione BCP47 lingua
checkLanguageOfParts(H58): aggiunta validazione del formato BCP47 sull'attributolang; codici lingua non validi comelang="frrr"(4 lettere, non ISO 639) vengono ora correttamente segnalati come fallimento con evidenza del codice lingua errato; prima passavano erroneamente il check
Passing Evidence — layout uniforme
PassingEvidencePanel: layout allineato al blocco Note — sfondowarm-50, bordowarm-200, badge sorgente con colorigetBadgeClass, spaziatura uniforme; fix alle note di revisione del code reviewgetBadgeClass: esportata daformatters.tsxper riuso inPassingEvidencePanel
Fix agentico
- Live-region agent (4.1.3): prevenuto crash da errore di navigazione Playwright — l'agente continua la valutazione invece di interrompere il round
Formatter Note block — design uniforme
Formatter — blocco Note
formatNotes: nuovo layout uniforme per il blocco Note — stripping dei simboli di stato iniziali (✓ ✗ ⚠ ➖ ✕ e varianti emoji), header di riepilogo[Analisi statica:]reso come lista bullet context-sm, sotto-blocchi[Altri motori:]come sezioni separate con badge dedicatistripLeadingStatusSymbols: nuova utility (con test) che rimuove i simboli✓ ✗ ⚠ ➖ ✕ ⚠️dai valori testuali prima del rendering; supporta sia le varianti bare (U+2717, U+26A0) sia le varianti emoji (U+FE0F)getBadgeClass: estratto daformatters.tsxcome helper riutilizzabile; aggiunto badge W3C Validator nel registryparseAltrMotoriBlocks: pattern di split derivato daSOURCE_BADGE_LABELS(fonte unica di verità); filtro null prima di map per robustezza; rimosso flag/sper compatibilità ES2018
Docker
- sharp: aggiunto
PUPPETEER_SKIP_DOWNLOAD=1nello step di installazione di sharp per evitare download non necessario di Chromium
UNI EN 301 549 + Layer Best Practice + W3C HTML Validator
Due nuovi layer di conformità affiancano la valutazione WCAG 2.2 AA: il layer normativo UNI EN 301 549 (sempre attivo) e il layer best practice opzionale per round. Integrato anche il validatore W3C Nu HTML Checker come sidecar Docker.
Layer UNI EN 301 549 (sempre attivo)
- DB schema: tre colonne booleane su
wcag_criteria—is_wcag22(default 1),is_en301549(default 1),is_best_practice(default 0); colonnabest_practices_enabledsuvalutazioni(default 0) - Seed: 8 criteri EN-only (4.2.1–4.2.8, Functional Performance Statements) con
is_wcag22=0, is_en301549=1; 5 pseudo-criteri best practice (BP.contrast.enhanced,BP.heading.hierarchy,BP.link.descriptive,BP.form.autocomplete,BP.motion.reduced) conis_best_practice=1 - IBM engine: configurato con policy
EN_301_549(superset di WCAG 2.1 AA + regole EN extra); nuova costanteIBM_RULE_TO_BPche mappa le regole IBM a livelloRECOMMENDATIONai codiciBP.*;evaluateWithIbmrestituiscebestPracticeFindings: CriterionEvaluation[]separati dalle violazioni normative - Pipeline tecnica:
bestPracticesEnabledaggiunto aSettingsLike; ibestPracticeFindingsIBM vengono mergiati nel risultato del round solo se il flag è attivo - Score utility: nuova funzione
buildComplianceScores(results, bestPracticesEnabled)insrc/lib/compliance-scores.ts— restituisce{ wcag22, en301549, bp? }con contatori (passed / failed / toVerify / na / total / percentage) filtrati per colonna booleana - API dashboard:
GET /api/dashboardarricchito con il campocomplianceScoresper ogni round nella risposta (retrocompatibile) - Export APIs: parametro opzionale
standard: 'WCAG22' | 'EN301549' | 'all'su Excel, DOCX, PDF (defaultWCAG22per retrocompatibilità); parametroreferencedStandard: 'WCAG22' | 'EN301549'sull'export Allegato 2 AgID per generare la dichiarazione con lo standard corretto
UI — Score duali e badge standard
- Creazione round (
NuovaValutazioneClient): toggle "Includi raccomandazioni qualitative (non normative)" sotto la selezione modalità; nota informativa statica: "La valutazione include sempre WCAG 2.2 AA e UNI EN 301 549" - Panoramica round (
ValutazioneClient): due score card affiancate — WCAG 2.2 AA e UNI EN 301 549 — con percentuale, passed/failed/toVerify/N/D; terza card opzionale per Best Practice se il flag è attivo - Dettaglio componente (
ComponenteClient): badge WCAG (blu) e EN (verde) accanto al codice criterio in entrambe le viste (lista e wizard); criteri conis_best_practice=1esclusi dalla lista normativa e mostrati in sezione separata "Raccomandazioni qualitative" con disclaimer non-normativo - Dashboard (
DashboardClient): widget conformità mostra sia lo score WCAG 2.2 AA sia EN 301 549 per ogni round
W3C Nu HTML Checker (validatore markup)
- Sidecar Docker: servizio
w3c-validator(ghcr.io/validator/validator) su porta 8888 neldocker-compose.yml - DB: colonna
w3c_enabledsuvalutazioniper attivare il controllo per singolo round - Engine:
src/lib/w3c-engine/index.ts— invia l'HTML al servizio Nu Checker e mappa gli errori/warning ai criteri WCAG pertinenti (4.1.1 Parsing, 4.1.2 Nome/Ruolo/Valore) - Pipeline: integrato nello Step 1 (fase tecnica) dopo IBM; attivato solo se
w3cEnabled = truenel round - UI creazione round: toggle "Includi validazione W3C Nu Checker (markup HTML)" nella form di creazione
- Dettaglio componente: pannello
W3cValidatorPanelnel tab Errori con badge dedicato; colonna "W3C" nell'export Excel
Design System v1.0 — audit contrasto completo + fix infrastruttura
Conformità WCAG contrasto colori (Design System v1.0)
- globals.css:
--color-success-fgscurito da#3A8224a#2D641C; aggiunto token--color-border-input: #847B6A; focus ring globale*:focus-visibleaggiornato da#5FB237a#3A8224(contrasto 3:1 su sfondo bianco) - StatusBadge:
text-leaf-700 → text-leaf-800per stato "Superato";text-warm-600 → text-warm-700per stato "N/D" - EffortBadge:
text-leaf-700 → text-leaf-800per sforzo minimo (livello 1) - TopBar: rimossi icone dai voci di navigazione (riduzione ~80 righe); layout nav semplificato
- ValidaLogo: aggiunto
display: blockall'SVG; corretti spazi JSX (val→{'val'},da→{'da'}) - Propagazione
focus:ring-leaf-500 → focus:ring-leaf-700etext-leaf-600 → text-leaf-700/800su tutti i client: ImpostazioniClient, KnowledgeBaseClient, DashboardClient, ConsumiClient, ProfiloClient, EntiClient, ProgettoClient, PanoramicaClient, ValutazioneClient, QualitaClient, NuovaValutazioneClient, NuovoProgettoClient, RtdClient, PdfRemediationClient
Infrastruttura pipeline
- agenticModel — nuova impostazione
agentic_modelinevaluate/route.tseweb-evaluation.ts: consente di configurare un modello separato (es. Haiku) per la fase agentica, indipendente dal modello Claude usato per la fase AI standard. Default:claude-haiku-4-5-20251001 - Deduplicazione upsert in
persistence.ts: aggiunta deduplicazione percriterionIdprima dell'upsert batch inevaluation_snapshotseaudit_results, prevenendo l'erroreON CONFLICT DO UPDATE cannot affect row a second timequando agenti agentic condividono gli stessi criteri
Expert Feedback — 5 gap implementati (v1.59.0)
Implementazione dei gap identificati dal feedback degli esperti di accessibilità WCAG 2.2.
Motore statico
- Gap 1 —
input[type="image"]: aggiunto warning quandoaria-labelè presente maaltmanca (HTML spec compliance);alt=""non triggera warning (immagine decorativa valida) - Gap 2 — Tecnica H2_ADJ: nuovo check per link adiacenti con stesso
hrefdove l'immagine haaltridondante — suggeriscealt=""per evitare duplicazione del nome accessibile - Gap 3a — Tecnica C22: nuovo check statico per CSS inline (
color,background-color,border-color) su elementi semantici come potenziale vettore di informazione senza alternativa testuale (1.3.1)
Motore AI
- Gap 3b — Hint per 1.3.1: Claude verifica CSS inline informativo nel contesto semantico tramite
CRITERION_HINTSmap inai-engine/index.ts - Gap 4 — Hint per 1.2.4: Claude distingue contenuti live da contenuti registrati e restituisce N/D motivato se non ci sono stream in diretta
UI e report
- Gap 5a — ComponenteClient: pill "IBM: N occ." nel pannello dettaglio criterio, parsata dal campo
notesIBM - Gap 5c — Export Excel/PDF/DOCX: campo/colonna "Occorrenze" con conteggio IBM per criterio (
IBM: N (X viol., Y pot.))
Fase agentica v3 — Gruppo C: 3 nuovi agenti Playwright
Completa il motore agentico con i 3 agenti stub del Gruppo C. Tutti i 14 agenti del registry sono ora implementati; makeStub rimosso da index.ts.
focus-visible(WCAG 2.4.7 — Focus visibile): confronto before/after suoutline-width,outline-color,box-shadowper ogni elemento focusabile (max 30);outcome: truese almeno una proprietà cambia al focus oppureoutline-width > 0pxdopo il focus; delega valutazione narrativa a Claudehover-focus(WCAG 1.4.13 — Contenuto al focus/hover): verifica i 3 sub-criteri WCAG per ogni elemento trigger ([title],[aria-describedby],[data-tooltip],[data-tippy-content],[aria-expanded="false"]): appeared (DOM node count), hoverable (pointer sul tooltip), persistent (pointer move-out), dismissable (Escape); max 8 trigger per paginapause-stop-hide(WCAG 2.2.2 — Pausa, stop, nascondi): rileva video autoplay non-muted, audio autoplay, animazioni CSS infinite o > 5s, carousel (.carousel,[data-bs-ride],.swiper,.slick-slider,[data-glide]), marquee/ticker; per ogni elemento risale 3 livelli di parent cercando un controllo pause/stop (keyword: pausa/pause/stop/ferma/nascondi/hide); max 10 elementi per pagina
Design System v1.0 — propagazione completa (Phase 5 — Round 2)
Completamento della migrazione DS v1.0 alle sezioni rimanenti dell'applicazione: pagine di valutazione, area RTD, impostazioni, knowledge base e tutti i client secondari. Tutte le pagine dell'applicazione usano ora esclusivamente i token navy/warm/leaf/tomato/saffron.
Pagine valutazione e audit (19 file)
- ValutazioneClient: badge metodologia e browser, sezione coerenza cross-pagina, log viewer, modal eliminazione — tutto su palette DS; ternari orange
medium/lowsemplificati a binario dopo convergenza semantica - ComponenteClient: 174 occorrenze migrate — badge categoria IBM, badge sorgente, log level colors, status counters, conformity bar, KB panel, evidenze di conformità
- QualitaClient:
getGulpeaseColor,getClarityColor,STATUS_CONFIG,TYPE_CONFIG,LOG_LEVEL_COLORS,SortableHeader— tutti su DS - NuovaValutazioneClient: radio WCAG version, eval mode, browser toggle (
peer-checked:bg-navy-700), viewport chips teal→leaf, modal agenti - PagineClient: crawler results, type badge PDF/Web, form submit buttons, error/warning states
- DettaglioClient, ErroriClient, ConfrontaClient (storico), DivergenzeBrowserClient, ReportValutazioneClient, FlashingAnimationPanel
Sezioni app restanti (18 file)
- HelpValutazioneClient: badge color-map (amber/sky/emerald/rose/slate → DS), CodeBlock, skip link, sidebar navigazione, 120+ sostituzioni gray→warm
- RtdClient:
AgidStatusBadge,KpiCard,StatPill,DeclarationModal,RevisionModal— rose/amber/emerald/violet → DS - PdfRemediationClient:
STATUS_CONFIG, action buttons Gemini/download, accordion, reading-order confirm - ConsumiClient: KPI cards, colonne cache-read/write, type badge chatbot/indexing, tooltip — pink→navy, orange→saffron
- ValutazioniClient, ImpostazioniClient (con
inputClshelper), KbClient (conConfidenceBadge,TYPE_COLORS) - ProfiloClient, UtentiClient, EntiClient, KnowledgeBaseClient, NotificheImpostazioniClient
- FeedbackClient, EstensioneClient, KbInsightsPanel, AmbitiClient, LoginClient, ImportaClient
Design System v1.0 — propagazione completa (Phase 5)
Propagazione della palette Valida Design System v1.0 (Navy + Avorio + Leaf) a tutti i componenti condivisi e alle pagine principali dell'applicazione. Completa il ciclo di migrazione avviato con la TopBar e ProgettiClient in v1.54–v1.55.
Componenti badge e UI primitivi
- StatusBadge: Superato → leaf-50/leaf-700/leaf-300; Non superato → error-bg/error-fg/tomato-500; N/D → warm-100/warm-600/warm-300; Da verificare → warning-bg/warning-fg/saffron-500
- PriorityBadge: Critico/Alto → tomato-500/saffron-500; Medio → #E8A93C/navy-900; Basso → navy-100/navy-800; default → warm-200/warm-700
- EffortBadge: S → leaf; M → navy; L → warning hex; XL/XXL → error hex
- Toast: success → leaf-600; error → #D63E2A; info → navy-900
- Breadcrumb: testo e separatori migrati da slate a warm/navy
- ConfirmModal: backdrop navy-900/60; icona warning-bg/fg; pulsanti warm e saffron-500
- InfoBanner: da sky a navy (#EEF0F4 / navy-700)
- ProjectSubNav (tab orizzontale): active → navy-900/navy-50; inactive → warm-600/warm-50
Pagine principali
- DashboardClient: funzione
complianceColormigrata da emerald/amber/rose a leaf/saffron/tomato; tutti i badge e controlli aggiornati; pulse indicator leaf-400/500 - ProgettoClient: 123 sostituzioni — inputClass, labelClass, conformityConfig (CONFORME/PARZIALMENTE/NON/ND), modal eliminazione, metadata pills, radio
accent-leaf-600 - NotificationBell: pannello dark slate → light white/warm; badge contatore tomato-500
- NotificheClient: lista notifiche dark → white/warm
Utility e formattatori
- formatters.tsx: tutti i formattatori React migrati — badge severità (ERRORE/AVVISO), badge sorgente (Agentico/Gemini/Claude/Tecniche WCAG), codice blocchi dark → navy-800/900, link WCAG → navy-700, inline code → warm-100/#962819
- version-compare.ts:
badgeClassebadgeDotmigrati da emerald/amber/rose/slate a leaf/saffron/tomato/warm
Pagina Changelog
- Componente
src/app/changelog/page.tsxmigrato da slate-/blue- alla palette DS: h1–h3 navy-, paragrafi warm-, bullet navy-900, link navy-700/900
Fase agentica v2 — Gruppi A+B: 7 nuovi agenti Playwright
Estende la fase agentica (v1.54.0) con 7 nuovi agenti specializzati e un refactor architetturale del runner.
Architettura — page-per-agent isolation
runActiveAgentsriceve orabrowserinvece di una singola pagina condivisa: per ogni agente viene creata e chiusa una pagina Playwright dedicata, eliminando la state pollution tra agenti che modificano viewport o font-sizeAgentIdesteso da 12 a 14 valori: aggiunti'reflow'(1.4.10) e'resize-text'(1.4.4)RawEvidence.notes?: string— nuovo campo opzionale per trasportare dati strutturati (JSON) all'interprete Claude, usato dagli agenti con logica deterministica
Gruppo A — 4 nuovi agenti
skip-nav(WCAG 2.4.1 — Salto di blocchi): naviga con Tab, rileva skip link e verifica che l'attivazione sposti il focus nell'area principalelabel-in-name(WCAG 2.5.3 — Etichetta nel nome): per ogni elemento interattivo verifica che il testo visibile sia sottostringa del nome accessibile; Claude produce la narrazionereflow(WCAG 1.4.10 — Riadattamento): porta il viewport a 320×480 px e rileva overflow orizzontale, testi troncati con ellipsis, elementi fixed che coprono il contenutoerror-identification(WCAG 3.3.1/3.3.3 — Identificazione e suggerimento errori): sottomette ogni form senza compilarlo e verifica la presenza di messaggi di errore accessibili (aria-invalid, aria-errormessage, role=alert)
Gruppo B — 3 nuovi agenti
focus-order(WCAG 2.4.3 — Ordine del focus): registra la sequenza di focus (fino a 30 Tab) con coordinate Y e delega la valutazione della coerenza logica a Claudeon-focus-input(WCAG 3.2.1/3.2.2 — Al focus / All'input): confronta snapshot del contesto pagina (URL, title, dialog count, DOM children) prima e dopo ogni focus/input; soglie conservative per evitare falsi positivi su SPAresize-text(WCAG 1.4.4 — Ridimensionamento del testo): impostafontSize: 200%e rileva overflow orizzontale di testo, contenitori con altezza fissa che troncano il testo, elementi sovrapposti
Impostazioni
- La schermata Impostazioni > Motore Agenti lista ora 14 agenti configurabili (11 reali + 3 stub per il futuro Gruppo C)
Fase agentica v1 — 4 agenti pilota Playwright
Prima implementazione della fase agentica: agenti Playwright che simulano il comportamento reale di un utente con tecnologie assistive, raccogliendo evidenze comportamentali non rilevabili dai motori statici o AI.
Architettura del motore agentico
- Nuovo modulo
src/lib/agentic-engine/contypes.ts(AgentContext, AgentResult, RawEvidence),interpreter.ts(interpretWithClaude),index.ts(AGENT_REGISTRY, runActiveAgents, toEvaluationResult) - Ogni agente riceve una pagina Playwright già navigata, raccoglie
RawEvidence[]strutturata e delega la valutazione narrativa a Claude tramiteinterpretWithClaude src/lib/evaluation-pipeline/agentic-phase.ts— step Inngest dedicato; riceve la pagina renderizzata dal Technical step ed esegue gli agenti attivi in sequenza (timeout 10 min)evalModeesteso con routing verso la fase agentica quandoagenticEnabled = truenelle impostazioni del progetto
4 agenti pilota
keyboard(WCAG 2.1.1 — Funzionalità da tastiera): raccoglie fino a 40 elementi focalizzabili (a,button,input,select, ecc.) e verifica la raggiungibilità da tastiera tramite Tab/Shift-Tab; Claude valuta le evidenzeno-trap(WCAG 2.1.2 — Nessuna trappola tastiera): trova fino a 5 trigger modal/popup (aria-haspopup,aria-controls,aria-expanded), apre il componente, preme Escape e verifica che il focus sia uscito dal dialoglive-region(WCAG 4.1.3 — Messaggi di stato): rileva fino a 10 elementiaria-live/role=status|alert|log|progressbar, cattura il contenuto prima e dopo un'eventuale sottomissione form adiacente; Claude valuta la correttezza degli annunciaria-state(WCAG 4.1.2 — Nome, ruolo, valore): trova fino a 20 elementi conaria-expanded,aria-checked,aria-selected,aria-pressed, registra i valori prima dell'interazione, fa click, attende 200 ms e confronta
UI e integrazione
- Impostazioni → Motore Agenti: nuovo pannello per abilitare/disabilitare la fase agentica e selezionare i singoli agenti attivi per progetto
- Badge sorgente Agentico (viola) in
formatters.tsxper distinguere i risultati agentic dagli altri motori - Inngest pipeline estesa con step
agentic-phase(eseguito dopo Technical, prima di AI e Visual)
Fix — IBM false positive lang
- Corretto il regex
computeHtmlHasValidLanginibm-engine/index.ts:xml:lang=senzalang=non è più riconosciuto come attributo valido (HTML5 richiedelang=, nonxml:lang=) - Aggiornati i test in
ibm-false-positives.test.tsestatic-checks-v147.test.tsper il comportamento corretto
Miglioramenti display scheda dettaglio criterio e fix pipeline
Problemi — severity badge, accordion e separazione multi-tecnica
formatProblemsinsrc/lib/formatters.tsxdivide ora le stringhe multi-tecnica sul separatore newline: ogni tecnica è un blocco indipendente con badge colorato severità (ERRORE rosso / AVVISO arancio) estratto dal prefisso[ERRORE]/[AVVISO]- Liste di item lunghe (> 10 voci) sono troncate con accordion
<details>: il riepilogo mostra i primi 10 elementi e un link "Mostra altri N…" per espandere - Tecnica ARIA16_PREF: rimosso
—dal campodetailper evitare il rilevamento errato come pattern batch - Tecnica C27_SEQ: aggiunto
domSelectornel campodetailper consentire all'esperto di identificare l'elemento nel DOM - Tecnica F89 (link vuoti): aggiunto
hrefnel campodetailper distinguere le decine di violazioni strutturalmente identiche - Tecnica C12 (font-size px): rimosso il
;finale dal valore CSS nel campodetail - Tecnica 3.1.1 (lingua pagina): aggiunto fallback
xml:langper pagine XHTML che non usano l'attributolangstandard
Note — badge sorgente come pill, pulizia wrapper e footnote statica
formatNotesestrae il badge sorgente dal prefisso tra parentesi quadre — reso come pill colorata arrotondata (Tecniche WCAG,Claude,Gemini Vision) invece di testo grezzo tra parentesi- Stripping del wrapper esterno
[Valutazione automatica (...)]: la notazione interna della fase è nascosta, resta solo il badge sorgente visibile - Stripping dei token
[Q:N](punteggio qualità alt text Gemini): non vengono più mostrati come testo grezzo nel campo Note - I segmenti
[Analisi statica: ...]sono estratti e resi come footnote corsivo/grigio con bordo superiore separatore - Le etichette badge abbreviate:
"Claude (fase dedicata)"→"Claude","Gemini Vision (fase dedicata)"→"Gemini Vision" - Guard note vuote: se dopo aver estratto il badge non rimane contenuto,
formatNotesrestituiscenull—DettaglioClientnasconde l'intera sezione Note invece di mostrare un box vuoto con solo il badge
Tipografia uniforme su tutti e 4 i campi del criterio
PassingEvidencePanel:SourceBadgeora usa pill arrotondata (rounded-full) con colori slate/violet, identica ai badgeformatNotesEvidenceItemtesto detail:text-xs→text-sm leading-relaxed(14 px body allineato alle altre sezioni)- Spaziatura
EvidenceItem:py-2→py-2.5, containerpy-1→py-2 formatTextAsListeformatProblemItem: aggiuntotext-smesplicito su tutti i ramiDettaglioClient— heading sezioni h4 (Problemi, Raccomandazioni, Note): aggiuntotext-sm
IBM Equal Access — badge ID regola e filtro falsi positivi
parseProblemTextinsrc/lib/formatters.tsxestrae ora il prefisso[snake_case_id](nome regola IBM comehtml_lang_exists,skip_main_exists) in un camporuleIdformatProblemItemmostra l'ID regola IBM come badge monospace grigio muto separato dal testo del messaggio — non più incorporato nella stringatechnical-phase.ts: le valutazioni IBM con esitoNon superatovengono filtrate quando il motore Tecniche WCAG ha già determinatoSuperatoper lo stesso criterio — previene che check IBM su pagina renderizzata (prima dell'idratazione JS) sovrascrivano risultati statici corretti (es. 3.1.1 conlang="it")- Aggiunto test
src/__tests__/formatters-problem-item.test.tsper verificare l'estrazione delruleId - Rimosso il prefisso ridondante
[Gemini Vision]nell'engine Gemini:persistence.tslo aggiunge già automaticamente
Dashboard — veraPDF: versione robusta per immagini container datate
getVeraPdfInstalledVersion()incheck-library-versions/route.tsinclude ora l'headerAccept: application/jsonnella GET/api- Validazione del
Content-Typeprima del parsing JSON: evitaSyntaxErrorsu immagini veraPDF REST più vecchie che restituiscono HTML non-JSON all'endpoint/api - Campo
debugopzionale nella risposta API: espone il motivo esatto del fallimento (HTTP non-200, risposta non-JSON, campoversionassente, eccezione) — visibile in DevTools Network senza accesso ai log server - La variabile d'ambiente
VERAPDF_REST_VERSION(impostata indocker-compose.yml) resta il fallback; l'aggiornamento in produzione richiededocker compose up --build -d
Analisi chatbot — filtro intervallo date inclusivo
analyzeChatbotConversationsinsrc/inngest/functions/analyzeChatbot.ts: il filtroltesulla data di fine usa oradateTo + 'T23:59:59.999Z'invece del solodateTo(che corrispondeva a mezzanotte)- In precedenza, le sessioni create nel giorno finale dell'intervallo selezionato venivano escluse dal conteggio e dall'analisi
Fix — Formattazione problemi, deduplicazione snippet e F89/F77 rilevamento link
formatProblems — pattern batch "intestazione — item; item; …"
formatProblemsinsrc/lib/formatters.tsxgestisce ora il pattern prodotto dal motore tecniche WCAG:"N link con testo generico — Testo link generico: "…"; Testo link generico: "…"; …"- Gli item identici vengono raggruppati con badge
×N(rosso): la lista non mostra duplicati ma la loro frequenza - Guard
!/<[a-zA-Z]/.test(body): i corpi che contengono HTML grezzo non vengono mai spezzati su;— il check evita di trattare snippet HTML come lista di item - Fix: uso di
lastIndexOf(' — ')invece diindexOfper trovare il separatore corretto tra messaggio ed evidenze — le tecniche C12 (font-size px) e simili includono—nel messaggio stesso ("27 dichiarazioni — preferire em/rem — item1; item2; …"), causando un split al posto sbagliato - Strip del
;finale da ogni item: le dichiarazioni CSS nel campodetailterminano con;che veniva preservato nell'output - Stesso fix in
countProblemsper il conteggio occorrenze nel badge dell'intestazione riga
Deduplicazione snippet nel report sorgente (techniqueResultsToEvaluations)
- Snippet nel campo
sourceCodevenivano mostrati due volte se la stessa evidenza esisteva con e senza prefisso[Riga N](prodotto dabuildSnippetquandosourceCodeLocationInfoè attivo) - Fix in
wcag-techniques/index.ts: il confronto per deduplicazione ora normalizza il corpo dello snippet rimuovendo^\[~?Riga\s+\d+\]\s*prima di inserirlo nelSet; viene preservata la versione con prefisso quando disponibile
checkF89EmptyLinks — riduzione falsi positivi su SPA Vue/React
- I siti Vue/React con navigazione duplicata (desktop + mobile via
v-showo conditional rendering) producevano 2× violazioni per ogni link privo di testo accessibile: la stessa ancora era presente due volte nel DOM di Cheerio come nodi distinti - Fix: deduplicazione basata su chiave
href||outerHTML(spazi normalizzati): solo la prima occorrenza di ogni link strutturalmente identico viene contata come violazione - Migliorato il rilevamento
aria-hidden: il check usa oraclosest('[aria-hidden="true"]')invece di verificare il solo attributo diretto, escludendo correttamente i link annidati in subtree nascosti - Aggiunto skip per link con attributo
hiddenesplicito - La risoluzione di
aria-labelledbyrecupera il testo degli elementi referenziati nel DOM invece di verificare solo la presenza dell'attributo
Storico & Qualità — 4 feature indipendenti
Wizard filter "Da verificare" (UI)
- Aggiunto toggle a 2 stati nel header del wizard nel pannello componente: Tutti (N) e Da verificare (M)
- Il filtro si applica alla lista criteri del wizard: in modalità "Da verificare" vengono mostrati solo i criteri con status
Da verificare, aggiornando il contatore "Criterio X di Y" e la progress bar - Il pulsante "Da verificare" è disabilitato con tooltip quando nessun criterio ha tale status
- Entrambi i bottoni hanno
aria-pressedper accessibilità con screen reader - Lo stato del filtro si azzera automaticamente tornando alla vista lista
Punteggio qualità alt text (Gemini Vision)
- Gemini Vision include ora un punteggio 0–3 nel campo
notesper il criterio 1.1.1, con formato[Q:N]:0= alt assente o vuoto su immagine significativa1= alt generico non descrittivo (es. "logo", "immagine", "banner")2= alt presente ma migliorabile3= alt eccellente che descrive accuratamente il contenuto informativo
- Il punteggio è omesso se
status = N/D(nessuna immagine trovata) - Badge colorato mostrato accanto all'evidenza 1.1.1 nell'UI: rosso / arancio / ambra / verde
- Funzione
parseAltQualityScoreinsrc/lib/alt-quality.tsestrae il punteggio dalle notes; tipoAltQualityScore = 0 | 1 | 2 | 3
Vista aggregata divergenze cross-browser (UI + API)
- Nuova tab Divergenze nella navigazione di ogni round di valutazione
- Nuovo endpoint
GET /api/ax-tree-divergences?round_id=X: calcola il diff AXTree tra browser per ogni componente del round e restituisce i conteggi per coppia (Chromium↔Firefox, Chromium↔WebKit, Firefox↔WebKit) - La pagina
/valutazioni/[roundId]/divergenzemostra una tabella compatta con un link diretto al pannello componente per ciascuna riga con divergenze - Stato di caricamento skeleton, banner verde se nessuna divergenza, banner rosso in caso di errore di rete
- Solo i componenti con almeno 1 divergenza totale sono inclusi nella risposta API
Modalità Storico nell'AXTree viewer (UI + API)
- Nuovo terzo toggle Storico nell'
AXTreeVisualizer(accanto a Singolo e Diff ⚡) - Visibile solo quando esiste un round precedente con snapshot per lo stesso componente
- Layout side-by-side: sinistra = round N-1, destra = round corrente (entrambi Chromium; fallback al primo browser disponibile)
- La colorazione dei nodi riusa il motore
diffAXTreesesistente — nodi modificati, aggiunti o rimossi sono evidenziati in entrambi i pannelli - Banner ambra con etichette "Round N-1 → Round N"
- Il pulsante Storico rimane accessibile anche su componenti con un solo browser disponibile
- Nuovo endpoint
GET /api/ax-tree-snapshot/history?round_id=X&component_id=Y: trova il round precedente per lo stesso progetto tramiteround_number, restituisce{ current, previous, currentLabel, previousLabel }
Fix — Gemini Vision: falso positivo "Superato" su criterio 1.4.3
Bug 1 — Selettori elemento mai trasmessi a Gemini
- In
visual-phase.tsi selettori degli elementi problematici venivano estratti tramitetechniqueResultsToEvaluations(), funzione che non restituisce il campoelementSelectors. Il risultato era cheselectorsByCriterionera sempre vuota: Gemini non riceveva mai snippet a livello di elemento e valutava 1.4.3 unicamente dallo screenshot fullpage, dove le singole violazioni di contrasto non sono distinguibili alla scala ridotta - Fix: i selettori vengono ora estratti direttamente da
techResult.criterionResults[].techniques[].evidence[].element, che li contiene effettivamente
Bug 2 — Gemini force=true sovrascriveva "Non superato" con "Superato"
- Il risultato "Superato" di Gemini veniva applicato con
force=true, sovrascrivendo il "Non superato" della fase tecnica che aveva dati di contrasto misurati via Playwright (getComputedStyle). Gli screenshot dell'evidenza acquisiti duranteenrichEvaluationsrimanevano in DB, causando la contraddizione nell'UI: stato "Superato" con ritagli di elementi con contrasto insufficiente - Fix: i risultati Gemini "Non superato"/"Da verificare" usano
force=true(possono aggiungere evidenza visiva anche su criteri già superati); i risultati "Superato" usanoforce=false(non possono ribaltare un "Non superato" con dati misurati)
Bug 3 — Nessuna passingEvidence da Gemini su criteri superati
- Quando Gemini dichiarava un criterio "Superato", non veniva generata alcuna
passingEvidence. Il pannello evidenze mostrava sempre il messaggio di fallback "valutazione precedente all'aggiornamento", indipendentemente dall'età della valutazione - Fix:
parseGeminiResponseaggiunge un'entrysource: 'ai'con il ragionamento visivo di Gemini per ogni criterio dichiarato "Superato" - Il messaggio di fallback nel
PassingEvidencePanelè stato corretto rimuovendo l'attribuzione errata "precedente all'aggiornamento"
Nuovi check — Chiusura gap vs axe/Lighthouse
G18_HOVER — Contrasto in stato hover (1.4.1)
- Nuovo collettore Playwright
collectHoverFocusContrast: individua fino a 10 elementi interattivi (a[href],button,[role="button"]) visibili, li porta in stato hover viapage.hover()e misura il contrasto del testo congetComputedStyle - Nuovo rendered check
checkHoverFocusContrast(tecnicaG18_HOVER): segnalafailse uno o più elementi hanno contrasto insufficiente in hover (< 4.5:1 testo normale, < 3:1 testo grande);passse nessun problema;cannot-tellse i dati non sono disponibili - Colma il gap: scanner statici come axe non rilevano variazioni di contrasto negli stati hover/focus (citato nell'articolo davidmello.com)
- Wiring in
runAllRenderedChecks,technique-map.ts(criterio 1.4.1), e collettore incollect-rendered-context.ts
ARIA16_PREF — Preferenza aria-labelledby (1.3.1, advisory)
- Nuovo static check
checkAriaLabelPreference(tecnicaARIA16_PREF): segnalawarning(confidence 0.65) quando un elemento interattivo (button,a,input,select,textarea) usaaria-labelma ha già testo visibile interno ≥ 3 caratteri - Eccezione: bottoni icon-only (figli solo
svg/img) non vengono segnalati —aria-labelè l'unica opzione valida in quel caso - Motivazione:
aria-labelnon viene tradotto automaticamente e può divergere dal testo visibile;aria-labelledbyè preferito dal W3C per riutilizzare il testo DOM - Registrato come
advisoryintechnique-map.ts(criterio 1.3.1)
G83_PROX — Prossimità messaggi errore (3.3.1)
- Nuovo static check
checkErrorProximity(tecnicaG83_PROX): per ogni campo conaria-errormessage, verifica che l'elemento referenziato sia entro 2 livelli di antenato del campo (fratello diretto, figlio diretto del parent, o figlio diretto del nonno) - Segnala
fail(confidence 0.85) se il messaggio errore esiste ma è distante nel DOM — condizione non rilevata dacheckARIA21(che verifica solo l'esistenza dell'id) not-applicablese nessun campo usaaria-errormessage;passse tutti i messaggi sono adiacenti- Registrato in
technique-map.ts(criterio 3.3.1)
Nuovi check — Copertura gap WCAG
H44_PH — Placeholder come unica etichetta (3.3.2)
- Il check
H44_PHincheckFormFieldLabelscopriva soloinput[type="password"]einput[type="search"]. Espanso a tutti i tipi testuali:text,email,tel,url,number,search,passwordetextarea - Rileva i campi che hanno
placeholdercome unica fonte di nome accessibile (source =placeholdernell'algoritmo W3C) e segnala il problema con evidenza dettagliata - Nessun falso positivo su campi con label,
aria-label,aria-labelledbyotitlecorretti - Tecnica
H44_PHregistrata intechnique-map.tssotto il criterio 3.3.2
ARIA5_toggle — Stato ARIA dei toggle (4.1.2)
- Nuovo collettore Playwright
collectToggleStateData: individua fino a 8 elementi[aria-pressed]e[role="switch"]visibili, li clicca uno alla volta e verifica che l'attributo ARIA cambi dopo l'interazione - Nuovo rendered check
checkToggleState(tecnicaARIA5_toggle): segnalafailse uno o più toggle non aggiornano lo stato (aria-pressed/aria-checked) dopo il click;passse tutti lo aggiornano;not-applicablese non esistono toggle nella pagina - Preme Escape dopo ogni click per ripristinare lo stato della pagina (stesso pattern di
collectInteractiveSnapshots) - Wiring in
runAllRenderedChecksetechnique-map.ts(criterio 4.1.2)
C29_DM — Contrasto in dark mode (1.4.3, opt-in)
- Nuova impostazione globale Testa contrasto dark mode (flag
dark_mode_contrast) nelle Impostazioni di Valida - Quando abilitata, dopo la raccolta ordinaria del contrasto, il renderer emula
prefers-color-scheme: darkviapage.emulateMedia(), eseguecollectContrastDatain dark mode e ripristina il tema originale contry/finally - I dati dark mode vengono salvati nel campo
darkModeContrastDatadiRenderedContext - Nuovo rendered check
checkDarkModeContrast(tecnicaC29_DM): stessa logica dicheckColorContrastma sudarkModeContrastData; restituiscenot-applicablese il flag è disabilitato - Wiring completo:
renderOptions.darkModeContrastpassato attraversorenderPage,renderPageMulti,renderPageMultiRemotee i call site ditechnical-phase.ts - Nessuna dipendenza npm aggiuntiva
Allegato 2 AgID — Modello Ufficiale
- Riscrittura completa di
POST /api/export/agid-report: il generatore usa ora docxtemplater sul template DOCX ufficiale AgID (AGID-Modello-Autovalutazione-Template.docx) anziché costruire il documento da zero condocx - Mapping dei risultati WCAG ai segnaposto del template (
{s_X_X_X}/{n_X_X_X}per status e note per ogni criterio) - Metadata:
{prodotto},{data},{responsabile},{campione_web}popolati dai dati del round - Criteri EN 301 549 mobile: default
Non applicabile(fuori scope web) - Import aggiornati al protocollo
node:(node:child_process,node:util,node:fs) - Dipendenza
docxrimossa, sostituita dadocxtemplater+pizzip
UI — Pagina Esporta Report
- Pagina report ridisegnata con card per formato: Allegato 2 AgID riceve bordo blu e badge "Modello Ufficiale AgID", Excel ha badge XLSX verde, Guida alla compilazione ha badge "Generato con AI" viola
- Icone Lucide per ogni tipo di export (ShieldCheck, FileSpreadsheet, BookOpen)
- Barra di avanzamento animata durante la generazione
- Testo disambiguante sotto il titolo ("L'Allegato 2 è il documento richiesto da AgID per la conformità WCAG")
UI — Sezione Coerenza Cross-Pagina
- La sezione appare anche quando l'analisi è pulita (condizione
consistencyPageCount > 0anzichéconsistencyIssues.length > 0) - Stato "nessuna inconsistenza" con icona verde e conteggio pagine analizzate
- Pulsante Analizza ora con spinner per triggerare
POST /api/consistency/[roundId]/analyzeon-demand loadConsistencyIssuesrestituisce la Promise per concatenazione asincrona corretta
UI — Badge Confidenza nel Wizard Componente
- Badge
~XX%su ogni criterio con confidence < 0.85 nella vista wizard del componente - Colorazione contestuale: giallo (≥ 0.70), rosso (< 0.70)
UI — Score Regressione nel Confronto Round
- Visualizzazione di
weightedDivergenceScorenella card "criteri peggiorati" del confronto tra round (calcolato come somma delle confidence pesate dei nuovi errori)
Help — Sezione AI
- Aggiunta nota sul context strutturato per criterio: per i criteri AI-only l'HTML grezzo è filtrato ai soli elementi rilevanti con riduzione stimata 60–80% dei token
Evoluzione A — ARIA Completeness Map
- Nuovo file
aria-spec.tsconARIA_ROLE_MAPper 19 ruoli ARIA 1.2 (requiredProps, supportedProps, prohibitedProps, requiredContext) - Nuovo check
checkAriaRoleProps(ARIA5_PROPS): rileva violazioni di prohibitedProps e requiredContext checkCustomControlsStateecheckAriaAttributeValidityrefactored per usareARIA_ROLE_MAP
Evoluzione B — Confidence Scoring
- Campo
confidence: number(0.0–1.0, default 1.0) aggiunto aTechniqueResult - Helper
result()aggiornati in tutti i moduli check con parametro opzionaleconfidence - I 9 check v1.47.0 ricevono confidence esplicita (0.55–0.98)
- Badge confidence visibile in UI per check con confidence < 0.85
- Peso confidence integrato in
/api/valutazioni/confrontaperweightedDivergenceScore
Evoluzione C — Prompt Engineering
- Nuovo file
criterion-context.tscon 7 estrattori per criteri AI-only (media, interactive, motion, navigation, ui-components, form, status) ai-phase.tsusabuildEnrichedPrompt()con context strutturato per criterio — riduzione stimata 60-80% dei token per criteri con estrattore- Fallback a HTML completo (8000 char) per criteri senza estrattore
Evoluzione D — Cross-Page Consistency Engine
- Nuova tabella DB
page_snapshotscon fingerprint nav/UI/help per ogni pagina valutata snapshot-extractor.tsintegrato intechnical-phase.ts(fire-and-forget)consistency-phase.ts: check 3.2.3 (nav), 3.2.4 (UI), 3.2.6 (help) cross-pagina- Nuovi endpoint:
GET /api/consistency/[roundId],POST /api/consistency/[roundId]/analyze - Nuova Inngest function
consistency-analysistriggerata daround/all-pages-completed - UI: sezione "Coerenza Cross-Pagina" nella pagina round
Refactoring
- Suddiviso
static-checks.ts(1965 righe) in 6 moduli tematici sottochecks/static/:media.ts,links.ts,forms.ts,aria.ts,structure.ts,interactions.ts. Backward-compatible tramiteexport *.
Nuovi check statici
checkVideoCaption(G87) — rileva video senza<track kind="captions|subtitles">· 1.2.2, 1.2.4checkLinkPurpose(H30_LP) — rileva testo link generico (clicca qui, leggi di più…) · 2.4.4, 2.4.9checkAriaAttributeValidity(ARIA5_VAL) — rileva attributiaria-*non validi o con valore errato · 4.1.2checkCustomControlsState(ARIA4_STATE) — verifica stati ARIA obbligatori su controlli custom (slider, combobox, checkbox, ecc.) · 4.1.2checkBCP47Lang(H57_BCP47) — verifica chelangsu<html>e elementi sia BCP47 valido · 3.1.1checkConsistentHelp(G224_HELP) — rileva meccanismi di aiuto (link contatti/FAQ/form supporto) con posizione · 3.2.6
Nuovi check rendered (Playwright)
checkMotionActuation(G213_MOT) — rileva listenerdeviceorientation/devicemotionsenza alternativa UI · 2.5.4checkPointerCancellation(G210_PC) — rileva elementi con down-event senza cancel handler · 2.5.2checkFocusManagement(SCR26) — verifica struttura dialog e spostamento focus all'apertura · 2.4.3
Miglioramenti
checkFormFieldLabels: rilevapassword/searchcon soloplaceholdercome etichettacollectContrastData: soglia large text corretta — bold 18.67px usa threshold 3.0:1 (non 4.5:1)checkARIA22_F103: rileva possibili messaggi di errore form senza ARIA live region
Aggiunto
- 11 nuovi check statici WCAG —
checkHeadingHierarchy(G130, criteri 1.3.1/2.4.6): verifica livelli saltati, heading vuoti, h1 multipli;checkDuplicateIds(H93, 4.1.1): ID duplicati su elementi ARIA-referenziati;checkSvgAccessibility(ARIA6_SVG, 1.1.1/4.1.2): SVG senza alternativa testuale;checkAutocompleteValues(H98_VAL, 1.3.5): validità valori autocomplete vs lista WCAG;checkFormFieldLabels(H44_AN, 3.3.2): campi form senza nome accessibile via motore W3C;checkAltTextQuality(F30_Q, 1.1.1): alt generico o nome file;checkAudioVideoAutoplay(F93_AUTO, 1.4.2): autoplay senza muted;checkAriaRolesMisuse(F15_ROLE, 4.1.2): ruoli interattivi senza tabindex;checkPageTitleQuality(H25_Q, 2.4.2): titolo generico o troppo corto;checkG19esteso con avvisoG19_PRM(2.3.1): animazioni infinite senzaprefers-reduced-motionnella stylesheet;checkLanguageOfParts(H58_PARTS, 3.1.2): blocchi di testo in script non-Latino (arabo, cinese/giapponese/coreano, cirillico, devanagari, greco, ebraico, thai) privi di attributolang - 6 nuovi check rendered WCAG (Playwright) —
checkReflow320(C31, 1.4.10): scroll orizzontale a 320px;checkKeyboardTrap(F10_TRAP, 2.1.2): focus bloccato durante navigazione Tab;checkSkipLinkFunctional(G1_R, 2.4.1): link di salto verificati funzionalmente;checkColorContrast(G18_CR, 1.4.3): rapporto contrasto testo con formula WCAG inline;checkNonTextContrast(G207_CR, 1.4.11): contrasto bordo/sfondo componenti UI;checkMeaningfulSequence(C27_SEQ, 1.3.2): ordine DOM degli elementi interattivi confrontato con ordine visivo misurato a runtime - Soppressione falsi positivi IBM —
aria_label_misusesu elementi landmark einput_label_existsquandoaria-labelledbyè già presente vengono filtrati prima della fusione dei risultati
Tecnico
- Aggiunte interfacce TypeScript
SequenceIssue,SequenceData,ReflowData,KeyboardTrapData,SkipLinkData,ContrastItem,NonTextContrastItemintypes.tscon relativi campi inRenderedContext - 9 nuovi collettori Playwright in
collect-rendered-context.ts: contrasto testo, contrasto non-testuale e ordine sequenza in parallelo; reflow, keyboard trap e skip link in sequenziale (modificano stato pagina) - Test suite estesa da 247 a 256 test (29 file)
Aggiunto
- G19 — rilevamento animazioni rapide per il criterio 2.3.1 (Three Flashes) — il checker
checkG19è stato riscritto per estrarre ogni regola CSS conanimationoanimation-durationdalla pagina e produrre un'evidence strutturata per ogni selettore con durata < 300 ms; ogni elemento dell'evidence contiene il selettore CSS, il nome dell'animazione, la durata in ms e il frammento di regola CSS originale; accanto alla fase statica (Cheerio), la fase di rendering Playwright raccoglie concollectFlashingAnimationsgli elementi DOM effettivamente animati al momento del caricamento (fino a 500 elementi visibili) — il risultato è serializzato come JSON nel camposourceCodedel criterio 2.3.1 tramitepatchG19SourceCode, che muta l'array di valutazioni in-place dopotechniqueResultsToEvaluations; gli elementi DOM sono marcati con prefisso[DOM]per distinguerli dagli item statici - FlashingAnimationPanel — pannello UI con card espandibili per il criterio 2.3.1 — nuovo componente
FlashingAnimationPanelinComponenteClient.tsx: mostra una card espandibile per ogni animazione rapida rilevata; l'intestazione visualizza il selettore CSS dell'animazione (o il selettore DOM se la regola CSS non è disponibile), un badge con la durata in ms e, su una seconda riga, il selettore DOM dell'elemento renderizzato corrispondente; espandendo la card è possibile leggere la regola CSS originale e il tag HTML dell'elemento; le card CSS e DOM vengono fuse per nome di animazione quando quest'ultimo è diverso daunknown; il pannello è visibile solo per il criterio 2.3.1 e si nasconde automaticamente quando non ci sono evidenze
Corretto
- KB — campo "Note" non finisce più in "Ragionamento esperto" — risolto il bug per cui, alimentando la Knowledge Base dalla pagina di valutazione di un criterio, il contenuto del campo "Note" veniva erroneamente scritto nel campo
expert_reasoning(visualizzato come "Ragionamento esperto" nella KB); causa: inlearnFromCorrectionsla costanteexpertReasoningera assegnata dacorrection.finalNotes || correction.finalProblems, quindi il testo libero delle note di valutazione finiva nel ragionamento KB; fix:expertReasoningviene ora popolato solo da un valore esplicitamente fornito dall'esperto (correction.expertReasoning), altrimenti rimanenull; corretto anche l'ordine di priorità inbuildRuleDescriptionche ora preferiscefinalProblemsafinalNotesper il campo "Motivazione esperto" - API
PUT /api/results/[id]—expertReasoningestratto prima del salvataggio DB — il campo non è una colonna diaudit_results; viene ora separato dal body prima dell'UPDATEe passato direttamente alearnFromSingleResult, evitando errori Drizzle su colonne sconosciute
Aggiunto
- Campo "Ragionamento esperto per KB" nella pagina di valutazione — nuovo campo opzionale con stile emerald (visivamente distinto dal campo "Note") disponibile in entrambe le view (wizard e lista) della pagina componente (
ComponenteClient.tsx); quando l'esperto corregge lo stato di un criterio può compilare separatamente il ragionamento — che verrà salvato nella KB comeexpert_reasoning— e le note di valutazione, che rimangono nel risultato di audit; il campo mostra un helper text esplicativo e viene inviato all'API solo al salvataggio, senza essere scritto nella tabellaaudit_results
Aggiunto
- H24 — checker statico per
<area>image map senza alt — implementata la tecnica WCAG H24 insrc/lib/wcag-techniques/checks/static-checks.ts(checkH24_AreaAlt): verifica che ogni elemento<area href>in una image map abbia un attributoaltdescrittivo (non assente e non vuoto); gli elementi<area>privi dihrefsono ignorati in quanto non interattivi; quando la pagina non contiene alcuna image map il checker restituiscenot-applicablecoerentemente con gli altri checker statici; aggiunto arunAllStaticCheckse mappato ai criteri 1.1.1 e 2.4.4 intechnique-map.ts; copertura test: 6 casi (pass, fail-alt-mancante, fail-alt-vuoto, no-href, nessuna-image-map, violazioni-multiple) - TextSpacingPanel — verifica visiva WCAG 1.4.12 — nuovo componente
src/components/evaluation/TextSpacingPanel.tsxche affianca il checker staticocheckC8_C21_TextSpacingcon uno strumento di verifica interattiva per il criterio 1.4.12 (Spaziatura del testo); il pannello è mostrato inline nella pagina del componente per i risultati Non superato / Da verificare di 1.4.12 (solo componenti web, non PDF); funzionalità: toggle che applica i quattro valori minimi WCAG (line-height 1.5,letter-spacing 0.12em,word-spacing 0.16em,margin-bottom 2em) su testo demo locale; pulsante "Copia bookmarklet" che copia un JavaScript pronto all'uso per iniettare/rimuovere il CSS di test nella pagina reale (gestisce shadow DOM e iframe); cleanup corretto del timeout su smontaggio del componente tramiteuseRef
Aggiunto
- Motore Accessible Name — cascata W3C accname-1.1 a 9 passi — nuovo modulo
src/lib/wcag-techniques/accessible-name.tsche implementacomputeAccessibleNameeanalyzeAccessibleName: calcola il nome accessibile di un elemento HTML seguendo la specifica W3C Accessible Name and Description Computation 1.1 con traccia diagnostica passo-passo (1.aria-labelledby, 2.aria-label, 3.input[value], 4.label[for], 5. label ancestor, 6.legend, 7.title, 8.placeholder, 9. testo visibile); ogni passo restituisce{ step, source, value, pass }per consentire la visualizzazione della traccia nell'UI; il modulo è condiviso da tutti i checker che necessitano di calcolo del nome accessibile - Criterio 2.5.3 — estensione con F111 e F96 — il check per "Etichetta nel nome" ora valuta due failure WCAG distinti: F111 (nome accessibile completamente assente su controllo interattivo) e F96 (nome accessibile presente ma non contenente il testo visibile dell'etichetta); entrambi usano
analyzeAccessibleNameper la diagnosi; l'evidence WCAG è arricchita con il camponameTracecontenente la traccia completa della cascata di calcolo - Sectioning checker — verifica label su landmark multipli (1.3.1) — nuovo checker in
src/lib/a11y-engine/checkers/sectioning.tsche verifica la presenza di label distinti quando lo stesso tipo di landmark compare più volte nella pagina; la prima versione gestiscesectioneform(conaria-labeloaria-labelledby); esteso successivamente anav,aside,header,footerquando presenti in numero > 1; integrato nel motore statico e mappato al criterio 1.3.1 - Structural Map extractor — nuovo modulo
src/lib/a11y-engine/structural-map.tsche estrae due mappe dalla pagina: Landmark Map (conteggio e stato label per ogni tipo di landmark HTML5:main,nav,header,footer,aside,section,form) e Role ARIA Map (lista elementi conroleesplicito, conaccessibleNameenameSourcecalcolati viacomputeAccessibleName); endpointGET /api/components/[id]/structural-mapservito dasrc/app/api/components/[id]/structural-map/route.ts - StructuralMapPanel — pannello UI mappa strutturale — nuovo componente
src/components/evaluation/StructuralMapPanel.tsxcaricato on-demand nella pagina componente: presenta la Landmark Map in formato tabella con conteggio e indicatorehas_label; presenta la Role ARIA Map con colonna Nome accessibile che mostra il valore calcolato e un badge colorato per la sorgente (aria-labelledby,aria-label,label,title,placeholder,testo visibile); integrato inComponenteClient.tsx - NameTraceCard — traccia visiva del calcolo del nome accessibile — nuovo componente
src/components/evaluation/NameTraceCard.tsxche mostra la traccia diagnostica completa per ogni controllo interattivo con violazione 2.5.3: fino a 10 controlli per pagina, ciascuno con la lista dei 9 passi della cascata con indicatori pass/fail e il valore trovato; dati forniti dal nuovo endpointGET /api/components/[id]/name-analysis; integrato nella vista del risultato 2.5.3 inComponenteClient.tsxper criterion F111 e F96 - ARIA checker — refactoring su
computeAccessibleNamecentralizzato — il checker insrc/lib/a11y-engine/checkers/aria.tsè stato refactorizzato per usare il modulo condivisoaccessible-name.tsal posto della logica di calcolo del nome duplicata; eliminata la dipendenza dalla logica inline; tutti i test del checker passano dopo il refactoring
Corretto
- PDF Remediation — storage migrato su MinIO — i file PDF caricati vengono ora archiviati su MinIO (prefisso
pdfs/) anziché sul filesystem locale del container Docker; risolve gli errori ENOENT in produzione causati dalla perdita del volumea11y_datadurante i rebuild. La funzioneresolvePdfBuffergestisce la retrocompatibilità con i path legacy già presenti in DB - PDF Remediation — errore
JSON.parsesu risposta vuota — rimosso il campocreated_atdall'INSERT (usa il DEFAULTnow()della colonna);applied_atora viene impostato conNOW()SQL; aggiunto try/catch globale nel POST handler per garantire sempre una risposta JSON valida anche in caso di eccezione non gestita - PDF Remediation — analisi post-bonifica usa il PDF corretto — dopo l'applicazione delle correzioni, il campo
file_pathdel componente viene aggiornato al PDF bonificato su MinIO; le valutazioni successive leggono automaticamente la versione corretta senza richiedere un re-upload manuale
Migliorato
- Breadcrumb pagina Remediation PDF — il filo d'Arianna mostra ora la gerarchia completa:
Home > Progetti > [nome progetto] > Valutazione #N > [nome componente] > Remediation PDF, con link cliccabili per ogni livello; l'APIGET /api/components/[id]è stata arricchita con LEFT JOIN suvalutazionieprojectsper fornire il contesto necessario - Pulsante "Correggi" — rinominato il pulsante "Remediation" nella tabella dei componenti in "Correggi" per uniformità con l'interfaccia in italiano
Migliorato
- Qualità codice — remediation SonarQube 174 issue, rating affidabilità A (1.0) — sistemazione sistematica di tutte le issue di affidabilità software rilevate dalla nuova tassonomia SonarQube (dual taxonomy), portando il rating da B (2.0) ad A (1.0) con zero issue residue:
- S6671 — Promise rejection con tipo
unknown(6 issue): tutti icatch(e)nei handler di rejection ora wrappano il valore innew Error(String(e))garantendo type safety e logging corretto degli errori - S6843 — Ruolo ARIA ridondante (4 issue): rimossi i
role="cell"espliciti da elementi<td>che già possiedono il ruolo implicito di cella secondo le specifiche ARIA - S6842 — Listbox su elemento non interattivo (1 issue): rifatto il pattern listbox in
HelpValutazioneClientda strutturaul/liadiv/divper eliminare il conflitto con la semantica nativa di lista HTML - S6845 — Regione scrollabile non accessibile da tastiera (2 issue): aggiunti
role="region"etabIndexai div scrollabili inConfrontaClientper renderli navigabili da tastiera - S6853 — Label form senza testo accessibile (9 issue): aggiunti attributi
aria-labele patternsr-onlynelle label con markup complesso annidato inImpostazioniClient,NuovaValutazioneClientePagineClient; il patternsr-onlyfornisce il testo accessibile direttamente nel DOM mentre il contenuto visivo viene marcatoaria-hidden="true"per evitare duplicazioni da parte dei lettori di schermo - S6772 — Spazio ambiguo tra elementi JSX inline (46 issue): aggiunte espressioni
{' '}esplicite in 17 file di componenti React per garantire la corretta spaziatura nel rendering (senza{' '}, React può collassare gli spazi tra un tag di chiusura e il testo successivo, causando parole unite) - S7781 — Flag
/gridondante inreplaceAll()(56 issue): il metodoreplaceAll()sostituisce per definizione tutte le occorrenze; convertite le regex con flag globale a stringhe letterali per pattern semplici (es.'&','-') e a templateString.rawper sequenze di escape complesse (backslash, virgolette, parentesi) in 7 file; una chiamata.replace(/pattern/g, ...)convertita nel corretto.replaceAll()
- S6671 — Promise rejection con tipo
- Documentazione infrastruttura tecnica — aggiornato
docs/tech_stack_infrastructure.mdriallineandolo alle versioni da v1.22.0 a v1.38.0: tutte le nuove funzionalità (effort engine, CSS probe, chatbot analytics, PDF remediation estesa, dashboard live, estensione Chrome, galleria screenshot, AX-Tree, cross-browser testing) ora documentate con versione di introduzione
Aggiunto
- CSS Probe — analisi criteri CSS con JavaScript disabilitato — nuovo modulo
src/lib/renderer/css-probe.tsche valuta i criteri WCAG dipendenti dal CSS su componenti conrenderJs = false: durante la pipeline tecnica viene eseguito automaticamente un secondo rendering con CSS preservato e JavaScript disabilitato; i 10 criteri valutabili via CSS (focus style, target size, link stile, orientamento, hover/focus content, text spacing) vengono analizzati e il risultato integrato nella fase tecnica con failure cache in memoria (TTL 10 minuti) per evitare probe ripetuti; il nuovo endpointPOST /api/evaluate/css-probeconsente analisi on-demand con retry manuale che bypassa intenzionalmente la failure cache automatica, permettendo all'utente di ritentare sempre - CssProbeCard — widget UI per il CSS probe — nuovo componente
CssProbeCard.tsxnella vista componente: mostra lo stato del probe (non eseguito / in esecuzione / risultati / errore) con quattro stati espliciti, ARIA live region per annunci screen reader, AbortController per cancellare richieste in volo su smontaggio del componente, pulizia del timeout tramiteuseRefper prevenire memory leak e errori su componenti smontati; visibile solo per i componenti conrenderJs = falseche hanno criteri CSS applicabili
Corretto
- Bug MAJOR — precedenza operatori nel filtro suggerimenti AI — in
QualitaClient.tsxla logica di filtro usava?? 0 > 0invece di(?? 0) > 0; per le regole di precedenza JavaScript,>si lega più strettamente di??, portando al confronto0 > 0(sempre falso) anziché confrontare la lunghezza con zero; corretto aggiungendo le parentesi necessarie, specularmente al pattern corretto già presente nella riga immediatamente superiore - Accessibilità tastiera — 22 elementi interattivi senza handler keyboard (S1082) — 22 elementi con
onClickin 15 file erano privi dell'equivalenteonKeyDown, escludendo gli utenti che navigano da tastiera; aggiunta gestioneEnter/Spaceper controlli primari (accordion, pannelli screenshot) eEscapeper overlay/backdrop conaria-hidden="true"per rimuoverli dall'albero di accessibilità senza interrompere il flusso di navigazione; i file corretti includonoConfirmModal,NotificationBell,VisualComparisonTool,ScreenshotGallery,ScreenshotLightbox,TraversaliPanel,EntiClient,StoricoClient,ProgettiClient,ProgettoClient,ValutazioneClient,QualitaClient,RtdClient,ValutazioniClient,TopBar
Corretto
- Numerazione round duplicata — la creazione di una nuova valutazione usava
existing.length + 1come numero di round; se un round veniva eliminato il conteggio scendeva e il round successivo riceveva un numero già in uso. Il calcolo usa oraMAX(round_number) + 1, immune alle eliminazioni e alle race condition. Corretta anche la riga in DB conround_numberduplicato (round 8 → 9 per il progetto AlphaGov).
Migliorato
- Ordinamento valutazioni nel progetto — la lista delle valutazioni in
/progetti/[id]è ora restituita dall'API con ordinamentodata DESC, id DESC: i round più recenti (o quelli avviati per ultimi nella stessa giornata) appaiono in cima. L'ordinamento interattivo per colonna rimane disponibile nella UI.
Aggiunto
- Stima effort di correzione accessibilità (Fasi 1–3) — ogni violazione WCAG 2.2 riceve ora una stima dell'effort necessario alla correzione, su scala T-shirt (S/M/L/XL/XXL) con punteggio numerico 1–5 in DB:
- Fase 1 — Mapping statico — dizionario
WCAG_EFFORT_MAPinsrc/lib/effort-engine/effort-map.tsmappa 56 criteri WCAG 2.2 al loro effort base; i punteggi vengono calcolati e salvati ineffort_statical termine di ogni pipeline tecnica, in modo non bloccante (fire-and-forget) - Fase 2 — Analisi AI on-demand — nuovo endpoint
POST /api/results/[id]/effort-aiche invoca Claude (modello configurato inapp_settings) con il criterio, la descrizione del problema e lo snippet HTML; restituisce{ score, label, reasoning }e salva il reasoning ineffort_ai_reasoning; il punteggio statico non viene mai sovrascritto - Fase 3 — UI badge e controlli — componente
EffortBadge(S/M/L/XL/XXL, colori emerald/sky/amber/orange/red) con icona ✏ per override manuale e ✦ per stima AI; il badge appare nella riga di ogni criterio Non superato / Da verificare; nella vista espansa compare il pannello "Stima Effort di Correzione" con stima statica, pulsante "Analizza con AI" (spinner + accept/ignore), select per override manuale con salvataggio dedicato e pulsante "Ripristina auto" - Aggregazione per componente — pill
⊕ N pt · XLnell'header dei contatori diComponenteClient, visibile quando almeno un criterio ha un punteggio; formula: somma dei punteggi effettivi (override ?? static) con conversione totale → label (≤3: S, 4–7: M, 8–12: L, 13–20: XL, >20: XXL) - Aggregazione per round — card "Effort Totale Stimato" in
ValutazioneClientcon punti totali, badge aggregato e conteggio componenti/criteri coinvolti; visibile solo quando almeno un criterio ha un punteggio - Schema DB — tre nuove colonne nullable in
valutazione_results:effort_static INTEGER,effort_override INTEGER,effort_ai_reasoning TEXT; aggiunte via migrazioneALTER TABLE … ADD COLUMN IF NOT EXISTSnon distruttiva - API estesa —
GET /api/resultsrestituisce i campi effort;PUT /api/results/[id]accetta e validaeffortOverride(intero 1–5 o null)
- Fase 1 — Mapping statico — dizionario
Aggiunto
- Dashboard home — monitoraggio live — nuova sezione nella home che mostra in tempo reale le valutazioni in corso (polling ogni 5 s via
/api/dashboard/live): per ogni round attivo vengono visualizzati progetto, URL, percentuale di completamento e motori attivi; la sezione scompare automaticamente quando non ci sono valutazioni in esecuzione - Dashboard home — feedback recenti — blocco che elenca gli ultimi feedback degli utenti con data, progetto e messaggio; alimentato dall'endpoint
/api/dashboard/livegià presente per il monitoraggio live - Dashboard home — stato librerie — badge che indicano la versione installata di IBM Equal Access e veraPDF rispetto all'ultima disponibile; la verifica avviene tramite
/api/dashboard/check-library-versionscon logicacompareVersionsper distinguere aggiornata/obsoleta/sconosciuta - Analisi conversazioni chatbot — pagina amministrativa
/admin/chatbotcon metriche aggregate (totale sessioni, messaggi medi, argomenti più discussi, lacune nella KB, ora di picco) calcolate su un intervallo temporale selezionabile; i dati vengono prodotti daanalyzeChatbotConversations(funzione Inngest schedulata) e salvati nella tabellachatbot_analysis_snapshots - Endpoint
/api/chatbot/analyze— GET per leggere l'ultimo snapshot di analisi, POST per avviare un'analisi manuale ad hoc sull'intervallo specificato - PDF Remediation — tipo esteso —
RemediationProposalora include i campiheadings(array diHeadingFixconobjectId, livello 1–6 e testo) elanguageTags(array diLanguageTagFixconobjectIde codice BCP-47); il prompt AI è stato aggiornato di conseguenza eparseRemediationJsonè stata esportata con validazione dei livelli - PDF Remediation — contatori violazioni — le colonne
violations_beforeeviolations_aftertracciano le violazioni veraPDF prima e dopo la bonifica; dopo l'applicazione delle correzioni (endpoint/api/pdf-remediation/[taskId]/apply) viene eseguita automaticamente una seconda validazione veraPDF sul file prodotto per popolareviolations_after - PDF Remediation — analisi ordine di lettura — endpoint
POST /api/pdf-remediation/reading-orderche renderizza la prima pagina del PDF come immagine PNG (tramitepdfjs-dist+canvas) e, opzionalmente, interroga Gemini Vision per suggerire l'ordine di lettura corretto degli elementi - PDF Remediation — cronologia task — il GET
/api/pdf-remediation?history=truerestituisce gli ultimi 10 task di un componente con stato, date e contatori di violazioni per tenere traccia dell'evoluzione nel tempo
Migliorato
- PDF Remediation — UI accordion — il pannello di bonifica è stato ristrutturato con sezioni accordion collassabili (Metadati, Testi alternativi, Intestazioni, Tag lingua, Ordine di lettura); la sezione Ordine di lettura appare solo quando le violazioni contengono la clausola
09-003o la parola chiave "reading order" - PDF Remediation — score before/after — quando disponibili, vengono mostrati i contatori di violazioni prima e dopo la bonifica con la riduzione percentuale calcolata
- PDF Remediation — sezione storico — se esistono task precedenti per il componente, viene visualizzato uno storico collassabile con stato, data e variazione delle violazioni per ogni esecuzione passata
Migliorato
- Qualità del codice — remediation SonarQube (~355 issue, 104 file) — applicata la correzione sistematica di sei categorie di violazioni individuate da SonarQube:
- S6759 — Readonly props (81 issue): tutte le firme di componenti React ora ricevono le props avvolte con
Readonly<>, prevenendo mutazioni accidentali in fase di rendering - S7773 — Globale numerico (70 issue): sostituiti
parseInt,parseFloateisNaNglobali con i metodi del namespaceNumber(Number.parseInt,Number.parseFloat,Number.isNaN), eliminando ambiguità di tipo in coerenza con le linee guida ES2015+ - S6594 —
RegExpmatching (47 issue): convertiti i metodiString.match()in chiamate al metodotesto al metodo corrispondente diRegExpdove la semantica lo consente (escluse le regex con flag/gche richiedono il comportamento "find-all") - S7764 —
globalThis(45 issue): uniformato il riferimento all'oggetto globale dawindow/globalaglobalThis, garantendo compatibilità in ambienti Node, browser e worker senza guardtypeof - S1128 — Import inutilizzati (14 file): rimossi tutti gli import di tipo e valore che TypeScript non utilizza, riducendo il bundle e il rumore nelle revisioni
- S4325 — Type assertion ridondanti (~10 casi): rimossi i cast
as Xche non modificano il tipo effettivo dell'espressione - S7735 — Condizioni negate (48 issue): invertite le condizioni nei costrutti
if (!x) { A } else { B }e!x ? A : Bin modo che il ramo positivo appaia per primo; le catene di sort con!an/!bnriscritte in forma positiva eliminando anche l'assegnazione ridondante a0
- S6759 — Readonly props (81 issue): tutte le firme di componenti React ora ricevono le props avvolte con
Aggiunto
- Navigazione a tab Storico/Panoramica — le pagine
/storicoe/panoramicadi un progetto erano isolate: una volta entrati non era possibile passare all'altra senza tornare alla pagina progetto; aggiunto il componenteProjectSubNav(tab strip conusePathname()per evidenziare il tab attivo) integrato sia inStoricoClientsia inPanoramicaClient; il tab attivo è evidenziato con bordo blu inferiore, sfondoblue-50earia-current="page"per accessibilità - Route
/panoramica(rinomina da/dashboard) — la sub-route del progetto si chiamava/dashboardma il tab label era già "Panoramica"; creata la route/progetti/[id]/panoramicaconPanoramicaClient.tsx; il vecchio percorso/dashboardrimane invariato per retrocompatibilità - "Notifica Valutatore" spostato nel header CTA — il pulsante "Notifica Valutatore" era nella sezione navigazione in fondo ai KPI; spostato nella riga del breadcrumb accanto a "Nuova valutazione" (visibile solo agli utenti con ruolo RTD/admin quando
declarationUrlè configurato), rendendolo immediatamente accessibile senza scorrimento
Corretto
- Home — heading "Dashboard" rinominato "Home" — la pagina principale mostrava il titolo H1 "Dashboard" mentre il link nella barra di navigazione e i breadcrumb delle pagine interne usavano già "Home"; rinominato l'heading in
DashboardClient.tsxe il metadatatitleinpage.tsxper eliminare l'incoerenza terminologica segnalata nell'analisi euristica PVA (osservazione #5): "La 'Dashboard' generale diventa 'Home' nel filo di Arianna — userei lo stesso termine" - Nuovo progetto — InfoBanner aggiunto — il form di creazione progetto mancava del messaggio informativo iniziale presente negli altri form (es. Importa Excel); aggiunto
InfoBannerAGID-style (bordo sinistro azzurro, icona ℹ) che indica all'utente i campi obbligatori contrassegnati con asterisco rosso, in linea con l'osservazione PVA #3 sull'uniformità dei messaggi informativi - Nuovo progetto — pulsante "Crea progetto" disabilitato fino a compilazione campi obbligatori — il pulsante era sempre attivo (
disabled={saving}durante il solo salvataggio), permettendo il submit senza "Nome del sito / progetto"; aggiunto!name.trim()(eisMobile && !sistemaOperativoper i progetti mobile) alla condizionedisabled, rendendo il comportamento coerente con l'osservazione PVA #10: "il pulsante non si attiva fino a quando l'utente non inserisce i dati obbligatori"
Corretto
- FAB chatbot — sovrapposizione sul contenuto eliminata — il pulsante flotante del chatbot (posizionato
fixed bottom-6 right-6, larghezza 52 px + offset 24 px = 76 px dal bordo destro) si sovrapponeva visivamente a card dashboard, badge errori e link "Elimina pagine" perché<main>usavalg:px-8(32 px su entrambi i lati); modificatosrc/app/layout.tsx: padding destro aumentato alg:pr-20(80 px) per liberare la colonna FAB e aggiuntopb-20(80 px) per evitare che il FAB copra il contenuto nella parte inferiore della pagina /chatbot— pagina 404 sostituita con redirect — l'URL/chatbotrestituiva 404 se navigato direttamente dall'utente o dai bookmark; aggiuntosrc/app/chatbot/page.tsxche esegueredirect('/admin/knowledge-base')via Next.js server component- BrowserMatrix — stato vuoto quando scansione single-browser — quando
effectiveBrowsers.length < 2il componenteBrowserMatrixrestituivanullsilenziosamente; l'accordion "Matrice Cross-Browser" si apriva mostrando uno spazio vuoto; aggiunto fallback testuale inComponenteClient("Nessuna divergenza rilevata tra browser — la scansione ha usato un solo motore di rendering") visibile solo quando il pannello è espanso e il numero di browser è inferiore a due - KB Insights — distribuzione confidence mostra tutti i 5 bucket — la query SQL usava
GROUP BY bucketconCASE/WHEN, escludendo automaticamente i bucket senza entry (nessuna riga per count = 0); riscritta come CTEWITH buckets(bucket, lo, hi) AS (VALUES ...)conLEFT JOINsuknowledge_entries, garantendo che tutti i bucket da "0.5–0.6" a "0.9–1.0" siano sempre restituiti anche quando vuoti
Aggiunto
- Estensione Chrome — interfaccia popup ridisegnata — layout completamente rinnovato con design system Valida (colori primari blu/slate, tipografia Inter, bordi e shadow coerenti con l'app web); status badge reso accessibile con elemento
<span>semantico e ruolo esplicito; header con logo SVG ufficiale Valida; sezione "Come usare" con step numerati e icone; form di configurazione API key con helper text e indicatore di sicurezza; bottoni con stati hover/active/disabled espliciti; footer con link alla documentazione - Estensione Chrome — icone ufficiali — generate le icone
icon16.png,icon48.png,icon128.pngdal logo SVG Valida e aggiunte inchrome-extension/icons/; aggiornatomanifest.jsoncon i riferimenti corretti - Estensione Chrome — script di packaging — aggiunto
chrome-extension/pack.shper generarevalida-extension.zippulito (esclude.sh,.DS_Store, sorgenti SVG), pronto per Chrome Web Store
Corretto
- Documentazione guida valutazione — nome prodotto e motore browser —
HelpValutazioneClient.tsxriportava il nome legacy del prodotto e citava Puppeteer come motore di rendering; aggiornato il nome a "Valida" e corretta la documentazione tecnica per riflettere l'uso di Playwright (Chromium/Firefox/WebKit) invece di Puppeteer
Corretto
- Qualità — JSON grezzo a video / suggerimenti vuoti (fix definitivo a monte) — la causa radice del double-wrap ricorrente (Gemini inseriva l'intera risposta JSON come stringa dentro
paragraphSuggestions) era l'assenza di JSON mode: aggiuntoresponseMimeType: "application/json"conresponseSchemaesplicito (clarityScore,issues[],paragraphSuggestions[]) incallGemini; Gemini è ora costretto a produrre JSON strutturato senza wrapping o markdown fence; aumentatomaxOutputTokensda 2500 a 4096 per eliminare anche il troncamento che rendeva invalido il JSON interno; nel backendparseAiJsonil fence-stripping prima del check{è stato mantenuto come safety net; nel frontendnormalizeParagraphSuggestionsaggiunto cortocircuito[]per stringhe{-starting (JSON di oggetto), impedendo che JSON grezzo diventi una lista di bullet in qualsiasi futuro edge case
Aggiunto
- Scoring WCAG pesato per livello — il calcolo della percentuale di conformità nella dashboard (singolo progetto e vista globale) ora applica i pesi WCAG (livello A = 3, AA = 2, AAA = 1); il denominatore è la somma ponderata dei criteri applicabili, il numeratore la somma ponderata dei criteri superati; le violazioni di livello A hanno quindi impatto triplo rispetto al livello AAA; aggiornati
src/app/api/dashboard/project/[id]/route.ts(via SQL aggregation conCASE wc.level) esrc/app/api/dashboard/route.ts(via Drizzle join conwcagCriteriae reduce lato TypeScript) - Badge occorrenze su criteri con problemi — nella pagina di dettaglio del componente (
DettaglioClient) ogni criterio che presenta problemi rilevati mostra ora un badge pill rosso ({n} occorrenze) accanto al badge di stato; il conteggio è calcolato dacountProblems()informatters.tsxche replica la logica di parsing diformatTextAsList(bullet list → liste numerate → split tag motore → multilinea) restituendo un numero invece di ReactNode - Guida metodologica aggiornata — nuova sezione "Evidence Layer e Conformità Trasversale" con documentazione di Passing Evidence, storage asincrono screenshot su MinIO e gestione Problemi Trasversali; accordion "Knowledge Base injection" esteso con il ciclo di Continuous Learning (consolidazione → feedback loop → aggiornamento confidence in tempo reale); accordion "Playwright e rendering del DOM" riscritto per documentare la delega al microservizio isolato
renderer-service; aggiunta sezione "Strumenti di supporto alla valutazione manuale" con Simulatore di Dislessia ed Estensione Valida; titolo architettura rinominato da "Architettura Pipeline (v1.9.0)" a "Architettura a Fasi (Atomic Pipeline)"
Corretto
- Numero di riga mancante negli snippet IBM —
findLineNumberconfrontava gli snippet IBM (estratti dal DOM renderizzato da Playwright) con ilsourceHtmlgrezzo restituito dallafetch()diretta; quando ilsourceHtmlnon contiene lo snippet (es. elementi generati o modificati al runtime, o pagine dove il server restituisce HTML diverso da quello renderizzato), il match falliva elineNumrestituiva 0, sopprimendo il badge[Riga N]in tutti i problemi IBM; aggiunta cattura direnderedHtml = await page.content()subito dopo la scansione IBM, prima della chiusura della pagina Playwright; usato come fallback quandosourceHtmlnon contiene lo snippet; i numeri di riga da DOM renderizzato vengono marcati con[~Riga N]e visualizzati con badge ambra (bg-amber-500) con prefisso~e tooltip esplicativo, distinguendoli visivamente dai numeri di riga sorgente esatti (badge blubg-blue-600); aggiornate le funzioniformatProblemItem,formatSourceCodeeformatInlineWithLineRefsinformatters.tsxper riconoscere entrambi i pattern
Corretto
- Screenshot puntuali — mai catturati (bug critico) — la funzione
enrichEvaluationsveniva chiamata solo nel percorso unificato dihandleTechnicalPhase(riga 727), raggiungibile solo quandophaseèundefined; Inngest chiama sempre conphase='technical'che causa un return anticipato alla riga 477, prima cheenrichEvaluationsfosse raggiunta; risultato: 0 screenshot salvati in DB, nessun log[evidence]; aggiunta la stessa chiamata fire-and-forget nel bloccophase === 'technical'prima del return
Aggiunto
- Galleria screenshot con lightbox — nuovo componente
ScreenshotGalleryche sostituisce il rendering inline inDettaglioClienteComponenteClient; thumbnails con skeleton di caricamento, stato di errore, badge criterio WCAG e badge contatore; lightbox con navigazione frecce (←/→), tasto ESC, chiusura clic sfondo, contatore ritagli, link "Apri in nuova scheda"; accessibilità completa (role=dialog, aria-modal, focus-trap, keyboard navigation)
Aggiunto
- Estensione Chrome rinominata "Valida" — tutti i riferimenti a "A11y Snapshot" aggiornati in
manifest.json(name,short_name,default_title),popup.html(titolo e header),popup.js(commento header e log console),background.js,popup.css,setup.sh,EstensioneClient.tsx(UI web, istruzioni, nome file scaricatovalida-extension.zip) - Endpoint dedicati all'estensione — creati
GET /api/extension/projects(lista progetti{id, name}) eGET /api/extension/audit-rounds?project_id=X(round{id, roundNumber, date}per progetto), entrambi protetti daX-Extension-API-Key; il popup ora punta a questi endpoint invece di/api/projectse/api/audit-roundsche richiedevano sessione NextAuth - Script
pack.sh— nuovo scriptchrome-extension/pack.shper generarevalida-extension.zippulito (esclude.sh,.DS_Store, sorgenti SVG), pronto per Chrome Web Store o distribuzione standalone
Corretto
- Middleware — blocco 401 sulle richieste dell'estensione —
auth.config.tsnon includeva/api/extensiontra i percorsi pubblici; il middleware NextAuth bloccava le richieste dell'estensione prima che raggiungessero la logica di validazione dell'API key; aggiuntopath.startsWith('/api/extension')aisPublicPath
Corretto
- Knowledge Base: override deterministico cieco rimosso —
applyDeterministicOverridesabbinava le entry KB solo percriterionCode + originalStatus, forzando il cambio di stato su qualsiasi pagina che corrispondeva alla coppia senza considerare il contenuto HTML, il ragionamento dell'esperto né le condizioni specifiche che avevano motivato la correzione originale; la funzione è stata disabilitata dalla pipeline tecnica (technical-phase.ts) in attesa di un meccanismo di matching contestuale; l'import inutilizzato è stato rimosso. - Knowledge Base: prompt AI riformulato con linguaggio condizionale — l'intestazione del blocco KB iniettato nel prompt Claude recitava "Usale per evitare errori noti" (imperativo assoluto), portando il modello ad applicare le regole indipendentemente dal contesto della pagina corrente; riformulato con linguaggio esplicito: le indicazioni devono essere applicate solo se le stesse condizioni specifiche sono presenti nella pagina, altrimenti ignorate.
- Knowledge Base:
buildRuleDescriptioncattura la condizione, non solo la transizione — le descrizioni delle regole ora includono la condizione originale che ha scatenato la correzione (autoProblems) e la motivazione dell'esperto (finalNotes/finalProblems), con linguaggio "in passato, quando [condizione], l'esperto ha verificato che..."; limiteexpert_reasoningnel prompt portato da 120 a 300 caratteri; label rinominata da[NOTA ESPERTO]a[CONTESTO ESPERTO — condizione specifica].
Corretto
- Evidenze di conformità mancanti per criteri valutati dal motore statico — dopo il deploy di v1.23.0, i criteri come 3.1.1 (Lingua della pagina), 1.1.1 (Testo alternativo), 4.1.1 (Analisi) e altri valutati esclusivamente dal motore a11y-engine mostravano sempre il banner "Evidenze non disponibili" anche dopo la rivalutazione, perché: (1)
mapToWcagCriterianon popolava maipassingEvidenceper i criteri "Superato"; (2) il technique engine non ha implementazione per H57; (3) l'AI engine salta criteri già "Superato" per ridurre i token; aggiunta funzionebuildStaticPassingEvidenceinwcag-mapper.tsche genera un entry sintetico consource: 'static'per ogni criterio che supera la verifica automatica; aggiunto caso UI nelPassingEvidencePanelperentries.length === 0che mostra "Criterio verificato automaticamente — nessun elemento specifico individuato" invece del banner fuorviante "Rivalutare il componente"
Aggiunto
- Evidenze di conformità per criteri superati — nuovo campo
passing_evidence TEXTinvalutazione_resultsche memorizza un array JSONPassingEvidence[]per ogni criterio con esito "Superato"; il technique engine estrae le evidenze dalle tecniche conoutcome === 'pass'(elementi verificati, snippet HTML con numero di riga); il prompt AI è stato esteso per restituireevidence[]sugli stessi criteri; le due fonti vengono unite e deduplicate inmergeEvaluations(); il componentePassingEvidencePanelmostra in sola lettura la lista delle tecniche verificate (badge tecnica/static/AI, elemento, spiegazione, snippet collassabile) in entrambe le pagine di dettaglio (DettaglioClient) e valutazione (ComponenteClient, lista e modalità wizard); le valutazioni precedenti mostrano il banner "Evidenze di conformità non disponibili — rivalutare il componente per generarle"
Corretto
- IBM — falso positivo
skip_main_exists— aggiunto filtro pre-scan: se il raw HTML contiene un tag<main>, un attributorole="main", un elemento conidcontenenteskiposalta, oppure un link conhref="#..."il cui testo includeskip/salta, la regola viene soppressa; IBM talvolta non rileva skip link o landmark<main>presenti ma nascosti via CSS o renderizzati dinamicamente, generando falsi positivi su WCAG 2.4.1
Corretto
- Check rendered — snippet con numero di riga — i check basati su dati Puppeteer (
checkTargetSize,checkFocusVisible,checkFocusAppearance,checkLinkColor,checkDraggingMovements) mostravano nella sezione "Codice sorgente coinvolto" solo il selettore CSS grezzo (es.a.element-invisible) senza il markup HTML reale e senza l'etichetta[Riga N]; aggiunta funzione helpersnippetFromSelectorche usa l'istanza Cheerio per cercare il selettore nell'albero HTML statico: se trovato, restituiscebuildCheckerSnippet(el, $)con prefisso[Riga N]; se il selettore non è presente nel DOM statico (elemento iniettato via JS), il fallback è il selettore testuale originale
Corretto
- Suggerimenti del linguista — JSON grezzo a video (prima pagina) —
parseAiSuggestionsriassegnava erroneamenteparagraphSuggestionsal valore diouter.paragraphSuggestions(l'intera stringa JSON) come fallback quando l'LLM usava il "doppio wrapping"; ora il fallback è'', evitando chenormalizeParagraphSuggestionsspezzasse la stringa JSON per righe e la mostrasse come lista di testo - Suggerimenti del linguista — "Analisi AI non disponibile" sulle altre pagine — quando l'LLM avvolgeva l'intero payload in backtick Markdown (
```json ... ```), il primoJSON.parse(raw)falliva e la funzione restituivanull; aggiunta pulizia preventiva della stringarawprima del bloccotry, che rimuove eventuali code fence prima del parse
Corretto
- Screenshot puntuali — pipeline rotta in Docker —
captureElementSnippetschiamava Playwright localmente (getBrowserForType) ma il containera11y-appnon ha browser installati; aggiunto controlloRENDERER_URLche delega al renderer-service tramite il nuovo endpointPOST /capture-element-snippets; aggiunto il modulorenderer-service/src/snippets.tscon la logica di cattura elementi - Accessibility Tree — mai valorizzato — la funzione
walkNodeinrenderer/index.tserenderer-service/src/render.tsricorreva nei figli solo se l'elemento corrente era landmark/interattivo o depth < 3; per i siti con markup annidato (<div>→<div>→<main>) gli elementi significativi oltre depth 3 venivano ignorati; rimossa la condizione di guardia per ricorrere sempre fino al limite massimo (depth 15);pruneAXTreerestituisce ora alberi non nulli eax_tree_snapshotsviene salvato correttamente - Confronto visivo — slider/overlay non funzionanti in modalità responsiva —
sliderLeft/sliderRightvenivano inizializzati quandoscreenshots=[]e rimanevano su'chromium'/'firefox'; quando i 3 screenshot responsivi (chromium_desktop,chromium_mobile,chromium_tablet) caricavano asincronamente,screenshotMap.get('chromium')restituivaundefinede slider/overlay non renderizzavano nulla; aggiuntouseEffectche sincronizza le selezioni quando la propscreenshotscambia, con default intelligente desktop↔mobile
Aggiunto
- Confronto visivo — UX responsiva migliorata — badge viewport con dimensione (1280px / 768px / 375px), icone dispositivo, rilevamento automatico modalità responsiva con badge "Responsive" nell'header; tooltip informativo nella griglia; default automatico desktop vs mobile per il confronto diretto
Aggiunto
- Gemini Vision — retry con backoff esponenziale — aggiunta logica di retry interno nel motore Gemini: in caso di errori temporanei dell'API (HTTP 429, 503 o messaggi "high demand"/"quota"), il sistema attende automaticamente 5 s, 10 s e 20 s prima di ogni nuovo tentativo (fino a 3 retry); i micro-disservizi vengono assorbiti senza esporre Inngest ai retry incondizionati
Corretto
- Pipeline valutazione — blocco su errore Gemini Vision — la fase visiva restituisce ora HTTP 200 con avviso invece di HTTP 500 quando tutti i retry falliscono; Inngest prosegue con i risultati parziali (tecnici + AI) anziché mandare l'intera valutazione in stato
failedper un picco di traffico temporaneo
Corretto
- IBM — falso positivo
html_lang_valid— esteso il filtro post-scan ahtml_lang_validoltre ahtml_lang_exists; la regex di riconoscimento ora richiede un subtag primario di 2-3 lettere (es.it,en,zh) evitando la soppressione di codici genuinamente invalidi comenotalang; supporto aggiunto perlang="it_IT"con trattino basso - IBM — falso positivo immagini — aggiunto filtro per
img_alt_valid/img_alt_null/img_alt_decorative/img_alt_misusequando tutte le immagini nel raw HTML hanno già un testo alternativo (alt,aria-label,aria-labelledby,title); il default ènull(non0) quando il raw HTML non è disponibile, evitando la soppressione silenziosa di violazioni reali - Leggibilità — un solo suggerimento mostrato —
parseAiJsonnel backend splitta oraparagraphSuggestionsstringhe (frasi separate da.seguite da maiuscola) in array di elementi distinti; in precedenza l'intera stringa compariva come un unico suggerimento - Leggibilità — JSON grezzo a video —
normalizeParagraphSuggestionsnel frontend gestisce ora le tre forme: array già corretto, stringa JSON array (anche con virgolette singole), testo piano con frasi concatenate o newline
Aggiunto
- Numeri di riga nei checker a11y-engine —
checkLanguageecheckImagespopolano i nuovi campilineNumberesourceSnippet(con prefisso[Riga N]) usandosourceCodeLocationInfodi Cheerio; ilwcag-mapperinclude lo snippet nel testo Problemi al posto del solo tag elemento - Screenshot per ogni errore (tutte le modalità) — Evidence Layer: dopo ogni valutazione tecnica web, un modulo fire-and-forget (
src/lib/evidence/) cattura in background screenshot degli elementi falliti tramite Playwright e li carica su MinIO; disponibili per la revisione visiva in tutte le modalità (non solo Gemini) - Galleria immagini per revisione alt text — Evidence Layer cattura screenshot individuali di ogni
<img>per il criterio 1.1.1; l'UI (ComponenteClienteDettaglioClient) mostra griglia con badge colorati — rosso (alt vuoto), arancione (alt generico), verde (alt presente) — per verifica visiva rapida; retrocompatibile con il vecchio formatostring[]
Corretto
- TraversaliPanel — "0 pagine" per problemi trasversali — la colonna SQL
affected_countora viene restituita con alias"affectedCount"(camelCase); il frontend leggeva la chiave camelCase ma la query restituiva snake_case, mostrando sempre 0 - Suggerimenti del linguista — JSON grezzo a video —
parseAiSuggestionsgestisce ora ancheparagraphSuggestionsformattato come array JSON[...]oltre agli oggetti{...}; in precedenza la stringa veniva stampata letteralmente invece di essere decodificata come lista di elementi - IBM Equal Access — falso positivo
html_lang_exists— aggiunto filtro post-scan: se il raw HTML contiene un attributolangoxml:langvalido su<html>, la regola viene soppressa prima del raggruppamento; il motore IBM in certi contesti Playwright segnalava come mancante un attributo già presente - Matrice cross-browser — colonna WebKit mancante — introdotto
effectiveBrowsers(useMemo): se la configurazione del round non contiene almeno 2 browser, i nomi vengono estratti direttamente dai JSONbrowserResultsdei risultati, normalizzando i suffissi_desktop/_mobile/_tablet - Motore statico — etichetta riga —
buildSnippetora emette[Riga N]invece di[Linea N], allineandosi al parser del frontend che cerca questa forma per renderizzare il badge numerico - Checker lingua —
xml:langXHTML — il controllo ora usaxml:langcome fallback quandolangè assente; evitava falsi positivi su pagine XHTML con<html xml:lang="it"> - Checker link — nome accessibile tramite
title— l'attributotitleè ora riconosciuto come fonte valida del nome accessibile di un link, riducendo i falsi positivi "Link senza nome accessibile" - Checker immagini — attributi ARIA e
title—<img>,<input type="image">e<area>ora accettanoaria-label,aria-labelledbyetitlecome alternative valide all'attributoalt - Parser bullet point — output Gemini — la regex in
formatTextAsListinclude ora il carattere•(tipico di Gemini) evitando la duplicazione del punto elenco nell'HTML renderizzato
Aggiunto
- Simulatore dislessia — confronto fianco a fianco — quando il simulatore è attivo, la sezione mostra due colonne affiancate: testo originale (leggibile, typography system-ui) e testo simulato (distorto con font graziato, giustificazione e stress visivo Irlen); il valutatore può confrontare direttamente le due esperienze senza perdere il contesto del testo reale
- Campo note nella valutazione manuale — aggiunto campo
<textarea>facoltativo per le note del valutatore sia nella modalità Wizard che nella modalità Lista diComponenteClient; il valore è incluso nel salvataggio tramitePUT /api/results/[id] - Istruzioni di preservazione HTML nei prompt AI — aggiunta istruzione esplicita a Claude (Anthropic) e Gemini affinché non alterino né rimuovano attributi HTML originali (
href,title,alt, ecc.) negli snippet estratti - Stato iniziale auto-calcolato in TraversaliPanel — all'apertura di un problema trasversale privo di status, il valore viene pre-calcolato come lo stato più frequente tra i
affected_resultscorrelati
Aggiunto
- Endpoint
reset-status—POST /api/valutazioni/[id]/reset-statusreimposta aidlei componenti bloccati in statorunningoqueued; accessibile ai ruoliadmineauditor - Pulsante "Sblocca analisi bloccate" — voce nel menu Azioni visibile solo quando esistono componenti bloccati e l'utente non è RTD; mostra spinner durante l'operazione e contatore dei componenti sbloccati al completamento
- Rilevamento automatico problemi trasversali al mount — se al caricamento della pagina risultano ≥ 2 componenti già valutati ma nessun problema trasversale rilevato, la detection viene avviata automaticamente (gestisce ricaricamento pagina, valutazioni singole e ripresa da Inngest)
- Trigger detection dopo
evaluateAll— al completamento del ciclo "Valuta tutto" viene sempre eseguita la detection dei problemi trasversali
Corretto
- Fingerprinting trasversale per risultati AI/IBM — i risultati privi di
source_code(motori AI e IBM) ora usano i primi 120 caratteri del campoproblemscome chiave di confronto strutturale; in precedenza venivano scartati silenziosamente - Consolidamento chiamate di detection — il
POST /api/cross-section-issuesè ora centralizzato nella funzionetriggerCrossSectionDetection, eliminando duplicazioni di codice nel client
Corretto
- VisualComparisonTool — viewport responsivi — normalizzazione dei suffissi
_desktop/_mobile/_tabletnelle chiavi browser prima della ricerca inBROWSER_LABELS; evita crash quando il rendering responsivo produce chiavi comechromium_desktop - Seed WCAG criteri — aggiornamento bloccato — sostituito
ON CONFLICT DO NOTHINGcon upsert completo; tutti i campi inclusomethodology_itora vengono aggiornati sui record esistenti ad ogni avvio - Motore leggibilità — fusione parole — aggiunta spaziatura tra elementi block-level prima dell'estrazione del testo con
.text(); previene la concatenazione errata di parole da paragrafi adiacenti (es."HelloWorld"invece di"Hello World") - QualitaClient — perdita campi suggerimenti AI — rimosso il blocco di estrazione regex di emergenza in
parseAiSuggestions; il fallback ora preserva il JSON originale intatto, evitando la perdita dicontextSentencee altri campi - Pipeline tecnica — posizione errori senza numero riga — abilitato
sourceCodeLocationInfo: trueincheerio.load; ogni elemento ha ora.sourceCodeLocation.startLinedisponibile - static-checks.ts — snippet troncati senza riferimento riga — introdotto helper
buildSnippet()che prepone[Linea N]quando disponibile e porta il limite da 100–150 a 700 caratteri; sostituiti tutti i pattern di troncamento nel file - cross-section-issues GET —
criterionCodemancante — aggiunto aliasAS "criterionCode"nella SELECT ecsi.criterion_codenella clausolaGROUP BY
Corretto
- "Valuta tutto" — errori silenziosi — il loop
evaluateAllora controllares.ok; se l'API restituisce un errore HTTP (rate-limit 429, timeout 504, 500) l'eccezione viene catturata, conteggiata infailede loggata in console; il ciclo prosegue sulle pagine successive - "Valuta tutto" — aggiornamento non incrementale —
fetchData()invocata dopo ogni singola valutazione riuscita: la tabella si aggiorna in tempo reale pagina per pagina senza attendere il completamento dell'intero ciclo - "Valuta tutto" — filtro URL stringente — rimosso il vincolo
&& c.urldal filtro iniziale; i componenti web senza URL vengono conteggiati comesaltatinel riepilogo finale invece di essere ignorati silenziosamente
Aggiunto
- Progress bar "Valuta tutto" — banner animato con barra di avanzamento e contatori live (ok / errori) visibile durante il ciclo di valutazione massiva
- Contatore progressivo nel pulsante — il pulsante "Valuta tutto" mostra
N/totalee il numero di errori correnti durante il ciclo - Alert finale dettagliato con dismissione — il messaggio di completamento riporta separatamente pagine analizzate, errori e pagine saltate (URL mancante); l'alert è chiudibile con pulsante ✕
- Delay anti rate-limit — pausa di 700 ms tra richieste consecutive per prevenire errori 429 dai modelli LLM
Aggiunto
- Contestualizzazione AI per tipologia ente (P2-03) — nome ente e tipologia dominio iniettati nel system prompt di Claude durante la valutazione web e PDF; recupero automatico tramite join con tabelle
entitiesedomains; parametro opzionale backward-compatible su tutta la chain (evaluateWithAi,evaluatePdfWithAi, phase handlers) - Endpoint
/api/health(P2-05) — health check strutturato con ping paralleli a DB (SELECT 1), veraPDF (/api/profiles) e renderer (/health); risponde 200okse DB attivo, 503degradedse DB ko; timeout 3s sui servizi esterni
Corretto
- Endpoint probe veraPDF in
/api/health— allineato a/api/profilescoerente converapdf-engine/index.ts(era/api/v1/info)
Aggiunto
- Renderer service microservizio (P1-01) — Playwright isolato in un servizio Express dedicato con immagine Docker playwright-jammy;
a11y-appdelega il rendering via HTTP; SSRF guard su URL con blocklist hostname e range IPv6 - Pipeline di valutazione modulare (P1-02) —
web-evaluation.tssuddiviso in tre moduli di fase indipendenti per migliore manutenibilità e testabilità - Dashboard progetto (P2-01) — nuova tab "Dashboard" nel dettaglio progetto con health score complessivo, radar chart principi WCAG, trend storico di conformità e top criteri falliti
- API
/api/projects/[id]/dashboard— endpoint analytics: health score, trend conformità, principi WCAG, top failing criteria - Feedback loop automatico esperto → KB (P2-02) — ogni salvataggio di una correzione di status chiama
learnFromSingleResulte aggiorna immediatamenteknowledge_entriessenza attendere la consolidazione manuale - Badge "KB aggiornata" — indicatore visivo nell'UI di valutazione che conferma l'aggiornamento automatico della Knowledge Base dopo una correzione
- KB Insights panel (P3-03) — pannello statistiche nella pagina Knowledge Base: top criteri corretti, trend di crescita KB, distribuzione confidence, heatmap criterio × content_type
- API
/api/knowledge/insights— metriche aggregate: top criteria per frequenza, trend crescita, distribuzione confidence, heatmap - Cache KB in-process — cache con TTL 5 min per
getKnowledgeForPrompt; invalidata automaticamente dopo consolidazione e dopo ogni correzione singola - Suite di test Vitest — framework di test aggiunto con smoke test per technique engine, KB cache e KB overrides (65 test)
- Batch SQL in
applyEvaluations— inserimenti raggruppati: da N query a 3 statement per valutazione, miglioramento prestazioni significativo
Corretto
- Guard URL malformati in
learnFromSingleResult— gestione eccezioni per URL non validi nella determinazione dello scope di apprendimento - SSRF guard renderer service — validazione URL e blocklist hostname (inclusi IPv6 loopback e link-local) prima di delegare al renderer service
- Health score denominator — calcolo del punteggio di conformità nel dashboard reso consistente tra API e UI
- Trend SQL e bucket ordering KB Insights — query corrette per crescita storica e ordinamento distribuzione confidence
scope="col"su intestazioni tabella KB Insights — fix accessibilità su table header nel pannello KB- 404 handling e logging in
PUT /api/results/[id]— risposta 404 corretta se il risultato non esiste; log di errore aggiunto - Validazione risposta HTTP in
saveResult— previene fallimenti silenziosi quando l'API restituisce status non-OK - Ordinamento filtri in pagina Valutazioni — uso di
localeCompare('it')per ordinamento alfabetico deterministico e localizzato nei menu Progetto, Valutatore ed Ente (fix S2871)
Aggiunto
- Resume automatico polling al ricaricamento pagina — Se l'utente ricarica la valutazione mentre Inngest sta ancora elaborando, il client rileva i componenti in stato
running/queuede riprende automaticamente il monitoraggio senza ri-triggerare la valutazione - Label di fase nel progresso valutazione — Il polling mostra la fase corrente in linguaggio naturale: "Fase tecnica in corso…", "Analisi Claude in corso…", "Analisi Gemini Vision in corso…"
- Colonna
eval_phasenel DB — Persiste la fase corrente della pipeline (technical|claude|visual) durante l'esecuzione Inngest; azzerata automaticamente al completamento o al fallimento - Badge "Divergenza rilevata" — Indicatore arancione sui criteri con risultati discordanti tra browser; bordo card arancione per distinguere le divergenze cross-browser dagli esiti "Non superato"
- Nuove sezioni guida metodologica — "Architettura Pipeline (v1.9.0)", "Bonifica PDF Assistita (AI)", "Analisi Cross-Browser e Divergenze" e "Acquisizione Snippet e Stati Dinamici" nella pagina
/help-valutazione; badge tecnologia Atomic Pipeline, PDF Remediation e Cross-browser nell'intestazione evalPhasenell'endpoint/api/components/[id]/eval-status— Il polling FE riceve la fase corrente e aggiorna il messaggio di progresso in tempo reale
Modificato
- Timeout polling valutazione: safety timeout esteso da 3 min a 30 min; il polling si interrompe correttamente anche su stato inatteso (es.
idledopo un reset manuale) - Fase route usa metodologia del round —
handleWebEvaluationriceve la metodologia associata al round specifico per decidere se eseguire la faseclaude/visual, evitando conflitti con le impostazioni globali modificate dopo l'avvio della valutazione - Prompt AI — risposta JSON pura: l'istruzione finale specifica esplicitamente di iniziare con
[senza blocchi markdown; il parsertryRepairJsongestisce ora risposte troncate con:pendente e virgola finale - Report Allegato 2 AgID: formato cambiato da PDF a DOCX; il pulsante "Dichiarazione di Accessibilità" rinominato in "Guida alla compilazione (PDF)" per maggiore chiarezza
- Styling panel componente: colore sky per il pannello "Log di Esecuzione"; colore ambra per il pannello "Matrice Cross-Browser"; messaggio AXTree vuoto arricchito con descrizione della funzione
- Tabella componenti valutazione: aggiunto
overflow-x-autoper scroll orizzontale su viewport stretti
Corretto
- Creazione progetto per PM: ruolo
pmora autorizzato suPOST /api/projects(in precedenza bloccato a 403); matrice permessi UI in/ruoliaggiornata con la riga Creazione Progetto - Consumi responsive: tab tipo con
flex-wrapper evitare overflow su mobile; griglia KPI a colonna singola su mobile (grid-cols-1 sm:grid-cols-2) - KB sidebar responsive: layout
flex-col lg:flex-rowcon sidebar filtri full-width su mobile
Aggiunto
- Pipeline valutazione a fasi atomiche (Inngest) — La funzione
evaluationPipelineè ora suddivisa in tre step indipendenti (technical-eval,claude-eval,visual-eval); ogni fase ha il proprio timeout di 10 minuti e retry separato; risolve i timeout su valutazioni complesse in modalitàtecnica-claude-gemini - Phase API per
/api/evaluate— Il parametro opzionalephase(technical|claude|visual) permette l'esecuzione parziale dei motori; faseclaudefa fetch HTML statico + esegue solo Claude; fasevisualrenderizza per screenshot + Gemini Vision + snippet; fasetechnicalesegue tutti i motori non-AI e salva i risultati di base - Concorrenza globale valutazione — Limite di 2 valutazioni simultanee sull'intera istanza (
global-evaluation-lock) invece che per singolo round; protegge RAM/CPU dalla saturazione da istanze browser headless parallele - Helper
saveCrossBrowserMatrix— Logica di salvataggio matrice cross-browser estratta come funzione riutilizzabile; chiamata sia dalla fase tecnica che dalla pipeline completa
Modificato
maxDuration/api/evaluate: aumentato da 300s a 600s (10 minuti) per supportare le fasi più lunghe- Timeout snippet capture:
page.goto()aumentato a 30s;scrollIntoViewIfNeededcon timeout 5s;page.screenshot()con timeout 8s su tutti i punti di cattura (snippet, focus, hover);page.evaluate(delay)sostituito conpage.waitForTimeout()(API Playwright nativa) - Fase claude/visual su PDF: le fasi dedicate ritornano
skipped=trueimmediatamente (non applicabili a documenti PDF)
Aggiunto
- Gemini Vision multi-immagine — Cattura snippet screenshot a livello di elemento (selettore CSS → ritaglio Puppeteer) e invio contestuale a Gemini Vision; modalità chirurgica con analisi multi-immagine quando sono presenti snippet, modalità classica in fallback automatico
- Modalità valutazione Wizard — Navigazione guidata per criteri WCAG raggruppati per categoria Accessibility Insights (Perceivable, Operable, Understandable, Robust); toggle Lista/Wizard nella toolbar; profilo utente determina la vista predefinita
- Test cross-browser — Infrastructure — Colonna
browser_results(JSON) invalutazione_resultseax_tree_snapshotsinevaluation_snapshots; cattura AXTree viapage.accessibility.snapshot()per ogni engine; endpoint/api/ax-tree-snapshot; motore diff AXTree per confronto accessibilità tra browser - AI Divergence Expert — Analisi automatica delle divergenze cross-browser tramite Claude; identifica criteri con esiti discordanti tra engines e produce raccomandazioni per l'auditor
- Componente
ApiKeyFieldriutilizzabile — Gestione unificata chiavi API con show/reveal/copy/generate; integrato su tutti i tab Impostazioni - Tab Notifiche in Impostazioni — Configurazione email (Resend) spostata in tab dedicato con link a template e matrice eventi; layout Quick links rimosso in favore di integrazione nel tab
- Parametro
?reveal=truein Settings API — Recupero valore reale di chiavi mascherate su richiesta esplicita;readability_ai_api_keyegemini_evaluation_api_keyaggiunte aSENSITIVE_KEYS - Visualizzatori cross-browser UI —
BrowserMatrix(griglia engine × criteri con icone stato) eAXTreeVisualizer(albero accessibilità interattivo) come componenti standalone
Modificato
- Problemi trasversali — UI: header card con numero progressivo
Problema #Ne formato integrato1.1.1 — Nome criterio; badge numerici con testo scurotext-amber-950su sfondo giallo per contrasto WCAG; label occorrenze cambiata inCoinvolge N pagine; titoli sezione e modale in sentence case - Chatbot sidebar — UI: allineamento textarea/pulsante invio corretto (
items-center); icona invio aggiornata a paper airplane standard TraversaliPanel: interfacciaIssueCardsemplificata rimuovendo la propindexridondante; numerazione inline calcolata viaindexOf
Aggiunto
- Problemi trasversali — Gestione issue a livello template con propagazione ai componenti; API con analisi similarità e pannello UI interattivo per creazione, conferma ed eliminazione
- Matrice eventi notifiche — Nuova pagina
/impostazioni/notifiche/matricecon visualizzazione tabellare: quali ruoli ricevono notifiche per ogni transizione del workflow e su quale canale - Tracciamento autore Knowledge Base — Campo
created_bysulle entry KB con visualizzazione in tabella e badge AI/Manuale - Warning soglia confidenza KB — Indicatore visivo per entry con confidenza sotto soglia e contatore occorrenze
- Log esecuzione per criterio — Campo
criterion_codenella tabellaexecution_logs; pannello log espandibile per singolo criterio nella scheda componente - Guida metodologica WCAG — Sezione inline nella pagina
/help-valutazionecon istruzioni di valutazione in italiano per tutti i criteri WCAG 2.2 - Campo methodology nei criteri WCAG — Aggiunto al DB e script di popolamento da dataset WCAG 2.2
- Supporto app mobile — Tipo progetto
mobilecon gestione URL multipli, configurazione piattaforma e store nel form creazione progetto
Modificato
- Permessi ruoli PM e Delegato aggiornati su navigazione, gestione progetti e route protette
- Card criteri nel dettaglio valutazione: espandibili con ricerca KB integrata e log di esecuzione per criterio
- Docker Compose aggiornato a stack multi-servizio con configurazione ambiente migliorata
Corretto
- Ripristino
node_modulese rimozione cache.nextobsoleta che causava 21 errori TypeScript
Aggiunto
- Sistema notifiche — NotificationBell nella TopBar con contatore non lette e pagina
/notifiche - NotificationDispatcher — Servizio con matrice eventi: notifiche automatiche ad ogni transizione di stato del workflow (submit, assign, complete, request-revision, publish)
- Template email admin — UI per la gestione dei template email con endpoint CRON per pulizia notifiche vecchie
- Campo tecnologie assistive — Aggiunto al form feedback pubblico con persistenza DB e invio email
Corretto
- Fix query
unreadCounte hardening POSTemail-templates - Rimosso
.catch()irraggiungibile suPromise.allSettled - Toast errore fetch in
NotificheImpostazioniClient - Fix permessi file DOCX templates: root-owned con chmod 555 (SonarQube docker:S6504)
Aggiunto
- Workflow stati valutazioni — Ciclo completo a 6 stati:
bozza→in_attesa_assegnazione→assegnata→completata→richiesta_revisione→pubblicata - Endpoint API workflow —
submit,assign,complete,request-revision,publishper ogni transizione di stato - RevisionModal — Componente per RTD per richiedere revisione con note opzionali all'auditor
- Dichiarazioni AGID — Calcolo automatico stato di conformità dalla valutazione collegata
- Changelog e Cookie Policy — Footer globale con link a pagine informative
- Log esecuzione — Visualizzatore log raggruppati per sessione con stile terminale
- Note AI in sola lettura — Visibili durante la valutazione manuale per supporto contestuale
Modificato
- Badge stato valutazione aggiornato per tutti i nuovi stati del workflow
- Bottoni avvio valutazione nascosti quando la valutazione è già completata
- Ottimizzazione criteri AI: valutati solo quelli non risolvibili staticamente
Corretto
- Ruolo RTD: RLS dashboard, sincronizzazione entità, modal dettagli log, visibilità consolida
- Parsing JSON da AI: gestione robusta di risposte troncate o malformate
Aggiunto
- Knowledge Base chatbot — Indicizzazione documenti e ricerca semantica RAG
- Screenshot visibili — Archiviazione MinIO con URL firmati per accesso sicuro
- Export report AGID Allegato 2 — Generazione documento Word strutturato
- Confronto storico — Visualizzazione delta tra round di valutazione successivi
Modificato
- Navbar valutazione: unificazione toolbar e pulizia sezione audit
- Sorting e unificazione tabelle valutazioni
Aggiunto
- Motore WCAG Techniques — 56 criteri con 3 modalità: static / rendered / AI
- IBM Equal Access — Integrazione checker su pagina renderizzata via Puppeteer
- RenderedContext — Raccolta dati Puppeteer: focus styles, dimensioni target, link styles
- veraPDF sidecar — Analisi PDF/UA-1 via servizio REST Docker
Modificato
- Pipeline valutazione web: rendering JS opzionale per siti con blocco crawler
- Prompt AI: injection della Knowledge Base per apprendimento continuo
Aggiunto
- Knowledge Base — Apprendimento automatico dalle correzioni degli esperti
- Consolidazione — Endpoint e UI per acquisire le correzioni post-round
- Confidence score — Ponderazione regole KB in base alle occorrenze (
min(1.0, 0.5 + 0.1×n)) - Ruolo RTD — Accesso area dedicata con dashboard dichiarazioni AGID
Corretto
- Sincronizzazione entità RTD: mapping automatico ente → dichiarazioni
Aggiunto
- Autenticazione NextAuth — Sessioni sicure con ruoli (admin, auditor, viewer, rtd)
- Gestione utenti — Inviti via email, gestione ruoli da pannello admin
- Multi-progetto — Organizzazione audit per progetto e round
- Export Excel — Report risultati in formato xlsx
Prima release
- Motore di analisi statica HTML (Cheerio) per ~20 criteri WCAG 2.2 AA
- Valutazione AI con Claude (claude-opus-4-6) su criteri non automatizzabili
- Motore PDF con pdf-parse per analisi struttura documenti
- Dashboard risultati con filtri per criterio, stato e priorità
- UI in italiano, stack Next.js 15 + React 19 + PostgreSQL + Tailwind CSS