PD-278 — Rapport de confrontation (Étape 5 — Gate AMBIGUITY)¶
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 d'origine | Version |
|---|---|---|
| PD-278-specification.md | Étape 1 | Correction finale v3 |
| PD-278-tests.md | Étape 2 | Correction finale v3 |
| PD-278-plan.md | Étape 4 | v2 (19 écarts corrigés) |
| PD-278-code-contracts.yaml | Étape 4 | v2 |
2. Convergences¶
- Machine à états DIP : Les 4 documents convergent sur les transitions
SEALED → DIP(gardes INV-278-02) etDIP → SEALED(explicite uniquement, INV-278-03). Transitions interdites identiques dans chaque document. - 14 invariants (INV-278-01 à INV-278-14) : Spec, tests et plan s'accordent sur la formulation, la justification et les observables de chaque invariant. Les code contracts reprennent fidèlement les invariants par module.
- Atomicité package multi-documents : Spec §5.8, tests TC-ERR-11/TC-INV-11, plan C9 et code contracts
dip-serviceconvergent sur le pattern all-or-nothing via ROLLBACK global. - Audit transitions + refus sécurité synchrone : Spec §5.8 (transaction légère dédiée pour refus), tests TC-INV-04 (audit complet chaque cas), plan C11 (QueryRunner dédié synchrone), code contracts
dip-exception-filter(persistance synchrone) — tous alignés. - Attestation de restitution : Spec §5.13 (schéma contractuel), tests TC-INV-05/TC-NOM-01, plan C1/C2/C9 (table
vault_secure.dissemination_attestations), code contractsdip-attestation— 1 attestation par requête, reliée aux documents et à l'acteur. - WORM préservé en DIP : Spec INV-278-06, tests TC-NR-01/TC-INV-06/TC-NOM-06, plan trigger DB
trg_motif_communication_worm, code contracts — alignés. - RLS préservée : Spec INV-278-07, tests TC-ERR-05/TC-NR-02/TC-INV-07, plan
SET LOCAL app.current_user_id, code contracts — alignés. - Contrôle de concurrence : Spec §5.9 (tri lexicographique), tests TC-INV-12, plan SELECT FOR UPDATE ORDER BY id ASC, code contracts
// LOCK-ORDER— alignés. - Ordre temporel : Spec INV-278-13 (
returned_at = max(now_utc, disseminated_at)), tests TC-NOM-03/TC-NEG-07/TC-INV-13, planGREATEST(NOW(), disseminated_at), code contracts — alignés. - Anti-contournement rétention : Spec INV-278-14, tests TC-ERR-14/TC-INV-13 cas B, plan C9 garde +
retention_serviceBYPASSRLS, code contracts — alignés. - Rate-limit : Spec §5.6 (60 req/min, 1000 req/jour), tests TC-ERR-13, plan C8 (Redis INCR + EXPIRE), code contracts
dip-rate-limit(fail-closed) — alignés. - Bornes numériques : Spec §5.6, tests TC-ERR-08A/08B, plan C4 (config Joi), code contracts
dip-config— alignés sur MIN_COPIES=2, N_MAX=100, motif max 1024. - Contrat API : Spec §5.14, plan C10 (2 endpoints POST), code contracts
dip-controller— convergents sur les routes et la sémantique. - Couverture tests : Tests matrice 100% des 14 invariants et 15 critères d'acceptation couverts. Plan confirme 49 tests contractuels. Aucun trou de couverture identifié.
- Scénarios spec ST-01 à ST-11 : Tous 11 scénarios de la spec sont traçables dans les tests (TC-NOM-01, TC-ERR-07, TC-NOM-04, TC-NOM-03, TC-ERR-08A, TC-ERR-08B, TC-ERR-11, TC-ERR-12, TC-INV-12, TC-INV-13 cas B, TC-ERR-14 respectivement).
- Patterns réutilisés : Plan et code contracts s'accordent sur la réutilisation de patterns établis : PD-250 (migration enum), PD-279 (RestitutionService/QueryRunner), PD-81 (LegalRateLimitGuard), PD-60 (DepositAuditExceptionFilter).
- Interdictions
Math.random()et subquery index : Code contracts et plan reprennent fidèlement les REX PD-63 et PD-55.
3. Divergences¶
Les conflits ne doivent JAMAIS être lissés. Chaque divergence est rendue visible.
- DIV-01 (MAJEUR) : Cardinalité du set d'états — spec « exactement 4 » vs plan/DB « 5 incluant RESTITUTED »
- Source Spec §4 INV-278-01 : « Le set des statuts implemente inclut exactement
PENDING,SEALED,DIP,EXPIRED. » - Source Plan §3 INV-278-01, §4 CA-01, V-01 : « L'enum DB contient 5 valeurs (PENDING, SEALED, DIP, EXPIRED, RESTITUTED). PD-278 ajoute DIP. Le test vérifie
DIP ∈ enum_range, pas la cardinalité exacte. » - Source Tests TC-INV-01 : « Ensemble exact = {PENDING, SEALED, DIP, EXPIRED} — Aucune valeur manquante ou additionnelle. »
- Impact : Le test TC-INV-01 tel que rédigé dans le document tests (vérifiant l'ensemble exact = 4 valeurs) échouera si l'enum DB contient RESTITUTED (PD-279 DONE). Le plan a corrigé cette interprétation mais le document tests n'a pas été mis à jour. Incohérence tests vs plan sur le même test TC-INV-01.
- DIV-02 (MAJEUR) : Codes d'erreur 404 et 503 absents de la spec
- Source Spec §6 : Liste les codes E-400 à E-500 (11 codes). Aucun E-404-NOT-FOUND ni E-503-REDIS.
- Source Plan §6 : Ajoute
E-404-NOT-FOUND(document non trouvé après SELECT FOR UPDATE) etE-503-REDIS(Redis indisponible, fail-closed). - Impact : Le plan introduit 2 codes d'erreur non contractualisés dans la spec. Si la gate valide le plan sans mise à jour spec, ces codes ne sont pas traçables vers un contrat. Le cas 404 est particulièrement ambigu : le plan note que RLS pourrait filtrer un document existant, aboutissant à un 404 qui devrait être un 403.
- DIV-03 (MINEUR) : Nommage du path parameter dans l'endpoint retour DIP→SEALED
- Source Spec §5.14 :
POST /api/v1/documents/{document_id}/dissemination-return - Source Plan §2.2, code contracts
dip-controller:POST /api/v1/documents/{id}/dissemination-return - Impact : Divergence cosmétique (
document_idvsid). Sans impact fonctionnel si le route pattern est identique, mais crée une ambiguïté de traçabilité dans la documentation API.
- DIV-04 (MINEUR) : Scope du fuzz test TC-INV-09 — 4×4 vs 5×5
- Source Tests TC-INV-09 : « Ensemble des transitions possibles sur 4 etats » → 4×4 = 16 combinaisons.
- Source Plan §5.3 TC-INV-09 : « Fuzz 5×5 combinaisons (incluant RESTITUTED) » → 25 combinaisons.
- Impact : Le document tests ne mentionne pas RESTITUTED dans le fuzz. Le plan étend le scope sans que le document tests ne le reflète. Les transitions impliquant RESTITUTED ne sont pas définies par PD-278 — le fuzz 5×5 du plan pourrait rejeter des transitions RESTITUTED valides (définies par PD-279) comme des erreurs.
- DIV-05 (MINEUR) : Absence de TC-INV-14 dédié pour la non-bypass rétention
- Source Spec §4 : INV-278-14 est un invariant distinct (« retention_due ne peut pas etre contournee via maintien indefini en DIP »).
- Source Tests §2 matrice : INV-278-14 couvert par TC-INV-13 (cas B) + TC-ERR-14.
- Source Plan §5.3 : Confirme mapping vers TC-INV-13 + TC-ERR-14.
- Impact : Pas de test dédié TC-INV-14. La couverture de INV-278-14 est dispersée dans un test nommé d'après INV-278-13. Risque de traçabilité : un échec de TC-INV-13 ne distingue pas clairement lequel des deux invariants (13 ou 14) est violé.
4. Zones d'ombre¶
-
ZO-01 : Mécanisme d'enforcement du hard timeout SLA — La spec §5.5 contractualise un hard timeout (5000 ms) au-delà duquel la transition doit échouer atomiquement. Le plan mentionne
slaHardTimeoutMsdans la config (C4) mais aucun mécanisme technique (PostgreSQLstatement_timeout, Node.jsAbortController, watchdog) n'est décrit dans C9 ni dans les code contracts pour l'enforcer. TC-NOM-05 l'observe mais ne décrit pas le mécanisme. -
ZO-02 : Algorithme de calcul de
hash_evidence— La spec §5.13 mentionne « empreinte intégrité » et le plan déclare le champTEXT NOT NULL. Aucun document ne spécifie l'algorithme (SHA-256 ? HMAC ? Quels inputs : document_ids + actor_id + issued_at ?), ni si le calcul est fait applicativement ou via HSM. Le code contractdip-servicene mentionne pas le mécanisme de calcul. -
ZO-03 : Schéma de la réponse API (201 Created / 200 OK) — La spec §5.14 définit les bodies d'entrée mais pas les bodies de réponse. Le plan mentionne
DisseminationResponseDto(C5) sans décrire ses champs. Le test TC-NOM-01 vérifieattestation_iddans la réponse mais le contrat de réponse n'est formalisé nulle part. -
ZO-04 : Authentification service-to-service pour
retention_service— La spec autoriseretention_servicepour DIP→SEALED (INV-278-03, INV-278-14). Le plan décrit BYPASSRLS et le flux §2.4 comme « appel interne ». Aucun document ne précise le mécanisme d'authentification (JWT technique ? mTLS ? service account dans le même runtime ?). Le guardJwtAuthGuardest appliqué sur le controller —retention_servicedoit donc avoir un JWT valide. -
ZO-05 : Comportement SELECT FOR UPDATE vs RLS — distinction 404 / 403 — Le plan §6 note que RLS peut filtrer des documents existants, rendant le SELECT vide. Le plan C9 étape 6 mentionne « E-404-NOT-FOUND si inexistant, E-403-RLS si RLS filtré ». Mais le mécanisme de distinction n'est pas décrit : un SELECT FOR UPDATE filtré par RLS retourne 0 lignes, indistinguable d'un document inexistant. Aucune requête secondaire (BYPASSRLS count check) n'est spécifiée.
-
ZO-06 : Monitoring dérive NTP — La spec §5.10 contractualise « derive max configurable, defaut 100 ms ». Aucun mécanisme de monitoring ou d'alerte n'est décrit dans le plan. Le GREATEST() gère le clock skew réactif mais pas la détection proactive.
-
ZO-07 : Trigger
motif_communication— gap défense-en-profondeur NULL→value — Le trigger du plan autoriseNULL → valeur(conditionOLD.motif_communication IS NOT NULL). Un UPDATE ultérieur sur un document DIP dont le motif était NULL pourrait définir un motif après coup au niveau DB. L'API ne fournit pas cette route, mais la « dernière ligne de défense » DB ne couvre pas ce cas. La spec dit « immuable après création » — si création = la transition SEALED→DIP, un NULL initial devrait rester NULL. -
ZO-08 : Partitioning de la table attestations — complexité vs scope — Le plan §13 (C1 step 17) propose un partitioning
RANGE(issued_at)mais mentionne aussi une « alternative si partitioning complexe hors scope : table simple + dette technique avec story destination ». La décision n'est pas tranchée. En Gate 5, ce choix devrait être résolu car il impacte la migration DDL. -
ZO-09 : Publication audit async — SLO 15 min (Q-04) — La spec Q-04 fixe un SLO de rattrapage <= 15 min. Le plan mentionne
AuditLogService.logAsync()post-commit (pattern existant) mais aucun mécanisme de monitoring du SLO 15 min n'est planifié dans les composants C1–C13.
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
Justification : DIV-01 (incohérence tests vs plan sur TC-INV-01 / cardinalité enum) et DIV-02 (codes 404/503 non contractualisés dans la spec) sont des écarts MAJEURS. DIV-01 aboutira à un test qui échoue en intégration (RESTITUTED déjà présent). DIV-02 introduit des comportements non traçables. Les zones d'ombre ZO-01 (hard timeout) et ZO-02 (hash_evidence) impactent l'implémentabilité de 2 invariants (§5.5 et INV-278-05/10).
Actions correctives suggérées : 1. Aligner la spec INV-278-01 sur la réalité DB 5 valeurs, ou reformuler en « DIP ∈ statuts implémentés ». 2. Aligner le document tests TC-INV-01 avec l'interprétation plan (DIP ∈ enum_range, pas cardinalité = 4). 3. Ajouter E-404-NOT-FOUND et E-503-REDIS dans la spec §6 ou les retirer du plan. 4. Trancher ZO-08 (partitioning oui/non) avant Gate 5.