Aller au contenu

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

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

1. Sources confrontées

  • Spécification (étape ⅓, v2) : PD-279-specification.md
  • Tests contractuels (étape 2) : PD-279-tests.md
  • Plan d'implémentation (étape 4) : PD-279-plan.md / PD-279-plan.md
  • Code contracts (étape 4) : PD-279-code-contracts.yaml

2. Convergences

  • Machine à états : Les 4 documents s'accordent sur les transitions SEALED→RESTITUTED et RESTITUTED→SEALED, et l'interdiction des transitions RESTITUTED→DISPOSED, RESTITUTED→PENDING, RESTITUTED→EXPIRED, RESTITUTED→RESTITUTED. Convergence totale.
  • Gardes de restitution : Ownership, legal_lock=false, geo_copy_count >= 2, état source SEALED sont présents dans la spec (INV-279-02), les tests (TC-NOM-01, TC-ERR-03/05/06/07), le plan (§2.1) et les code contracts (module restitution-service).
  • Gardes de retour : Ownership + état RESTITUTED, sans vérification geo_copy_count — convergent dans les 4 documents.
  • Atomicité transactionnelle : Spec (INV-279-09), plan (§2.1 étape 8, §5.8), tests (TC-INV-09A/B, TC-ERR-10), code contracts (restitution-service) s'accordent sur l'atomicité ACID status + lifecycle_log.
  • SLA temporels : Spec (INV-279-05, §5.6), plan (§2.4), tests (TC-NOM-03) et code contracts (restitution-sla-scheduler) convergent sur les 3 seuils (80%, 100%, OVERDUE) et l'absence de transition automatique.
  • Interdiction de destruction : Spec (INV-279-06, INV-279-10), plan (§2.3, §11), tests (TC-NOM-05), code contracts (destruction-guard-extension) convergent sur le rejet HTTP 409 pour les documents RESTITUTED.
  • Migration DDL réversible : Spec (INV-279-08), plan (C1, §9 V2), tests (TC-NOM-06), code contracts (migration-ddl) convergent sur la réversibilité avec précondition.
  • Vérification TLA+ : Spec (CA-279-11), plan (C13), tests (TC-NOM-07), code contracts (tla-plus-model) convergent.
  • Idempotence des endpoints : Spec (INV-279-11), plan (§2.1 étape 4, §2.2 étape 4), code contracts (restitution-service INV-279-11) convergent sur le principe : état cible déjà atteint → 200 sans attestation.

3. Divergences

