In questa sezione

CapDAG trasporta in modo strutturale l’identità e la posizione di una diagnostica. Il codice sorgente ne dichiara il significato; i livelli di trasporto e di esecuzione lo preservano senza analizzare testo in prosa né inventare valori mancanti.

Classe di attribuzione

AttributionClass identifica il dominio responsabile di una diagnostica. È usata dai frame ERR terminali, da ogni frame LOG non di avanzamento e dai frame Cancel (§La cancellazione parla la tassonomia).

Classe Significato Politica di nuovo tentativo terminale
input Deterministica rispetto all’input o agli argomenti forniti. Permanente.
resource Una risorsa di calcolo è stata esaurita. Ritentabile.
environment È fallito il deployment, la rete, il registro, il modello o l’ambiente del processo. Ritentabile.
internal Un difetto del motore o della cartuccia. Ritentabile; il guasto è nel motore o nella cartuccia.
user L’ha deciso l’operatore: una cancellazione, o una policy impostata dall’operatore che rifiuta il lavoro (NETWORK_POLICY, Policy). Non è un fallimento. Permanente (mai ritentata automaticamente).

La colonna della politica di nuovo tentativo viene applicata in un solo punto: un passo il cui wiring porta retry=N (Notazione delle macchine) viene tentato di nuovo solo quando la classe del suo fallimento è qui ritentabile (Esecuzione). Nulla ritenta un passo che non l’ha chiesto, e nulla ritenta input o user, qualunque cosa sia stata chiesta. on_error=absent tollera un fallimento di qualsiasi classe tranne user.

La classificazione viene dichiarata nella definizione dell’errore tipizzato o nel punto di emissione della diagnostica. I livelli che la incapsulano la trasportano alla lettera. Un ordinario errore dell’handler che raggiunge il confine di emissione terminale senza una classificazione tipizzata viene emesso esplicitamente come HANDLER_ERROR / internal; si tratta di una decisione presa al confine della sorgente, non di un ripiego del ricevente.

Contratto sul wire

Il protocollo v6 usa un’unica forma di attribuzione per le diagnostiche della sorgente:

Frame Metadati obbligatori Metadati facoltativi
LOG non di avanzamento level non vuoto, message non vuoto, attribution_class valido arg_urn non vuoto
ERR code non vuoto, message non vuoto, attribution_class valido arg_urn non vuoto
LOG di avanzamento level = progress, progress nessuno dei campi di attribuzione diagnostica

La chiave sul wire è attribution_class. Token di classe mancanti o sconosciuti, testo obbligatorio vuoto, un arg_urn vuoto o metadati diagnostici su un frame di avanzamento sono violazioni del protocollo. I riceventi fanno fallire la richiesta o lo stream malformati; non deducono mai internal né un argomento.

Coordinata nel grafo

La posizione di una diagnostica viene composta dal livello di esecuzione a partire dalla notazione delle macchine, che è stabile, e dal frame sorgente:

  • token_id è lo StrandStep.token_id stabile e indirizza il passo del grafo.
  • body_index, quando presente, è soltanto l’ordinale, a partire da zero, dell’invocazione all’interno di una regione ForEach. Non identifica mai un nodo del grafo.
  • arg_urn, quando presente, identifica l’unico argomento nominato dalla sorgente dell’emissione.

La coordinata completa è quindi token_id più un eventuale body_index più un eventuale arg_urn. Una diagnostica senza token di passo ha come ambito la macchina. Un corpo o un argomento non può esistere senza un token di passo, e un argomento non viene mai dedotto dal testo del messaggio.

I fallimenti di una regione ForEach usano il token stabile del passo della regione. I nomi interni dell’executor, come foreach_region_1, non sono indirizzi rivolti all’utente.

Percorso di un fallimento terminale

sequenceDiagram
    participant H as Handler
    participant R as Runtime della cartuccia
    participant W as Frame ERR
    participant E as Executor
    participant A as Applicazione

    H->>R: errore tipizzato o ordinario
    R->>W: code + attribution_class + message + arg_urn facoltativo
    W->>E: frame rigoroso del protocollo v6
    E->>E: associa token_id e body_index facoltativo
    E->>A: ExecutionError attribuito
    A->>A: rende persistente il fallimento terminale dell'esecuzione

I fallimenti terminali restano durevoli. Il motore rende persistenti la loro classe, il codice, il motivo, il token del passo, l’eventuale ordinale del corpo e l’eventuale argomento usando il modello esistente dei fallimenti delle esecuzioni. La colonna attribution_class del database memorizza lo stesso token AttributionClass trasportato dal protocollo; non definisce una seconda tassonomia.

