Memorie di un tizio che ha passato troppo tempo a ripulire la merda degli altri e a pagare per le loro cattive decisioni, episodio #troppi.
Copiare/incollare cronologie e stili sono funzioni fondamentali in Ansel, e quella che gli fa meritare (o meno) il suo titolo di «app per il flusso di lavoro». Ma è anche tra le più difficili da realizzare correttamente a livello interno. Gli utenti vedono un elenco di modifiche, ma sotto il cofano quelle modifiche dipendono dall’ordine della pipeline, dalle istanze dei moduli e dalle maschere. Se due immagini hanno topologie di pipeline diverse, copiare le modifiche in modo ingenuo può produrre incoerenze.
Questo aggiornamento rende la fusione della cronologia robusta, coerente e trasparente, dopo un lavoro noioso di pulizia e semplificazione del codice. Introduce inoltre una chiara gestione degli errori quando una fusione perfetta è matematicamente impossibile.
Una breve storia del cattivo design
Fino all’inizio del 2019, Darktable era progettato attorno a una pipeline fissa: i moduli avevano un ordinamento deciso al momento della compilazione da uno script Python , creato dal fondatore di Darktable, Johannes Hanika, nel 2011 . L’unica cosa che ci si può aspettare da Jo è che i suoi calcoli siano corretti, e infatti questo script fa esattamente ciò che andrebbe fatto in tali circostanze:
- lasciare che i programmatori dichiarino i moduli,
- lasciare che definiscano quali moduli debbano precedere ciascun modulo, in un modo pigro e comodo che permette semplicemente di dire «A dovrebbe stare prima di C», «B dovrebbe stare prima di A», «D dovrebbe stare prima di A», ecc.
- trasformare tutto ciò in un grafo orientato , che è semplicemente l’oggetto matematico che tiene insieme tutti quei vincoli,
- risolvere il grafo orientato con uno degli algoritmi di ordinamento topologico , che risalgono agli anni ‘60,
- scrivere il numero d’indice di ciascun modulo nella pipeline, e fatto.
Purtroppo, quella pipeline fissa significava che cambiare in seguito l’ordinamento relativo dei moduli avrebbe rotto le cronologie più vecchie. Il che era una seccatura, perché alcuni errori di progettazione erano stati fatti con la posizione della trasformazione di visualizzazione e del modulo curva di base, che avvenivano all’inizio della pipeline e facevano impazzire i colori nelle situazioni HDR. Per risolvere il problema, ho dovuto creare il modulo filmic come un modulo a sé, per poterlo mettere alla fine della pipeline. E tutti gli altri moduli riferiti al display avrebbero dovuto essere duplicati nel codice base per poterli inserire dove necessario senza rompere le modifiche più vecchie (comprese le mie). Inutile dire che la pipeline fissa aveva fatto il suo tempo, e io ero un sostenitore di questo cambiamento.
Con una pipeline fissa, fondere insieme le cronologie, ossia copiare/incollare cronologie tra immagini o applicare stili alle immagini (che finisce per essere la stessa cosa), era facile: si trattava semplicemente di sostituire 1:1 i parametri dei moduli e le maschere. Anche con i moduli multi-istanza, che erano stati introdotti intorno al 2014, non era poi così male, perché tutte le istanze erano obbligate a essere consecutive e numerate relativamente le une alle altre. Quindi, dopo aver creato le istanze mancanti, in una posizione prevedibile e invariante (in ordine sequenziale dopo l’istanza di base), si trattava ancora di sostituire 1:1 i parametri dei moduli e le maschere.
Ma la tanto necessaria funzione di riordinamento della pipeline, a runtime e da parte degli utenti, arrivata con Darktable 3.0, fu realizzata nel modo peggiore possibile. Lo sviluppatore che la implementò gestì l’indicizzazione con array di priorità a virgola mobile, che non è la struttura dati giusta per il problema. Inoltre, il codice di gestione della cronologia non fu rifattorizzato e semplificato prima dell’estensione, ma modificato alla bell’e meglio sul posto con modifiche minime, il che lo rese davvero complesso e offuscato. Pascal Obry, che aveva accettato il cambiamento nel 2019, dovette riscrivere l’intero backend di riordinamento della pipeline nel 2020, usando la struttura dati appropriata per il compito (liste concatenate ), perché la precedente era fragile e impossibile da mantenere.
Ma fondere pipeline, che possono avere numeri diversi di moduli, ordinati in posizioni imprevedibili e non invarianti, non può essere fatto in altro modo se non usando l’ordinamento topologico a runtime, perché non è più un semplice problema di cronologie (ossia istantanee dei parametri dei moduli), è un problema duplice che include le cronologie ma anche la topologia della pipeline. In altre parole: dobbiamo risolvere dove inserire le istanze dei moduli che esistono nella pipeline sorgente ma non in quella di destinazione. Eppure, lo script Python che lo faceva al momento della compilazione fu cancellato nel 2019, e la funzione non fu mai portata in C.
Quindi, il modo in cui Darktable, a oggi, gestisce la fusione delle pipeline è tramite euristiche raffazzonate a partire dal paradigma della pipeline fissa e in generale rotte, tranne nei bei casi che corrispondono alle condizioni delle pipeline del 2018 e precedenti:
- se le tue pipeline sorgente e di destinazione hanno lo stesso numero di moduli ordinati nello stesso modo, tutto va bene.
- se la tua pipeline sorgente ha istanze di moduli aggiuntive, rispetto alla destinazione, ma quelle istanze si trovano tutte immediatamente dopo l’istanza di base, tutto va ancora bene,
- ma… se hai istanze aggiuntive, sia nella pipeline sorgente sia in quella di destinazione, e sono state spostate altrove nella pipeline, allora il comportamento è indefinito, imprevedibile, e ho passato più di 5 anni a copiare-incollare la cronologia trattenendo il fiato,
- inoltre, c’è un elenco di moduli «recinto» (fence) che richiedono un ordine relativo specifico (come calibrazione del colore che deve assolutamente andare dopo profilo colore di ingresso), ma nessun modo per imporli, nessun modo per correggerli quando si verificano ordinamenti sbagliati, solo silenziosi messaggi di errore in console.
Combina tutto ciò con le maschere raster, dove il modulo che riutilizza una maschera raster deve assolutamente trovarsi più avanti nella pipe rispetto al modulo che la produce, e dove, tra l’altro, potresti voler copiare il produttore insieme al consumatore, o almeno ottenere un avviso se non lo fai… e hai la ricetta per la follia.
In pratica, dovrai aprire ogni immagine e controllare lo stack dei moduli in camera oscura, il che è dolorosamente lento.
Poiché Darktable è definito da una cattiva gestione delle priorità, molto lavoro è stato dedicato a molti aspetti estetici: ti lascerà modificare le tue foto con i gamepad della PlayStation, sta per ottenere funzioni di mascheratura basate sul deep learning, ma riesce comunque a rovinare le basi fondamentali, senza alcun miglioramento in 6 anni nonostante l’apparente attività nel progetto.
Il problema (TL;DR)
In precedenza, l’incollaggio della cronologia tentava di fondere i moduli uno per uno man mano che procedeva. Funzionava nei casi semplici, ma diventava inaffidabile quando:
- l’immagine sorgente aveva un ordine di pipeline diverso,
- c’erano più istanze dello stesso modulo,
- oppure erano coinvolte maschere e fusione.
Poiché la fusione non risolveva l’intero problema di ordinamento, il risultato poteva dipendere da molte cose. In alcuni casi, la pipeline finale poteva risultare incoerente con lo stack della cronologia.
La soluzione
Ho implementato un algoritmo di ordinamento topologico in C. O più precisamente, l’ha fatto ChatGPT e io l’ho controllato (più avanti su questo). 304 righe di codice, che, per il C, sono davvero poche.
Ora trattiamo l’ordine della pipeline come un insieme di vincoli che deve essere risolto globalmente, non modulo per modulo. In pratica, ciò significa:
- Risolviamo prima la topologia della pipeline, e per ultima la cronologia di sviluppo (parametri dei moduli). Due passaggi chiari che rendono possibile la gestione degli errori.
- La fusione calcola un unico ordine di pipeline valido che soddisfa sia la sorgente sia la destinazione ogniqualvolta possibile, indipendentemente da quante istanze di moduli ci sono e da come sono ordinate.
- Se i vincoli sono incompatibili, Ansel può spiegare il conflitto e chiedere quale ordinamento preservare.
- Se un conflitto è irrisolvibile, Ansel lo segnala chiaramente invece di produrre una pipeline rotta o instabile e ti offre opzioni per correggerlo.
- Vincoli sono posti anche sui moduli che usano e consumano maschere raster, e viene emesso un avviso se un utilizzatore di una maschera raster viene copiato senza il produttore.
- Vincoli codificati a livello hardware (hard-coded) sono posti sui moduli che si richiedono l’un l’altro prima, per ragioni tecniche (ricostruzione delle alte luci prima della demosaicizzazione, profilo colore di ingresso prima della calibrazione del colore, ecc.). Quei vincoli sono gestiti allo stesso modo degli altri e risolti insieme (nessun caso speciale).
Riguardo alla seconda parte, la cronologia propriamente detta, va chiarita una cosa qui. La cronologia di Darktable e Ansel è molto simile a un elenco di annulla/ripristina di istantanee dei parametri dei moduli: ogni voce della cronologia è collegata a un modulo, e rappresenta il suo stato interno di parametri e maschere. Quando carichiamo la cronologia nei nodi della pipeline (i nodi essendo i filtri di pixel collegati ai «moduli» che vedi nella GUI), leggiamo la cronologia dal basso verso l’alto e copiamo ciascuna voce/istantanea nei moduli, il che significa che le istantanee successive sovrascrivono sempre quelle precedenti.
Vale a dire, la cronologia è ordinata in base al momento della modifica dell’utente (di nuovo… pensa a un elenco annulla/ripristina di istantanee), non in base all’ordine dei nodi della pipeline. Ma, dove diventa confuso è che le voci della cronologia memorizzano anche la posizione nella pipeline di un modulo, il che significa che il riordinamento dei moduli lascia voci di cronologia. Questo confondeva gli sviluppatori di Darktable, e molto di più gli utenti. Quindi, separiamo concettualmente le due cose, sia nelle nostre menti sia nel software.
La topologia (l’ordinamento dei nodi della pipeline) è indipendente dalla cronologia. A differenza di Darktable, che gestisce tutto attraverso le voci della cronologia (compreso l’ordine della pipeline), il che è attivamente dannoso in termini sia di comprensione sia di complessità del codice (le due cose vanno comunque insieme), noi risolviamo la pipeline come una collezione autonoma di nodi (moduli), e poi riabbiniamo i nodi con la loro ultima fase di cronologia. Mi ci sono voluti un paio d’anni per vedere attraverso tutto lo schiacciante offuscamento presente in questo software, sepolto in codice copiato-incollato, e vedere la luce: una volta astratto, il problema è piuttosto facile.
Quindi, una volta risolta la topologia, ci restano 3 modalità di fusione della cronologia:
- replace: la cronologia sorgente sostituisce l’intera destinazione, i moduli obbligatori come la demosaicizzazione per le immagini RAW potrebbero comunque essere aggiunti in cima (in modo che copiare-incollare sia sicuro tra JPEG e RAW). Questa modalità non porta ad alcun ordinamento topologico, è una copia diretta della cronologia e dell’ordine della pipeline.
- append: la cronologia sorgente va in cima alla destinazione, quindi le voci di cronologia che puntano agli stessi moduli in sorgente e destinazione vengono sovrascritte dalla sorgente,
- appstart: la cronologia sorgente va in fondo alla destinazione, quindi le voci di cronologia che puntano agli stessi moduli in sorgente e destinazione vengono sovrascritte dalla destinazione.
L’ordine della pipeline risolto tramite ordinamento topologico viene aggiornato nelle ultime voci di cronologia, sia nella modalità append sia in appstart, il che significa che tornare indietro nella cronologia annullerà anche l’ordinamento topologico. Le cronologie di proposito non vengono compresse durante la fusione, così gli utenti mantengono la capacità di annullare la fusione sia usando le funzioni di annulla/ripristina, sia tornando indietro nel pannello della cronologia, in camera oscura, prima del punto di fusione.
In breve: l’incollaggio della cronologia è ora deterministico, sicuro, non distruttivo, anche per modifiche complesse.
Come usarlo
Ci sarà una cronologia sorgente (quella che copi) e una cronologia di destinazione (dove incolli). Lo stesso si applicherà con gli stili quando verranno reimplementati; la cronologia sorgente sarà definita dallo stile invece che da un’altra immagine, e il resto sarà uguale.
Nel menu globale Modifica → Modalità di incollaggio cronologia, puoi scegliere tra append, appstart o replace. L’impostazione è globale per l’intera applicazione. Ciò determina quale cronologia (sorgente o destinazione) ha la precedenza sovrascrivendo i moduli comuni.
In Modifica → Modalità di incollaggio nodi, puoi attivare/disattivare Copia ordine dei moduli. Se disattivato, l’ordine della pipeline della destinazione viene mantenuto così com’è. Se attivato, facciamo del nostro meglio per importare l’ordine della pipeline sorgente nella destinazione.
Come prima, nel menu Modifica, hai le opzioni per copiare/incollare tutto, o solo i moduli selezionati (tramite la finestra modale). Sono disponibili scorciatoie globali e modificabili dall’utente. Nota che copiare-incollare le cronologie è esplicitamente vietato nella vista camera oscura, anche dal provino a striscia, perché è ambiguo determinare se vuoi copiare tra le miniature, dalla miniatura all’immagine principale o viceversa. Nel tavolo luminoso, selezioni la sorgente, copi, selezioni la destinazione, incolli, e tutto è chiaro.
Ora, c’è un presupposto importante da tenere a mente: i moduli che hanno lo stesso nome di istanza (il numero di istanza per impostazione predefinita, o un nome definito dall’utente) sono considerati la stessa entità nelle cronologie di destinazione e sorgente. Quindi, ogni Esposizione (cielo) verrà fusa con ogni altro modulo Esposizione (cielo) (con distinzione tra maiuscole e minuscole), e dovrebbe esserci una sola istanza di Esposizione (cielo) nelle cronologie di destinazione e sorgente. In precedenza, il codice usava i numeri di istanza, che è più fragile perché sono imposti dal software e incrementati nell’ordine di creazione, che non ha alcun significato per gli utenti.
GUI e gestione degli errori
La bellezza della nuova soluzione è che non devi aprire la camera oscura per vedere il pasticcio che hai creato copiando-incollando spazzatura; puoi rivederlo prima che qualsiasi danno venga fatto alle tue modifiche, nel tavolo luminoso. Inoltre, quando il risolutore non riesce a trovare una soluzione, cosa che accade con vincoli incompatibili (A dovrebbe essere preceduto da B, ma B dovrebbe essere preceduto da A) o cicli (più avanti), è in grado di dire cosa fallisce, segnalarlo, e o richiedere l’input dell’utente per correggerlo o ripiegare sul percorso più sensato. Lascia che ti mostri:
Cicli banali
Esposizione 1 è prima di Esposizione nella cronologia sorgente, ma dopo nella cronologia di destinazione. L’insieme dei vincoli finisce con Esposizione → Esposizione 1 → Esposizione, che è irrealizzabile. Ecco cosa succede in Ansel:

