Aller au contenu

PD-47 — Specification Review (v2)

Auditeur : ChatGPT (review indépendante) Documents audités : PD-47-specification.md (post-CORR E-01→E-18), PD-47-tests.md (post-CORR) Date : 2026-03-03 Itération : v2 (après corrections des 18 écarts v1)


Vérification des corrections v1

Les 18 écarts identifiés en v1 ont tous reçu une correction explicite dans la spécification et/ou les tests. Vérification point par point :

Écart v1 Correction Statut
E-01 (charset vs regex s3_object_key_wal) Charset corrigé en [a-zA-Z0-9/_\.-] incluant uppercase ✅ Résolu
E-02 (FAILED_FORMAT non défini) Explicitement qualifié "code de rejet" hors enum status et machine d'états ✅ Résolu
E-03 (SCHEDULED absent enum status) Ajouté dans l'enum status §5.1 ✅ Résolu
E-04 (cardinalité "exactement un" vs "au moins un") INV-47-05 et §5.6 harmonisés sur "au moins un" + déduplication ✅ Résolu
E-05 (fréquence max 48h > RPO 24h) Max fréquence réduit à 24h ✅ Résolu
E-06 (terminologie full vs pg_dump) Regex backup_id corrigée avec pg_dump ✅ Résolu
E-07 (bornes longueur backup_id) Corrigé en "18 à 56 caractères" — cohérent avec regex ✅ Résolu
E-08 (seuil suppression min/max incohérent) Min=Max=300 (non configurable), RPO Min=Max=24 ✅ Résolu
E-09 (réconciliation non observable) Observabilité ajoutée (trace/log/événement d'audit) ✅ Résolu (résiduel mineur — cf. R-07)
E-10 (crash scenarios non testés) TC-NOM-07 et TC-NOM-08 ajoutés ✅ Résolu
E-11 (retry vs transition FAILED→SCHEDULED) Clarification ajoutée §5.7 : retries internes vs replanification ✅ Résolu (résiduel — cf. R-01)
E-12 (WAL hash at upload) Hash SHA3-256 par segment WAL avant upload ajouté à F2 ✅ Résolu
E-13 (rotation K_backup) §10.3 ajouté avec exigences de compatibilité ✅ Résolu (résiduel mineur — cf. R-06)
E-14 (backoff sans délai initial) Paramètre "Délai initial backoff" ajouté §5.8 ✅ Résolu
E-15 (synchronisation horaire) H-47-06 ajoutée (NTP) ✅ Résolu
E-16 (périodicité trimestrielle non contractualisée) Contractualisée à 90 jours dans §5.8 ✅ Résolu
E-17 (relance unique sans escalade) Escalade critique ajoutée si relance échoue ✅ Résolu
E-18 (cycle de vie DEK) §10.4 ajouté avec cycle complet ✅ Résolu

Écarts résiduels et nouveaux écarts identifiés

R-01 — Replanification FAILED→SCHEDULED non bornée

Type : Hypothèse dangereuse Référence : Spec §5.7 (transition FAILED→SCHEDULED), §5.8 (paramètre "Retry pg_dump/upload") Description : La clarification [CORR E-11] distingue correctement les retries internes (max 3, dans la même tentative RUNNING) de la replanification (transition FAILED→SCHEDULED). Cependant, aucune borne n'est définie sur le nombre de cycles FAILED→SCHEDULED→RUNNING→FAILED. Le paramètre "Retry" en §5.8 couvre uniquement les retries internes. Un échec persistant (ex : bucket S3 inaccessible durablement) entraînerait un cycle infini de replanifications, avec accumulation d'événements journal, d'alertes, et de consommation de ressources. Impact : Risque de boucle infinie opérationnelle. Aucun seuil d'escalade ni de circuit-breaker n'est contractualisé pour ce scénario. Gravité : Majeur


R-02 — Hash SHA3-256 : moment de calcul non spécifié pour F1 et F3

Type : Ambiguïté Référence : Spec §5.2 (F1), §5.4 (F3), §5.1 (champ hash_sha3_256), F2 [CORR E-12] Description : Le champ hash_sha3_256 est contractualisé dans le modèle de données §5.1 pour tout événement de backup. Le flux F2 (WAL) précise explicitement [CORR E-12] que le hash est calculé par segment WAL avant upload (donc sur le clair avant chiffrement). En revanche : - F1 (backup logique quotidien) ne mentionne aucune étape de calcul de hash. Le hash apparaît implicitement dans l'événement journal (étape 6), mais on ignore s'il porte sur le dump clair, le dump compressé, ou l'artefact chiffré. - F3 (basebackup hebdomadaire) ne mentionne le hash nulle part dans son flux.

Cette asymétrie rend le comportement de hachage indéterminé pour 2 des 3 types de backup. Impact : L'implémentation ne peut pas déterminer quoi hasher ni quand pour pg_dump et basebackup. La vérification d'intégrité en F4 (restauration) est compromise si le hash n'est pas calculé de manière cohérente entre les types. Gravité : Majeur


R-03 — Tests d'invariants (TC-INV-*) sans scénarios formalisés

Type : Incohérence Spec↔Tests Référence : Tests §2 (matrice), §5 (table invariants), comparaison avec §3-§4 (TC-NOM/TC-ERR) Description : Les 9 tests d'invariants (TC-INV-01 à TC-INV-09) sont référencés dans la matrice de couverture (§2) avec "Couverture: Oui" et décrits dans la table §5 avec observable et commentaire. Cependant, aucun d'entre eux ne dispose d'un scénario GIVEN/WHEN/THEN formalisé, contrairement aux 8 TC-NOM et 8 TC-ERR qui sont tous détaillés. Les observables en §5 décrivent le quoi vérifier mais pas les préconditions ni les actions de déclenchement.

Exemples : - TC-INV-06 (indépendance watchdog) : observable = "Détection watchdog maintenue malgré panne orchestrateur" — comment injecter la panne ? Quel type de panne (process kill, network isolation, host down) ? - TC-INV-09 (envelope encryption DEK) : observable = "Aucun artefact crypto temporaire en clair au repos" — comment observer l'absence en DB/disque ? Scan mémoire ? Inspection base ? Impact : Les tests d'invariants — les plus critiques par définition — sont les moins formalisés du document. Un testeur tiers ne pourrait pas les implémenter sans interprétation. Gravité : Majeur


R-04 — s3_object_key_backup : plage de longueur déconnectée de la regex

Type : Ambiguïté Référence : Spec §5.1, champ s3_object_key_backup Description : La longueur est spécifiée "24 à 128 caractères". La regex ^[0-9]{4}/[0-9]{2}/backup_[0-9]{8}\.enc$ produit une longueur fixe de 27 caractères (2026/03/backup_20260303.enc). La plage 24-128 est trompeuse : aucune valeur de 24-26 caractères ni de 28-128 caractères ne peut satisfaire la regex. Impact : Mineur — la regex est la contrainte effective, mais la plage de longueur pourrait induire en erreur une validation en deux passes (longueur d'abord, regex ensuite). Gravité : Mineur


R-05 — size_bytes : regex autorise des valeurs au-delà de Number.MAX_SAFE_INTEGER

Type : Ambiguïté Référence : Spec §5.1, champ size_bytes Description : La spécification indique "max 53 bits JS-safe" (soit Number.MAX_SAFE_INTEGER = 9 007 199 254 740 991) mais la regex ^[0-9]{1,16}$ autorise des valeurs jusqu'à 9 999 999 999 999 999. Les valeurs dans l'intervalle ]9 007 199 254 740 991, 9 999 999 999 999 999] satisfont la regex mais dépassent la limite JS safe integer, entraînant une perte de précision silencieuse. Impact : Risque de corruption de la valeur size_bytes sans erreur explicite dans un runtime JavaScript. Le comportement "rejet" spécifié ne serait pas déclenché car la regex est satisfaite. Gravité : Mineur


