Mise en service de la vigie faite le 23/10 côté Gitea et serveur : compte
dédié `vigie` (restreint, Écriture sur le seul dépôt qualicheck), jeton à
la seule portée « tickets », secrets du dépôt, étiquette `simulation`,
entrée DNS et bloc Caddy du sous-domaine. La procédure de mise en service
le dit désormais.
Chaque ligne des trois tableaux de la page « Santé du service » porte une
pastille « i » dont l'infobulle dit en une phrase ce que mesure le signal
(clé `description` de chaque signal dans config.yml).
Infobulle en CSS seul (pas de JavaScript sur la page, CSP) : ouverte au
survol de la cellule et au focus clavier de la pastille, lue par les
lecteurs d'écran au focus, placée au-dessus de la ligne pour ne pas être
coupée par le défilement horizontal du tableau. Écart assumé : Échap ne
la ferme pas (RGAA 10.13). L'état « Mesure » prend l'icône « – » pour ne
pas répéter le « i ».
Clôture de la carte #61 (vigie, brief 5) après la PR #112 : ligne du
CHANGELOG (contrôle de la page, temps reconstitué, critère des alertes en
état de marche attendu de l'essai réel en staging), entrées C20 et C11,
index des compétences, notes de l'oral (vigie dans le compose, moindre
privilège, Langfuse en panne), burndown et curseur de l'agenda.
Corrections de la revue finale de la vigie (carte #61) :
- le service vigie ne reçoit plus tout le .env (env_file retiré) mais
seulement les variables qu'il lit (moindre privilège) ;
- fichier provisoire de la page nommé d'après le processus (démo et boucle
en parallèle) ;
- textes périmés corrigés (docstrings, feuille de style, .env.example) ;
- limites documentées : Langfuse injoignable, démo sans jeton, retour
arrière et copie de l'override du serveur ;
- tests : bloc de l'override borné, cas du jeton vide.
La page de la vigie a son sous-domaine (DNS + bloc Caddy posé par un script
serveur testé en local). Documentation alignée sur le service docker-compose :
exploitation, déploiement du staging (secrets VIGIE_*, override lu dans le
dépôt), surveillance, fiches de conception (phrases datées du 2026-10-23),
liste des modules de app/CLAUDE.md et entrée du CHANGELOG.
La vigie devient un service du docker-compose (qualicheck-vigie, page sur
8884, boucle toutes les 15 minutes, journal de purge en lecture seule).
L'image copie scripts/vigie.py ; le staging tire l'image de l'API business
pour la vigie et lit l'override dans le dépôt (COMPOSE_FILE). Le déploiement
écrit les secrets VIGIE_*, tire l'image et contrôle la page.
Démo par un workflow manuel (vigie-demo.yml) ; le workflow planifié et son
test sont retirés. make vigie passe par le conteneur.
Serveur HTTP de la bibliothèque standard pour sante.html et sante.css
(503 avant le premier passage, 404 ailleurs, aucun journal de requêtes).
Ajout de boucler et de l'option --boucle. Les alertes ne partent que si
VIGIE_GITEA_TOKEN est posé et hors --a-blanc. Écriture de la page atomique.
L'environnement surveillé est passé en paramètre de lire_traces (APP_ENV, dev par défaut) au lieu de la config. La config perd vigie.environnement, pointe /health sur l'API business par le réseau interne et gagne le bloc page (port 8884, messages absente/introuvable).
Décision du 2026-10-23 : le déploiement lit le fichier de surcharge du
staging dans le dépôt (COMPOSE_FILE), un changement du compose du staging
part avec le code. La copie du serveur reste pour les retours arrière vers
un ancien run. Fiche (sections 10 et 14) et plan de reprise mis à jour.
Plan de reprise en quatre tâches : environnement lu dans APP_ENV, serveur
de la page et boucle, service compose et démo manuelle, script Caddy et
documentation.
Révision de la fiche de la vigie, validée le 2026-10-23 : la vigie devient
un service du docker-compose (même image que les API), sert sa page sur le
port 8884 derrière vigie.qualicheck.koabana.fr, n'envoie d'alertes que si
VIGIE_GITEA_TOKEN est défini, et sa démo passe par un workflow manuel.
Le workflow planifié et le dépôt par scp sont retirés. Schéma mis à jour.
Les sections du guide d'exploitation vont de 1 à 9 dans l'ordre, les renvois internes (retour arrière, limites) suivent. Une ligne trop longue du guide de déploiement est recoupée sans changer les mots.
Mise en service décrite avant la fusion, cycle de la rechute dans la fiche et le guide, montage du dossier de la page à faire à la main, corrections des fiches (citations, jeton refusé, seuil des refus, changement de seuil).
Une panne qui revient avant la fermeture du ticket est signalée par un commentaire « Revenu » et un e-mail. La page dit qu'un passage est simulé. Les booléens de Langfuse en texte sont acceptés, « 1 compte » s'accorde, SMTP_EXPEDITEUR est vérifiée, et les chemins d'échec de passer sont testés.
Ajoute docs/exploitation.md (page d'entrée et une fiche par signal, avec ancre), son test de garde, les seuils décidés (metriques_seuils.md section 6), les renvois dans surveillance.md, app/CLAUDE.md et docs/README.md, l'entrée du CHANGELOG et l'amendement du plan sur intervalle_min.
Le workflow Vigie tourne toutes les 15 minutes (cron lu depuis
intervalle_min de la config) et sur demande avec l'option simulation.
La page est publiée même quand le passage échoue, et le passage tourne
avec uv run --no-project : un test vérifie que la vigie n'importe que
la bibliothèque standard, httpx, yaml, dotenv et app.observability.
Le passage évalue les signaux, écrit la page puis ouvre ou commente les tickets et envoie les e-mails. Démo de 25 questions dont 4 coupées. Cibles make vigie et acceptance-vigie, étape CI.
Le pied de page de la page « Santé du service » disait « toutes les 15
minutes » en dur, alors que la fréquence des passages est fixée par le
cron de la vigie. L'intervalle passe dans la section `vigie` de la
config (`intervalle_min`) et est transmis à `rendre_page`.
Collecte de la vigie : lecture des spans du staging dans Langfuse (API v2,
filtrée par environnement), essais de /health, dernière ligne datée du
journal de la purge, et fichier de traces simulées pour la démo.
Ajoute la page « Santé du service » : rendre_page (fonction pure qui
transforme les états des signaux en HTML statique, sans JavaScript) et
ecrire_page (écrit sante.html et copie sante.css). L'état se lit en icône
et en mot, chaque tableau a sa légende, et la mise en forme est dans
sante.css pour respecter la politique de sécurité (pas de style en ligne).
Gitea.ouvrir lève ValueError si l'étiquette est absente du dépôt, au lieu de créer un ticket sans étiquette que la vigie ne reverrait jamais. La liste des étiquettes demande aussi 50 entrées.
Ajout du client Gitea de la vigie (lecture des tickets ouverts, ouverture, commentaire, adresse du ticket) et de l'envoi SMTP des e-mails d'alerte, avec un faux Gitea en mémoire pour les tests.
Ajoute le module alertes.py : decider() applique les quatre lignes du cycle (ouvrir, retablir, sinon rien), et les textes des tickets, commentaires et e-mails ne portent que des nombres, des états et des heures.
Ajout des dérives (parts, centiles, fidélité, refus d'authentification, latence par groupe) sous un minimum de questions, des mesures sans seuil et de evaluer(), qui enchaîne pannes, dérives puis mesures.
Section vigie de config.yml (fenêtres, seuils, libellés, ancres de la fiche). Nouveau paquet app/observability/vigie : charger_config lit cette section, signaux.pannes évalue les 9 pannes (alerte dès la première occurrence dans les 30 dernières minutes, /health et purge à part). Fonctions pures, sans accès réseau. Tests unitaires des pannes, RED puis GREEN.
lire_metadonnees demande fields=core,basic,metadata et rend, pour chaque span, les clés niveau (niveau du span, ERROR…) et debut (startTime complet). Prépare le script de vigie (carte #61).
Le span questions_libres.juge porte désormais les attributs fidelite et
provenance, lus par la vigie (carte #61) : le Langfuse du staging ne rend
pas les scores par l'API.
Plan en 14 tâches, TDD, pour la vigie de la carte #61.
Fiche complétée (section 13) : le Langfuse du staging (v4, mode
events_only) refuse la lecture des scores par l'API, la fidélité se lit
donc sur le span du juge ; la page /sante a son propre bloc Caddy et sa
feuille de style à part (CSP) ; la latence compte pour deux signaux ; le
workflow lit le journal de la purge et dépose la page ; option --a-blanc
pour l'essai local.
Fiche de conception de la vigie (carte #61, brief 5), validée le
2026-10-23 : vigie maison planifiée par Gitea Actions toutes les
15 minutes, page « Santé du service » statique et publique sur /sante,
9 pannes et 6 dérives, minimum de 20 questions par fenêtre de dérive,
tickets « incident » comme mémoire entre deux passages, démo par traces
simulées. Schéma draw.io joint.
CHANGELOG : la vigie et la fusion de la PR #111.
Les spans de l'auditeur de page portaient questions_libres.appel_llm et questions_libres.appel_outil.*, y compris dans l'audit. Ils portent désormais auditeur_page.* ; l'orchestrateur garde questions_libres.*. Traces et span_outil reçoivent un préfixe obligatoire. make traces-agent et l'acceptance lisent les deux préfixes, pour les traces d'avant le changement. Vérifié sur un audit local dans le Langfuse de dev.
Spec validée le 23/10 : les appels de l'auditeur de page, partagé par la question libre et l'audit, portent le nom de son paquet (auditeur_page.*) au lieu de questions_libres.*.
Une fiche par risque du Top 10 OWASP:2025 dans docs/owasp/web/, sur
le modèle des fiches de l'IA ; index élargi aux trois listes, avec les
correspondances entre risques web et risques de l'IA.
Essai réel fait depuis le client (audit d'une page et d'une règle) ;
relectures orphelines expliquées : rechargement à chaud de Vite
pendant les mutations, disparues au rechargement de la page.
Un arrêt du minuteur inutile retiré (mutation survivante : le premier
minuteur qui sonne annule l'autre), test ajouté sur l'annonce sans
hausse ; 20 mutations sur 20. Écarts de la maquette notés ; essai
réel d'un audit qui se termine en attente d'accord (lancement payant).
D1 (carte #106), tâche 3 : la pastille de l'en-tête est relue après
une suppression (réussie ou déjà faite ailleurs), quand la page
d'avancement voit la fin de l'analyse, et après la validation du
rapport ; sans attendre le minuteur de 60 s.
D1 (carte #106), tâche 2 : nombre d'audits à relire en pilule
d'accent, sinon horloge cerclée pendant une analyse ; état dit par le
nom du lien (texte masqué, pastille aria-hidden) ; annonce polie de la
fin d'une analyse, sauf sur la page d'avancement ; relecture à chaque
changement de page. Scénarios Gherkin de la fiche et axe-core.
D1 (carte #106), tâche 1 : composable useEtatAudits. Nombre d'audits
à relire, audit en cours, relecture de GET /audits (rien sans jeton ;
jeton refusé : état vidé ; panne : état gardé), minuteur de 60 s
pendant une analyse, annonce d'une hausse vue par le minuteur.
Fiche de D1 (carte #106) validée le 22/10 ; plan en 4 tâches :
composable partagé, pastille de l'en-tête, relecture après les
actions du client, vérifications et documentation.
Fiche de la marche D1 (carte #106) : pastille sur « Mes audits »
(nombre d'audits à relire, sinon horloge), relecture de GET /audits
au changement de page, après une action et toutes les 60 s pendant
une analyse ; annonce aux lecteurs d'écran. Client seulement, contrat
inchangé. Schéma dérivé de celui de C4. Statut : à valider.
Deux mutations survivantes de C4 attrapées (ordre des pages par
identifiant, date du nom du fichier) ; écarts à l'exécution notés dans
le plan ; app/CLAUDE.md et CHANGELOG à jour.
Rapport validé (A18, C4, carte #105) : boutons « Télécharger le tableur
(ODS) » et « (XLSX) » sous le bandeau, et leur phrase d'aide. Le fichier
est demandé avec le jeton en en-tête (lecture binaire dans api.js), son
nom lu dans Content-Disposition, puis enregistré par un lien temporaire.
Boutons inactifs pendant l'appel (aria-disabled : le focus reste),
pannes sous les boutons.
GET /audits/{id}/export?format=ods|xlsx (C4, carte #105) : 200 le
fichier et son nom (Content-Disposition), 404 hors du compte, 409 pour
un rapport pas validé, 422 pour un format inconnu, 503 si la base ou
l'API des règles ne répond pas. Content-Disposition exposé par le CORS.
Contrat 0.30.0 -> 0.31.0, miroir complété à la main (chemin identique
au schéma servi). Essai d'ouverture dans LibreOffice, sauté sans
LibreOffice.
Module pur app/audit/export.py (C4, carte #105) : contrôle du statut,
une ligne par constat retenu dans l'ordre des écrans, écriture ODS
(odfpy) et XLSX (openpyxl), toute cellule de texte écrite en texte
(injection de formule), numéro de règle en nombre, caractères de
contrôle retirés, nom du fichier en ASCII. Réglages et message dans
config.yml ; dépendances openpyxl et odfpy.
Fiche de C4 (carte #105) passée en « validée » le 22/10 ; plan d'implémentation en 4 tâches (module d'export, route et contrat, client, vérifications et documentation).
Fiche de C4 (carte #105) : GET /audits/{id}/export?format=ods|xlsx,
une ligne par constat retenu, fichiers fabriqués par openpyxl et odfpy,
toute cellule écrite en texte (injection de formule), deux boutons sur
A18, téléchargement avec le jeton en en-tête et Content-Disposition
exposé par le CORS. Contrat 0.30.0 -> 0.31.0, sans migration. Schéma
dérivé de celui de C3, le nouveau en orange.
Une fiche par risque des deux listes de l'OWASP GenAI Security Project :
docs/owasp/llm/ (Top 10 pour les applications LLM, édition 2026) et
docs/owasp/asi/ (Top 10 pour les applications agentiques, édition 2026).
Fiches de cours, sans lien avec le code du projet. Index avec les
classements, l'évolution depuis 2025, les correspondances entre les deux
listes et les sources.
Correctif CORS poussé avec la PR #108 ; temps ajouté à la carte #103 ;
point faible ajouté aux notes de l'oral (le même défaut de CORS deux
fois, et l'essai réel sur l'API reconstruite comme vérification).
Valider, corriger ou écarter un constat (C2, carte #103) passe par un
PATCH ; la requête préliminaire du navigateur était refusée (400
« Disallowed CORS method ») et le client affichait « Votre décision n'a
pas été enregistrée ». PATCH ajouté aux méthodes autorisées, test
d'abord, comme pour le DELETE du 14/10.
Clôture de C3 (carte #104) après la PR #107 : entrée du CHANGELOG et
décision ajoutée aux notes de l'oral (synthèse calculée dans le client,
confirmation hors maquette, limite assumée, leçon du commit invisible
dans la même session).
C3 (carte #104) : entrée du CHANGELOG, écarts à l'exécution du plan,
lignes A15, A16 et A18 de maquettes_integrees.md. Trois tests renforcés
après les mutations survivantes : statut relu par une connexion à part
(le commit de la route), double clic avant la mise à jour de l'écran,
focus sur une carte quand le catalogue arrive après.
C3 (carte #104), côté écrans :
- vue d'ensemble (A15) : « Valider le rapport » en bas, jamais grisé ;
« Il reste N constats à traiter. » sans appel (RM11), sinon une
confirmation sur place comme la suppression d'un audit (focus sur
« Non », Échap), puis POST ; pannes 409, 404, 503, jeton refusé ;
- rapport validé (A18), même adresse : bandeau, synthèse par thème en
tableau accessible qui défile seul à 375 px, constats retenus en
cartes sans crayon, retour à Mes audits ; focus sur « Rapport » ;
- page d'une règle d'un rapport validé : « Constats d'une règle »,
lecture seule, retour « Le rapport » ;
- Mes audits : la ligne d'un rapport validé devient un lien ;
- indicateur d'étapes : 6 = toutes faites.
Scénarios Gherkin de la fiche sur les écrans, axe-core sur chaque état,
captures clair, sombre et 375 px comparées à A15 et A18.
C3 (carte #104), logique pure du client : src/services/rapport.js
(constats retenus, cartes et synthèse par thème, textes du bandeau, des
cartes et des thèmes, phrase « Il reste N constats à traiter. ») ;
validerRapport (POST /audits/{id}/validation) ; formaterJour ;
phraseRegle en lecture seule (« N retenus »). Fixture d'audit du contrat
complétée par date_validation (contrat 0.30.0).
C3 (carte #104), côté API : POST /audits/{audit_id}/validation passe un
audit « à relire » sans constat à traiter en « rapport validé », avec sa
date. Contrôle pur dans app/audit/validation.py : 409 pour une analyse pas
finie, un rapport déjà validé ou des constats à traiter (« Il reste N
constats à traiter. », RM11). GET /audits/{id} rend date_validation.
Contrat 0.30.0, miroir de conception patché par blocs. Sans migration.
Tests : unitaires du contrôle et de la route (refus, 503, span),
intégration sur la base de test (statut et date écrits, PATCH refusé
après validation, 404, 401, requête de la mesure du délai).
Fiche de la marche C3 (carte #104) : POST /audits/{id}/validation,
confirmation sur place, rapport validé (A18) avec synthèse calculée dans
le client, page d'une règle en lecture seule. Schéma repris de C2, le
nouveau en bordure orange. Statut : proposée, à valider.
Clôture de C1 et C2 (cartes #102 et #103) après la PR #106 : entrée
du CHANGELOG et décision ajoutée aux notes de l'oral (valider ou
écarter efface la correction, le verdict de l'agent n'est jamais
écrasé, pas de retour à « à traiter »).
Entrée du CHANGELOG pour C2 (#103) ; écarts à l'exécution notés dans
le plan. Le test du passage à la règle suivante part du lien qui a le
focus : la mutation qui retirait le focus sur le titre survivait.
C2 (carte #103), écrans A16 et A17 : la page d'une règle
(/mes-audits/{id}/regles/{numero}) montre ses constats, un par page,
avec le verdict et le commentaire retenus, leur état et les actions
permises (valider, corriger, écarter) ; la correction s'ouvre sur
place (verdict, commentaire obligatoire, Échap pour annuler).
Annonce polie et focus gardé après chaque décision, un seul appel à
la fois, pannes affichées sous le constat ; règle précédente et
suivante. Dans la vue d'ensemble, la carte devient un lien avec le
crayon et reprend le focus au retour. Icône arrow-left ajoutée.
C2 (carte #103), logique pure du client : actions permises (RM10),
verdict et commentaire retenus, ligne d'état, libellé d'une page,
constats d'une règle dans l'ordre des pages, phrase de la règle,
règle précédente et suivante. La vue d'ensemble compte désormais les
verdicts retenus. traiterConstat appelle PATCH /audits/{id}/constats/{id}
et garde les messages de l'API pour 409 et 422.
C2 (carte #103), côté serveur : PATCH /audits/{id}/constats/{id}
enregistre la décision de l'auditeur (RM10). app/audit/traitement.py
porte les passages permis et le contrôle d'une demande : 409 hors
relecture, 422 pour un passage impossible ou une correction sans
verdict ou sans commentaire (2 000 caractères au plus, en config).
Valider ou écarter efface la correction ; le verdict de l'agent est
gardé. GET /audits/{id} rend verdict_auditeur, commentaire_auditeur et
date_traitement. Contrat 0.28.0 -> 0.29.0, sans migration. Le test
d'intégration joue la requête de la mesure lue dans la fiche.
Plan d'implémentation joint.
Fiche de la marche C2 (#103), validée le 22/10 : route PATCH des
constats, contrat 0.29.0 sans migration, page d'une règle (A16) et
correction sur place (A17), requête de la mesure. Schéma joint.
C1 (carte #102), tâche 2 du plan : vue d'ensemble (A15) sur /mes-audits/{id} dès que l'audit n'est plus en cours, bouton « Relire les constats » quand l'analyse finit sous les yeux, ligne « À relire » de Mes audits en lien. Résumé d'un thème calculé sur le thème entier. Tests d'abord (échec initial constaté), 16 mutations attrapées, captures clair, sombre et 375 px ; suivi des maquettes et CHANGELOG.
C1 (carte #102), tâche 1 du plan : logique pure de la vue d'ensemble (regroupement dans l'ordre du catalogue, verdicts comptés dans un ordre fixe, filtre, textes accordés), carte d'une règle sans lien avant C2, thème repliable ouvert au départ. Icônes dash-lg et question-lg ; couleurs du verdict non applicable lues dans Figma (contraste 4,78:1). Tests d'abord (25, échec initial constaté : modules absents).
Fiche de la carte #102, validée le 22/10 : vue d'ensemble des constats (A15) sur /mes-audits/{id} dès que l'audit est à relire, client seulement, contrat inchangé (0.28.0). Cartes sans lien jusqu'à C2, filtre « Règles à traiter » coché à l'ouverture, thèmes dépliés. Schéma repris de B6, le neuf en bordure orange.
TODO : les six secrets du relais sont dans Gitea, authentification vérifiée sans envoi ; reste un e-mail reçu sur une vraie boîte au prochain déploiement.
Clôture de B6 (carte #101) après la PR #105 : entrée du CHANGELOG et
décision ajoutée aux notes de l'oral (annonce de l'avancement par
quart, nombre de vérifications calculé par le client et vérifié contre
la règle du serveur, adresse du compte nommée sans être affichée).
Étape 4 de « Préparer un audit » (A13, carte #101) : récapitulatif avec
« Modifier », nombre de vérifications calculé avec la portée des règles
(RM8), bloc « Être prévenu » (ajouter, retirer des adresses), lancement
par POST /audits avec les refus de l'API affichés tels quels.
Avancement (A14) sur /mes-audits/:id : relu toutes les 5 secondes,
barre native, annonce aux lecteurs d'écran à chaque quart et à la fin.
« Mes audits » mène à l'avancement d'un audit en cours. Ligne Caddy de
la documentation du déploiement complétée. Contrat inchangé.
Fiche de B6 (carte #101), validée le 2026-10-21 : récapitulatif de
l'étape 4 (A13), nombre de vérifications calculé avec la portée (RM8),
bloc « Être prévenu », lancement, page de l'avancement (A14) relue
toutes les 5 secondes et annoncée par quart. Client seulement, contrat
inchangé. Schéma tiré de celui de D2, plan d'implémentation.
make comptes ARGS=lister : une ligne par compte, dans l'ordre de
création (identité, e-mail, actif ou désactivé, fin de validité du
jeton le plus durable ou « aucun jeton valide »). Lecture seule,
jamais d'empreinte. Tests d'abord.
Clôture de D2 (carte #107) après la PR #104 : entrée du CHANGELOG et
décision ajoutée aux notes de l'oral (adresses ajoutées effacées même en
cas d'échec, aucune adresse tracée).
D2 (carte #107), côté serveur : POST /audits accepte adresses_notification
(facultatif, 5 au plus, contrôlées au lancement : adresse_invalide,
adresse_en_double, trop_d_adresses) ; quand l'audit passe à relire, un
e-mail en texte brut part au compte et à chaque adresse ajoutée, un par
destinataire, par smtplib (trois tentatives pour une panne passagère,
aucune pour un refus), puis les adresses ajoutées sont effacées. Un échec
d'envoi ne change rien à l'audit ; span audit.notification sans aucune
adresse. En-têtes Date et Message-ID. Contrat 0.27.0 -> 0.28.0.
D2 (carte #107) : service Mailpit (profil dev, démarré par make up,
jamais en staging) qui reçoit les e-mails sans les transmettre ;
variables SMTP de l'API (Mailpit par défaut en local), écrites dans le
.env du staging depuis les secrets Gitea ; .env.example, procédure et
secrets documentés. Décision 10 de la fiche amendée : le traitement de
la notification va dans le volet anticipé du registre interne, publié
avec l'audit à sa mise en ligne (TODO.md).
Fiche D2 (carte #107) validée le 21/10 : e-mail au compte et aux
adresses ajoutées quand l'audit passe à relire, adresses effacées dès
l'envoi, relais SMTP de la messagerie du domaine, Mailpit en local,
registre des traitements complété ; schéma et plan d'implémentation.
Clôture de B5 (carte #100) après la PR #103 : entrée du CHANGELOG,
décision ajoutée aux notes de l'oral (règles non auditables montrées,
nombres jamais en dur) et couleur des cases à cocher notée dans TODO.md.
B5 (carte #100), côté client : l'étape 3 de « Préparer un audit »
(maquettes A11 et A12). Thèmes repliés avec une case à trois états,
règles auditables à cocher ligne entière, les autres listées sans case,
raccourcis, compteur annoncé, nombres comptés sur la réponse de
GET /audits/regles. Catalogue lu une fois, « Réessayer » en cas d'échec,
choix gardé en allers et retours. « Continuer » mène à l'étape 4, vide
jusqu'à B6.
B5 (carte #100), côté API : GET /audits/regles rend les thèmes du
référentiel et leurs règles, auditables ou non, avec leur portée, lus
dans l'API des règles par le client HTTP des outils. Thèmes dans l'ordre
alphabétique français (accents et casse ignorés), règles par numéro ;
un plan d'outils vide n'est pas auditable, comme au lancement. 503
regles_indisponibles si l'API des règles ne répond pas ; rien n'est
écrit en base. Contrat 0.26.0 -> 0.27.0.
Fiche B5 (carte #100) validée le 21/10 : GET /audits/regles (thèmes et
règles, auditables ou non, avec leur portée), étape 3 du client (thèmes
dépliables, case à trois états, raccourcis, compteur), schéma et plan
d'implémentation.
Clôture de B4 (carte #99) après la PR #102 : entrée du CHANGELOG,
décision ajoutée aux notes de l'oral (page ajoutée vérifiée en direct,
en 2xx) et contenu périmé des pages statiques noté dans TODO.md pour
plus tard.
B4, carte #99 : l'étape 2 de « Préparer un audit » devient interactive.
Une case par page trouvée, toute la ligne cliquable ; compteur annoncé ;
limite de N pages lue dans la réponse de la détection, la case de trop ne
se coche pas et n'est jamais désactivée. Ajout d'une page à la main :
contrôles du client d'abord (aucune requête s'ils échouent), puis
vérification en direct par l'API ; la page s'affiche à son adresse finale
avec son titre. « Continuer » mène à l'étape 3, encore vide ; le choix est
gardé au retour. Sept scénarios Gherkin d'écran, axe-core sur chaque état.
B4, carte #99 : la réponse de POST /audits/pages porte pages_max (RM6, lu
en configuration). POST /audits/pages/verification contrôle la déclaration
et les deux adresses (RM2, RM3), puis lit la page par la lecture protégée :
acceptée si elle répond 2xx sur le même nom d'hôte que l'adresse de départ,
avec son adresse finale et son titre nettoyé. Refus 422 url_refusee,
autre_site ou page_illisible ; une redirection vers un autre site est
reconnue par les refus notés par page_web. Contrat 0.26.0, quatre
scénarios Gherkin.
Fiche de la carte #99 (étape 2 de la préparation d'un audit), validée le
2026-10-21 : cases, compteur, limite de N pages lue dans la réponse de la
détection (pages_max), ajout d'une page à la main vérifiée en direct
(POST /audits/pages/verification, 2xx), étape 3 vide. Schéma et plan
d'implémentation.
Clôture de B3 (carte #98) après la PR #101 : entrée du CHANGELOG et
décision ajoutée aux notes de l'oral (liens vérifiés, étiquettes
traitées comme des données non sûres).
Amendement de B3 (carte #98, décisions 13 à 19) : case décochée
« Chercher aussi les pages dans le plan du site (sitemap.xml) » à
l'étape 1, envoyée à POST /audits/pages ; pages trouvées en deux
groupes (accueil, plan du site) ; message quand aucun plan n'a pu être
lu ; avertissement de A10 seulement si rien d'autre que l'accueil.
Une page sans titre affiche son chemin en texte courant.
Amendement de B3 (carte #98, décisions 13 à 19) : avec plan_du_site, la
détection lit robots.txt puis les plans déclarés, sinon /sitemap.xml,
un niveau d'index, 5 documents au plus. Les adresses du plan passent
les filtres des liens de l'accueil et la même vérification, sous le
plafond commun de 50. Chaque page porte sa source ; un plan absent ou
mal formé n'est pas une erreur (plan_du_site : introuvable). XML lu par
le module standard (entités externes et expansion abusive refusées).
Origine « accueil » redéfinie, sans migration. 7e scénario Gherkin.
Amendement validé le 2026-10-21 (décisions 13 à 19) : case décochée
pour chercher aussi les pages dans le plan du site (robots.txt puis
sitemap.xml, un niveau d'index), mêmes filtres et vérification que les
liens, plafond commun, origine « accueil » redéfinie, deux groupes à
l'affichage. RM4 de la spec et schéma mis à jour.
CHANGELOG de la réalisation de B3 (carte #98), suivi des maquettes
intégrées (A04 à A06, A07 et A10 en partie), ligne Caddy du serveur à
mettre à jour au prochain déploiement (TODO).
B3 (carte #98), côté client :
- barre « Adresse du site à auditer » + « Préparer l'audit » dans Mes
audits, sans audit en cours (A01, A02) ;
- écran « Préparer un audit » (/mes-audits/nouveau, réservé) avec
l'indicateur d'étapes : adresse et déclaration (A04, A05), détection
annulable (A06, AbortController), pages trouvées en lecture seule,
avertissement quand seul l'accueil est proposé, liens non vérifiés ;
- titres de pages affichés en texte, jamais en HTML (test avec une
étiquette <img onerror>, vérifié en cassant le code) ;
- « Mes audits » reste marqué dans l'en-tête sur ses sous-pages
(aria-current=true) ; libellé et message de la déclaration partagés
avec la question libre ; route ajoutée à la ligne Caddy documentée.
B3 (carte #98), côté API : la route lit l'accueil du site déclaré
(lecture protégée de la question libre) et propose les pages qu'il
relie, après la déclaration (RM2) et le contrôle de l'adresse (RM3).
- chaque lien est lu (5 à la fois, 50 au plus) et gardé seulement s'il
répond 2xx en HTML sur le même nom d'hôte, à son adresse finale ;
- fichiers écartés par l'extension puis par le type ; adresses à secret
ou donnée personnelle écartées avant lecture ;
- étiquettes nettoyées à la source (texte brut, Cc/Cf retirés, 255) ;
- 422 url_refusee ou site_illisible avec un message pour l'auditeur.
Contrat 0.25.0. Six scénarios Gherkin en intégration, faux site.
Fiche de B3 (carte #98) validée le 2026-10-21 : POST /audits/pages lit
l'accueil du site déclaré et propose les pages qu'il relie, chaque lien
vérifié (2xx et HTML), fichiers écartés, étiquettes nettoyées. Schéma
B3_site_et_pages.drawio et plan d'implémentation.
La section 1 de docs/developpement/couverture.md donnait encore le seuil
du 2026-10-05 (94 %) ; pyproject.toml est à 95 % depuis le 2026-10-18
(97 % mesurés en CI sur la PR #91). Vérifications du 2026-10-05 gardées,
avec le seuil d'alors.