Percorso delle diagnostiche dal vivo

I frame LOG non di avanzamento seguono lo stesso percorso attraverso l’host della cartuccia, il relay, l’executor e il motore, dove viene associata la coordinata stabile del passo. Vengono poi trasmessi ai client connessi come diagnostiche dal vivo ordinate dell’esecuzione.

Queste diagnostiche ordinarie sono transitorie:

  • non vengono scritte nel database strutturato delle esecuzioni;
  • non vengono riprodotte dopo una riconnessione o un riavvio dell’applicazione;
  • i client le conservano in una memoria limitata e possono segnalare gli intervalli scartati;
  • i normali sink di logging restano responsabili dei log operativi a lungo termine.

I frame LOG di avanzamento sono esclusi perché guidano già lo stato di avanzamento e hanno un ciclo di vita funzionale separato.

Capacità e perdita di processi

L’ammissione avviene per POOL, con chiave data dall’identità completa dell’installazione più il nome del pool, ed è guidata dalla mappa obbligatoria dello stato dei pool dichiarata in HELLO e aggiornata a ogni risposta di heartbeat. Un dispatch viene trattenuto rispetto all’intera CATENA di pool della sua cap (il pool singleton della cap stessa, ogni pool condiviso dichiarato che la contiene, poi all), e viene ammesso solo quando ogni pool della catena ha spazio, con un’unica decisione atomica. Una capacità pari a zero significa illimitata; per una capacità effettiva positiva (min(configured, available)), il lavoro che la eccede attende in un’unica fila per installazione, e passa per prima la richiesta che ha atteso più a lungo fra quelle la cui intera catena ha spazio, in ordine di arrivo su tutte le cap dell’installazione: la stessa regola con cui ammette il runtime della cartuccia (quella del modello dimostrato, formal/CapDAG/Bifaci/Pools.lean). Più record di processo che portano la stessa identità completa di installazione condividono quei bersagli di ammissione; l’ordine di registrazione nell’host seleziona la mappa autorevole, in accordo con il dispatch delle richieste lato host. Identità di installazione distinte che rivendicano la stessa cap selezionata costituiscono un’ambiguità e falliscono in modo netto. Il pool all di un record NOT-RUNNING viene limitato a uno (il canarino dell’avvio a freddo): il primo corpo dimostra che l’avvio del processo funziona prima che si applichino le capacità dichiarate.

Le cap le cui catene condividono un pool limitato sono confini di materializzazione nell’esecuzione del DAG. L’executor alimenta e completa una di queste cap prima di ammettere una cap dipendente che contende lo stesso pool; non acquisisce mai in anticipo più richieste limitate per una pipeline dal vivo. Il permesso di ammissione denota slot di pool posseduti attivamente, quindi un’acquisizione anticipata potrebbe altrimenti attendere uno slot a valle mentre il possessore a monte resta senza alimentazione. Le catene che non condividono alcun pool limitato (cartucce diverse, o cap della stessa cartuccia in pool limitati disgiunti) continuano a fare streaming dal vivo.

Se il processo di una cartuccia muore, falliscono solo le richieste possedute attivamente da quel processo. Ai corpi in coda non viene assegnato un fallimento terminale a causa della perdita del processo di un altro corpo. Un bersaglio di ammissione che diventa non disponibile NON rifiuta chi è in attesa: le richieste in attesa restano in coda per ADMISSION_UNAVAILABLE_GRACE (60s, misurati dall’inizio dell’interruzione, non dall’arrivo di ciascuna richiesta in attesa), così che, non appena un’istanza sostitutiva dichiara capacità, il lavoro in coda viene ammesso su quell’istanza attiva. La finestra è un limite, non un nuovo tentativo: un’interruzione che dura più a lungo fa fallire in modo netto le richieste in attesa, classificate environment (RelaySwitchError::CartridgeUnavailable → ExecutionError::CartridgeUnavailable), così che un bersaglio realmente scomparso emerga subito invece di bloccare l’esecuzione. Quanto ancora una richiesta in attesa possa attendere è la patience del modello: il tempo è quello dell’interruzione, quindi una richiesta che entra in fila tardi durante un’interruzione riceve ciò che ne resta. L’ammissione è replicata in Rust, Go, Python e ObjC; capdag-js non ha un runtime Bifaci. Questo preserva l’isolamento dei corpi nell’esecuzione ForEach, mantenendo la morte di un processo visibile come fallimento environment sul corpo interessato.

