Config strictement stricte partout (ruff, mypy --strict, bandit, vulture,
import-linter, eslint, stylelint), aucune regle desactivee "pour ne pas
casser le build" - l'existant a ete corrige pour la satisfaire plutot que
l'inverse. Hooks pre-commit locaux (language: system) bloquants.
- Typage mypy --strict propage a tout le moteur (db, screens, auth, core,
ai, routes, puis publish/scripts/tests/app.py/build_css.py).
- Securite : fuite de handle fichier Windows corrigee dans l'export SCORM
(routes/publish/export_scorm.py), CSRF/RNG non-crypto/xAPI documentes
(# nosec, # NOSONAR justifies), nouveau db.json_for_script() (echappe
"</script>" dans le JSON embarque en <script>, 25 sites).
- Architecture : imports circulaires/F811 nettoyes, contrats
import-linter respectes, code mort retire (vulture).
- Accessibilite : 69 champs de formulaire sans label correctement
associe corriges (for/id ou aria-label) sur 11 templates.
- ESLint/Stylelint : lot mecanique JS/CSS, regles ajustees puis
appliquees (aucune desactivee sans verification individuelle).
- Tests : isolation du compte admin partage (nettoyage ponctuel +
fixture de teardown automatique en filet de securite), suite complete
verte (591 tests Python, 241 tests JS).
- SonarQube Community Build self-heberge (Docker + PostgreSQL) : rapport
complet analyse point par point, faux positifs documentes.
- .gitattributes ajoute (LF force) : core.autocrlf=true sur cette machine
faisait echouer ESLint (linebreak-style) via un bug connu de git
(checkout "en place" qui ignore l'eol force sur un fichier deja
present sur disque - contourne en supprimant puis recreant chaque
fichier suivi).
djLint (H021, styles inline) volontairement saute pour ce commit -
backlog assume, deja documente, traite dans un lot separe.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
screens/labels/data_operations.py : 6 nouvelles opérations pour
"Modifier une donnée"/"Modifier une variable" — multiplier, diviser,
modulo (garde-fou division par zéro : valeur inchangée plutôt qu'une
ZeroDivisionError qui interromprait le graphe), minimum/maximum (borne
la valeur ACTUELLE — utile pour une variable globale, qui n'a pas de
min_value/max_value comme un champ d'objet), et alea (tire un nombre
aléatoire entre deux bornes).
screens/data_actions/compute_operation.py (déjà factorisé en Phase 0,
donc une seule implémentation pour apply_data_action.py/
apply_variable_action.py) : implémente les 6. "alea" est la seule à
deux opérandes — réutilise data_value au format "min,max" plutôt qu'une
nouvelle colonne de nœud (bornes remises dans l'ordre si inversées).
random.uniform pour un résultat décimal, random.randint pour un entier.
static/js/screen_edit/flow-editor.js + templates/screen_edit.html :
petit indice visuel — le champ "Valeur / montant" du formulaire de nœud
affiche "min,max (ex. 1,6)" quand "alea" est choisi, pour ne pas laisser
deviner ce format à deux nombres, différent de toutes les autres
opérations. Sinon aucun nouveau champ/changement de schéma nécessaire,
le <select> était déjà généré depuis DATA_OPERATIONS.
Vérifié : 250 tests passent (19 dans test_compute_operation.py, dont un
qui a dû être corrigé — il utilisait "multiplier" comme exemple
d'opération INCONNUE, devenu un mauvais exemple maintenant qu'elle
existe), 13 tests node:test toujours au vert, syntaxe JS validée.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
1. Duplication éliminée avant que la Phase 2 (hasard/opérations
mathématiques) n'en ajoute 6 de plus aux DEUX fichiers : la chaîne
d'opérations quasi identique entre screens/data_actions/
apply_data_action.py (champ d'objet) et apply_variable_action.py
(variable globale) est factorisée dans un nouveau
compute_operation.py::compute_new_value(operation, current, raw_value,
is_decimal), réutilisé par les deux. Nouveau tests/test_compute_operation.py
verrouille le comportement des 7 opérations existantes (dont les cas
limites : valeur invalide, type décimal vs entier, opération inconnue)
avant d'en ajouter d'autres.
2. .gitea/workflows/deploy.yml déployait en prod à chaque push sur main
sans jamais exécuter la suite de tests — rien ne bloquait
techniquement un commit cassé. Nouveau job "test" (pytest + node:test
sur la logique pure de static/js/play/, via des conteneurs officiels
plutôt que des actions du marketplace, cohérent avec le choix déjà
fait dans ce fichier) tourne sur CHAQUE push (main ET dev, utile pour
ce dépôt qui travaille sur dev) ; "build-and-push"/"deploy" gagnent un
"needs: test" et restent réservés à main (filtre sur gitea.ref) — un
push sur dev ne redéploie jamais la prod, seulement les tests.
Vérifié : 223 tests passent (8 nouveaux), YAML validé.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>