Vai al contenuto

Fase 4 - Piano mirato Formazione con protezione Besant

Data audit: 2026-06-30

Documenti collegati:

  • docs/processo/fase3_pilota_modulo_formazione.md
  • docs/processo/fase3_formazione_matrice_tenant.md

Premessa

tenant_besant contiene dati di produzione. Per questo motivo non deve essere usato come tenant di sperimentazione distruttiva.

Besant resta il tenant piu importante per validare il modulo Formazione su dati reali, ma solo in modalita osservazione, shadow e preview finche non ci sono report puliti.

Regola principale

Nessuna modifica runtime o dati applicativi su tenant_besant senza conferma esplicita.

Questo include:

  • nessuna migrazione dati non necessaria;
  • nessun seed automatico distruttivo;
  • nessuna riscrittura massiva menu/policy;
  • nessuna conversione automatica feature hardcoded in feature flag;
  • nessun enforcement nuovo sulle route core Formazione;
  • nessun job WooCommerce sperimentale;
  • nessun cambio listini, corsi, iscrizioni, attestati, scadenze o discenti.

Stato tenant Formazione

Tenant Ruolo rollout Stato modulo Uso
tenant_besant Produzione / osservazione active/on Solo shadow, preview, compatibilita
tenant_echos Pilota secondario active/on Primo tenant per prove non distruttive
tenant_fornautic Pilota secondario active/on Prove base senza WooCommerce
tenant_retra Pilota secondario active/on Prove language check
tenant_netopen Controllo negativo disabled/off Verifica modulo negato
tenant_safecondo Escluso disabled/off Verifica modulo negato
dorotech Escluso disabled/off Verifica modulo negato
tenant_ids Escluso disabled/off Verifica modulo negato

Ordine rollout consigliato

Step 1 - Solo documentazione e specifica

Stato: in corso.

Output:

  • piano mirato;
  • guardrail produzione;
  • checklist operativa;
  • ordine tenant.

Nessun codice.

Step 2 - Tool di shadow read-only

Primo codice futuro ammesso, ma solo read-only.

Deve produrre report su:

  • stato modulo legacy formazione;
  • stato catalogo futuro safe_formazione;
  • menu Formazione atteso;
  • menu Formazione reale;
  • feature attese;
  • feature reali;
  • profili assegnati;
  • mismatch.

Regola: nessuna scrittura DB.

Tenant da eseguire:

  1. tenant_netopen come controllo negativo;
  2. tenant_echos come pilota base;
  3. tenant_fornautic come pilota base;
  4. tenant_retra come pilota con language check;
  5. tenant_besant solo quando i primi quattro report sono coerenti.

Step 3 - Seed catalogo in modalita dry-run

Produrre preview seed:

  • module_catalog;
  • module_feature;
  • module_profile;
  • module_menu_item;
  • module_dependency;
  • module_plan;
  • module_service_mapping.

Regola: output file/report, nessuna scrittura DB.

Step 4 - Seed catalogo su DB di controllo

Solo dopo approvazione.

Scritture ammesse:

  • tabelle nuove del catalogo;
  • nessuna modifica ai dati Formazione esistenti;
  • nessuna modifica a corsi, date, discenti, attestati, iscrizioni, WooCommerce.

Besant:

  • ammesso solo seed di catalogo globale;
  • vietata scrittura su dati operativi Formazione.

Step 5 - Shadow menu

Confrontare menu dichiarativo e menu reale.

Besant:

  • solo lettura;
  • nessuna sostituzione menu;
  • nessun cambio policy menu.

Step 6 - Adapter entitlement in preview

Confrontare:

  • safe_formazione target;
  • formazione legacy;
  • TenantModuleEntitlement;
  • eventuale futura tenant_module_subscription.

Regola: nessun sync automatico verso entitlement su Besant.

Step 7 - Primo enforcement

Non partire da Besant.

Ordine consigliato:

  1. tenant_netopen: verifica negazione;
  2. tenant_echos: menu base;
  3. tenant_fornautic: menu base;
  4. tenant_retra: language check;
  5. tenant_besant: solo dopo conferma esplicita.

