Aller au contenu

PD-265 — Retour d'expérience (REX)

1. Résumé exécutif

Métrique Valeur
Objectif initial Monitoring TSA RFC 3161, cycle de vie clés HSM, re-horodatage, machine d'états composite avec fail-closed
Résultat obtenu Conforme — 14 composants livrés, machine d'états composite exhaustive, 222 tests PASS
Verdict final GO (Gate 8 v1, 8.25/10)
Tests contractuels 222/222 passés (14 invariants vérifiés : 11 PASS + 3 PASS-STUB)

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 16 min 1 -47%
1 - Spécification 2h 3 min 1 -98%
2 - Tests 1h 2 min 1 -97%
3 - Gate spec 1h 47 min 3 -22%
4 - Plan 1h 15 min 1 -75%
5 - Gate plan 1h 2h 06 min 3 +110%
6 - Implémentation 4h 59 min 1 -75%
7 - Acceptabilité 2h 19 min 1 -84%
8 - Gate acceptabilité 1h 14 min 1 -77%
9 - REX 30 min ~30 min 1 0%
TOTAL ~14h ~5.5h 14 -61%

2.2 Scores de convergence par gate

Gate Score v1 Score final Delta Itérations
Gate 3 5.875/10 8.75/10 +2.875 3
Gate 5 8.00/10 8.625/10 +0.625 3
Gate 8 8.25/10 8.25/10 1

2.3 Écarts par catégorie

Catégorie d'écart Gate 3 Gate 5 Gate 8 Total
ECT (complétude/testabilité) 6 2 0 8
DIV (divergence spec/impl) 6 2 0 8
AMB (ambiguïté) 7 3 0 10
SEC (sécurité) 1 0 0 1
PERF (performance) 0 0 0 0
TOTAL écarts 20 7 0 27

3. Points fluides

Ce qui a bien fonctionné : - Gate 8 GO en v1 : première fois pour une story de cette complexité (14 composants, 14 invariants). L'effort de structuration en Gate ⅗ a payé. - Implémentation rapide (59 min pour 49 fichiers, 5707 insertions) : la décomposition en 14 composants avec un plan détaillé (14 code contracts, 5 décisions architecturales, mapping INV→mécanismes exhaustif) a permis une exécution directe. - Réutilisation de services existants : JsonCanonicalizeService, AuditSignatureService, OcspClientService, ReferenceClockService — aucune nouvelle dépendance npm. - Pattern stubs inter-PD avec story destination (PD-37, PD-265 phase 2) : 6 STUBs tracés, tous acceptés en Gate 8 sans écart MAJEUR. - Acceptabilité phase 1 (quality gates auto) : ESLint 0 errors, TS 0 errors, 222/222 tests — pré-filtre efficace avant reviews LLM.

4. Points difficiles

Obstacles rencontrés (sans justification) : - Gate 3 v1 NON_CONFORME à 5.875/10 : la spec initiale (12 invariants, 7 CA) manquait de formalisme sur le modèle composite d'états (flags comme source de vérité, état dérivé). 3 itérations nécessaires. - Gate 5 v2 convergence STOP (delta=+0.125) : la stagnation a nécessité une refonte structurelle (DA-05 clarification, séparation des deux enums refusalReasonCode vs auditReasonCode) plutôt qu'une correction incrémentale. - Faux positifs LLM en reviews d'acceptabilité : 5 findings LLM (3 MAJEUR tests, 2 MAJEUR sécurité) rejetés après analyse contextuelle. Pattern récurrent (9ème occurrence). - Sonar scanner auth 401 : le token Sonar a expiré pendant la phase 1.5, nécessitant un fallback sur la vérification API. 0 issue sur code PD-265.

5. Hypothèses révélées tardivement

Hypothèses non explicites découvertes en cours de workflow : - NTS multi-serveurs worst-case — découverte à l'étape 5 (Gate 5 R-20). La spec ne définissait pas la stratégie d'agrégation quand ntsReferenceServers contient plusieurs serveurs. La décision architecturale DA-01 (worst-case fail-closed) a dû être prise au plan. - SLA post-rotation non enforcés par flag — découverte à l'étape 5 (Gate 5 R-21). Les délais keyRetiredToArchivedDelay et keyArchivedToDestroyedDelay nécessitent un scheduler dédié (C7 KeyLifecycleSlaScheduler) non prévu dans la spec initiale. - État dérivé vs état stocké — découverte à l'étape 5. La spec n'explicitait pas que tsaServiceState est une vue calculée depuis tsaServiceDegradationFlags. Ce choix architectural (DA-05) a nécessité 2 itérations de Gate 5 pour être stabilisé.

6. Invariants complexes

