Vai al contenuto

Audit strutturale SafeOps: menu, navigazione, ruoli e permessi

Data audit: 2026-09-29
Perimetro: checkout PROD /home/safeops/safeops e checkout DEV /home/safeops/safeops-dev.
Metodo: ispezione statica AST/grep, lettura di configurazione e template, lettura di cataloghi FAB e tabelle ACL disponibili. Nessun endpoint è stato modificato, nessuna migration è stata applicata e nessun dato è stato scritto.

1. Executive Summary

SafeOps non ha oggi una singola sorgente di verità per la navigazione e l'autorizzazione. La registrazione iniziale delle view è concentrata in app/__init__.py, ma il menu finale è il risultato di più strati: Flask-AppBuilder, menu_service, menu_runtime, policy persistite, monkey patch in bootstrap e classificazione/filtering nei template Jinja. Le ACL cliente/tenant sono invece replicate nei moduli Documentale, Anagrafiche, Formazione, Commerciale, DVR, Ticket e Tecnici.

La superficie è ampia: 349 chiamate add_* individuate staticamente nel bootstrap, 625 ab_view_menu, 1.422 ab_permission_view e 221 nomi di permission distinti nel catalogo PROD letto in sola lettura. Sono presenti 35 ruoli FAB, 62 utenti e 14 profili tenant. Il checkout DEV ha un albero applicativo sostanzialmente analogo ma, come il PROD, contiene modifiche preesistenti non riconducibili a questo audit.

Il rischio principale non è un singolo link errato: è la separazione non documentata tra voce visibile, accesso all'endpoint, accesso ai dati e filtro tenant/cliente. Un add_view_no_menu, un link nascosto o una regola Jinja non costituiscono da soli una protezione dell'endpoint. Prima di qualsiasi restyling o centralizzazione serve una matrice verificata per ruolo, URL/API, tenant e cliente.

2. Baseline analizzata

PROD

  • percorso: /home/safeops/safeops;
  • branch: wip/integration-safeops-20260421;
  • HEAD: 58230ec9ad7b1a7a5a372c6372230a57c3472dc8;
  • working tree: dirty, 439 percorsi modificati o non tracciati al momento della rilevazione; non sono stati alterati, ripristinati o messi in stash;
  • Python 3.12.6, Flask 3.1.2, Flask-AppBuilder 5.1.0, SQLAlchemy 2.0.36;
  • configurazione principale: config.py, con DATABASE_URL come sorgente URI e configurazione tenant/module entitlement;
  • Alembic head disponibile dal repository comune: 0004_dvr_structure_tables;
  • le statistiche del catalogo FAB riportate in questo documento sono una lettura read-only del database PROD configurato. Nessuna credenziale è inclusa.

DEV

  • percorso: /home/safeops/safeops-dev;
  • branch: dev/safeops-20260819;
  • HEAD: c55d0012821280a4b40f77b4d2f0b735f1334f82;
  • working tree: dirty, 479 percorsi modificati o non tracciati;
  • il checkout DEV contiene gli stessi strati principali di bootstrap, IAM, template e moduli, con divergenze applicative da verificare prima di parlare di allineamento;
  • non è stato eseguito alcun collaudo autenticato o query di scrittura sul database DEV. L'assenza di una verifica runtime DEV è un blocker per la fase successiva, non un'indicazione di equivalenza con PROD.

La working tree preesistente rende non affidabile un diff completo PROD↔DEV come prova di allineamento. Un futuro confronto dovrà usare snapshot congelati o commit/artefatti esplicitamente identificati.

3. Architettura attuale

Il bootstrap applicativo (app/__init__.py) registra blueprint e centinaia di view/link. La registrazione FAB costruisce un albero iniziale (appbuilder.menu). Successivamente:

  1. app/modules/iam/menu_service.py legge il menu FAB, policy utente/gruppo/ruolo/tenant e nodi/override strutturali;
  2. app/modules/iam/menu_runtime.py applica entitlements di modulo e regole speciali per tenant/ruolo;
  3. patch in app/__init__.py intercettano redirect, contesto permission/menu e cicli FAB;
  4. app/templates/appbuilder/_safeops_menu_tree.html riclassifica, nasconde e raggruppa le voci in Jinja;
  5. ogni modulo applica autonomamente permission FAB, ruoli, tenant_id, cliente e regole di visibilità dati.

Non esiste un registro dichiarativo unico dal quale derivino contemporaneamente menu, permission e ACL. La catena effettiva è quindi:

registrazioni FAB + blueprint
        ↓
menu_service/menu_runtime + policy DB + patch bootstrap
        ↓
template Jinja (ulteriore filtering e classificazione)
        ↓
ACL e permission applicate dalle singole view/API/moduli

4. Mappa completa del menu e della navigazione

Punti di costruzione

L'AST del bootstrap rileva 349 chiamate add_* in app/__init__.py (tra add_view, add_view_no_menu, add_link, add_separator, add_category); app/views.py mantiene anche una registrazione FAB storica. Sono inoltre presenti nove register_blueprint espliciti per gateway AI, chat, sync/editor documenti, lead, mobile, portale formazione e tutor AI.

Le registrazioni principali sono:

Area/categoria Voci e view rappresentative Metodo/origine Note
Security / Controllo Accessi / Impostazioni UserSecuritySettingView, RBAC/IAM, policy, diagnostics, module access add_view in app/__init__.py circa 2019–2251 area composta da FAB e console IAM parallele
Documentale / Import e Sync / Configurazioni documenti, workflow, template, import, sync e audit add_view/add_link circa 2366–3010 molte view operative sono no_menu e raggiunte da link o URL
Operativo / Ticket ticket, assegnazioni, ODL e tecnici circa 3012–3285 registrazioni legacy e nuove convivono
Commerciale clienti, offerte, contratti, preventivi, RAS e servizi circa 3286–3900 categorie e nomi ripetuti in più contesti
Formazione / Pianificazione FormazioneDashboardView, CorsoView, Corsi, iscrizioni, discenti, docenti, aule, registro presenze, import, report, scadenze, qualità, WooCommerce circa 6377–6730 numerose view legacy no_menu, link manuali e percorsi /formazione-*//corsoview/*
Anagrafiche / Clienti clienti, aziende, dipendenti, territori registrazioni distribuite nel bootstrap accesso e filtro cliente sono anche implementati nelle view
Archivi documenti formazione e archivi speciali add_link e categorie custom categorie condizionali per ruolo/tenant

Inventario Formazione (tutte le famiglie registrate)

Nel blocco formazione risultano, tra le altre, FormazioneDashboardView, DashboardDemo, CalendarDemo, ClientiDemo, CorsoWizard, Corsi, Iscrizioni, CorsistiImport, workspace corsisti, mapping legacy, categorie, libretto, fascicolo, discenti, reportistica, gap, scadenze, tablet, CorsoView, ente formatore, portale discenti, registro presenze, aule, docenti, dipendente-corso, materiale, monitoraggio, iscrizioni WooCommerce, sconti, catalogo, manuale, storico, survey, qualità, audit invii, date/orari ed edizioni. Molte sono registrate con add_view_no_menu e sono esposte tramite link hardcoded o da altre view.

Duplicazioni e fragilità osservate

  • la stessa capacità è esposta sia come ModelView FAB sia come link custom (Corsi/CorsoView, reportistica, materiali, iscrizioni);
  • esistono categorie e label quasi duplicate (Commerciale, Tecnici, Gestione email, Offerte, Formazione);
  • numerosi href sono stringhe manuali (/formazione-corsi/, /formazione-clienti/, /corsoview/list/, ecc.);
  • l'ordine delle registrazioni nel bootstrap influenza l'albero e i nomi FAB;
  • menu_keys.py deve ripiegare su chiavi unmapped.* quando label/URL non sono riconducibili a una chiave stabile;
  • il menu policy è basato su label/tenant/subject, quindi una rinomina può scollegare una policy senza errore di tipo;
  • il template sidebar aggiunge shortcut amministrativi al di fuori del registro FAB.

5. Mappa permission e autorizzazione

Catalogo FAB e dati osservati

La lettura read-only PROD ha restituito 35 ruoli FAB, 221 nomi permission distinti, 625 ab_view_menu e 1.422 ab_permission_view. Le PermissionView includono primitive automatiche (can_add, can_delete, can_edit, can_index, can_list, can_show) e numerose azioni custom. menu_access è associato a 337 PermissionView.

Le policy menu persistite contano 140 righe menu_policy, 62 menu_policy_user, nessuna menu_policy_group attiva e un override in menu_structure_override; menu_structure_node è vuota nel catalogo letto. Ciò documenta l'esistenza di più meccanismi, non una loro precedenza unica documentata dall'applicazione.

Primitive e punti di controllo