⚠️ Les conflits ne doivent JAMAIS être lissés. Chaque divergence est rendue visible.

  • DIV-01 : Code HTTP pour legal_lock=true — 409 (spec) vs 423 (plan)
  • Spec §5.3 (étape 7) : legal_lock=false409 Conflict (error_code: DOCUMENT_UNDER_LEGAL_LOCK)
  • Spec §6 (table des erreurs) : aucune entrée HTTP 423 ; DOCUMENT_UNDER_LEGAL_LOCK est listé sous 409 Conflict
  • Plan §2.1 (étape 5) : legal_lock423 DOCUMENT_UNDER_LEGAL_LOCK
  • Plan §6 (table des erreurs) : ligne explicite 423 | DOCUMENT_UNDER_LEGAL_LOCK
  • Plan §9 (V7) : affirme « La spec v2 utilise HTTP 423 » — affirmation contredite par le texte réel de la spec
  • Tests (TC-ERR-07) : mentionnent 409 (alignés sur la spec)
  • Code contracts (restitution-service) : ne spécifient pas le code HTTP explicitement
  • Impact : BLOQUANT — contrat API incohérent entre spec et plan. L'implémentation doit choisir un seul code. HTTP 423 (WebDAV) est sémantiquement plus précis pour un verrou juridique ; HTTP 409 est le choix de la spec. Les tests sont alignés spec.

  • DIV-02 : Numérotation des invariants — INV-279-11 redéfini dans le plan

  • Spec §4 : INV-279-11 = idempotence (retour 200 sans attestation si état cible déjà atteint)
  • Plan §3 (table mapping) : INV-279-11 = destruction-checkpoint-uniqueness (double vérification inclusion + exécution) — concept différent
  • Plan §3 (table mapping) : INV-279-12 = expired-terminal (EXPIRED sans transition sortante) — invariant absent de la spec
  • Code contracts (restitution-service) : INV-279-11 = idempotence (aligné spec)
  • Code contracts (state-machine-extension) : INV-279-12 référencé (non canonique)
  • Impact : MAJEUR — le plan a une incohérence interne : sa table de mapping §3 contredit ses propres code contracts sur INV-279-11. La traçabilité invariant→mécanisme est rompue pour l'audit.

  • DIV-03 : CA-279-13 ajouté par le plan, absent de la spec

  • Spec §7 : 12 critères d'acceptation (CA-279-01 à CA-279-12)
  • Plan §4 : ajoute CA-279-13 (détection idempotence) hors canon
  • Tests : ne référencent pas CA-279-13
  • Impact : MINEUR — extension du plan, mais écart de traçabilité par rapport au corpus canonique. Le concept est déjà couvert par INV-279-11.

  • DIV-04 : Ordre d'évaluation des gardes — idempotence vs état source

  • Spec §5.3 : étape 5 = vérifier état SEALED (→ 409 si non-SEALED), étape 6 = vérifier idempotence (si déjà RESTITUTED → 200). Or un document RESTITUTED échoue à l'étape 5 avant d'atteindre l'étape 6, rendant l'idempotence irréalisable dans cet ordre.
  • Plan §2.1 : étape 4 vérifie d'abord l'idempotence (si RESTITUTED → 200), puis si != SEALED → 409. Cet ordre rend l'idempotence fonctionnelle.
  • Tests (TC-NEG-02) : attendent un 409 sur un appel return idempotent (document déjà SEALED), ce qui contredit INV-279-11 de la spec qui spécifie un 200 idempotent pour le retour aussi.
  • Impact : BLOQUANT — la spec a une incohérence interne entre l'ordre des gardes §5.3 et l'invariant d'idempotence INV-279-11. Le plan corrige implicitement ce problème mais ne le documente pas comme écart.

  • DIV-05 : Tests TC-NEG-02 — rejeu retour attendu 409 vs spec 200 idempotent

  • Spec INV-279-11 : « POST /documents/:id/return-from-restitution est idempotent : si le document est déjà SEALED, retourner HTTP 200 sans nouvelle attestation »
  • Tests TC-NEG-02 : « Rejeu d'appel return-from-restitution après retour déjà effectué → Requête rejouée en 409 »
  • Plan §2.2 (étape 4) : « Si déjà SEALED → 200 idempotent » (aligné spec)
  • Impact : BLOQUANT — le document de tests contredit directement l'invariant INV-279-11 de la spec sur le comportement idempotent du retour.

  • DIV-06 : lifecycle_log (spec) vs integrity_journal_entries (plan)

  • Spec §4, §5.3, §5.4 : utilise systématiquement le terme lifecycle_log
  • Plan §2.1, §3, §5 : utilise IntegrityJournalService.appendEntry() et integrity_journal_entries (table existante PD-251)
  • Impact : MAJEUR — le plan effectue une substitution sémantique non formalisée. Si lifecycle_log de la spec EST integrity_journal_entries du backend, l'équivalence doit être déclarée contractuellement. Si ce sont deux artefacts distincts, le plan manque la création de lifecycle_log.

  • DIV-07 : geo_copy_count — champ existant (spec) vs colonne à créer (plan)

  • Spec §5.1 : traite geo_copy_count comme un attribut existant du document, avec format et validation définis
  • Plan §8 (HT-279-02) : identifie que le champ n'existe pas dans l'entité DocumentSecure et propose de l'ajouter en migration PD-279 avec DEFAULT 0
  • Spec §2 (périmètre inclus) : ne mentionne pas l'ajout de geo_copy_count comme livrable
  • Spec §5.7 (migration DDL) : ne liste que l'extension enum + restituted_at + restitution_deadline comme colonnes ajoutées
  • Impact : MAJEUR — extension de périmètre technique non contractualisée. La migration PD-279 touche un concept (comptage copies géo) qui n'est pas dans le périmètre déclaré de la spec.

  • DIV-08 : Couverture tests INV-279-11 (idempotence)

  • Spec §4 : INV-279-11 est un invariant non négociable
  • Tests §2 (matrice) : INV-279-11 n'apparaît pas dans la matrice de couverture
  • Tests §5 (table invariants) : INV-279-10 est le dernier invariant couvert
  • Impact : MAJEUR — l'invariant d'idempotence n'a pas de couverture de test formelle dans la matrice. TC-NEG-02 le couvre partiellement mais avec le mauvais code HTTP attendu (409 vs 200).

  • DIV-09 : Routes destruction — contrôle explicite vs filtre implicite

  • Spec §5.5 : exige un rejet HTTP 409 explicite à l'inclusion batch (POST /destruction/batches)
  • Plan §2.3 : pour l'inclusion, s'appuie sur EligibilityService.selectEligible() qui filtre status=EXPIRED (excluant implicitement RESTITUTED par filtre SQL, pas par rejet explicite)
  • Plan §11 : mentionne EligibilityService.selectEligible() comme « filtre déjà status=EXPIRED, exclut implicitement RESTITUTED »
  • Impact : MAJEUR — un filtre SQL implicite ne produit pas de HTTP 409 ni d'audit de refus. La spec exige un rejet explicite auditable. Le plan ne satisfait pas cette exigence pour la route d'inclusion.

