PD-280 — Plan d'implementation¶
1. Decoupe en composants¶
C1 — proof-verification-domain (enum + types + configuration)¶
Responsabilite : Extension du domaine CheckResult a 4 valeurs, introduction des types DTO de verification, configuration SLA.
Fichiers impactes : - src/modules/integrity/enums/check-result.enum.ts — ajout PENDING - src/modules/integrity/enums/verification-status.enum.ts — nouveau : enum VerificationStatus { PENDING, DONE } - src/modules/integrity/enums/pending-reason.enum.ts — nouveau : enum PendingReason { ANCHOR_BATCH_NOT_FINALIZED, TX_NOT_ENOUGH_CONFIRMATIONS } - src/modules/integrity/interfaces/proof-verification-result.interface.ts — nouveau : interface ProofVerificationResult (reponse verification) - src/modules/integrity/config/proof-verification.config.ts — nouveau : configuration SLA (pendingResolutionTtl, nextCheckAfterSeconds defaut) - src/modules/integrity/dto/proof-verification-response.dto.ts — nouveau : DTO reponse API - src/modules/integrity/dto/proof-verification-error.dto.ts — nouveau : DTO erreur metier
Decisions : - CheckResult est etendu sur place (pas de nouvel enum) car les 27 references existantes doivent toutes couvrir 4 valeurs. - VerificationStatus est un enum separe (niveau job, pas niveau maillon).
C2 — proof-verification-service (logique metier)¶
Responsabilite : Service de verification de preuve avec logique PENDING, reevaluation SLA lazy, mapping interne/API, controles de coherence.
Fichiers impactes : - src/modules/integrity/services/proof-verification.service.ts — nouveau : service central de verification - src/modules/integrity/services/proof-verification-mapper.service.ts — nouveau : mapping CheckResult.PENDING -> null en API
Decisions : - Service distinct de ArchiveChainVerifierService (PD-251) car le perimetre PD-280 est la verification de preuve (proof), pas la verification d'integrite periodique (archive). - La reevaluation SLA est lazy : declenchee uniquement a l'appel API, pas par worker background.
C3 — proof-verification-controller (endpoint API)¶
Responsabilite : Endpoint REST GET /proofs/:proofId/verification, validation d'entree, reponse formatee.
Fichiers impactes : - src/modules/integrity/controllers/proof-verification.controller.ts — nouveau - src/modules/integrity/integrity.module.ts — modifie : enregistrement du controller, service, mapper et intercepteur dans le module existant IntegrityModule - Pipe de validation UUID v4 pour proofId (reutilisation ParseUUIDPipe existant)
C4 — proof-verification-guards (controles de coherence sortie)¶
Responsabilite : Intercepteur de sortie verifiant les invariants de coherence avant envoi au client.
Fichiers impactes : - src/modules/integrity/interceptors/verification-contract.interceptor.ts — nouveau : controle INV-280-02, INV-280-03, valeur "PENDING" interdite en API, validation pendingReason dans l'enum autorisee
Decisions : - Intercepteur NestJS plutot que middleware pour acceder au corps de reponse. - En cas de violation, retourne 500 VERIFICATION_CONTRACT_BROKEN au lieu d'envoyer une reponse incoherente. - Validation runtime de pendingReason : si la valeur provient de la base de donnees et n'est pas dans l'enum PendingReason, l'intercepteur rejette avec 500 (protection contre desynchronisation code/DB).
C5 — proof-verification-migration (DDL)¶
Responsabilite : Migration TypeORM pour l'extension de l'enum PostgreSQL check_result, ajout de colonnes pour verification_request_id et pending_since.
Fichiers impactes : - src/database/migrations/XXXXXXXXX-PD-280-AddPendingToCheckResult.ts — nouveau
Decisions architecturales : - Decision DDL-01 (enum) : L'enum PostgreSQL check_result passe de 3 a 4 valeurs via ALTER TYPE ... ADD VALUE 'PENDING' (et non varchar). Raison : type strict, validation DB-side, coherence avec les tables existantes. - ALTER TYPE ADD VALUE est non-transactionnel en PostgreSQL : la migration doit utiliser queryRunner.query() directement (pas de transaction wrapping). - Decision DDL-02 (verification_request_id) : Ajout d'une colonne verification_request_id UUID NOT NULL DEFAULT gen_random_uuid() sur la table integrity_verification_jobs (ou equivalent). Le UUID est genere a la creation du job de verification (processus externe, hors perimetre PD-280), pas par l'endpoint GET. L'endpoint GET lit simplement cette colonne. Le verificationRequestId est donc stable de PENDING a DONE sans aucune mutation par l'endpoint (conforme INV-280-09, lecture seule). Les rappels retournent le meme UUID (INV-280-06). - Decision DDL-03 (pending_since) : Ajout d'une colonne pending_since TIMESTAMPTZ DEFAULT NULL sur la table integrity_check_attempts pour chaque maillon. Remplie lors de la transition vers PENDING. Utilisee par le calcul SLA lazy pour determiner si elapsed > pendingResolutionTtl. - Down migration : recreation du type enum sans PENDING + cast controle (avec guard sur absence de lignes PENDING actives en base).
C6 — tla-alignment (artefacts formels)¶
Responsabilite : Alignement _AnchoredFacts_PROOF.tla et norm.yaml sur 4 etats.
Fichiers impactes (dans ProbatioVault-doc) : - docs/normes/pv-proof/formal/_AnchoredFacts_PROOF.tla — ajout PENDING dans RealStates et NormMapping - docs/normes/pv-proof/formal/norm.yaml — ajout PENDING: PENDING dans state_mapping
Decisions : - PV_Proof.tla n'est PAS modifie (il definit deja CheckResults a 4 valeurs depuis l'origine). - Le fichier _AnchoredFacts_PROOF.tla est genere par CI (extract-facts.py) mais la source de verite est norm.yaml. Modifier norm.yaml suffit pour la regeneration CI.
C7 — rfc-documentation (documentation RFC)¶
Responsabilite : Ajout de la section distinguant PENDING vs INDETERMINATE dans la RFC PV-PROOF.
Fichiers impactes (dans ProbatioVault-doc) : - RFC PV-PROOF : ajout section semantique PENDING vs INDETERMINATE
C8 — proof-verification-tests (tests)¶
Responsabilite : Tests unitaires, d'integration et contractuels couvrant les 30 scenarios TC-* (incluant TC-NOM-04).
Fichiers impactes : - src/modules/integrity/services/__tests__/proof-verification.service.spec.ts — nouveau - src/modules/integrity/services/__tests__/proof-verification-mapper.service.spec.ts — nouveau - src/modules/integrity/controllers/__tests__/proof-verification.controller.spec.ts — nouveau - src/modules/integrity/interceptors/__tests__/verification-contract.interceptor.spec.ts — nouveau
2. Flux techniques¶
2.1 Flux nominal — Verification avec maillons PENDING¶
Client Controller Service Mapper DB/Chain
|-- GET /proofs/:id/verification -->| | | |
| |-- validateUUID(id) -->| | | |
| | | | | | |
| |<-- ok ----| | | | |
| | | | | |
| |-- verify(proofId) ---------->| | |
| | | |-- findProof(id) ---| |
| | | |<-- proof entity ---| |
| | | | | |
| | | |-- checkSlaExpiry() | |
| | | | (lazy reeval) | |
| | | | | |
| | | |-- verifyChainLinks | |
| | | |<-- ChainLinkResult | |
| | | | | |
| | | |-- toApiResponse(result) -------------->|
| | | | PENDING -> null | |
| | | | compute status | |
| | | |<-- VerificationResponse ---------------|
| | | | | |
| |<-- ProofVerificationResult --| | |
| | | | | |
| |-- [Interceptor: check coherence] -->| | |
| |<-- ok (or 500) ---------| | | |
| | | | | |
|<-- 200 { verificationRequestId, verificationStatus, linkResults, pending* } | |
2.2 Flux de reevaluation SLA lazy (lecture seule — pas de mutation DB)¶
1. Client appelle GET /proofs/:proofId/verification
2. Service charge l'etat courant des maillons depuis la DB (SELECT, pas UPDATE)
3. Pour chaque maillon en etat interne PENDING :
a. Calculer elapsed = now() - maillon.pending_since
b. Si elapsed > pendingResolutionTtl :
- **Projeter** le maillon comme INDETERMINATE dans la reponse courante
(pas de mutation en base — l'etat DB reste PENDING)
- Logger l'evenement de reclassification SLA pour observabilite
4. Construire la reponse avec les etats projetes
5. Si tous les maillons sont resolus (incluant projections) -> verificationStatus = DONE
IMPORTANT : Conformement a INV-280-09 et CA-12, cet endpoint est en LECTURE SEULE.
La reclassification en base est du ressort du processus de resolution externe (hors perimetre PD-280).
Le mode lazy se traduit par une PROJECTION a la lecture, pas une mutation.
SEMANTIQUE : La spec dit "reclassifie" — cela signifie que l'endpoint PRESENTE le maillon comme
INDETERMINATE dans sa reponse (projection), pas qu'il ecrit en base. L'etat en base reste PENDING
jusqu'a ce qu'un processus externe le mute. L'observabilite est assuree par un log applicatif
horodate emis a chaque projection SLA (evenement "sla_projection", pas "sla_mutation").
Cet evenement de log constitue la "preuve horodatee" exigee par les tests (§8).
2.3 Flux d'idempotence (rappel, PENDING ou DONE)¶
1. Client appelle GET /proofs/:proofId/verification
2. Service charge le job de verification existant (incluant verificationRequestId, genere a la creation du job)
3. Service charge l'etat courant des maillons
4. Construit la reponse avec le meme verificationRequestId (lu, pas genere)
5. Si tous resolus : verificationStatus = DONE
Si au moins un en cours : verificationStatus = PENDING
6. Retourne la reponse avec le meme verificationRequestId dans les deux cas
7. Aucune mutation d'etat — le verificationRequestId est stable de PENDING a DONE car genere a la creation du job
NOTE : Le verificationRequestId est genere par le processus externe qui cree le job de verification
(ex: lors de la soumission de la preuve ou de la planification de la verification).
L'endpoint GET le lit simplement. Cela resout la tension lecture seule vs persistance UUID.
2.4 Diagrammes Mermaid¶
2.4.1 Graphe de dependances des composants¶
graph TD
C1[C1 — proof-verification-domain<br/>enum + types + config]
C2[C2 — proof-verification-service<br/>logique metier]
C3[C3 — proof-verification-controller<br/>endpoint API]
C4[C4 — proof-verification-guards<br/>intercepteur coherence]
C5[C5 — proof-verification-migration<br/>DDL PostgreSQL]
C6[C6 — tla-alignment<br/>artefacts formels]
C7[C7 — rfc-documentation<br/>RFC PV-PROOF]
C8[C8 — proof-verification-tests<br/>tests]
C3 --> C2
C3 --> C4
C3 --> C1
C2 --> C1
C2 --> C5
C4 --> C1
C8 --> C1
C8 --> C2
C8 --> C3
C8 --> C4
C6 -.->|alignement formel| C1
C7 -.->|documentation| C1
style C1 fill:#e8f4fd,stroke:#2196f3
style C2 fill:#e8f4fd,stroke:#2196f3
style C3 fill:#fff3e0,stroke:#ff9800
style C4 fill:#fce4ec,stroke:#e91e63
style C5 fill:#f3e5f5,stroke:#9c27b0
style C6 fill:#e8f5e9,stroke:#4caf50
style C7 fill:#e8f5e9,stroke:#4caf50
style C8 fill:#fff9c4,stroke:#fbc02d 2.4.2 Diagramme de sequence — Flux nominal (verification avec maillons PENDING)¶
sequenceDiagram
participant Client
participant Controller as C3 — Controller
participant Interceptor as C4 — Intercepteur
participant Service as C2 — Service
participant Mapper as C2 — Mapper
participant DB as PostgreSQL
Client->>Controller: GET /proofs/:proofId/verification
Controller->>Controller: ParseUUIDPipe(proofId)
alt proofId invalide
Controller-->>Client: 400 INVALID_PROOF_ID
end
Controller->>Service: verify(proofId)
Service->>DB: SELECT proof + check_attempts + verification_job
DB-->>Service: proof entity + maillons + job (verification_request_id)
loop Pour chaque maillon PENDING
Service->>Service: checkSlaExpiry(maillon)
alt elapsed > pendingResolutionTtl
Service->>Service: projeter comme INDETERMINATE (pas de mutation DB)
end
end
Service->>Mapper: toApiResponse(internalResult)
Note over Mapper: PENDING interne → null en API<br/>Derive verificationStatus (PENDING/DONE)<br/>Inclut/exclut pendingReason + nextCheckAfterSeconds
Mapper-->>Service: ProofVerificationResponseDto
Service-->>Controller: ProofVerificationResult
Controller->>Interceptor: validation coherence sortie
alt INV-280-02/03 viole
Interceptor-->>Client: 500 VERIFICATION_CONTRACT_BROKEN
end
Interceptor-->>Controller: ok
Controller-->>Client: 200 { verificationRequestId, verificationStatus, linkResults, pending* } 2.4.3 Diagramme de sequence — Reevaluation SLA lazy¶
sequenceDiagram
participant Client
participant Service as C2 — Service
participant DB as PostgreSQL
Client->>Service: GET /proofs/:proofId/verification
Service->>DB: SELECT maillons WHERE proof_id = :proofId
DB-->>Service: maillons (certains en etat PENDING)
loop Pour chaque maillon PENDING
Service->>Service: elapsed = now() - maillon.pending_since
alt elapsed > pendingResolutionTtl
Note over Service: Projection lecture seule :<br/>maillon presente comme INDETERMINATE<br/>Etat DB reste PENDING<br/>Log "sla_projection" emis
else elapsed <= pendingResolutionTtl
Note over Service: Maillon reste PENDING<br/>→ null en API
end
end
Service-->>Client: Reponse avec etats projetes (pas de mutation DB) 3. Mapping invariants -> mecanismes¶
| Invariant ID | Exigence | Mecanisme | Composant | Observable | Risque |
|---|---|---|---|---|---|
| INV-280-01-checkresult-domain | CheckResult contient exactement 4 valeurs | Enum TypeScript compile + test exhaustif | C1 | Compilation echoue si 5e valeur ; test verifie cardinalite = 4 | Faible : enum TypeScript strict |
| INV-280-02-status-coherence | PENDING ssi au moins un linkResults[*] = null ; DONE ssi tous resolus | Mapper : derive verificationStatus depuis linkResults ; intercepteur de sortie verifie biconditionnelle | C2 + C4 | Intercepteur rejette 500 si incoherence detectee | Moyen : logique de derivation doit etre testee en profondeur |
| INV-280-03-pending-fields | pendingReason + nextCheckAfterSeconds obligatoires si PENDING, interdits si DONE | Mapper : inclut/exclut conditionnellement ; intercepteur verifie | C2 + C4 | 500 si champs presents en DONE ou absents en PENDING | Faible : test schema conditionnel |
| INV-280-04-finalization | Si proof_finalized = TRUE, jamais PENDING/null | Guard dans le service : si finalized, assert aucun maillon PENDING | C2 | 500 VERIFICATION_CONTRACT_BROKEN si violation detectee | Moyen : depend de l'integrite des donnees en base |
| INV-280-05-link-transitions | Maillon : PENDING -> {OK,KO,INDETERMINATE} uniquement ; retour interdit | Reclassification SLA lazy + verif chain links : seules transitions forward autorisees ; guard dans le mapper | C2 | Test TC-INV-05 : tentative de transition retour refusee | Faible : endpoint lecture seule, mutations par processus externes |
| INV-280-06-job-transitions | verificationRequestId : DONE terminal ; rappel retourne reponse identique | Service : si DONE existant, retourne tel quel sans reouverture | C2 | Test TC-INV-06 : rappel idempotent, meme verificationRequestId | Faible : lecture seule |
| INV-280-07-tla-bijection | RealStates = CheckResults (4 valeurs) | Mise a jour norm.yaml + _AnchoredFacts_PROOF.tla ; CI verifie ASSUME | C6 | Pipeline TLA+ vert (ASSUME satisfait) | Faible : modification mecanique |
| INV-280-09-concurrent-idempotence | Deux appels concurrents retournent le meme resultat | Endpoint lecture seule, pas de lock applicatif | C2 + C3 | Test TC-NOM-10 : appels paralleles -> reponses identiques | Faible : pas de mutation |
4. Mapping criteres d'acceptation -> mecanismes¶
| Critere ID | Mecanisme(s) | Composant | Observable | Risque |
|---|---|---|---|---|
| CA-01 | Enum CheckResult a 4 valeurs compile + test domaine exhaustif | C1, C8 | Compilation + test TC-NOM-08 | Faible |
| CA-02 | DTO ProofVerificationResponseDto avec verificationRequestId, verificationStatus, linkResults ; mapper null pour PENDING | C1, C2 | Test TC-NOM-01 : payload JSON conforme | Faible |
| CA-03 | Service verification : batch PENDING_FINALITY -> maillon interne PENDING (pas INDETERMINATE) | C2 | Test TC-NOM-03 : assert PENDING et non INDETERMINATE | Moyen |
| CA-04 | Mapper derive DONE ssi tous linkResults non-null ; intercepteur valide biconditionnelle | C2, C4 | Tests TC-NOM-02, TC-ERR-03 | Faible |
| CA-05 | Mapper inclut/exclut pendingReason+nextCheckAfterSeconds selon status ; intercepteur valide | C2, C4 | Tests TC-NOM-01, TC-NOM-02, TC-ERR-04 | Faible |
| CA-06 | norm.yaml + _AnchoredFacts_PROOF.tla alignes sur 4 etats | C6 | Pipeline TLA+ vert : TC-NOM-07, TC-ERR-06 | Faible |
| CA-07 | Guard finalization dans service : assert aucun PENDING si proof_finalized=TRUE | C2 | Test TC-NOM-06 | Moyen |
| CA-08 | Suite de tests existante (PD-251 etc.) reste verte apres extension enum | C8 | Regression suite verte | Moyen : impact potentiel sur les consommateurs de CheckResult |
| CA-09 | Section ajoutee dans RFC PV-PROOF distinguant PENDING vs INDETERMINATE | C7 | Test TC-NR-03 : verification textuelle | Faible |
| CA-10 | Config pendingResolutionTtl [1h, 30j] ; clamp nextCheckAfterSeconds [1, 86400] | C1, C2 | Tests TC-NOM-05, TC-NEG-05, TC-NEG-09 | Faible |
| CA-11 | Validation UUID v4 en entree (ParseUUIDPipe) ; enums case-sensitive en sortie | C3, C4 | Tests TC-ERR-01, TC-NEG-02, TC-NEG-03, TC-NOM-09 | Faible |
| CA-12 | Endpoint lecture seule ; idempotence DONE ; concurrence identique | C2, C3 | Tests TC-NOM-10, TC-INV-06 | Faible |
5. Mapping tests (TC-*) -> mecanismes + observables¶
| Test ID | Reference spec | Mecanisme(s) | Point(s) d'observation | Niveau de test vise |
|---|---|---|---|---|
| TC-NOM-01 | INV-280-02, INV-280-03, CA-02, CA-05, CA-10, CA-11 | Service verify + Mapper + Intercepteur | Payload JSON : verificationRequestId UUID v4, status=PENDING, linkResults 4 cles, null pour PENDING, pendingReason present, nextCheckAfterSeconds dans [1,86400] | Integration |
| TC-NOM-02 | INV-280-02, INV-280-03, CA-02, CA-04, CA-05 | Service verify + Mapper | Payload : status=DONE, aucun null, aucun PENDING, pas de pending* | Integration |
| TC-NOM-03 | INV-280-05, CA-03 | Service verify : evaluation maillon blockchain | Maillon interne = PENDING (pas INDETERMINATE) quand PENDING_FINALITY | Unitaire |
| TC-NOM-04 | SCN-280-04, CA-03 | Service : evaluation maillon blockchain inaccessible | Maillon interne = INDETERMINATE (determination immediate, distinct de SLA lazy) | Unitaire |
| TC-NOM-05 | INV-280-05, CA-10, ERR-280-05 | Service SLA lazy : projection PENDING -> INDETERMINATE a la lecture | Etat maillon projete apres expiration SLA + absence erreur transport | Integration |
| TC-NOM-06 | INV-280-04, CA-07 | Guard finalization dans service | Aucun PENDING/null si proof_finalized=TRUE | Unitaire |
| TC-NOM-07 | INV-280-07, CA-06 | norm.yaml + _AnchoredFacts_PROOF.tla | Pipeline TLA+ ASSUME satisfait | CI/Formel |
| TC-NOM-08 | INV-280-01, CA-01 | Enum CheckResult | Cardinalite = 4, valeurs exactes | Unitaire |
| TC-NOM-09 | AMB-V2-01, CA-02, CA-11 | Mapper : generation verificationRequestId | UUID v4 present, stable en rappel DONE | Integration |
| TC-NOM-10 | INV-280-09, CA-12 | Controller : 2 appels concurrents | Reponses identiques, pas de mutation | Integration |
| TC-ERR-01 | ERR-280-01, CA-11 | ParseUUIDPipe : rejet proofId invalide | 400 INVALID_PROOF_ID | Unitaire |
| TC-ERR-02 | ERR-280-02 | Service : proofId inexistant | 404 PROOF_NOT_FOUND | Unitaire |
| TC-ERR-03 | ERR-280-03, INV-280-02, CA-04 | Intercepteur coherence | 500 VERIFICATION_CONTRACT_BROKEN (DONE + null) | Unitaire |
| TC-ERR-04 | ERR-280-04, INV-280-03, CA-05 | Intercepteur coherence | 500 VERIFICATION_CONTRACT_BROKEN (DONE + pending*) | Unitaire |
| TC-ERR-06 | ERR-280-06, INV-280-07, CA-06 | CI TLA+ | Build refuse (desalignement) | CI/Formel |
| TC-NOM-04 | SCN-280-04, CA-03 | Service : evaluation maillon blockchain inaccessible | Maillon interne = INDETERMINATE (determination immediate, distinct du SLA lazy) | Unitaire |
| TC-INV-05 | INV-280-05, CA-03, CA-10 | Couche de persistance : guard transition retour (processus externe). Dans le perimetre PD-280 : le mapper verifie qu'aucun maillon terminal ne peut etre projete comme PENDING. | Maillon terminal en base -> jamais expose comme null en API | Unitaire |
| TC-INV-06 | INV-280-06, CA-04, CA-12 | Service : rappel DONE idempotent | Reponse identique, verificationRequestId stable | Integration |
| TC-NR-01 | CA-08 | Suite de tests PD-251 existante | Tests verts apres extension enum | Regression |
| TC-NR-02 | CA-08 | Schema linkResults 4 cles | Compatibilite conservee | Regression |
| TC-NR-03 | CA-09 | RFC PV-PROOF | Presence textuelle section PENDING vs INDETERMINATE | Documentaire |
| TC-NR-04 | CA-06, CA-08 | Pipeline TLA+ | Build formel vert sur baseline + story | CI/Formel |
| TC-NEG-01 | CA-11 | ParseUUIDPipe | 400 pour UUID non v4 | Unitaire |
| TC-NEG-02 | CA-11 | Intercepteur | 500 pour cle linkResults hors contrat | Unitaire |
| TC-NEG-03 | CA-11 | Intercepteur | 500 pour enum minuscule | Unitaire |
| TC-NEG-04 | INV-280-02 | Intercepteur | 500 pour DONE avec linkResults contenant "PENDING" | Unitaire |
| TC-NEG-05 | CA-10 | Mapper clamp | Clamp nextCheckAfterSeconds en [1, 86400] | Unitaire |
| TC-NEG-06 | INV-280-05 | Service/Mapper | Transition OK->PENDING refusee | Unitaire |
| TC-NEG-07 | INV-280-06 | Service | DONE->PENDING retourne reponse DONE existante (pas erreur) | Integration |
| TC-NEG-09 | CA-10 | Config validation | Bornes pendingResolutionTtl [1h, 30j] | Unitaire |
| TC-NEG-11 | Perimetre | Inspection architecture | Aucun worker/cron SLA | Documentaire |
6. Gestion des erreurs¶
| Code erreur | Code HTTP | Condition | Mecanisme | Observable |
|---|---|---|---|---|
INVALID_PROOF_ID | 400 | proofId non UUID v4 | ParseUUIDPipe avec exceptionFactory custom | Corps JSON deterministe avec code erreur |
PROOF_NOT_FOUND | 404 | proofId UUID v4 valide mais inexistant | Service : query DB retourne null | Corps JSON avec code erreur |
VERIFICATION_CONTRACT_BROKEN | 500 | Incoherence detectee (DONE+null, DONE+pending*, enum minuscule, cle hors contrat, PENDING en linkResults) | VerificationContractInterceptor | Corps JSON avec code + requestId/proofId pour correlation |
Strategie de traces : - Chaque erreur 500 VERIFICATION_CONTRACT_BROKEN produit un log error avec requestId et proofId pour correlation. - Pas de journal d'audit dedié (hors perimetre CA explicite) — les traces applicatives standard suffisent.
7. Impacts securite¶
| Risque | Mitigation | Composant |
|---|---|---|
Exposition de "PENDING" en API publique (INV-280-05) | Intercepteur de sortie valide que linkResults[*] ne contient jamais la string "PENDING" ; 500 sinon | C4 |
| Fuite d'information sur l'etat interne des maillons | Seul null est expose pour les maillons en cours ; pas de detail sur le type de pending | C2 |
| IDOR sur proofId | ParseUUIDPipe + verification que la preuve appartient au tenant (reutilisation guard RLS existant PD-251, non reimplement dans PD-280 — pattern deja contractualise) | C3 |
| Timing attack sur l'etat de verification | Endpoint lecture seule, pas de mutation, latence constante | C2, C3 |
| Injection dans proofId | Validation UUID v4 stricte via regex contractuelle | C3 |
8. Hypotheses techniques¶
| ID | Hypothese | Impact si faux |
|---|---|---|
| HT-01 | L'entity LegalCompositeProof (schema vault_secure) expose un champ proofId UUID v4 qui servira de cle d'entree pour la verification | Si non, il faudra trouver ou creer la table/colonne de reference des preuves |
| HT-02 | L'enum PostgreSQL check_result est utilise dans integrity_check_attempts et potentiellement dans d'autres tables de verification ; ALTER TYPE ADD VALUE est supporte sans downtime | Si non, migration complexe avec recreation de type |
| HT-03 | Le champ chain_blockchain dans IntegrityCheckAttempt a deja default: 'PENDING' en DB — ce qui confirme que PostgreSQL accepte deja la string 'PENDING' meme si l'enum TypeScript ne le contient pas | Si l'enum PostgreSQL est stricte et ne contient que 3 valeurs, la migration ALTER TYPE est necessaire |
| HT-04 | La table anchor_batches (schema vault_blockchain) contient un champ status avec au moins la valeur 'PENDING_FINALITY' pour detecter les maillons blockchain en attente | Si non, le mecanisme de detection PENDING_FINALITY doit etre adapte |
RESOLU : Decision DDL-02 — colonne verification_request_id UUID NOT NULL DEFAULT gen_random_uuid() ajoutee a la migration C5. Genere a la creation du job de verification (processus externe), lu par l'endpoint GET (conforme INV-280-09 lecture seule). Stable de PENDING a DONE. | ||
RESOLU : Decision DDL-03 — colonne pending_since TIMESTAMPTZ ajoutee a la migration C5. Remplie lors de la transition vers PENDING. |
9. Points de vigilance (risques, dette, pieges)¶
9.1 Extension de CheckResult — impact transversal¶
Risque : L'enum CheckResult est utilise dans PD-251 (integrity verification) par ArchiveChainVerifierService, ChainLinkResult, et tous les processors. Ajouter PENDING signifie que tous les pattern-match (switch/if) doivent couvrir 4 cas.
Mitigation : TypeScript en mode strict detecte les exhaustive checks manquants. Executer tsc --noEmit apres modification de l'enum pour identifier tous les sites a mettre a jour.
Action : Ajouter un test TC-NR-01 qui verifie que la suite existante reste verte.
9.2 Migration enum PostgreSQL¶
Risque : ALTER TYPE ... ADD VALUE n'est pas reversible dans une transaction PostgreSQL (pre-v12). La down migration necessite une recreation complete du type enum.
Mitigation : Down migration avec cast temporaire varchar + recreation enum 3 valeurs + cast retour. Guard : verifier qu'aucune ligne PENDING n'existe en base avant down migration.
9.3 Reevaluation SLA sans worker¶
Risque : Le mode lazy signifie que si personne n'appelle l'endpoint de verification, un maillon PENDING peut rester indefiniment sans reclassification.
Acceptation : Par perimetre de la spec — le worker background est explicitement hors perimetre (§2). La reclassification n'a lieu qu'a l'appel API.
9.4 Coherence avec PD-251¶
Risque : PD-251 utilise CheckResult pour le resultat de verification d'integrite periodique. PD-280 l'utilise pour la verification de preuve. Les deux modules partagent l'enum mais ont des semantiques distinctes.
Mitigation : L'enum est partage (correction de domaine), mais les services sont distincts. PD-251 ne doit jamais produire PENDING dans ses resultats finaux (il verifie dans un run batch complet).
9.5 Stubs inter-PD¶
// STUB: PD-281 — Zlint anchor enum: si PD-281 modifie les validateurs d'ancrage blockchain, la detectionPENDING_FINALITYdans le maillon blockchain pourrait etre impactee.// STUB: PD-??? — Worker SLA background: le worker de reevaluation en arriere-plan est hors perimetre actuel. Quand il sera implemente, il devra utiliser les memes regles de reclassification que le mode lazy.
10. Hors perimetre¶
| Element | Justification |
|---|---|
| Pattern asynchrone complet de verification (queue/BullMQ) | Spec §2 : exclu explicitement |
| Worker/cron de reevaluation SLA en background | Spec §2 : mode lazy uniquement dans ce perimetre |
| Changement de l'endpoint de creation de preuve | Spec §2 : hors perimetre |
| Changement de la logique metier de finalisation de preuve | Spec §2 : hors perimetre |
| Invariants constitutionnels transversaux (chiffrement at-rest, HSM/KMS) | Spec §2 : non repetes, appliques par defaut |
| Politique de versionnement API pour ajout d'enum | Spec §10.2 : non fournie, non testable |
| Comportement client legacy face a PENDING | Spec §10.2 : non fournie |
| Down migration en production avec donnees PENDING actives | Spec §10.2 : non tranchee |
11. Mecanismes cross-module¶
Aucune modification d'autres modules. Le plan ne prevoit pas d'ajouter de guard, middleware ou intercepteur sur les routes d'un autre module. L'intercepteur VerificationContractInterceptor est enregistre uniquement sur le controller ProofVerificationController (pas en APP_GUARD global).
Interaction PD-251 : La dependance avec PD-251 est au niveau de l'enum partage CheckResult. Les services PD-251 continuent de ne produire que {OK, KO, INDETERMINATE} dans leurs resultats finaux — l'ajout de PENDING a l'enum n'affecte pas leur logique (les branches exhaustives seront a completer).
12. Perimetre de test¶
| Niveau de test | In scope | Hors scope (justification) |
|---|---|---|
| Unitaire | Tous les composants C1 a C5 : enum, service, mapper, intercepteur, controller, config | — |
| Integration | Interactions service + DB (reevaluation SLA lazy), controller + service + intercepteur (flux complet), concurrence (TC-NOM-10) | — |
| E2E | Flux complet GET /proofs/:id/verification avec DB reelle (pending nominal, done nominal, reclassification SLA) | — |
| CI/Formel | Pipeline TLA+ (TC-NOM-07, TC-ERR-06, TC-NR-04) | — |
| Documentaire | RFC PV-PROOF (TC-NR-03) | — |
| Regression | Suite PD-251 complete + compilation exhaustive (TC-NR-01, TC-NR-02) | — |
Tous les niveaux de test sont couverts, aucune exclusion.
Couverture attendue : exhaustive sur le perimetre in scope (C1 a C5 + C8). Chaque invariant (INV-280-), critere d'acceptation (CA-) et cas d'erreur (ERR-280-*) DOIT avoir au moins un test dedie avec oracle deterministe.
13. Contraintes techniques¶
| ID | Contrainte | Detail |
|---|---|---|
| CT-01 | Framework de test | Jest (framework standard du projet ProbatioVault-backend, configure dans jest.config.ts) |
| CT-02 | Compatibilite ESM/CJS | Le runner Jest est configure en mode ts-jest avec module: commonjs. Les imports ESM-only doivent utiliser le pattern await import() dynamique ou jest.unstable_mockModule si mocking necessaire. |
| CT-03 | Variables CI | DATABASE_URL (PostgreSQL), CI=true (active SonarQube reporter). Configuration dans .gitlab-ci.yml template standard. |
| CT-04 | Dependencies inter-PD | PD-251 (DONE, enum CheckResult partage), PD-281 (TODO, Zlint anchor — STUB: // STUB: PD-281), Worker SLA background (TODO, story non creee — STUB: // STUB: PD-??? Worker SLA background) |