Sono presenti @has_access, @has_access_api, base_permissions, class_permission_name, method_permission_name, is_accessible, permission_name, controlli current_user, controlli di ruolo e controlli tenant/client. menu_service usa la precedenza user → group → role per le policy menu; menu_runtime applica entitlements solo alla visibilità; le view devono quindi ripetere il gate sul percorso reale.

app/modules/iam/policy.py centralizza alcuni helper (current_role_names, is_platform_admin, tenant_context, effective_tenant_key, can_choose_tenant), ma non è l'unico punto di decisione: DVR, SafeCondo, Anagrafiche, Documentale, Formazione, Commerciale, Ticket e Tecnici mantengono policy locali.

6. Ruoli e matrice ruolo → funzione

Ruoli presenti

I 35 nomi letti comprendono ruoli FAB storici (Admin, Public, GlobalClientManager, ROLE_ADMIN), profili applicativi (ROLE_PROFILE_*), ruoli tecnici/commerciali e template (TEMPLATE_*), oltre a ruoli utente storici. Sono presenti 62 utenti; il report non riporta username o dati personali.

Il profilo tenant è un secondo livello: 14 profili includono admin_tecnico, admin_tenant, amministratore_condominio, cliente, commerciale, gestione, gestore_cliente, operatore_admin, operatore_formazione, operatore_osm, operatore_amministro, readonly, tecnico, utente_condominio. I due profili non di sistema operatore_osm e operatore_amministro risultano nel DB ma non nella lista di default Python osservata: possibile drift storico da verificare.

Matrice comportamentale (baseline da verificare con test)

Legenda: S = previsto da ruolo e codice; C = condizionato da tenant/modulo/cliente; N = non previsto; ? = non dimostrabile senza collaudo autenticato per endpoint e dati. La presenza nel menu non equivale all'accesso.

Funzione Admin / ROLE_ADMIN Admin tenant Tecnico / Admin tecnico Gestione / Commerciale Operatore formazione Amministratore condominio Cliente / readonly
Clienti / Anagrafiche S C C S C C C
Documentale S C C C C C C
RAS / DVR S C S C N C C
Corsi / Formazione S C C C S C C
Preventivi / Commerciale S C N/C S N/C N C
ODL / Interventi S C S C N C C
Calendario S C C C C C C
Chat / portali S C C C C C C
Configurazione moduli S S C C C N N
Amministrazione utenti/RBAC S C N N/C N N N

La matrice è una mappa di lavoro, non una certificazione: per ogni cella C/S occorre verificare menu, URL diretto, API, lettura dati e scrittura dati nello stesso tenant e in un tenant diverso.

7. MENU VS PERMESSI

La relazione MENU → VIEW → PERMISSION → ROLE → ACL DATI è interrotta in più punti:

  • add_view_no_menu rimuove la voce dal menu ma non costituisce un diniego dell'endpoint;
  • menu_module_state_snapshot dichiara esplicitamente che l'entitlement è display-only e non è un endpoint gate;
  • Jinja nasconde label in base a ruolo/tenant e riorganizza l'albero dopo che FAB ha già costruito il menu;
  • API e blueprint applicano controlli distinti da quelli delle ModelView;
  • permission generate automaticamente coesistono con permission custom e label storiche;
  • la configurazione MENU_FILTER_FAIL_OPEN può restituire il menu completo quando la costruzione filtrata è vuota (menu_service.py, ramo di fallback): questo può esporre navigazione e metadati in caso di errore di configurazione, anche se FAB deve ancora proteggere l'endpoint.

Conclusione: oggi “voce non visibile” e “funzione non accessibile” non sono equivalenti e non devono essere trattate come tali nei test o nel nuovo template.

8. ACL cliente e multi-tenant

Il modello combina tenant_id/tenant_key, ruoli FAB, profili tenant, is_platform_admin, entitlements modulo e associazioni utente-cliente. Nel database esistono mapping storici user_cliente/cliente_user (la relazione è rilevata anche nel codice Anagrafiche); non è presente una tabella con un nome unico usato da tutti i moduli.

Le implementazioni si classificano così:

  • parzialmente centralizzate: helper in iam/policy.py, tenant_context, profili ed entitlements;
  • duplicate: _visible_*, _effective_tenant_key, set di ruoli e controlli current_user ripetuti in Anagrafiche, Documentale, Formazione, Commerciale, DVR, Ticket e Tecnici;
  • divergenti: ogni modulo decide autonomamente se un filtro cliente è obbligatorio, se il platform admin bypassa il filtro e come gestire un tenant mancante;
  • legacy: mapping utente-cliente e view no_menu che usano percorsi propri;
  • potenzialmente rischiose: filtri applicati dopo la selezione dei dati, policy display-only e fallback fail-open devono essere verificati con dati cross-tenant.

