Six appels à `update(QDateTime::currentDateTime())` manquaient la garde ENERGY_SIMULATION que leurs voisins ont (l.124, l.288) : setPhasePowerLimit, setAcquisitionTolerance, et trois dans onActionExecuted — qui partent sur toute action manuelle appliquée à une borne. Un cycle d'arbitrage complet partait donc à l'horloge MURALE au milieu d'une suite qui injecte un temps simulé. Les deux horloges se mélangeaient dans les verrous : l'armement paresseux (ECS-412) figeait m_lastSwitch sur l'heure qu'il était, et un scénario daté en dur se déroulait « avant » son propre armement — elapsed négatif, verrou jamais ouvert. Ce n'est pas un défaut de production : il n'y a là-bas qu'une seule horloge. C'est une faille de REPRODUCTIBILITÉ, et elle touchait tout test de simulation réglant une limite de phase, une tolérance d'acquisition, ou exécutant une action manuelle sur une borne. PREUVE DU CORRECTIF : la garde rendue, testL4ShedsAPacAndSaysItForcedTheLock repasse avec une DATE FIXE, trois fois sur trois — ce qu'il ne pouvait pas faire. L'ancrage sur l'horloge réelle, posé la veille faute d'avoir la cause, était un contournement ; il est retiré, et la date fixe revient parce qu'elle rend les journaux reproductibles. tools/check-simulation-clock.py, câblé en 4/4 de ci-quality.sh, et vérifié mordant : garde retirée, il nomme la ligne exacte. Il a trouvé trois des six déclencheurs dès son premier passage. Pourquoi une vérification STATIQUE et pas un test : la suite ne peut pas dire d'elle-même que son verdict dépend de l'heure, puisque c'est le verdict qui ment. Le défaut n'a été trouvé que parce qu'il échouait cinq fois sur cinq à code identique — une fenêtre de nuisance plus étroite ne basculerait qu'une fois sur vingt, passerait pour une fragilité de temporisation, et serait relancée jusqu'à ce qu'elle passe. C'est la famille de la semaine sous une forme de plus : après l'instrument dont le silence ressemble à un résultat, l'instrument dont le VERDICT ment selon l'heure. Suite complète : simulation 54/54, charging 48/48 identique à la référence ligne à ligne, loadmodel 21/21, spotmarket 32/32, doxygen 0 avertissement. Aucun test ne dépendait de ces cycles. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015F7G5VeaPVSMeVNjiGj36p
125 lines
6.0 KiB
Bash
Executable File
125 lines
6.0 KiB
Bash
Executable File
#!/bin/bash
|
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
# Copyright (C) 2025 - 2026, Patrick Schurig / ETM PowerSync
|
|
#
|
|
# Contrôle de qualité documentaire — TROIS vérifications, un seul endroit.
|
|
#
|
|
# ./tools/ci-quality.sh
|
|
#
|
|
# Elles se posent au même endroit parce qu'elles partagent la même sortie
|
|
# (`doc-generated/`) et la même commande de réparation. Les séparer ferait trois
|
|
# jobs qui regénèrent trois fois la même chose.
|
|
#
|
|
# 1. L'INDEX DES RÈGLES est-il VRAI ? (tools/gen-rules.py, code de retour)
|
|
# Casse sur : un identifiant cité et jamais défini ; un identifiant qui n'existe
|
|
# qu'en ligne de tableau ; un domicile ambigu ; un numéro retiré réattribué.
|
|
# 2. La DOC DOXYGEN est-elle complète ? (0 avertissement — DoD §5 d'AGENTS.md)
|
|
# 3. Le tout est-il REPRODUCTIBLE ? (deux passes donnent le même index)
|
|
#
|
|
# ── CE QU'ON NE VÉRIFIE PAS, ET POURQUOI ────────────────────────────────────
|
|
#
|
|
# « Régénérer et refuser si ça diffère de ce qui est commité » n'a rien à comparer :
|
|
# `doc-generated/` est dans .gitignore, décision du 2026-08-29 — versionner une page
|
|
# générée produirait un diff à chaque édition de spec et des conflits sur un fichier
|
|
# que personne ne relit. Le risque qu'un tel diff attrape — un artefact commité qui
|
|
# dérive de sa source — n'existe pas quand l'artefact n'est pas commité : il a été
|
|
# SUPPRIMÉ, pas déplacé. Ce qui reste à garantir est que l'index soit VRAI, et c'est
|
|
# la vérification 1. La 3 couvre le reste : un générateur non déterministe.
|
|
#
|
|
# ── ÉTAT : ÉCRIT, PAS EN SERVICE ────────────────────────────────────────────
|
|
#
|
|
# Aucun déclencheur automatique n'est posé. `ci/gitea-workflow.yml.disabled` porte le
|
|
# workflow prêt à l'emploi et la marche à suivre pour l'activer. La raison est datée :
|
|
# au 2026-08-29 la forge ne répond pas et plus de trente commits attendent en local.
|
|
# Un job activé à l'aveugle échouerait à son premier passage — au moment précis où
|
|
# tout le monde pousse — et le premier réflexe serait de le désactiver. Un garde-fou
|
|
# désactivé une fois ne se réactive jamais.
|
|
#
|
|
# Il PASSE sur l'état du dépôt au 2026-08-29 : index RC=0 (113 règles), doxygen 0
|
|
# avertissement. Vérifié avant d'être déclaré prêt, parce qu'un job qu'on n'a jamais
|
|
# vu passer n'est pas un job, c'est une intention.
|
|
|
|
set -u
|
|
|
|
RACINE="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
cd "$RACINE" || exit 1
|
|
|
|
ROUGE=""; VERT=""; GRAS=""; FIN=""
|
|
if [ -t 1 ]; then ROUGE=$'\e[31m'; VERT=$'\e[32m'; GRAS=$'\e[1m'; FIN=$'\e[0m'; fi
|
|
|
|
echec=0
|
|
titre() { printf '\n%s── %s %s\n' "$GRAS" "$1" "$FIN"; }
|
|
ko() { printf '%s✗ %s%s\n' "$ROUGE" "$1" "$FIN"; echec=1; }
|
|
ok() { printf '%s✓ %s%s\n' "$VERT" "$1" "$FIN"; }
|
|
|
|
# Toute panne doit dire QUOI LANCER. Un job qui annonce « l'index est périmé » sans
|
|
# donner la commande se contourne en le désactivant — c'est le chemin de moindre
|
|
# effort, et il gagne toujours.
|
|
reparer() { printf ' → réparer : %s\n' "$1"; }
|
|
|
|
# --- 1. L'index des règles est-il vrai ? ------------------------------------
|
|
titre "1/4 · index des règles"
|
|
if python3 tools/gen-rules.py; then
|
|
ok "index complet — aucun orphelin, aucun domicile ambigu"
|
|
else
|
|
ko "l'index des règles est INCOMPLET (détail ci-dessus)"
|
|
reparer "définir la règle manquante dans specs/, ou la déclarer dans tools/rules-retired.txt"
|
|
reparer "puis : python3 tools/gen-rules.py"
|
|
fi
|
|
|
|
# --- 2. La doc est-elle complète ? ------------------------------------------
|
|
titre "2/4 · documentation Doxygen"
|
|
if ! command -v doxygen >/dev/null 2>&1 || ! command -v dot >/dev/null 2>&1; then
|
|
ko "doxygen ou graphviz absent — la vérification n'a PAS eu lieu"
|
|
reparer "apt install doxygen graphviz"
|
|
else
|
|
./tools/gen-doc.sh >/dev/null 2>&1
|
|
JOURNAL="$RACINE/doc-generated/warnings.txt"
|
|
# `wc -l` et non `grep -c` : grep -c AFFICHE 0 puis SORT EN 1 quand il ne trouve rien,
|
|
# si bien qu'un « || echo 0 » empile un second zéro et que le test entier échoue sur un
|
|
# dépôt SAIN. Une commande de mesure dont l'échec produit une lecture fausse — le motif
|
|
# de la semaine, appliqué à l'outil (AGENTS.md, « Lire le journal de la box »).
|
|
N=0
|
|
[ -f "$JOURNAL" ] && N="$(grep -i "warning" "$JOURNAL" 2>/dev/null | wc -l)"
|
|
if [ "$N" -eq 0 ]; then
|
|
ok "0 avertissement"
|
|
else
|
|
ko "$N avertissement(s) Doxygen — DoD §5 : toute méthode publique d'etm/ se documente"
|
|
printf '\n'; sed 's|^.*/energyplugin/| |' "$JOURNAL" | head -20
|
|
reparer "./tools/gen-doc.sh puis lire doc-generated/warnings.txt"
|
|
fi
|
|
fi
|
|
|
|
# --- 3. Le générateur est-il déterministe ? ---------------------------------
|
|
titre "3/4 · reproductibilité de l'index"
|
|
A="$(mktemp)"; B="$(mktemp)"
|
|
python3 tools/gen-rules.py >/dev/null 2>&1; cp doc-generated/RULES.md "$A"
|
|
python3 tools/gen-rules.py >/dev/null 2>&1; cp doc-generated/RULES.md "$B"
|
|
if diff -q "$A" "$B" >/dev/null; then
|
|
ok "deux passes, un seul résultat"
|
|
else
|
|
ko "l'index n'est PAS reproductible — deux passes ont divergé"
|
|
printf '\n'; diff "$A" "$B" | head -20
|
|
reparer "un ordre d'itération dépend d'un QHash ou du système de fichiers : trier"
|
|
fi
|
|
rm -f "$A" "$B"
|
|
|
|
# --- 4. Un verdict de test peut-il dépendre de l'HEURE ? ---------------------
|
|
titre "4/4 · horloge des déclencheurs"
|
|
if sortie="$(python3 tools/check-simulation-clock.py 2>&1)"; then
|
|
ok "${sortie#✓ }"
|
|
else
|
|
ko "un déclencheur d'arbitrage tourne à l'horloge murale sans garde"
|
|
printf '\n'; printf '%s\n' "$sortie" | sed '1d;s/^/ /'
|
|
reparer "entourer l'appel de #ifndef ENERGY_SIMULATION, comme ses voisins"
|
|
fi
|
|
|
|
printf '\n'
|
|
if [ "$echec" -eq 0 ]; then
|
|
printf '%s✓ qualité documentaire : tout passe.%s\n' "$VERT" "$FIN"
|
|
else
|
|
printf '%s✗ qualité documentaire : au moins une vérification a échoué.%s\n' "$ROUGE" "$FIN"
|
|
printf ' Tout relancer d'"'"'un coup : ./tools/ci-quality.sh\n'
|
|
fi
|
|
exit "$echec"
|