Cosa NON fare su Besant nella prima implementazione

  • Non modificare iscrizioni.
  • Non modificare partecipanti.
  • Non modificare corsi.
  • Non modificare date corso.
  • Non modificare attestati.
  • Non modificare scadenze.
  • Non modificare mapping WooCommerce.
  • Non modificare ordini/import WooCommerce.
  • Non attivare portale esterno.
  • Non attivare SSO.
  • Non attivare certificate sync.
  • Non introdurre readonly enforcement sulle route core senza prova su altri tenant.

Cosa si puo fare su Besant

Solo dopo report puliti sugli altri tenant:

  • leggere stato modulo;
  • leggere menu effettivo;
  • leggere feature flag;
  • leggere profili;
  • confrontare menu atteso vs reale;
  • produrre report mismatch;
  • verificare che il catalogo descriva correttamente lo stato esistente.

Checklist di sicurezza pre-codice

Prima di qualunque implementazione:

  1. Il primo comando deve essere read-only.
  2. Il primo tenant operativo deve essere tenant_netopen o tenant_echos, non Besant.
  3. Ogni comando deve avere --dry-run se puo scrivere.
  4. Ogni comando che scrive deve richiedere --apply.
  5. Ogni report deve indicare tenant, modulo, feature, menu e timestamp.
  6. Ogni scrittura futura deve creare evento audit.
  7. Ogni script che puo scrivere deve rifiutare Besant se non riceve un flag esplicito tipo --allow-production-tenant.
  8. Nessun job automatico deve essere attivato su Besant nella fase pilota.

Piano tecnico minimo futuro

Primo artefatto implementato:

tools/formazione_catalog_shadow_report.py

Input:

  • --tenant-key;
  • --all-active-training-tenants;
  • --include-disabled-controls;
  • --output.

Output:

  • JSON/Markdown con stato modulo;
  • menu atteso;
  • menu reale;
  • feature;
  • profili;
  • mismatch;
  • raccomandazioni.

Regola:

  • nessuna scrittura DB;
  • nessuna chiamata WooCommerce;
  • nessuna chiamata training engine;
  • nessuna modifica menu.

Comandi di validazione iniziali:

/root/.pyenv/versions/safeops/bin/python tools/formazione_catalog_shadow_report.py \
  --tenant-key tenant_netopen \
  --tenant-key tenant_echos \
  --format markdown \
  --output /tmp/formazione_shadow_netopen_echos.md
/root/.pyenv/versions/safeops/bin/python tools/formazione_catalog_shadow_report.py \
  --tenant-key tenant_retra \
  --tenant-key tenant_besant \
  --format json \
  --output /tmp/formazione_shadow_retra_besant.json

Esito prova iniziale:

Tenant Esito
tenant_netopen OFF, menu atteso tutto nascosto
tenant_echos BASE, menu base visibile e add-on nascosti
tenant_retra LANGUAGE_CHECK, segnala feature logica non ancora TenantFeatureFlag
tenant_besant WOOCOMMERCE, segnala guardrail produzione read-only

Secondo artefatto implementato:

tools/formazione_catalog_seed_preview.py

Scopo:

  • produrre seed preview per safe_formazione;
  • non aprire connessioni DB;
  • non scrivere file se non viene passato --output;
  • non avere modalita --apply;
  • rendere revisionabili modulo, feature, profili, piani, limiti, service mapping e menu prima della migrazione.

Comandi di validazione:

/root/.pyenv/versions/safeops/bin/python tools/formazione_catalog_seed_preview.py \
  --format markdown \
  --output /tmp/formazione_catalog_seed_preview.md
/root/.pyenv/versions/safeops/bin/python tools/formazione_catalog_seed_preview.py \
  --format json \
  --output /tmp/formazione_catalog_seed_preview.json

Contenuto preview iniziale:

Sezione Righe
module_catalog 1
module_dependency 1
module_feature 5
module_profile 4
module_plan 6
module_limit_template 8
module_service_mapping 5
tenant_plan_preview 4
module_menu_item 31

Nota: tenant_plan_preview e' solo proposta, non attivazione. Tutti i record includono apply=False dove pertinente.

Seed globale applicato il 2026-07-01:

Tool:

tools/formazione_catalog_seed_apply.py

