Aller au contenu

PD-103 — Agent Developer — Module capture-ocr (M3)

1. Identite

Attribut Valeur
Module M3 — capture-ocr
Agent Agent Developer (Claude)
Story PD-103
Date 2026-04-03
Spec version v3

2. Fichiers produits

Fichier Type Lignes
src/capture/ocr-service.ts Implementation ~195
src/__tests__/capture/ocr-service.test.ts Tests contractuels ~310

3. Invariants couverts

Invariant Mecanisme Observable
INV-103-04 — OCR local, non-bloquant, sans appel IA externe Interface OcrEngine locale uniquement (pas de parametre URL/endpoint) ; try/catch absorbant dans extract() Aucun appel reseau dans les traces ; echec absorbe
INV-103-05 — OCR enrichit metadonnees, ne remplace jamais l'image probatoire OCR retourne OcrResult \| null ; imageBytes jamais mute ; champs OCR optionnels dans payload POST Tests verifient bytes image identiques pre/post extraction

4. Criteres d'acceptation couverts

Critere Mecanisme Observable
CA-103-05 — OCR desactivable par l'utilisateur Parametre enabled: boolean dans extract() ; retourne null immediatement si false TC-NOM-02 : aucun appel moteur quand desactive
CA-103-06 — OCR local et non bloquant try/catch absorbe toute erreur du moteur ; aucun appel reseau TC-NOM-03, TC-ERR-03 : flux continue sur echec

5. Matrice de couverture tests contractuels

Test-ID Fichier Description
TC-NOM-02 src/__tests__/capture/ocr-service.test.ts (ligne ~45) OCR desactive retourne null, 0 appels moteur
TC-NOM-03 src/__tests__/capture/ocr-service.test.ts (ligne ~65) Erreur Vision absorbee, flux continue
TC-ERR-03 src/__tests__/capture/ocr-service.test.ts (ligne ~95) Echec OCR force ne bloque pas le flux probatoire
TC-NEG-07 src/__tests__/capture/ocr-service.test.ts (ligne ~221) Texte > 20000 chars rejete (erreur absorbee)

6. Tests additionnels (non contractuels)

Test Description
Flux nominal extraction reussie OcrResult valide avec text, confidence, language
sanitizeOcrText — C0/C1 stripping Control chars supprimes sauf TAB/LF/CR
sanitizeOcrText — NFC normalization NFD -> NFC
clampOcrConfidence Bornes [0, 1], NaN, Infinity
validateOcrLanguage BCP-47 valide/invalide
Language fallback Tag invalide -> "und"
NoOpOcrEngine Environnement sans Vision natif
INV-103-05 integrite image Bytes inchanges apres extraction reussie et echouee

7. Couverture

Metrique Valeur Seuil
Statements 97.67% >= 80%
Branches 90.00% >= 80%
Functions 100.00% >= 80%
Lines 97.61% >= 80%
Tests passes 34/34 100%

8. Architecture

8.1 Interface abstraite OcrEngine

interface OcrEngine {
  recognizeText(imageBytes: Uint8Array): Promise<RawOcrResult>;
}

Pattern identique a KekProvider (M2) : interface abstraite permettant : - MockOcrEngine pour les tests (resultat deterministe configurable) - NoOpOcrEngine pour les environnements sans Vision natif (Expo Go, simulateur) - Implementation native Apple Vision a venir (native module React Native)

8.2 OcrService

Service principal consomme par l'orchestrateur (M6) :

OcrService.extract(imageBytes, enabled)
  |
  +-- enabled=false → null (CA-103-05)
  +-- imageBytes vide → null
  +-- try {
  |     engine.recognizeText(imageBytes) → RawOcrResult
  |     sanitizeOcrText(raw.text) → NFC + strip C0/C1
  |     clampOcrConfidence(raw.confidence) → [0.0, 1.0]
  |     validateOcrLanguage(raw.language) → BCP-47 ou fallback
  |     → OcrResult { ocrText, ocrConfidence, ocrLanguage }
  |   }
  +-- catch → null (INV-103-04, INV-103-05)

8.3 Sanitisation contractuelle (§5.1)

  • NFC : String.prototype.normalize("NFC") — normalisation Unicode
  • C0 strip : [\x00-\x08\x0B\x0C\x0E-\x1F] supprime (conserve TAB \x09, LF \x0A, CR \x0D)
  • C1 strip : [\x7F-\x9F] supprime
  • Longueur max : 20000 chars post-sanitisation, rejet si depasse (pas de troncature — §5.1)

9. Decisions architecturales

Decision 1 : Interface abstraite OcrEngine

  • Decision : Abstraire le moteur OCR derriere une interface OcrEngine
  • Rationale : Apple Vision n'est disponible qu'en environnement natif iOS ; les tests unitaires tournent en Jest/jsdom sans acces au framework natif
  • Alternatives considerees : (1) Mock inline du module natif dans jest.config.js, (2) Conditionnel Platform.OS dans le service
  • Trade-offs : Indirection supplementaire vs testabilite complete + swap facile de l'implementation native

Decision 2 : Absorption des erreurs dans le service (pas dans l'orchestrateur)

  • Decision : Le try/catch absorbant est dans OcrService.extract(), pas dans l'orchestrateur M6
  • Rationale : INV-103-04 et INV-103-05 sont des invariants du module OCR ; la responsabilite de non-blocage appartient au service OCR, pas a l'appelant
  • Alternatives considerees : (1) Laisser l'orchestrateur catch, (2) Pattern Result
  • Trade-offs : L'orchestrateur ne voit jamais d'erreur OCR, simplifie M6 ; perte de visibilite erreur (acceptable car OCR est optionnel)

10. Hypotheses

ID Hypothese Impact si faux
HM3-01 Apple Vision est accessible via un native module React Native (bridge ou JSI) L'implementation OcrEngine native devra etre creee comme module natif Expo
HM3-02 recognizeText retourne un texte brut UTF-8 sans necessiter de post-traitement structurel (bounding boxes, etc.) Adapter RawOcrResult si des coordonnees sont necessaires pour l'UI
HM3-03 La sanitisation NFC + C0/C1 est suffisante pour le texte OCR Vision Si Vision retourne des caracteres hors plan BMP necessitant un traitement specifique, adapter sanitizeOcrText

11. Dependances

Consomme par ce module

Dependance Source Usage
OcrResult, OcrText, OcrLanguage src/capture/types.ts (M1/T1) Types branded et interface resultat
OCR_TEXT_MAX_LENGTH src/capture/types.ts (M1/T1) Constante contractuelle 20000 chars
CAPTURE_ERROR_CODES src/capture/types.ts (M1/T1) Code erreur OCR_FAILED
CAPTURE_VALIDATION_PATTERNS src/capture/types.ts (M1/T1) Regex BCP-47 pour validation langue

Consomme ce module

Consommateur Usage
M6 — capture-orchestrator Appel OcrService.extract() a l'etape 6 du flux nominal A

12. Verification qualite

Critere Statut
TypeScript strict : 0 erreur OK
Coverage >= 80% (lignes + branches) OK (97.61% / 90%)
Tests contractuels TC-NOM-02, TC-NOM-03, TC-ERR-03, TC-NEG-07 OK (4/4)
0 regression sur tests existants (state-machine) OK (90/90)
Aucun appel reseau dans le module OK (structurel)
Aucune donnee sensible dans les logs OK (aucun log)
INV-103-04, INV-103-05 respectes OK