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è loStrandStep.token_idstabile 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 auserdalle sue cartucce (la cancellazione in gara con l’esito terminale dell’esecuzione stessa) registraCancelled, maiFailed. - 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_idindirizza un passo;body_indexindirizza 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.