Complete the A-to-Z Lardon3D maintenance and coherence pass. Generalize host resource policy, remove the global CPU12 ceiling, preserve host CPU/RAM reserves, scale Task capabilities through the Resource Governor, and validate deterministic parallel GV execution. Migrate Project DB to v23 with data-driven camera, lens, optical configuration and calibration profiles, including manual lenses without EXIF. Integrate safe optional LARDON SSD swap/scratch control with Governor and F10 drain/safe-to-unplug semantics. Refactor the ncurses TUI into a runtime observatory with durable progress, elapsed time, smoothed ETA, throughput, resource telemetry, Governor state, optics workflow, colors and compact/no-color fallbacks. Reconcile Queue lifetime, persistence, concurrency, comments, tests, README, AGENTS and canonical documentation. GLOBAL_MAINTENANCE_AUDIT=PASS/FROZEN
16 KiB
Exécution et runtime Lardon3D
Modèle d'exécution
Threads
- Thread principal : entrée, modèle de vue et rendu TUI ncursesw (exclusif)
- Thread worker Queue : exécution sérielle des tâches métier
- Participants internes : uniquement ceux du contrat Task admis, joints par le callback propriétaire avant publication
- Opération SSD : au plus un thread joinable, uniquement pendant une opération UDisks bornée ; il ne rend rien et ne devient ni Queue ni scheduler
Synchronisation
- Mutex pour les accès partagés
- Variables de condition pour la coordination
- Atomicité des opérations critiques
Cycle de vie d'une tâche
1. Création (PENDING)
2. Soumission à la file
3. Sélection FIFO/adaptative par la Queue
4. Réservation obligatoire
5. Exécution (RUNNING)
- Pause/reprise coopérative
- Annulation coopérative
- Séquences adaptatives
6. Complétion (COMPLETED) ou Échec (FAILED)
7. Nettoyage des ressources
Synchronisation
Mutex
- Protection des données partagées
- Accès exclusif aux ressources critiques
Variables de condition
- Coordination entre threads
- Notification de changement d'état
- Attente passive ; timeout borné seulement pour réévaluer un
WAITressources
Atomicité
- Opérations indivisibles
- État cohérent garanti
Gestion des erreurs
Rollback
- Rollback des transactions locales avant publication
- Nettoyage complet des ressources possédées par l'opération
- Une publication fichier réussie suivie d'un échec DB peut laisser un orphelin valide ; aucune transaction distribuée fichier+SQLite n'est revendiquée
Récupération
- Reprise à la dernière frontière connue
- Ignorance des artefacts partiels
- Validation avant publication
Limites actuelles
- Worker Queue unique (pas de pools inter-Tasks multiples)
- Pas de parallélisme inter-Tasks ; certains kinds possèdent des participants internes bornés, comptés par leur contrat Governor
- Reprise automatique limitée aux tâches indépendantes reconstructibles
Reprise durable
Un snapshot ne conserve que l'état logique d'une tâche. RUNNING et PAUSED
sont normalisés vers PENDING; aucun worker, callback brut, pointeur, contrat
ou réservation n'est restauré. Le propriétaire fournit un nouveau callback et
resoumet la tâche. Les états terminaux sont conservés.
started_at désigne le début de la tentative d'exécution courante, pas le
premier démarrage historique. Un checkpoint RUNNING restauré en PENDING
conserve temporairement l'horodatage de la tentative interrompue pour
l'observation ; lors de lardon3d_task_start(), started_at est remplacé par le
nouveau démarrage et finished_at est remis à zéro. finished_at n'est fixé
qu'à la terminaison de cette tentative.
IMPLEMENTED — snapshot, codec v1 et restauration isolée.
IMPLEMENTED — l'import import.images se sauvegarde à chaque fin de lot et
se reconstruit explicitement avec un userdata neuf lié au projet rouvert. Son
intention durable contient source_path + scanset_id; le hash/copie et la
transaction catalogue restent hors mutex Task et hors mutex DB pendant l'I/O.
IMPLEMENTED — project_open() inventorie par pages de 8, restaure puis
resoumet automatiquement les tâches production valides. Il retourne après
l'enqueue et n'attend jamais leur terminaison.
L'ordre d'initialisation production est : politique driver, profil matériel,
Governor, backend, Queue/worker, contrôleur SSD optionnel et binding
Governor, puis TUI. L'ouverture DB/projet et la reprise synchrone sont pilotées
ensuite depuis le thread principal. Une fermeture ne peut commencer qu'après le
retour de project_open(). Le worker peut consommer pendant le scan ; chaque
tâche exécutée est néanmoins réadmise normalement.
NOT_YET_WIRED — reprise ordonnée par dépendances/DAG. Les kinds de production reconstructibles checkpointent déjà à leurs frontières métier ; aucun timer autosave générique ne doit avancer devant leur publication durable.
IMPLEMENTED — reprise sélective des kinds reconstructibles via Project DB, Task Kind Registry et Queue. Les dépendances/DAG restent différées ; il n'existe pas de scheduler global distinct à restaurer.
Accès Project Database
IMPLEMENTED — une connexion SQLite opaque sérialisée par mutex interne ; les opérations multi-tables sont transactionnelles et bornées.
IMPLEMENTED — le cycle de vie projet ouvre/crée project.db, vérifie
l'identité et ferme la connexion. Ouvrir, fermer ou changer de projet est une
frontière exacte : l'observateur et la vue optique libèrent leurs borrows, puis
l'unique Queue est annulée, jointe et détruite, callbacks terminaux inclus,
avant la fermeture de Project DB. Une seule Queue vide est ensuite recréée et
les observateurs sont rebondés. Il n'existe jamais deux schedulers simultanés.
L'historique terminal et l'espace d'IDs Queue sont ainsi propres à la session ;
les mêmes Task IDs durables de deux projets restent indépendants et aucun
historique fourni n'est affiché lorsqu'aucun projet n'est chargé.
IMPLEMENTED — la registry reconstruit explicitement callback/userdata hors mutex DB pour un kind connu ; elle ne soumet aucune tâche.
IMPLEMENTED — la queue accepte un identifiant restauré préassigné s'il n'entre en collision avec aucune tâche connue. L'import production peut donc être reconstruit puis soumis explicitement.
IMPLEMENTED — la resoumission automatique utilise la registry production, conserve le task ID et laisse le worker obtenir une nouvelle réservation. Kinds inconnus, tâches legacy, checkpoints invalides et sources absentes ne bloquent pas l'ouverture.
IMPLEMENTED — visual_index.update reprend à la dernière membership
commitée. Un segment temporaire n'est jamais visible et un rejeu exclut les
Feature Sets déjà membres.
Durée de vie terminale et fermeture Queue
Une Task terminale reste vivante jusqu'au retour complet de son callback
terminé. Queue la retire alors de la liste active et la détruit hors de son
mutex ; seule une histoire de 64 snapshots reste observable. Les appels déjà
enregistrés avant task_queue_destroy() sont attendus. Le propriétaire doit
empêcher tout nouvel appel dès le début de la destruction, règle nécessaire à
toute API C adressée par pointeur brut.
Un callback terminé peut consulter les vues Queue tant que le propriétaire la maintient vivante. Il ne peut pas détruire cette Queue, retirer son propre record ni attendre une opération dépendante de son retour.
Observatoire TUI actuel
CURRENT / VALIDATED OPERATIONAL. Ce statut décrit l'implémentation et ses
tests courants. L'audit global qui contient cette frontière est désormais
PASS/FROZEN après revue indépendante ; le statut TUI reste volontairement
opérationnel et n'interdit pas ses évolutions futures sous un ticket distinct.
Séparation modèle, observation et rendu
Le modèle tui_model est pur et testable sans terminal. Le renderer reçoit
seulement des copies bornées et n'interroge ni Queue, ni Governor, ni Project
DB, ni contrôleur SSD. Toutes les fonctions ncurses, l'entrée clavier et le
rendu demeurent sur le thread principal.
L'observateur runtime emprunte Queue et Governor et conserve une seule copie
cohérente. Les captures ordinaires sont coalescées pendant au moins une seconde
monotone ; un échec conserve la dernière vue bornée en la marquant stale.
Il observe au plus 129 Tasks : les 64 pending possibles, l'unique active et les
64 snapshots terminaux récents. L'ordre Queue place le travail vivant du plus
récent au plus ancien, puis l'histoire par terminaison décroissante ; une Task
active ne peut donc pas être masquée par un vieux préfixe historique. Il
n'existe ni scan DB par frame, ni lecture /proc volumineuse, ni historique
non borné.
Les ABI historiques restent exactes : Lardon3DTaskSnapshot,
Lardon3DResourceSnapshot, Lardon3DAppState et
lardon3d_layout_draw() ne sont pas étendus en place. Les surfaces additives
Lardon3DTaskObservation, lardon3d_task_queue_observe(),
Lardon3DResourceObservation, Lardon3DRuntimeSnapshot et
lardon3d_layout_draw_runtime() portent les nouveaux champs. De même,
lardon3d_tui_run() reste le symbole historique ; l'application utilise
lardon3d_tui_run_with_ssd_operation() avec un owner SSD conservé hors de
Lardon3DAppState.
Progression et ETA
Une Task typée publie completed/total seulement après son propre commit
métier durable. Quand ces compteurs sont connus, la TUI les affiche toujours et
en dérive le pourcentage sans utiliser le message ou le nom. Une Task marquée
COMPLETED avec un préfixe durable incomplet est une erreur d'intégrité
visible, jamais 100 %. Quand les comptes typés sont inconnus, le lifecycle peut
être terminal mais la progression scientifique reste indéterminée. Le
pourcentage générique non typé, lorsqu'il est utile, porte explicitement le
libellé runtime.
Le débit est un EWMA borné. La première observation établit seulement le
préfixe de reprise et ne contribue pas au taux ; une reprise de RUNNING remet
également la fenêtre temporelle à zéro. Deux intervalles strictement positifs
sont nécessaires avant un débit et une ETA connus. Une absence de progrès,
une pression Governor, une régression ou une preuve insuffisante produit
respectivement STALLED, THROTTLED, reset ou INDETERMINATE/CALCULATING.
Seule une complétion cohérente vaut exactement 100 % et ETA zéro ; aucune fausse
précision n'est affichée.
Pipeline et ressources
La synthèse utilise les étapes Acquisition, RAW, Quality, Features, Visual
Index, Candidate, Matcher, GV, Tracks, Sparse SfM et future Dense. Les états
sont NOT_READY, READY, QUEUED, RUNNING, THROTTLED, BLOCKED,
COMPLETE, FAILED et NOT_APPLICABLE. Dense reste explicitement
NOT_APPLICABLE tant qu'aucun Task kind de production n'existe ; une étape
future n'est jamais devinée active depuis un nom ou un message.
Le panneau ressources expose CPU actif/admis/disponible et sa raison, GPU
présent/mémoire/busy/backend lorsqu'ils sont connus, RAM/MemAvailable/réserve,
swap total/utilisé et deltas actifs, lot/inflight/helpers/I/O, scratch et
pression Governor GREEN/YELLOW/RED. Le contrat installé de l'exacte Task active
est l'autorité pour CPU et lot. Un dernier diagnostic privé seulement indexé
par kind peut appartenir à une autre Task ou séquence : backend, inflight,
helpers, utilisation ou raison restent donc UNKNOWN sans association exacte
Task+séquence. La mémoire UMA est comptée une seule fois et ni swap ni scratch
ne sont ajoutés à la capacité RAM.
Dimensions, couleurs et clavier
Le layout complet demande au moins 100×30. Le layout compact est validé à la
frontière 72×20 et reste supporté jusqu'au minimum 60×15. En dessous, le rendu
se réduit au message borné Terminal trop petit; un resize recalcule la classe
sans faire travailler un worker. Les rôles sémantiques sont healthy vert,
warning jaune, error rouge, GPU cyan, CPU bleu, SSD magenta, plus dim/bold.
Les libellés textuels demeurent l'autorité lorsqu'il n'y a pas de couleur ou
pas assez de paires terminal.
Les écrans courants sont accueil, projets, import, viewer futur, tâches,
ressources, optique, SSD et aide. F1..F7 naviguent respectivement vers aide,
projets, import, viewer, tâches, ressources et optique. Le segment littéral
F10 SSD est réservé au début du footer et reste visible à 60 colonnes dans
tous les modes pertinents. Les footers dérivent du même mode que le handler :
- saisie active : Enter valide, Échap annule, F10 reste disponible ;
- import actif :
Xdemande l'annulation et F10 reste disponible ;qet Échap sont affichés comme désactivés ; - mode idle :
q, Échap/navigation et les commandes propres à l'écran sont annoncés seulement lorsqu'ils sont réellement traités ; - Tasks : flèches/
j/k,Ppause,Rreprise,Cannulation ; - Optique : Tab change de panneau, flèches/
j/ksélectionnent,[revient à la première page et]charge la suivante ;B/L/C/V/A/G/K/Edéclenchent les opérations indiquées etRretente explicitement un bind/chargement.
Workflow optique
La TUI consomme les API v23 décrites dans Project Database, sans SQL direct ni édition d'une ligne immuable. Elle inspecte une affectation Capture, effectue seulement des lookup metadata exacts, liste les profils de boîtier/objectif/configuration et accepte un objectif manuel sans électronique ni alias — le Meike de test est un cas normal, pas une branche produit. « Modifier » signifie créer un nouveau profil ou une nouvelle configuration immuable, puis l'assigner explicitement à un groupe de campagne encore éligible ou à un Capture non affecté. Les calibrations listées doivent être compatibles avec l'exacte configuration et la sélection reste explicite ; absence, ambiguïté, incompatibilité, BUSY, I/O et corruption sont rendues sans profil fabriqué. Les pages ont 16 lignes, rapportent un compte page-local et un indicateur « suite » exact.
SSD F10 et lifetime application
La TUI affiche les huit états physiques ABSENT, DETECTED, ENABLING,
ENABLED, IN_USE, DRAINING, SAFE_TO_UNPLUG et ERROR, avec identité
stable, modèle/télémétrie lorsqu'ils sont connus, swap, scratch, mount, usage,
leases, drain et raison. UNKNOWN n'est jamais remplacé par zéro ou par une
supposition ; SAFE_TO_UNPLUG est mis en évidence comme endpoint sûr. F10
choisit exclusivement l'une des capacités exactes
can_enable, can_disable ou can_cancel_drain publiée par le contrôleur ; un
état incomplet, une paire de remplacement ou un résultat malformé n'accorde
aucune autorité. L'opération synchrone UDisks s'exécute dans au plus un thread
joinable, tandis que le main continue de rendre et de poller sans blocage.
La validation qui alimente ces capacités est fail-closed par état : toute
autorité exige Drive et deux partitions détectés, identités Drive+UUID exactes,
extents positifs connus et faits mount/activité/drain cohérents. ABSENT ne
peut transporter aucun fait actif, DETECTED partiel n'a aucune action et un
hazard ERROR déconnecté ne peut que retenir l'identité originale sans
allocation. Seule la reconnexion complète de ce tuple peut autoriser son drain.
Après chaque observation ou résultat validé, l'adaptateur enregistre une copie
bornée de l'état physique auprès du Governor. Une copie malformée devient
ERROR et interdit les nouvelles allocations ; l'observation ressources lit
cet état Governor-owned, tandis que les détails/permissions F10 restent dans
le snapshot physique. La génération source peut saturer à UINT64_MAX : une
update publique égale ne réaccorde jamais une autorité stale ; seule la
complétion du wrapper exact déjà engagé réconcilie son lease adressé. À l'arrêt,
l'ordre est : destruction/join de la Queue et
libération de chaque lease Task, fermeture du projet/DB, join puis unregister
vérifié de l'adaptateur SSD, destruction du contrôleur, puis destruction du
Governor. Les tests utilisent un provider factice et n'exécutent aucune vraie
mutation SSD.
Invariants
- ncurses appartient exclusivement au thread principal
- Aucune tâche ne démarre sans réservation active
- Les réservations sont libérées exactement une fois
- Les buffers sont strictement bornés
- Le Resource Governor reste l'unique propriétaire de l'admission ; ni Queue, ni contrôleur SSD ne constituent un second orchestrateur de ressources
Statut : CURRENT / VALIDATED OPERATIONAL
La TUI/runtime et son raccordement SSD sont implémentés, testés et relus dans
leur tranche. Le statut global est
GLOBAL_MAINTENANCE_AUDIT=PASS/FROZEN. Les builds portables/Vulkan,
sanitizers, contrôles de concurrence et ABI frais sont acquis ; l'unique revue
finale indépendante a conclu PASS sans finding bloquant.