PD-275 — Rapport de confrontation (Étape 5 — Gate Plan, v2)¶
Ce rapport est produit par l'orchestrateur Claude avant la gate PMO 5 (itération 2). Il confronte la spécification, les tests, le plan d'implémentation (post-corrections v2) et les code contracts pour identifier convergences, divergences et zones d'ombre.
1. Sources confrontées¶
- Spécification :
PD-275-specification.md(étape 1, inchangée) - Tests :
PD-275-tests.md(étape 2, inchangés) - Plan d'implémentation :
PD-275-plan.md(étape 4, avec corrections v2 : suppression C8, BLK-01 pré-check signer, MAJ-02 double protection rôles) - Code Contracts :
PD-275-code-contracts.yaml(étape 4, inchangés)
2. Convergences¶
- INV → mécanismes : Les 12 invariants (INV-275-01 à INV-275-12) sont tous mappés vers des mécanismes concrets dans le plan (§3) avec composants identifiés et observables. La couverture est exhaustive.
- CA → mécanismes : Les 11 critères d'acceptation (CA-01 à CA-08 + CA-02b/04b/04c/05b) sont tous mappés dans le plan (§4) avec des composants et observables précis.
- TC → mécanismes : Le plan (§5) mappe chaque test (TC-NOM, TC-ERR, TC-INV, TC-NR, TC-NEG) vers des mécanismes et des points d'observation, avec le niveau de test visé (unit/integration/E2E).
- Codes d'erreur : Les 9 codes ERR-* de la spec sont tous repris dans le plan (§6, composant C10) avec condition, effet et observable. Concordance exacte.
- Fail-closed : Les trois documents s'accordent sur le deny-by-default systématique pour
finalizeBatch(),submitBatch()etrevokeSigner(). - Verrou pessimiste : Le
SELECT ... FOR UPDATEest contractualisé dans la spec (INV-275-11), testé (TC-NOM-10, TC-NOM-11), implémenté dans le plan (C4findByAddressForUpdate) et interdit en contournement dans les code contracts (forbidden: lecture sans verrou pessimiste). - Anti-usurpation revokedBy : Concordance spec (INV-275-10) → plan (C7 validation DTO + extraction
request.user.sub) → tests (TC-ERR-09) → code contracts (forbidden:Accepter revokedBy depuis le body). - État terminal REVOKED : Concordance spec (INV-275-06) → plan (pas de méthode de réactivation) → tests (TC-INV-06, TC-NEG-06) → code contracts (forbidden:
transition REVOKED -> *). - Migration réversible : Concordance spec (CA-08) → plan (C1 migration up/down) → tests (TC-NOM-09) → code contracts (INV migration réversible).
- Bornes confirmation_count : Les bornes
[0, 2147483647]sont identiques entre spec (§10.2), plan (C1 CHECK constraint, C5 validation), tests (TC-ERR-01, TC-NEG-01, TC-NEG-02). - Hypothèses : Le plan reprend les hypothèses de la spec (H-01 à H-07) et les traduit en hypothèses techniques (HT-01 à HT-07) avec des mitigations concrètes.
- Hors périmètre : Concordance exacte entre spec (§2 Exclu) et plan (§10).
- Stack technique : Le plan est conforme à la stack contractuelle spec (NestJS + TypeORM + PostgreSQL + TypeScript). Aucune mention de stack non conforme.
- Schéma signer_registry : Concordance spec (§3, §5) → plan (C1 DDL, C2 entity) → code contracts (module signer-registry-entity).
- Suppression C8 (v2) : La correction v2 supprime
SignerActiveGuard(C8), résolvant la divergence DIV-03 de la confrontation v1. Le contrôle signer est désormais exclusivement dans le service layer avecSELECT ... FOR UPDATEtransactionnel, conforme à INV-275-11. Le flux BullMQ worker est correctement adressé. - Pré-check signer (BLK-01) : La correction v2 déplace le contrôle signer AVANT
createBatch()dans FT4, garantissant CA-05 ("aucun batch créé" si signer REVOKED/inconnu). Conforme à l'exigence spec. - Double protection rôles (MAJ-02) : Le service
revokeSigner()vérifie les rôles en plus du controller@Roles(), couvrant les appels service-to-service. Conforme à INV-275-09. - Autorisation revoke : Concordance spec (INV-275-09, Flux F3 étape 2) → plan (C7
@Roles+ C4 assertion interne) → tests (TC-ERR-08) → code contracts (INV-275-09 dans signer-controller).
3. Divergences¶
⚠️ Les conflits ne doivent JAMAIS être lissés. Chaque divergence est rendue visible.
- DIV-01 : Nom de l'état batch pré-finalisation
- Source A (Plan §2 FT2, §3, §6) : utilise
PENDING_FINALITYcomme état pré-finalisation. - Source B (Spec §5 Modèle d'états) : utilise
NON_FINALIZEDcomme état pré-finalisation. - Source C (Tests TC-INV-07B) : reprend
NON_FINALIZEDde la spec. -
Impact : Divergence terminologique. Le plan s'appuie sur l'état réel du code existant (PD-55) tandis que la spec utilise une abstraction. La correspondance doit être explicitement documentée (
NON_FINALIZED=PENDING_FINALITYdans le code) sinon les tests TC-INV-07B risquent de vérifier un état inexistant. -
DIV-02 : Signature de
revokeSigner()— incohérence interne au plan - Source A (Plan C4 — MAJ-02) :
revokeSigner(address: string, actorIdentity: string, actorRoles: string[], manager: EntityManager)— 4 paramètres, incluantactorRolespour la double protection. - Source B (Plan C7 — FT3 flux) :
SignerRegistryService.revokeSigner(address, actorIdentity, manager)— 3 paramètres,actorRolesabsent de l'appel. -
Impact : La correction v2 (MAJ-02) a ajouté
actorRolesdans la définition du service C4 mais le flux technique FT3 n'a pas été mis à jour pour refléter ce 4ème paramètre. Ambiguïté contractuelle : l'agent implémentant C7 (controller) ne sait pas qu'il doit transmettreactorRolesau service. -
DIV-03 : Anti-usurpation
revokedBy— couverture des vecteurs d'injection - Source A (Spec §6, ERR-REVOKEDBY-SPOOFING) : "presence d'un champ revokedBy fourni par appelant (payload/query/header metier) → requete invalide/refusee".
- Source B (Plan C7, FT3, §7.2) : La validation anti-spoofing couvre uniquement le body DTO ("si le body contient un champ revokedBy → ERR-REVOKEDBY-SPOOFING"). Les vecteurs query param et header métier ne sont pas explicitement traités dans le flux technique.
-
Impact : La spec contractualise le rejet sur trois vecteurs (payload, query, header). Le plan ne couvre explicitement que le body. Conformité anti-usurpation potentiellement incomplète.
-
DIV-04 :
INV-275-09absent des code contracts du servicesigner-registry-service - Source A (Plan C4 — MAJ-02) : Le service vérifie
actorRoleset throwsERR-REVOKE-UNAUTHORIZED— double protection controller + service. - Source B (Code Contracts
signer-registry-service) : Les invariants listés sont INV-275-04, INV-275-05, INV-275-06, INV-275-11, INV-275-12. INV-275-09 (revoke-authorization) est absent. - Source C (Code Contracts
signer-controller) : INV-275-09 est présent. -
Impact : Si le service est censé vérifier les rôles (MAJ-02), l'invariant INV-275-09 doit aussi figurer dans les code contracts du service. L'agent implémentant le service (
agent-signer) n'a pas cette exigence dans son contrat. -
DIV-05 : Double vérification signer dans le flux de soumission (FT4)
- Source A (Spec Flux F4) : Décrit une fenêtre de sérialisation unique — "Le systeme ouvre une transaction DB et verrouille la ligne signer ciblee via SELECT ... FOR UPDATE" → décision → soumission dans la même transaction.
- Source B (Plan FT4 — BLK-01) : Introduit deux vérifications distinctes : (1) pré-check avec transaction dédiée AVANT
createBatch(), (2) re-vérification dans la transactionsubmitBatch()APRÈScreateBatch()/buildBatch(). -
Impact : Le plan crée deux fenêtres de sérialisation au lieu d'une seule. Entre les deux,
createBatch()etbuildBatch()s'exécutent hors verrou signer. SirevokeSigner()intervient entre le pré-check et le re-check, un batch sera créé puis immédiatement rejeté au submit — ce qui satisfait CA-05 ("aucun batch créé" ne veut-il dire aucun batch persisté en DB ou aucun batch entamé ?). La sémantique spec F4 semble exiger une fenêtre unique, le plan en introduit deux pour des raisons pratiques (le pré-check évite le travail inutile de createBatch/buildBatch). -
DIV-06 : Duplication de tests entre sections nominales et erreurs
- Source A (Tests §3-4) : TC-ERR-02 ≡ TC-NOM-02, TC-ERR-04 ≡ TC-NOM-07, TC-ERR-05 ≡ TC-NOM-06.
- Source B (Plan §5) : Reconnaît les duplications mais mappe des niveaux de test différents (TC-ERR → Unit, TC-NOM → Unit + Integration).
-
Impact : Mineur. Redondance de maintenance sans valeur ajoutée fonctionnelle si les Given/When/Then sont identiques.
-
DIV-07 : HTTP status
ERR-SIGNER-REVOKEDdans le contexte BullMQ worker - Source A (Plan §6) :
ERR-SIGNER-REVOKED→ HTTP 409. - Source B (Plan FT4) :
submitBatch()est appelé depuis un BullMQ worker, pas un endpoint HTTP. L'erreur est une exception interne (batch →FAILED). - Impact : Mineur. Le code HTTP est trompeur pour une erreur interne. N'affecte pas le comportement mais peut induire en erreur lors de l'implémentation.
4. Zones d'ombre¶
-
ZO-01 : Seed initial du registre signer — aucun composant ni contrat. Le plan (§9.1) identifie le risque critique du seed initial et recommande un script
seed-signers.tsou une insertion SQL. Mais aucun composant (C1-C11) ne couvre cette responsabilité, et les code contracts ne mentionnent aucun fichier de seed. Ce risque est identifié mais non contractualisé. -
ZO-02 : Rôle
SIGNER_ADMINdans Keycloak — hors périmètre code mais bloquant. Le plan (HT-02, §7.1) mentionne queSIGNER_ADMINest un nouveau rôle à provisionner dans Keycloak. La spec (INV-275-09) l'exige. Aucun composant ni contrat ne couvre cette provision. -
ZO-03 : Normalisation EIP-55 des adresses — recommandée mais non contractualisée. Le plan (§9.4, HT-07) recommande la normalisation EIP-55 systématique. La spec (Q-03) l'identifie comme question ouverte. Ni les invariants de la spec ni les code contracts ne contractualisent cette normalisation. Risque de collision d'adresses en casse différente.
-
ZO-04 :
ConfirmationTracker— interface d'appel non contractualisée. Le plan (FT1, FT4) montreConfirmationTracker→FinalityGuardService.updateConfirmationCount(). Mais l'interface d'appel n'est contractualisée ni dans le plan ni dans les code contracts. Aucun composant ne couvre la modification du tracker existant pour appelerupdateConfirmationCount(). -
ZO-05 : Identité service account pour
revokedBy— non traitée. La spec (§3, H-06) définitactor identitycomme "JWT sub ou identite de service account". Le plan (C7, FT3) fixeactorIdentity = request.user.sub. Le cas d'un service account appelantrevokeSigner()sans JWT HTTP (appel interne) n'est pas documenté dans le plan. Si un service account appelle le service directement (pas via le controller), la source deactorIdentityn'est pas contractualisée. -
ZO-06 : Audit events — placement transactionnel asymétrique. FT3 place
SIGNER_REVOKEDdans la transaction (atomicité avec la mutation, conforme INV-275-05). FT2 placeANCHOR_BATCH_FINALIZEDaprès le commit. Un crash entre commit et logging deANCHOR_BATCH_FINALIZEDperd la trace d'audit de finalisation. La spec ne contractualise pas l'atomicité audit pour la finalisation (seulement pour la révocation), donc techniquement conforme, mais potentiellement incohérent. -
ZO-07 : Questions ouvertes Q-01 à Q-05 non résolues entre spec et plan. Les 5 questions ouvertes de la spec sont transmises comme hypothèses techniques dans le plan. Le verdict QA des tests confirme que la testabilité est partielle. Ces réserves persistent inchangées.
-
ZO-08 : Responsabilité de production des tests d'intégration. Les code contracts assignent
owner_agent: agent-testau moduletests-pd275. Mais les fichiers.spec.tssont aussi listés dans les modules individuels (ex:signer-registry.service.spec.tsdanssigner-registry-serviceavecowner_agent: agent-signer). Double assignation sans clarification : qui produit les tests unitaires du service —agent-signerouagent-test? -
ZO-09 : Sémantique de "aucun batch créé" (CA-05) vs pré-check FT4. CA-05 exige "Aucun batch n'est cree" si signer REVOKED/inconnu. Le plan (BLK-01) ajoute un pré-check qui bloque AVANT
createBatch(), satisfaisant cette exigence. Mais si le pré-check passe etrevokeSigner()intervient entre le pré-check etsubmitBatch(), le re-check danssubmitBatch()rejetera — maiscreateBatch()aura déjà été exécuté. La question est :createBatch()persiste-t-il un batch en DB ? Si oui, CA-05 est partiellement violé dans la fenêtre de course.
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 : Les convergences sont solides (12/12 invariants mappés, 11/11 CA mappés, couverture tests complète). La correction v2 a résolu les divergences v1 (suppression C8, pré-check signer BLK-01). Cependant :
- DIV-02 (MAJEUR) : Incohérence interne — la signature
revokeSigner()dans C4 (4 params avecactorRoles) ne correspond pas à l'appel dans FT3 (3 params). L'agent implémentant C7 n'a pas l'information contractuelle pour transmettreactorRoles. - DIV-03 (MAJEUR) : La couverture anti-spoofing
revokedBydu plan ne traite que le body, alors que la spec exige query et header aussi. - DIV-04 (MAJEUR) : INV-275-09 manquant dans les code contracts du service — contrat incomplet pour la double protection MAJ-02.
- DIV-05 (MAJEUR) : La double fenêtre de vérification signer (pré-check + re-check) diverge de la sémantique de sérialisation unique de la spec F4, avec impact potentiel sur l'interprétation de TC-NOM-10.
- DIV-01 (MINEUR) : Réconciliation terminologique
PENDING_FINALITYvsNON_FINALIZEDnécessaire.