Catena del Freddo
Monitoraggio della temperatura per refrigerazione e trasporto con deviazioni tracciabili, grace configurabile e journey/leg per le spedizioni.
Catena del Freddo
La catena del freddo è il controllo ininterrotto della temperatura dalla produzione al consumo. Se un asset o una spedizione refrigerata esce dalla banda di sicurezza, il prodotto può essere compromesso. Rela-ai monitora ogni asset, ogni lotto e ogni tratta di trasporto, lasciando un audit trail verificabile per i regolatori.
Sintesi esecutiva
Catena del freddo senza cry-wolf: ogni deviazione attraversa filtri di staleness, flapping e grace configurabile prima di raggiungere l'inbox — e finisce collegata al lotto e al leg di trasporto che l'hanno provocata.
Prima di questa iterazione, una deviazione di 40 secondi per l'apertura di una porta poteva generare tre alert separati e zero informazioni sul lotto interessato. Adesso:
| Prima | Adesso |
|---|---|
| Sensore morto che invia 0 °C fabbricava deviazioni false | Filtro CC-H1: sorgenti stale scartate |
| Cold chain alertava separatamente dal resto del sistema | CC-H2: feed all'alert aggregator con source_system=cold_chain |
| Impossibile sapere QUALE sensore aveva aperto la deviazione | CC-H3: source_id persistito su ogni deviazione |
| Apri/chiudi/apri porta = 2 incidenti artificiali | CC-M2: riapertura dentro una flap window di 5 min |
Mutazioni di temperature_max invisibili | CC-M5: audit trail fire-and-forget |
| Grace rigida "warning a critical" | CC-M1: modalità escalate o suppress (stile HACCP) |
| Impossibile sapere QUALE lotto era dentro | CC-L1: batch_id e product_type sulla deviazione |
| Impossibile sapere QUALE leg di trasporto aveva fallito | CC-M6: journey + leg con excursion_ids |
A cosa serve
- Rilevare temperature fuori banda in congelatori, celle frigorifere e veicoli.
- Distinguere una porta aperta 30 secondi da un guasto del compressore di 2 ore.
- Attribuire ogni breach al lotto e al leg di trasporto responsabili.
- Produrre audit trail immutabile per HACCP, GDP, WHO TRS 961, FSMA.
- Unificare gli alert con il resto del pipeline (manutenzione, qualità, SPC).
Come funziona
flowchart LR
SENSOR[Sensore] --> READING[Lettura]
READING --> STALE{Sorgente stale?}
STALE -- sì --> DROP[Scarto silenzioso]
STALE -- no --> BAND{Fuori banda?}
BAND -- no --> CLEAR[Pulisce buffer + risolve attiva]
BAND -- sì --> ACTIVE{Deviazione attiva?}
ACTIVE -- sì --> UPDATE[Update peak, duration, sample_count]
ACTIVE -- no --> FLAP{Flap window 5 min?}
FLAP -- sì --> REOPEN[Riapre deviazione precedente]
FLAP -- no --> MODE{Grace mode?}
MODE -- escalate --> OPEN[Apre in warning, escala a critical se dura]
MODE -- suppress --> BUFFER[Buffer volatile: promuove solo se dura]
OPEN --> AGG[Alert Aggregator]
REOPEN --> AGG
BUFFER --> AGG
OPEN --> JOURNEY[Attach al leg attivo se applicabile]
BUFFER --> JOURNEYFiltri e protezioni
- CC-H1 — staleness: una sorgente contrassegnata
staledal sensor watchdog viene ignorata prima di valutare la banda. Un sensore morto che invia 0 °C non può fabbricare deviazioni false. - CC-M2 — flap window: se una deviazione risolta della stessa direzione riappare entro 5 minuti, viene riaperta invece di crearne una nuova. Evita di frammentare un incidente reale.
- CC-H2 — aggregator feed: ogni deviazione nuova o escalata pubblica all'alert aggregator con
source_system=cold_chain, severità canonica e riferimentoexcursion_id. - CC-M1 — grace configurabile:
escalate(default): ogni deviazione apre inwarning, promuove acriticalal superamento del grace.suppress(stile HACCP): i transienti dentro il grace non vengono persistiti; una deviazione si apre solo se supera il grace, e apre direttamente incritical.
Configurazione dell'asset
Dashboard
- Asset → apparecchiatura → modifica.
- Attiva Catena del Freddo.
- Compila:
- Temperatura min / max: limiti di sicurezza.
- Unità: °C o °F.
- Minuti di grace: da 1 a 1440. Fino a 24 ore per spedizioni farmaceutiche (WHO TRS 961 Annex 9, EU GDP).
- Modalità grace:
escalateosuppress. - Sorgente eventi: il
source_idche fornisce le letture. Validata contro_machine_event_sources(CC-M4).
API
PATCH /api/v1/assets/{asset_id}
{
"cold_chain_enabled": true,
"cold_chain_source_id": "src_fridge_01",
"cold_chain_metric": "temperature",
"temperature_min": -22,
"temperature_max": -16,
"temperature_unit": "C",
"excursion_grace_minutes": 15,
"cold_chain_grace_mode": "escalate"
}CC-M5: qualsiasi modifica agli 8 campi regolati (cold_chain_enabled, cold_chain_source_id, cold_chain_metric, temperature_min, temperature_max, temperature_unit, excursion_grace_minutes, cold_chain_grace_mode) scrive una entry in _audit_trail con actor, timestamp, snapshot precedente e nuovo. Un auditor può rispondere "chi ha spostato temperature_max da −18 a −12 il 3 marzo".
Ingestione delle letture
Due percorsi, stesso pipeline (stessi filtri, stessa dedup, stesso aggregator).
Percorso 1 — via machine events
Configura una event_source di tipo http / mqtt / opcua. Ogni evento con il campo temperature (o quello configurato in cold_chain_metric) attiva check_cold_chain_from_event.
Percorso 2 — ingestione diretta POST /api/v1/cold-chain/readings
Per sensori fuori dal pipeline di machine_events: logger Bluetooth, sonde portatili al ricevimento, registratori di trasporto che caricano in batch all'arrivo.
POST /api/v1/cold-chain/readings
{
"source_id": "logger-bt-042",
"asset_id": "65a...",
"temperature": -12.4,
"batch_id": "LOT-2026-03-A",
"product_type": "vaccino mRNA"
}Se ometti asset_id, il servizio risolve tutti gli asset con quel cold_chain_source_id e valuta ciascuno.
CC-L1: batch_id e product_type vengono propagati sulla deviazione generata. Un breach su un frigorifero che conteneva insulina alle 14:00 e yogurt alle 17:00 sono due storie di compliance diverse — l'asset è lo stesso, il lotto dentro ruota.
Modalità di grace — escalate vs suppress
escalate (default)
Ogni deviazione viene registrata dalla prima lettura. Adatto per food-service in cui ogni apertura di porta conta.
t=0s Lettura fuori banda -> Deviazione aperta (severity=warning)
t=5s Lettura fuori banda -> Update: sample_count=2, peak aggiornato
t=30s Lettura in banda -> Deviazione risoltaRisultato: una riga in _cold_chain_excursions con duration=0.5 min.
suppress (stile HACCP)
Le deviazioni transienti vengono perdonate. Si persistono solo quelle che superano il grace, e si aprono direttamente in critical. Adatto per farmaceutica e GDP dove il cry-wolf alerting distrugge il segnale.
t=0s Lettura fuori banda -> Buffer aperto (NESSUNA deviazione persistita)
t=30s Lettura fuori banda -> Buffer aggiorna sample_count + peak
t=2min Lettura in banda -> Buffer cancellato silenziosamente (0 deviazioni)Se la deviazione dura abbastanza:
t=0s Fuori banda -> Buffer aperto
t=15min Fuori banda -> elapsed >= grace: promuove
deviazione persistita con severity=critical,
started_at=t=0s, duration=15minCambia modalità per asset via cold_chain_grace_mode. Il default escalate preserva il comportamento storico per asset precedenti al flag.
Journey / Leg — spedizioni refrigerate
CC-M6. Un journey è una spedizione end-to-end. Un leg è un segmento di custodia (magazzino poi camion poi centro di distribuzione poi farmacia). I passaggi di consegna tra leg sono i momenti di maggiore rischio nella catena del freddo e sono dove sorgono le domande di attribuzione ("di chi è il camion che si è scaldato?").
Modellare una spedizione
POST /api/v1/cold-chain/journeys
{
"journey_code": "JRN-2026-03-001",
"origin": "DC Madrid",
"destination": "Farmacia Valencia",
"batch_id": "LOT-42",
"product_type": "insulina",
"legs": [
{"sequence": 0, "from_location": "DC Madrid", "to_location": "Cross-dock"},
{"sequence": 1, "from_location": "Cross-dock", "to_location": "Camion 7", "asset_id": "65a..."},
{"sequence": 2, "from_location": "Camion 7", "to_location": "Farmacia"}
]
}Ciclo di vita
stateDiagram-v2
[*] --> planned
planned --> in_transit: start_leg (prima volta)
in_transit --> in_transit: start_leg / complete_leg
in_transit --> delivered: ultimo leg completato
planned --> cancelled
in_transit --> cancelledPOST /api/v1/cold-chain/journeys/{id}/legs/{leg_id}/start— marca il leg comeactivee, alla prima attivazione, stampaactual_start_atsul journey. Attivazioni successive NON sovrascrivono la partenza originale.POST /api/v1/cold-chain/journeys/{id}/legs/{leg_id}/complete— marca il leg comecompletedconended_at.
Attribuzione automatica delle deviazioni
Quando check_excursion apre una nuova deviazione per un asset_id, il servizio cerca un journey in_transit con un leg active che referenzi quell'asset. Se lo trova, aggiunge l'excursion_id all'array legs.$.excursion_ids del leg. Fire-and-forget: un fallimento sul lato journey non interrompe mai il pipeline della catena del freddo.
L'audit risponde "quale leg di trasporto ha fallito?" senza join manuali tra collection.
Dashboard di stato
GET /api/v1/cold-chain/status restituisce, per asset, i tre numeri che un operatore legge a colpo d'occhio:
| Campo | Significato |
|---|---|
peak_deviation | Di quanto è uscito dalla banda (unità dell'asset). |
duration_minutes | Da quanto tempo è fuori. |
sample_count | Quante letture sostengono la deviazione (CC-L3). |
Un critical con 1 campione è un glitch; un critical con 20 campioni in 20 minuti è un evento reale. Il terzo numero è ciò che separa il triage reale dal rumore.
[
{
"id": "65a...",
"name": "Freezer Unit A",
"temperature_min": -22,
"temperature_max": -16,
"has_excursion": true,
"peak_deviation": 3.2,
"duration_minutes": 18.5,
"sample_count": 37,
"excursion": { "id": "...", "severity": "critical", "source_id": "src_fridge_01", "batch_id": "LOT-42" }
}
]Endpoint
| Metodo | Path | Cosa fa |
|---|---|---|
POST | /api/v1/cold-chain/readings | Ingestione diretta con batch_id / product_type. |
GET | /api/v1/cold-chain/status | Stato per asset + peak/duration/sample. |
GET | /api/v1/cold-chain/excursions | Storico con filtri asset_id, resolved. |
POST | /api/v1/cold-chain/journeys | Crea journey + leg. |
GET | /api/v1/cold-chain/journeys | Lista, filtra per status e batch_id. |
POST | /api/v1/cold-chain/journeys/{id}/legs | Aggiunge un leg. |
POST | /api/v1/cold-chain/journeys/{id}/legs/{leg_id}/start | Marca leg active. |
POST | /api/v1/cold-chain/journeys/{id}/legs/{leg_id}/complete | Marca leg completed. |
Scenari reali
1. Apertura breve di porta su freezer carne
Grace mode escalate, grace 15 min. Un operatore apre la porta per 40 secondi. La temperatura sale da −20 a −16.5 °C.
- t=0s: deviazione aperta (warning, duration 0).
- t=20s: update (sample_count=2, peak 3.5 °C).
- t=40s: in-banda poi resolve. Duration 0.7 min.
- Dashboard mostra
peak=3.5,duration=0.7,sample_count=2. L'operatore scarta.
2. Spedizione di vaccini con breach al leg 2
Grace mode suppress, grace 60 min. Journey con 3 leg. Il camion resta imbottigliato nel traffico e la temperatura supera 8 °C per 75 minuti.
- Leg 2 attivo, asset = "Camion 7".
- t=0: buffer aperto (nessuna deviazione persistita).
- t=60min: buffer supera il grace poi promuove. Deviazione
criticalconstarted_at= t=0,duration=60min,severity=critical,batch_id=LOT-42,product_type=insulina. - L'hook CC-M6 aggiunge
excursion_idal leg 2. Audit risponde: "il breach è avvenuto sul leg Cross-dock poi Camion 7".
3. Sensore morto che invia 0 °C
Il sensor watchdog contrassegna src_fridge_05 come stale. Il reading pipeline riceve 0 °C (fuori banda contro un pavimento di −18). CC-H1 scarta la lettura alla prima riga di check_excursion. Nessuna deviazione fantasma.
Limitazioni e ipotesi
cold_chain_enabled=Truerichiede uncold_chain_source_idvalido in_machine_event_sources(CC-M4). Se la sorgente viene eliminata successivamente, il servizio logga un warning ma non blocca; la validazione è un early-warning.- La flap window è fissa a 5 minuti. Regolabile solo nel codice (
_COLD_CHAIN_FLAP_WINDOW_MINUTES). suppressnon si applica al flap-reopen: se una deviazione PERSISTITA risolve e ne appare una nuova nella flap window, riapre indipendentemente dalla modalità (la decisione sul rumore è già stata presa).journey.statusnon si chiude automaticamente quando l'ultimo leg viene completato; richiede unPATCHesplicito adelivered.
Collection MongoDB per-tenant
| Collection | Contenuto |
|---|---|
_cold_chain_excursions | Deviazioni persistite (schema completo con batch_id, product_type, source_id, sample_count, peak_deviation, duration_minutes). |
_cold_chain_excursion_buffers | Buffer volatili di modalità suppress. Cancellati al rientro in banda o alla promozione. |
_cold_chain_journeys | Spedizioni con leg embedded. |
_audit_trail | Entry fire-and-forget con action=asset_cold_chain_updated. |
Findings chiusi in questa iterazione
| ID | Cosa è stato chiuso |
|---|---|
| CC-H1 | Filtro di staleness prima del range check. |
| CC-H2 | Feed all'aggregator con source_system dedicato. |
| CC-H3 | source_id persistito sulla deviazione (propagato dal webhook). |
| CC-M1 | Flag cold_chain_grace_mode con modalità HACCP-style. |
| CC-M2 | Flap window di 5 min per il reopen di deviazioni consecutive. |
| CC-M3 | Endpoint POST /readings per ingestione senza machine_events. |
| CC-M4 | Validazione di cold_chain_source_id in asset create/update. |
| CC-M5 | Audit trail sulle mutazioni degli 8 campi regolati. |
| CC-M6 | Journey + Leg con attribuzione automatica. |
| CC-L1 | batch_id + product_type persistiti sulla deviazione. |
| CC-L2 | Range di excursion_grace_minutes allargato a 1 fino 1440. |
| CC-L3 | peak_deviation, duration_minutes, sample_count nello status. |
Benefici chiave
- Audit trail verificabile per HACCP, GDP, WHO TRS 961, FSMA.
- Zero cry-wolf: staleness + flapping + modalità di grace rimuovono il rumore strutturale.
- Attribuzione reale: ogni breach conosce lotto, prodotto e leg.
- Inbox unificato: stesso inbox di manutenzione e qualità; l'operatore non cambia schermata.
- Pharma-ready: grace fino a 24 h, modalità suppress, tracking batch, modello journey.
HACCP — Controllo Sanitario Verificabile
Piani HACCP, CCP con grace period reale, azioni correttive che generano work order, e chain-of-custody per lotto. Certificabile sotto SQF / BRC / FSSC 22000.
Azioni Correttive
Documenta azioni correttive per deviazioni HACCP ed escursioni della catena del freddo