← Developers

02.1 — Motore 1: Esecuzione (FSM + pipeline atomica)

Spec riferita a SPEC-DX-01 / actions v1

L’Invariante

Una mutazione di business o committa in modo atomico — transizione FSM, scrittura data-link, riga di audit, evento outbox CDC — oppure non esiste. Non c’è stato parziale: un constraint fallito fa rollback della transazione e lascia solo l’evidenza in audit. Lo stesso Idempotency-Key restituisce sempre lo stesso esito.

Il Contratto

POST /api/v1/actions/:trigger sull’edge è l’unica superficie di mutazione. Ogni richiesta attraversa la stessa pipeline fail-closed, in ordine:

auth → idempotenza → RBAC → constraint → FSM → data-link → audit → commit

Un rifiuto a qualsiasi stadio interrompe la richiesta — nulla a valle viene eseguito.

Il Flusso

  1. auth — PIN operatore o device key risolvono tenant e ruoli dalla sessione autenticata, mai dai campi payload.
  2. idempotenza — l’header Idempotency-Key viene deduplicato; un replay restituisce la risposta originale.
  3. RBAC — le permission rbac_engine del manifest mappano ruoli → trigger ammessi; OPERATORE non può lanciare QC_PASS.
  4. constraint — le pre_commit_rules legate al trigger vengono valutate; una violazione risponde 422 CONSTRAINT_VIOLATED.
  5. FSM — il trigger deve corrispondere a una transizione from → to dichiarata per lo stato corrente dell’aggregato.
  6. data-link — la query manifest del trigger viene eseguita nella stessa transazione.
  7. audit + commit — riga di audit ed evento ax_cdc_outbox vengono scritti col commit; il sync drena in seguito.

Esempio di Payload

# Un operatore qualità chiude un collaudo — il constraint engine verifica la misura
curl -X POST http://edge:8291/api/v1/actions/QC_PASS \
  -H "Authorization: Bearer $EDGE_TOKEN" \
  -H "Idempotency-Key: qc-L0043-001" \
  -H 'content-type: application/json' \
  -d '{
        "payload": {
          "piece_id": "PZ-9044",
          "misura": 0.018
        }
      }'
# → 200 { committed: true, to_state: "CONFORME" }
#
# La stessa chiamata con "misura": 0.026 viola `tolleranza_collaudo`
# → 422 { error: "CONSTRAINT_VIOLATED", rule: "tolleranza_collaudo" }
# → nessun commit, nessuna riga di business — la catena di audit conserva il tentativo

I Fallimenti Noti

Sintomo Causa
401 Sessione mancante/scaduta — rifare login via /api/v1/auth/login
403 Il ruolo non ha il trigger in rbac_engine.permissions — è un diniego, non un bug
409 Transizione FSM non dichiarata dallo stato corrente (es. QC_PASS prima di START_MEASURE)
422 CONSTRAINT_VIOLATED Una pre_commit_rule è fallita — il payload è fuori dall’inviluppo dichiarato
Un retry ha successo in silenzio Lo stesso Idempotency-Key ha restituito la risposta originale in cache — by design