CONFRONTATION Gate 5 — PD-254 : Protocole formel de migration probatoire vérifiable¶
1. Sources confrontées¶
| Document | Version | Taille |
|---|---|---|
| PD-254-specification.md | Gate 3 RESERVE v2 | §1-§10, 11 invariants, 10 états, 18 CA, 10 scénarios |
| PD-254-tests.md | Post-Gate 3 | 8 flux nominaux, 12 erreurs, 11 invariants, 11 négatifs, 8 spécifiques |
| PD-254-plan.md | Étape 4 DRAFT | 8 composants (C1-C8), 13 constats Gate 3 traités, 6 hypothèses techniques |
| PD-254-code-contracts.yaml | Étape 4 | 8 modules, 4 décisions architecturales |
2. Convergences (points confirmés entre review et documents)¶
C-01 — Mapping invariants exhaustif : La review confirme que les 11 invariants sont tous mappés à des mécanismes explicites. Vérifié dans le plan §3 (table mapping invariants → mécanismes) et les code-contracts (chaque module liste ses invariants). Convergence totale.
C-02 — Résolutions Gate 3 documentées : La review confirme que R-01 à R-08 sont traités. Le plan §13 documente explicitement les 8 constats avec hypothèses H-T01 à H-T06. Les code-contracts reprennent H-T01/H-T02 dans migration-state-machine. Convergence totale.
C-03 — Architecture 8 composants cohérente : Le découpage C1-C8 du plan est cohérent avec la spec (flux F1-F5, §5.10, §5.13) et les code-contracts (8 modules avec fichiers, interfaces, invariants, forbidden). Convergence totale.
C-04 — Canonicalisation RFC 8785 : La review mentionne le choix canonicalize (npm) sans le contester. Le plan §1 C3 et AD-01 documentent ce choix. Les code-contracts manifest-service l'imposent. Convergence totale.
C-05 — Protection manifest (INV-254-12) : La review ne conteste pas les mécanismes de protection manifest. Le plan (C3 ManifestService, C4 VerificationEngine step 4/7), la spec (§5.6, §5.7), les tests (TC-INV-12, TC-ERR-11) et les code-contracts sont alignés. Convergence totale.
C-06 — Clearing conditionnel persisté en DB : La review ne conteste pas ce choix. Le plan §9 point 7 et AD-04 justifient la persistance DB vs Redis. Les code-contracts distributed-protection l'imposent explicitement. Convergence totale.
C-07 — Learnings PD-282/PD-85/PD-63 intégrés : La review ne signale aucun oubli de learnings. Le plan §7 et les code-contracts intègrent : crypto.verify(null, ...) (PD-282), fail-closed audit (PD-85), crypto.randomUUID() (PD-63), labels HSM pv-test-* (PD-63). Convergence totale.
3. Divergences (contradictions, écarts review vs documents)¶
DIV-01 — Framework de test non spécifié (Review écart 1 — MAJEUR)¶
Review : Le plan ne spécifie pas le framework de test (Jest ou Vitest).
Documents : Le plan §11 liste les niveaux de test (unitaire, intégration, E2E) sans nommer le runner. Les code-contracts ne mentionnent pas de framework. La spec §10.2 indique "NestJS + TypeORM + PostgreSQL" — NestJS utilise Jest par défaut.
Verdict : Écart CONFIRMÉ mais sévérité RÉDUITE à MINEUR. Le projet ProbatioVault-backend utilise NestJS qui embarque Jest nativement. Le choix est implicite par la stack. Documenter explicitement "Jest" dans le plan §11 suffit. L'argument ESM-only de canonicalize est pertinent mais relève de la configuration tsconfig/jest.config, pas du choix de runner.
DIV-02 — Statut dépendances inter-PD absent (Review écart 2 — MAJEUR)¶
Review : Les dépendances PD-44, PD-54, PD-55, PD-36/37 sont mentionnées sans statut (DONE/TODO/STUB).
Documents : Le plan §9 point 4 reconnaît le risque ("interfaces doivent être stables") mais ne donne pas le statut. Les code-contracts ne listent pas les interfaces consommées inter-PD. La spec §5.13 se limite à "absence d'une donnée obligatoire => campagne KO".
Verdict : Écart CONFIRMÉ, sévérité MAJEUR maintenue. Le plan doit expliciter le statut de chaque dépendance. PD-44 (WORM) et PD-54 (Merkle) sont des prérequis fonctionnels — si non DONE, des stubs documentés (avec story destination, cf. learning PD-250/PD-251) sont nécessaires.
DIV-03 — Compatibilité ESM/CJS de canonicalize (Review écart 3 — MAJEUR)¶
Review : canonicalize v2 est ESM-only, incompatible avec NestJS CJS par défaut.
Documents : Le plan §1 C3 choisit canonicalize (npm) sans mentionner la version ni la compatibilité ESM. AD-01 justifie RFC 8785 sans aborder l'intégration. Les code-contracts manifest-service imposent la bibliothèque sans contrainte de version.
Verdict : Écart CONFIRMÉ, sévérité RÉDUITE à MINEUR. Le package canonicalize (npm) v1.x supporte CJS. La v2 n'est pas publiée au 2026-03-13. Le plan doit figer la version (canonicalize@^1.0.0) et ajouter un invariant : "si migration vers v2 ESM-only, vérifier compatibilité NestJS build". Risque actuel faible.
DIV-04 — Seed de sampling non spécifié pour reproductibilité (Review écart 4 — MAJEUR)¶
Review : Le mécanisme de seed déterministe pour ReadabilityCheck n'est pas spécifié. INV-254-09 exige un résultat déterministe.
Documents :
- Spec §3 : "artefacts logiques (verdict, écarts, hash objets) sont identiques" — le sampling est un mécanisme, pas un artefact logique.
- Plan §3 INV-254-09 : "Seed de sampling doit être figé pour reproductibilité" (colonne Risque).
- Code-contracts
verification-engine: "Utiliser crypto.randomUUID() ou seed déterministe" (forbidden). - Tests TC-NOM-03 : "seed déterministe ou liste d'objets explicitée".
- Tests TC-INV-09 : "Horloge et source d'aléa contrôlées".
Verdict : Écart PARTIELLEMENT CONFIRMÉ, sévérité RÉDUITE à MINEUR. Le plan identifie le risque (§3, colonne Risque). Les tests exigent un seed contrôlé. Le mécanisme concret (seed persisté en DB ? dérivé de migration_id ?) n'est pas spécifié, mais c'est un détail d'implémentation. La spec définit la reproductibilité sur les "artefacts logiques" — le sampling produit toujours le même verdict si les objets sont identiques. Le plan devrait toutefois documenter le mécanisme de dérivation du seed (ex: SHA3-256(migration_id + "sampling") tronqué).
DIV-05 — Fencing token absent sur lock distribué (Review écart 5 — MAJEUR)¶
Review : Le lock Redis SET NX sans fencing token expose au scénario classique client A retardé au-delà du TTL.
Documents :
- Spec §5.10 : "Lock distribué, Scope par migration_id, TTL lock_ttl, Si lock non acquis → CONCURRENT_EXECUTION_DENIED". Pas de fencing token.
- Plan §1 C6 : "SET NX" sans fencing.
- Code-contracts
distributed-protection: "Lock distribué : clé migration, SET NX, TTL configurable". Interdit "Supprimer le lock manuellement sans vérifier l'ownership".
Analyse : Le scénario redouté par la review (deux clients en parallèle) est réel en théorie. Cependant :
- Le protocole PD-254 est batch/offline — une campagne est lancée par un initiateur unique (service account ou DevOps), pas par des requêtes concurrentes massives.
- Le rate-limiting (3/h/initiateur) limite drastiquement la probabilité de collision.
- L'idempotence (§5.10) garantit que deux exécutions de la même phase retournent le même résultat — le risque n'est pas la corruption mais la double-exécution inutile.
- La spec ne mentionne pas de fencing token — l'ajouter serait un ajout non spécifié.
Verdict : Écart CONTESTÉ, sévérité RÉDUITE à MINEUR. Le scénario théorique est valide mais le contexte batch + rate-limiting + idempotence rend l'impact négligeable. Un fencing token est un over-engineering pour un protocole offline à 3 lancements/h max. Le plan devrait documenter cette analyse de risque (acceptation explicite) plutôt qu'implémenter un fencing token.
DIV-06 — Migrations TypeORM non mentionnées (Review écart 6 — MINEUR)¶
Review : C2 et C3 créent des tables mais aucune migration TypeORM dans le plan §10.
Documents : La spec §5.12 dit "Aucune stratégie de migration DDL applicable". Le plan §10 dit "Migration effective des données" hors périmètre — ce qui concerne la migration de données, pas les DDL.
Verdict : Écart CONFIRMÉ, sévérité MINEUR maintenue. La spec exclut "migration de schéma base de données existantes", pas la création de nouvelles tables. Le plan doit ajouter une tâche "Génération migration TypeORM pour MigrationCampaignEntity + ManifestRegistryEntity" dans les tâches d'implémentation.
DIV-07 — Disponibilité Redis non en précondition F1 (Review écart 7 — MINEUR)¶
Review : Redis n'est pas listé dans les préconditions F1 step 2 (source/cible/registre/HSM/runbook).
Documents : Plan §2 F1 step 6 : "accès source, accès cible, accès registre blockchain, accès HSM, runbook rollback présent" — pas Redis. Code-contracts distributed-protection : Redis est requis pour lock, idempotence, rate-limiting, réconciliation.
Verdict : Écart CONFIRMÉ, sévérité MINEUR maintenue. Redis est une dépendance critique de C6. Ajouter "accès Redis" aux préconditions F1 step 6. Comportement si indisponible : fail-closed (aucune campagne ne démarre sans protection distribuée).
DIV-08 — TC-PRECHECK-EXP-01 absent des tests (Review écart 8 — MINEUR)¶
Review : Le plan ajoute TC-PRECHECK-EXP-01 (résolution R-04) qui n'existe pas dans PD-254-tests.md.
Documents : Vérifié — PD-254-tests.md ne contient pas de test TC-PRECHECK-EXP-01. Le plan §5 le référence. Le scénario PRECHECK_EXPIRED est couvert implicitement par TC-INV-10A (matrice transitions) mais sans scénario Given/When/Then dédié.
Verdict : Écart CONFIRMÉ, sévérité MINEUR maintenue. Asymétrie documentaire. TC-PRECHECK-EXP-01 doit être ajouté au document PD-254-tests.md avec son scénario Given/When/Then.
DIV-09 — Codes HTTP ajoutés non spécifiés (Review écart 9 — MINEUR)¶
Review : Le plan attribue des codes HTTP (400, 422, 409, 503, 410) non présents dans la spec.
Documents : La spec §6 définit des codes métier et des comportements, pas de codes HTTP. Le plan §6 ajoute un mapping HTTP explicite.
Verdict : Écart CONFIRMÉ mais ACCEPTABLE. Le plan est une étape d'implémentation — il est normal qu'il concrétise les codes métier en codes HTTP. Les valeurs choisies sont conformes aux conventions REST (400=validation, 422=sémantique, 409=conflit, 503=indisponibilité, 410=expiré). Ce n'est pas une divergence mais une décision d'implémentation légitime. Sévérité RÉDUITE à COSMÉTIQUE — documenter comme décision technique (AD-05).
DIV-10 — Retry 3× non spécifié dans la spec (Review écart 10 — MINEUR)¶
Review : Le plan ajoute un retry 3× backoff exponentiel pour les erreurs I/O stockage, non prévu par la spec qui ne prévoit que la réconciliation cron.
Documents : Spec §5.10 réconciliation : "reprise de phase idempotente, sinon RECONCILIATION_FAILED". Spec ERR-07 : "dépassement verification_timeout → échec job, entrée réconciliation". Le plan §6 ajoute "retry 3× avec backoff exponentiel" comme traitement non-métier.
Verdict : Écart CONFIRMÉ, sévérité MINEUR maintenue. Le retry intra-phase est un ajout comportemental. Il est raisonnable (évite une réconciliation pour une erreur I/O transitoire) mais doit être documenté comme hypothèse technique (H-T07) avec la contrainte : le retry ne doit pas retarder l'entrée en réconciliation au-delà du verification_timeout.
DIV-11 — Variables CI non documentées (Review écart 11 — MINEUR)¶
Review : DATABASE_URL, REDIS_URL, credentials CloudHSM non listés dans le plan.
Documents : Le plan §1 C7 mentionne "Job GitLab CI" et "script migration-gate.sh" sans variables d'environnement. Les code-contracts ci-pipeline ne les mentionnent pas.
Verdict : Écart CONFIRMÉ, sévérité MINEUR maintenue. Les variables CI sont un détail d'implémentation mais leur absence peut bloquer les tests d'intégration. Ajouter une section "Variables CI requises" dans C7 ou dans le script migration-gate.sh.
4. Zones d'ombre (non couvert ni par les documents ni par la review)¶
ZO-01 — Taille maximale du manifest et performance de sérialisation¶
Aucun document ni la review ne mentionnent la limite pratique du manifest. Avec source_object_count max à 999 999 999 (§5.3), le manifest JSON pourrait atteindre plusieurs dizaines de Go. La sérialisation canonique RFC 8785 et le calcul SHA3-256 sur un tel volume ne sont pas bornés. Le verification_timeout (max 14400s) est la seule borne indirecte.
Impact : Potentiel OOM ou timeout sur de très gros lots. Le plan devrait documenter une borne pratique réaliste (ex: 1M objets max par campagne) ou un mécanisme de streaming.
ZO-02 — Politique de rétention des artefacts de campagnes échouées¶
La spec et le plan documentent l'archivage de l'attestation (campagne réussie), mais pas la rétention des rapports precheck/postcheck des campagnes terminées en ROLLED_BACK ou RECONCILIATION_FAILED. Ces artefacts ont une valeur probatoire (preuve de tentative + preuve d'échec).
Impact : Mineur — les rapports existent en tant qu'artefacts CI, mais leur archivage probatoire n'est pas contractualisé.
ZO-03 — Comportement en cas de campagne orpheline et lock expiré simultanément¶
Le plan décrit séparément le lock TTL (C6) et la réconciliation orphelin (C6). Si une campagne est orpheline ET que son lock a expiré, le cron de réconciliation peut-il reprendre la campagne sans lock ? Le plan ne clarifie pas l'interaction entre ces deux mécanismes.
Impact : Mineur — la reprise idempotente devrait réacquérir le lock avant de continuer.
5. Recommandation¶
- Rework nécessaire — divergences à résoudre
Corrections requises avant soumission Gate 5¶
| Priorité | Correction | Écart |
|---|---|---|
| MAJEUR | Expliciter le statut (DONE/TODO/STUB+destination) de PD-44, PD-54, PD-55, PD-36/37 dans le plan §9 | DIV-02 |
| MINEUR | Documenter le mécanisme de seed de sampling (ex: dérivé de migration_id) | DIV-04 |
| MINEUR | Ajouter "accès Redis" aux préconditions F1 step 6, comportement fail-closed | DIV-07 |
| MINEUR | Ajouter TC-PRECHECK-EXP-01 dans PD-254-tests.md | DIV-08 |
| MINEUR | Figer version canonicalize@^1.0.0 dans C3, documenter risque ESM v2 | DIV-03 |
| MINEUR | Ajouter tâche migration TypeORM (CREATE TABLE) pour C2/C3 | DIV-06 |
| MINEUR | Documenter retry I/O comme H-T07 avec contrainte verification_timeout | DIV-10 |
| MINEUR | Ajouter variables CI requises (DATABASE_URL, REDIS_URL, HSM credentials) dans C7 | DIV-11 |
| COSMÉTIQUE | Documenter mapping HTTP comme AD-05 | DIV-09 |
| COSMÉTIQUE | Nommer "Jest" explicitement dans §11 | DIV-01 |
Réévaluation de la sévérité review¶
| Écart review | Sévérité review | Sévérité confrontation | Justification |
|---|---|---|---|
| 1 (framework test) | MAJEUR | MINEUR | NestJS = Jest par défaut, implicite par stack |
| 2 (statut inter-PD) | MAJEUR | MAJEUR | Confirmé — bloquant potentiel pour l'implémentation |
| 3 (ESM/CJS) | MAJEUR | MINEUR | canonicalize v1 est CJS, v2 ESM non publiée |
| 4 (seed sampling) | MAJEUR | MINEUR | Risque identifié dans le plan, détail d'implémentation |
| 5 (fencing token) | MAJEUR | MINEUR | Contexte batch + rate-limit + idempotence = risque négligeable |
| 6 (migration DDL) | MINEUR | MINEUR | Confirmé |
| 7 (Redis precondition) | MINEUR | MINEUR | Confirmé |
| 8 (TC-PRECHECK-EXP-01) | MINEUR | MINEUR | Confirmé |
| 9 (codes HTTP) | MINEUR | COSMÉTIQUE | Décision d'implémentation légitime |
| 10 (retry I/O) | MINEUR | MINEUR | Confirmé |
| 11 (variables CI) | MINEUR | MINEUR | Confirmé |
Synthèse post-confrontation¶
| Sévérité | Review initiale | Après confrontation |
|---|---|---|
| BLOQUANT | 0 | 0 |
| MAJEUR | 5 | 1 |
| MINEUR | 6 | 8 |
| COSMÉTIQUE | 0 | 2 |
| Total | 11 | 11 |
La review Phase 1 a surévalué 4 écarts MAJEUR → MINEUR (framework test, ESM/CJS, seed sampling, fencing token) en ne tenant pas suffisamment compte du contexte projet (stack NestJS, version npm, nature batch du protocole). Un seul écart MAJEUR subsiste : le statut des dépendances inter-PD (DIV-02), qui doit être résolu avant Gate 5.