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
4.1 KiB
4.1 KiB
Rapport d'audit : Decouplage
Release : 0.5.0 Date : 2026-04-28 Auditeur : claude-code
Score de compliance
| Metrique | Valeur |
|---|---|
| Items conformes | 13 / 15 |
| Score | 86 / 100 |
| Ecarts CRITICAL | 0 |
| Ecarts MAJOR | 1 |
| Ecarts MINOR | 1 |
| Ecarts INFO | 0 |
Ecarts constates
[MAJ] Inter-store coupling in history feature test
- Localisation :
frontend/src/features/history/navigation.test.ts:30-31, 67, 82-83, 143-144, 169-170, 188 - Constat : Le test
navigation.test.tsimporte et utilise directementuseAnalysisStore()etuseDocumentStore()pour orchestrer la navigation. Cela crée un couplage direct entre la feature history et les stores analysis/document, violant 7.2.2. - Regle violee : 7.2.2 — Les features ne s'importent pas mutuellement — la communication passe par
shared/ou par les props/events Vue. - Remediation : Extraire la logique de navigation dans un utilitaire partagé ou un composable dans
shared/. Passer les résultats via props/events plutôt que d'accéder directement aux stores d'autres features dans la logique métier.
[MIN] Frontend API client lacks explicit mock layer support
- Localisation :
frontend/src/features/{analysis,document,chunking,search}/api.ts— aucun pattern d'injection de mock ou de stub - Constat : Les fichiers
api.tsutilisentapiFetchdirectement sans interface abstraite. Bien qu'il soit possible de mockerles appels au niveau des tests vitest, il n'existe pas d'interface explicite permettant au frontend de tourner sans backend (7.1.3). - Regle violee : 7.1.3 — Le frontend peut tourner avec un mock du backend (les appels API sont isoles dans des fichiers
api.tspar feature). - Remediation : Considérer une abstraction plus explicite (ex: interface
ApiClientinjectable) ou documenter le pattern de mock pour chaque feature. Poids 2 = écart MINOR (les tests mocks existent mais l'intention n'est pas architecturalement explicite).
Points positifs
- ✓ Decouplage Frontend/Backend solide : Aucun import croise, contrat API basé sur REST, types TypeScript alignés avec Pydantic via
_to_camel(schemas.py). Frontend utiliseapiFetch(shared/api/http.ts) comme couche unique. - ✓ Hexagonal Architecture Backend : Ports dans
domain/ports.pydéfinis avec types du domaine (ConversionResult, ChunkResult, etc.). Services importent uniquementdomain.portsetdomain.value_objects, zéro fuite de types docling.*. Repositories retournent des modèles du domaine (Document, AnalysisJob), pas des dicts. - ✓ Schemas API fortement typés : Tous les DTOs dans
api/schemas.pyutilisent Pydantic BaseModel strictement (pas dedictouAny). Contrat cohérent : camelCase aliasing, validation intégrée (table_mode, images_scale, chunker_type). - ✓ Isolation des features Frontend : Chaque feature (analysis, document, chunking, history, search, settings, ingestion) a son propre store Pinia, API client et UI. Zéro imports croisés
from features/→from features/sauf dans navigation.test.ts. - ✓ Infrastructure Adapter Boundary :
infra/local_converter.pyimporte Docling (DoclingConverter, CodeItem, etc.) mais retourne uniquementConversionResult(domaine). Services ne voient jamais les types docling. - ✓ Configuration d'infra propre :
docker-compose.ymletnginx.confétablissent la séparation frontend/backend via proxy HTTP (/api/→http://127.0.0.1:8000). CORS déclaré explicitement.
Verdict partiel : GO CONDITIONNEL
Conditions :
- Corriger l'inter-store coupling dans
frontend/src/features/history/navigation.test.ts(MAJ). - Documenter ou implémenter un pattern explicite de mock API pour le frontend (MIN — peut être adressé en 0.5.1).
Score 86/100 : Satisfait >= 80 (GO). Un seul écart MAJOR, zéro CRITICAL. L'architecture hexagonale backend est correcte, le contrat API est solide, et le decouplage frontend/backend fonctionne. Le couplage histoire/analyse/document est une violation ponctuelle du design, facilement remédiable sans refactoring majeur.