Un host che RITIRA una cartuccia (una sincronizzazione del roster indica che l’installazione non è più desiderata) la svuota invece di terminarla: la cartuccia esce subito dalla tabella delle cap, e il suo processo sopravvive finché le richieste che sta già servendo non terminano. Il lavoro terminato da un ritiro viene attribuito come CARTRIDGE_RETIRED / environment, mai CANCELLED, che affermerebbe che un utente ha cancellato qualcosa che nessuno ha cancellato.

La cancellazione rilascia una richiesta in attesa di ammissione o un permesso attivo esattamente una volta. Non crea storia diagnostica.

La cancellazione parla la tassonomia

Un Cancel viene attribuito esattamente come un fallimento (code, attribution_class, message nel meta del frame, 12.2 §Cancel), e la tassonomia ha una classe per esso: user, la decisione dell’operatore stesso. Un esito attribuito a user è permanente (mai ritentato automaticamente: l’umano ha detto di fermarsi) ed è l’unica classe sotto cui “cancellato” è la verità. Chi cancella decide l’attribuzione, e l’esito terminale con cui la richiesta cancellata si conclude la porta con sé:

  • collaterale (ABORTED_COLLATERAL): l’executor ha smontato la catena perché un altro passo è fallito. Il Cancel porta la classe del fallimento originario; il record di fallimento dell’esecuzione è quell’errore originario, e gli esiti terminali collaterali nominano nel loro messaggio il passo fallito. Un collaterale non è mai il fallimento dell’esecuzione e non viene mai imputato all’utente.
  • interruzione dell’host (ABORTED): ha deciso l’host. Il livello dei task attribuisce il proprio motivo: rimozione dei task inattivi → environment, ABORTED_STALE; sollievo dalla pressione sulla memoria → resource, ABORTED_MEMORY_PRESSURE, sui frame Cancel e sul record dell’esecuzione (Failed). Il controllo esistente sulla classe per il nuovo tentativo automatico si applica invariato.
  • cancellazione dell’operatore (CANCELLED, user): Cancelled è lo stato dell’esecuzione; nessun record di fallimento. Un esito terminale di un’esecuzione attribuito a user dalle sue cartucce (la cancellazione in gara con l’esito terminale dell’esecuzione stessa) registra Cancelled, mai Failed.
  • non attribuito: un Cancel senza meta cancella comunque; il suo esito terminale è CANCELLED / internal (non classificato significa “nostro”), mai un’ipotesi su chi abbia deciso.

Le interfacce mostrano l’attribuzione (“interrotto — il passo X è fallito”, colorato secondo la classe originaria), mai la parola “cancellato” per qualcosa che non sia una cancellazione attribuita a user. Un fallimento che raggiunge il livello dei task dopo una cancellazione viene registrato sul task come annotazione (Cancelled → Cancelled su execution_failed), mai rifiutato. Chiudere un input dal vivo non è affatto una cancellazione: è un frame CloseStream (12.2 §CloseStream), e la richiesta si conclude normalmente.

Presentazione

Un’interfaccia utente dell’host raggruppa le diagnostiche dal vivo di informazione, avviso ed errore alla coordinata disponibile più specifica. Un fallimento durevole e le sue diagnostiche di errore dal vivo condividono un unico pannello del nodo o della macchina, invece di produrre interfacce di errore duplicate. Le diagnostiche con ambito la macchina restano visibili a livello di macchina. I badge dei nodi mostrano i conteggi; pannelli limitati e scorrevoli conservano il dettaglio cronologico.

Invarianti

  • Il codice sorgente dichiara la classe e l’eventuale argomento; i livelli a valle non tirano a indovinare.
  • token_id indirizza un passo; body_index indirizza soltanto un’invocazione ForEach.
  • Il fallimento di un corpo non termina corpi in coda non correlati, e nemmeno lo fa un’interruzione transitoria della cartuccia su cui sono in coda.
  • Gli errori sono durevoli; i log ordinari esistono solo dal vivo; l’avanzamento resta stato funzionale.
  • I valori di protocollo sconosciuti falliscono in modo netto invece di attivare comportamenti di compatibilità.

Un host può mappare queste identità a livello di protocollo in una tassonomia più ampia dei fallimenti del prodotto. Tale mappatura dell’host è al di fuori del contratto di CapDAG.