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
5.3 KiB
5.3 KiB
Rapport d'audit : DRY (Don't Repeat Yourself)
Release : 0.5.0 Date : 2026-04-28 Auditeur : claude-code
Score de compliance
| Metrique | Valeur |
|---|---|
| Items conformes | 5 / 7 |
| Score | 71 / 100 |
| Ecarts CRITICAL | 0 |
| Ecarts MAJOR | 2 |
| Ecarts MINOR | 1 |
| Ecarts INFO | 2 |
Ecarts constates
[MAJ] Magic API URL hardcodé en frontend
- Localisation :
frontend/src/features/settings/store.ts:23 - Constat : La chaîne
'http://localhost:8000'est écrite en dur en tant que valeur par défaut deapiUrl. Aucune centralization dans un fichier de configuration partagé. - Regle violee : Item 5.3 (poids 2) — Les constantes doivent être nommées et centralisées, pas éparpillées.
- Remediation : Créer
frontend/src/shared/config.ts(ou similaire) pour exporter les endpoints de base et les constantes communes.
[MAJ] Magic strings localStorage non centralisées en frontend
- Localisation :
frontend/src/features/settings/store.ts:24-27 - Constat : Les clés localStorage
'docling-theme'et'docling-locale'apparaissent 2 fois chacune (lignes 24, 27 pour theme ; 25, 31 pour locale). Ces chaînes ne sont pas extraites en constantes nommées. - Regle violee : Item 5.3 (poids 2) — Les magic strings doivent être nommées et centralisées.
- Remediation : Créer des constantes nommées (e.g.,
STORAGE_KEY_THEME = 'docling-theme') dansfrontend/src/shared/appConfig.tsou un nouveaufrontend/src/shared/constants.ts.
[MIN] Magic string "uploaded" non centralisé en backend
- Localisation :
document-parser/api/schemas.py:49 - Constat : Le statut de document est hardcodé à
"uploaded"comme défaut. Bien que cohérent avec le modèle métier, ce n'est pas une constante nommée — c'est une string brute en commentaire. - Regle violee : Item 5.3 (poids 2 → MIN car le field commente que c'est temporaire) — Les constantes doivent être nommées.
- Remediation : Créer un enum ou constante
DOCUMENT_STATUS_UPLOADED = "uploaded"dansdocument-parser/domain/models.py(déjà utilisé pour AnalysisStatus).
[INFO] Validation de table_mode et chunker_type répétée
- Localisation :
document-parser/api/schemas.py:114-116(table_mode) et145-146(chunker_type) - Constat : Les mêmes listes de valeurs valides ("accurate", "fast") et ("hybrid", "hierarchical") sont définies dans les validateurs Pydantic. Ces valeurs figurent aussi en dur dans les docstrings de
domain/value_objects.py(lignes 41, 67). - Regle violee : Item 5.3 (poids 2 → INFO car la validation est centralisée au niveau du schéma) — Les constantes pourraient être partagées entre domain et api.
- Remediation : Créer un module
document-parser/domain/constants.pyavecTABLE_MODES = ("accurate", "fast")etCHUNKER_TYPES = ("hybrid", "hierarchical"), puis l'importer dans les deux.
[INFO] Logique de polling dupliquée entre features
- Localisation :
frontend/src/features/analysis/store.ts(ligne 67+) etfrontend/src/features/ingestion/store.ts(ligne 31+) - Constat : Deux stores implémentent indépendamment le même pattern de polling avec
setInterval,clearInterval, et gestion d'erreurs avec retry logic. Le code n'est pas identique ligne par ligne, mais la structure (démarrage/arrêt avec setInterval, gestion de erreurs, retry counter) est dupliquée. - Regle violee : Item 5.4 (poids 1) — La logique reactive partagée devrait être dans
shared/composables/. - Remediation : Extraire un composable réutilisable (e.g.,
usePollAsyncouuseAsyncPoller) dansfrontend/src/shared/composables/pour encapsuler le pattern setInterval + erreur + retry.
Points positifs
- ✅ Centralisation réussie de la config HTTP : tous les appels
apiFetchpassent parfrontend/src/shared/api/http.ts— aucun fetch() direct détecté. - ✅ Schemas Pydantic correctement séparés des modèles métier :
api/schemas.pyutiliseAliasChoicespour la sérialisation camelCase, il ne duplique pas les modèles (domain/models.pyreste pur). - ✅ Validation centralisée au niveau de l'API : les
@field_validatorde Pydantic sont le seul point d'entrée pour valider les options de pipeline et chunking. - ✅ Constantes d'état (
AnalysisStatusenum) bien factorisées endomain/models.py— aucune duplication de "PENDING", "RUNNING", "COMPLETED", "FAILED". - ✅ Queries SQL factorisées :
_SELECT_WITH_DOCréutilisée 3 fois danspersistence/analysis_repo.py.
Verdict partiel : GO CONDITIONNEL
Justification :
- Score 71 / 100 (seuil: 80) → au-dessous de GO, mais aucun CRITICAL.
- 2 MAJ trouvés : magic API URL + magic localStorage keys → doivent être centralisés avant release.
- 1 MIN + 2 INFO pour amélioration future (pas bloquant immédiatement).
Conditions pour GO :
- Centraliser
http://localhost:8000et'docling-theme','docling-locale'dansfrontend/src/shared/(e.g.,config.tsouconstants.ts). - Exécuter la re-vérification après correction pour atteindre ≥ 80.
Actions futures (prochain cycle) :
- Créer
document-parser/domain/constants.pypour les validations (table_mode, chunker_type). - Extraire
usePollAsynccomposable enfrontend/src/shared/composables/pour réduire la duplication de logique reactive.