Qualification hors ligne des applications AGILAB ================================================ Le harnais ``tools/testing/agilab_offline_app_qualification.py`` exécute des contrôles explicitement choisis avec des environnements déjà provisionnés. Il n'installe aucune dépendance. Les manifestes, les données et les reçus privés restent dans un répertoire de qualification extérieur au dépôt public. Manifeste et commande --------------------- Le schéma est ``agilab.offline_app_qualification_manifest.v1``. Chaque source déclare un ``root`` absolu, sa révision Git exacte de 40 caractères et des ``paths`` relatifs. Chaque contrôle fournit ``id``, ``app``, ``layer``, ``scope``, ``argv`` et ``cwd``. Les couches sont ``science``, ``web`` et ``notebook``. Utiliser un Python absolu propre à l'environnement isolé dans ``argv`` et ``control_python``. ``env`` complète son environnement ; ``timeout`` vaut entre 1 et 1800 secondes. ``network`` choisit ``deny_all`` ou ``loopback``. ``{output}`` est remplacé dans les arguments par le répertoire de preuves. Un ``junit`` désigne un XML de tests exécutés ; ``json_evidence`` désigne un JSON dont ``status`` doit valoir ``passed``. ``artifacts`` déclare les autres fichiers à conserver et à vérifier. Ces chemins de preuves sont relatifs à la sortie. Exemple de commande, après création d'un manifeste contenant de vrais contrôles scientifiques et des chemins absolus résolus : .. code-block:: console tokki run -- uv run --no-sync python tools/testing/agilab_offline_app_qualification.py \ --manifest "$QUALIFICATION_ROOT/agilab_offline_science_manifest.json" \ --output-dir "$QUALIFICATION_ROOT/agilab_offline_science_evidence" La sortie doit être nouvelle. Une session agent gérée emploie le préfixe authentifié fourni par son hook pour cette commande. Un nom de test, un import réussi ou une sortie zéro ne suffit pas à établir une fonction scientifique. Sources et reçus ---------------- Le harnais vérifie la révision, l'absence de modifications suivies et les hashes des sources avant et après exécution. Il refuse les entrées non suivies dans les périmètres déclarés, y compris le code masqué par des règles Git d'exclusion. Les caches Python canoniques sont liés à une source suivie et leurs octets sont hashés, même pour un périmètre limité à un fichier. Les caches de test et de typage non exécutables, les métadonnées de construction reconnues et ``uv.lock`` restent des sous-produits exclus. Le bytecode sans source et le code ajouté dans un répertoire portant un nom de cache sont refusés. Le hash du manifeste correspond aux octets lus avant le calcul. Une modification du manifeste, du harnais ou des entrées liées invalide la qualification. Les preuves JSON imbriquées gardent leur chemin relatif complet. Vérifier aussi les modules effectivement chargés, leur ``__file__`` et le bundle des vues : une révision déclarée ne choisit pas automatiquement les imports de l'environnement. Chaque reçu conserve les contrôles réseau, la portée annoncée, les sorties et leurs hashes. Les tests entièrement ignorés restent bloqués. Les couches non exécutées restent non qualifiées. Une reprise crée de nouveaux reçus ; les échecs et les contrôles ignorés précédents restent intacts. Réseau et processus ------------------- Le backend actuel nécessite macOS et ``sandbox-exec``. ``deny_all`` refuse le réseau, y compris la boucle locale. ``loopback`` autorise la boucle locale et les sockets Unix nécessaires au navigateur et au noyau Jupyter ; les connexions IP externes restent refusées. Les sondes vérifient le refus externe dans le processus et un enfant avant les contrôles. Cela décrit une exécution contrainte par le système, sans établir un isolement physique du poste. Le nettoyage lie les descendants observés à leur PID et à leur instant de création. Un enfant détaché observé est arrêté et vérifié ; une erreur concernant un processus vivant reste un échec. Ce mécanisme ne garantit pas le confinement d'un descendant très rapidement daemonisé avant son observation. Provisionnement et portée vérifiée le 8 octobre 2026 ---------------------------------------------------- Provisionner les fermetures de dépendances des gestionnaires, travailleurs et contrôles choisis dans des environnements et caches dédiés hors dépôt. Les téléchargements appartiennent à cette phase préalable. En exécution, le réseau externe reste bloqué et aucune installation n'est effectuée. Conserver les logs, les versions, les contrôles de cohérence et les hashes ; préserver les environnements de l'utilisateur et les données absentes. Les preuves existantes couvrent 14 projets intégrés et 9 projets privés par des périmètres scientifiques réels explicitement nommés. Au total, les périmètres réussis contiennent 212 cas JUnit exécutés sans cas ignoré, dont 96 contrôles de planification et de contrôle du CLI privé. Des travailleurs réels et un dispatch multi-application produisent également des artefacts vérifiés hors JUnit. Ces résultats ne qualifient pas toutes les fonctions de chaque application. Le dataplane réel du CLI reste bloqué sur macOS : il exige un noyau Linux, les outils réseau et les privilèges nécessaires au contrôle réel. Les contrôles de planification ne remplacent pas cette exécution. La vue native spécialisée et l'export spécialisé commun exécuté dans un noyau frais disposent de preuves séparées. Les anciens notebooks de test avec des données externes absentes ne constituent pas des workflows exposés qualifiés. Le bundle d'un nouveau candidat doit recevoir sa propre vérification de parité web et notebook avant de reprendre les résultats d'une version précédente.