PD-280 — Specification Review (v3)¶
Date : 2026-03-01 Reviewer : Claude (MODE FACTUEL) Documents d'entrée : PD-280-specification.md (v3 corrigée), PD-280-tests.md (v3 corrigés) Learnings injectés : PD-81 (SLA temporels), PD-275 (alignement spec↔tests), PD-250 (contractualisation mécanismes), PD-31 (faux positifs)
Vérification des corrections v2 → v3¶
| Écart v2 | Statut v3 | Justification |
|---|---|---|
| #1 (MAJEUR) — « même vérification » non défini | RÉSOLU | AMB-V2-01 : verificationRequestId (UUID serveur) introduit comme identifiant technique ; INV-280-06 reformulé sur cet identifiant. |
| #2 (MAJEUR) — Mécanisme SLA non spécifié | RÉSOLU | AMB-V2-02 : Réévaluation SLA spécifiée en mode lazy sur appel API ; aucun worker background dans le périmètre. |
| #3 (MAJEUR) — Hypothèse mono-thread dangereuse | RÉSOLU | AMB-V2-03 : INV-280-09 (idempotence concurrente) ajouté ; endpoint lecture seule ; résolution des maillons par processus externes. H-280-06 reformulé. |
| #8 (MAJEUR) — TC-INV-06 oracle non déterministe | RÉSOLU | ECT-V2-01 : Oracle tranché — tentative DONE→PENDING retourne la réponse DONE existante à l'identique (idempotence, pas rejet). |
#4 (Mineur) — pendingResolutionTtl bornes | PARTIELLEMENT RÉSOLU | SCN-280-09 et TC-NEG-09 reformulés mais comportement hors bornes reste « rejet/normalisation selon mécanisme de config ». Voir écart résiduel #3 ci-dessous. |
| #5 (Mineur) — ERR-280-07 couverture | NON RÉSOLU | Voir écart résiduel #4. |
#6 (Mineur) — PENDING_FINALITY non défini | NON RÉSOLU | Voir écart résiduel #5. |
| #7 (Mineur) — TC-INV-05 mécanisme forcé | NON RÉSOLU | Voir écart résiduel #6. |
| #9 (Mineur) — « absent » vs « null » JSON | NON RÉSOLU | Voir écart résiduel #1. |
| #10 (Mineur) — TC-NOM-07 critère « preuve explicite » | NON RÉSOLU | Voir écart résiduel #7. |
| #11 (Mineur) — Dérive d'horloge | NON RÉSOLU | Voir écart résiduel #8. |
Écarts identifiés (v3)¶
1¶
Type : Ambiguïté Référence : PD-280-specification.md §4 (INV-280-03-pending-fields), §5.1 — PD-280-tests.md §3 (TC-NOM-02 « absent ») Description : INV-280-03 spécifie que pendingReason et nextCheckAfterSeconds sont « interdits » si verificationStatus = DONE. Le terme « interdits » en contexte de sérialisation JSON peut signifier : (a) clé omise du payload JSON, (b) clé présente avec valeur null. TC-NOM-02 utilise « absent », TC-ERR-04 utilise « présents ». La frontière entre « absent » (clé absente) et « présent avec null » n'est pas contractualisée dans §5.1 (table des formats). Cette distinction impacte la validation de schéma JSON (additionalProperties: false, required). Impact : Un consommateur API avec validation stricte JSON Schema pourrait rejeter un payload conforme si l'interprétation diffère. Gravité : Mineur
2¶
Type : Ambiguïté Référence : PD-280-specification.md §5.1 (verificationRequestId), §5.3 (Règles idempotentes d'appel) Description : §5.3 stipule : « Si un résultat DONE existe déjà pour le proofId dans ce périmètre, l'endpoint retourne cette réponse DONE à l'identique (y compris verificationRequestId) ». Cependant, la spec ne définit pas le comportement au premier appel quand la preuve est déjà dans un état résolu (tous maillons terminaux) mais qu'aucun verificationRequestId n'existe encore. Le verificationRequestId est-il matérialisé au premier appel, persisté, puis retourné à l'identique ? Ou est-il généré à chaque appel mais stable pour un même proofId résolu ? Le mécanisme de persistance de cet identifiant n'est pas spécifié. Impact : Sans convention de persistance, deux implémentations conformes pourraient diverger : l'une générant un nouvel UUID à chaque appel, l'autre retournant toujours le même. TC-NOM-09 teste la stabilité du verificationRequestId sur rappel DONE sans que la spec contractualise le moment de matérialisation. Gravité : Mineur
3¶
Type : Ambiguïté Référence : PD-280-specification.md §5.2 (pendingResolutionTtl), SCN-280-09, PD-280-tests.md §7 (TC-NEG-09) Description : Le comportement aux bornes de pendingResolutionTtl reste indéterminé. Pour nextCheckAfterSeconds, §5.1 contractualise explicitement un « clamp serveur en sortie vers [1,86400] ». Pour pendingResolutionTtl, §5.2 dit « Configurable (paramètre applicatif) » avec min=1h et max=30j, sans préciser le comportement hors bornes. TC-NEG-09 reflète cette indétermination : « rejet de configuration ou normalisation selon mécanisme contractuel de config ». L'asymétrie entre les deux paramètres temporels (clamp explicite vs mécanisme non spécifié) crée une incohérence contractuelle. Impact : TC-NEG-09 accepte deux comportements mutuellement exclusifs (rejet OU normalisation), le rendant non discriminant comme oracle de test. Gravité : Mineur
4¶
Type : Incohérence Spec↔Tests Référence : PD-280-specification.md §6 (ERR-280-07), PD-280-tests.md §2 (Matrice de couverture), §7 (TC-NEG-04) Description : ERR-280-07 spécifie que linkResults[*] exposant "PENDING" en API doit déclencher 500 VERIFICATION_CONTRACT_BROKEN. TC-NEG-04 couvre le cas verificationStatus=DONE + linkResults[*]="PENDING" → 500, mais (a) le cas verificationStatus=PENDING avec linkResults[*]="PENDING" (au lieu de null) n'est pas couvert, et (b) ERR-280-07 n'apparaît pas dans la matrice de couverture §2. La spec interdit la valeur littérale "PENDING" en API dans TOUS les cas (§5.7), pas seulement quand le job est DONE. Impact : Un cas d'erreur spécifié n'a pas de traçabilité de couverture complète dans la matrice. Gravité : Mineur
5¶
Type : Ambiguïté Référence : PD-280-specification.md §7 (CA-03), §8 (SCN-280-03), §3 (Définitions) Description : PENDING_FINALITY est utilisé dans CA-03 et SCN-280-03 comme un état métier déclencheur (« Given un batch en PENDING_FINALITY »), mais n'est défini ni dans §3 (Définitions) ni dans l'enum pendingReason (§5.1). Le mapping entre PENDING_FINALITY (état de batch d'ancrage) et les valeurs pendingReason (ANCHOR_BATCH_NOT_FINALIZED, TX_NOT_ENOUGH_CONFIRMATIONS) est implicite et non contractualisé. Impact : Une équipe tierce doit déduire quel pendingReason associer à PENDING_FINALITY. Si le mapping est incorrect, CA-03 et TC-NOM-03 produisent des résultats erronés. Gravité : Mineur
6¶
Type : Incohérence Spec↔Tests Référence : PD-280-tests.md §5 (TC-INV-05), PD-280-specification.md §5.3 Description : TC-INV-05 teste « une tentative de réévaluation forcée cherche à remettre ce maillon en PENDING ». La spécification ne définit aucun mécanisme de « réévaluation forcée » ni aucune API/commande permettant de tenter une transition retour. Le THEN (« La transition est refusée de manière déterministe ») est correct par INV-280-05, mais le GIVEN/WHEN n'a pas de fondement contractuel : il présuppose un vecteur d'attaque ou une interface interne non spécifié. Impact : Le scénario de test n'est pas reproductible à partir de la spec seule — une équipe tierce ne sait pas COMMENT provoquer cette « tentative de réévaluation forcée ». Le test est conceptuellement correct mais opérationnellement non implémentable sans interprétation. Gravité : Mineur
7¶
Type : Non testable Référence : PD-280-tests.md §3 (TC-NOM-07 AND), PD-280-specification.md §7 (CA-06) Description : TC-NOM-07 AND stipule « Le rapport de build contient la preuve explicite de l'égalité des ensembles ». Le critère « preuve explicite » n'est pas défini : format de sortie TLC attendu ? Ligne de log spécifique ? Pattern grep-able dans le rapport CI ? Sans critère d'assertion précis, le test est subjectif. Impact : Deux auditeurs pourraient diverger sur la conformité du même rapport de build. Gravité : Mineur
8¶
Type : Hypothèse dangereuse Référence : PD-280-specification.md §5.2 (SLA temporels), PD-280-tests.md §3 (TC-NOM-01 « Horloge de test contrôlée », TC-NOM-05) Description : Les règles SLA (pendingResolutionTtl, nextCheckAfterSeconds) reposent sur une horloge fiable sans borne de dérive. En environnement distribué (cluster multi-nœuds), les horloges peuvent diverger de plusieurs secondes. La spec ne contractualise aucune politique de tolérance de dérive. TC-NOM-01 mentionne « Horloge de test contrôlée » (correct pour les tests) mais pas pour la production. Impact : Reclassification prématurée ou tardive de maillons PENDING → INDETERMINATE si dérive d'horloge > granularité du SLA en production. Gravité : Mineur
9¶
Type : Incohérence Spec↔Tests Référence : PD-280-specification.md §6 (ERR-280-03), PD-280-tests.md §4 (TC-ERR-03) Description : TC-ERR-03 GIVEN dit « Réponse candidate incohérente (verificationStatus="DONE" avec au moins un linkResults[]=null) ». Le test valide le contrôle de cohérence de sortie. Cependant, la spec (INV-280-09, §5.3) stipule que l'endpoint de vérification est en *lecture seule** — il lit et retourne un état déjà persisté. Si l'état persisté est incohérent, c'est un bug de persistance, pas un cas d'erreur de l'endpoint de vérification. La spec ne précise pas QUI effectue ce « contrôle de cohérence de sortie » : le mapper DTO ? Un guard NestJS ? Un interceptor ? L'absence de localisation contractuelle du contrôle rend le test implémentable par plusieurs mécanismes incompatibles. Impact : L'emplacement du contrôle de cohérence impacte le comportement observable (le 500 est-il déclenché avant ou après la sérialisation ? Le body d'erreur est-il généré par le même pipeline que les erreurs 400 ?). Impact faible car le résultat HTTP final est le même. Gravité : Mineur
Synthèse¶
| Gravité | Nombre |
|---|---|
| Bloquant | 0 |
| Majeur | 0 |
| Mineur | 9 |
Corrections v2 → v3 vérifiées : Les 4 écarts MAJEUR de la review v2 ont été résolus par les corrections AMB-V2-01, AMB-V2-02, AMB-V2-03 et ECT-V2-01. La spec v3 est significativement plus robuste.
Écarts résiduels : 9 écarts MINEUR subsistent, dont 7 reportés de la v2 (non corrigés car mineurs) et 2 nouveaux (écart #2 — persistance verificationRequestId ; écart #9 — localisation contrôle de cohérence). Aucun ne bloque l'implémentation ni l'acceptation formelle, mais leur documentation sert de mise en garde pour l'équipe d'implémentation.
Points forts de la v3 : - Introduction de verificationRequestId élimine l'ambiguïté pivot de la v2 - Mode lazy explicitement contractualisé avec périmètre clair (pas de worker background) - INV-280-09 (idempotence concurrente) résout proprement la question de concurrence sans locking - Oracle TC-INV-06 tranché de manière déterministe (idempotence, pas rejet)