Invariants difficiles à implémenter ou sensibles aux régressions : - INV-265-01 (NTS fail-closed + machine d'états composite) — TC-NOM-01, TC-ERR-01 : le modèle composite (flags comme source de vérité, état dérivé par calcul, clearing par 2 cycles consécutifs conformes sur TOUS les serveurs) est le cœur de la complexité. Toute régression sur le clearing ou la dérivation invalide la conformité. - INV-265-06 (re-horodatage avec deadline + politique par état) — TC-NOM-05, TC-ERR-10 : le path conditionnel DEGRADED_KEY_LIFECYCLE autorise le re-horodatage uniquement si flag unique + certificat valide + TL valide. 3 conditions à vérifier atomiquement. - INV-265-11 (machine d'états exhaustive) — TC-ERR-12 : transitions non listées interdites. La dérivation de l'état depuis les flags élimine les transitions directes, mais toute tentative de bypass (écriture directe en DB) doit être détectée. - INV-265-14 (RBAC MAINTENANCE + journalisation refus) — TC-NEG-09 : le toggle MAINTENANCE est un vecteur de DoS interne si non protégé. Le refus DOIT être journalisé avec événement signé.

7. Dette technique

Compromis acceptés et non bloquants : - E-01 : persistToDb() singleton sans clause WHERE explicite — impact: faible (singleton garanti par migration seed) - E-02 : escalatedFlags in-memory, perte de déduplication au restart — impact: faible (alertes idempotentes par nature, pas de double action) - E-03 : Offset NTS global vs per-server (ntpd-rs limitation) — impact: faible (worst-case DA-01 respecté fonctionnellement) - E-06 : 6 STUBs tracés (OCSP, CRL, TL, TST detection, audit signing) — impact: moyen (fonctionnalités complètes dépendent de PD-37 et PD-265 phase 2) - E-08 : Transition callback failure ne bloque pas la persistence (audit=STUB PD-37) — impact: moyen (à réévaluer quand PD-37 sera implémenté)

8. Risques résiduels

Risque Type Probabilité Impact Mitigation
ntpd-rs indisponible en production (H-IMPL-01) tech faible moyen Fallback NTS direct via socket UDP prévu dans le design
Faux positifs NTS worst-case sur serveur instable (V-01) ops moyen faible ntsReferenceServers configurable, choisir serveurs fiables
Clé RETIRED persistante si alertes SLA ignorées (V-08) ops faible moyen Alerte CRITICAL + monitoring exploitation
Format TL ETSI XML instable (V-04) tech faible moyen Parser robuste aux variations mineures, version de schéma documentée
OCSP/CRL endpoints tiers indisponibles (H-02) tech moyen élevé Timeouts configurables, fail-closed par design
STUBs PD-37 non implémentés à temps métier moyen moyen Audit signing est non-CRITICAL pour le fonctionnement de la machine d'états

8bis. Matrice de délégation inter-PD

Story Direction Statut Nature de la dépendance Problème rencontré
PD-37 ← dépend de TODO AuditSignatureService, JsonCanonicalizeService, AuditLogService STUB — signatures d'événements d'audit non réelles
PD-39 ← dépend de DONE Learning fail-closed NTS/TSA RAS — learning bien intégré dans INV-265-01
PD-63 ← dépend de DONE Convention labels HSM pv-tsa-signing-YYYY RAS — INV-265-03/04
PD-264 ← dépend de DONE Nonce anti-rejeu context RAS — hors périmètre
PD-282 ← dépend de DONE Learning roundtrip sign+verify, trustedRoots obligatoire RAS — INV-265-10, learning intégré
PD-7 ← dépend de DONE Cluster HSM, PKCS#11 provider RAS — mock CI existant
PD-55 ← dépend de DONE BullMQ v5 API, worker d'ancrage RAS — API migrée
PD-265 phase 2 → bloque TODO OCSP/CRL/TL réels, TST detection réelle À surveiller — 4 STUBs en attente

8ter. Bugs de tests

Aucun bug de test rencontré. Les 222 tests ont passé en première exécution après implémentation.

8quater. Corrections post-Gate 8

Aucune correction post-Gate 8 nécessaire. Gate 8 GO en v1 (8.25/10).

9. Patterns récurrents détectés

