Vai al contenuto

DVR First Slice Operational Hardening

Stato di riferimento:

  • dvr_first_slice_completed_with_notes
  • dvr_operational_hardening_ready_with_notes

Scopo di questo documento:

  • chiudere l'operativita' del primo slice DVR
  • definire una strategia migration prudente
  • definire uno smoke script ufficiale del modulo DVR

Warning forti

  • Nessun stamp cieco.
  • Nessun intervento su ambienti non allineati senza backup DB.
  • Non riscrivere la migration 0003_dvr_initial_slice se e' gia' stata applicata su ambienti reali.
  • Su ambienti schema-misaligned non eseguire flask db upgrade alla cieca.

Stato Alembic del modulo DVR

Chain DVR nel repository:

  • 0001_initial
  • 0002_optimization_results
  • 0003_dvr_initial_slice

La revisione 0003_dvr_initial_slice.py crea:

  • dvr_case
  • dvr_case_snapshot
  • dvr_section_instance
  • dvr_measure_action
  • dvr_export
  • dvr_case_event

Strategia operativa

Caso A. Ambiente pulito

Condizioni:

  • history Alembic coerente col repository
  • tabelle DVR non presenti

Procedura:

  1. Eseguire backup DB.
  2. Verificare:
  3. flask db current
  4. flask db history
  5. Applicare:
  6. flask db upgrade

Sicuro:

  • migration Alembic normale

Non sicuro:

  • creare tabelle DVR a mano
  • fare stamp senza motivo

Caso B. Tabelle DVR presenti ma migration non registrata

Condizioni:

  • tabelle dvr_* gia' presenti
  • migration 0003_dvr_initial_slice non registrata

Procedura:

  1. Eseguire backup DB.
  2. Verificare parity completa tra DB reale e migration 0003.
  3. Solo se la parity e' completa:
  4. flask db stamp 0003_dvr_initial_slice
  5. Riprendere il flusso standard:
  6. flask db upgrade

Sicuro:

  • stamp solo dopo audit strutturale completo

Non sicuro:

  • stamp cieco
  • upgrade diretto se Alembic tenterebbe di creare oggetti gia' esistenti

Caso C. Ambiente schema-misaligned

Condizioni:

  • alembic_version fuori tree
  • history diverge dal repository
  • tabelle create manualmente o schema non coerente

Procedura:

  1. Eseguire backup DB.
  2. Leggere:
  3. SELECT version_num FROM alembic_version;
  4. Verificare presenza e forma delle tabelle DVR.
  5. Scegliere esplicitamente:
  6. parity + stamp, solo se schema reale e 0003 coincidono
  7. migration correttiva nuova, se non coincidono

Sicuro:

  • runbook + audit + decisione esplicita

Non sicuro:

  • stamp head
  • aggiornare alembic_version manualmente
  • drop/recreate delle tabelle con dati

Checklist di audit tabella-per-tabella

Usare:

SHOW CREATE TABLE dvr_case;
SHOW CREATE TABLE dvr_case_snapshot;
SHOW CREATE TABLE dvr_section_instance;
SHOW CREATE TABLE dvr_measure_action;
SHOW CREATE TABLE dvr_export;
SHOW CREATE TABLE dvr_case_event;

dvr_case

Colonne attese:

  • id
  • tenant_key
  • documento_id
  • cliente_id
  • cliente_sede_id
  • case_key
  • status
  • revision_no
  • derived_from_case_id
  • template_registry_id
  • template_version_id
  • easy_builder_template_id
  • opened_at
  • ready_for_generation_at
  • generated_at
  • created_by_id
  • created_at
  • updated_at

FK attese:

  • documento_id -> documento.id
  • cliente_id -> cliente.id
  • cliente_sede_id -> cliente_sede.id
  • derived_from_case_id -> dvr_case.id
  • template_registry_id -> template_registry.id
  • template_version_id -> template_version.id

Unique attesi:

  • uq_dvr_case_case_key su case_key

Indici attesi:

  • ix_dvr_case_tenant_key_status
  • ix_dvr_case_cliente_id
  • ix_dvr_case_documento_id
  • ix_dvr_case_cliente_sede_id
  • ix_dvr_case_derived_from_case_id

dvr_case_snapshot

Colonne attese:

  • id
  • dvr_case_id
  • snapshot_kind
  • payload_json
  • checksum
  • created_by_id
  • created_at

FK attese:

  • dvr_case_id -> dvr_case.id

Unique attesi:

  • uq_dvr_case_snapshot_kind su (dvr_case_id, snapshot_kind)

Indici attesi:

  • ix_dvr_case_snapshot_dvr_case_id
  • ix_dvr_case_snapshot_snapshot_kind
  • ix_dvr_case_snapshot_checksum

