05-PLAN : plan TDD par petits lots¶
Statut : v0.1 du 2026-07-01. Chaque lot suit le cycle rouge → vert → refactor : les tests du lot sont écrits et échouent AVANT le code. Un lot n'est commencé que lorsque le précédent est vert. Les REQ citées renvoient au 03-SPEC.
Stack : Python 3.12+, pyproject.toml (uv), FastAPI, pytest, ruff. Structure
src/sas_confiance_ia/ et tests/. Docker en Lot 10.
Phase 0 : le socle qui prouve les invariants¶
Lot 0 : scaffold¶
Repo initialisé : pyproject.toml, src/, tests/, .gitignore strict
(vaults, clés, .env, corpus non synthétiques), CI GitHub Actions (pytest +
ruff), licence EUPL-1.2, README minimal avec crédit Romain Bochet.
Vert : pytest passe (un test sanité), CI verte.
Lot 1 : validateurs français purs¶
nir_key, nir_is_valid (dont Corse 2A/2B), luhn_ok (SIREN/SIRET),
iban_valid. Fonctions pures, sans dépendance modèle : réemploi des
implémentations de Romain Bochet après accord (ADR-001), avec tests exhaustifs
(valides, invalides, clés fausses, espaces, séparateurs).
REQ : 016 (partiel). Rouge d'abord : cas de test issus du corpus.
Lot 2 : corpus synthétique et oracle¶
corpus/synthetique/ : 5 scénarios + dossier multi-pièces +
valeurs-connues.json (oracle de non-fuite). Test de cohérence : chaque valeur
déclarée est bien présente dans son document source.
REQ : 009.
Lot 3 : détection déterministe (C1)¶
Reconnaisseurs en Python pur (regex + validateurs du Lot 1) : NIR, SIRET, SIREN, IBAN, email, téléphone FR, plaque, date de naissance en contexte. Résolution des chevauchements selon la priorité REQ-016 (le SIRET l'emporte sur CREDIT_CARD). Interface compatible avec l'intégration Presidio du Lot 9 : la Phase 0 reste sans modèle et sans téléchargement (règle 1 du HANDOFF), Presidio in-process (ADR-009) arrive avec le NER en Phase 1. Vert : rappel 1,0 sur les types déterministes du corpus.
Lot 4 : pseudonymisation et vault en mémoire¶
Placeholders typés [PERSONNE_001], compteurs par dossier et par type,
mapping bidirectionnel. reidentifier(pseudonymiser(t)) == t.
REQ : 002 (en mémoire), 011 (squelette : même valeur → même placeholder).
Lot 5 : vault persistant chiffré et compteurs durables¶
SQLite chiffré (SQLCipher) ou fichier Fernet, clé via keyring. Test de redémarrage simulé : aucun placeholder réattribué, dossiers isolés. Inspection binaire : aucune valeur en clair. REQ : 004, 005.
Lot 6 : faux backend de capture et proxy minimal¶
POST /v1/chat/completions + GET /v1/models (FastAPI). Le faux backend
enregistre le payload HTTP exact. Tests de non-fuite : aucune valeur de
l'oracle dans le payload capturé. stream=true → 400 explicite.
Écart livré (Lot 7) : le 400 a été remplacé par la conversion en
non-streaming journalisée (REQ-010, ADR-008), pour ne pas casser les clients
qui activent le streaming par défaut.
REQ : 001, 010, 013 (partiel : format OpenAI).
Lot 7 : contrôle d'intégrité et ré-identification de la réponse¶
Placeholders inconnus / manquants / altérés → rapport d'intégrité, blocage ou
review_required. Headers X-Dossier-Id, X-Privacy-Mode,
X-Reidentify-Response.
Écart livré : X-Privacy-Mode n'a jamais été implémenté. Le pilotage par
mode global a été remplacé au Lot 14 par les politiques par type d'entité,
plus fines et persistées par dossier (voir 01-PRD §5).
REQ : 006, et les 5 cas de test d'intégrité du cadrage §14.6.
Lot 8 : journalisation propre¶
Journal structuré (métadonnées seules). Test : aucune valeur de l'oracle, aucun prompt, aucune réponse, aucun secret dans les logs produits par toute la suite. REQ : 003.
Fin de Phase 0 : les 7 invariants du cadrage sont prouvés par des tests sur faux backend. Aucun appel externe n'a jamais été émis.
Phase 1 : détection sérieuse et premier vrai backend¶
Lot 9 : NER CamemBERT (C2)¶
Moteur transformers de Presidio avec modèle NER français épinglé ; repli spaCy configurable. Mesure de rappel / précision PERSON, ORG, LOC publiée. REQ : 016 complet. Porte d'éval 4.4 du 02-AI-SPEC en place.
Lot 10 : Docker et backend réel¶
Dockerfile + compose (GPU NVIDIA optionnel), backend Ollama local puis Infomaniak par configuration (ADR-005, ADR-013). Les tests restent sur faux backend ; un test d'intégration manuel documenté valide Ollama. REQ : 013 complet.
Lot 11 : coréférence par dossier (C4)¶
Normalisation (casse, civilités, nom seul vs nom complet), alias persistants par dossier, multi-pièces. Ambiguïtés → revue. REQ : 011.
Lot 12 : interface web minimale¶
Page unique servie par le même FastAPI : coller, choisir le mode, voir le résumé des détections (types et comptes en mode sérieux), télécharger, ré-identifier. Bandeau démo distinct. REQ : 007 côté UI.
Phase 2 : différenciateurs¶
Lot 13 : juge LLM local (C3)¶
Passe Ollama optionnelle, sortie JSON stricte, candidats en revue humaine, test réseau zéro appel sortant, éval canaris. REQ : 014.
Lot 13 bis : durcissement de la détection (arbitrage Q4, 2026-07-02)¶
Né d'un test réel sur un PV fictif de conseil médical (intégré au corpus :
07-conseil-medical.md + oracle). Fail-safe contextuel : motifs structurels
à clé invalide masqués en types *_SUSPECT quand un mot de contexte les
précède (NIR, SIRET, SIREN, IBAN) ; nouveaux types RPPS, MATRICULE,
CODE_POSTAL ; priorité REQ-016 étendue (un NIR fictif ne part plus en
CARTE) ; fusion des empans NER contigus de même type (fin des placeholders
collés). Voir QUESTIONS.md Q4 pour l'arbitrage et les limites.
REQ : 001 (périmètre élargi), 016 (priorité étendue).
Lot 14 : politiques par type et surrogates¶
Politique configurable par type d'entité (cadrage §9.5), dates différenciées, surrogates genrés optionnels (Faker fr_FR). REQ : 008, 012.
Lot 15 : fichiers¶
Ingestion .txt, .md, .docx (paragraphes et tableaux), .pdf textuel ;
refus documenté des PDF scannés. Côté UI (enrichissement acté le 2026-07-02,
inspiré de rbochet/amo-presidio) : page Fichiers avec dépôt par glisser-déposer,
affichage côte à côte du texte d'origine et du texte pseudonymisé avec entités
surlignées côté client (arbitrage Q3), exports .txt et .docx. Le PDF
caviardé (PyMuPDF, licence AGPL : compatible EUPL-1.2 mais contaminante) est
un extra optionnel à trancher pendant le lot.
Lot 16 : publication du commun¶
MkDocs Material + GitHub Pages, tutoriel d'installation Kubuntu / Docker, parcours formateur, CONTRIBUTING, checklist de publication, scan de secrets sur l'historique. REQ : 015.
Phase 3 : pistes actées (2026-07-02), à spécifier après le lot 16¶
Non planifiées en lots : elles exigent d'abord une extension du 02-AI-SPEC.
- Boucle d'anonymisation inspirée de la conception d'ecluse-dev/ecluse (Apache-2.0, non implémentée chez eux) : mesurer un risque de ré-identification par faisceau d'indices (1/F, k-anonymat), généraliser les quasi-identifiants par hiérarchies françaises (commune→département→région, âge→tranche, pathologie en dernier recours), re-mesurer jusqu'au seuil ; trois sorties (succès, garde-fou, épuisement qui rend la main à l'humain). Pertinent pour les données médico-sociales des collectivités (PAP, DYS/TDAH, allocations handicap des départements).
- Détecteur RPPS (professionnels de santé, Luhn) pour le médico-social.
- Générateur de corpus à pièges (valeurs à clés invalides non annotées) et scoreur strict/souple repris du bench d'Écluse (Python, Apache-2.0, attribution requise) : mesurer notre taux de faux positifs.
Règles transverses¶
- Jamais de code sans test rouge préalable dans le lot.
- Jamais de donnée réelle, jamais d'appel réseau externe dans la suite de tests (un test l'interdit activement).
- Chaque lot = un ou plusieurs commits atomiques, messages en français accentué.
- Toute limite découverte (cas non détecté, ambiguïté) devient soit un test, soit une ligne dans la section « limites » de la doc : jamais un silence.