Confirmed. Now I have all the facts needed. Let me produce the structured audit report.
PD-103 — Revue d'acceptabilité (post-correction)¶
1. Références¶
- Spécification : PD-103-specification.md (v3)
- Tests contractuels : PD-103-tests.md (v3)
- Acceptabilité existante : PD-103-acceptability.md
- Date de revue : 2026-04-03
- Reviewer : Claude (auditeur Gate 8 CLOSURE)
2. Suivi des écarts (append-only)¶
[2026-04-03] — Suivi E-01¶
- Statut précédent : OUVERT (BLOQUANT)
- Statut actuel : PARTIELLEMENT RÉSOLU
- Justification factuelle :
SEALEDajouté dansNotifiableCaptureState(notifications.ts:38)- Contenu notification défini (notifications.ts:116-119)
isCaptureStateNotifiable()inclutSEALED(notifications.ts:214)- DIVERGENCE : le body de la notification est
"Votre capture probatoire a été scellée et est en attente d'ancrage blockchain."alors que la spec §5.6 et TC-NOM-08 exigent exactement"Votre capture a été scellée avec succès. Preuve vérifiable disponible." - NON PROUVÉ : le déclenchement effectif à l'état SEALED n'est pas observable dans l'orchestrateur (orchestrator.ts s'arrête à UPLOADED). Le mécanisme de réception push backend → appel
sendCaptureNotification('SEALED')n'est pas visible dans le code audité. - Preuve de vérification :
- commit 5abf199 (app) —
src/capture/notifications.ts(+12/-2) - Lecture directe notifications.ts:116-119 (message divergent)
- Lecture directe orchestrator.ts (aucun appel notification à SEALED)
[2026-04-03] — Suivi E-02¶
- Statut précédent : OUVERT (BLOQUANT)
- Statut actuel : PARTIELLEMENT RÉSOLU
- Justification factuelle :
- Alignements réalisés :
dek_wrapped_b64 TEXT(migration:52 ↔ entity:96),aes_gcm_nonce_b64 VARCHAR(32)(migration:54 ↔ entity:88),aes_gcm_tag_b64 VARCHAR(32)(migration:55 ↔ entity:91), colonnesskew_ms/reconciliation_attempts/last_reconciled_atajoutées,seal_delayed_conformant_cyclesrenommé. - Audit log aligné :
event_payload JSONB,user_id,http_status,source_ip. - BLOQUANT RÉSIDUEL : La contrainte CHECK ligne 104 de la migration référence
seal_delayed_conforming_cycles(colonne inexistante) au lieu deseal_delayed_conformant_cycles(colonne réelle ligne 85). LeCREATE TABLEéchouera à l'exécution. - MINEUR résiduel : Le
COMMENT ON COLUMNligne 169 référencedek_wrappedau lieu dedek_wrapped_b64. - MINEUR résiduel : Contrainte
UNIQUE (capture_id)(migration:97) vs index unique composite['userId', 'captureId'](entity:46). Sémantique différente. - MINEUR résiduel : Default state migration
'CAPTURED'(ligne 47) vs entityPENDING_SEAL(ligne 133). Sans impact runtime (le service impose explicitement PENDING_SEAL), mais incohérence contractuelle. - Preuve de vérification :
- commit 5228173 (backend) — 6 fichiers modifiés
- Migration 1742400000000:104
seal_delayed_conforming_cycles≠ colonneseal_delayed_conformant_cycles(ligne 85) - Migration 1742400000000:169
COMMENT ON COLUMN ...dek_wrapped≠ colonnedek_wrapped_b64
[2026-04-03] — Suivi E-03¶
- Statut précédent : OUVERT (BLOQUANT)
- Statut actuel : RÉSOLU
- Justification factuelle :
SEAL_DELAYEDprésent dans l'enum SQLcapture_state_enum(migration:32)SEAL_DELAYED = 'SEAL_DELAYED'présent dans l'enum TypeORMCaptureEventState(entity:25)- Colonne
seal_delayed BOOLEAN+seal_delayed_conformant_cycles INTEGERprésentes (migration:84-85, entity:146-151) - Service de réconciliation exploite cet état (capture-reconciliation.service.ts existe)
- Preuve de vérification :
- commit 5228173 (backend)
- Migration 1742400000000:26-36 (enum 9 valeurs)
- Entity capture-event.entity.ts:19-29 (enum 9 valeurs)
[2026-04-03] — Suivi E-04¶
- Statut précédent : OUVERT (MAJEUR)
- Statut actuel : RÉSOLU
- Justification factuelle :
- Décorateur
@HttpCode(202)remplacé par@Res({ passthrough: true })+res.status()conditionnel (controller:77-81) result.idempotent ? HttpStatus.OK : HttpStatus.ACCEPTEDimplémente correctement le contrat §5.12- Service retourne
idempotent: truesur replay (ingest.service.ts:171) - Preuve de vérification :
- commit 5228173 (backend)
- capture.controller.ts:77-81
- capture-ingest.service.ts:152-171
[2026-04-03] — Suivi E-05¶
- Statut précédent : OUVERT (MAJEUR)
- Statut actuel : NON RÉSOLU
- Justification factuelle :
_cancelOnCryptoError()(orchestrator.ts:510) passe{ userCancelled: true }comme guard context pour une erreur crypto. INV-103-21 réserveCAPTURED → CANCELLEDà l'annulation utilisateur explicite. La sémantique est incorrecte : une erreur crypto n'est pas une annulation utilisateur.- Preuve de vérification :
- orchestrator.ts:510 —
fsm.transition("CANCELLED", { userCancelled: true })dans_cancelOnCryptoError
[2026-04-03] — Suivi E-06¶
- Statut précédent : OUVERT (MAJEUR)
- Statut actuel : OUVERT
- Justification factuelle :
- Non corrigé dans les commits 5abf199 / 5228173. Aucune modification visible de la cohérence session multipart ↔ objet backend.
[2026-04-03] — Suivi E-07¶
- Statut précédent : OUVERT (MAJEUR)
- Statut actuel : OUVERT
- Justification factuelle :
_cancelFromUploading()(orchestrator.ts:542) conditionne l'abort S3 àif (jwtToken). Si jwtToken est absent, l'abort est sauté. Pas de correction visible.
[2026-04-03] — Suivi E-08¶
- Statut précédent : OUVERT (MAJEUR)
- Statut actuel : OUVERT
- Justification factuelle :
- Divergence payload canonique spec v3 ↔ code-contracts.yaml non vérifiée dans cette revue (hors scope correction). Aucune modification visible.
[2026-04-03] — Suivi E-09¶
- Statut précédent : OUVERT (MAJEUR)
- Statut actuel : OUVERT
- Justification factuelle :
- Fichiers CaptureProgress.tsx (→ renommé CaptureProgressOverlay.tsx existant), tests app, module infra S3. Partiellement couvert par l'existence de CaptureProgressOverlay.tsx mais tests app et infra S3 non vérifiés dans cette revue.
[2026-04-03] — Suivi E-10¶
- Statut précédent : OUVERT (MAJEUR)
- Statut actuel : OUVERT
- Justification factuelle :
- Markdownlint non vérifiable dans cette revue.
[2026-04-03] — Suivi E-12¶
- Statut précédent : OUVERT (MAJEUR)
- Statut actuel : OUVERT
- Justification factuelle :
- Invariants hérités PD-105/106/107 non vérifiables dans le scope PD-103.
[2026-04-03] — Suivi T-01¶
- Statut précédent : OUVERT (MAJEUR)
- Statut actuel : OUVERT
- Justification factuelle :
- Guard
@Roles('user')présent dans controller:45, mais test explicite vérifiant HTTP 403 + body + auditService.log non visible dans les commits de correction.
[2026-04-03] — Suivi T-02¶
- Statut précédent : OUVERT (MAJEUR)
- Statut actuel : OUVERT
- Justification factuelle :
- Test 401 absent de la matrice — non corrigé.
[2026-04-03] — Suivi S-01¶
- Statut précédent : OUVERT (MAJEUR)
- Statut actuel : OUVERT
- Justification factuelle :
- Confusion clé objet single ↔ multipart non corrigée dans les commits audités.
[2026-04-03] — Suivi S-02¶
- Statut précédent : OUVERT (MAJEUR)
- Statut actuel : OUVERT
- Justification factuelle :
deferCapture()persistedekWrappedB64dans le store AsyncStorage (orchestrator.ts:598). Le champ DEK wrappé est chiffré (enveloppe RSA-OAEP), donc pas un DEK en clair. Cependant,ocr_texten clair dansdeferredCapturesAsyncStorage reste un risque de fuite vie privée sur lockscreen/backup. Non corrigé.
[2026-04-03] — Suivi S-03¶
- Statut précédent : OUVERT (MINEUR)
- Statut actuel : OUVERT
- Justification factuelle :
- Notifications définies pour
UPLOADED,UPLOAD_DEFERRED(états non terminaux). Risque divulgation lockscreen. Non corrigé.
[2026-04-03] — Suivi S-04¶
- Statut précédent : OUVERT (MINEUR)
- Statut actuel : OUVERT
- Justification factuelle :
purgeStale()sans whitelist de préfixe. Non corrigé.
3. Verdict d'acceptabilité (courant)¶
Verdict actuel : ⛔ REFUSÉ Date : 2026-04-03 Motif synthétique : 1 BLOQUANT résiduel (E-02 : CHECK constraint chk_seal_delayed_cycles référence colonne inexistante seal_delayed_conforming_cycles — la migration SQL échouera à l'exécution). E-01 partiellement résolu (message notification divergent de la spec, déclenchement effectif non prouvé). 10 MAJEURS ouverts, 2 MINEURS ouverts.
4. Historique des verdicts¶
| Date | Verdict | Version / commit | Commentaire |
|---|---|---|---|
| 2026-04-03 | ⛔ REFUSÉ (initial) | pre-correction | 3 BLOQUANTS, 12 MAJEURS, 4 MINEURS |
| 2026-04-03 | ⛔ REFUSÉ (post-correction v1) | 5abf199 (app) / 5228173 (backend) | E-03 RÉSOLU, E-04 RÉSOLU, E-01 PARTIELLEMENT RÉSOLU, E-02 PARTIELLEMENT RÉSOLU (CHECK constraint SQL invalide). 1 BLOQUANT résiduel. |
Synthèse des corrections requises avant re-soumission¶
| Priorité | Écart | Action requise |
|---|---|---|
| BLOQUANT | E-02 | Corriger chk_seal_delayed_cycles : remplacer seal_delayed_conforming_cycles par seal_delayed_conformant_cycles (migration:104). Corriger COMMENT dek_wrapped → dek_wrapped_b64 (migration:169). |
| BLOQUANT | E-01 | (a) Aligner le body notification SEALED avec la spec : "Votre capture a été scellée avec succès. Preuve vérifiable disponible." (notifications.ts:118). (b) Prouver le déclenchement effectif (handler push backend → sendCaptureNotification). |