Fase 4 - Piano mirato Formazione con protezione Besant¶
Data audit: 2026-06-30
Documenti collegati:
docs/processo/fase3_pilota_modulo_formazione.mddocs/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:
tenant_netopencome controllo negativo;tenant_echoscome pilota base;tenant_fornauticcome pilota base;tenant_retracome pilota con language check;tenant_besantsolo 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_formazionetarget;formazionelegacy;TenantModuleEntitlement;- eventuale futura
tenant_module_subscription.
Regola: nessun sync automatico verso entitlement su Besant.
Step 7 - Primo enforcement¶
Non partire da Besant.
Ordine consigliato:
tenant_netopen: verifica negazione;tenant_echos: menu base;tenant_fornautic: menu base;tenant_retra: language check;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:
- Il primo comando deve essere read-only.
- Il primo tenant operativo deve essere
tenant_netopenotenant_echos, non Besant. - Ogni comando deve avere
--dry-runse puo scrivere. - Ogni comando che scrive deve richiedere
--apply. - Ogni report deve indicare tenant, modulo, feature, menu e timestamp.
- Ogni scrittura futura deve creare evento audit.
- Ogni script che puo scrivere deve rifiutare Besant se non riceve un flag esplicito tipo
--allow-production-tenant. - 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 |
Seed preview catalogo¶
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_previewignorato 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_flagpertenant_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.pyabilita WooCommerce pertenant_besanttramite providerwordpress_woocommerce;- nessun record preesistente in
tenant_feature_flagpertenant_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_flagpertenant_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_besantbloccato senza--allow-production-tenant;- nessuna scrittura eseguita;
tenant_module_subscriptionresta 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 persafe_formazione;tenant_module_rule: 0 righe persafe_formazione;tenant_module_activation_event: 0 righe persafe_formazione;- shadow report:
tenant_echosrestaBASE, 24 menu visibili, 7 nascosti, nessuna anomalia; tenant_besantresta 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_*etenant_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,UPDATEoDELETE; - nessuna modifica a tabelle operative Formazione esistenti;
- nessuna foreign key verso corsi, iscrizioni, attestati, WooCommerce o dati tenant;
- vincoli unique verificati senza colonne
NULLcritiche su MySQL; legacy_service_codeescope_refnormalizzati 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,e5a7c9d2f410af0c2d4e6a8b0; - 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_echosetenant_fornauticper prove base;tenant_retraper language check;tenant_netopenper controllo negativo;tenant_besantsolo 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_fornauticnon usa il pianoBASE;tenant_fornauticusasafe_formazione/archive;- il piano
archiveserve 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 Fornauticpunta a/documenti-fornautic/list/; - il link
Materiali Formazioneusa/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_besantnon modificato.