Questi cicli banali che coinvolgono vicini immediati vengono intercettati prima della risoluzione, quindi non interrompono il flusso di controllo.
Cicli non banali
Quei cicli non banali coinvolgono diversi moduli e non possono essere rilevati prima di tentare di risolvere il grafo orientato. Quando ciò accade:

In questo caso, non c’è nulla da fare: riproveremo automaticamente usando l’ordine di destinazione, poiché ciò si verifica tipicamente quando si tenta di fondere l’ordine sorgente nella destinazione.
Maschere raster dimenticate
Qualsiasi modulo che usa una maschera raster dovrebbe essere copiato insieme al suo modulo che produce la maschera, a meno che tu non abbia intenzione di risolverlo da te in seguito. Nel caso sia un errore, se lo tenti:

Hai la possibilità di annullare la fusione subito se non era ciò che volevi.
La GUI del rapporto di fusione (nuova)
Nozioni di base
La finestra di dialogo del rapporto è progettata per rispondere a una semplice domanda dell’utente: «cosa è successo esattamente alla mia pipeline?»
Mostra quattro pipeline affiancate:
- Originale (la pipeline di destinazione prima della fusione),
- Sorgente (l’immagine da cui hai copiato),
- Override (dove le modifiche sorgente hanno sostituito le modifiche di destinazione),
- Destinazione (la pipeline finale dopo la fusione).