R-06 — Rotation K_backup : fenêtre de rétention des anciennes versions de clé

Type : Ambiguïté Référence : Spec §10.3 Description : §10.3 exige que "la rotation ne doit pas rendre les backups historiques indéchiffrables (compatibilité de déchiffrement avec versions précédentes)". La durée de cette compatibilité n'est pas bornée. Doit-elle couvrir uniquement la fenêtre de rétention S3 (30 jours) ? Ou toute la durée de conservation légale ? Le nombre de versions de clé à maintenir simultanément actives n'est pas défini. Impact : Sans borne, toutes les anciennes versions de K_backup doivent être conservées indéfiniment, créant un risque de surface cryptographique croissante. Gravité : Mineur


R-07 — Réconciliation post-crash : pas de SLA de délai ni de comportement en cas d'échec

Type : Non testable Référence : Spec §5.6 (crash post-commit état local) [CORR E-09] Description : La correction E-09 a ajouté l'observabilité de la réconciliation (trace/log/événement d'audit). Cependant, trois aspects restent non spécifiés : 1. Déclencheur : quand la réconciliation démarre-t-elle (au redémarrage ? sur cron ? sur détection d'incohérence ?) ; 2. SLA : délai maximal pour atteindre la cohérence après crash ; 3. Échec de réconciliation : comportement si la réconciliation elle-même échoue.

TC-NOM-08 teste que la réconciliation "est exécutée jusqu'à cohérence" et "observable", mais sans SLA, le test ne peut pas définir de timeout d'attente. Impact : Test TC-NOM-08 implémentable pour vérifier l'exécution, mais pas pour valider la complétude dans un délai borné. Gravité : Mineur


R-08 — Backoff retry : délai initial min=0 autorise des retries instantanés

Type : Hypothèse dangereuse Référence : Spec §5.8 (paramètre "Délai initial backoff") Description : Le délai initial de backoff a pour bornes min=0, max=60000 ms [CORR E-14]. Combiné avec le facteur min=1 (§5.8), une configuration delay_initial=0, factor=1 produit des retries à 0ms, 0ms, 0ms — soit 3 tentatives instantanées sans pause. Cela peut aggraver un problème transitoire (ex : surcharge S3) au lieu de le laisser se résorber. Impact : Configuration valide mais potentiellement nocive. Aucune validation croisée (ex : delay_initial >= 100 si factor == 1) n'est spécifiée. Gravité : Mineur


R-09 — CA-47-07 ne vérifie pas la périodicité contractuelle de 90 jours

Type : Incohérence Spec↔Tests Référence : Spec §5.8 (périodicité 90 jours [CORR E-16]), CA-47-07, TC-ERR-08 Description : §5.8 contractualise la périodicité du test de restauration staging à 90 jours (non configurable). TC-ERR-08 couvre le cas d'échec du test trimestriel. Cependant, aucun critère d'acceptation ni test ne vérifie que le test de restauration est effectivement exécuté tous les 90 jours. CA-47-07 vérifie uniquement qu'une restauration staging est "conforme" (hash + cohérence), sans condition de fréquence. Un système qui exécute un seul test de restauration en 365 jours (au lieu de 4) satisferait CA-47-07 tout en violant §5.8. Impact : La périodicité contractualisée n'est pas couverte par les critères d'acceptation ni les tests. Vérifiable uniquement par audit opérationnel externe. Gravité : Mineur


Synthèse

Gravité Nombre IDs
Bloquant 0
Majeur 3 R-01, R-02, R-03
Mineur 6 R-04, R-05, R-06, R-07, R-08, R-09
Total 9

Évolution v1 → v2

Métrique v1 v2 Δ
Bloquant 4 0 −4
Majeur 10 3 −7
Mineur 4 6 +2
Total 18 9 −9

Les 4 bloquants v1 sont tous résolus. Les 3 majeurs résiduels portent sur des lacunes de second ordre (bornage d'un cycle opérationnel, asymétrie de spécification entre types de backup, formalisation des tests d'invariants) et ne remettent pas en cause l'architecture ni la cohérence fondamentale de la spécification.

Majeurs à résoudre avant Gate 3 GO

  1. R-01 : Définir un nombre maximal de cycles FAILED→SCHEDULED (ou un circuit-breaker) avec escalade après épuisement.
  2. R-02 : Ajouter explicitement l'étape de calcul hash SHA3-256 dans les flux F1 (après production dump, avant chiffrement) et F3 (après basebackup, avant chiffrement), par cohérence avec F2.
  3. R-03 : Formaliser les 9 scénarios TC-INV-* en GIVEN/WHEN/THEN avec préconditions, actions de déclenchement et observables explicites.