lardon3d/docs/architecture/runtime.md
fy59 b84f860d86 refactor(core): freeze global maintenance baseline
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
2026-09-01 08:00:47 +02:00

16 KiB
Raw Blame History

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 WAIT ressources

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.

IMPLEMENTEDproject_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.

IMPLEMENTEDvisual_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 : X demande l'annulation et F10 reste disponible ; q et É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, P pause, R reprise, C annulation ;
  • Optique : Tab change de panneau, flèches/j/k sélectionnent, [ revient à la première page et ] charge la suivante ; B/L/C/V/A/G/K/E déclenchent les opérations indiquées et R retente 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.