dvr_section_instance

Colonne attese:

  • id
  • dvr_case_id
  • section_code
  • section_label
  • sort_order
  • applicability_status
  • structured_payload_json
  • technical_notes
  • updated_by_id
  • created_at
  • updated_at

FK attese:

  • dvr_case_id -> dvr_case.id

Unique attesi:

  • uq_dvr_section_case_code su (dvr_case_id, section_code)

Indici attesi:

  • ix_dvr_section_instance_dvr_case_id
  • ix_dvr_section_instance_section_code
  • ix_dvr_section_instance_applicability_status

dvr_measure_action

Colonne attese:

  • id
  • dvr_case_id
  • section_instance_id
  • action_type
  • description
  • responsabile_label
  • due_date
  • status
  • note
  • created_by_id
  • created_at
  • updated_at

FK attese:

  • dvr_case_id -> dvr_case.id
  • section_instance_id -> dvr_section_instance.id

Unique attesi:

  • nessuno

Indici attesi:

  • ix_dvr_measure_action_dvr_case_id
  • ix_dvr_measure_action_section_instance_id
  • ix_dvr_measure_action_action_type
  • ix_dvr_measure_action_status
  • ix_dvr_measure_action_due_date

dvr_export

Colonne attese:

  • id
  • dvr_case_id
  • documento_id
  • source_snapshot_id
  • export_kind
  • version_no
  • render_checksum
  • created_by_id
  • created_at

FK attese:

  • dvr_case_id -> dvr_case.id
  • documento_id -> documento.id
  • source_snapshot_id -> dvr_case_snapshot.id

Unique attesi:

  • nessuno

Indici attesi:

  • ix_dvr_export_dvr_case_id
  • ix_dvr_export_documento_id
  • ix_dvr_export_source_snapshot_id
  • ix_dvr_export_render_checksum

dvr_case_event

Colonne attese:

  • id
  • dvr_case_id
  • event_code
  • from_status
  • to_status
  • payload_json
  • actor_user_id
  • created_at

FK attese:

  • dvr_case_id -> dvr_case.id

Unique attesi:

  • nessuno

Indici attesi:

  • ix_dvr_case_event_dvr_case_id
  • ix_dvr_case_event_event_code

Comandi operativi

Verifica revision Alembic:

/root/.pyenv/versions/safeops/bin/flask --app app db current
/root/.pyenv/versions/safeops/bin/flask --app app db history

Verifica DB:

SELECT version_num FROM alembic_version;
SHOW TABLES LIKE 'dvr_%';

Solo dopo parity completa, nel caso B:

/root/.pyenv/versions/safeops/bin/flask --app app db stamp 0003_dvr_initial_slice
/root/.pyenv/versions/safeops/bin/flask --app app db upgrade

Smoke script ufficiale

Script:

Scopo:

  • verificare il primo slice DVR via services esistenti
  • nessuna nuova logica di dominio

Input minimi:

DATABASE_URL='mysql+pymysql://appuser:***@127.0.0.1:3306/appdb' \
SAFEOPS_SKIP_APPBUILDER_BOOTSTRAP=1 \
/root/.pyenv/versions/3.11.5/bin/python /home/safeops/safeops/scripts/dvr_first_slice_smoke.py \
  --documento-id 123 \
  --actor-username dvr_smoke

Verifiche eseguite:

  1. create_case
  2. bootstrap 3 sezioni
  3. snapshot prefill
  4. compilazione 3 sezioni
  5. almeno una azione
  6. transizione in_compilazione
  7. transizione pronto_per_generazione
  8. snapshot issued
  9. export PDF
  10. stato finale generato

Exit code:

  • 0 se tutto passa
  • 1 se una verifica critica fallisce

Output JSON stabile:

  • ok
  • script
  • started_at
  • finished_at
  • duration_ms
  • input
  • case
  • sections
  • actions
  • snapshots
  • export
  • events
  • errors
  • warnings

Cleanup:

  • nessun cleanup automatico
  • usare gli ID stampati in output per eventuale cleanup manuale in ambienti non produttivi

Guardrail

Non toccare:

  • dominio DVR
  • workflow DVR
  • Compila DVR
  • renderer core
  • snapshot logic

Non rimettere dentro documentale:

  • workflow DVR
  • bootstrap case
  • snapshot logic
  • audit di dominio DVR

Non complicare questo hardening:

  • Easy alignment
  • nuove feature
  • cleanup automatico
  • automazioni aggressive per ambienti schema-misaligned

Lo smoke script non deve diventare:

  • un workflow parallelo
  • un provisioning tool
  • un refactor mascherato del dominio DVR