Aller au contenu

PD-278 — Rapport de confrontation (Étape 5)

Ce rapport est produit par l'orchestrateur Claude avant la gate 5 PMO. Il confronte les documents produits pour identifier convergences, divergences et zones d'ombre.

1. Sources confrontées

Document Étape
PD-278-specification.md Étape 1 (Spécification)
PD-278-tests.md Étape 2 (Tests & Validation)
PD-278-plan.md Étape 4 (Plan d'implémentation)
PD-278-code-contracts.yaml Étape 4 (Code contracts)

2. Convergences

CONV-01 — Set d'états (INV-278-01) : Les 4 documents s'accordent sur {PENDING, SEALED, DIP, EXPIRED} comme états contractuels. Spec §4, Tests TC-INV-01, Plan §1 C2, Contracts dip-entity-extension.

CONV-02 — Matrice de transitions (INV-278-09) : Seules SEALED → DIP et DIP → SEALED sont ajoutées. EXPIRED terminal strict. Toute transition non listée explicitement interdite. Cohérent dans spec §5.2, tests TC-INV-09/TC-ERR-06, plan C3, contracts dip-state-machine.

CONV-03 — Gardes SEALED→DIP (INV-278-02) : 6 gardes identiques : état=SEALED, copies≥MIN_COPIES, retention_due=false, authentification, rôle∈{PA,SA,auditor}, RLS. Spec §5.3, tests TC-NOM-01/TC-INV-02, plan C9 §13, contracts dip-service.

CONV-04 — Retour explicite uniquement (INV-278-03) : Aucun timeout implicite, aucun scheduler, aucun cron. DIP→SEALED uniquement par action explicite acteur autorisé. Spec §5.4, tests TC-NOM-04, plan V-06, contracts dip-state-machine forbidden.

CONV-05 — Auditabilité transitions réussies (INV-278-04) : Outbox INSERT synchrone dans la même transaction ACID pour DOCUMENT_DISSEMINATED et DOCUMENT_RETURNED. Spec §5.8, tests TC-INV-04, plan C9, contracts dip-service.

CONV-06 — Attestation 1:1 par requête (INV-278-05) : Exactement 1 attestation par requête SEALED→DIP, mono ou multi-documents. Schéma §5.13 cohérent. Spec §5.13, tests TC-INV-05, plan C9 step 10, contracts dip-service.

CONV-07 — WORM préservé (INV-278-06) : Contenu documentaire immutable en DIP. Trigger DB sur motif_communication après insertion. Spec §4, tests TC-NR-01/TC-INV-06/TC-NOM-06, plan C1 step 9, contracts dip-migration.

CONV-08 — RLS préservée (INV-278-07) : SET LOCAL app.current_user_id dans QueryRunner. Politiques RLS existantes inchangées. Spec §4, tests TC-ERR-05/TC-NR-02, plan C9, contracts.

CONV-09 — Atomicité package (INV-278-11) : 1 échec = ROLLBACK global, aucun effet partiel (statut, attestation, audit succès). Spec §5.3, tests TC-ERR-11/TC-INV-11, plan C9 step 7, contracts dip-service.

CONV-10 — Contrôle concurrence (INV-278-12) : SELECT FOR UPDATE ordonné par document_id ASC. Conflit → 409 E-409-CONFLICT. Spec §5.9, tests TC-INV-12, plan C9, contracts dip-service.

CONV-11 — Ordre temporel (INV-278-13) : returned_at = GREATEST(NOW(), disseminated_at). CHECK constraint DB. Spec §5.10, tests TC-NOM-03/TC-NEG-07, plan C1 step 8 + C9, contracts dip-migration.

CONV-12 — Anti-contournement rétention (INV-278-14) : Garde retention_due=false sur SEALED→DIP. Clôture explicite par retention_service (BYPASSRLS). Spec §5.12, tests TC-ERR-14/TC-INV-13 cas B, plan C9, contracts.

CONV-13 — Contrat API : POST /api/v1/documents/disseminations (F1) et POST /api/v1/documents/{id}/dissemination-return (F2). Spec §5.14, plan C10.

CONV-14 — Bornes numériques : MIN_COPIES=2, N_MAX=100, rate-limit 60/min, quota 1000/jour, motif 1024 chars, SLA P95 2000ms/1500ms, hard timeout 5000ms. Spec §5.5/§5.6, plan C4, contracts dip-config.

CONV-15 — 14 codes d'erreur : E-400 à E-500 identiques entre spec §6, tests §4, plan §6. Catégorisation audit/non-audit cohérente.

CONV-16 — Périmètre audit refus sécurité : Limité à 401/403/429/409-RETENTION-DUE. E-409-STATE et E-409-CONFLICT explicitement exclus. Spec INV-278-04, plan §6, plan C11.

CONV-17 — Chiffrement repos (INV-278-10) : AES-256-GCM/HSM envelope encryption. Aucun secret en clair en base. Spec §4, tests TC-INV-10/TC-NEG-05, plan §7, contracts dip-service.


3. Divergences

DIV-01 — Enum DB 5 valeurs vs spec "exactement" 4 valeurs

  • Spec §4 INV-278-01 : "Le set des statuts implemente inclut exactement PENDING, SEALED, DIP, EXPIRED."
  • Tests TC-INV-01 : "Ensemble exact = {PENDING, SEALED, DIP, EXPIRED} — Aucune valeur manquante ou additionnelle."
  • Plan §9 V-01 : "PD-279 a ajouté RESTITUTED. L'enum PostgreSQL contiendra 5 valeurs. Le test CA-01 doit vérifier que DIP est présent, pas que |enum| = 4."
  • Plan §4 CA-01 : "4 valeurs incluant DIP, sans compter RESTITUTED PD-279"
  • Impact : Le test TC-INV-01 tel que rédigé (aucune valeur additionnelle) échouerait en intégration car RESTITUTED existe dans l'enum PostgreSQL. Le mot "exactement" dans la spec et "aucune valeur additionnelle" dans les tests contredisent la réalité du schéma DB. Le plan identifie l'écart mais la spec et les tests n'ont pas été mis à jour.

DIV-02 — Table attestation absente de la migration C1

  • Spec §5.13 : Définit le schéma contractuel de l'attestation (attestation_id, request_id, document_ids, actor_id, issued_at, motif_communication, hash_evidence, signature_ref) avec "Conservation minimale : 10 ans".
  • Plan C9 §13 : "INSERT attestation (même tx)" — step 10 de la méthode disseminate().
  • Contracts dip-service : "Table dédiée vault_secure.dissemination_attestations pour attestations (vs lifecycle_log)".
  • Plan C1 §13 : 12 étapes DDL détaillées — aucune ne crée la table dissemination_attestations.
  • Contracts dip-migration : Ne mentionne aucune table attestation dans ses invariants ou fichiers.
  • Impact : BLOQUANT. Le service C9 fait INSERT attestation dans une table qui n'existe pas dans la migration C1. Ni l'entity TypeORM correspondante, ni la DDL CREATE TABLE ne sont spécifiées. L'INSERT échouerait en runtime.

DIV-03 — Audit refus sécurité : spec synchrone vs plan async

  • Spec §5.8 : "Persistance audit refus securite | Synchrone (transaction legere dediee) | Aucune tentative refusée sans trace"
  • Plan C11 : this.auditLogService.logAsync(...) — méthode async non-bloquante.
  • Contracts dip-exception-filter forbidden : "Audit synchrone bloquant la réponse HTTP (logAsync non bloquant)" — explicitement INTERDIT d'être synchrone.
  • Impact : La spec exige une persistance synchrone pour garantir "aucune tentative refusée sans trace". Le plan et les contracts interdisent explicitement le synchrone et utilisent logAsync. Si logAsync échoue ou perd l'événement (crash post-réponse, buffer plein), un refus sécurité peut ne laisser aucune trace, violant INV-278-04. Contradiction frontale entre spec et plan/contracts.

DIV-04 — TC-ERR-12 ne vérifie pas l'audit DENIED sur DIP→SEALED

  • Spec INV-278-04 : "chaque tentative refusee pour 401/403/429/409-retention_due persiste un evenement audit securite" — couvre F1 et F2.
  • Tests TC-ERR-03/04/05/13/14 (tous F1) : THEN inclut "Audit securite DOCUMENT_DISSEMINATION_DENIED persiste".
  • Tests TC-ERR-12 (F2 — DIP→SEALED) : THEN = "Rejet 403 / Status reste DIP / Aucun DOCUMENT_RETURNED de succes" — aucune vérification audit DENIED.
  • Impact : Le refus 403 sur F2 (DIP→SEALED) n'a pas de vérification d'audit dans les tests, alors que INV-278-04 l'exige. Gap de couverture test.

DIV-05 — TC-FML-01 et TC-FML-02 sans scénario Given/When/Then

  • Tests §2 matrice : CA-10 → TC-FML-01, CA-11 → TC-FML-02 — référencés.
  • Tests §3-7 : Ces deux tests ne sont définis nulle part dans le document. Aucun Given/When/Then.
  • Plan §5.3 : TC-FML-01 et TC-FML-02 sont listés avec mécanismes et observables dans le plan.
  • Impact : Deux critères d'acceptation (CA-10 TLA+, CA-11 tests contractuels) n'ont pas de scénario de test défini dans le document de tests. La matrice de couverture les référence mais sans définition, ils ne sont pas testables en l'état.

DIV-06 — TC-NOM-07 absent de la matrice de couverture

  • Tests §3 : TC-NOM-07 est défini avec Given/When/Then pour dissemination_package_id (mono=NULL, multi=non NULL).
  • Tests §2 matrice : TC-NOM-07 n'apparaît dans aucune ligne de la matrice de couverture.
  • Impact : Mineur. Le test existe mais n'est pas tracé vers un invariant ou critère d'acceptation. Risque : test orphelin non relié à une exigence formelle.

4. Zones d'ombre

ZO-01 — Entity/modèle TypeORM de l'attestation : Le plan décrit une table dédiée vault_secure.dissemination_attestations (decision architecturale dip-service) mais aucun composant ne définit l'entity TypeORM correspondante. C2 (dip-entity-extension) ne couvre que DocumentSecure. Aucun fichier attestation.entity.ts n'est listé dans aucun composant.

ZO-02 — Mécanisme hash_evidence et signature_ref : Spec §5.13 exige ces champs dans l'attestation. Plan H-08/H-09 mentionne HSM (dépendance PD-37, stub possible). Aucun document ne précise : (a) quel contenu est hashé pour hash_evidence, (b) quel algorithme de hash, © quelle clé HSM/KMS signe, (d) le comportement stub si HSM indisponible pour PD-278.

ZO-03 — performance_flag=SLOW_OPERATION : Spec §5.5 et tests TC-NOM-05 exigent ce flag. Le plan ne détaille dans aucun composant : (a) où ce flag est stocké (colonne ? log ? métrique Prometheus ?), (b) comment il est calculé (horloge monotone mentionnée mais non implémentée dans C9), © le comportement "échec atomique" si hard timeout > 5000ms.

ZO-04 — Écriture de retention_due : La migration C1 crée la colonne retention_due BOOLEAN NOT NULL DEFAULT false. Le plan confirme "L'écriture du champ retention_due est hors périmètre PD-278". Aucun document ne spécifie le mécanisme de mise à jour (retention orchestrator mentionné en spec §5.11 mais hors scope). Tant que personne n'écrit true, la garde INV-278-14 ne bloque jamais rien.

ZO-05 — E-409-TEMPORAL-ORDER non testable : Spec §5.10 définit ce code d'erreur. Le plan utilise GREATEST(NOW(), disseminated_at) qui rend ce cas théoriquement impossible. Aucun test ne couvre E-409-TEMPORAL-ORDER. Si GREATEST() ne peut pas échouer, ce code d'erreur est mort-né.

ZO-06 — Body de réponse 201 Created (F1) : Spec §5.14 ne spécifie pas la structure de la réponse HTTP 201 de POST /documents/disseminations. Plan mentionne "201 Created + attestation response". Le DTO DisseminationResponseDto est listé dans les contracts mais sa structure n'est définie dans aucun document.

ZO-07 — Rétention attestations (10 ans) : Spec §5.13 exige "Conservation minimale : 10 ans". Aucun document (plan, contracts) ne spécifie la politique de rétention technique (partitioning, archivage, purge) pour la table attestations.

ZO-08 — Vérification cross-module ordre de verrouillage (V-02) : Plan V-02 identifie le risque de deadlock cross-module et demande une "Revue cross-module pré-Gate 8". Mais aucun composant, test, ou critère d'acceptation ne formalise cette vérification. Spec §5.9 exige l'ordre global mais la conformité des modules existants (DocumentPurgeService, RestitutionService) n'est pas testée.

ZO-09 — Normalisation UUID lowercase (TC-NEG-01) : Test exige "Persistance normalisee" en lowercase. Aucun mécanisme de normalisation n'est décrit dans le plan. ParseUUIDPipe NestJS accepte les deux casses mais ne normalise pas. Le service et la DB non plus.


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

Divergences bloquantes à résoudre impérativement :

ID Sévérité Action requise
DIV-02 BLOQUANT Ajouter DDL CREATE TABLE vault_secure.dissemination_attestations + entity TypeORM dans C1 ou nouveau composant dédié
DIV-03 MAJEUR Trancher : soit la spec s'aligne sur async (modifier §5.8), soit le plan implémente une persistance synchrone (transaction légère dédiée) pour les refus sécurité
DIV-01 MAJEUR Reformuler INV-278-01 ("inclut au moins" au lieu de "exactement") et TC-INV-01 ("DIP ∈ enum" au lieu de "aucune valeur additionnelle")
DIV-04 MINEUR Ajouter "AND Audit securite DOCUMENT_DISSEMINATION_DENIED persiste" dans THEN de TC-ERR-12
DIV-05 MINEUR Ajouter scénarios Given/When/Then pour TC-FML-01 et TC-FML-02 dans le document de tests
DIV-06 MINEUR Ajouter TC-NOM-07 dans la matrice de couverture (§5.1 dissemination_package_id / CA-04 ou CA-12)