← Developers

00.1 — Contratto Zero: fatti immutabili

Spec riferita a SPEC-DX-01

L’Invariante

La realtà di produzione si registra come fatti immutabili, append-only. Un lotto verificato, un ordine accettato, un verdetto di collaudo — ognuno si scrive una volta e non si muta mai. Ogni superficie di lettura (dashboard, export, verifica) è calcolata in lettura dalle righe committate e dallo stream, mai fidata da contatori in cache.

La regola da non violare mai: lo stream è la verità, tutto il resto è una vista derivata. Se un numero non si può ricostruire rileggendo gli eventi, non appartiene al sistema.

È ciò che rende l’architettura replicabile, auditabile e debuggabile — lo stesso pattern dei ledger finanziari, applicato alla manifattura.

Il Contratto

Envelope canonico dell’evento — il contratto sul filo tra edge e Control Plane:

interface ProductionEvent {
  id: string;                    // uuid — chiave di dedup GLOBALE (cp_event_dedup)
  aggregate_type: string;        // la tabella biz_* che l'ha prodotto (es. BIZ_LOTTI)
  aggregate_id: string;          // uuid/id della riga proprietaria
  event_type: string;            // INSERT_* | UPDATE_* — l'op data-link che l'ha scritto
  payload: Record<string, unknown>; // il fatto stesso — mai mutato dopo la scrittura
  created_at?: string;           // ISO-8601 UTC, orologio lato edge
}

Il Flusso

produttore (dashboard operatore / integrazione macchina / plugin)
  → POST /api/v1/actions/:trigger        (edge, sessione autenticata)
  → la pipeline valida: RBAC + constraint + FSM + data-link   [una transazione]
  → riga in biz_* + riga audit + entry ax_cdc_outbox
  → drain outbox → POST /sync/ingest     (cloud, edge api_key, firmata)
  → cp_events (dedup su id) → i read model calcolano in lettura

Le scritture sono local-first: l’edge lavora anche a cloud spento; l’outbox si svuota quando torna la connettività. La consegna duplicata è un path di retry progettato, non un errore.

Esempio di Payload

# Un fatto di produzione come lo sottomette il daemon edge
curl -X POST https://cp.axiom-trace.eu/api/v1/sync/ingest \
  -H "Authorization: Bearer $EDGE_API_KEY" \
  -H 'content-type: application/json' \
  -d '{"events":[{
        "id":"9f2c…","aggregate_type":"BIZ_COLLAUDI",
        "aggregate_id":"<row-uuid>","event_type":"INSERT_BIZ_COLLAUDI",
        "payload":{"pezzo":"PZ-001","misura":0.018,"esito":"CONFORME"},
        "created_at":"2026-09-26T14:01:04Z"}]}'
# → 201 { processed_count: 1, duplicate_count: 0 }
# Rimanda lo stesso batch → 201 { processed_count: 0, duplicate_count: 1 }

I Fallimenti Noti

Violazione Esito
Stesso id evento rimandato Deduplicato in silenzio — cresce duplicate_count, nessun errore
created_at in ora locale Corruzione dell’audit — i timestamp sono UTC
Mutare un evento dopo la scrittura Nessun code path esiste — emetti un fatto correttivo
>1000 eventi per batch ingest 413 BATCH_TOO_LARGE — spezza il batch

Testo normativo: spec/ nel repository Axiom-Trace. Se questa pagina e la spec divergono, vince la spec.