Ogni colonna elenca le istanze dei moduli attivi, nell’ordine della GUI. Questa vista include marcatori aggiuntivi:
- Le parentesi quadre
[name]indicano i moduli che sono stati inseriti ex novo. - Un asterisco
*indica i moduli che usano maschere. - Un’etichetta in grassetto indica i moduli la cui posizione relativa è cambiata tra sorgente e destinazione.
- Le frecce di override mostrano dove la cronologia sorgente ha effettivamente sostituito le modifiche di destinazione (con
→*quando anche le maschere sono state sovrascritte).
Chicche
La colonna di destinazione è riordinabile con trascina e rilascia, il che significa che se non sei soddisfatto del risultato dell’ordinamento topologico, puoi correggerlo da te subito, prima ancora che venga salvato nel tuo database e negli XMP, e senza dover aprire la camera oscura. Questo ti permette di regolare manualmente la pipeline finale prima di accettarla, oppure di annullare tutto e non riscrivere la cronologia.
Quando riordini:
- l’ordine della pipeline viene aggiornato immediatamente,
- le voci della cronologia vengono mantenute coerenti con il nuovo ordine,
- e la vista del rapporto aggiorna di conseguenza le sue etichette e i marcatori «spostato».
Questo è concepito come una valvola di sicurezza: anche se l’ordine calcolato è valido, hai comunque un modo semplice per metterlo a punto.
Perché questo è importante
Questo migliora direttamente i flussi di lavoro che comportano la modifica in batch con pipeline complesse:
- copiare modifiche tra immagini,
- mescolare sorgenti/destinazioni RAW e JPEG,
- e modifiche pesanti multi-istanza o basate su maschere.
L’obiettivo è rendere l’incollaggio della cronologia prevedibile, anche quando le pipeline sottostanti differiscono. Questa affidabilità è particolarmente importante per le modifiche avanzate, dove piccole differenze di ordinamento possono cambiare i risultati.
Questo cambiamento non aggiunge nuove funzioni appariscenti — rende affidabile una delle funzioni più usate. Le fusioni della cronologia ora si comportano come gli utenti si aspettano: risultati coerenti, rapporti chiari e ripieghi sicuri quando i vincoli sono in conflitto.
E non capisco perché, 6 anni dopo, i tizi del It Works For Me® che mandano in crash Darktable al rallentatore non abbiano pensato di migliorare una funzione così basilare eppure critica. Se questo non grida priorità sbagliate, non so cosa lo farà.
Cosa lo ha reso possibile
Voglio sottolineare qui che tutta questa riscrittura è stata resa possibile perché ho prima riscritto quasi interamente il backend di gestione della cronologia in Ansel, dato che era un disastro:
- C’erano funzioni duplicate ovunque, che eseguivano la stessa operazione molte volte ma nascoste in funzioni chiamanti/chiamate in tutto il software, alcune inducendo I/O sul filesystem (scrittura di XMP) senza motivo, una che riscriveva la cronologia ogni volta che aprivamo la camera oscura (che rovinava il timestamp ultima modifica),
- C’erano diversi blocchi di thread intrecciati che rendevano praticamente impossibile qualsiasi modifica senza mandare le cose in deadlock,
- C’era codice di recupero della cronologia SQLite3 intrecciato all’interno del codice C, molte query SQL duplicate, nessuna di esse thread-safe (perché SQLite3 stesso non è thread-safe), quindi ho sepolto tutto il codice SQL all’interno di un’interfaccia C che gestisce la thread safety in modo centralizzato, e ora tutto il codice C recupera le informazioni della cronologia dal database della libreria con un’unica API, il che significa che sappiamo che tutto ciò che legge la cronologia la leggerà allo stesso modo ovunque nell’app,
- Alcune parti della lettura, inizializzazione e fusione della cronologia erano fatte in SQL (sfruttando le istruzioni
JOIN, il che ha senso, ma…), e alcune altre erano fatte in C (perché i controlli di sicurezza dei moduli e l’inizializzazione dei preset sono ovviamente in C). Questo portava a cose stupide come re-indicizzare manualmente le voci della cronologia in C prima di salvare nel database tra scritture transitorie (perché SQLite3 non garantisce che le voci della cronologia vengano salvate nel database nello stesso ordine in cui sono state passate… è a questo che servono le chiavi primarie). Quindi ho riscritto l’intera cosa in C, il che può essere leggermente più lento ma garantisce la coerenza dei dati: le cronologie vengono gestite esattamente allo stesso modo, sia che le carichiamo per fonderle, sia che apriamo la camera oscura o esportiamo un’immagine. Se c’è un bug da qualche parte, sarà dappertutto e lo troveremo prima, e per di più lo correggeremo in un unico posto. - Il codice di gestione della cronologia era intrecciato anche con il codice della GUI, ma può funzionare anche da
ansel-cli(senza GUI), quindi ciò portava a molte euristiche che controllavano se avevamo una GUI o no, in molti posti.
Quindi, una volta fatto tutto quel lavoro da custode, allora ho cominciato a vedere la struttura di ciò che veniva effettivamente fatto e che doveva essere fatto. Da lì, una semplificazione ha portato a un’altra, finché ChatGPT 5.2 Codex ha fatto il resto. Fino a 2 settimane fa, tutto ciò era ancora fatto interamente a mano e mi ha fatto impazzire molte volte. È davvero solo vernice che teneva su quei muri, vernice scrostata, e cercare di ripulirla distruggeva molte cose perché niente in questo software era modulare (ossia racchiuso). Qualcosa che cambi in un posto ha conseguenze inaspettate altrove, ed è per questo che abbiamo l’incapsulamento, la modularità e i design pattern, perché il linguaggio di programmazione C non è stato progettato per applicazioni desktop complesse come questa, e richiede davvero disciplina da parte dello sviluppatore per evitare di diventare l’incubo che è.
Questo è interamente vibecoded
Quindi la pulizia della cronologia andava avanti dal 2023, gestendo il burnout e la depressione indotta dal software. Questa è una pessima qualità della vita, non ne hai idea. Chi pensa che io esageri non sa cosa comporta spalare le feci cerebrali degli altri per più di 3 anni. Perché ho conosciuto un tempo in cui tutto ciò era, se non migliore, almeno meno complicato e più gestibile. Fino a quando la follia del COVID-19 ha colpito, e degli idioti hanno avuto troppo tempo libero a disposizione, che hanno usato per distruggere qualcosa che grosso modo funzionava.
E poi ho scoperto ChatGPT 5.2 Codex 2 settimane fa, e l’ho installato nell’editor VS Code. Quindi, mi ci sono voluti 3 giorni di lavoro per fare tutte le cose che ho presentato qui. Senza ChatGPT, sarebbero state ben 3 settimane, più l’interminabile armeggiare con i dettagli di GTK. Parliamo dell’esperienza.
Non sono d’accordo con quelli che cercano di farci credere che la GenAI sia solo uno strumento. Uno strumento funziona solo nella mia mano. Non mentre dormo. Ho fatto lavorare ChatGPT per me mentre cucinavo la cena (sì, è così lento). Non comunichi con uno strumento, lo usi soltanto al meglio delle tue capacità. In caso di fallimento, beh, alcuni danno la colpa allo strumento, ma sappiamo tutti cosa significa. Il problema è che ChatGPT non ha pulsanti o cursori, interpreta ciò che gli dici, e non necessariamente come lo intendi. E poi uno strumento non prende iniziative. Beh, ChatGPT ha di sicuro un’opinione su come dovrebbe apparire il codice, e a volte devi combatterlo.
La GenAI è uno stagista. Uno stagista non ha esperienza e conosce solo ciò che si insegna a scuola. Uno stagista può portare idee nuove e fresche che sfidano le tue abitudini, e suggerimenti deliranti allo stesso modo, che non sono minimamente rilevanti per il tuo contesto e a volte nemmeno realizzabili. Ma uno stagista ha bisogno di lavorare sotto stretta supervisione e di ricevere istruzioni chiare e non ambigue. ChatGPT è molto più uno stagista che uno strumento.
ChatGPT fa un sacco di errori, e sono subdoli perché sono sepolti in mezzo a roba perfettamente valida. Ha alcune strane ossessioni (come controllare che ogni puntatore che già sappiamo non poter essere NULL non sia NULL). Quindi, devi davvero tenerlo d’occhio. Anche se rivedere e correggere i suoi errori è comunque più veloce che scrivere tutto il codice da solo, per non parlare del fatto che il mio primo problema di sindrome del tunnel carpale è stato 10 anni fa, quindi è sempre tanto in meno da digitare. Per di più, fa errori sulla logica, ma nessun errore di battitura, e almeno molti meno di me.
Ma dove ChatGPT Codex brilla è in 2 cose.
Primo, il tedioso gioco di fare grep delle funzioni attraverso il codice base per scoprire (fare reverse-engineering) il ciclo di vita dei dati e controllare tutti i punti di chiamata per costruire un modello mentale di ciò che sta succedendo. Ci vuole un’eternità, è molto impegnativo dal punto di vista cognitivo, specialmente in un codice base così scadente. ChatGPT fa meraviglie nell’attraversare dozzine di file, estrarre pattern, scoprire cosa potrebbe essere fattorizzato, e seguire sequenze di esecuzione. Sia ben chiaro che, in un codice base ben mantenuto, ciò non dovrebbe essere necessario perché il codice sarebbe auto-racchiuso in moduli, isolato dal resto. Ma ChatGPT ha aiutato molto a rendere le cose più modulari.
Secondo, tutto ciò che riguarda GTK e GLib. Questi sono documentati male sul web, e molti pattern idiomatici di interazione sono noti solo agli sviluppatori di GTK. ChatGPT ha ovviamente ingerito una gran quantità di codice open-source e può produrre codice GUI boilerplate molto migliore di quanto potrei (o mi importerebbe di fare). Comunque, prima di ChatGPT, ciò si trasformava in tediose sessioni di ricerca di informazioni su Google, e non riesco a trovare alcuna informazione tecnica rilevante su Google dal 2020 circa, quando hanno cambiato i loro algoritmi per mettere aggressivamente in dubbio ogni cosa. Ma io lavoro per risolvere problemi, e tutte le funzioni GUI boilerplate che inizializzano i widget e le loro proprietà in uno stile dichiarativo non sono degne della mia intelligenza, si tratta solo di cercare di non introdurre errori di battitura.
Ma per avere un’idea migliore, ecco il tipo di prompt che ho dovuto dargli per costruire ciò che ho appena presentato:
ora, in _hm_try_merge_iop_order_topologically(), costruisci all’inizio della funzione una GHashtable di tutti gli ID dei moduli legati a mod_list, poi a dev_src->iop, poi a dev_dest->iop. Queste saranno utili per calcolare l’intersezione degli insiemi più tardi. non modificare dev_dest->iop_order_list. Per ogni elemento nella lista ordinata (l’elemento essendo un ID di nodo legato a un op di modulo e a multi_name):
- scopri se esiste un’istanza di modulo corrispondente in dev_dest->iop, se no creala. Poiché dev_dest->iop è già inizializzato e sanitizzato a monte, possiamo tranquillamente assumere che ogni modulo non trovato debba essere inserito come nuova istanza. Se l’ID dell’istanza del modulo viene trovato nella mod_list in input, l’intero contenuto del modulo (parametri, blendop, ecc.) dovrebbe essere copiato dall’istanza sorgente all’istanza di destinazione. Attento alle copie profonde che devono avvenire.
- sovrascrivi tutti i valori module->iop_order con il nuovo numero d’indice che abbiamo appena trovato risolvendo
- ricostruisci dev_dest->iop_order_list da zero e aggiorna module->multi_priority di conseguenza
ora, in dt_history_merge_module_list_into_image_advanced, la cronologia temporanea deve essere costruita come segue:
- deimplementa il percorso force_new_modules per ora, ci torneremo più tardi e in modo diverso,
- costruisci una cronologia temporanea come segue: per ogni modulo in mod_list:
- ottieni la voce di cronologia associata da dev_src->history (che sarebbe l’ultima corrispondente a questo modulo nello stack della cronologia),
- ottieni le informazioni di ordinamento della pipeline (iop_order, instance, multi_priority) dal modulo corrispondente in dev_dest->iop
- aggiorna la voce di cronologia esistente da dev_src->history con l’ordinamento della pipeline, poiché potrebbe essere cambiato dopo l’ordinamento topologico, rispetto alla voce di cronologia originale,
- aggiungi questa voce di cronologia alla cronologia temporanea
- concatena la cronologia temporanea con dev_dest->history, per prima o per ultima a seconda della modalità append o appstart.
Cerca di usare il più possibile i metodi di history.c e dev_history.c per la gestione della cronologia da/verso i moduli. Estendi quelli esistenti se ti servono solo modifiche minori.
No, annulla quello. Non va bene eliminare le voci di cronologia oltre l’history_end in generale. Qualunque cosa sia in dev->history dovrebbe finire nel DB. Inoltre, non è un problema perché anche l’history_end viene salvato nel DB. Il problema qui è che voci di cronologia casuali vengono aggiunte quando si rilegge la cronologia dal DB. Tutto fino alla scrittura della cronologia, che avveniva in C, era a posto. Scopri perché otteniamo voci di cronologia extra quando rileggiamo dal DB, rispetto a ciò che abbiamo al momento della scrittura prima.
alla fine di dt_history_merge in history_merge.c, voglio che tu mostri una finestra popup di rapporto. Un’etichetta di testo dirà prima «Copia, fusione della pipeline in modalità {MERGE_MODE} e della cronologia in modalità {STRATEGY}», dove {MERGE_MODE} dipende da merge_iop_order (merge o destination), e {STRATEGY} dipende da strategy. Poi voglio una GtkTreeView in modalità lista, con 3 colonne:
- la sorgente della copia, con ID immagine e nome file (non il percorso completo),
- l’override,
- la destinazione della copia, con ID immagine e nome file.
Nelle colonne 1. e 3., ogni riga mostrerà le istanze dei moduli, cominciando con il loro ordine di pipeline, module->name e module->multi_name. Solo i moduli abilitati saranno mostrati. La colonna 2 disegnerà una freccia tra le istanze sorgente e destinazione quando la cronologia sorgente sovrascrive la cronologia di destinazione. Ciò si fa controllando, nella cronologia di destinazione, se l’ultima voce che punta a questo modulo corrisponde alla cronologia di destinazione o alla sorgente. Nel caso corrisponda a entrambe, non mostrare nulla poiché non è un override. I nodi della pipeline saranno mostrati in ordine inverso per corrispondere all’ordinamento della GUI, poiché è una specie di stack di livelli. Entrambi dovrebbero essere allineati in basso in modo che i passaggi iniziali abbiano la possibilità di stare sulla stessa riga finché la topologia non diverge tra le due pipe
Una cosa che ho scoperto è che con ChatGPT puoi decisamente essere troppo specifico e portarlo contro un muro. Quando ciò accade, la migliore linea d’azione è prendere in mano la situazione manualmente.
Il costo energetico di quella cosa è insopportabile, ma diciamo che, diviso per i circa 900 tizi che hanno messo la stella ad Ansel su Github (non ho statistiche di download), è per il bene superiore. È solo un modo più efficiente di risparmiare il succo del mio cervello per pensare a cosa dovrebbe essere fatto (design e architettura), piuttosto che a come farlo. Probabilmente non è così che i ragazzini fanno vibecoding di questi tempi, però.
Prossimamente: gli stili.
Translated from English by : Claude. In case of conflict, inconsistency or error, the English version shall prevail.