Aller au contenu

PD-299 — Expression de Besoin

Story : [GOV+APP] Consolidation PD-298 — dette technique, guards cross-module, gate 8 tests, prompts companion Domaine : workflow (ia-governance) + sharing (app) Date : 2026-04-23 Statut : BESOIN Origine : PD-298 REX §10, Rétrospective §Recommandations, Patterns 1-4


1. Contexte

PD-298 (UI liens de partage sans compte) a été livrée en RESERVE (Gate 8, 7.5/10). Le REX et la rétrospective ont identifié 4 patterns récurrents impactant 2-3 stories chacun, plus une dette technique de 8 points dans le module app src/sharing/.

Cette story de consolidation regroupe : - Partie A : corrections techniques dans ProbatioVault-app (module sharing PD-298) - Partie B : améliorations du workflow de gouvernance dans ProbatioVault-ia-governance

Le regroupement se justifie par la cohérence : les améliorations B empêchent la récurrence des problèmes A.


2. Objectifs principaux

Partie A — Dette technique app (8 items)

A1 — Tests Jest module sharing (45 TC contractuels)

Implémenter les 45 scénarios de test contractuels de PD-298-tests.md en Jest pour le module src/sharing/. Couverture cible >= 80%. Pattern récurrent : 2e occurrence (PD-287, PD-298).

A2 — Câblage guard owner dans ProofDetailScreen

Monter canShowShareCta dans ProofDetailScreen.tsx pour garantir INV-298-12 : le CTA "Partager" ne s'affiche que si ownerUserId == currentUser.id. Pattern récurrent : 3e occurrence (PD-251, PD-279, PD-298).

A3 — Intégration auth réelle

Remplacer le stub getAuthToken() (retourne null) par l'intégration avec le système d'auth existant (PD-99). Tous les appels API sharing doivent inclure Authorization: Bearer.

A4 — Allowlist PII stricte en telemetry

Typer metadata dans logShareEvent() avec un z.object({}) Zod strict (allowlist). Supprimer Record<string, string|number|boolean>. INV-298-07 exige zéro email en log.

A5 — maskIp fallback robuste

maskIp() doit retourner "IP masquée indisponible" en fallback sur entrée invalide, jamais la valeur brute. INV-298-09.

A6 — Détection offline NetInfo

Utiliser @react-native-community/netinfo (déjà installé) pour bloquer les actions sharing hors ligne et afficher une erreur explicite. INV-298-17.

A7 — Fix useProofShares(undefined) dans ShareDetailScreen

Corriger ShareDetailScreen.tsx:46 qui appelle useProofShares(undefined) alors que le hook exige un proofId non vide.

Faire valider par PO/Legal les textes de révocation (ARB-7), absence de DRM (ARB-8) et notice RGPD (rétention 90j) rédigés par l'orchestrateur IA. Mettre à jour les clés i18n si nécessaire.

Partie B — Améliorations workflow gouvernance (6 items)

B1 — Phase 6c.bis vérification cross-module

Après la synthèse 6c dans /gov-impl, ajouter un grep systématique des points d'intégration cross-module listés dans le plan §Mécanismes cross-module. Si le fichier cible n'est pas dans le diff → correction obligatoire avant step 7. Pattern 1 : 3 stories (PD-251, PD-279, PD-298).

B2 — Gate 8 check déterministe : 0 test = test_coverage plafonné à 6.0

Dans /gov-gate, ajouter un check pré-verdict Gate 8 : si find src/{module}/ -name "*.test.ts" | wc -l == 0, plafonner test_coverage à 6.0. Force NON_CONFORME ou ajout de tests. Pattern 2 : 2 stories (PD-287, PD-298).

B3 — Injection arbitrages story source dans prompts gates companion

Pour les stories companion (ex: PD-298 = companion de PD-287), assemble-prompt.sh doit injecter les arbitrages PO et invariants de la story source dans le bloc stable du prompt reviewer. Coût : +2KB contexte. Gain : -4 faux positifs par gate. Pattern 3 : 4 bloquants reclassés en Gate 3.

