PD-277 — Rapport de confrontation (Étape 5 — Gate Plan v2)¶
Ce rapport est produit par l'orchestrateur Claude avant la gate PMO 5 (v2). Il confronte les documents produits pour identifier convergences, divergences et zones d'ombre.
1. Sources confrontées¶
| # | Document | Étape | Version |
|---|---|---|---|
| A | PD-277-specification.md | 1 (Spec) | v2 |
| B | PD-277-tests.md | 2 (Tests) | v2 |
| C | PD-277-plan.md | 4 (Plan) | v2 |
| D | PD-277-code-contracts.yaml | 4 (Plan) | v2 (header: "v1.0.0") |
| E | PD-277-review-step5-v2.md | 5 (Review Phase 1) | v2 |
| F | PD-277-review-step5.md | 5 (Review Phase 1) | v1 (contexte) |
2. Convergences¶
-
CONV-01 — Périmètre fonctionnel : Les 6 documents s'accordent sur le périmètre : anti-rejeu nonce sur
reEncrypt()+ PKI certificate binding surgenerateReKey(), modulelegal-preuniquement. Aucun document ne tente d'étendre le périmètre horslegal-pre. -
CONV-02 — 8 invariants : Les invariants INV-277-01 à INV-277-08 sont repris de manière cohérente dans Spec (A §5), Tests (B §5), Plan (C §3) et Code contracts (D modules 1-7). Aucun invariant n'est contredit ou omis.
-
CONV-03 — Objectif 24/24 checks bloquants : Spec (A §1, §11 CA-277-07), Tests (B TC-NOM-04), Plan (C §2 F3) et Code contracts (D) convergent sur l'objectif de conformité Prolog 24/24.
-
CONV-04 — Atomicité SERIALIZABLE : Spec (A §7), Tests (B TC-NEG-02), Plan (C §1 C3, §2 F2) et Code contracts (D module 3) convergent sur la transaction
SERIALIZABLEpour l'anti-rejeu nonce. -
CONV-05 — Format nonce UUID v4 lowercase : Spec (A §4), Tests (B TC-ERR-02, TC-NEG-01), Plan (C §1 C3, §5 TC-ERR-02) et Code contracts (D module 3) convergent sur le format strict UUID v4 lowercase ASCII 36 caractères avec la même regex.
-
CONV-06 — Fail-closed systématique : Tous les documents convergent sur le principe fail-closed (INV-277-01). Spec (A §5, §10), Tests (B §4-5), Plan (C §3, §6) et Code contracts (D module 3 forbidden) appliquent ce principe sans exception.
-
CONV-07 — Aucun nouveau StatusEnum : Spec (A §2, INV-277-08), Tests (B TC-INV-08), Plan (C §3 INV-277-08) et Code contracts (D module 2 forbidden) s'accordent : aucun nouvel état métier.
-
CONV-08 — Corrections v1→v2 appliquées : Les constats v1 (review F) suivants sont adressés en v2 :
- Frontière code contracts (2 modules → 1 pour
legal-rekey-manager.service.ts) — CORRIGÉ (D module 3pd277-rekey-manager-controls). - Module repository manquant → AJOUTÉ (D module 4b
pd277-rekey-repository). - Fail-closed sur ReKeys hérités → AJOUTÉ (C §2 F2 étape 3b, D module 3 invariant hérité).
- Validation certificats invalides/révoqués → ENRICHI (C §1 C4 : validation non expiré, non révoqué, compatible mandat).
- Statut ACTIVE hérité PD-81 → CLARIFIÉ (C §2 F2 "hors scope PD-277").
-
Dépendances inter-PD → AJOUTÉ (C §11 tableau structuré avec statuts).
-
CONV-09 — Couverture tests ↔ invariants : La matrice de couverture (B §2) associe chaque invariant à au moins un test, et le plan (C §5) mappe chaque test à un mécanisme et un composant. Pas de trou de couverture identifié.
-
CONV-10 — 5 codes d'erreur : Les codes
ERR-NONCE-MISSING,ERR-NONCE-FORMAT,PRE_NONCE_REPLAY_DETECTED,PRE_CERTIFICATE_BINDING_FAILED,ERR-PERSISTENCE-CONTROLsont identiques en Spec (A §10), Plan (C §6), Code contracts (D module 5) et Tests (B §4).
3. Divergences¶
⚠️ Les conflits ne doivent JAMAIS être lissés. Chaque divergence est rendue visible.
- DIV-01 : DDL certificats — DEFAULT '' non contractualisé dans la spec
- Source A (Spec v2 §6) : DDL canonique =
owner_certificate_id VARCHAR(255) NOT NULLetrecipient_certificate_id VARCHAR(255) NOT NULL— pas de DEFAULT. - Source C (Plan v2 §1 C1, §2 F4) : DDL =
NOT NULL DEFAULT ''. Le plan affirme : "Ce choix de migration est contractualisé dans la spécification §6.1 (DDL canonique mis à jour en v2)." - Source D (Code contracts module pd277-migration) : Invariants mentionnent
VARCHAR(255) NOT NULLsans préciser le DEFAULT. - Source E (Review v2 constat 1) : Classé BLOQUANT — "La spec impose NOT NULL sans DEFAULT, le plan implémente DEFAULT ''."
-
Impact : La spec v2 fournie ne contient PAS le DEFAULT '' que le plan prétend y avoir contractualisé. Les 3 artefacts (Spec, Plan, Code contracts) disent 3 choses différentes. La migration échouera si des
LegalReKeypré-existants existent et que le DDL n'a pas de DEFAULT. -
DIV-02 : Source de génération du nonce — contradiction interne à la spec
- Source A (Spec v2 §4) : "Génération:
crypto.randomUUID()côté serveur uniquement, jamais côté client." - Source A (Spec v2 §9 F2) : "Le système reçoit une demande
reEncryptcontenant un nonce." — implique que le nonce est fourni par l'appelant. - Source C (Plan v2 §2 F2) : Le nonce arrive dans la requête (étape 1 : "Valider format nonce").
- Source C (Plan v2 §6) : "Le client reçoit un HTTP 500 et peut retenter avec un nouveau nonce. [...] le client génère un nouveau nonce via
crypto.randomUUID()et réessaie." - Source E (Review v2 constat 2) : Classé MAJEUR — "nonce fourni/renouvelé côté client, contraire à l'exigence server-only."
-
Impact : La spec se contredit elle-même (§4 "jamais côté client" vs §9 F2 "contenant un nonce"). Le plan adopte implicitement le modèle "nonce fourni par l'appelant" sans trancher l'ambiguïté. Si le nonce est généré côté serveur uniquement, toute l'architecture de F2 et le mécanisme de retry changent fondamentalement.
-
DIV-03 : Frontière API — endpoint controller non stabilisée
- Source C (Plan v2 §2 F2) : "LegalPreController.reEncryptDocument() [nouveau endpoint ou méthode]" — alternative ouverte.
- Source C (Plan v2 §10) : Hors périmètre = "aucune modification d'API, de contrôleur, ou de module tiers."
- Source D (Code contracts) : Aucun module controller défini. Le fichier controller n'apparaît dans aucun
files:. - Source E (Review v2 constat 3) : Classé MAJEUR — "Le plan ouvre deux options incompatibles et ne contractualise pas la frontière controller/orchestrator."
-
Impact : Si
reEncryptWithNonce()est une nouvelle méthode dans le service, elle doit être exposée par un controller. Le plan décrit un point d'entrée controller (F2 étape 1) tout en excluant les modifications de controller du périmètre. Les agents d'implémentation n'ont pas de contrat sur le controller. -
DIV-04 : Immuabilité certificats — couverture incomplète des chemins d'écriture
- Source A (Spec v2 INV-277-05) : "Les identifiants de certificats liés à un LegalReKey sont immuables après création."
- Source C (Plan v2 §1 C5) : Protection via
updateStatus()+ guard service/DTO. - Source D (Code contracts module pd277-rekey-repository) : Forbidden = "Méthode update() sans exclusion explicite des champs certificats." Mais ne mentionne pas
save(),QueryBuilder.update(), ou accès direct. - Source E (Review v2 constat 4) : Classé MAJEUR — "pas de garantie explicite contre autres chemins d'écriture repository/query builder hors ce flux."
-
Impact : La protection couvre
updateStatus()et les DTO mais pas les autres chemins d'accès TypeORM. Un développeur futur pourrait contourner involontairement la garde d'immuabilité viasave()ou QueryBuilder. -
DIV-05 : Référence de version dans les code contracts
- Source D (Code contracts header) : "Source normative: PD-277-specification.md v2, PD-277-plan.md v1".
- Source C (Plan) : Le plan est en version 2.
- Source E (Review v2 constat 5) : Classé MINEUR — dette de traçabilité.
- Impact : Les code contracts référencent le plan v1 alors qu'ils intègrent les corrections v2. Incohérence de traçabilité documentaire.
4. Zones d'ombre¶
-
ZO-01 — Modèle d'appel nonce non formalisé : La spec §4 dit "serveur uniquement" mais §9 F2 dit "contenant un nonce". Aucun document ne tranche explicitement : le nonce est-il (a) généré dans le service
legal-preavant l'appel àreEncrypt, (b) généré par l'appelant interne (un autre service backend), ou © fourni par le client HTTP ? Le modèle de retry (plan §6) suppose que l'appelant régénère un nonce, orientant vers (b) ou ©. Cette ambiguïté doit être résolue avant implémentation. -
ZO-02 — Interface d'extraction certificats depuis TSP : Le plan (C4) mentionne "Extraire ownerCertificateId depuis tspResult.certificateChainRef" et "recipientCertificateId depuis tspResult (bob certificate)". Mais la spec ne définit pas ces champs dans
TspVerificationResult. Le code contracts (D module 4) les définit comme optionnels. Aucun document ne spécifie le comportement si le TSP réel (non-stub) ne retourne pas ces champs — fallback ou fail-closed ? -
ZO-03 — Coexistence reEncrypt() / reEncryptWithNonce() : Le plan crée
reEncryptWithNonce()comme nouvelle méthode. Aucun document ne précise si l'anciennereEncrypt()duLegalReKeyManagerServicecontinue d'exister, est dépréciée, ou est supprimée. Les appelants existants dereEncrypt()ne sont pas identifiés. -
ZO-04 — Backfill des ReKeys pré-existants : Le plan (V5) mentionne que les ReKeys pré-existants avec certificats vides ne peuvent pas utiliser
reEncryptWithNonce()(fail-closed). Aucun document ne précise : combien de ReKeys pré-existants existent en production ? Quels cas d'usage métier sont affectés ? Quand la story de backfill sera-t-elle planifiée ? -
ZO-05 — Simulation certificats invalides dans le stub TSP : Le plan (C4) mentionne : "En contexte stub, le stub retourne toujours des certificats valides sauf configuration de test explicite." Mais le code contracts du stub (D module 6) ne mentionne aucun mécanisme de configuration pour simuler des certificats invalides. Comment les tests TC-ERR-06 et TC-NEG-05 (certificat expiré/révoqué/non autorisé) sont-ils exécutés ?
-
ZO-06 — Détection ERR-PROLOG-FACTS-OUTDATED : Le plan (§10) exclut la "détection runtime de faits Prolog obsolètes" et renvoie au CI/CD. Mais le code d'erreur
ERR-PROLOG-FACTS-OUTDATEDest défini dans la spec (§10) et testé par TC-ERR-09. Aucun mécanisme de détection automatique n'est décrit — le constat est-il manuel ou CI/CD ?
5. Recommandation¶
- Procéder — convergence confirmée, aucun conflit bloquant
- Rework nécessaire — divergences à résoudre avant de continuer
- Escalade — décision humaine requise sur un point structurant
Actions de rework requises :
-
DIV-01 (BLOQUANT) : Aligner le DDL canonique dans TOUS les artefacts. Soit ajouter
DEFAULT ''dans la spec v2 §6 et les code contracts, soit retirer le DEFAULT du plan et utiliser une migration en 2 étapes. Un seul DDL de référence doit exister. -
DIV-02 (MAJEUR) : Résoudre la contradiction interne de la spec §4 vs §9 sur la source du nonce. Formaliser explicitement qui génère le nonce et propager la décision dans plan, tests et code contracts.
-
DIV-03 (MAJEUR) : Trancher la frontière controller. Soit ajouter un module controller dans les code contracts, soit documenter explicitement que le controller est modifié a minima et qu'un agent en est propriétaire.
-
DIV-04 (MAJEUR) : Renforcer la garde d'immuabilité en ajoutant dans le code contracts des forbidden couvrant
save()etQueryBuilder.update()sur les champs certificats, ou en documentant un mécanisme TypeORM (subscriber/listener). -
DIV-05 (MINEUR) : Corriger la référence de version dans l'en-tête des code contracts (
plan.md v1→plan.md v2).