Comandi eseguiti:

/root/.pyenv/versions/safeops/bin/python tools/formazione_catalog_seed_apply.py \
  --format markdown \
  --output /tmp/formazione_catalog_seed_plan.md
/root/.pyenv/versions/safeops/bin/python tools/formazione_catalog_seed_apply.py \
  --apply \
  --format markdown \
  --output /tmp/formazione_catalog_seed_apply.md

Righe inserite:

Tabella Righe
module_catalog 1
module_dependency 1
module_feature 5
module_profile 4
module_plan 6
module_limit_template 8
module_service_mapping 5
module_menu_item 31

Verifica post-seed:

  • dry-run successivo idempotente: tutte le sezioni risultano skip;
  • tenant_plan_preview ignorato dal tool applicativo;
  • tenant_module_subscription: 0 righe;
  • tenant_module_limit: 0 righe;
  • tenant_module_rule: 0 righe;
  • tenant_module_activation_event: 0 righe;
  • nessuna modifica a dati operativi Formazione o tenant_besant.

Shadow validation catalogo DB

Eseguita il 2026-07-01 con tool read-only:

tools/formazione_catalog_shadow_report.py

Comandi:

/root/.pyenv/versions/safeops/bin/python tools/formazione_catalog_shadow_report.py \
  --tenant-key tenant_netopen \
  --tenant-key tenant_echos \
  --tenant-key tenant_fornautic \
  --tenant-key tenant_retra \
  --tenant-key tenant_besant \
  --format markdown \
  --output /tmp/formazione_catalog_shadow_catalog_validation.md
/root/.pyenv/versions/safeops/bin/python tools/formazione_catalog_shadow_report.py \
  --tenant-key tenant_netopen \
  --tenant-key tenant_echos \
  --tenant-key tenant_fornautic \
  --tenant-key tenant_retra \
  --tenant-key tenant_besant \
  --format json \
  --output /tmp/formazione_catalog_shadow_catalog_validation.json

Esito catalogo:

  • sorgente menu: db;
  • module_menu_item: 31 righe;
  • module_feature: 5 righe;
  • mismatch menu catalogo vs atteso statico: 0;
  • mismatch feature catalogo vs atteso statico: 0;
  • tenant_module_subscription: 0 righe;
  • tenant_module_limit: 0 righe;
  • tenant_module_rule: 0 righe;
  • tenant_module_activation_event: 0 righe.

Esito tenant:

Tenant Stato Plan hint Menu visibili Menu nascosti Note
tenant_netopen disabled/off OFF 0 31 controllo negativo coerente
tenant_echos active/on BASE 24 7 nessuna anomalia
tenant_fornautic active/on BASE 24 7 nessuna anomalia
tenant_retra active/on LANGUAGE_CHECK 25 6 feature language check ancora logica, non TenantFeatureFlag
tenant_besant active/on WOOCOMMERCE 29 2 produzione read-only; feature WooCommerce ancora logica, non TenantFeatureFlag

Conversione feature logica Retra

Eseguita il 2026-07-01 solo su tenant pilota tenant_retra.

Comando:

/root/.pyenv/versions/safeops/bin/flask feature_flag_set \
  --tenant-key tenant_retra \
  --feature-code feature.formazione.language_check \
  --enabled \
  --status active \
  --notes "Catalogo moduli Formazione: conversione feature logica language_check per pilota Retra"

Esito:

  • creato/aggiornato tenant_feature_flag per tenant_retra;
  • feature: feature.formazione.language_check;
  • stato: active;
  • enabled: 1;
  • nessuna modifica su tenant_besant;
  • nessuna modifica su dati operativi Formazione.

Post-check shadow:

/root/.pyenv/versions/safeops/bin/python tools/formazione_catalog_shadow_report.py \
  --tenant-key tenant_netopen \
  --tenant-key tenant_echos \
  --tenant-key tenant_fornautic \
  --tenant-key tenant_retra \
  --tenant-key tenant_besant \
  --format markdown \
  --output /tmp/formazione_catalog_shadow_after_retra_feature_flag.md

Risultato post-check:

Tenant Stato Plan hint Menu visibili Menu nascosti Note
tenant_netopen disabled/off OFF 0 31 controllo negativo coerente
tenant_echos active/on BASE 24 7 nessuna anomalia
tenant_fornautic active/on BASE 24 7 nessuna anomalia
tenant_retra active/on LANGUAGE_CHECK 25 6 nessuna anomalia
tenant_besant active/on WOOCOMMERCE 29 2 produzione read-only; feature WooCommerce ancora logica

Conversione feature logica Besant

Eseguita il 2026-07-01 solo dopo verifica read-only pre-conversione.

Verifica pre-conversione:

  • tenant_policy.py abilita WooCommerce per tenant_besant tramite provider wordpress_woocommerce;
  • nessun record preesistente in tenant_feature_flag per tenant_besant + feature.formazione.woocommerce_sync;
  • nessuna scrittura su ordini, corsi, iscrizioni, cataloghi WooCommerce o dati operativi.

Comando:

/root/.pyenv/versions/safeops/bin/flask feature_flag_set \
  --tenant-key tenant_besant \
  --feature-code feature.formazione.woocommerce_sync \
  --enabled \
  --status active \
  --notes "Catalogo moduli Formazione: conversione feature logica WooCommerce per Besant; nessun cambio dati operativi"

Esito:

  • creato/aggiornato tenant_feature_flag per tenant_besant;
  • feature: feature.formazione.woocommerce_sync;
  • stato: active;
  • enabled: 1;
  • nessuna subscription catalogo creata;
  • nessuna modifica a dati operativi Formazione/WooCommerce.

Post-check shadow:

/root/.pyenv/versions/safeops/bin/python tools/formazione_catalog_shadow_report.py \
  --tenant-key tenant_netopen \
  --tenant-key tenant_echos \
  --tenant-key tenant_fornautic \
  --tenant-key tenant_retra \
  --tenant-key tenant_besant \
  --format markdown \
  --output /tmp/formazione_catalog_shadow_after_besant_woocommerce_flag.md

Risultato post-check:

Tenant Stato Plan hint Menu visibili Menu nascosti Note
tenant_netopen disabled/off OFF 0 31 controllo negativo coerente
tenant_echos active/on BASE 24 7 nessuna anomalia
tenant_fornautic active/on BASE 24 7 nessuna anomalia
tenant_retra active/on LANGUAGE_CHECK 25 6 nessuna anomalia
tenant_besant active/on WOOCOMMERCE 29 2 resta solo guardrail produzione read-only

Tabelle tenant nuovo catalogo dopo post-check:

  • tenant_module_subscription: 0 righe;
  • tenant_module_limit: 0 righe;
  • tenant_module_rule: 0 righe;
  • tenant_module_activation_event: 0 righe.

Preview subscription tenant pilota

Preparata il 2026-07-01 con tool read-only/apply esplicito:

tools/formazione_subscription_plan.py

Preview eseguita senza scritture:

/root/.pyenv/versions/safeops/bin/python tools/formazione_subscription_plan.py \
  --tenant-key tenant_echos \
  --tenant-key tenant_fornautic \
  --format markdown \
  --output /tmp/formazione_subscription_preview_echos_fornautic.md

Esito preview:

Tenant Plan Azione prevista Stato legacy
tenant_echos base insert active
tenant_fornautic base insert active

Guardrail produzione verificato:

/root/.pyenv/versions/safeops/bin/python tools/formazione_subscription_plan.py \
  --tenant-key tenant_besant \
  --format markdown \
  --output /tmp/formazione_subscription_preview_besant_blocked.md

Esito:

  • tenant_besant bloccato senza --allow-production-tenant;
  • nessuna scrittura eseguita;
  • tenant_module_subscription resta invariata in questa fase preview.

Allineamento subscription tenant_echos

Eseguito il 2026-07-01 come primo tenant pilota, senza coinvolgere tenant_besant.

Comando:

/root/.pyenv/versions/safeops/bin/python tools/formazione_subscription_plan.py \
  --tenant-key tenant_echos \
  --apply \
  --format markdown \
  --output /tmp/formazione_subscription_apply_echos.md