Gli entitlements mostrano che lo stesso modulo può essere attivo in un tenant e disattivo in un altro (ad esempio Formazione attiva in tenant_besant/tenant_retra e disattiva in altri tenant osservati). L'entitlement deve quindi essere testato sia come visibilità sia come diniego server-side.

9. Struttura template e frontend

Gerarchia primaria osservata:

FAB base_template / appbuilder/init.html
        ↓
app/templates/appbuilder/base.html
        ↓
app/templates/appbuilder/baselayout.html
        ↓
_safeops_sidebar.html + _safeops_topbar.html + _safeops_topbar_badges.html
        ↓
_safeops_menu_tree.html
        ↓
template specifico del modulo

baselayout.html costruisce una shell SafeOps e valuta ruolo, tenant e accesso. _safeops_sidebar.html inserisce shortcut amministrativi hardcoded. _safeops_menu_tree.html contiene macro di rendering ma anche classificazione per parole chiave, liste di label nascoste, condizioni ruolo/tenant e sezionamento commerciale/documentale/tecnico. navbar.html FAB standard rimane nel repository; non è la sola sorgente della navigazione.

CSS/JS SafeOps (in particolare app/static/js/safeops.js) gestiscono ricerca, apertura e toggle del menu. Il JavaScript modifica la presentazione del menu già ricevuto: non deve essere considerato un controllo ACL. Sono presenti riferimenti AdminLTE/Bootstrap e override SafeOps; non è emerso un secondo template AdminLTE3 attivo come fonte unica, ma esistono prove/compatibilità residue da rimuovere solo dopo una mappa di utilizzo.

10. Dipendenze Flask-AppBuilder

FAB resta responsabile di SecurityManager, permission automatiche, ViewMenu, PermissionView, ModelView e menu iniziale. SafeOps estende FAB con monkey patch, registrazioni custom, policy DB, entitlements e template. Il bootstrap può anche predisporre tabelle/permission quando DB_AUTO_CREATE è attivo e invoca helper _ensure_*: questo è un side effect di startup che deve essere separato da un futuro registro dichiarativo.

Il codice applicativo dipende sia da nomi FAB generati automaticamente sia da label/URL di presentazione. Questo rende fragile una rinomina o una migrazione di classe: può cambiare il nome della ViewMenu, lasciare PermissionView obsolete o disallineare una MenuPolicy basata su label.

11. Finding e debito tecnico

ID Severità Area Evidenza (file/funzione) Impatto / dipendenze Strategia futura (non applicata)
SEC-001 CRITICAL endpoint vs menu app/__init__.py registrazioni add_view_no_menu; menu_runtime.py display-only una funzione può essere raggiunta via URL/API anche se non appare; serve matrice negativa completa catalogo endpoint con gate server-side verificato
ACL-001 CRITICAL tenant/cliente iam/policy.py insieme a policy locali in Anagrafiche, Documentale, Formazione, Commerciale, DVR, Ticket, Tecnici rischio di regole divergenti e isolamento non uniforme servizio ACL comune, migrazione per modulo e test cross-tenant
BOOT-001 HIGH bootstrap app/__init__.py, _ensure_*, DB_AUTO_CREATE side effect di schema/security all'avvio; refactoring non deterministico bootstrap read-only in produzione e migration/seed espliciti
NAV-001 HIGH registro menu app/__init__.py, circa 349 add_* ordine di import, duplicati e dipendenze nascoste manifest/registry con adapter FAB
NAV-002 HIGH pipeline menu menu_service.py, menu_runtime.py, menu_filter.py, patch FAB, template Jinja più precedenze e impossibilità di spiegare una voce senza tracing pipeline unica con decisione backend e audit reason
TEMPLATE-001 HIGH autorizzazione nel template _safeops_menu_tree.html, baselayout.html, _safeops_sidebar.html logica RBAC/tenant e URL hardcoded duplicano backend template solo di presentazione
SEC-002 HIGH errore di filtering menu_service.py fallback MENU_FILTER_FAIL_OPEN errore configurativo può mostrare menu completo fail-closed con telemetria e test
PERM-001 MEDIUM catalogo permission 221 permission name, 1.422 PermissionView, 625 ViewMenu naming e residui legacy aumentano il costo ACL inventario canonico e deprecazione controllata
POLICY-001 MEDIUM policy DB 140 menu policy, 62 user policy, 0 group policy, 1 override modello parzialmente usato e basato su label chiavi stabili e schema di precedenza documentato
ROLE-001 MEDIUM profili profili DB operatore_osm/operatore_amministro non nei default Python osservati drift tra seed e runtime registro profili versionato e check di drift
LEGACY-001 MEDIUM formazione/commerciale classi e link paralleli, no_menu, path storici duplicazione funzionale e refactoring rischioso migrazione per capability, poi rimozione legacy
API-001 MEDIUM API/blueprint blueprint e has_access_api distribuiti UI e API possono avere policy differenti contratto permission comune per UI/API
NAMING-001 LOW naming/presentazione label italiane/inglesi, icone e categorie ripetute confusione operativa e snapshot instabili dizionario label e navigation key

