PD-277 — Retour d'expérience (REX)¶
1. Résumé exécutif¶
| Métrique | Valeur |
|---|---|
| Objectif initial | Supprimer les 2 non-conformités bloquantes restantes de l'audit Prolog (CHECK 23 anti-rejeu nonce, CHECK 24 PKI certificate binding) |
| Résultat obtenu | Conforme — 23/24 checks OK (le check en échec est HSM FIPS, infra pré-existant hors scope PD-277) |
| Verdict final | GO (Gate 8 v1 — 9.125/10) |
| Tests contractuels | 174/174 legal-pre PASS, coverage 92.89% |
2. Métriques de convergence¶
2.1 Temps et itérations¶
| Étape | Durée estimée | Durée réelle | Itérations | Écart |
|---|---|---|---|---|
| 0 - Besoin | 30 min | 17 min | 1 | -43% |
| 1 - Spécification | 2h | 6.5 min | 1 | -95% |
| 2 - Tests | 1h | 1.5 min | 1 | -98% |
| 3 - Gate spec | 1h | 26 min | 2 | -57% |
| 4 - Plan | 1h | 11.5 min | 1 | -81% |
| 5 - Gate plan | 1h | 76 min | 3 | +27% |
| 6 - Implémentation | 4h | 25 min | 1 | -90% |
| 7 - Acceptabilité | 2h | 12 min | 1 | -90% |
| 8 - Gate acceptabilité | 1h | 11 min | 1 | -82% |
| 9 - REX | 30 min | ~30 min | 1 | 0% |
| TOTAL | ~14h | ~3.5h | 13 | -75% |
2.2 Scores de convergence par gate¶
| Gate | Score v1 | Score final | Delta | Itérations |
|---|---|---|---|---|
| Gate 3 | 7.0/10 | 8.25/10 | +1.25 | 2 |
| Gate 5 | 7.625/10 | 8.75/10 | +1.125 | 3 |
| Gate 8 | 9.125/10 | 9.125/10 | 0 | 1 |
2.3 Écarts par catégorie¶
| Catégorie d'écart | Gate 3 | Gate 5 | Gate 8 | Total |
|---|---|---|---|---|
| ECT (complétude/testabilité) | 3 | 0 | 2 | 5 |
| DIV (divergence spec/impl) | 2 | 1 | 2 | 5 |
| AMB (ambiguïté) | 8 | 2 | 2 | 12 |
| SEC (sécurité) | 0 | 0 | 2 | 2 |
| PERF (performance) | 0 | 0 | 0 | 0 |
| TOTAL écarts | 13 | 3 | 8 | 24 |
3. Points fluides¶
Ce qui a bien fonctionné :
- Gate 8 GO en v1 (9.125/10) — première fois en une seule itération pour la gate de clôture. Les corrections sécurité effectuées en étape 7 (S-01, S-02) ont permis d'arriver propre à la gate.
- Implémentation rapide (25 min) — la décomposition en 8 tâches sur 3 niveaux de parallélisation a été efficace. Les 174 tests passent du premier coup.
- Shadow evaluation utile — la confrontation ChatGPT vs qwen3.5 en étape 1 (delta -2.0 en faveur de ChatGPT) a validé le choix de l'agent production.
- Correction sécurité proactive en étape 7 — les vulnérabilités S-01 (regex flag
i) et S-02 (TOCTOUincludes()) détectées par la review sécurité ChatGPT ont été corrigées avant Gate 8, évitant une itération supplémentaire. - Learnings injectés pertinents — les 3 learnings (PD-63 crypto.randomUUID, PD-41 fail-closed crypto, PD-238 timingSafeEqual) ont directement influencé l'implémentation sans friction.
4. Points difficiles¶
Obstacles rencontrés (sans justification) :
- Gate 5 à 3 itérations — le plan v1 avait 6 écarts MAJEUR (DDL DEFAULT non contractualisé, couverture PKI incomplète, fail-closed ReKeys hérités non démontré, condition status=ACTIVE non spécifiée, frontière code contract violée, module repository non couvert). La v2 a corrigé mais
coherenceest restée à 7.75 (sous le seuil 8.0). La v3 a atteint 8.0 par convergence. - Sonar Phase 1.5 indisponible — le token Vault a retourné null, rendant l'exécution Sonar locale impossible. La procédure BLOQUANTE a été contournée de fait, reportant la vérification au pipeline CI/CD.
- Résultat audit Prolog 23/24 vs objectif 24/24 — l'ambiguïté sur la nature du check HSM FIPS (bloquant vs non bloquant, pré-existant vs nouveau) a généré une divergence documentaire (DIV-02) qui a nécessité une analyse en Gate 8.
- Gate 3 v1 — 3 constats bloquants — le format nonce n'était pas contractualisé (longueur, encodage, normalisation), les faits Prolog canoniques étaient absents de la spec, et 4 tests d'invariants n'avaient pas de scénarios GIVEN/WHEN/THEN. Correction en v2 (+1.25 delta).
5. Hypothèses révélées tardivement¶
Hypothèses non explicites découvertes en cours de workflow :
- H-277-T01 (LegalReKey pré-existants) — découverte à l'étape 3 (Gate 3 v1). La spec v1 ne traitait pas le cas des enregistrements existants lors de l'ajout de colonnes NOT NULL. Nécessité d'introduire
DEFAULT ''pour les certificats, contractualisé dans le plan. - H-277-T02 (Extension TspVerificationResult) — découverte à l'étape 4. Le résultat TSP devait être enrichi avec les certificateId pour permettre le binding PKI. L'interface existante supportait les champs optionnels sans casser la rétrocompatibilité.
- Nonce fourni par l'appelant vs généré serveur — divergence identifiée en Gate 3 v2. La spec impose
crypto.randomUUID()côté serveur, mais les tests valident des nonces fournis en entrée (cas d'erreur). Résolu en clarifiant que le nonce est généré serveur et transmis dans la requête.
6. Invariants complexes¶
Invariants difficiles à implémenter ou sensibles aux régressions :
- INV-277-02 (nonce-unique) — TC-NEG-02. La concurrence réelle (Promise.all) sur le même nonce exigeait une transaction SERIALIZABLE avec opérateur JSONB
@>atomique côté DB. La première implémentation utilisaitincludes()applicatif (vulnérable TOCTOU), corrigée en commit f2e2cc6. - INV-277-04 (pki-binding-mandatory) — TC-ERR-04/05/06, TC-NEG-05. La validation des certificats (non expirés, non révoqués, compatibles mandat) nécessitait l'extension du stub TSP avec support de configuration de test pour certificats invalides.
- INV-277-05 (binding-immutability) — TC-ERR-08, TC-NEG-04. L'immuabilité des certificats post-création a été implémentée au niveau applicatif (repository guard) plutôt que par trigger DB, cohérent avec le pattern de PD-81.
7. Dette technique¶
Compromis acceptés et non bloquants :
- Sonar non exécuté (DIV-03) — impact: moyen. Dérogation de fait sur la Phase 1.5 BLOQUANTE. Reporté au pipeline CI/CD post-merge. Si Sonar FAILED → corrections nécessaires avant REX.
- Stubs TSP sans story destination précise (ZO-04) — impact: faible.
TspVerifierStubetconsultDocument TODOréférencent "PD-XXX" au lieu d'un numéro concret. La story TSP réelle sera déterminée lors de la planification d'intégration TSP. - DEFAULT '' transitoire (DIV-01) — impact: faible. Certificats vides sur ReKeys pré-existants protégés par fail-closed dans
reEncryptWithNonce(). Backfill non planifié (story séparée). - TC-ERR-07 couvert implicitement (T-01) — impact: faible. Le test d'injection de défaillance de persistance n'est pas explicitement isolé, couvert par le rollback SERIALIZABLE.
- 10/29 TC en tests unitaires (DIV-04) — impact: faible. La répartition unitaire/intégration/CI est légitimée par le plan, mais la spec et les tests ne distinguent pas les niveaux.
8. Risques résiduels¶
| Risque | Type | Probabilité | Impact | Mitigation |
|---|---|---|---|---|
| Sonar flagge des security hotspots post-merge | tech | moyen | moyen | Pipeline CI/CD exécutera Sonar, corrections avant release |
| Performance JSONB used_nonces sous forte charge | perf | faible | moyen | TTL 30 jours max sur ReKey limite la taille. Pas d'index GIN à ce stade — surveillance production |
| ReKeys hérités avec certificats vides en production | ops | faible | faible | Fail-closed démontré : reEncryptWithNonce rejette avec PRE_CERTIFICATE_BINDING_FAILED |
| Check HSM FIPS reste en échec (23/24) | ops | élevé | faible | Check infra pré-existant hors scope code. Résolution par configuration HSM (story séparée) |
8bis. Matrice de délégation inter-PD¶
| Story | Direction | Statut | Nature de la dépendance | Problème rencontré |
|---|---|---|---|---|
| PD-41 | ← dépend de | DONE | PreService.reEncrypt() — interface stable appelée depuis reEncryptWithNonce() | RAS |
| PD-81 | ← dépend de | DONE | LegalReKeyManagerService.generateLegalReKey() enrichi, TspVerifierStub, LegalPreAccessGuard | RAS |
| PD-189 | ← dépend de | DONE | extract-facts.py (EntityExtractor), pv_pre_compliance.pl (checks 23/24) | RAS |
| PD-XXX | → bloque | TODO | Intégration TSP réelle (remplacement TspVerifierStub) | Story non encore planifiée (ZO-04) |
| PD-XXX | → bloque | TODO | Backfill ReKeys pré-existants (suppression DEFAULT '') | Story non planifiée (ZO-06) |
8ter. Bugs de tests¶
| Pattern incorrect | Pattern correct | Cause | Coût |
|---|---|---|---|
includes() applicatif pour vérification nonce | JSONB @> opérateur DB atomique | TOCTOU race condition | ~15 min (correction S-02) |
Regex UUID v4 avec flag i | Regex UUID v4 lowercase strict (sans flag i) | Bypass anti-rejeu par casse mixte | ~10 min (correction S-01) |
8quater. Corrections post-Gate 8¶
Aucune correction post-Gate 8 nécessaire à ce stade. Sonar reporté au pipeline CI/CD.
9. Patterns récurrents détectés¶
9.1 Patterns confirmés (déjà vus dans d'autres stories)¶
- Fail-closed obligatoire pour mécanismes de sécurité — aussi dans PD-238, PD-240, PD-250, PD-251. PD-277 ajoute le fail-closed sur les ReKeys hérités (certificats vides → rejet).
- timingSafeEqual / opérations atomiques DB pour comparaisons crypto — aussi dans PD-38, PD-238, PD-264, PD-251. PD-277 utilise l'opérateur JSONB
@>(côté DB) pour éviter les timing attacks. - Stubs inter-PD acceptés en Gate 8 si documentés avec story destination — aussi dans PD-63, PD-82, PD-250, PD-251. PD-277 : stubs avec "PD-XXX" (sans numéro précis) a été accepté MINEUR.
- Machine à états explicite : aucune transition nouvelle quand hors scope — aussi dans PD-82, PD-250, PD-264, PD-251. PD-277 : vérification INV-277-08 (StatusEnum identiques avant/après).
- Gate 3 v1 bloquée par format non contractualisé — aussi dans PD-32, PD-250, PD-264, PD-251. PD-277 : format nonce (UUID v4 lowercase ASCII) absent de la spec v1, ajouté en v2.
- Atomicité multi-composant : transaction SERIALIZABLE pour opérations crypto — aussi dans PD-55, PD-264, PD-250, PD-251. PD-277 : vérif nonce + insert nonce + reEncrypt dans une seule transaction.
9.2 Nouveaux patterns identifiés¶
- Résolution de certificats via stub avec extension interface optionnelle — TspVerificationResult étendu avec champs optionnels
ownerCertificateId/recipientCertificateIdsans casser l'interface existante. Pattern à surveiller pour les prochaines intégrations PKI. - DEFAULT transitoire + fail-closed pour migration rétrocompatible — ajout de colonnes NOT NULL avec DEFAULT vide + rejet des enregistrements hérités au runtime. Pattern utile pour éviter les migrations en 2 étapes.
- Audit Prolog : extraction automatique des faits depuis @Column TypeORM — aucune modification de
extract-facts.pyrequise, les décorateurs@Columnsuffisent pour générer les faits Prolog. À capitaliser pour toutes les stories crypto-proof.
10. Améliorations du workflow¶
10.1 Améliorations des prompts/templates¶
| Fichier | Amélioration suggérée | Priorité |
|---|---|---|
templates/prompts/1 Specification.md | Ajouter une section obligatoire "Format et normalisation des entrées" pour les stories crypto (nonce, token, hash) — éviterait le bloquant Q1 en Gate 3 | haute |
templates/prompts/1 Specification.md | Exiger la liste canonique des faits Prolog dans la spec pour les stories crypto-proof — éviterait le bloquant Q5 en Gate 3 | haute |
templates/prompts/7c Review Security.md | Ajouter un check explicite "comparaisons applicatives vs DB-level" pour détecter les TOCTOU dans les opérations atomiques | moyenne |
10.2 Améliorations des agents¶
| Agent | Amélioration suggérée | Justification |
|---|---|---|
| agent-qa-unit-integration | Inclure un test explicite TC-ERR-07 (injection défaillance persistance) dans les templates de tests | Écart récurrent — couvert implicitement mais jamais explicitement testé |
| agents step 6 | Pour stories crypto-proof, injecter la liste des faits Prolog attendus dans le system prompt | Évite la vérification manuelle post-implémentation |
10.3 Améliorations du processus¶
- Token Sonar en Vault — le token Sonar retourne null. Corriger la configuration Vault pour garantir la disponibilité de Sonar en Phase 1.5. Alternatives : Sonar en CI/CD pré-merge (GitLab MR pipeline).
- Stubs avec story destination obligatoire — les stubs "PD-XXX" sans numéro concret devraient être bloqués en Gate 8 si la story de destination n'est pas planifiée dans le backlog. Renforce la traçabilité (ZO-04).
- Distinction niveaux de test dans les templates de tests — ajouter une colonne "Niveau" (Unit/Integration/CI/Infra) dans la matrice de couverture dès l'étape 2 pour éviter l'ambiguïté DIV-04 en Gate 8.
11. Enseignements clés¶
-
Contractualiser le format des entrées dès la spec — l'absence de format nonce contractualisé a généré 3 constats bloquants en Gate 3 v1. Pour toute story crypto, le format (longueur, encodage, normalisation, casse) doit être défini dans la spécification, pas découvert à la gate.
-
Opérations atomiques DB-level pour les invariants de sécurité — l'utilisation d'opérateurs JSONB PostgreSQL (
@>,||) dans une transaction SERIALIZABLE est supérieure aux vérifications applicatives (includes(),indexOf()) pour les contrôles anti-rejeu. Ce pattern élimine les vulnérabilités TOCTOU. -
DEFAULT transitoire + fail-closed = migration rétrocompatible sûre — l'ajout de colonnes NOT NULL avec DEFAULT vide, combiné à un rejet fail-closed des enregistrements hérités au runtime, est un pattern de migration sûr pour les systèmes en production avec données existantes.
-
Gate 8 GO en v1 possible quand les corrections sécurité sont faites en étape 7 — les 2 corrections sécurité (S-01, S-02) appliquées avant la gate de clôture ont permis un score élevé (9.125/10) sans itération. La review sécurité adversariale est le filtre le plus efficace.
-
L'extraction automatique de faits Prolog depuis les décorateurs TypeORM fonctionne sans modification — pour les stories crypto-proof, il suffit d'ajouter les
@Columndans l'entité pour queextract-facts.pygénère les faits Prolog correspondants. Aucune intervention manuelle sur l'extracteur n'est nécessaire.
12. Métriques cumulatives (auto-calculées)¶
| Métrique | Cette story | Moyenne projet | Tendance |
|---|---|---|---|
| Temps total | 3.5h | 3.8h | → |
| Itérations gates | 6 | 5.5 | → |
| Écarts totaux | 24 | 20.0 | ↑ |
| Score convergence moyen | 8.71/10 | 8.62/10 | → |