B4 — Règle Zod allowlist metadata telemetry dans learnings-universal

Ajouter dans learnings-universal.md : "Pour tout module telemetry avec invariant PII-free, typer metadata avec Zod allowlist stricte. Jamais Record<string, unknown>."

B5 — Traçabilité résolution placeholders en décomposition 6a

Ajouter un bloc obligatoire "Données résolues (pré-implémentation)" dans le template 6a Decomposition.md avec source humaine explicite (PO, Legal, story précédente).

B6 — Détection automatique extensions plan→spec

Créer scripts/detect-plan-extensions.py comparant endpoints/headers/timeouts du plan aux contrats de la spec. Invoqué par /gov-check-plan phase 4. Si extensions non ratifiées → forcer ratification PO ou retour Gate 3. Pattern récurrent : PD-287 + PD-298 (4+ extensions non ratifiées chacune).


3. Non-objectifs

NOBJ-1 — Pas de refonte du module sharing

Les corrections A1-A7 sont des fixes ciblés sur le code existant, pas un refactoring global.

NOBJ-2 — Pas de modification des gates ⅗

Seule la Gate 8 est modifiée (B2). Les gates 3 et 5 ne changent pas de scoring.

A8 est un process humain. L'IA ne valide pas les textes juridiques.


4. Contraintes

CTR-1 — Bi-projet

La story touche 2 repos : ProbatioVault-app (partie A) et ProbatioVault-ia-governance (partie B). Deux branches features, deux pipelines.

CTR-2 — Rétro-compatibilité scripts

Les modifications de assemble-prompt.sh (B3), gov-gate.md (B2), gov-impl.md (B1) doivent être rétro-compatibles. Les workflows en cours ne doivent pas être cassés.

CTR-3 — Tests existants non cassés

Les corrections app (A1-A7) ne doivent pas casser les tests existants du projet ProbatioVault-app.


5. Scénarios d'échec

SE-1 — Tests Jest cassent le build existant

Les nouveaux tests importent des modules de manière incompatible avec le setup Jest existant (ESM, mocks). Vérifier la config Jest avant d'écrire.

SE-2 — Phase 6c.bis trop stricte

Le grep cross-module bloque sur des faux positifs (fichier modifié pour autre raison). Prévoir un mécanisme d'override documenté.

SE-3 — Cap test_coverage=6.0 trop agressif

Des stories légitimement sans tests (doc, infra) seraient bloquées. Limiter le cap aux projets app/backend uniquement.


6. Invariants

INV-299-01 — Tests sharing >= 80% coverage

Après A1, npm test -- --testPathPattern=sharing doit passer avec >= 80% coverage sur src/sharing/.

INV-299-02 — Guard owner fonctionnel

Après A2, ProofDetailScreen n'affiche le CTA "Partager" que si le propriétaire est l'utilisateur connecté.

INV-299-03 — Auth header présent

Après A3, tous les appels sharesApi.* incluent Authorization: Bearer (vérifiable en log réseau).

INV-299-04 — PII impossible en metadata

Après A4, logShareEvent({metadata: {recipientEmail: "x"}}) lève une erreur Zod.

INV-299-05 — IP jamais brute

Après A5, maskIp("invalid") retourne "IP masquée indisponible", jamais la valeur d'entrée.

INV-299-06 — Offline bloqué

Après A6, les écrans sharing affichent une erreur si NetInfo.isConnected == false.

INV-299-07 — Phase 6c.bis exécutée

Après B1, /gov-impl vérifie les points d'intégration cross-module post-6c.

INV-299-08 — Gate 8 cap tests

Après B2, /gov-gate Gate 8 plafonne test_coverage à 6.0 si 0 test dans le module livré.

INV-299-09 — Prompts companion enrichis

Après B3, assemble-prompt.sh injecte les arbitrages de la story source pour les gates companion.


7. Périmètre MVP

Partie A (app) : A1-A7 obligatoires, A8 best-effort (dépend du Legal). Partie B (gov) : B1-B6 tous obligatoires.


Fin de l'Expression de Besoin PD-299.