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
30 KiB
Global maintenance audit — état consolidé
Lifecycle et portée
GLOBAL_MAINTENANCE_AUDIT=PASS/FROZEN.
Les tranches décrites ci-dessous, y compris l'observatoire TUI, F10 et le raccordement SSD/Governor, sont implémentées, validées opérationnellement et relues. Les builds Clang frais portable et Vulkan, les suites 64/64 et 65/65, les sanitizers applicables, TSan, les probes de headers publics et d'ABI sont acquis. L'unique revue finale indépendante GPT-5.6 SOL/ULTRA du dépôt consolidé a ensuite conclu PASS sans finding bloquant. La gate de maintenance est donc fermée et gelée ; ce statut ne réinterprète aucune science FROZEN et n'exécute pas la tranche scientifique suivante.
Ce record réconcilie l'état courant sans rouvrir les contrats scientifiques FROZEN. Les détails normatifs restent dans les documents de domaine :
- Project Database pour les migrations, identités et transactions ;
- Resource Governor pour l'admission et la matrice des quatorze Task kinds ;
- Task, Queue et runtime pour ownership, reprise et concurrence ;
- parallélisme interne et Geometric Verifier pour les bornes CPU et la science ;
- Resource Boundary pour la séparation des systèmes ;
- roadmap pour l'ordre des tranches.
Positionnement produit et identité des preuves
Lardon3D est un moteur de photogrammétrie générique, persistant et sensible aux ressources. Sony A6000, Samsung S21 FE, Ryzen 7 8845HS et Radeon 780M sont des fixtures, corpus ou hôtes de validation. Ils ne sont ni une identité produit, ni une branche de code métier, ni une limite portable de caméra, CPU ou GPU.
Les identités restent celles des contrats existants : Capture, Asset,
image_id, Task ID, groupe de campagne, profil de boîtier, profil d'objectif,
configuration optique et calibration sont distincts. Un chemin, basename,
SHA-256, champ EXIF ou modèle d'appareil n'est jamais promu implicitement en une
autre identité.
Project Database v23 et contexte optique générique
La tête de schéma courante est Project DB v23. La v23 est une migration additive au-dessus de la fondation scientifique et de persistance v22 gelée. Elle ne réécrit aucune ligne scientifique v16–v22 et crée neuf tables vides :
| Table v23 | Concept possédé |
|---|---|
camera_body_profiles |
profil de boîtier explicite |
camera_body_aliases |
alias metadata make/model exact, binaire |
lens_profiles |
profil d'objectif manuel, électronique ou intégré |
lens_profile_aliases |
alias metadata d'objectif exact et optionnel |
optical_configurations |
boîtier + objectif + focale exacte optionnelle |
acquisition_campaign_group_optics |
configuration explicite d'un groupe de campagne |
capture_optical_configurations |
configuration retenue pour un Capture et sa provenance |
optical_calibration_profiles |
calibration existante compatible avec une configuration exacte |
capture_calibration_selections |
sélection explicite de calibration pour un Capture |
La migration v22→v23 s'exécute dans la transaction de migration existante. Le marqueur de version n'est publié qu'après création de toutes les tables ; tout échec rollbacke l'ensemble. Elle n'infère et ne backfill aucun profil depuis EXIF, chemin, nom, SHA-256, dimensions, calibration historique ou marque d'appareil. Une base v22 migre donc avec les neuf tables optiques vides.
Un objectif manuel sans EXIF est un cas normal : un profil explicite peut ne posséder aucun alias metadata. Le Meike manuel utilisé dans les tests est une preuve de ce chemin générique, pas un modèle hardcodé. Une campagne peut assigner des configurations différentes à ses groupes ; lors de la rétention du Capture, le binding campagne/configuration et l'avancement du curseur sont atomiques. Un Capture importé hors campagne peut recevoir une affectation explicite distincte, sans valeur « inconnue » fabriquée.
Une calibration optique référence une calibration sparse immuable existante et une seule configuration exacte. Il n'existe ni interpolation, ni fallback par focale proche, ni sélection automatique. Le Capture et le profil doivent référencer la même configuration ; plusieurs profils compatibles restent ambigus jusqu'à une sélection explicite. Les retries exacts convergent, les conflits valides sont des contraintes et les dépendances durables malformées sont de la corruption.
Les migrations exercées via l'API production sur des copies des projets S21 et
A6000 ont atteint v23 avec integrity_check et clés étrangères propres, comptes
scientifiques inchangés et tables optiques vides. Les SHA-256 des projets source
sont restés inchangés. La validation v23 retenue comprend la suite normale
57/57, dix tests focalisés, cinq cibles ASan/UBSan, les contrôles de headers C17
et C++, les scénarios de rollback/retry et une revue indépendante après
correction. Cette preuve ne réinterprète aucune calibration réelle des deux
campagnes.
Resource Governor portable
Le Resource Governor demeure l'unique propriétaire de l'admission RAM, CPU, GPU et I/O et le seul orchestrateur des leases scratch de production. Le contrôleur SSD décrit plus bas est une frontière physique ; il n'est ni un scheduler, ni un second Governor, ni une source de capacité RAM.
La politique hôte courante réserve d'abord le système :
- quatre CPU logiques lorsque l'hôte le permet, tout en conservant au moins un CPU de calcul sur un petit hôte ;
- quand la topologie est fiable, un ensemble déterministe de coeurs physiques complets, frères SMT inclus, avec le dépassement minimal de la cible logique ;
- les CPU déjà exclus par une affinité externe comptent dans la réserve hôte, afin de ne pas soustraire deux fois la même capacité ;
- sans topologie ou masque fiable, le fallback ne fabrique aucun ID ni masque : il conserve seulement un budget de compte portable ;
- environ 3 Gio de
MemAvailablecomme réserve dure sur un hôte capable ; la bande 3–4 Gio est une prudence YELLOW qui bloque la croissance mais ne soustrait pas 4 Gio à la capacité ; - sur un petit hôte RAM, une réserve fractionnaire déterministe permet une dégradation sûre sans prétendre conserver 3 Gio inexistants ;
- PSI CPU/mémoire/I/O et deltas actifs de swap pilotent la pression ; l'occupation historique du swap ne suffit pas ;
- la mémoire UMA est débitée exactement une fois et swap/zram/SSD ne deviennent jamais un objectif de travail ni une extension de la RAM admise.
Il n'existe plus de plafond CPU global à 12. Le Governor borne chaque contrat
par le compute-pool réel et par la capacité intrinsèque du Task kind. Les IDs
0-5,8-13 et la réserve 6,7,14,15 restent la preuve historique correcte de
l'hôte 16-CPU validé, pas une politique portable.
Capacités des quatorze Task kinds
La matrice chiffrée complète appartient au Resource Governor. La classification courante est :
| Kind v1 | CPU et lot courants | Nature de la borne |
|---|---|---|
raw.develop |
CPU 1, lot 1 | opération atomique ; OpenCV global appliqué/restauré, allowance RAW 2 Gio |
photo_quality.triage |
CPU 1, lot 1 | un groupe borné ; aucune preuve de scaling utile |
acquisition_campaign.run |
CPU 1, lot 1 | orchestration d'un groupe ; coût JPEG/RAW exact, aucun Task imbriqué |
import.images |
CPU 1, lot 1..32 | copie/hash I/O-bound, réadmission par lot |
features.extract |
CPU 1..compute-pool, lot 1 | API OpenCV positive-int, plafond hôte Governor |
features.extract.sift |
CPU 1..compute-pool, lot 1 | même contrat OpenCV ; historique CPU12/CPU1 accepté exactement |
features.extract.rootsift |
CPU 1..compute-pool, lot 1 | même contrat OpenCV ; aucune voie GPU validée |
visual_index.update |
CPU 1..16, lot 1..16 | maximum algorithmique du segment de 16 Feature Sets |
candidate_pair.generate |
CPU 1..64, lot 1..64 | maximum algorithmique/ressource d'un batch de 64 sources |
matcher.run |
CPU sûr 1..12 ; lot AUTO utile 1..8, sûr 1..12 | batch Matcher mesuré ; Vulkan ORB exact, SIFT/RootSIFT CPU |
geometric_verifier.run |
CPU utile 1..8, fenêtre CPU sûre 16, lot 1..16 | outer-parallelism opérationnel ; USAC interne reste isParallel=false |
track_builder.run |
CPU 1, lot 1 | rebuild DSU complet, publication atomique owner-only |
sparse_sfm.run |
CPU 1, lot 1 | contrat scientifique/atomique FROZEN, BA à un thread |
incremental_reconstruction.run |
CPU 1, lot 1 | recomputation atomique FROZEN depuis entrées immuables |
Candidate utilise au plus cpu_threads-1 enfants et un handle DB privé par
participant, le callback Queue étant lui-même participant. Ses piles et états
sont facturés à 8 Mio par item ; batch=1 ne crée pas de travail vide.
Visual Index conserve ses buffers de segment fixes et publie seul après toutes
les jointures. ORB/SIFT/RootSIFT utilisent INT_MAX seulement comme borne de
l'API OpenCV : l'admission réelle reste le compute-pool. Les signatures CPU12
historiques sont des formats de reprise exacts et ne réintroduisent aucun
plafond portable.
La même discipline vaut à la frontière géométrique Sparse SfM : les champs
publics uint32_t max_iterations et minimum_inliers de relative pose et PnP
doivent être représentables par le int OpenCV, donc être au plus INT_MAX
(max_iterations reste strictement positif). Une valeur supérieure est
refusée avant narrowing, allocation, appel solveur ou mutation des sorties.
L'encodage F0 reste u32, et tous les paramètres FROZEN représentables,
valeurs de référence, fingerprints et résultats scientifiques restent
inchangés : cette borne est opérationnelle/ABI externe, pas une nouvelle
politique scientifique.
Le Geometric Verifier prépare désormais des Match Results indépendants en
parallèle, puis le propriétaire unique publie dans l'ordre et checkpointe un
préfixe contigu. Sur le corpus représentatif réel de 4 113 parents, les sorties,
IDs canoniques et le digest scientifique
9401ef6168804b6f1d51f4cdf64cd6b33cbebd2934e5294c8feacc87f9c8ce86
restent identiques. Les débits Task complets CPU1/2/4/8/12 sont
60,7514/83,9556/104,7545/116,6329/119,7606 parents/s. CPU12 n'ajoute que
2,68 % sur CPU8, sous le seuil d'acceptation 5 % : le plafond utile est 8,
la fenêtre sûre et le lot maximal restent 16. La preuve S21 complète reste une
preuve historique CPU1/batch8 valide : 172 741 parents traversés, 172 275
applicables, 24 065 vérifiés et 148 210 rejetés en 3 221,758 s, sans doublon,
avec reprise/SIGKILL validée. Elle n'est pas présentée comme un rerun du
nouveau chemin. Le manifest retenu de la preuve outer-parallel a le SHA-256
52a4412299c74050a66d5690122a793c9451c79faf47e32b6e65a5958f804856 :
il prouve littéralement les 4 102 lignes applicables, leurs IDs 1..4102,
statuts, modèles, masques et compteurs, pas seulement un wall. Un second run
complet S21 de 3 221 s n'est pas requis pour accepter cette maintenance : le
run S21 acquis porte déjà la science/persistance v3 gelée, tandis que la preuve
réelle bornée et les tests de publication/reprise discriminent exactement le
seul changement, l'ordonnancement externe de préparation.
Task, Queue, persistance et concurrence
Une seule Queue possède un worker et un callback actif. Le Governor admet la séquence ; la Task possède son état d'exécution ; la Queue possède seulement ordre/backpressure et, après retour du callback terminal, une histoire de 64 snapshots maximum. Task et userdata sont détruits hors mutex Queue et seulement après la fin du callback.
La fermeture Queue ferme d'abord l'ingress, attend le worker et tous les appels déjà enregistrés, puis détruit exactement une fois. Comme pour tout pointeur C brut, le propriétaire doit empêcher le début de nouveaux appels après le début de la destruction. Les callbacks terminaux peuvent observer les API read-only, mais ne doivent pas retirer synchroniquement leur propre entrée, détruire leur Queue ou attendre une opération dépendant de leur propre retour.
Les IDs Queue générés sont monotones et leur épuisement à UINT64_MAX est
sticky ; ni un ID restauré plus petit, ni l'éviction de l'histoire ne réarme la
génération. La reprise restaure état métier et estimation durable, jamais
thread, callback, pointeur, réservation ou contrat actif. Les signatures de
ressources historiques explicitement reconnues sont normalisées seulement en
mémoire ; aucun checkpoint « estimate-only » n'est publié. Les curseurs métier
et la publication scientifique restent les seules frontières de progression.
Ouvrir, fermer ou changer de projet constitue une frontière de session exacte. Les vues libèrent leurs borrows, puis la Queue est annulée, jointe et détruite avant Project DB ; une unique Queue vide est ensuite recréée et les observateurs sont rebondés. Les callbacks terminaux peuvent donc finir avec leur DB encore vivante, et histoire/namespace Queue d'un projet ne fuient pas dans le suivant.
Observatoire et centre de contrôle TUI
CURRENT / VALIDATED OPERATIONAL. ncurses, l'entrée et le rendu appartiennent
uniquement au thread principal. Un modèle de vue pur reçoit des copies bornées
et ne mute ni Queue, ni Governor, ni DB, ni science. L'observateur coalesce les
captures ordinaires pendant au moins une seconde et couvre exactement le pire
cas production : 64 pending, une active et 64 historiques, soit 129 Tasks. Les
ABI historiques TaskSnapshot, ResourceSnapshot, AppState et layout sont
préservées ; les identités typées, comptes durables, contrats installés,
SwapTotal et vues riches passent par des types/fonctions additifs.
La progression exacte emploie toujours le préfixe métier durable quand il est
connu. Un terminal 2/7 est une erreur d'intégrité et un terminal typé sans
compteur scientifique reste indéterminé ; aucun des deux ne devient un faux
100 %. L'EWMA/ETA exclut le préfixe repris et exige deux intervalles positifs ;
les états calculating, indeterminate, stalled, throttled et complete restent
explicites. Le pipeline expose Acquisition, RAW, Quality, Features, Visual
Index, Candidate, Matcher, GV, Tracks, Sparse SfM et Dense, avec
NOT_READY/READY/QUEUED/RUNNING/THROTTLED/BLOCKED/COMPLETE/FAILED/NOT_APPLICABLE.
Dense future reste NOT_APPLICABLE et n'est jamais montrée en cours.
Le panneau ressources présente CPU actif/admis/disponible et sa raison,
GPU/mémoire/busy/backend, RAM/MemAvailable/réserve, swap total/utilisé/deltas,
lot/inflight/helpers/I/O/scratch et Governor GREEN/YELLOW/RED. Le contrat de
l'exacte Task active est prioritaire ; backend/inflight/helpers ou raison
restent UNKNOWN lorsqu'une association Task+séquence n'est pas prouvée.
L'espace scratch enregistré auprès du Governor est présenté séparément des
détails physiques contrôleur et n'est jamais additionné à la RAM.
Les layouts validés sont full à partir de 100×30, compact à 72×20 et jusqu'au
minimum 60×15 ; en dessous, seul Terminal trop petit est rendu. Vert/jaune/
rouge/cyan/bleu/magenta et dim/bold ont toujours des libellés de repli sans
couleur. F10 SSD est un segment littéral garanti à 60 colonnes dans les modes
idle, saisie et import. L'aide est contextuelle : saisie = Enter/Échap/F10,
import actif = X/F10 avec q/Échap désactivés, et les écrans idle n'annoncent que
les touches réellement traitées.
L'écran optique effectue seulement des lookup metadata exacts, accepte le
Meike manuel sans EXIF comme cas normal, crée de nouveaux profils/configurations
immuables et affecte explicitement groupe de campagne ou Capture. Il liste et
sélectionne uniquement les calibrations exactement compatibles ; unresolved,
absence, ambiguïté, incompatibilité, BUSY, I/O et corruption restent visibles.
Les pages de 16 rapportent leur compte local et l'existence exacte d'une suite ;
[ revient à la première page, ] avance et R retente explicitement.
Contrôleur SSD externe optionnel
Le contrôleur SSD est la frontière physique synchrone et bornée vers UDisks2
via GIO/GDBus. Il découvre exactement les labels LARDON_SWAP et
LARDON_SCRATCH, exige des UUID stables et une même identité UDisks Drive, et
ne dépend jamais d'un /dev/sdX, modèle, numéro de série ou object path comme
identité produit. Un renommage de device node ne change donc pas l'identité.
Les états publics sont ABSENT, DETECTED, ENABLING, ENABLED, IN_USE,
DRAINING, SAFE_TO_UNPLUG et ERROR. Les snapshots sont des copies bornées ;
les valeurs inconnues restent explicitement inconnues. Le poll ordinaire est
coalescé autour d'une seconde. Le chemin production n'appelle pas statvfs
depuis le poll synchrone : un scratch monté peut donc rapporter total/libre
inconnus.
La conversion vers le Governor valide l'état complet et échoue fermée. Toute
paire ou autorité (pairing_valid, allocation, enable/disable/cancel, ou état
ENABLING/ENABLED/IN_USE/DRAINING/SAFE_TO_UNPLUG) exige la détection actuelle
du Drive et des deux partitions, les trois identités exactes non vides, des
tailles de partition connues strictement positives et des faits mount/activité/
capacité cohérents. ABSENT rejette toute télémétrie active, montée, louée ou
capacitaire ; un DETECTED partiel reste visible mais non actionnable. Un
hazard ERROR déconnecté peut conserver son tuple pour la sécurité, jamais
pour allouer ; can_disable n'est acceptable qu'après reconnexion prouvée de
l'exact tuple original.
L'activation appelle directement UDisks et exige le point de montage exact
/mnt/lardon-scratch. Elle ne formate, partitionne, répare, fsck, poweroff ni
supprime rien. Avant toute action potentiellement side-effecting, l'ownership
physique est lié conservativement au tuple Drive+deux UUID. Un timeout ou une
vérification indéterminée laisse un hazard sticky ; aucun disque de remplacement
ne reçoit alors Start/Mount/Stop/Unmount. Seul le même tuple reconnecté peut
être vérifié et drainé jusqu'à SAFE_TO_UNPLUG.
Le scratch utilise au plus 64 leases explicites. L'adresse de l'objet lease
fait partie de son ownership process-local : copie, reconstruction, objet
étranger, stale ou double release ne décrémentent rien. DRAINING refuse de
nouvelles leases. Le swap n'est arrêté que si les leases sont nulles, que les
PSI et deltas swap sont calmes, et que son usage peut être absorbé tout en
conservant la réserve dure de 3 Gio. La propriété UDisks
Swapspace.Active doit être un booléen exact connu ; absence ou mauvais type
échoue fermé. Aucun arrêt forcé n'existe.
Le contrôleur ne crée ni thread de polling, ni scheduler, ni capacité Task
scratch implicite. L'adaptateur TUI possède au plus un thread joinable pendant
un poll ou contrôle UDisks borné ; il ne touche jamais ncurses. F10 consomme
exclusivement can_enable, can_disable ou can_cancel_drain du snapshot
validé, jamais une inférence depuis l'état. Les huit états, identité stable,
modèle, lien, swap, scratch, mount, usage, drain et raison sont présentés ; une
valeur inconnue reste UNKNOWN, et SAFE_TO_UNPLUG est explicite.
Après chaque snapshot ou contrôle validé, l'adaptateur enregistre l'état
physique auprès du Governor. Un snapshot malformé enregistre ERROR et bloque
les nouvelles allocations. Les wrappers Governor sont l'unique voie de lease
scratch de production ; le contrôleur conserve seulement son API physique
basse. Les quatorze Task kinds actuels n'ont aucun consommateur scratch, donc zéro
lease est la vérité courante et une capacité disponible n'est pas un usage.
La génération source peut légalement saturer à UINT64_MAX. Une update publique
matériellement différente au même watermark reste stale et ne peut pas rendre
de l'autorité ; seule la fin sérialisée du wrapper exact déjà engagé peut
réconcilier à ce watermark sa propre acquisition/libération et le compte de
leases fondé sur les adresses. Même son erreur ne peut recopier un compte stale.
Le shutdown suit Queue/leases → fermeture projet → join+unregister SSD →
contrôleur → Governor. Tous les tests SSD/TUI utilisent un provider factice et
n'exécutent aucune mutation réelle de l'hôte.
GPU, build et ABI
Le backend Vulkan ORB Matcher est une voie production validée et GPU-first en mode AUTO quand l'admission GPU/UMA est sûre. CPU reste le fallback portable. Le batch AUTO utile est 8 ; batch 12 et inflight 2 restent des bornes de sûreté et de benchmark rejetées pour la politique normale faute de gain supérieur à 5 %. Candidate, Feature et Visual Index restent CPU ; SIFT/RootSIFT Vulkan est rejeté par l'évidence disponible ; GV reste CPU et conserve son USAC interne non parallèle. Sur la Radeon 780M réelle RADV PHOENIX, la feasibility explicite compare 24 160 requêtes SIFT et 24 161 RootSIFT : chaque mode produit une divergence d'index, respectivement 20 251 et 20 824 divergences de bits de distance, mais zéro divergence de décision Lowe. C'est une preuve de feasibility décisionnelle, pas une équivalence binaire ; le hot path SIFT/RootSIFT Vulkan reste donc rejeté. Les overrides CPU/Vulkan sont des outils de debug, benchmark ou reproductibilité, pas un choix backend exigé de l'utilisateur normal.
Le dépôt est mixte C17/C++17. Les headers publics sous include/lardon3d/
restent compilables comme C17, n'exposent aucun type C++ et décrivent ownership,
bounds et erreurs. Aucune exception C++ ne traverse une frontière C. Meson et
Ninja sont le build de référence ; Clang est préféré et GCC reste couvert pour
les contrôles C17 pertinents. GIO/GDBus est la dépendance directe du contrôleur
SSD ; aucune commande shell mount, swapon ou équivalente n'est construite.
Évidence finale acquise et revue indépendante
Builds normaux, Vulkan et ABI
Deux répertoires entièrement frais utilisent Clang/Clang++ 22.1.8, C17 et C++17 :
- portable
-Dvulkan_orb=disabled: graphe complet 931/931 puis suite sérielle 64/64 ; - Vulkan
-Dvulkan_orb=enabled: graphe complet 939/939 puis suite sérielle 65/65, dontorb-vulkan-backendsur l'iGPU réelAMD Radeon 780M Graphics (RADV PHOENIX); la dépendance Vulkan détectée vaut 1.4.357 ; - la feasibility SIFT/RootSIFT, cible non enregistrée dans la suite normale, a été compilée et exécutée explicitement ; ses divergences exactes sont consignées ci-dessus et n'autorisent aucun backend production nouveau ;
- les probes autonomes stricts GCC/Clang C17 et C++17 passent 76/76 sur les
19 headers publics modifiés ou nouveaux, le fixture ABI historique, le
lien de l'application et
git diff --checkpassent, etgit status --short -- scan3dreste vide.
La correction de narrowing Sparse SfM, postérieure à la capture des deux logs
complets, a ensuite passé son test focalisé, 20 répétitions, ASan/UBSan ciblé,
les inclusions C17/C++17 et le lien application. Elle retire les conversions
publiques uint32_t → int sans changer les appels valides ni la science.
Déterminisme post-freeze du fixture Feature
La validation post-freeze a exposé une dépendance exclusivement test à
l'activité de l'hôte : test-feature-task construisait des profils et
Governors synthétiques, mais leur admission capturait encore le vrai
/proc/loadavg, les PSI et les deltas de swap. Une charge minute suffisante
pouvait donc maintenir correctement la Queue en WAIT jusqu'à son délai de
30 s, sans divergence scientifique ni défaut de la politique production.
Le fixture possède désormais un override privé par Governor qui copie sous
mutex un ResourceSnapshot complet et ne rafraîchit que son horodatage. Il est
compilé exclusivement dans test-feature-task ; la capture réelle et les
symboles production restent inchangés. Une régression directe prouve que la
charge synthétique 5 produit toujours WAIT, puis que la charge 0 produit
START : le seam contrôle la télémétrie, jamais la politique. La revue bornée
a ensuite identifié le second Governor créé par le helper runtime ; celui-ci
reçoit maintenant le même snapshot avant toute création de Queue, avec cleanup
explicite des échecs.
Après cette correction et sa revue, le test Feature complet passe 100/100,
la matrice ordonnée Candidate→Matcher→Matcher pipeline→Feature 4/4, la suite
portable finale 64/64 et la suite Vulkan finale 65/65. Les exécutions
ciblées ASan/UBSan avec detect_leaks=0 et TSan passent également. La
qualification LSan externe décrite ci-dessous reste inchangée. Une suite
normale mixte lancée après une reconstruction large a aussi observé un unique
timeout task ; le ciblé immédiat et la matrice de revue 100/100 passent.
Ce cas reste qualifié non reproductible/environnemental : ni le timeout ni le
comportement Task n'ont été modifiés.
ASan, UBSan et LeakSanitizer
Le build frais Clang 22.1.8 address,undefined, portable/Vulkan désactivé, a
d'abord été exécuté avec détection de fuites : 57 OK, 6 FAIL, 1 TIMEOUT.
Cette première passe n'est pas masquée :
feature-store,visual-index,candidate-pair-task,precision-featuresetprecision-consolidationterminent chacun sur la même fuite externe, exactement 3 808 octets en 68 allocations, avec la dernière frame/opt/cuda/lib64/libOpenCL.so+0x2fd4, Build IDb3217362255db6f1188e7596454ffe8bc4606b53, résolue vers/opt/cuda/targets/x86_64-linux/lib/libOpenCL.so.1.0.0, sans frame projet ;feature-taskatteint une fois son attente adaptative interne de 30 s ettaskle timeout Meson de 30 s, sans diagnostic ASan/UBSan/LSan.
Après cette attribution, la matrice complète repasse avec
detect_leaks=0 mais ASan et UBSan toujours actifs : 64/64, zéro timeout.
Une matrice séparée de vingt cibles dont ldd prouve qu'elles ne chargent ni
OpenCV ni OpenCL passe 20/20 avec LeakSanitizer actif. Les anomalies de
temps initiales ne s'étaient pas reproduites dans le suivi initial : 20/20
exécutions feature-task à affinité complète et 100/100 exécutions task avec
perturbations allocateur. Le cas Feature a été reproduit plus tard par la suite
portable ordonnée, puis déterminisé comme décrit ci-dessus ; le cas Task reste
non reproductible. La conclusion sanitizer exacte demeure : ASan/UBSan projet
64/64, chemins projet loader-free LSan 20/20, limitation LSan du loader
OpenCL externe conservée explicitement — jamais un faux « LSan global 64/64 ».
Concurrence
Le build frais GCC/G++ 16.2.1 TSan, volontairement Vulkan-disabled, passe les
quatorze cibles concurrentes Task/Project/Queue/Governor/SSD/TUI/Candidate/
Visual/Feature/Matcher/GV 14/14, puis 220/220 répétitions déterministes,
soit 234/234 exécutions. tests/tsan-opencv.supp ne supprime que les races
issues des objets externes non instrumentés libopencv_features.so,
libopencv_core.so et libtbb.so; aucune frame Lardon3D n'est supprimée.
Cette preuve ne couvre pas Vulkan sous TSan : le backend réel est couvert par
le build/suite Vulkan ci-dessus et ses tests propres.
Audit des avertissements
Le build Vulkan complet et ses cibles non-default ont produit 785 émissions :
117 dans des sources projet/tests/benchmarks et 668 dans les headers OpenCV
installés. L'audit n'a trouvé qu'une famille matérielle pour la frontière
publique courante : le narrowing Sparse max_iterations/
minimum_inliers → int, corrigé et revalidé comme décrit plus haut. Les
avertissements restants sont classés :
| Classe | Emplacements | Qualification |
|---|---|---|
| Baseline production, non matériel pour cette maintenance | src/acquisition_campaign_task.cpp:68, src/raw_development.cpp:185, src/acquisition_pairing.cpp:549, src/feature_extractor_opencv.cpp:231 |
conversions sur valeurs déjà bornées par leur structure/validation ; aucune identité ni sortie modifiée dans ce ticket |
| Baseline géométrique bornée | src/sparse_sfm_geometry.cpp:312-313 |
index OpenCV signé sur les deux vecteurs construits avec le même nombre de colonnes ; distinct du narrowing public corrigé |
| Tests/benchmarks seulement | tests/test_dense_mvs.cpp:45, tests/test_precision_consolidation.c:846-847, shadows dans tests/test_sparse_sfm_incremental.cpp:441-723, API d'initialisation OpenCV dépréciée et indices signés dans tests/benchmark_geometric_verifier.cpp:101-190 |
aucun chemin production ni format durable |
| GCC TSan, non matériel | src/feature_task.c:209-215, src/sift_task.c:170-175 |
-Wmaybe-uninitialized interprocédural ; le callback fournit une Task non nulle et le helper initialise le contrôle avant toute autre sortie d'échec |
| Externe | headers OpenCV 5 installés ; découverte CMake Ceres | conversions/extensions et warnings de generator expressions hors sources Lardon3D |
Les catégories baseline ne sont pas promues en dette scientifique cachée : elles sont consignées pour une maintenance ultérieure, tandis que le seul narrowing pouvant accepter une valeur publique non représentable a été traité avant la revue finale.
Revue finale et clôture de la gate
Les tests focalisés TUI/runtime/SSD, les répétitions déterministes et la validation F10 par providers factices restent acquis. La preuve GV finale comprend 8/8 tests d'intégration focalisés, 60/60 répétitions de stress, 3/3 ASan/UBSan et 3/3 TSan ; les détails Project DB v23 sont consignés plus haut.
L'unique revue finale indépendante GPT-5.6 SOL/ULTRA a conclu PASS, avec
zéro finding bloquant. Elle a indépendamment exécuté le build portable, la
suite complète 64/64, la matrice focalisée 15/15, les probes strictes
GCC/Clang C17+C++17 76/76 sur 19 headers publics modifiés/nouveaux, le
fixture ABI, les négatifs de symboles/strings des seams production, la
vérification du SHA-256 du manifest GV retenu et git diff --check.
Cette preuve indépendante ferme la gate sans effacer les qualifications
sanitizer consignées ci-dessus. L'ordre autorisé est désormais :
science réelle acquise jusqu'à GV
→ GLOBAL_MAINTENANCE_AUDIT (PASS/FROZEN)
→ prochaine tranche séparée : poursuite réelle depuis Tracks
→ Sparse SfM réel / Dense selon calibration et roadmap
scan3d/ et les contrats scientifiques Tracks/SfM/Dense restent protégés et
n'ont pas été modifiés par cette réconciliation.