12. Responsabilità attuali

Responsabilità Dove risiede oggi Valutazione
Definizione view/menu soprattutto app/__init__.py, più app/views.py centralizzata solo per registrazione, non per policy
Permission FAB SecurityManager/FAB + attributi view/decorator automatica e custom mescolate
Menu policy menu_service, menu_filter, tabelle menu policy duplicata con runtime e template
Entitlement modulo menu_runtime, tenant_module_entitlement display-first; gate endpoint distribuito
ACL dati policy centrali + ogni modulo parzialmente centralizzata e divergente
Sidebar/layout template SafeOps e FAB responsabilità di autorizzazione impropriamente nel template
Registrazione moduli bootstrap + blueprint side effect e ordine di import

13. Architettura target proposta

La direzione è compatibile con FAB se FAB resta un adapter, non il registro business principale:

modulo: metadata + capability + navigation + permission contract
                 ↓
        Navigation Registry (chiavi stabili)
                 ↓
        Permission/ACL service (ruolo + tenant + cliente + endpoint)
                 ↓
        adapter FAB / API policy / SafeOps UI

Il registry dovrebbe produrre un albero autorizzato backend-side con key, label, endpoint, capability e reason. FAB può continuare a registrare ModelView e PermissionView durante la transizione; l'adapter assocerà le view legacy a chiavi stabili. Il template riceverà solo la struttura filtrata e non deciderà se un utente è autorizzato. API e view dovranno usare lo stesso permission contract e lo stesso ACL cliente/tenant.

14. Piano di migrazione incrementale

Fase Obiettivo e componenti Rischio/prerequisiti Test e rollback
0 congelare baseline DEV/PROD, esportare menu/permission/endpoint dirty tree e DB non equivalenti snapshot; rollback = nessuna modifica
1 normalizzare naming e contratto permission, senza rimuovere legacy permission obsolete e ruoli storici catalogo diff, test negativi; revert seed/adapter
2 introdurre Navigation Registry e adapter FAB in shadow mode ordine import e duplicati snapshot menu per ruolo; disattivare adapter
3 collegare policy menu legacy al registry, mantenendo fallback osservabile label policy esistenti confronto old/new, rollback flag
4 nuovo template SafeOps che renderizza solo l'albero autorizzato regressioni responsive/accessibilità test template/visual; flag per tenant
5 migrare moduli per capability, iniziando da Formazione e Documentale ACL locali divergenti matrice UI/API/tenant; rollback per modulo
6 rimuovere duplicati, no_menu e patch non necessari dipendenze non mappate endpoint legacy e permission orphan; rollback per release

15. Strategia di test necessaria prima del refactoring

Non sono stati implementati test in questa fase. Servono almeno:

  • snapshot del menu per ogni ruolo/profilo e per entitlements modulo;
  • accesso positivo e negativo a ogni view, URL diretto e endpoint legacy;
  • test API separati dalla UI;
  • lettura e scrittura nello stesso tenant, diniego cross-tenant e cross-cliente;
  • bypass controllato per platform admin e verifica che non diventi bypass globale non previsto;
  • test di Cliente.users/mapping storico e utenti senza cliente;
  • regressioni FAB su can_list/show/add/edit/delete e permission custom;
  • snapshot delle chiavi navigation e test di rinomina label;
  • test MENU_FILTER_FAIL_OPEN/fail-closed e indisponibilità policy;
  • test entitlements DEV/PROD con configurazioni isolate;
  • verifica che bootstrap non crei o alteri schema/security durante un avvio normale;
  • test template che accerti che Jinja non possa concedere accesso, ma solo rappresentare una decisione backend.

