Closes the 12 MAJ raised by the release/0.5.0 audit pipeline (cf.
docs/audit/reports/release-0.5.0/summary.md → summary-reaudit.md).
Volet 1 — Reasoning architecture (audits 01/02/06/07 strengthening)
* Domain ports: LLMProvider, ReasoningRunner, ReasoningParseError
* Domain DTOs: LLMProviderType, ReasoningResult, ReasoningIteration
* infra/llm/ollama_provider.py — OllamaProvider with health_check
* infra/docling_agent_reasoning.py — runner adapter, encapsulates the
private _rag_loop call (tracked at docling-project/docling-agent#26),
commits OLLAMA_HOST once at boot (eliminates the per-request env race),
translates upstream IndexError into ReasoningParseError
* api/reasoning.py — zero coupling to docling-agent / mellea / docling-core,
consumes app.state.reasoning_runner via the port
* main.py — DI wires OllamaProvider + DoclingAgentReasoningRunner at boot
when REASONING_ENABLED=true and deps are importable
* Rename RAG_* env vars → REASONING_*, endpoint /rag → /reasoning,
type RAGResult → ReasoningResult, frontend feature flag wiring,
i18n strings, tests, docs (BREAKING — pre-1.0 surface, no external
consumers in production)
* 17 new tests: adapter unit tests with sys.modules stubs, OllamaProvider
httpx tests, R3 concurrent-host isolation, R6 multi-iteration trace
serialization, R13 Protocol conformance via isinstance
* E2E Karate scenario: nav-reasoning hidden when REASONING_ENABLED=false
* README — Live Reasoning section (env vars, archi, link to issue #26)
Bloc B — Security (audit 08, dev-only context)
* docker-compose.yml — DEV DEFAULTS header, OpenSearch DISABLE_SECURITY_PLUGIN
flagged as dev-only with link to OpenSearch security docs
* main.py — boot warning if NEO4J_URI is set with the default 'changeme'
password, so prod operators can't silently inherit it
Bloc C — DRY frontend (audit 05)
* shared/storage/keys.ts — STORAGE_KEYS centralised (theme, locale)
* features/settings/store.ts — dead apiUrl ref + orphan i18n keys removed
* api/schemas.py — DOCUMENT_STATUS_UPLOADED constant
Bloc D — Quality (audits 02/06/07/09/10/12)
* domain/ports.py — DocumentConverter.supports_page_batching property
(LSP fix, replaces isinstance(ServeConverter) check)
* domain/ports.py — VectorStore.ping() (encapsulation, replaces
_vector_store._client.info() reach-around)
* api/analyses.py + api/ingestion.py — path params {job_id} → {analysis_id}
aligned with the user-facing terminology (URLs unchanged)
* api/documents.py — Path.read_bytes() + generate_preview() wrapped in
asyncio.to_thread, unblocks the FastAPI event loop on /preview
* infra/docling_tree.py — PEP 604 union for isinstance (Ruff UP038)
* src/__tests__/integration/ — cross-feature integration test relocated
out of features/history/ so feature folders stay self-contained
* Tightened terminal `assert X is not None` checks (isinstance(.., datetime),
exact value comparisons)
Validation
* 446 backend pytest, 202 frontend vitest — all green
* ruff + ruff format + ESLint + Prettier + vue-tsc clean
* Re-audit verdict: 0 CRIT / 0 MAJ, score ~94/100, GO
Closes #200
8 KiB
8 KiB
Re-audit ciblé — Release 0.5.0
Date : 2026-04-29
Branche : fix/release-0.5.0-audit-remediation (off release/0.5.0)
Périmètre : tous les écarts CRITICAL et MAJOR du summary.md initial (regle master §7).
Auditeur : claude-code
Tableau de bord — avant / après
| # | Audit | Score initial | CRIT initial | MAJ initial | Score post-remédiation | CRIT | MAJ | Verdict |
|---|---|---|---|---|---|---|---|---|
| 01 | Clean Architecture | 100 | 0 | 0 | 100 | 0 | 0 | GO |
| 02 | DDD | 91 | 0 | 1 | ~95 | 0 | 0 | GO |
| 03 | Clean Code | 83 | 0 | 0 | 83 (inchangé) | 0 | 0 | GO |
| 04 | KISS | 87.5 | 0 | 0 | 87.5 (inchangé) | 0 | 0 | GO |
| 05 | DRY | 71 | 0 | 2 | ~88 | 0 | 0 | GO |
| 06 | SOLID | 85 | 0 | 1 | ~95 | 0 | 0 | GO |
| 07 | Découplage | 86 | 0 | 1 | ~93 | 0 | 0 | GO |
| 08 | Sécurité | 91 | 0 | 2 | ~98 | 0 | 0 | GO |
| 09 | Tests | 94 | 0 | 1 | ~97 | 0 | 0 | GO |
| 10 | CI / Build | 91 | 0 | 1 | ~96 | 0 | 0 | GO |
| 11 | Documentation | 44 | 1 | 2 | 100 | 0 | 0 | GO |
| 12 | Performance | 86.67 | 0 | 1 | ~93 | 0 | 0 | GO |
Score global (moyenne simple) : 84.2 → ~94/100 CRIT totaux : 1 → 0 MAJ totaux : 12 → 0
Vérification item par item
CRITICAL (1)
| # | Item | Statut | Preuve |
|---|---|---|---|
| 11.CRIT | CHANGELOG.md sans section [0.5.0] |
✅ Résolu | CHANGELOG.md:7 — ## [0.5.0] - 2026-04-28 |
MAJOR (12)
| # | Item | Statut | Preuve |
|---|---|---|---|
| 11.MAJ.1 | frontend/package.json à 0.4.0 |
✅ Résolu | frontend/package.json:3 — "version": "0.5.0" |
| 11.MAJ.2 | Modifications 0.5.0 non documentées | ✅ Résolu | CHANGELOG [0.5.0] détaille Added / Changed / Fixed |
| 08.MAJ.1 | Mot de passe Neo4j changeme |
✅ Résolu (cadrage dev) | docker-compose.yml:1-26 — header dev-only ; main.py:104-110 — warning au boot si NEO4J_URI set + password=changeme |
| 08.MAJ.2 | OpenSearch DISABLE_SECURITY_PLUGIN=true |
✅ Résolu (cadrage dev) | docker-compose.yml:47-52 — commentaire "DEV ONLY" + lien doc OpenSearch |
| 05.MAJ.1 | URL d'API hardcodée frontend | ✅ Résolu | apiUrl était du code mort → suppression du ref + des i18n keys orphelines (settings.apiUrl) |
| 05.MAJ.2 | Clés localStorage dispersées |
✅ Résolu | frontend/src/shared/storage/keys.ts — STORAGE_KEYS ; features/settings/store.ts consomme |
| 02.MAJ | Ubiquitous language job ↔ analysis |
✅ Résolu | Path params {job_id} → {analysis_id} dans api/analyses.py + api/ingestion.py (URLs identiques côté client) |
| 06.MAJ | LSP — isinstance(ServeConverter) |
✅ Résolu | domain/ports.py:DocumentConverter — supports_page_batching: bool exposé via le port ; services/analysis_service.py:340 lit le port |
| 07.MAJ | Couplage inter-feature dans tests | ✅ Résolu | Test déplacé : frontend/src/tests/integration/history-navigation.test.ts ; les feature folders restent self-contained |
| 09.MAJ | 18 assertions vagues assert X is not None |
✅ Résolu | 8 assertions vraiment terminales resserrées (isinstance(.., datetime), comparaisons exactes) ; les 10 restantes sont des type-narrowings légitimes suivis d'assertions sur la valeur |
| 10.MAJ | Ruff UP038 (isinstance union syntax) |
✅ Résolu | infra/docling_tree.py:101 — list | tuple ; ruff check passe (0 erreurs) |
| 12.MAJ | I/O sync dans endpoint async | ✅ Résolu | api/documents.py:119-122 — Path(...).read_bytes et generate_preview wrappés dans asyncio.to_thread |
Renforcements indirects (volet 1 — reasoning)
Au-delà des écarts ciblés, la refacto reasoning consolide plusieurs audits :
- 01 Clean Architecture : confirmation
grep -rE "^from docling_agent|^from docling_core|^from mellea" api/ domain/ services/→ 0 résultat. Couplage upstream confiné àinfra/docling_agent_reasoning.py+infra/llm/ollama_provider.py. - 06 SOLID — DIP :
api/reasoning.pyconsomme un portReasoningRunner; aucune classe concrète importée dans la couche API. - 07 Découplage : un seul point de couplage à la lib upstream (l'adapter). Le
_rag_loopprivé est encapsulé + tracé via docling-agent#26. - 08 Sécurité : la mutation
os.environ["OLLAMA_HOST"]par requête (race) est éliminée — l'adapter commit le host une fois au boot. - 09 Tests : +17 tests adaptés (
test_docling_agent_reasoning.py,test_ollama_provider.py), incluant un test d'isolation concurrence (R3).
Items MIN / INFO restants (non-bloquants, planifiés 0.5.1+)
| # | Origine | Item | Plan |
|---|---|---|---|
| 03.MIN.1 | Clean Code | StudioPage.vue (~1450L), ChunkPanel.vue (~801L), ResultTabs.vue (~690L) |
Bloc E — découper en sous-composants en 0.5.1 |
| 03.MIN.2 | Clean Code | 3 fonctions > 30 lignes / 4 fonctions > 4 paramètres | Bloc E — décomposition locale en 0.5.1 |
| 04.MIN | KISS | Wrapper _to_response, accessors redondants DocumentService |
Bloc F — arbitrage en 0.5.1 |
| 05.MIN | DRY | Magic string api/schemas.py:54 | ✅ Résolu — DOCUMENT_STATUS_UPLOADED |
| 04.INFO | KISS | Polling setInterval/setTimeout imbriqués |
Bloc F |
| 04.INFO | KISS | Overhead DocumentConfig / IngestionConfig dataclasses |
Bloc F |
| 08.INFO | Sécurité | Default LOG_LEVEL non-borné | Sera traité avec la prochaine vague observabilité |
| 12.INFO | Performance | find_all implicite (pas de pagination forte) |
Bloc E (split graph endpoint) en 0.5.1 |
Verdict final post-remédiation : GO ✅
Justification :
- Zéro écart CRITICAL (la règle absolue du master §3 est levée).
- Zéro écart MAJOR — tous les MAJ initiaux sont résolus.
- Score global ~94/100 ≥ 80 (seuil GO).
- Tous les audits individuels passent en GO.
- La pipeline de validation est verte : ruff + format + 446 pytest backend, ESLint + Prettier + vue-tsc + 202 vitest frontend.
Conditions tenues :
- ✅ CHANGELOG
[0.5.0]complet - ✅
frontend/package.jsonà0.5.0 - ✅ Sécurité dev-only documentée + warning au boot sur
changeme - ✅ DRY frontend (storage keys + dead apiUrl supprimé)
- ✅ Refacto archi reasoning (port + adapter + DI) — bonus volet 1
Reste planifié hors release 0.5.0 :
- Bloc E (découpage Vue files volumineux + signatures de fonctions back) → 0.5.1
- Bloc F (KISS micro-optimisations + INFO) → 0.5.1
La release 0.5.0 peut être taguée depuis release/0.5.0 une fois la branche fix/release-0.5.0-audit-remediation mergée.