4. Zones d'ombre

  • ZO-01 : Mécanisme de mise à jour de geo_copy_count. Le plan ajoute la colonne avec DEFAULT 0 et documente un STUB, mais aucun document ne précise quel mécanisme (trigger, job, événement) mettra à jour cette valeur. Avec DEFAULT 0, tous les documents existants sont bloqués pour la restitution — est-ce le comportement voulu ? La spec suppose le champ comme « déjà renseigné ».

  • ZO-02 : Source exacte de security_level au moment de la transition. La spec exige security_level dans chaque attestation (INV-279-04). Le plan (HT-279-03) propose de le récupérer depuis @CurrentUser() ou le coffre-fort associé, mais sans spécifier lequel est normatif. Les tests (§9) marquent ce point comme « non testable » en citant Q-279-03, alors que la spec v2 indique ce point comme RÉSOLU.

  • ZO-03 : Tests rédigés sur une version antérieure de la spec. Les tests §9 (règles non testables) référencent Q-279-02/03/04/05 comme ouverts, alors que la spec v2 les marque tous RÉSOLUS (§10.2). Le document de tests n'a pas été mis à jour après la Gate 3.

  • ZO-04 : RESTITUTION_OVERDUE vs escalade 100% — même événement ou séquence ? La spec §5.6 mentionne à la fois « escalade à 100% » et « événement RESTITUTION_OVERDUE ». Le plan §2.4 les traite comme 3 événements séquentiels distincts (80%, 100%, OVERDUE). La spec ne clarifie pas si l'escalade 100% et OVERDUE sont le même moment ou deux moments distincts.

  • ZO-05 : DELETE /documents/:id dans les code contracts. La spec §5.5 et le plan §11 listent 3 routes destruction protégées, incluant DELETE /documents/:id. Les code contracts du module destruction-guard-extension ne mentionnent que eligibility.service.ts et destruction-execution.service.ts — le fichier du controller soft-delete n'est pas listé dans files.

  • ZO-06 : Framework de test. Le plan §12 ne spécifie pas le runner de test (Jest ou Vitest). Les code contracts du module tests listent des fichiers .spec.ts (convention Jest), mais aucune déclaration normative.

5. Recommandation

  • Rework nécessaire — divergences à résoudre avant de continuer

Divergences bloquantes à résoudre : 1. DIV-01 : Trancher 409 vs 423 pour legal_lock et aligner spec/plan/tests. 2. DIV-04 : Corriger l'ordre des gardes dans la spec §5.3 (idempotence avant vérification état source) ou documenter l'écart. 3. DIV-05 : Corriger TC-NEG-02 (409 → 200 pour idempotence retour) conformément à INV-279-11.

Divergences majeures à résoudre : 4. DIV-02 : Réaligner la table de mapping §3 du plan sur la numérotation canonique des invariants spec. 5. DIV-06 : Formaliser l'équivalence lifecycle_log = integrity_journal_entries. 6. DIV-07 : Contractualiser l'ajout de geo_copy_count dans le périmètre spec §2 et §5.7, ou le retirer du plan. 7. DIV-08 : Ajouter INV-279-11 dans la matrice de couverture tests avec les bons observables. 8. DIV-09 : Remplacer le filtre implicite par un rejet explicite 409 à l'inclusion batch.