Spaces:
Sleeping
Plan de remédiation — Picarones vers le niveau BnF / British Library
Réponse opérationnelle à
institutional-readiness-2026-05.md(13 BLOCKERS, 28 MAJORS, 18 MINORS, 1 faux positif). Cible : 0 BLOCKER, 0 MAJOR ouvert ; CITATION valide ; audit RGAA AA passé ;pip install picaronesfonctionnel ; lock file utilisé en prod.15 sprints, 8 phases, ~58 PJ, ~12 semaines en 1 ETP. Avec 2 ETP et la parallélisation décrite §2 : ~7–8 semaines.
1. Principes directeurs
Six principes structurent le séquençage. Chacun est tenu sur toute la durée du plan, pas seulement à un sprint isolé.
Scaffolding avant contenu. Les garde-fous CI (Phase 0) sont posés en premier pour que les sprints suivants ne puissent pas régresser sur ce qu'on vient de corriger. Sans cela on déboguerait des régressions au lieu d'avancer.
Tests avant fixes. Chaque correctif d'audit s'accompagne d'un test de non-régression dans le même PR. Un fix sans test est une dette qui se rouvrira au sprint suivant.
DRY entre code et documentation. Une assertion vérifiable (compteur de tests, liste des moteurs, liste des commandes CLI) doit être générée depuis le code, pas dupliquée à la main. Le sprint A2 pose ce principe pour le README et l'audit.
Source unique de vérité. À chaque divergence entre deux documents (CLAUDE.md vs README vs SPECS), on désigne le canon et on déprécie le reste. Pas de patch parallèle.
Refonte documentation produit en dernier. Le README et SPECS reflètent un état du code. On ne refait pas ces documents tant que les phases 1–6 ne sont pas stabilisées, sinon on les refait deux fois.
Parallélisation contrôlée. Les phases 3 (accessibilité) et 4 (reproductibilité opérationnelle) touchent des fichiers disjoints : templates HTML / CSS / JS d'un côté, Dockerfile / pyproject / workflows GitHub de l'autre. Elles peuvent tourner en parallèle si l'équipe a deux ETP. Avec un seul ETP, séquentiel.
2. Vue d'ensemble — 15 sprints en 8 phases
Tableau récapitulatif
| Phase | Sprint | Thème | PJ | Sem. (1 ETP) | Items audit |
|---|---|---|---|---|---|
| 0 | A1 | Hardening CI | 4 | 1 | B-7, B-8, M-4, M-15, m-7, m-8, m-9 |
| 0 | A2 | Tests de cohérence documentation | 3 | 2 | (préparation A13, m-12) |
| 1 | A3 | Refactor cercles + importers | 3 | 3 | B-1, B-2, B-3, m-17 |
| 2 | A4 | Sécurité web (CSRF, /health) | 3 | 4 | B-11, M-3 |
| 2 | A5 | Concurrence et performance | 5 | 4–5 | M-13, M-14, M-16, m-10 |
| 3 | A6 | WCAG niveau A (bloquant) | 3 | 5–6 | B-9, B-10, m-3, m-4 |
| 3 | A7 | WCAG AA + i18n résiduel + a11y statement | 3 | 6 | m-1, m-2, m-5, m-6, M-9 |
| 4 | A8 | Reproductibilité opérationnelle | 3 | 5–6 (parallèle phase 3) | M-1, M-2, M-12, M-18, m-11, m-13, m-14 |
| 4 | A9 | Distribution PyPI + ghcr.io + releases | 3 | 7 | M-5, M-6, m-15, m-16 |
| 5 | A10 | Politiques de gouvernance | 2 | 8 | M-10, M-11 |
| 5 | A11 | Documentation institutionnelle | 5 | 8–9 | M-7, M-8, M-17 |
| 6 | A12 | Publication scientifique | 5 (+ peer review externe) | 9–10 | B-4, B-5, B-6 |
| 7 | A13 | Refonte README | 4 | 10–11 | B-13, M-19 à M-28, m-18, §9.3 |
| 7 | A14 | Refonte SPECS.md | 3 | 11 | B-12 |
| 8 | A15 | Audits externes (RGAA + sécurité) | 1 (+ cycle externe) | 11–12 | validation finale |
| 4* | A16 | Build Docker reproductible (digest + lock file) | 1 | (post-A14) | M-2 (clôture) |
| TOTAL | ~59 PJ | ~12 sem. (1 ETP) | 60 items |
Note : Sprint A16 a été ajouté après l'exécution d'A14 pour clôturer M-2 bout-en-bout (digest sha256 sur les deux ARG + lock file
requirements-docker.lockconsommé par le Dockerfile). La dette résiduelleapt-getnon figé est tracée comme nouvel item M-29 dansinstitutional-readiness-2026-05.md(différé post-v1.2).
Diagramme de Gantt synthétique
Sem. 1 2 3 4 5 6 7 8 9 10 11 12
Phase 0 ████░░ ← CI hardening + doc consistency tests (gates)
Phase 1 ███ ← refactor cercles + importers
Phase 2 █████ ← web sécurité + concurrence/perf
Phase 3 ██████ ← a11y niveau A puis AA
Phase 4 █████ ← reproductibilité ops + distribution (parallèle)
Phase 5 ████ ← gouvernance + doc institutionnelle
Phase 6 █████ ← CITATION + JOSS draft
════════════════════ ← peer review externe (calendrier propre)
Phase 7 █████ ← refonte README + SPECS
Phase 8 ███ ← audits externes
════ ← audit RGAA externe (calendrier propre)
Légende : █ = sprint actif (1 ETP) ; ═ = calendrier externe en parallèle.
Dépendances dures
- A1 doit précéder tout : sans seuil de couverture, scanners de sécurité et timeout pytest, les sprints suivants ne peuvent pas être validés en CI.
- A2 doit précéder A13/A14 : les tests de cohérence documentation posés en A2 deviennent les gates qui valident la refonte README/SPECS.
- A3 doit précéder A12 : les violations Cercle 2→3 doivent être réparées avant que SPECS et le papier JOSS décrivent l'architecture.
- A8 doit précéder A12 : le snapshot de reproductibilité doit être documenté avant qu'un papier puisse promettre la reproductibilité.
- A9 doit précéder A12 : un papier qui pointe vers
pip install picaronesexige que ça fonctionne. - Phases 1–6 doivent précéder Phase 7 : on ne refait pas le README tant que le code n'est pas stable.
Dépendances souples (parallélisables avec 2 ETP)
- A6+A7 (a11y) ⫽ A8+A9 (ops/distribution) — fichiers disjoints.
- A11 (doc institutionnelle) peut commencer dès la fin de A8.
- A12 démarre dès que A3+A8+A9 sont clos ; sa rédaction peut chevaucher A13/A14.
3. Phase 0 — Garde-fous (sem. 1–2)
Objectif : aucun sprint ultérieur ne peut faire régresser ce qu'on a corrigé, parce que la CI le détecte au PR. Sans cette phase, chaque correctif est susceptible de se rouvrir trois sprints plus tard.
Sprint A1 — Hardening CI (4 PJ)
Pourquoi à cette position dans la séquence
Les phases suivantes corrigent des dizaines de fichiers. Sans seuil de couverture, sans scanners de sécurité, sans type-check, sans timeout pytest et sans validation pre-commit en CI, chaque correctif est une prise de risque silencieuse : on apprend la régression à la PR suivante, voire en production. C'est le sprint qui rend tous les autres exécutables de manière crédible.
Items de l'audit résolus
B-7 (scanners sécurité CI), B-8 (cov-fail-under), M-4 (mypy en CI), M-15 (pytest-timeout), m-7 (pre-commit non rejoué en CI), m-8 (Python 3.13 manquant de la matrice), m-9 (API stability defaults).
Livrables concrets
.github/workflows/ci.yml: ajout d'un jobsecurity(bandit + pip-audit- trivy sur l'image Docker), ajout
--cov-fail-under=85au jobtests, ajoutpytest-timeout=300global, ajout d'un jobtypecheck(mypy strict surpicarones/core/, lax ailleurs), ajout Python 3.13 à la matrice (mode warning, pas bloquant 6 mois).
- trivy sur l'image Docker), ajout
.github/workflows/precommit.yml(nouveau) : rejoue tous les hooks pre-commit en CI pour empêcher le bypass--no-verify.pyproject.toml:[tool.mypy]avecstrict = truesurpicarones.core.*,[tool.pytest.ini_options]timeout = 300ettimeout_method = "thread". Ajoutpytest-timeoutà[dev].picarones/py.typed(marqueur PEP 561 pour signaler le typage aux consommateurs externes).tests/core/test_public_api_signatures.py: pour chaque fonction exposée parpicarones/__init__.py, vérifier les valeurs par défaut des paramètres viainspect.signature(couvre m-9).
Critères d'acceptation
-
ruff check picarones/ tests/passe (régression nulle). -
pytest tests/passe avec--cov-fail-under=85. -
bandit -r picarones/ -llpasse en CI. -
pip-audit --strictpasse en CI. -
mypy picarones/core/ --strictpasse. - Un PR qui supprime un default value d'une fonction publique fait
échouer la CI sur
test_public_api_signatures. - Un PR qui dépasse 5 minutes sur un test individuel fait échouer la CI avec un message explicite, pas un hang.
Risques et mitigation
- Risque : seuil de couverture initialement fixé trop haut, premier PR
bloqué. Mitigation : mesurer le baseline avant de fixer le seuil
(
pytest --covsurmain), poser le plancher 2 points en dessous. - Risque :
mypy --strictsurcore/révèle 50 erreurs cachées. Mitigation : démarrer parcore/qui est le plus stable et le mieux typé ; les autres cercles passent en mode--no-strict-optionalpendant 1 sprint, durci en A11.
Sprint A2 — Tests de cohérence documentation (3 PJ)
Pourquoi à cette position dans la séquence
L'audit §9 montre que README liste un moteur Kraken qui n'existe pas, documente des variables AWS sans adapter, annonce 1 242 tests au lieu de 3 356, et liste 9 commandes CLI au lieu de 15. Ces erreurs ne viennent pas d'incompétence — elles viennent du fait que le README n'a aucun gardien automatique. Si on refait le README en A13 sans poser ce garde-fou, il dérivera à nouveau dès le sprint suivant.
A2 pose donc des tests de cohérence entre la documentation publiée et le code. Ces tests deviennent ensuite les gates qui valident A13 et A14.
Items de l'audit résolus
Préparation des gates pour M-19 (compteur tests), M-23 (moteurs annoncés), M-24 (variables env sans adapter), M-25 (CLI), M-26 (API web), M-27 (métriques), M-28 (sections rapport). m-12 (numérotation sprint des tests).
Livrables concrets
tests/docs/test_readme_consistency.py(nouveau) :- parse les tableaux Markdown du README ;
- pour chaque moteur listé, vérifie qu'un fichier
picarones/engines/{nom_normalisé}.pyexiste ; - pour chaque commande CLI listée, vérifie qu'elle apparaît dans
picarones --help; - pour chaque endpoint web listé, vérifie qu'il apparaît dans
app.openapi()["paths"]; - pour la phrase « pytest tests/ → N passed », vérifie que N
correspond au baseline collecté par
pytest --collect-only.
tests/docs/test_specs_consistency.py(nouveau) : même approche pour SPECS.md, avec acceptation explicite des sections marquées « Reporté » ou « Abandonné au profit de … » (lecture des balises).tests/docs/test_changelog_links.py(nouveau) : vérifie que toute référenceSprint Ndans CHANGELOG correspond à une entrée existante.Makefile: ciblemake doc-checkqui lance ces tests seuls (utile pour l'auteur du README).docs/developer/doc-consistency.md(nouveau, ~80 L) : explique le contrat (« si vous ajoutez un moteur, ajoutez la ligne dans le tableau README ; le test la valide »).tests/__init__.pyouconftest.py: helper qui audite la numérotationtest_sprintNNet signale les trous suspects (couvre m-12, sortie informative pas bloquante).
Critères d'acceptation
-
pytest tests/docs/passe sur l'état actuel des docs après avoir corrigé le README et SPECS minimalement (suppression simple des fausses promesses pour ne pas bloquer le présent sprint ; refonte complète en A13/A14). - Un PR qui ajoute un nouvel adapter OCR sans mettre à jour le
tableau README échoue à
pytest tests/docs/test_readme_consistency.py. - Un PR qui supprime une commande CLI sans mettre à jour le README échoue de la même manière.
-
make doc-checkproduit un rapport lisible en < 5 s.
Risques et mitigation
- Risque : les tests sont trop stricts et bloquent un PR légitime
(ex : moteur en cours d'ajout, doc à jour ensuite). Mitigation :
tolérer une « exception déclarée » via une balise HTML invisible
<!-- doc-check: skip-engine -->dans le tableau, à utiliser avec modération et auditée à la PR. - Risque : la mise à jour minimale du README/SPECS pour faire passer le test interfère avec la refonte A13/A14. Mitigation : se limiter à supprimer les promesses fausses (Kraken ligne, AWS env vars), pas à ajouter du contenu nouveau — ça reste pour A13.
4. Phase 1 — Hygiène architecturale (sem. 3)
Objectif : ramener l'architecture à 100 % de conformité au modèle 3 cercles avant que la documentation produit (Phase 7) ou un papier JOSS (Phase 6) ne décrivent l'architecture.
Sprint A3 — Refactor cercles + importers (3 PJ)
Pourquoi à cette position dans la séquence
L'audit §2 identifie deux violations Cercle 2 → Cercle 3
(measurements/statistics.py:861 importe report.diff_utils ;
measurements/difficulty.py:195 importe report.colors) et trois
except Exception: pass qui violent la règle propre du projet.
Ces dettes architecturales sont locales mais doivent être payées
avant la Phase 6 : un papier JOSS qui décrit une architecture en
3 cercles ne peut pas se faire mentir par le code. De plus, c'est
le sprint le moins risqué des phases suivantes — il rode l'équipe
sur le pattern fix + test de non-régression posé par A1.
Items de l'audit résolus
B-1 (statistics.py:861 → core/), B-2 (difficulty.py:195 →
report/), B-3 (3 importers except Exception: pass), m-17
(tests/measurements/test_sprint11_i18n_english.py importe Cercle 3 →
déplacement en tests/integration/).
Livrables concrets
- Création de
picarones/core/diff_utils.py(déplacement depuispicarones/report/diff_utils.py).picarones/report/diff_utils.pydevient un ré-export trivial pour rétrocompat.statistics.py:861importe désormais depuiscore. - Création de
picarones/report/difficulty_render.pyqui contientdifficulty_color().picarones/measurements/difficulty.pyne contient plus que la logique numérique. picarones/extras/importers/huggingface.py:266, 416: remplacer les deuxexcept Exception: passparlogger.warning("[importers/hf] <opération> a échoué (mode dégradé) : %s", e).picarones/extras/importers/htr_united.py:448: idem.- Émettre un
FactIMPORTER_FALLBACK_TRIGGERED(priorité MEDIUM, template factuel sans chiffres en dur) pour que la synthèse du rapport mentionne l'incident à l'utilisateur final. - Déplacement de
tests/measurements/test_sprint11_i18n_english.pyverstests/integration/test_sprint11_i18n_english.py(couvre m-17). - Déplacement de
tests/measurements/test_sprint94_error_absorption.pyverstests/integration/(audit §2 MINOR 4). - Tests de non-régression :
tests/core/test_diff_utils.py: reproduire les tests detests/report/test_diff_utils.pyau nouveau chemin (le doublon surreport/reste pour la rétrocompat).tests/measurements/test_difficulty_pure.py: vérifier quepicarones.measurements.difficultyn'importe plus rien depuis Cercle 3 (importlib.util.find_spec+ analyse AST).tests/extras/test_importer_warnings.py: vérifier que chaque chemin d'erreur des 3 importers loggue un warning explicite (capturer viacaplog).
- Garde-fou architectural :
tests/core/test_circle_dependencies.py(nouveau) qui parse les imports de tous les fichierspicarones/measurements/,engines/,llm/,pipelines/,modules/et échoue si l'un d'eux importepicarones.report.*,picarones.cli.*,picarones.web.*,picarones.extras.*.
Critères d'acceptation
-
pytest tests/passe (régression nulle ; rappel : 3 356 baseline). -
tests/core/test_circle_dependencies.pyrapporte 0 violation. -
grep -rn "except Exception:$" picarones/extras/importers/renvoie 0 ligne (les 3 violations B-3 sont remplacées). - Le rapport HTML d'un benchmark où un fallback importer a été
déclenché contient le
FactIMPORTER_FALLBACK_TRIGGEREDdans la synthèse narrative.
Risques et mitigation
- Risque : la fonction
compute_word_diffdéplacée a des callers externes non documentés. Mitigation : laisser un ré-export danspicarones/report/diff_utils.pyavec unDeprecationWarningau premier appel (suppression planifiée 2 versions plus tard). - Risque : le test
test_circle_dependenciesrefuse un import légitime (par exemple un test qui mocke). Mitigation : limiter le test àpicarones/, pas àtests/; ne scanner que les imports top-level, pas les imports paresseux dans des fonctions (qui sont acceptables pour break dependency cycles).
5. Phase 2 — Robustesse runtime (sem. 4–5)
Objectif : durcir l'application web et l'orchestrateur de benchmark avant de pouvoir promettre une adoption institutionnelle. Une bibliothèque nationale ne déploie pas un service qui n'a ni protection CSRF, ni endpoint
/health, ni test de concurrence.
Sprint A4 — Sécurité web (3 PJ)
Pourquoi à cette position dans la séquence
A1 a posé les scanners (bandit + trivy) qui détecteront toute régression
sécurité. A3 a stabilisé l'architecture. Il est maintenant temps de
fermer les deux trous fonctionnels : B-11 (pas de CSRF) et M-3
(le HEALTHCHECK Docker pointe vers un /health qui n'existe pas).
Ces deux items sont indépendants des autres phases — on les fait avant
A5 (concurrence) parce qu'A5 va ajouter des tests d'intégration qui ont
besoin du middleware CSRF stabilisé.
Items de l'audit résolus
B-11 (CSRF), M-3 (/health absent).
Livrables concrets
picarones/web/security.py: ajout du middlewarecsrf_middlewarebasé surstarlette-csrf(ou implémentation maison ~80 L : token signé HMAC-SHA256 dans cookiepicarones_csrf+ en-têteX-CSRF-Tokenexigé sur POST/PUT/DELETE). Activé par variablePICARONES_CSRF_REQUIRED=1. Désactivé par défaut sur Space public (pas de session authentifiée à protéger), activé d'office en mode institutionnel.picarones/web/routers/system.py: ajouterGET /healthqui retourne{"status": "ok", "version": __version__}en < 50 ms, sans toucher à la BD ni aux engines (vrai healthcheck Kubernetes-ready). Conserver/api/statusqui reste plus riche (pour le frontend).Dockerfile:96: pointer leHEALTHCHECKvers/healthau lieu de/api/status.picarones/web/templates/_app.js: pour chaque appelfetchPOST, injecter automatiquement l'en-têteX-CSRF-Tokenlu depuis le cookie.tests/web/test_csrf.py(nouveau, ~12 cas) : POST sans token → 403 ; POST avec token invalide → 403 ; POST avec token valide → 200 ; en mode public (sansPICARONES_CSRF_REQUIRED) → POST passe sans token (rétrocompat HF Space).tests/web/test_health.py(nouveau, ~4 cas) :GET /health→ 200 + JSON valide ; latence < 100 ms ; ne déclenche pas de log SQL ; fonctionne même si la BD jobs est down.- Documentation :
SECURITY.mdmise à jour avec un encart « Mode institutionnel : exporterPICARONES_CSRF_REQUIRED=1derrière votre reverse-proxy ».
Critères d'acceptation
-
pytest tests/web/passe. -
curl -X POST -H 'X-CSRF-Token: invalid' http://localhost:7860/api/config/saveretourne 403 quandPICARONES_CSRF_REQUIRED=1. -
docker run … && sleep 35 && docker inspect <id> | grep -c '"Status": "healthy"'retourne 1 (le HEALTHCHECK passe). -
bandit -r picarones/web/ne signale aucune nouvelle issue.
Risques et mitigation
- Risque : une intégration tierce (jq, script CI maison) qui appelle
/api/benchmark/startsans token casse en mode institutionnel. Mitigation : documenter explicitement dansSECURITY.mdla procédure « générer un token viaGET /api/csrf/tokenpuis le passer dans toutes les requêtes » + exemple curl. - Risque : l'ajout du middleware ralentit les requêtes. Mitigation :
benchmark p99 sur
/api/statusavant/après ; ajouter au jobtestsCI un check< 200 ms p99 sur 100 requêtes.
Sprint A5 — Concurrence et performance (5 PJ)
Pourquoi à cette position dans la séquence
L'orchestrateur (measurements/runner.py, 1 019 lignes) gère
ProcessPool + ThreadPool, et la couche web utilise SSE +
SQLite WAL. L'audit §3 (M-13, M-14, M-16) signale qu'aucun test ne
couvre les cas concurrence sous charge, qu'il n'existe pas de
garde-fou anti-régression de performance, et que les rapports HTML
peuvent dépasser 200 MB sur de gros corpus. Tant que ces trois trous
ne sont pas fermés, la promesse « plateforme robuste » est
invérifiable. A5 vient après A4 parce que les tests de concurrence
appellent l'API web et ont besoin du middleware CSRF stable.
Items de l'audit résolus
M-13 (tests concurrence runner + SSE), M-14 (anti-régression CER), M-16 (lazy loading rapports), m-10 (tests cloud OCR sur erreurs HTTP 429/401/503).
Livrables concrets
tests/integration/test_runner_concurrency.py(nouveau, ~50 cas) :- 32 jobs concurrents avec
PICARONES_MAX_CONCURRENT_JOBS=32; - épuisement du ProcessPool puis recovery ;
- mort d'un worker au milieu d'un benchmark ;
- timeout d'un doc dans un workers (le runner doit isoler) ;
- écritures SSE concurrentes sur la même file ;
- réception
Last-Event-IDaprès reconnexion → replay correct.
- 32 jobs concurrents avec
tests/web/test_sqlite_concurrent_writes.py(nouveau, ~10 cas) : 10 threads écrivent simultanément dansJobStore→ 0 corruption, pas deSQLITE_BUSYqui remonte au client.tests/web/test_public_mode_hot_swap.py(nouveau) : passage à chaudPICARONES_PUBLIC_MODE=0 → 1au milieu d'un benchmark ne casse pas les jobs en cours.tests/engines/test_cloud_http_errors.py(nouveau, ~12 cas par cloud) : Mistral OCR / Google Vision / Azure DI mockés pour retourner 429 (rate limit), 401 (clé invalide), 503 (indisponible), réponse vide. Vérifier le retry exponentiel + le warning explicite.tests/fixtures/reference_corpus/(nouveau) : 10 documents libres de droits couvrant 3 strates (médiéval, imprimé ancien, moderne) avec GT manuelle. Source : Gallica + Wikisource (à vérifier les licences doc par doc)..github/workflows/perf_regression.yml(nouveau) : workflow hebdomadaire (cron) qui lancepicarones runsur le corpus de référence avec Tesseract + Pero, échoue si CER > 15 %. Pas à chaque PR (coût) mais audit hebdo + rapport en GitHub Issue auto.picarones/report/generator.py: nouveau paramètrelazy_images: bool = False(défaut conservateur). Si activé, externalise les images dansreport-assets/{doc_id}.pngà côté du HTML, avec<img loading="lazy" src="report-assets/…" />. Le HTML reste auto-portant si on copie aussi le dossier.picarones/cli/__init__.py: option--lazy-imagessurpicarones reportqui propage le paramètre.- Documentation dans
docs/user/reading-a-report.md: encart « Pour les corpus > 50 documents, activer--lazy-images».
Critères d'acceptation
-
pytest tests/integration/test_runner_concurrency.pypasse en < 3 min sur le runner CI. -
pytest tests/web/test_sqlite_concurrent_writes.pypasse sans flakiness sur 10 runs successifs. - Le job
perf_regressionhebdomadaire publie un commentaire automatique sur une GitHub Issue dédiée (#perf-baseline) avec le CER mesuré. - Un rapport généré avec
--lazy-imagessur le corpus de référence pèse < 5 MB (vs ~50 MB sans). - Aucun test n'a un timeout > 60 s individuel (limite douce pour parallélisation pytest-xdist plus tard).
Risques et mitigation
- Risque : le corpus de référence introduit un coût CI permanent.
Mitigation : ne le lancer qu'en hebdo (cron), pas en PR. Le job
est skippable manuellement via
[skip perf]dans le commit message pour les release urgentes. - Risque :
--lazy-imagescasse l'auto-portance promise du rapport. Mitigation : documenter explicitement (encart en tête du rapport généré avec ce flag) et ajouter un sous-flag--bundle-zipqui produit un.zipcontenant HTML + dossier d'images.
6. Phase 3 — Accessibilité (sem. 5–6, parallélisable avec Phase 4)
Objectif : atteindre WCAG 2.1 niveau A bloquant (A6) puis niveau AA cible BnF (A7). Cette phase ne touche que les templates HTML/CSS/JS et les fichiers i18n — fichiers disjoints de Phase 4. Avec 2 ETP, A6+A7 et A8+A9 tournent en parallèle. Avec 1 ETP, A6 → A7 → A8 → A9.
Sprint A6 — WCAG niveau A bloquant (3 PJ)
Pourquoi à cette position dans la séquence
A1 a posé les outils CI mais aucun n'audite l'accessibilité. A4 a durci les endpoints. Il est maintenant temps de fermer les deux violations de niveau A identifiées par l'audit §3 : graphiques Chart.js Canvas inaccessibles aux lecteurs d'écran (B-9) et absence de lien « Aller au contenu » (B-10). Sans ces deux corrections, aucune déclaration de conformité RGAA n'est légalement possible. Le sprint ouvre aussi la voie pour A11 (declaration d'accessibilité) qui ne peut pas être rédigée sans audit niveau A passé.
Items de l'audit résolus
B-9 (Canvas charts inaccessibles, ~12 graphiques Chart.js),
B-10 (skip-to-content), m-3 (bouton « Réinitialiser » sans clé
i18n), m-4 (tableaux HTML sans scope="col" sur <th>).
Livrables concrets
picarones/report/templates/_app.js: pour chaque instanciation Chart.js (new Chart(canvas, …)lignes 1062, 1102, et autres) :- ajouter
aria-labeldescriptif sur le<canvas>; - générer en parallèle un
<table>masqué visuellement (.visually-hidden) avec les mêmes données, lié au canvas pararia-describedby; - ajouter un bouton « Voir les données » qui révèle la table à tous (utile aussi pour la copie). Bouton masqué par défaut.
- ajouter
picarones/report/templates/_header.html: en premier enfant du<body>, ajouter<a href="#main" class="skip-link">{{ i18n.skip_to_content }}</a>. Ajouterid="main"sur le<main>dubase.html.j2.picarones/report/templates/_styles.css: classe.skip-linkcachée hors:focus(position:absolute; left:-9999px;→ revient àtop:0; left:0;au focus, contraste AA).- Tableaux dans
view_*.html: ajouterscope="col"sur tous les<th>du tableau classement, du tableau NER, du tableau philologie, du tableau levers, etc. (~12 tables totales). - Bouton « Réinitialiser » (
_header.html:25) : remplacer le label hardcodé par{{ i18n.reset_all }}. Ajouter la clé danspicarones/report/i18n/{fr,en}.json. picarones/report/i18n/fr.jsoneten.json: nouvelle cléskip_to_content(« Aller au contenu » / « Skip to content ») etreset_all(« Réinitialiser » / « Reset all »).tests/report/test_a11y_level_a.py(nouveau, ~15 cas) :- chaque rapport HTML généré (demo + corpus de référence) contient
<a class="skip-link">en premier enfant du<body>; - chaque
<canvas>a unaria-labelnon vide ; - chaque
<table>a au moins un<th scope="col">; - chaque
<canvas>a un<table>jumelé viaaria-describedby; - aucune chaîne hardcodée FR/EN ne traîne dans
_header.html.
- chaque rapport HTML généré (demo + corpus de référence) contient
- Optionnel mais recommandé : ajouter
axe-core(CLI) en CI sur le rapport demo (pytest tests/report/test_a11y_level_a.pyproduit le HTML,axe http://file://…audite). Siaxecomplexe à déployer en CI, lancer manuellement à chaque release.
Critères d'acceptation
-
pytest tests/report/test_a11y_level_a.pypasse (15/15). - Lecture du rapport demo par NVDA (test manuel, capté en
vidéo dans
docs/audits/a11y-tests/A6.mp4) : chaque graphique annonce son contenu viaaria-label+ table jumelle accessible. - Tab depuis l'URL bar atteint le
<main>en 1 tabulation (skip-link visible et fonctionnel). -
axe-coreaudit sur le rapport demo signale 0 violation niveau A.
Risques et mitigation
- Risque : la table jumelle pour chaque chart double le poids HTML.
Mitigation : table générée à la demande via JS (
onclick="showTable(canvas_id)"), pas dans le DOM initial. Pour les utilisateurs AT, table rendue viaaria-describedbyqui pointe vers une<template>JS-rendered. - Risque :
axe-coreen CI introduit dépendance Node/Chromium. Mitigation : le test pytest natif est suffisant pour bloquer les régressions ;axe-corereste audit manuel à chaque release.
Sprint A7 — WCAG AA + i18n résiduel + déclaration (3 PJ)
Pourquoi à cette position dans la séquence
A6 a fermé les violations de niveau A (donc éligibilité à une conformité). A7 monte au niveau AA qui est le standard institutionnel BnF / Service-Public.fr / European Accessibility Act, et publie la déclaration d'accessibilité formelle. Sans A7, l'institution ne peut pas afficher l'engagement légal de conformité sur la home web. A7 vient juste après A6 parce que la déclaration agrège les résultats des deux sprints.
Items de l'audit résolus
m-1 (_app.js:1087 chaîne FR hardcodée), m-2 (_app.js:1049
chaîne FR hardcodée), m-5 (palette heatmap non-daltonien-friendly),
m-6 (nombres non localisés dans les tableaux), M-9 (déclaration
d'accessibilité absente).
Livrables concrets
picarones/report/templates/_app.js:1087: remplacer'Données d'ancrage non disponibles.'parI18N.no_anchor_data. Ligne 1049 : remplacer le fallback FR parI18N.no_gini.picarones/report/i18n/{fr,en}.json: ajout des clésno_anchor_data,no_gini(la deuxième existe peut-être déjà — à vérifier).picarones/report/colors.py: nouvelle palette daltonien-friendly (Okabe-Ito :#0072B2bleu /#E69F00orange /#009E73vert /#CC79A7rose /#56B4E9cyan). Conserver l'ancienne palette commecolors_classicpour rétrocompat. Variablereport_palettedans_styles.cssqui pointe vers la nouvelle par défaut, avec override possible via?palette=classicdans l'URL.- Toggle dans le panneau « Avancé » du rapport :
<label><input type="checkbox" data-key="palette"> Mode daltonien-friendly</label>qui bascule la palette via classe CSS sur<body>. picarones/report/templates/_app.js: utiliserNumber(value).toLocaleString(I18N.locale)partout où un nombre s'affiche dans un tableau (chercher${cer},${pct}, etc.).picarones/report/i18n/{fr,en}.json: ajouter le champlocale: "fr-FR"/"en-GB"(utilisé partoLocaleString).ACCESSIBILITY.md(nouveau, ~150 L à la racine) :- engagement de conformité WCAG 2.1 AA / RGAA 4.1 ;
- méthode d'audit (interne A6+A7 + externe en A15) ;
- dérogations connues (ex : matrice de confusion Unicode reste visuellement dense, contournement table jumelle décrit) ;
- contact référent accessibilité ;
- calendrier de réaudit (annuel).
- Lien
ACCESSIBILITY.mdajouté en footer du rapport HTML et de la home web. tests/report/test_a11y_level_aa.py(nouveau, ~12 cas) :- palette daltonien-friendly active par défaut ;
- contraste cellules tableau classement ≥ 4,5:1 (pour normal text) sur les 4 tiers (excellent/acceptable/médiocre/ critique) ;
- nombres dans les tableaux respectent
toLocaleString(report_lang)(test :1234567rendu en FR contient un espace fineou un espace insécable) ; - aucune chaîne FR n'apparaît dans le HTML rendu en mode EN.
tests/web/test_accessibility_link.py: la home web et le footer du rapport HTML linkent vers/accessibilityouACCESSIBILITY.md.
Critères d'acceptation
-
pytest tests/report/test_a11y_level_aa.pypasse. - Le toggle « Mode daltonien-friendly » dans le panneau Avancé
bascule la palette en < 100 ms et persiste en URL (
?palette=…). - Sur le rapport demo,
axe-coresignale 0 violation niveau AA (audit manuel). -
ACCESSIBILITY.mdpublié et linké depuis le footer.
Risques et mitigation
- Risque : la nouvelle palette Okabe-Ito n'est pas appréciée
esthétiquement par l'équipe. Mitigation : laisser le toggle URL
?palette=classicqui revient à l'ancienne palette ; l'équipe trouvera son équilibre par usage. - Risque : la déclaration d'accessibilité engage légalement
(loi 2005-102 art. 47 en France). Mitigation : audit externe en
A15 avant publication officielle de la déclaration. La version
rédigée en A7 reste en mode draft (
ACCESSIBILITY.mdavec encart « Audit externe en cours, version provisoire ») jusqu'à validation.
7. Phase 4 — Reproductibilité opérationnelle (sem. 5–7, parallélisable avec Phase 3)
Objectif : passer d'une plateforme « ça compile chez moi » à une plateforme dont tout artefact est reproductible bit-à-bit ou à minima reconstructible à un commit/digest donné. Préalable absolu à la Phase 6 (publication scientifique) qui ne peut pas garantir la reproductibilité tant que les builds eux-mêmes ne le sont pas.
Sprint A8 — Lock file + Docker digest + ops files (3 PJ)
Pourquoi à cette position dans la séquence
A1 a hardci la CI mais elle reste vulnérable à une dérive de
dépendances : aucun lock file, image Docker python:3.11-slim sans
digest, requirements.txt à la racine divergent. Toute promesse
de reproductibilité (Phase 6) tombe si on ne peut pas reconstruire
exactement l'environnement d'un benchmark. A8 vient avant A9
(distribution PyPI) parce qu'on ne publie pas un wheel sans avoir
verrouillé ses transitives.
Items de l'audit résolus
M-1 (lock file), M-2 (Docker digest), M-12 (snapshots
reproductibilité sous-documentés), M-18 (.dockerignore +
.env.example), m-11 (versionnement testdata), m-13
(requirements.txt divergent), m-14 (staleness pricing.yaml).
Livrables concrets
- Adoption de
uv(plus rapide que pip-tools, projet Astral) pour la génération du lock :uv pip compile pyproject.toml --extra dev --extra web --extra stats --extra llm --extra ner --output-file requirements.lockuv pip compile pyproject.toml --output-file requirements-runtime.lock(cœur seulement, utilisé par Docker production).
Dockerfile: remplacerpip install .paruv pip sync --system requirements-runtime.lock && pip install . --no-deps. ÉpinglerFROM python:3.11.10-slim@sha256:…(digest obtenu viadocker pull python:3.11.10-slim && docker inspect)..dockerignore(nouveau, ~30 L) : exclure.git,tests/,docs/,*.pyc,__pycache__/,.venv/,.github/,examples/,*.spec,node_modules/,*.mdsaufREADME.md(utile pour le message de welcome)..env.example(nouveau, ~30 L) : toutes les variables d'env utilisées pardocker-compose.ymletpicarones/web/security.py(PICARONES_PUBLIC_MODE, PICARONES_RATE_LIMIT_PER_HOUR, PICARONES_MAX_UPLOAD_MB, PICARONES_BROWSE_ROOTS, PICARONES_MAX_CONCURRENT_JOBS, PICARONES_CSRF_REQUIRED, MISTRAL_API_KEY, OPENAI_API_KEY, ANTHROPIC_API_KEY, GOOGLE_APPLICATION_CREDENTIALS, AZURE_DOC_INTEL_*) avec une ligne de commentaire chacune.requirements.txt(existant) : devient un alias-r requirements.lockou supprimé avec un message dansINSTALL.mdredirigeant verspip install -e ".[dev,web]".tests/.testdata/VERSION.yaml(nouveau) : pour chaque corpus de test versionnable, une entrée{name, sha256, source_url, commit_picarones, date_added}. Permet de détecter une dérive accidentelle des fixtures.picarones/data/pricing.yaml: ajouter en têtelast_updated:etvalid_until:(par défaut +6 mois). Le générateur de rapport émet unFactPRICING_STALENESS_WARNING(importance MEDIUM) sitoday > valid_until.docs/reproducibility-snapshots.md(nouveau, ~250 L) :- ce qu'un snapshot contient (déjà documenté en partie dans
picarones/report/snapshot.pymais éparpillé) ; - comment recharger un snapshot pour rejouer un benchmark ;
- comment versionner un snapshot dans un papier (commit picarones + digest Docker + lock file hash) ;
- exemple complet bout-en-bout avec un mini corpus.
- ce qu'un snapshot contient (déjà documenté en partie dans
.github/workflows/lock_refresh.yml(nouveau) : workflow mensuel (cron 1er du mois) qui régénère les locks et ouvre un PR automatique pour validation humaine.
Critères d'acceptation
-
pytest tests/passe identiquement avecpip install -r requirements.lockque avecpip install -e ".[dev,web]". -
docker build .fait sans network après le premier pull du base image (toutes les deps sont dans le cache). -
docker images picarones --format "{{.Size}}"< 1.5 GB (réduction grâce à.dockerignore). - Un benchmark reproduit à 6 mois d'intervalle avec les mêmes lock file + digest Docker + commit picarones produit un rapport bit-à-bit identique (test bonus, lourd, fait manuellement avant release v1.1.0).
- Le rapport demo généré 2 jours après
valid_untildu pricing contient leFactPRICING_STALENESS_WARNING.
Risques et mitigation
- Risque :
uv pip syncintroduit un comportement subtilement différent depipsur des deps avec extras conditionnels. Mitigation : test parité installation (pytest -k "test_install_parity") qui comparepip freezeavant/après. - Risque : le PR mensuel auto de refresh des locks crée du bruit.
Mitigation : labels GitHub
auto-refresh+ auto-merge si tous les checks passent (zone configurable selon politique de l'institution).
Sprint A9 — Distribution PyPI + ghcr.io + releases (3 PJ)
Pourquoi à cette position dans la séquence
A8 a posé la base reproductible. A9 transforme cette base en
artefacts publiables : wheel sur PyPI, image conteneur sur ghcr.io,
release GitHub avec changelog auto. Sans A9, le projet reste invitable
en pip install picarones ; un papier JOSS qui pointe vers le
projet (Phase 6) doit pouvoir citer une version installable.
Items de l'audit résolus
M-5 (PyPI release pipeline), M-6 (image conteneur immutable
publiée), m-15 (picarones.spec PyInstaller hidden_imports
manuels), m-16 (extras placeholder [historical] [importers]).
Livrables concrets
pyproject.toml: adoptersetuptools_scmpour la version (via tag Git). Suppression de la version hardcodée1.0.0..github/workflows/release.yml(nouveau) : déclenché sur tagv*:- build sdist + wheel via
python -m build - test parité (
twine check) - publication TestPyPI
- smoke test (
pip install --index-url testpypi picarones==<version> && picarones --version) - publication PyPI via
pypa/gh-action-pypi-publishavec OIDC trust (pas de token long-lived) - build image multi-arch (amd64 + arm64 pour Mac M-series) via
docker buildx - push
ghcr.io/maribakulj/picarones:<version>+:latest - création GitHub Release avec corps généré depuis le CHANGELOG (parser Keep-a-Changelog)
- build sdist + wheel via
picarones.spec(PyInstaller) : remplacer la listehiddenimportsmanuelle parfrom PyInstaller.utils.hooks import collect_allet un parcours auto. Tester via un nouveau job CIbuild-exe(non bloquant jusqu'à v1.1.0, bloquant ensuite).pyproject.tomlextras : retirer les placeholdershistorical = []etimporters = [](les modules sont dans le package principal — l'extra n'apporte rien). Documenter dansCHANGELOG.mdque la séparation en packages PyPI séparés (picarones-historical,picarones-importers) est une décision architecturale future, pas un placeholder vide aujourd'hui.tests/release/test_pypi_install.py(nouveau) : sur un job CIrelease-smoke, lance dans un container videpip install picarones && picarones demo --output /tmp/demo.htmlet vérifie que le HTML est produit.docs/operations/release-process.md(nouveau, ~80 L) : procédure release manuelle (tag → workflow → vérifs → annonce).
Critères d'acceptation
-
pip install picarones==1.1.0-rc1depuis TestPyPI fonctionne sur Linux + macOS + Windows. -
docker pull ghcr.io/maribakulj/picarones:1.1.0-rc1retourne une image fonctionnelle qui démarre en < 30 s. - La release GitHub
v1.1.0-rc1est créée automatiquement, son corps reflète la section correspondante du CHANGELOG. -
picarones.specbuild sans erreur et l'exécutable produit lancepicarones demo --output /tmp/demo.htmlcorrectement.
Risques et mitigation
- Risque : conflit de nom sur PyPI (un autre projet
picaronesexisterait). Mitigation : vérifier en début de sprint viapip search(déprécié) →https://pypi.org/project/picarones/. Si conflit, négocier avec le maintainer ou renommer enpicarones-bench. - Risque : OIDC trust setup demande des permissions GitHub Actions
spécifiques. Mitigation : documenter dans
release-process.mdla procédure de setup PyPI Trusted Publisher + capture d'écran.
8. Phase 5 — Gouvernance institutionnelle (sem. 7–8)
Objectif : passer du « projet maintenu par une personne » à un artefact institutionnel avec politiques publiques (gouvernance, conflit d'intérêt), documentation opérationnelle pour DSI BnF (déploiement intranet, RGPD, traduction des guides clés), et CODEOWNERS qui désigne les responsabilités de revue.
Sprint A10 — Politiques de gouvernance (2 PJ)
Pourquoi à cette position dans la séquence
A8 et A9 ont rendu le projet distribuable. Avant qu'une institution ne l'évalue pour adoption, elle doit pouvoir lire la politique de maintenance, la divulgation de conflit d'intérêt (le projet benchmarke des APIs cloud payantes — biais éditorial possible) et l'identification des mainteneurs. C'est un sprint court mais bloquant pour toute relation avec un service achat / juridique d'institution publique.
Items de l'audit résolus
M-10 (divulgation de conflits d'intérêt), M-11 (CODEOWNERS, politique de maintenance, GOVERNANCE).
Livrables concrets
.github/CODEOWNERS(nouveau) : pour chaque sous-package, désigner le mainteneur de revue. Exemple :
À l'arrivée de contributeurs domain experts (paléographe, archiviste), les mettre dans le CODEOWNERS pour les fichiers qui les concernent (cas d'études, prompts, glossaire)./picarones/core/ @maribakulj /picarones/measurements/ @maribakulj /picarones/engines/ @maribakulj /picarones/web/ @maribakulj /picarones/report/ @maribakulj /docs/ @maribakulj /docs/case-studies/ @maribakulj # rotater quand contributeurs domain expertsGOVERNANCE.md(nouveau, ~120 L à la racine) :- rôles : maintenance team, contributeurs occasionnels, reviewers ;
- cadence release : versions mineures mensuelles ; versions majeures trimestrielles ; patches sécurité 72 h ;
- SLO réponse aux issues : 5 jours ouvrés pour triage initial ;
- politique de breaking changes : interdits sans tag
v2.0.0; - procédure de transfert de mainteneur (BDFL → equipe → fondation en cas de croissance).
CODE_OF_CONDUCT.md(nouveau si absent ; ~40 L) : adopter Contributor Covenant 2.1.README.mdouGOVERNANCE.md: section « Conflicts of interest » :- les mainteneurs déclarent leurs affiliations académiques / industrielles ;
- les fournisseurs cloud benchmarkés (OpenAI, Anthropic, Mistral, Google, Azure) n'ont aucun lien capitalistique avec le projet (à vérifier puis affirmer) ;
- le
pricing.yamlreflète les tarifs publics observés à la datelast_updated, sans accord commercial avec les providers.
paper.md(draft JOSS, créé en A12) : reproduira la section COI.tests/docs/test_governance_files_present.py(nouveau) : vérifie queCODEOWNERS,GOVERNANCE.md,CODE_OF_CONDUCT.md,SECURITY.md,LICENSEexistent et ne sont pas vides. Garde-fou contre suppression accidentelle.
Critères d'acceptation
-
pytest tests/docs/test_governance_files_present.pypasse. -
gh repo view --json codeOfConduct,licenseInforetourne les bons noms. - La page « About » du repo GitHub est complète (description, website, topics).
- Au moins un PR test passe par le mécanisme de review CODEOWNERS (assigned reviewers correspondant au path).
Risques et mitigation
- Risque : le mainteneur unique ne veut pas s'engager sur des SLO publics. Mitigation : formuler les SLO en mode « best effort current » avec date de revue annuelle, pas en engagement contractuel.
Sprint A11 — Documentation institutionnelle (5 PJ)
Pourquoi à cette position dans la séquence
A10 a publié les politiques publiques. A11 produit la documentation opérationnelle qu'un DSI BnF lit en deuxième : guide de déploiement intranet (au-delà du seul Space HuggingFace), politique RGPD/rétention, déclaration d'accessibilité finalisée (issue de A6+A7), traduction anglaise des guides utilisateur et développeur prioritaires. Sans A11, l'institution ne peut pas mettre Picarones en production sur ses propres infrastructures.
Items de l'audit résolus
M-7 (guide déploiement institutionnel), M-8 (politique RGPD /
rétention des données), M-17 (traduction EN documentation
prioritaire). Validation finale de M-9 initiée en A7
(ACCESSIBILITY.md passe en mode « audit interne validé, audit externe
en cours »).
Livrables concrets
docs/operations/deployment-institutional.md(nouveau, ~250 L) :- pré-requis (Python 3.11+, Tesseract, optionnel : SSO Shibboleth / CAS / OIDC, BD partagée optionnelle Postgres en remplacement de SQLite, reverse-proxy Nginx/Apache) ;
- architecture cible (mono-instance simple ; multi-instance derrière load balancer + BD centralisée) ;
- intégration SSO via en-tête trusté
X-Remote-User(déjà géré par la plupart des proxies institutionnels) ; - configuration des
PICARONES_*variables d'env pour mode institutionnel (CSRF activé, browse roots restreints, rate limit aligné sur la politique interne) ; - intégration observabilité : format de log JSON pour ELK/Loki,
métriques Prometheus exposées sur
/metrics(à implémenter — voir note Risque) ; - sauvegarde / restauration de l'historique SQLite et des rapports.
docs/operations/data-retention-rgpd.md(nouveau, ~150 L) :- quelles données Picarones collecte (uploads, IPs dans le rate-limiter, historique benchmarks) ;
- durées de rétention par défaut et configurables :
- uploads : 7 jours après dernier accès, purge auto via cron ;
- logs IP : 24 h ;
- historique benchmarks : indéfini par défaut, purge sur demande ;
- procédure d'export / suppression des données d'un usager (droit à l'oubli) ;
- mention RGPD dans le footer web (lien vers cette page).
picarones/web/maintenance.py(nouveau, ~80 L) : tâche de purge schedulée (asyncio.create_taskau démarrage de l'app) qui scanneuploads/et supprime ce qui dépassePICARONES_UPLOAD_RETENTION_DAYS(défaut 7).tests/web/test_upload_retention.py(nouveau, ~5 cas) : un upload daté de 8 jours est supprimé ; un upload daté de 6 jours est conservé ; la purge n'efface pas les rapports générés.ACCESSIBILITY.md: passer en mode « audit interne validé », mettre à jour la date de réaudit pour A15.- Traduction prioritaire en anglais :
docs/user/reading-a-report.md→docs/user/reading-a-report.en.mddocs/developer/index.md→docs/developer/index.en.mddocs/developer/narrative-engine.md→.en.mddocs/developer/extending-glossary.md→.en.mddocs/developer/extending-i18n.md→.en.mdCONTRIBUTING.md→CONTRIBUTING.en.md- chaque fichier en français reçoit un en-tête
> 🇬🇧 [English version](file.en.md)et réciproquement.
tests/docs/test_translation_parity.py(nouveau) : vérifie que chaque*.mda son.en.mdéquivalent et que les sections de premier niveau (##) correspondent (titres traduits ou identiques).- CHANGELOG.md (cible non-rétroactive) : adopter à partir de v1.2.0 un format bilingue (sections « Ajouté / Added », « Modifié / Changed », « Corrigé / Fixed »).
Critères d'acceptation
-
pytest tests/docs/test_translation_parity.pypasse (5 fichiers traduits, 5 paires). - Un test manuel de la purge auto sur un upload daté de J-8
confirme la suppression effective et un log
[maintenance] purged upload <id>. - La home web affiche en footer les liens vers
RGPD,ACCESSIBILITY,GOVERNANCE. -
docs/operations/deployment-institutional.mdest revu par au moins un DSI partenaire (BnF, BL, KBR ou autre — sollicitation à externaliser).
Risques et mitigation
- Risque : l'intégration Prometheus / observabilité dépasse le scope
prévu. Mitigation : au pire, A11 ne livre que la documentation
de l'intégration (
docs/operations/observability.md) et l'instrumentation effective passe en backlog sprint A16+. - Risque : la traduction EN demande une compétence linguistique pas
toujours interne. Mitigation : version EN passée par DeepL
- relecture humaine, marquée
<!-- translation: machine + human review -->jusqu'à validation par un anglophone natif.
- relecture humaine, marquée
9. Phase 6 — Publication scientifique (sem. 8–10)
Objectif : rendre le projet citable. Sans CITATION.cff, sans DOI Zenodo, sans papier JOSS, et sans citation primaire des méthodes statistiques dans le code, Picarones reste un dépôt GitHub mutable qu'aucune thèse, aucun papier, aucune institution sérieuse ne peut citer comme référence stable.
Sprint A12 — CITATION + traçabilité méthodes + draft JOSS (5 PJ + cycle externe)
Pourquoi à cette position dans la séquence
C'est le sprint qui exige le plus de stabilisation préalable :
- A3 (architecture propre) avant que SPECS et le papier ne décrivent les 3 cercles ;
- A8 (lock file) avant que le papier ne promette la reproductibilité ;
- A9 (PyPI release) avant que le papier ne pointe vers
pip install picarones; - A10 (COI) avant que le papier ne déclare l'absence de conflit ;
- A11 (a11y validée, RGPD documenté) avant que la soumission ne passe les comités d'éthique le cas échéant.
A12 démarre dès que A9 ferme. Le sprint produit la doc et le draft ; le cycle de revue par les pairs JOSS (8–12 semaines) tourne en parallèle de la suite (Phase 7).
Items de l'audit résolus
B-4 (CITATION.cff + Zenodo + draft JOSS), B-5 (références primaires des méthodes statistiques dans le code), B-6 (traçabilité des profils de normalisation aux standards éditoriaux MUFI / TEI / DEAF).
Livrables concrets
CITATION.cff(nouveau, racine) :- format Citation File Format 1.2.0 (parsé automatiquement par GitHub) ;
- champs :
authors(avec ORCID),title,version,date-released,doi: 10.5281/zenodo.<id>(assigné par Zenodo après release),url,keywords,license: Apache-2.0,repository-code,preferred-citationpointant vers le papier JOSS quand publié.
- Configuration Zenodo : activer l'intégration GitHub-Zenodo dans les
settings du repo. Une fois la release v1.1.0 publiée (sortie d'A9),
Zenodo crée un DOI immutable. Mettre à jour
CITATION.cff. paper.md(nouveau, racine, format JOSS, ~6–8 pages soit ~600 L) :- Summary (200 mots) : qu'est-ce que Picarones, à qui ça s'adresse, en quoi c'est différent d'ocrevalUAtion / dinglehopper ;
- Statement of need (300 mots) : pourquoi un banc d'essai (pas un atelier de production) ; pourquoi les métriques philologiques et la neutralité éditoriale comptent pour les institutions patrimoniales ;
- Functionality (1 page) : architecture en 3 cercles, registre
typé de métriques (Sprint 34), interface
BaseModule(Sprint 33), moteur narratif factuel anti-hallucination (Sprint 19), pipelines composables (Sprints 63–66) ; - Quality control (½ page) : 3 356 tests, ruff, scanners CI, snapshots reproductibles, conformité WCAG AA ;
- References (BibTeX) : Demšar 2006, Wilcoxon 1945, Efron 1979, MUFI v4.0, TEI P5, HTR-United, etc.
paper.bib(nouveau, racine) : entries BibTeX correspondantes.- Soumission JOSS : fork JOSS reviews repo, pre-review issue, attente de l'attribution d'un éditeur. Cycle externe non bloquant pour les sprints suivants.
picarones/measurements/statistics.py: ajouter en-tête de module avec les références primaires (BibTeX en commentaire) :"""Tests statistiques pour la comparaison de moteurs OCR. Méthodes implémentées et leurs références primaires : - Test de Wilcoxon signé : Wilcoxon, F. (1945). Individual comparisons by ranking methods. Biometrics Bulletin, 1(6), 80–83. - Test de Friedman + post-hoc Nemenyi : Demšar, J. (2006). Statistical comparisons of classifiers over multiple data sets. JMLR, 7, 1–30. - Bootstrap intervalles de confiance : Efron, B. (1979). Bootstrap methods. Annals of Statistics, 7(1), 1–26. - Critical Difference Diagram : Demšar 2006 (cf. ci-dessus). """- dans la docstring de chaque fonction publique (
compute_friedman,compute_nemenyi_posthoc,bootstrap_ci, etc.), ajouter une ligne:references: Demšar 2006 §3.2.
- dans la docstring de chaque fonction publique (
picarones/measurements/normalization.py: pour chaque profil (DIPLOMATIC_FR,MUFI,EARLY_MODERN_*,MEDIEVAL_*), ajouter en commentaire la spec source (URL stable, version, date d'extraction) :MEDIEVAL_FRENCH = { # Source: TEI P5 §3.4 Unicode (https://tei-c.org/release/doc/...) # MUFI v4.0 (https://mufi.info/m.php?p=mufi/specifications) # DEAF normalization conventions (Möhren 2017) # Date d'extraction: 2026-05-02 "ſ": "s", # long s ... }docs/normalization-specs.md(nouveau, ~200 L) : tableau exhaustif profil × spec source × date d'extraction × DOI/URL. Liens vers les documents source. Politique de mise à jour (à chaque révision MUFI ou TEI, ouvrir un PR avec la diff).tests/measurements/test_normalization_traceability.py(nouveau) : pour chaque profil, vérifier qu'il existe une entrée dansdocs/normalization-specs.md(parsing simple).picarones/report/glossary/{fr,en}.yaml: pour les 25 entrées, vérifier que le champreferenceest rempli avec une citation primaire (audit ligne par ligne ; corriger les manquants).
Critères d'acceptation
-
CITATION.cffest valide seloncffconvert --validate. - Le bouton « Cite this repository » apparaît sur la page GitHub du repo et produit BibTeX + APA correct.
- DOI Zenodo
10.5281/zenodo.<id>est attribué et le badge apparaît dans le README (mise à jour finale en A13). -
paper.mdest validé localement parwhedon(validateur JOSS,whedon prepare picarones-paper). -
tests/measurements/test_normalization_traceability.pypasse. - Une recherche
grep -rn "Demšar 2006" picarones/measurements/retourne au moins 3 occurrences.
Risques et mitigation
- Risque : revue JOSS demande des changements méthodologiques de fond (par ex. ajouter une métrique ou changer une interprétation). Mitigation : prévoir un sprint A12-bis dédié à ces retours, en Phase 8 ; ne pas bloquer A13/A14 sur ce cycle externe.
- Risque : Zenodo intégration ne fonctionne qu'avec une release GitHub publique. Mitigation : confirmer que le repo est public ou rendre public en début de sprint (cohérent avec Apache-2.0 affiché).
- Risque : MUFI v4.1 sort pendant la rédaction → références
obsolètes en publication. Mitigation :
docs/normalization-specs.mddate l'extraction explicitement ; la version implémentée reste v4.0 avec tracking ouvert pour v4.1 en backlog.
10. Phase 7 — Refonte documentation produit (sem. 10–11)
Objectif : maintenant que le code, la CI, l'a11y, l'ops, la gouvernance et la communication scientifique sont stabilisées, on refait README et SPECS en dernier — précisément parce qu'ils doivent refléter un état figé. Refaire ces documents avant aurait impliqué de les refaire deux fois.
Sprint A13 — Refonte README (4 PJ)
Pourquoi à cette position dans la séquence
Le README est la première impression. L'audit §9.2 a identifié 13 items : markdown cassé, compteurs faux, roadmap arrêtée 75 sprints en arrière, project structure pré-Sprint 32-34, moteurs annoncés sans adapter, CLI/API/métriques sous-documentées de moitié à deux tiers. A13 vient en avant-dernier parce que le contenu à publier dépend de :
- A1+A2 (gates anti-régression activés),
- A3 (architecture stabilisée),
- A6+A7 (a11y validée → on peut afficher le badge AA),
- A8+A9 (
pip install picaronesfonctionne, image ghcr.io disponible), - A12 (CITATION.cff présent → bouton « Cite this repository » à promouvoir dans le README).
Items de l'audit résolus
B-13 (markdown des taglines cassé), M-19 (compteur de tests),
M-20 (Roadmap Sprint 22 → 97), M-21 (Known Issues obsolète),
M-22 (Project Structure pré-refactor), M-23 (Kraken /
custom YAML annoncés sans implémentation), M-24 (variables
AWS_* sans adapter), M-25 (CLI 6/15 documentée), M-26 (API
web 10/27), M-27 (métriques 8/28), M-28 (sections rapport
15/25), m-18 (copyright, lien SPECS, prompts latin),
§9.3 (alignement compteurs entre 3 docs).
Livrables concrets
README.mdréécrit intégralement à partir du squelette de l'existant, en respectant les sections suivantes (et seulement celles-ci, pour ne pas reproduire l'enflure originelle) :- Header (titre, taglines bilingues fermées correctement, badges CI / Python / License / DOI Zenodo / PyPI / HF Space).
- What is Picarones (paragraphe de 100 mots, FR puis EN — pas de duplication de section).
- Use case (paragraphe d'archive/library, 80 mots).
- Quick start (3 commandes :
pip install picarones,picarones demo,picarones serve). - Installation (renvoie à
INSTALL.mdpour le détail). - Documentation (table des renvois vers
docs/user/,docs/developer/,docs/operations/). - Citation (BibTeX généré depuis CITATION.cff + DOI Zenodo).
- License (Apache 2.0).
- Tableau « Supported engines » auto-généré par un script
scripts/gen_readme_tables.py(nouveau) qui litpicarones/engines/__init__.pyet produit la liste à partir du registre. Idem pour la liste des commandes CLI (litpicarones --help) et la liste des endpoints (litapp.openapi()). Ces tableaux sont insérés dans le README via des balises HTML invisibles<!-- generated:engines -->/<!-- /generated:engines -->. Régénération en CI à chaque PR via un job qui échoue si le contenu généré diffère du contenu commité. - Section « Heritage-specific metrics » : 3 sous-sections
(« Métriques classiques OCR/HTR », « Métriques philologiques »,
« Métriques de comparaison et décision »), chacune avec 3 à 5
bullets et un lien vers
docs/views.mdpour le détail. Plus de liste linéaire de 28 items. - Section « Interactive HTML Report » : screenshot du rapport
demo (image dans
docs/assets/report-screenshot.png, taille < 200 KB)- liste structurée des 25 sections du rapport regroupées en 5 familles (synthèse, classement, vues thématiques, analyses statistiques, panneaux interactifs).
- Roadmap : tableau condensé des sprints par phase, pas
individuellement. Trois colonnes : phase / focus / état. Détail
technique renvoyé vers
CHANGELOG.mdetdocs/roadmap/. - Known Issues : suppression intégrale. Cette section devient
caduque (le présent plan de remédiation et l'audit la remplacent).
Note de redirection : « Voir
docs/audits/pour les audits en cours ». - Project Structure : régénéré à partir du repo réel via un
script
scripts/gen_project_structure.py(nouveau). Insertion via<!-- generated:structure -->. - Footer :
Copyright 2024–2026 Picarones contributors. Lien RGPD / Accessibility / Governance / Contributing. tests/docs/test_readme_consistency.py(déjà créé en A2) doit passer strictement sur le nouveau README — tous les moteurs, toutes les commandes, tous les endpoints, tous les compteurs sont vérifiés.tests/docs/test_readme_dual_lang.py(nouveau) : la version FR et la version EN ont des sections de premier niveau qui se correspondent.
Critères d'acceptation
-
pytest tests/docs/test_readme_consistency.pypasse (0 divergence entre tableau README et code). - Les badges CI / Codecov / PyPI / DOI / HF Space sont tous verts au moment de la PR de refonte.
- Le nouveau README compte < 400 lignes (vs 786 actuelles, en
grande partie déléguées à
docs/). - Le markdown des taglines est correct, validé par
markdown-link-checkou équivalent. - Un test manuel de rendu sur GitHub ET sur HuggingFace Space
affiche un README propre, sans
> **jamais fermé.
Risques et mitigation
- Risque : la génération auto des tableaux casse à un PR futur si
un moteur n'a pas la bonne shape. Mitigation : le script de
génération échoue lui-même en CI avant l'étape de comparaison,
avec un message explicite (« Engine
xyzmanque le champdisplay_name»). - Risque : le screenshot du rapport demo prend de la place dans le
repo. Mitigation : 200 KB plafonné par pre-commit (déjà actif
via
check-added-large-files --maxkb=500). Image optimisée viapngquant.
Sprint A14 — Refonte SPECS.md (3 PJ)
Pourquoi à cette position dans la séquence
SPECS.md (Mars 2025, addendum Sprints 16-30) est désynchronisé d'~75 sprints. L'audit §9.1 identifie 9 promesses non tenues sans deprecation et ~25 modules majeurs ajoutés invisibles dans SPECS. A14 vient après A13 parce que :
- le README est la première lecture et doit être impeccable avant qu'on touche au document plus technique ;
- A13 a posé le pattern de génération auto des tableaux que SPECS va réutiliser pour ses listes de moteurs / métriques ;
- A12 a publié les références primaires des méthodes statistiques — SPECS peut maintenant les citer correctement.
Items de l'audit résolus
B-12 (SPECS à refondre intégralement, 9 promesses non tenues + 25 modules ajoutés non documentés).
Livrables concrets
SPECS.mdréécrit intégralement, version 2.0 datée mai 2026, reflétant strictement le code réel, structuré comme suit :- Vision et positionnement : philosophie banc d'essai (pas atelier), neutralité éditoriale, contribution scientifique du projet (registres typés, narrative anti-hallucination, interface BaseModule).
- Architecture : diagramme des 3 cercles à jour (Cercle 1 = 7 modules, Cercle 2 = ~70, Cercle 3 = ~50), règle de dépendance, registre typé Sprint 34, interface BaseModule Sprint 33, GT multi-niveaux Sprint 32.
- Modules fonctionnels : 6 sous-sections (Corpus, Adaptateurs OCR, Pipelines composables, Métriques, Rapport, Interface). Chacune décrit ce qui existe aujourd'hui, sans projection.
- Métriques : tableau exhaustif des 28+ métriques avec leur statut (« stable »/« expérimental »), spécification exacte, citation primaire, jonction de type (TEXT, ALTO, etc.), limites connues.
- Modes pipeline :
zero_shot,post_correction_texte,post_correction_image_texte,pipeline_composable_yaml(Sprint 70). - Sécurité institutionnelle : récap des garde-fous (PICARONES_PUBLIC_MODE, CSRF, zip-slip, defusedxml, rate limit, validation Pillow).
- Reproductibilité : snapshots, lock file, digest Docker.
- Limites assumées et non-fonctionnalités : liste explicite
de ce que Picarones ne fait pas et ne fera pas dans la
v1.x — par exemple :
- pas de recommandation prescriptive (pivot philosophique vs SPECS v1) ;
- pas d'export PDF (CSV + JSON suffisent ; export PDF reporté car coût de maintenance disproportionné) ;
- pas d'adapter Kraken / AWS Textract / Calamari / OCRopus4 intégré (raisons : maintenance par adapter ~50 PJ ; ouverture en plugins externes prévue Sprint 97+) ;
- pas de moteur custom YAML (refondu en pipelines composables Sprint 63) ;
- pas de k-means clustering automatique des erreurs (taxonomie discrète + co-occurrence Jaccard couvrent l'usage) ;
- pas de dataset curé livré avec le projet (philosophie « banc d'essai sur votre golden dataset »).
- Roadmap d'évolution 2026 : pointe vers
docs/roadmap/evolution-2026.md.
- Tableau de migration v1 → v2 SPECS (annexe) : pour chaque promesse v1 non tenue, ligne « v1 disait X / v2 documente Y / raison Z ». Permet à un lecteur qui avait lu v1 de comprendre ce qui a changé.
tests/docs/test_specs_consistency.py(déjà créé en A2) doit passer.- Lien dans le README (A13) : « Pour la spécification fonctionnelle et technique complète, voir SPECS.md ».
Critères d'acceptation
-
pytest tests/docs/test_specs_consistency.pypasse. - Aucune section de SPECS ne décrit une fonctionnalité absente du code.
- Toute fonctionnalité majeure citée dans le CHANGELOG depuis Sprint 30 est mentionnée dans SPECS v2.
- La section « Limites assumées » est non vide et liste les 9+ promesses v1 explicitement abandonnées avec leur raison.
Risques et mitigation
- Risque : la section « Limites assumées » est lue comme une régression par un primo-lecteur. Mitigation : reformuler positivement (« choix éditoriaux du projet ») et expliquer le pivot vers la philosophie banc d'essai en intro.
- Risque : SPECS.md devient un duplicata de CLAUDE.md. Mitigation : garder SPECS fonctionnel et tourné public (vocabulaire bibliothécaire, exemples patrimoniaux), CLAUDE.md technique et tourné contributeur. Les deux docs ne se chevauchent pas.
11. Phase 8 — Validation externe (sem. 11–12 + calendrier externe)
Objectif : valider l'ensemble par des tiers indépendants. C'est ce qui transforme l'auto-déclaration en certification.
Sprint A15 — Audits externes (1 PJ interne + cycle externe)
Pourquoi à cette position dans la séquence
Le travail interne est terminé. A15 est volontairement court côté équipe (1 PJ) parce que la valeur vient des prestataires externes :
- audit RGAA externe (cabinet d'a11y, ~3 semaines après contractualisation) ;
- audit sécurité externe (pentest léger, ~2 semaines) ;
- finalisation de la revue JOSS (commencée en A12, ~12 semaines cumulées au total).
A15 est purement coordination + intégration des retours.
Items de l'audit résolus
Validation finale de B-9, B-10, M-9 (audit RGAA externe), B-7, B-11 (audit sécurité externe), B-4, B-5 (acceptation JOSS).
Livrables concrets
- Sélection et contractualisation d'un cabinet RGAA (par ex. Access42, Atalan, Tanaguru en France ; Tetralogical au UK ; ou service interne BnF si disponible). Cahier des charges : audit WCAG 2.1 AA sur le rapport HTML demo + l'interface web. Livrable attendu : rapport d'audit + déclaration de conformité signée.
- Sélection et contractualisation d'un audit sécurité (par ex. Synacktiv, Ambionics, ou équivalent BL-side). Cahier des charges : pentest application web (focus authentification/CSRF, file upload, injection, RCE), revue de la chaîne de build (CI scanners + image Docker).
ACCESSIBILITY.md: intégrer les retours RGAA, supprimer la mention « audit externe en cours ».SECURITY.md: intégrer les retours pentest, ajouter la date de prochain réaudit (annuel).- Réponse aux reviewers JOSS : un PR par retour majeur, intégré au paper. Tour de rév normalement court car le draft a été préparé rigoureusement en A12.
docs/audits/external-audits-2026/(nouveau dossier) :rgaa-audit-2026-MM.pdf(rapport scanné/PDF du cabinet) ;pentest-2026-MM.md(résumé public, le rapport complet reste confidentiel) ;joss-review-correspondence.md(résumé public des échanges).
- Annonce publique sur la home web et le README v2.1 : badges « WCAG 2.1 AA conformité totale » et « JOSS published » avec liens.
Critères d'acceptation
- Audit RGAA externe rapporte ≥ 95 % de critères AA conformes
(le 5 % résiduel listé en dérogations dans
ACCESSIBILITY.md). - Audit sécurité externe ne signale aucune vulnérabilité de sévérité HIGH / CRITICAL.
- Le papier JOSS est accepté (
accepted by JOSSissue closed). - Le DOI JOSS est ajouté au CITATION.cff comme
preferred-citation.
Risques et mitigation
- Risque : audit RGAA trouve un bloqueur niveau A imprévu (par ex. une matrice de confusion Unicode jugée non navigable au clavier). Mitigation : sprint A15-bis dédié à la remédiation, planifié en buffer fin de Phase 8.
- Risque : reviewers JOSS demandent une fonctionnalité scientifique manquante (par ex. métrique nouvelle). Mitigation : sprint A12-bis (cf. A12 risques), buffer Phase 8.
- Risque : prestataire externe indisponible. Mitigation : démarcher 2 candidats par audit et choisir le premier qui répond dans le délai.
12. Matrice de couverture
Chacun des 59 items identifiés par l'audit est mappé à un sprint. Cette matrice est la garantie d'exhaustivité : si un item n'apparaît pas ici, le plan a un trou.
Bloqueurs (13/13 couverts)
| ID | Item | Sprint | Livrable spécifique |
|---|---|---|---|
| B-1 | Violation Cercle 2→3 (statistics.py:861) |
A3 | core/diff_utils.py créé, ré-export rétrocompat |
| B-2 | Violation Cercle 2→3 (difficulty.py:195) |
A3 | report/difficulty_render.py créé |
| B-3 | 3 except Exception: pass importers |
A3 | logger.warning + Fact IMPORTER_FALLBACK_TRIGGERED |
| B-4 | Pas de CITATION.cff / JOSS | A12 | CITATION.cff + Zenodo DOI + paper.md |
| B-5 | Méthodes statistiques non citées | A12 | En-tête module statistics.py + :references: par fonction |
| B-6 | Profils normalisation non tracés | A12 | docs/normalization-specs.md + commentaires inline |
| B-7 | Aucun scanner sécurité CI | A1 | Job security (bandit + pip-audit + trivy) |
| B-8 | Pas de --cov-fail-under |
A1 | --cov-fail-under=85 dans ci.yml |
| B-9 | Canvas Chart.js inaccessibles | A6 | aria-label + <table> jumelle + bouton |
| B-10 | Pas de skip-to-content | A6 | <a class="skip-link"> dans _header.html |
| B-11 | Pas de CSRF | A4 | Middleware csrf_middleware + tests |
| B-12 | SPECS désynchronisé | A14 | Refonte intégrale v2.0 |
| B-13 | Markdown taglines README cassé | A13 | Refonte README intégrale |
Majors (28/28 couverts)
| ID | Item | Sprint | Livrable spécifique |
|---|---|---|---|
| M-1 | Lock file absent | A8 | requirements.lock + requirements-runtime.lock via uv |
| M-2 | Image Docker non épinglée | A8 | FROM python:3.11.10-slim@sha256:… |
| M-3 | /health absent |
A4 | GET /health dans system.py |
| M-4 | Pas de mypy en CI | A1 | Job typecheck (core/ strict, ailleurs lax) |
| M-5 | Pas de release PyPI | A9 | release.yml + setuptools_scm + OIDC |
| M-6 | Image conteneur non publiée | A9 | Push ghcr.io multi-arch dans release.yml |
| M-7 | Pas de guide déploiement institutionnel | A11 | docs/operations/deployment-institutional.md |
| M-8 | Pas de politique RGPD | A11 | docs/operations/data-retention-rgpd.md + purge auto |
| M-9 | Pas de déclaration a11y | A7 + A11 + A15 | ACCESSIBILITY.md (draft A7, draft validé A11, finalisé A15) |
| M-10 | Pas de COI | A10 | Section dans GOVERNANCE.md + paper.md |
| M-11 | Pas de CODEOWNERS / governance | A10 | .github/CODEOWNERS + GOVERNANCE.md + CODE_OF_CONDUCT.md |
| M-12 | Snapshots reproductibilité sous-doc | A8 | docs/reproducibility-snapshots.md |
| M-13 | Tests concurrence runner+SSE | A5 | test_runner_concurrency.py + test_sqlite_concurrent_writes.py |
| M-14 | Pas d'anti-régression CER | A5 | perf_regression.yml cron hebdo + corpus de référence |
| M-15 | Pas de timeout pytest | A1 | [tool.pytest.ini_options] timeout = 300 |
| M-16 | Pas de lazy loading rapports | A5 | Option --lazy-images dans picarones report |
| M-17 | Doc déséquilibrée FR/EN | A11 | 5 fichiers traduits + test_translation_parity.py |
| M-18 | .dockerignore + .env.example |
A8 | Deux fichiers créés |
| M-19 | Compteur tests faux × 3 | A13 | Génération auto via gen_readme_tables.py |
| M-20 | Roadmap arrêtée Sprint 22 | A13 | Roadmap condensée par phase + lien CHANGELOG |
| M-21 | Known Issues obsolète | A13 | Section supprimée + redirection docs/audits/ |
| M-22 | Project Structure trompeuse | A13 | gen_project_structure.py + insertion balisée |
| M-23 | Kraken / customYAML annoncés | A13 | Suppression du tableau (statut documenté en SPECS A14) |
| M-24 | Variables AWS_* sans adapter |
A13 | Suppression des 3 lignes |
| M-25 | CLI sous-documentée | A13 | Génération auto via picarones --help |
| M-26 | API web sous-documentée | A13 | Génération auto via app.openapi() |
| M-27 | Métriques sous-vendues | A13 | 3 sous-sections + lien docs/views.md |
| M-28 | Sections rapport sous-vendues | A13 | Liste structurée 5 familles + screenshot |
Mineurs (18/18 couverts)
| ID | Item | Sprint | Livrable spécifique |
|---|---|---|---|
| m-1 | _app.js:1087 chaîne FR hardcodée |
A7 | I18N.no_anchor_data |
| m-2 | _app.js:1049 chaîne FR hardcodée |
A7 | I18N.no_gini |
| m-3 | Bouton « Réinitialiser » sans i18n | A6 | i18n.reset_all |
| m-4 | Tableaux sans scope="col" |
A6 | Audit + ajout sur ~12 tables |
| m-5 | Palette non daltonien-friendly | A7 | Palette Okabe-Ito + toggle |
| m-6 | Nombres non localisés | A7 | toLocaleString(I18N.locale) |
| m-7 | Pre-commit non rejoué en CI | A1 | .github/workflows/precommit.yml |
| m-8 | Pas de Python 3.13 | A1 | Matrice ["3.11", "3.12", "3.13"] |
| m-9 | API stability defaults | A1 | test_public_api_signatures.py |
| m-10 | Tests cloud OCR sans HTTP errors | A5 | test_cloud_http_errors.py (12×3 cas) |
| m-11 | Versionnement testdata | A8 | tests/.testdata/VERSION.yaml |
| m-12 | Numérotation sprint des tests | A2 | Helper conftest.py informatif |
| m-13 | requirements.txt divergent |
A8 | Alias -r requirements.lock ou suppression |
| m-14 | pricing.yaml staleness |
A8 | valid_until: + Fact PRICING_STALENESS_WARNING |
| m-15 | PyInstaller hiddenimports manuels |
A9 | collect_all auto + job CI build-exe |
| m-16 | Extras placeholder vides | A9 | Suppression historical = [] / importers = [] |
| m-17 | Test mesures importe Cercle 3 | A3 | Déplacement vers tests/integration/ |
| m-18 | Petits items README (copyright, etc.) | A13 | Refonte intégrale couvre |
| §9.3 | Compteurs tests divergents 3 docs | A13 | Source unique CLAUDE.md, vérifié par A2 |
Total : 13 + 28 + 18 + 1 = 60 entrées (les 59 items + §9.3 bookkeeping). Aucune entrée orpheline.
13. Risques transverses et stratégies de contingence
Risque R-1 — JOSS reviewer demande des changements méthodologiques de fond
Probabilité : moyenne (JOSS est généralement constructif mais exigeant sur la rigueur statistique). Impact : peut décaler la publication de 4 à 8 semaines supplémentaires. Mitigation :
- A12 prépare un draft défendable (refs primaires citées, tests
méthodologiques rigoureux comme
test_friedman_canonical, etc.) ; - Sprint A12-bis dédié et planifié en buffer Phase 8 (sem. 12+) ;
- Si refus JOSS, fallback arXiv preprint (sans peer-review formel mais citable et DOI-stable).
Risque R-2 — Audit RGAA externe trouve un bloqueur niveau A imprévu
Probabilité : faible si A6 est rigoureux (pytest + axe-core auto). Impact : sprint A15-bis nécessaire, retard ~2 semaines. Mitigation :
- A6 inclut un audit
axe-coreavant la fin du sprint, pas seulement à la livraison ; - A15 prévoit explicitement un buffer A15-bis ;
- Si critique : démarche de conformité partielle documentée avec dérogations argumentées (acceptable RGAA).
Risque R-3 — Refactor architecture casse un test caché ou un
consommateur externe
Probabilité : faible (lint passe, 3 356 tests, ré-exports rétrocompat). Impact : régression silencieuse découverte tard. Mitigation :
- A3 maintient des ré-exports rétrocompat avec
DeprecationWarning; test_circle_dependencies.pydétecte toute nouvelle violation immédiatement ;- Suppression effective des ré-exports planifiée 2 versions plus tard (v1.3.0 minimum), pas dans la même release.
Risque R-4 — Indisponibilité d'une dépendance cloud (HF Datasets,
Gallica, MUFI registry)
Probabilité : faible. Impact : tests d'importation cassent en CI. Mitigation :
- Tous les imports de corpus en CI sont mockés (vérifier en A1) ;
- Le test de cohérence
docs/normalization-specs.mdarchive les versions des spécifications sources dans le repo, pas via lien externe live.
Risque R-5 — Le mainteneur unique manque de bande passante
Probabilité : forte sur projet académique mono-personne. Impact : glissement calendrier. Mitigation :
- Le plan est séquencé pour qu'un sprint soit fermable en isolation — abandon partiel possible sur les phases tardives sans casser les phases livrées (par ex. A15 peut s'étaler tant que les audits externes tournent) ;
- Documentation A11 + GOVERNANCE A10 préparent l'arrivée de contributeurs domain-experts (paléographe, archiviste, dev EN natif) — recrutement ouvert dès Phase 5 ;
- En cas de blocage long, annoncer un « release v1.1 minimal institutional » après A11 (Phase 5 close) — ce serait déjà un saut qualitatif majeur sur l'état actuel.
Risque R-6 — Découverte d'un bug de fond lors d'un audit externe
Probabilité : faible (la base de tests est solide). Impact : hotfix sprint nécessaire. Mitigation :
- A1 (scanners + cov-fail-under) attrape la majorité des cas silencieux ;
- A8 (snapshots reproductibles) permet de rejouer un benchmark pré-bug pour confirmer la régression ;
- Politique de hotfix sécurité 72 h documentée en A10.
14. Définition de « niveau BnF / British Library atteint »
Le projet est considéré au niveau institutionnel cible quand toutes les conditions suivantes sont satisfaites simultanément. Cette liste est la Definition of Done du présent plan.
Côté code
- Audit interne
institutional-readiness-2026-05.md: 0 BLOCKER ouvert, 0 MAJOR ouvert. -
pytest tests/: 100 % vert sur Linux + macOS + Windows × Python 3.11 + 3.12 + 3.13. -
ruff check0 erreur. -
mypy picarones/core/ --strict0 erreur. -
bandit -r picarones/ -ll0 issue HIGH/CRITICAL. -
pip-audit --strict0 vulnérabilité ouverte. - Couverture ≥ 85 % (
--cov-fail-under=85actif et tenu). -
tests/core/test_circle_dependencies.py0 violation. -
tests/docs/test_readme_consistency.py0 divergence. -
tests/docs/test_specs_consistency.py0 divergence.
Côté distribution
-
pip install picaronesfonctionne depuis PyPI sur 3 OS. -
docker pull ghcr.io/maribakulj/picarones:<version>retourne une image immutable épinglée. -
requirements.lockversionné et utilisé en prod (Dockerfile). - Release GitHub
v1.x.yautomatique avec corps depuis CHANGELOG.
Côté reproductibilité
- Un benchmark rejoué à 6 mois d'intervalle avec mêmes lock file + digest Docker + commit picarones produit un rapport bit-à-bit identique (ou avec diff explicable).
-
docs/reproducibility-snapshots.mddocumente la procédure end-to-end.
Côté communication scientifique
-
CITATION.cffvalide (cffconvert --validate), bouton « Cite this repository » fonctionnel sur GitHub. - DOI Zenodo attribué et présent dans le README.
- Papier JOSS : statut
accepted+ DOI JOSS dans CITATION.cff commepreferred-citation. (ou, en fallback, arXiv preprint avec DOI alternatif). -
docs/normalization-specs.md: chaque profil a sa source citée avec date d'extraction.
Côté gouvernance et conformité
-
CODEOWNERS,GOVERNANCE.md,CODE_OF_CONDUCT.md,SECURITY.md,LICENSE,ACCESSIBILITY.md,CITATION.cfftous présents et non-vides (test CItest_governance_files_present). - Audit RGAA externe : conformité WCAG 2.1 niveau AA ≥ 95 %, le résiduel listé en dérogations argumentées.
- Audit sécurité externe : 0 finding HIGH/CRITICAL.
- Politique RGPD documentée + purge auto fonctionnelle.
- Guide de déploiement institutionnel revu par au moins un DSI partenaire.
Côté documentation produit
- README < 400 lignes, markdown valide, badges verts, tableaux auto-générés depuis le code.
- SPECS.md v2 reflète strictement le code, contient une section « Limites assumées » non vide.
- Documentation utilisateur et 4 guides développeur traduits en anglais avec parité de structure validée par test.
- CHANGELOG.md à jour, format Keep-a-Changelog respecté.
Critère méta — sécabilité
- Le repo peut être récupéré et son rapport demo généré sur une
machine vierge en moins de 5 minutes
(
git clone && pip install -e . && picarones demo), preuve par job CI dédiétest_quickstart.
Quand ces 30 cases sont cochées, l'institution peut adopter Picarones comme outil de référence interne et les chercheurs peuvent le citer dans des publications scientifiques avec garantie de stabilité, reproductibilité, et conformité.
Plan rédigé en réponse à l'audit
institutional-readiness-2026-05.md
sur la branche claude/audit-institutional-readiness-8Cw4w.
Période de mise en œuvre suggérée : mai–juillet 2026.