Spaces:
Running
Running
Claude commited on
docs(architecture): manifeste 3 cercles avec règle de dépendance stricte
Browse filesRéécriture du manifeste pour clarifier la frontière entre cercles :
- Cercle 1 (`core/`) : abstractions pures, pas de logique métier.
Liste exhaustive : modules, corpus, results, metric_registry,
metric_hooks, pipeline, facts.
- Cercle 2 (`measurements/`, `engines/`, `llm/`, `pipelines/`,
`modules/`) : implémentations officielles distribuées.
- Cercle 3 (`extras/`, `report/`, `cli/`, `web/`) : extensions et
présentation.
Règle de dépendance : les flèches d'import vont uniquement de
l'extérieur vers l'intérieur. Pas de shim — un module a un seul
emplacement physique.
https://claude.ai/code/session_01Hsd7kL8yeCbXn1mA7GQK9L
- docs/architecture.md +132 -170
docs/architecture.md
CHANGED
|
@@ -1,181 +1,143 @@
|
|
| 1 |
-
# Architecture Picarones —
|
| 2 |
-
|
| 3 |
-
Ce document décrit l'architecture du projet **après les chantiers 1-5**
|
| 4 |
-
du plan d'évolution post-Sprint 97. Il complète le `CLAUDE.md`
|
| 5 |
-
historique (qui reste l'historique chronologique des sprints) en
|
| 6 |
-
donnant une **vue thématique** de l'organisation actuelle du code.
|
| 7 |
-
|
| 8 |
-
## Vue d'ensemble
|
| 9 |
|
| 10 |
Picarones est un **banc d'essai** pour pipelines OCR/HTR sur documents
|
| 11 |
-
patrimoniaux. Le
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
Google Vision, Azure Document Intelligence).
|
| 15 |
-
2. **Adapters LLM** (4 providers : OpenAI, Anthropic, Mistral, Ollama)
|
| 16 |
-
pour les pipelines OCR+LLM (zero-shot, post-correction…).
|
| 17 |
-
3. **Modules de référence** (chantier 1) : `TextToAltoMonoRegion`
|
| 18 |
-
pour démonstrer l'extension `BaseModule` (image+texte → ALTO).
|
| 19 |
-
4. **Runner** orchestrateur multi-moteurs avec parallélisation
|
| 20 |
-
ProcessPool (CPU-bound) ou ThreadPool (IO-bound).
|
| 21 |
-
5. **Rapport HTML** auto-suffisant (Chart.js embarqué) avec 5+ vues
|
| 22 |
-
thématiques composables.
|
| 23 |
-
6. **Interface web FastAPI** + **CLI Click** (15 sous-commandes).
|
| 24 |
-
|
| 25 |
-
## Structure des packages
|
| 26 |
|
| 27 |
```
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
│ ├── _history.py history (consultation SQLite)
|
| 36 |
-
│ └── _robustness.py analyse robustesse
|
| 37 |
-
│
|
| 38 |
-
├── core/
|
| 39 |
-
│ ├── corpus.py Document, Corpus, GTLevel multi-niveaux
|
| 40 |
-
│ ├── modules.py BaseModule + ArtifactType (Sprint 33)
|
| 41 |
-
│ ├── metric_registry.py Registre typé (Sprint 34)
|
| 42 |
-
│ ├── builtin_metrics.py Métriques scalaires natives
|
| 43 |
-
│ ├── alto_metrics.py (chantier 1) Métriques (ALTO, ALTO)
|
| 44 |
-
│ ├── metric_hooks.py (chantier 2) Profils + registre de hooks
|
| 45 |
-
│ ├── builtin_hooks.py (chantier 2) 12 hooks doc + 12 agrégateurs
|
| 46 |
-
│ ├── runner.py Orchestrateur multi-moteurs
|
| 47 |
-
│ ├── pipeline_runner.py Banc d'essai de pipelines composées
|
| 48 |
-
│ ├── narrative/
|
| 49 |
-
│ │ ├── facts.py Modèle Fact + 18+ FactType
|
| 50 |
-
│ │ ├── registry.py Registre déclaratif
|
| 51 |
-
│ │ ├── arbiter.py Arbitrage des Facts (anti-redondance)
|
| 52 |
-
│ │ ├── renderer.py Rendu i18n YAML → str.format_map
|
| 53 |
-
│ │ └── detectors/ (chantier 5) 6 sous-modules thématiques
|
| 54 |
-
│ │ ├── ranking.py
|
| 55 |
-
│ │ ├── pareto.py
|
| 56 |
-
│ │ ├── stratum.py
|
| 57 |
-
│ │ ├── quality.py
|
| 58 |
-
│ │ ├── history.py
|
| 59 |
-
│ │ └── ensemble.py
|
| 60 |
-
│ └── ... (~60 modules métriques philologiques, statistiques, etc.)
|
| 61 |
-
│
|
| 62 |
-
├── engines/
|
| 63 |
-
│ ├── base.py (chantier 1) BaseOCREngine factorisée
|
| 64 |
-
│ │ avec hooks _run_with_native +
|
| 65 |
-
│ │ _extract_raw_confidences
|
| 66 |
-
│ ├── tesseract.py
|
| 67 |
-
│ ├── pero_ocr.py
|
| 68 |
-
│ ├── mistral_ocr.py
|
| 69 |
-
│ ├── google_vision.py
|
| 70 |
-
│ └── azure_doc_intel.py
|
| 71 |
-
│
|
| 72 |
-
├── llm/
|
| 73 |
-
│ ├── base.py (chantier 4) Helpers normalize_llm_content +
|
| 74 |
-
│ │ log_http_error factorisés
|
| 75 |
-
│ ├── mistral_adapter.py
|
| 76 |
-
│ ├── openai_adapter.py
|
| 77 |
-
│ ├── anthropic_adapter.py
|
| 78 |
-
│ └── ollama_adapter.py
|
| 79 |
-
│
|
| 80 |
-
├── modules/ (chantier 1) Modules BaseModule de référence
|
| 81 |
-
│ └── alto_text_to_mono_region.py
|
| 82 |
-
│
|
| 83 |
-
├── importers/
|
| 84 |
-
│ ├── _http.py (chantier 4) Helpers HTTP factorisés
|
| 85 |
-
│ ├── iiif.py
|
| 86 |
-
│ ├── htr_united.py
|
| 87 |
-
│ ├── gallica.py (chantier 4) Délègue à _http
|
| 88 |
-
│ ├── huggingface.py
|
| 89 |
-
│ └── escriptorium.py
|
| 90 |
-
│
|
| 91 |
-
├── pipelines/ Pipelines OCR+LLM (zero_shot, post_correction)
|
| 92 |
-
│
|
| 93 |
-
├── prompts/ 8 templates .txt FR+EN
|
| 94 |
-
│
|
| 95 |
-
├── report/ Rapport HTML
|
| 96 |
-
│ ├── generator.py Orchestrateur Jinja2
|
| 97 |
-
│ ├── views/ (chantier 3) 5 vues thématiques composables
|
| 98 |
-
│ │ ├── economics.py throughput + cost projection
|
| 99 |
-
│ │ ├── advanced_taxonomy.py taxonomy comparison + lexical_modernization
|
| 100 |
-
│ │ ├── diagnostics.py levers + image_predictive + baseline + worst_lines
|
| 101 |
-
│ │ ├── pipeline.py DAG + error_absorption + incremental + audit
|
| 102 |
-
│ │ └── robustness.py robustness projection
|
| 103 |
-
│ ├── *_render.py 26 renderers atomiques
|
| 104 |
-
│ ├── templates/ Jinja2 (10 vues HTML + partials)
|
| 105 |
-
│ ├── i18n/{fr,en}.json 410 clés
|
| 106 |
-
│ ├── glossary/ 25 entrées YAML bilingues
|
| 107 |
-
│ └── vendor/ Chart.js embarqué
|
| 108 |
-
│
|
| 109 |
-
└── web/
|
| 110 |
-
├── app.py FastAPI (2065 lignes — découpage reporté)
|
| 111 |
-
├── security.py
|
| 112 |
-
├── templates/, static/
|
| 113 |
-
└── ...
|
| 114 |
```
|
| 115 |
|
| 116 |
-
##
|
| 117 |
|
| 118 |
-
|
|
|
|
| 119 |
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
↓ (profil active 12 hooks doc + 12 agrégateurs via builtin_hooks)
|
| 130 |
-
ProcessPoolExecutor / ThreadPoolExecutor
|
| 131 |
-
↓
|
| 132 |
-
_compute_document_result(doc, profile)
|
| 133 |
-
↓ (run_document_hooks itère sur les hooks actifs du profil)
|
| 134 |
-
DocumentResult (avec confusion, taxonomy, calibration, …)
|
| 135 |
-
↓ (run_corpus_aggregators)
|
| 136 |
-
EngineReport (avec aggregated_*)
|
| 137 |
-
↓
|
| 138 |
-
BenchmarkResult
|
| 139 |
-
↓
|
| 140 |
-
ReportGenerator.generate()
|
| 141 |
-
↓ (build_economics_view_html + build_advanced_taxonomy_view_html
|
| 142 |
-
+ build_diagnostics_view_html selon profil)
|
| 143 |
-
report.html (autonome, ~450 Ko)
|
| 144 |
-
```
|
| 145 |
|
| 146 |
-
|
|
|
|
| 147 |
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
|
| 162 |
-
|
| 163 |
-
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 164 |
|
| 165 |
-
|
| 166 |
-
|
| 167 |
-
- [`docs/profiles.md`](profiles.md) — les 7 profils de calcul du chantier 2.
|
| 168 |
-
- [`docs/cli-workflows.md`](cli-workflows.md) — les 15 commandes CLI.
|
| 169 |
-
- [`docs/views.md`](views.md) — les vues HTML disponibles dans le rapport.
|
| 170 |
-
- [`docs/user/reading-a-report.md`](user/reading-a-report.md) — guide
|
| 171 |
-
utilisateur pour lire un rapport.
|
| 172 |
-
- [`docs/user/writing-a-pipeline-module.md`](user/writing-a-pipeline-module.md)
|
| 173 |
-
— guide pour brancher un module tiers (`BaseModule`).
|
| 174 |
-
- [`docs/developer/narrative-engine.md`](developer/narrative-engine.md)
|
| 175 |
-
— détecteurs narratifs : architecture, comment en ajouter.
|
| 176 |
-
- [`docs/developer/module-policy.md`](developer/module-policy.md) — manifest
|
| 177 |
-
+ audit pour modules contribués (Sprint 97).
|
| 178 |
-
- [`docs/case-studies/`](case-studies/) — 2 cas d'école (registres
|
| 179 |
-
paroissiaux, édition critique).
|
| 180 |
-
- [`docs/roadmap/evolution-2026.md`](roadmap/evolution-2026.md) — plan
|
| 181 |
-
d'évolution (axe A métrique + axe B pipelines composées).
|
|
|
|
| 1 |
+
# Architecture Picarones — manifeste
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
|
| 3 |
Picarones est un **banc d'essai** pour pipelines OCR/HTR sur documents
|
| 4 |
+
patrimoniaux. Le code est organisé en **3 cercles concentriques** avec
|
| 5 |
+
une règle de dépendance stricte : les flèches d'import vont uniquement
|
| 6 |
+
de l'extérieur vers l'intérieur.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
|
| 8 |
```
|
| 9 |
+
Cercle 3 (extras, report, cli, web)
|
| 10 |
+
│
|
| 11 |
+
▼
|
| 12 |
+
Cercle 2 (measurements, engines, llm, pipelines, modules)
|
| 13 |
+
│
|
| 14 |
+
▼
|
| 15 |
+
Cercle 1 (core)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
```
|
| 17 |
|
| 18 |
+
## Cercle 1 — `picarones/core/` : abstractions de domaine
|
| 19 |
|
| 20 |
+
Pas de logique métier, pas d'I/O. Uniquement des **contrats** que les
|
| 21 |
+
cercles supérieurs implémentent.
|
| 22 |
|
| 23 |
+
| Module | Contenu |
|
| 24 |
+
|---|---|
|
| 25 |
+
| `modules.py` | `BaseModule`, `ArtifactType`, `validate_inputs`/`validate_outputs` |
|
| 26 |
+
| `corpus.py` | `Document`, `Corpus`, `GTLevel`, payloads typés (`TextGT`, `AltoGT`, `PageGT`, `EntitiesGT`, `ReadingOrderGT`) |
|
| 27 |
+
| `results.py` | `DocumentResult`, `EngineReport`, `BenchmarkResult` |
|
| 28 |
+
| `metric_registry.py` | `MetricSpec`, `register_metric`, `select_metrics`, `compute_at_junction` |
|
| 29 |
+
| `metric_hooks.py` | `register_document_metric`, `register_corpus_aggregator`, profils de calcul |
|
| 30 |
+
| `pipeline.py` | `PipelineRunner`, `PipelineSpec`, `PipelineStep` (DAG de modules) |
|
| 31 |
+
| `facts.py` | `Fact`, `FactType`, `FactImportance`, `DetectorRegistry` |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 32 |
|
| 33 |
+
**Règle** : un module du cercle 1 peut importer un autre module du
|
| 34 |
+
cercle 1. Il ne peut **rien** importer des cercles 2 ou 3.
|
| 35 |
|
| 36 |
+
## Cercle 2 — implémentations officielles
|
| 37 |
+
|
| 38 |
+
Les implémentations distribuées par défaut dans le package `picarones`.
|
| 39 |
+
|
| 40 |
+
### `picarones/measurements/` — métriques (~50 modules)
|
| 41 |
+
|
| 42 |
+
| Catégorie | Modules |
|
| 43 |
+
|---|---|
|
| 44 |
+
| Coeur | `metrics.py`, `statistics.py`, `runner.py`, `builtin_hooks.py`, `builtin_metrics.py`, `normalization.py` |
|
| 45 |
+
| Erreurs | `confusion.py`, `taxonomy.py`, `taxonomy_comparison.py`, `taxonomy_cooccurrence.py`, `taxonomy_intra_doc.py` |
|
| 46 |
+
| Lignes/structure | `line_metrics.py`, `structure.py`, `worst_lines.py`, `char_scores.py` |
|
| 47 |
+
| Calibration/fiabilité | `calibration.py`, `reliability.py`, `hallucination.py` |
|
| 48 |
+
| Image | `image_quality.py`, `image_predictive.py`, `difficulty.py` |
|
| 49 |
+
| Robustesse | `robustness.py`, `robustness_projection.py` |
|
| 50 |
+
| Inter-moteurs | `inter_engine.py`, `specialization.py` |
|
| 51 |
+
| Statistique avancée | `baseline_comparison.py`, `longitudinal.py`, `incremental_comparison.py` |
|
| 52 |
+
| Contenu | `searchability.py`, `numerical_sequences.py`, `rare_tokens.py`, `readability.py` |
|
| 53 |
+
| Structure ALTO | `layout.py`, `reading_order.py`, `ner.py`, `ner_backends.py`, `error_absorption.py` |
|
| 54 |
+
| Économie | `cost_projection.py`, `marginal_cost.py`, `pricing.py`, `throughput.py` |
|
| 55 |
+
| Philologie historique | `mufi.py`, `abbreviations.py`, `unicode_blocks.py`, `early_modern_typography.py`, `modern_archives.py`, `roman_numerals.py`, `lexical_modernization.py`, `philological_runner.py` |
|
| 56 |
+
| Pipelines composées | `pipeline_benchmark.py`, `pipeline_comparison.py`, `pipeline_spec_loader.py`, `alto_metrics.py` |
|
| 57 |
+
| Divers | `equivalence_profile.py`, `levers.py`, `module_policy.py`, `history.py` |
|
| 58 |
+
| Runners adaptifs | `readability_runner.py`, `searchability_runner.py`, `numerical_sequences_runner.py` |
|
| 59 |
+
| Narratif | `narrative/` (arbiter, renderer, registry, 18 détecteurs en 6 familles) |
|
| 60 |
+
|
| 61 |
+
### `picarones/engines/` — adapters OCR (5)
|
| 62 |
+
|
| 63 |
+
`tesseract.py`, `pero_ocr.py`, `mistral_ocr.py`, `google_vision.py`,
|
| 64 |
+
`azure_doc_intel.py`. Tous héritent de `picarones.core.engine.BaseOCREngine`
|
| 65 |
+
(qui vit dans `engines/base.py` pour la lisibilité).
|
| 66 |
+
|
| 67 |
+
### `picarones/llm/` — adapters LLM (4)
|
| 68 |
+
|
| 69 |
+
`mistral_adapter.py`, `openai_adapter.py`, `anthropic_adapter.py`,
|
| 70 |
+
`ollama_adapter.py`. Interface commune dans `base.py`.
|
| 71 |
+
|
| 72 |
+
### `picarones/pipelines/` — pipelines OCR+LLM intégrés
|
| 73 |
+
|
| 74 |
+
`base.py` (`OCRLLMPipeline`, qui hérite de `BaseOCREngine`),
|
| 75 |
+
`over_normalization.py`.
|
| 76 |
+
|
| 77 |
+
### `picarones/modules/` — modules `BaseModule` officiels
|
| 78 |
+
|
| 79 |
+
Démonstrateurs qui prouvent l'axe B du plan d'évolution :
|
| 80 |
+
`alto_text_to_mono_region.py`.
|
| 81 |
+
|
| 82 |
+
## Cercle 3 — extensions et présentation
|
| 83 |
+
|
| 84 |
+
### `picarones/extras/importers/` — connecteurs corpus
|
| 85 |
+
|
| 86 |
+
`iiif.py`, `gallica.py`, `htr_united.py`, `huggingface.py`,
|
| 87 |
+
`escriptorium.py`, `_http.py`. Plugins pluggable, certains expérimentaux.
|
| 88 |
+
|
| 89 |
+
### `picarones/report/` — rendu HTML
|
| 90 |
+
|
| 91 |
+
| Sous-dossier | Contenu |
|
| 92 |
+
|---|---|
|
| 93 |
+
| `generator.py` | Orchestration Jinja2 |
|
| 94 |
+
| `views/` | 5 vues thématiques (economics, advanced_taxonomy, diagnostics, pipeline, robustness) |
|
| 95 |
+
| `templates/` | Jinja2 (base, header, footer, vues, partials) |
|
| 96 |
+
| `i18n/` | FR/EN |
|
| 97 |
+
| `glossary/` | 25 entrées bilingues |
|
| 98 |
+
| `vendor/` | Chart.js |
|
| 99 |
+
| `*_render.py` | ~22 renderers (calibration, NER, Pareto, Sankey, etc.) |
|
| 100 |
+
|
| 101 |
+
Pas de sous-dossier `extras/render/` — tout le rendu est ici.
|
| 102 |
+
|
| 103 |
+
### `picarones/cli/` — Click (7 fichiers)
|
| 104 |
+
|
| 105 |
+
Point d'entrée `picarones.cli:cli` (référencé dans `pyproject.toml`).
|
| 106 |
+
15 sous-commandes : `run`, `diagnose`, `economics`, `edition`,
|
| 107 |
+
`compare`, `metrics`, `engines`, `info`, `report`, `demo`, `serve`,
|
| 108 |
+
`history`, `robustness`, `pipeline run/compare`, `import`.
|
| 109 |
+
|
| 110 |
+
### `picarones/web/` — FastAPI
|
| 111 |
+
|
| 112 |
+
Interface web (`app.py`).
|
| 113 |
+
|
| 114 |
+
## Données
|
| 115 |
+
|
| 116 |
+
| Dossier | Rôle |
|
| 117 |
+
|---|---|
|
| 118 |
+
| `picarones/prompts/` | Prompts LLM versionnés (8 fichiers, FR + EN) |
|
| 119 |
+
| `picarones/data/` | Tables indicatives (pricing, etc.) |
|
| 120 |
+
| `picarones/fixtures.py` | Corpus de démonstration |
|
| 121 |
+
|
| 122 |
+
## Règles de migration
|
| 123 |
+
|
| 124 |
+
1. **Pas de shim** : un module a un seul emplacement physique. Les
|
| 125 |
+
imports pointent directement vers la vraie source.
|
| 126 |
+
2. **Pas de double API** : une fonction a un seul nom canonique. Les
|
| 127 |
+
alias historiques sont supprimés et les tests mis à jour.
|
| 128 |
+
3. **Frontières strictes** : si un module Y du cercle N importe le
|
| 129 |
+
module X, alors le cercle de X est ≤ N. Une exception
|
| 130 |
+
pragmatique : `engines/base.py` est conceptuellement cercle 1
|
| 131 |
+
mais physiquement dans `engines/` pour rester avec ses
|
| 132 |
+
implémentations.
|
| 133 |
+
4. **Les dépendances optionnelles** (`scipy`, `spacy`, etc.) sont
|
| 134 |
+
gérées par try/except à l'import — pas par shim.
|
| 135 |
+
|
| 136 |
+
## Tests
|
| 137 |
+
|
| 138 |
+
Organisés par cercle : `tests/core/`, `tests/measurements/`,
|
| 139 |
+
`tests/engines/`, `tests/extras/`, `tests/report/`,
|
| 140 |
+
`tests/integration/` (tests E2E croisant plusieurs cercles).
|
| 141 |
|
| 142 |
+
Un test du cercle N **n'importe pas** les implémentations des
|
| 143 |
+
cercles > N (sauf `tests/integration/`).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|