#+title: Architecture et conventions de développement #+author: Sébastien Miquel #+date: 22-08-2026 #+OPTIONS: Ce document décrit les invariants techniques du projet. Il s'adresse aux développeurs et aux agents logiciels qui modifient le dépôt. - [[file:Readme.org][Guide de démarrage]] - [[file:Script.org][Référence des étapes et des scripts]] * API commune pour les scripts Le paquet =copienator= centralise les chemins d'une évaluation et les écritures JSON sûres. Un script ne devrait donc plus reconstruire les chemins partagés à la main : #+BEGIN_SRC python from copienator import EvaluationWorkspace, atomic_write_json workspace = EvaluationWorkspace("Interro") atomic_write_json(workspace.correction_file, corrections) #+END_SRC =EvaluationWorkspace.discover(path)= retrouve également la racine d'une évaluation à partir d'un fichier ou d'un sous-dossier. Sa construction ne crée aucun fichier. La création explicite de =.copienator/logs/= et =.copienator/runs/= se fait avec =workspace.ensure_control_directories()=. Le chemin =.copienator/state.sqlite3= est réservé à une future couche d'état transactionnelle. =atomic_write_json= écrit d'abord dans un fichier temporaire situé dans le même dossier, synchronise son contenu, puis remplace la destination. Une interruption ne laisse donc pas un JSON partiellement écrit. Pour une modification concurrente de type lire-modifier-écrire, utiliser =atomic_update_json= : cet utilitaire protège l'opération complète avec un verrou inter-processus Linux/Windows. * Convention des scripts standardisés Les scripts standardisés exposent =build_parser()=, =run(...)= et =main(argv=None)=. Leur import ne lance aucun traitement. Ils acceptent le dossier d'évaluation comme premier argument positionnel, utilisent =EvaluationWorkspace= pour les chemins partagés et peuvent afficher la trace complète d'une erreur avec =--verbose=. Les codes de sortie communs sont : | Code | Signification | |------+---------------| | 0 | réussite | | 1 | erreur de traitement | | 2 | arguments invalides | | 3 | évaluation ou prérequis invalides | | 4 | traitement partiel, avec avertissements | | 130 | interruption par l'utilisateur | Le GUI distingue notamment un traitement partiel d'un échec. Toutes les commandes du paquet suivent désormais cette convention. Elles sont regroupées dans =copienator.commands= et exposées par le répartiteur =python -m copienator=. * Organisation du code - Le paquet =copienator= contient les abstractions partagées par les scripts, notamment =EvaluationWorkspace= et les écritures atomiques. - Le paquet =copienator_gui= contient la définition du workflow, l'état persistant, le lanceur de processus et l'application Tk. - Le sous-paquet =copienator.commands= contient les points d'entrée. Leur import ne doit pas déclencher de traitement. - =copienator.dispatcher= expose le CLI unifié et =copienator_gui= contient l'application Tk. * État et exécution du GUI L'état d'une évaluation est conservé dans =.copienator-gui.json=. Les valeurs des arguments, le dernier mode exécuté, les statuts et un historique borné y sont enregistrés atomiquement. Les sorties des processus sont conservées dans =.copienator/logs/=. Le GUI lance les scripts dans des processus séparés afin de pouvoir afficher leur sortie, leur transmettre une saisie et les interrompre. Les retours en arrière ne suppriment pas les artefacts. Ils marquent les étapes suivantes comme étant à revalider. Les automatismes de première visite sont également persistés et ne doivent pas être rejoués lors d'un retour en arrière. * Invariants à préserver - Utiliser =EvaluationWorkspace= pour les chemins partagés d'une évaluation au lieu de reconstruire ces chemins dans chaque script. - Publier les JSON et les ensembles de fichiers importants de façon atomique. Une interruption ou un résultat partiel doit conserver la dernière sortie complète lorsqu'elle existe. - Conserver la convention =build_parser()=, =run(...)= et =main(argv=None)= pour les scripts standardisés. - Ne lancer aucun traitement lors de l'import d'un module. - Retourner les codes de sortie communs afin que le GUI distingue une réussite, un résultat partiel, un échec et une interruption. - Préserver les modifications manuelles et les fichiers déjà présents, sauf lorsqu'une option explicite comme =--overwrite= ou =--reset= autorise leur remplacement. - Les labels deviennent des noms de fichiers. Ils doivent donc respecter les contraintes de la plateforme, notamment l'interdiction de =:= sous Windows. * Règle de classement de la documentation - Une information nécessaire pour installer ou démarrer l'application appartient à [[file:Readme.org][Readme.org]]. - Une commande, un argument ou une étape du workflow appartient à [[file:Script.org][Script.org]]. - Une convention interne, un invariant ou une décision d'architecture appartient au présent document.