Esito:

  • inserita 1 riga in tenant_module_subscription;
  • tenant: tenant_echos;
  • modulo: safe_formazione;
  • piano: base;
  • stato: active;
  • enabled: 1;
  • source: legacy_entitlement_shadow;
  • source_ref: formazione;
  • nessun limite/regola/evento creato.

Post-check:

/root/.pyenv/versions/safeops/bin/python tools/formazione_subscription_plan.py \
  --tenant-key tenant_echos \
  --format markdown \
  --output /tmp/formazione_subscription_postcheck_echos.md

Risultato:

  • azione successiva prevista: skip;
  • tenant_module_limit: 0 righe per safe_formazione;
  • tenant_module_rule: 0 righe per safe_formazione;
  • tenant_module_activation_event: 0 righe per safe_formazione;
  • shadow report: tenant_echos resta BASE, 24 menu visibili, 7 nascosti, nessuna anomalia;
  • tenant_besant resta solo in guardrail produzione read-only.

Migration catalogo moduli

Primo artefatto schema-only preparato:

migrations/versions/f0c2d4e6a8b0_add_module_catalog_tables.py

Scopo:

  • creare le tabelle nuove module_* e tenant_module_*;
  • non inserire seed;
  • non modificare tenant, menu, corsi, iscrizioni, scadenze o dati operativi Formazione;
  • non introdurre enforcement runtime;
  • unire le tre head Alembic locali in una sola head.

Verifiche eseguite:

/root/.pyenv/versions/safeops/bin/python -m py_compile \
  migrations/versions/f0c2d4e6a8b0_add_module_catalog_tables.py

Esito grafo migration locale:

heads:
- f0c2d4e6a8b0 f0c2d4e6a8b0_add_module_catalog_tables.py

Nota operativa iniziale: la migration e' stata preparata senza seed e senza modifiche ai dati Formazione.

Review tecnica del 2026-07-01:

  • nessun INSERT, UPDATE o DELETE;
  • nessuna modifica a tabelle operative Formazione esistenti;
  • nessuna foreign key verso corsi, iscrizioni, attestati, WooCommerce o dati tenant;
  • vincoli unique verificati senza colonne NULL critiche su MySQL;
  • legacy_service_code e scope_ref normalizzati a stringa vuota nello schema fisico per evitare duplicati non intercettati da MySQL;
  • head Alembic locale unica: f0c2d4e6a8b0.

Applicazione migration del 2026-07-01:

/root/.pyenv/versions/safeops/bin/flask db upgrade

Esito:

  • upgrade eseguito solo da 20260618_0835, d9e0f1a2b3c4, e5a7c9d2f410 a f0c2d4e6a8b0;
  • Alembic current: f0c2d4e6a8b0;
  • create 13 tabelle nuove catalogo moduli;
  • tutte le nuove tabelle risultano vuote;
  • nessun seed eseguito;
  • nessuna modifica a corsi, iscrizioni, attestati, WooCommerce o dati operativi di tenant_besant.

Decisione operativa

Procedere con Formazione in modo mirato:

  • tenant_echos e tenant_fornautic per prove base;
  • tenant_retra per language check;
  • tenant_netopen per controllo negativo;
  • tenant_besant solo per validazione read-only su dati reali.

Besant non deve essere il primo tenant su cui provare enforcement o sync.

Fornautic archivio formazione

Correzione mirata applicata il 2026-07-06:

  • tenant_fornautic non usa il piano BASE;
  • tenant_fornautic usa safe_formazione/archive;
  • il piano archive serve solo archivio corsi, materiali/documentazione corso, iscrizioni/discenti, anagrafiche essenziali, categorie e documentazione Fornautic;
  • i menu operativi di pianificazione, aula, import, WooCommerce, monitoraggio, reportistica, scadenze, gap, survey e qualita sono nascosti dal runtime;
  • il link Documenti Fornautic punta a /documenti-fornautic/list/;
  • il link Materiali Formazione usa /materialeformazioneview/list/ per evitare voci archivio senza URL risolvibile.

Verifica:

  • shadow report Fornautic: ARCHIVE, 7 menu visibili, 25 nascosti, nessuna anomalia;
  • audit runtime su demo.fornautic: visibili solo le aree archivio/anagrafiche/didattica minima;
  • tenant_besant non modificato.