16. File che presumibilmente dovranno essere modificati in fasi successive

Questa lista è indicativa e non autorizza modifiche: app/__init__.py, app/views.py, app/modules/iam/policy.py, menu_service.py, menu_runtime.py, menu_filter.py, menu_keys.py, menu_diagnostics.py, module_access_console.py, app/modules/menu_policy/*, policy/view dei moduli Anagrafiche, Documentale, Formazione, Commerciale, DVR, Ticket e Tecnici, app/templates/appbuilder/baselayout.html, _safeops_sidebar.html, _safeops_menu_tree.html, base.html, index.html, app/static/js/safeops.js, test IAM/ACL/menu e seed/migration solo dopo review separata.

17. Blocker e decisione

  • working tree DEV e PROD entrambe dirty: non esiste una baseline riproducibile per un allineamento diretto;
  • non esiste un registro canonico che leghi capability, menu, permission e ACL;
  • cataloghi FAB molto più grandi del set di menu attuale e presenza di residui/duplicati;
  • ACL cliente/tenant distribuite e non ancora certificate endpoint per endpoint;
  • runtime DEV autenticato e confronto DB DEV↔PROD non eseguiti in questo audit;
  • bootstrap con possibili side effect schema/security;
  • policy gruppo prevista dal modello ma senza righe attive osservate.
AUDIT_STATUS: COMPLETE_STATIC_AND_READONLY_DB
MENU_MODEL_UNDERSTOOD: PARTIALLY — registrazioni e principali filtri mappati; manca una matrice runtime autenticata per tutte le combinazioni ruolo/tenant.
PERMISSION_MODEL_UNDERSTOOD: PARTIALLY — catalogo FAB e layer custom censiti; non è ancora provato il comportamento effettivo di ogni endpoint.
ACL_MODEL_UNDERSTOOD: PARTIALLY — helper centrali e implementazioni per modulo identificati; divergenze cliente/tenant da certificare.
TEMPLATE_MODEL_UNDERSTOOD: YES_FOR_PRIMARY_SHELL — gerarchia FAB/SafeOps e punti di decisione Jinja identificati.
SAFE_TO_REFACTOR: NO — prima occorrono baseline congelate e test negativi di autorizzazione.
BLOCKERS: working tree DEV/PROD dirty; assenza registry canonico; catalogo permission/menu duplicato; ACL distribuite; runtime DEV e matrice endpoint non verificati; bootstrap side effects.
RECOMMENDED_NEXT_PHASE: congelare snapshot DEV/PROD e produrre export read-only canonico di menu, permission, endpoint e ACL, quindi validarlo con test per ruolo/tenant/cliente.

18. Tracciabilità del task

change_summary: creato solo questo report di audit; nessun codice applicativo modificato.
root_cause: non applicabile; attività di analisi strutturale.
files_changed: docs/tecnico/audit/SAFEOPS_MENU_PERMISSION_AUDIT.md
user_impact: nessun cambiamento runtime.
roles_impacted: nessuno.
surfaces_impacted: sola documentazione interna.
behavior_change: nessuno.
permission_change: nessuno.
operator_action_required: revisionare il report e autorizzare separatamente la fase successiva.
application_code_changed: no.
database_schema_changed: no.
migrations_changed: no.
acl_changed: no.
api_contract_changed: no.
targeted_tests: NOT_RUN — audit statico/read-only.
python_tests: NOT_RUN — nessuna modifica applicativa.
javascript_tests: NOT_RUN — nessuna modifica frontend.
runtime_smoke: NOT_RUN — nessun endpoint DEV/PROD attivato.
data_preservation: dati e database non modificati.
helper_impact: no_helper_impact.
helper_content_type: NOT_APPLICABLE.
helper_destination: NOT_APPLICABLE.
release_severity: L0.
helper_ready_for_release: NOT_APPLICABLE.
block_release: true — refactoring non autorizzato e baseline non congelata.
block_reason: review del report, baseline DEV/PROD e matrice ACL/endpoint ancora necessari.
blockers_remaining: vedi sezione 17.
ready_for_dev_test: no.
ready_for_release_candidate: no.
prod_impact: nessuno.
commit_created: no.
tag_created: no.
push_performed: no.

Lavoro fermato come richiesto.