9.1 Patterns confirmés (déjà vus dans d'autres stories)

  • Machine à états composite avec transitions interdites — aussi dans PD-82, PD-250, PD-264, PD-279, PD-280, PD-278, PD-282. Le pattern flags-as-source-of-truth + derived-state est une variante plus robuste que le state-column direct.
  • Stubs inter-PD acceptés si documentés avec story destination — aussi dans PD-63, PD-82, PD-250, PD-251, ..., PD-282. 6 STUBs tracés vers PD-37 et PD-265 phase 2, tous acceptés.
  • Format non contractualisé dans spec v1 — aussi dans PD-32, ..., PD-282. La spec v1 manquait de formalisme sur les formats (tsaServiceDegradationFlags tri lexicographique, refusalReasonCode enum, signatureKeyId).
  • Faux positifs LLM en reviews d'acceptabilité — aussi dans PD-251, ..., PD-282. 5 findings rejetés après analyse contextuelle. Les grilles LLM ne distinguent pas STUBs tracés des fonctions manquantes.
  • Guard de sécurité fail-closed obligatoire — aussi dans PD-238, ..., PD-282. TstEmissionGuard + canEmitTst() + canRehorodatage() avec refus explicite et motif normé.
  • Atomicité : ACID synchrone + post-commit async — aussi dans PD-55, PD-264, ..., PD-282. Persistance flags DB (transaction) + émission métriques/événements (async post-commit).
  • Double NON_CONFORME Gate 3+5 v1 = complexité conceptuelle élevée — aussi dans PD-280, PD-278. Gate 3 v1 à 5.875 et Gate 5 v1 à 8.0 (RESERVE), confirmant que 3 itérations structurelles sont nécessaires.

9.2 Nouveaux patterns identifiés

  • Flags-as-source-of-truth + derived-state pour machine composite — nouveau pattern architectural : stocker les conditions individuelles (flags) en base et dériver l'état visible par calcul. Élimine les incohérences état/flags. À surveiller dans les prochaines stories avec machine d'états multi-conditions.
  • Clearing conditionnel avec N contrôles consécutifs conformes — le clearing NTS requiert 2 cycles conformes sur TOUS les serveurs. Ce pattern de stabilisation avant retour à l'état nominal est nouveau et pourrait s'appliquer à d'autres domaines (ex: monitoring réseau, vérification de certificats).
  • SLA post-rotation enforcés par alerte uniquement (pas par flag de dégradation) — les transitions RETIRED→ARCHIVED et ARCHIVED→DESTROYED ne déclenchent pas de flag de dégradation car ce sont des actions opérateur. L'enforcement par alerte CRITICAL est un compromis nouveau.
  • Contrat de refus API avec motif normé et état courant — le pattern refusalReasonCode + tsaServiceState + tsaServiceDegradationFlags dans la réponse de refus est réutilisable pour tout endpoint gardé par une machine d'états.

10. Améliorations du workflow

10.1 Améliorations des prompts/templates

Fichier Amélioration suggérée Priorité
templates/prompts/1 Specification.md Pour les stories avec machine d'états multi-conditions, exiger dès v1 : flags comme source de vérité, état dérivé par calcul, clearing conditions, politique par état haute
templates/prompts/1 Specification.md Ajouter section obligatoire "Contrat de refus API" pour toute story avec guard de blocage (motif normé, état, flags) moyenne
templates/prompts/5 Plan Review.md Lors de stagnation delta=0 en Gate 5, forcer la résolution structurelle (pas incrémentale) — cf. Gate 5 v2 STOP convergence moyenne
templates/prompts/7a Review Code.md Injecter la distinction STUB-tracé vs fonction-manquante pour réduire les faux positifs LLM (récurrence : 10 stories) haute

10.2 Améliorations des agents

Agent Amélioration suggérée Justification
config/agents step 1 Pour les stories monitoring/observabilité, injecter la nomenclature de métriques existante du projet (préfixes, labels, cardinalité) Évite les corrections R-15/16 en Gate 3
config/agents step 4 Quand la spec définit un modèle d'états composite (>= 4 états), générer automatiquement le mapping état→politique d'action (émission, re-horodatage, etc.) Réduit les ambiguïtés AMB en Gate 5

10.3 Améliorations du processus

  • Pré-screening du modèle d'états en étape 0 : si le besoin implique >= 4 conditions de dégradation, le PO devrait valider explicitement le choix flags-as-source vs state-column avant la spec. Cela aurait évité 2 des 3 itérations de Gate 3.
  • Gate 5 convergence STOP avec refonte structurelle : la règle de stagnation delta=0 (CONSTITUTIONAL Art. I) a forcé une correction v3 alors que le score était déjà 8.125. Le pattern "STOP → refonte → v3 GO" a bien fonctionné ici mais le temps passé (58 min de correction structurelle entre v2 et v3) pourrait être réduit par une checklist de points structurels à vérifier systématiquement.

11. Enseignements clés

  1. Flags-as-source-of-truth élimine les incohérences — pour toute machine d'états composite, stocker les conditions individuelles et dériver l'état par calcul. Ne JAMAIS stocker un état pré-calculé qui pourrait diverger des flags.

  2. Le contrat de refus API est un livrable de premier ordre — définir refusalReasonCode + état/flags dans la réponse de refus dès la spec, pas en Gate 5. Cela économise une itération de gate.

  3. La complexité Gate 3 se paie en spec, pas en implémentation — Gate 3 à 5.875/10 (3 itérations, 20 écarts) mais implémentation en 59 min. L'investissement en formalisme de spec a un ROI direct sur la vitesse d'implémentation.

  4. SLA post-rotation par alerte ≠ par flag de dégradation — les actions opérateur (destruction de clé) ne doivent pas être couplées aux flags automatiques. L'enforcement par alerte CRITICAL est le bon pattern quand l'action finale est humaine.

  5. Le clearing conditionnel (N cycles conformes) est plus sûr que le clearing immédiat — pour les mécanismes fail-closed, ne pas revenir à HEALTHY dès le premier contrôle OK. 2 cycles consécutifs conformes sur TOUS les serveurs est un bon compromis entre réactivité et stabilité.

12. Métriques cumulatives (auto-calculées)

Métrique Cette story Moyenne projet Tendance
Temps total 5.5h 5.82h → stable
Itérations gates 7 5.7 ↑ au-dessus
Écarts totaux 27 21.2 ↑ au-dessus
Score convergence moyen 8.54/10 8.58 → stable