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 (
tsaServiceDegradationFlagstri lexicographique,refusalReasonCodeenum,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→ARCHIVEDetARCHIVED→DESTROYEDne 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+tsaServiceDegradationFlagsdans 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¶
-
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.
-
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. -
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.
-
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.
-
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 |