PD-84 — Rapport de confrontation (Gate 5 — v2)¶
Ce rapport est produit par l'orchestrateur Claude avant la gate PMO Gate 5 v2. Il confronte les documents produits pour identifier convergences, divergences et zones d'ombre. Il intègre l'analyse des 2 BLOQUANT et 1 MAJEUR signalés par la review P1 v2 (ChatGPT).
1. Sources confrontées¶
| Document | Version | Étape d'origine |
|---|---|---|
| PD-84-specification.md | v1.3.0 | Étape 1 |
| PD-84-tests.md | v1.3.0 | Étape 2 |
| PD-84-plan.md | v1.1.0 | Étape 4 |
| PD-84-code-contracts.yaml | v1.1.0 | Étape 4 |
| PD-84-review-step5-v2.md | — | Gate 5 P1 v2 |
2. Convergences¶
CONV-01 — Quotas structurels (3 dossiers / 100 documents)¶
Les 4 documents s'accordent sur les quotas exacts : 3 dossiers actifs maximum en FREE, 100 documents scellés par dossier en FREE, aucune limite en PREMIUM, aucun reset périodique. Le plan implémente via pg_advisory_xact_lock + comptage atomique. Les tests (TC-01 à TC-04, TC-LIM-01/02) couvrent les cas nominaux et de concurrence. Les code contracts formalisent les invariants INV-84-04, INV-84-11.
CONV-02 — Transition de plan (upgrade + downgrade)¶
Spec (§3.2.3, §3.2.4), tests (TC-10, TC-18, TC-19), plan (§2.4, §2.7) et code contracts (module freemium-services) convergent sur : upgrade FREE→PREMIUM via PUT /account/plan, downgrade PREMIUM→FREE avec conservation des dossiers existants et re-verrouillage des quotas/exports. Endpoint protégé par PlanStubGuard (triple verrou : guard runtime + TC-10-bis + check CI).
CONV-03 — Audit non-bloquant (5 types d'événements)¶
Les 4 documents s'accordent sur 5 types d'événements audit : FOLDER_CLOSE, QUOTA_FOLDER_REFUSED, QUOTA_DOCUMENT_REFUSED, PLAN_CHANGE, EXPORT_REFUSED. Audit via AuditLogService.logAsync() (non-bloquant). TC-13 vérifie les 5 événements. Code contracts (module audit-extension) formalise l'extension. Conformité SEC-84-03/ECT-v2-03/INV-84-03.
CONV-04 — Parité cryptographique FREE/PREMIUM¶
Spec (INV-84-01, INV-84-02, §8.3), tests (TC-11, TC-SEC-01), plan (§3 mapping INV-84-01/02) et code contracts (module freemium-services, forbidden "Ne PAS conditionner le scellement par le plan") convergent : aucun branchement plan dans le pipeline de scellement PD-60/PD-79. Le scellement est identique FREE/PREMIUM.
CONV-05 — CapabilityState calculé à la volée¶
Spec (§3.1 entité 4, ECT-06), tests (TC-18), plan (§2.6, §9.3 piège #2) et code contracts (module freemium-services, forbidden "Ne PAS persister CapabilityState") convergent : CapabilityState est une vue calculée depuis User.plan, jamais persistée. Aucun risque de désynchronisation.
CONV-06 — Clôture idempotente et unidirectionnelle¶
Spec (§3.2.1, ECT-12), tests (TC-07, TC-08, TC-17), plan (§2.3) et code contracts (module freemium-entities) convergent : transition ACTIVE→CLOSED_READ_ONLY unidirectionnelle. Deuxième appel retourne 409 CONFLICT avec code FOLDER_ALREADY_CLOSED.
CONV-07 — Sécurité (RLS + guards + isolation)¶
Spec (SEC-84-02), tests (TC-SEC-02, TC-09), plan (§7.1) et code contracts (module freemium-guards) convergent : double vérification propriétaire (FolderOwnerGuard + RLS), quotas isolés par owner_user_id, refus accès cross-user.
CONV-08 — Codes erreur métier¶
Spec (§7.2), tests (préconditions + résultats attendus), plan (§6.1) et code contracts (module freemium-exceptions) convergent sur les 8 codes erreur : QUOTA_FOLDER_LIMIT_REACHED, QUOTA_DOCUMENT_LIMIT_REACHED, PREMIUM_REQUIRED, FOLDER_CLOSED_READ_ONLY, FOLDER_ALREADY_CLOSED, INVALID_FOLDER_CATEGORY, FOLDER_NOT_FOUND, PLAN_STATE_INCONSISTENT. Mapping HTTP aligné (422 pour quotas/capabilities, 409 pour conflit d'état, 400 pour validation, 404 pour ressource, 500 pour incohérence).
CONV-09 — Hors périmètre explicite¶
Les 4 documents s'accordent pour exclure : tarification Premium, paiement, rate limiting spécifique (ECT-v2-04), validation UX complète adolescent (ECT-v2-01), consentement parental RGPD, implémentation réelle des exports (PD-85).
CONV-10 — Corrections v1→v1.1.0 levées¶
La review P1 v2 confirme la levée de ⅚ MAJ : MAJ-02 (CA-84-06 UI → API data structurées), MAJ-03 (CA-84-08 mapping export), MAJ-04 (account_role scope), MAJ-05 (ENABLE_PLAN_STUB triple verrou), MAJ-06 (QuotaGuard dans code contracts, §guards module 6). La convergence est désormais forte sur ces 5 points.
3. Divergences¶
Les conflits ne sont JAMAIS lissés. Chaque divergence est rendue visible.
DIV-01 — Export PREMIUM : 501 Not Implemented vs "autorisé" dans les tests¶
- Source A (Plan §2.5) : "Si plan === PREMIUM → 501 Not Implemented (PD-85 pas implémenté)."
- Source B (Tests TC-10, action 3) : "Tentative d'export sur F1 → Exports deviennent autorisés sur dossier existant."
- Source C (Tests TC-LIM-04, action 3) : "Relancer export immédiatement → 2e appel: autorisé, sans état intermédiaire incohérent."
- Source D (Spec §7.1, endpoint exports) : "En PREMIUM: hors périmètre PD-84 (PD-85)."
- Analyse factuelle : La review P1 v2 qualifie ce point de BLOQUANT. Cependant, la spec elle-même déclare le comportement export PREMIUM comme "hors périmètre PD-84 (PD-85)". Le plan est aligné avec la spec sur ce point : en PREMIUM, il n'y a pas d'implémentation export dans PD-84. Le conflit réel est entre les tests (TC-10/TC-LIM-04) et la spec/plan : les tests exigent un export "autorisé" que ni la spec ni le plan ne contractualisent pour PD-84.
- Nuance importante : TC-10 couvre principalement INV-84-05 (montée Premium immédiate) et INV-84-06 (auto-déverrouillage). Le coeur du test (actions 1-2 : capabilities déverrouillées après changement de plan) est pleinement testable. Seule l'action 3 ("tentative d'export sur F1 → autorisé") est en conflit, car elle teste un comportement PD-85. De même, TC-LIM-04 teste la cohérence d'état pendant une transition — le refus FREE (action 1) et l'absence d'état incohérent (action 3) restent testables, mais le "autorisé" de l'action 3 présuppose PD-85.
- Impact : Si non résolu, TC-10 action 3 et TC-LIM-04 action 3 échoueront avec un 501 au lieu d'un 200. La couverture de INV-84-05/INV-84-06 reste assurée par TC-10 actions 1-2 et TC-18 (capabilities). Le 501 est lui-même un signal que l'export n'est plus "refusé pour plan FREE" — il confirme que la transition de plan a bien eu lieu.
- Qualification : Divergence tests↔spec/plan. La review P1 v2 a raison de constater l'incohérence entre TC-10/TC-LIM-04 et le plan. Cependant, le plan est conforme à la spec (qui exclut l'export PREMIUM du périmètre PD-84). Le conflit est localisé dans les tests, pas dans le plan. Un 501 après upgrade confirme que la garde FREE (
PREMIUM_REQUIRED) est levée, ce qui satisfait fonctionnellement l'invariant INV-84-05/06.
DIV-02 — SLA : mesure "COMMIT transactionnel" vs "enregistrement append-only PD-31"¶
- Source A (Spec §9, SLA-84-01) : "Mesure : de la réception API (PUT /account/plan reçu par le serveur) à l'enregistrement effectif dans le journal append-only (PD-31)."
- Source B (Tests TC-SLA-01, protocole) : "Point d'arrivée : enregistrement effectif dans le journal append-only (PD-31)."
- Source C (Plan §2.4, "SLA — Garantie par construction") : "Le temps est mesuré entre le début de la transaction et le COMMIT, puis loggé dans l'événement audit PLAN_CHANGE (champ duration_ms dans les metadata)."
- Analyse factuelle : La review P1 v2 qualifie ce point de BLOQUANT (mesure SLA) et MAJEUR (conformité spec). Examinons la chaîne réelle :
- Le plan décrit une architecture entièrement synchrone :
UPDATE User.plan+COMMITdans une seule transaction PostgreSQL. - L'audit est émis via
AuditLogService.logAsync()qui, selon le plan (§6.3) et le code existant, utilise BullMQ en fallback. LelogAsync()retourne{ queued: true }— l'événement est mis en file d'attente, pas écrit immédiatement dans le journal append-only. - Le point d'arrivée contractuel (spec) est "l'enregistrement effectif dans le journal append-only PD-31". Le plan mesure jusqu'au COMMIT de la transaction
UPDATE User.plan, pas jusqu'à l'écriture effective dans le journal PD-31. - En architecture synchrone, les capabilities sont effectives dès le COMMIT (car calculées à la volée depuis
User.plan). L'enregistrement PD-31 est un effet de bord asynchrone (logAsync via BullMQ). Il y a donc un écart structurel entre ce que le plan mesure (capabilities effectives = COMMIT) et ce que la spec/tests exigent (enregistrement PD-31). - Impact : Le SLA de capabilities effectives (ce qui impacte l'utilisateur) est garanti par construction (< 50ms). Le SLA de journalisation PD-31 dépend de BullMQ et n'est pas mesuré par le plan. La question est : la spec mesure-t-elle la "disponibilité fonctionnelle" ou la "traçabilité audit" ? Le libellé spec ("capacités Premium effectives sur dossiers existants" dans la colonne Transition) pointe vers la disponibilité fonctionnelle. Mais le protocole de mesure ("enregistrement journal append-only") pointe vers la traçabilité.
- Qualification : Divergence réelle spec↔plan sur le point de mesure SLA. Le plan pourrait aligner sa mesure sur le COMMIT + confirmation de queuing BullMQ, ou la spec pourrait clarifier que le point d'arrivée est la visibilité des capabilities (pas l'écriture PD-31).
DIV-03 — SealedDocument : entité modélisée dans la spec, absente du plan¶
- Source A (Spec §3.1) : Définit
SealedDocumentavec 7 attributs (document_id, folder_id, document_type, sealed_at, probatory_seal_ref, integrity_state, anchoring_state). - Source B (Plan §1 COMP-02) : Ne définit pas d'entité
SealedDocument. Le plan délègue entièrement le scellement àDepositService(PD-60/PD-79). - Analyse factuelle : Le plan est cohérent avec la spec §2.2 ("Architecture cryptographique interne détaillée de scellement couverte par PD-60/PD-79"). PD-84 ne crée pas de table
sealed_documents— il réutilise le schéma PD-60/PD-79 existant. La spec modéliseSealedDocumentcomme entité conceptuelle pour décrire les attributs attendus, pas comme prescription d'implémentation. - Impact : Faible. Le
sealed_document_countest incrémenté dansProbatoryFolder, et les attributsprobatory_seal_ref,integrity_state,anchoring_statesont ceux de PD-60. La cohérence est maintenue. - Qualification : Divergence de modélisation sans impact fonctionnel. Déjà identifiée en confrontation v1 (DIV-01).
DIV-04 — AuditLogEvent : entité dans la spec, externe dans le plan¶
- Source A (Spec §3.1) : Définit
AuditLogEventavec 5 attributs. - Source B (Plan §1 COMP-05) : Traite l'audit comme un service existant (PD-31) à étendre, pas comme une entité à créer.
- Analyse factuelle : Le plan est aligné avec la spec §10.1 ("PD-31 (transitive): audit log des événements"). PD-84 étend
AuditActionType, il ne crée pas de table audit. - Impact : Aucun. L'entité existe dans PD-31, PD-84 ne fait que l'utiliser.
- Qualification : Divergence de frontière module, sans impact. Déjà identifiée en confrontation v1 (DIV-02).
DIV-05 — account_role : défini spec, reporté comme dette dans le plan¶
- Source A (Spec §3.1) : Définit
account_roleavec 3 valeurs (MINOR, LEGAL_GUARDIAN, OTHER). - Source B (Plan §9.2) : "Aucun service PD-84 ne lit ni ne conditionne sur ce champ — il prépare les stories suivantes de l'epic PD-185."
- Analyse factuelle : Le plan crée la colonne
account_role(enum PostgreSQL, nullable, default NULL) dans la migration. Le champ existe physiquement mais reste inerte. La spec (INV-84-09) exige "plan universel sans distinction d'âge" — ce qui est exactement ce que le plan implémente en ne conditionnant rien suraccount_role. TC-15 vérifie l'absence de branchement par rôle. - Impact : Faible. Review P1 v2 a confirmé la levée de MAJ-04.
- Qualification : Divergence acceptée (dette technique explicite).
DIV-06 — premium_activated_at : dans la spec, pas explicite dans la migration¶
- Source A (Spec §3.1) :
UserAccount.premium_activated_at(nullable). - Source B (Plan §2.4) : "UPDATE User.plan = newPlan, premium_activated_at = NOW() (si PREMIUM)".
- Source C (Plan §1 COMP-02, fichier migration) : Ne mentionne pas explicitement
premium_activated_atdans la description de la migration. - Analyse factuelle : Le flux §2.4 utilise
premium_activated_at, donc il est implicitement dans le schéma. La migration devrait l'ajouter comme colonne nullable surusers. L'omission dans la description du COMP-02 est un défaut documentaire mineur. - Impact : Faible si la migration l'inclut effectivement. Risque si l'agent développeur ne le crée pas.
- Qualification : Divergence documentaire mineure. Déjà identifiée en confrontation v1 (DIV-06).
DIV-07 — Valeur "business" du champ User.plan pour la migration enum¶
- Source A (Plan §8, H-01) : "Le champ User.plan (string 'free') peut être migré vers un enum PostgreSQL plan_type ('FREE'/'PREMIUM')."
- Source B (Review P1 v2, MINEUR) : "Le plan suppose l'absence de valeur 'business' en base sans preuve documentaire."
- Source C (Go/No-Go §Phase 0) : "user.entity.ts ligne 56-57 : plan: string (free/premium/business)."
- Analyse factuelle : Le Go/No-Go confirme que le champ actuel accepte 'business'. La migration vers un enum
FREE | PREMIUMdoit gérer les éventuelles lignes avecplan = 'business'. Le plan ne documente pas de script de backfill pour cette valeur. - Impact : Si des lignes
businessexistent en base, la migration échouera avec une erreur de cast enum. Le plan §9.1 (risque #1) mentionne "gérer les valeurs existantes ('free' → 'FREE', 'premium' → 'PREMIUM')" mais omet 'business'. - Qualification : Divergence technique à résoudre. Le plan doit documenter la stratégie pour
business(conserver comme 3e valeur enum, mapper vers PREMIUM, ou vérifier absence en base).
4. Zones d'ombre¶
ZO-01 — Relation ProbatoryFolder → Deposit (PD-60)¶
Le plan (H-05) suppose que DepositService.createDeposit() accepte un folderId en paramètre. Si l'interface PD-60 ne prévoit pas ce paramètre, le lien entre un document scellé et son dossier probatoire est non documenté. Le plan mentionne "étendre l'interface CreateDepositDto" comme fallback, mais sans détailler l'impact sur PD-60.
ZO-02 — PLAN_STATE_INCONSISTENT : surdimensionnement structurel¶
Le plan §2.4 documente PLAN_STATE_INCONSISTENT comme "garde-fou théorique" qui "en architecture synchrone, ne se produit pas". La spec §3.2.2 le définit comme comportement à expiration de la borne absolue 30s. En architecture synchrone (< 50ms), cette erreur ne peut être émise que si la connexion PostgreSQL timeout, ce qui relève de l'infrastructure, pas de la logique métier PD-84. La question est : faut-il coder un path qui ne se produit jamais ? Le plan dit oui (défense en profondeur), ce qui est conservateur et acceptable.
ZO-03 — ClockProvider pour tests SLA¶
Le plan (H-04) mentionne un ClockProvider injectable mais ne détaille pas son implémentation. Les tests SLA (TC-SLA-01) mesurent des durées réelles (N=50 répétitions). L'interaction entre horloge injectable et mesure de durée réelle n'est pas clarifiée.
ZO-04 — Variables CI pour tests d'intégration¶
La review P1 v2 signale (MINEUR) que les variables CI (DATABASE_URL, CI=true) ne sont pas documentées dans la section "Contraintes techniques → Framework de test". Le plan mentionne "PostgreSQL réel avec RLS" mais ne précise pas la configuration CI/CD requise.
ZO-05 — Pagination (FolderListResponseDto)¶
Le code contracts (module freemium-dto) définit FolderListResponseDto avec pagination. Ni la spec ni les tests ne mentionnent de pagination pour GET /folders. Le plan ne la mentionne pas non plus dans le flux §2.1. C'est un ajout non contractualisé dans les code contracts.
ZO-06 — Type audit DOCUMENT_SEAL¶
Le plan (§2.2, étape g) émet un événement DOCUMENT_SEAL qui n'est pas listé dans les 5 types d'audit PD-84 (§7.2). FOLDER_CREATE existe déjà (Go/No-Go). DOCUMENT_SEAL est-il un type PD-60 existant ou un nouveau type PD-84 non documenté ?
5. Analyse des BLOQUANT P1 v2¶
BLOQUANT P1 #1 — Export PREMIUM 501 vs TC-10/TC-LIM-04¶
Verdict de confrontation : Faux positif partiel.
La review P1 v2 a raison de constater une incohérence textuelle entre les tests (TC-10 action 3 : "autorisé", TC-LIM-04 action 3 : "autorisé") et le plan (501 Not Implemented en PREMIUM). Cependant :
- La spec elle-même exclut le comportement export PREMIUM du périmètre PD-84 (§7.1 : "En PREMIUM: hors périmètre PD-84 (PD-85)").
- Le plan est conforme à la spec sur ce point : il implémente le stub 501 pour signaler que PD-85 n'est pas encore fait.
- Le conflit est dans les tests, pas dans le plan. TC-10 et TC-LIM-04 incluent une action (export "autorisé") qui teste un comportement PD-85, pas PD-84.
- La couverture INV-84-05/INV-84-06 est assurée par TC-10 actions 1-2 (capabilities déverrouillées) et TC-18 (CapabilityState recalculé). L'action 3 de TC-10 est un test de bout en bout qui présuppose PD-85.
- Solution possible sans modifier le plan : ajuster TC-10 action 3 et TC-LIM-04 action 3 pour accepter un 501 (pas un
PREMIUM_REQUIRED422) comme preuve que la garde FREE est levée. Le 501 confirme que l'utilisateur PREMIUM atteint bien le code d'export (juste pas implémenté).
Conclusion : Le plan n'a pas à implémenter PD-85 pour satisfaire PD-84. Les tests doivent être ajustés pour refléter le périmètre réel de PD-84. Ce n'est pas un bloquant du plan — c'est un écart de rédaction dans les tests.
BLOQUANT P1 #2 — TC-SLA-01 mesure append-only PD-31 vs COMMIT¶
Verdict de confrontation : Divergence réelle, mais gravité surestimée.
La review P1 v2 a raison de constater un écart entre le point de mesure spec/tests ("enregistrement append-only PD-31") et le point de mesure plan ("COMMIT transaction"). Cependant :
- L'architecture synchrone garantit que les capabilities sont effectives dès le COMMIT (< 50ms). C'est ce qui impacte l'utilisateur.
- L'événement audit PLAN_CHANGE est émis via
logAsync()(BullMQ). Son écriture dans le journal append-only est asynchrone et dépend de PD-31, pas de PD-84. - Le protocole de test TC-SLA-01 mesure par polling
GET /capabilities(toutes les 200ms). Ce polling mesure la visibilité des capabilities, pas l'écriture PD-31. Le protocole de test est donc lui-même aligné avec le plan (capabilities), pas avec le point d'arrivée déclaré (append-only PD-31). - Le contrat spec (SLA-84-01, colonne Transition) est ambigu : la transition est "Activation Premium → capacités Premium effectives", mais la mesure est "jusqu'à l'enregistrement journal append-only". Ce sont deux choses différentes.
Conclusion : L'écart est réel sur le libellé du point de mesure. Mais le plan, les tests (protocole de polling capabilities) et la réalité architecturale convergent vers la même chose : mesurer la visibilité des capabilities. Le point "enregistrement append-only" dans le libellé spec/test est un artefact rédactionnel qui ne correspond ni à ce que le test mesure réellement (polling capabilities), ni à ce qui impacte l'utilisateur. La résolution optimale serait de clarifier le libellé spec pour aligner le point de mesure déclaré sur le protocole de test réel.
6. Synthèse¶
| Catégorie | Nombre | Détail |
|---|---|---|
| Convergences | 10 | Quotas, transition plan, audit, parité crypto, CapabilityState, clôture, sécurité, codes erreur, hors périmètre, corrections MAJ |
| Divergences | 7 | DIV-01 (export 501, faux positif partiel), DIV-02 (SLA mesure), DIV-03 (SealedDocument), DIV-04 (AuditLogEvent), DIV-05 (account_role dette), DIV-06 (premium_activated_at), DIV-07 (valeur business) |
| Zones d'ombre | 6 | ZO-01 à ZO-06 |
| BLOQUANT P1 | 2 qualifiés | #1 = faux positif partiel (conflit tests↔spec, pas plan), #2 = divergence réelle mais gravité surestimée |
| MAJEUR P1 | 1 réabsorbé | Couvert par l'analyse DIV-02/BLOQUANT #2 |
Divergences à résoudre avant Gate 5¶
| ID | Gravité réelle | Action recommandée |
|---|---|---|
| DIV-01 (export 501) | MINEUR (plan conforme à spec) | Ajuster TC-10 action 3 et TC-LIM-04 action 3 dans les tests (accepter 501 comme preuve de levée garde FREE) |
| DIV-02 (SLA mesure) | MINEUR (protocole test aligné avec plan) | Clarifier le libellé SLA spec pour aligner le point de mesure déclaré sur le polling capabilities |
| DIV-07 (valeur business) | MINEUR | Documenter la stratégie migration pour la valeur 'business' dans le plan (H-01) |
7. 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 : Les 10 convergences majeures confirment un alignement solide entre spec, tests, plan et code contracts. Les 2 BLOQUANT signalés par la review P1 v2 sont soit un faux positif partiel (export 501 — le plan est conforme à la spec, le conflit est dans les tests), soit une divergence de libellé surestimée (SLA — le protocole de test mesure les capabilities, pas l'append-only). Les 7 divergences identifiées sont toutes de gravité MINEUR ou documentaire. Les 3 actions recommandées (ajustement tests TC-10/TC-LIM-04, clarification SLA, stratégie 'business') sont des corrections mineures qui ne remettent pas en cause l'architecture du plan.