Aller au contenu

PD-177 — Decomposition en taches agents

Version : 1.0 Date : 2026-02-23 Etape : 6a (decomposition) Entrees : PD-177-specification.md v2, PD-177-tests.md v2, PD-177-plan.md v1.1, PD-177-code-contracts.yaml v1.1


1. Matrice des dependances

Tache Module code-contract Agent Depends_on Fichiers produits
T-01 blockchain-error-codes agent-developer (aucune) src/modules/blockchain/constants/error-codes.ts
T-02 blockchain-confirmation-policy agent-developer (aucune) src/modules/blockchain/constants/network-confirmation-policy.ts
T-03 blockchain-config-enrichment agent-developer T-02 src/modules/blockchain/blockchain.config.ts, src/modules/blockchain/blockchain.module.ts
T-04 blockchain-transaction-enrichment (DTO) agent-developer (aucune) src/modules/blockchain/dto/transaction.dto.ts
T-05 anchor-entity-enrichment agent-sre (aucune) src/modules/anchor/entities/anchor-batch.entity.ts, src/database/migrations/*-PD177-AddSignerAddress.ts
T-06 blockchain-custody-gate agent-developer T-01 src/modules/blockchain/custody/custody-mode.guard.ts
T-07 blockchain-security agent-developer T-01 src/modules/blockchain/security/secret-leak.interceptor.ts
T-08 blockchain-transaction-enrichment (services) agent-developer T-01, T-02, T-04 src/modules/blockchain/transaction/confirmation.tracker.ts, src/modules/blockchain/transaction/transaction.service.ts
T-09 blockchain-wallet-operational agent-developer T-01, T-06, T-07 src/modules/blockchain/wallet/wallet-operational.service.ts, src/modules/blockchain/wallet/anchor-exclusivity.guard.ts, src/modules/blockchain/wallet/wallet-recovery.service.ts
T-10 anchor-service-enrichment agent-developer T-01, T-02, T-04, T-05, T-08 src/modules/anchor/services/blockchain-adapter.service.ts, src/modules/anchor/services/anchor-batch.service.ts, src/modules/anchor/processors/blockchain-anchor.processor.ts
T-11 anchor-proof-validation agent-developer T-01, T-05 src/modules/anchor/validators/anchor-proof.validator.ts, src/modules/anchor/dto/proof-artifact.dto.ts
T-12 anchor-constants-enrichment agent-developer T-02 src/modules/anchor/constants/anchor.constants.ts
T-13 blockchain-config-enrichment (wiring) agent-developer T-03, T-06, T-07, T-08, T-09, T-11, T-12 src/modules/blockchain/blockchain.module.ts (providers + exports finaux)
T-14 operational-documentation agent-sre (aucune) docs/runbooks/wallet-recovery-procedure.md, docs/runbooks/wallet-rotation-log-template.md
T-15 tests-pd177 agent-qa-unit-integration T-01..T-13 src/modules/blockchain/__tests__/pd177/**/*.spec.ts, src/modules/anchor/__tests__/pd177/**/*.spec.ts

2. Parallelization

parallelization:
  level_0:
    description: "Fondations independantes (aucune dependance PD-177)"
    tasks: [T-01, T-02, T-04, T-05, T-14]
    agents_actifs: [agent-developer, agent-sre]
    note: >
      T-01 (error codes), T-02 (confirmation policy), T-04 (DTO signerAddress)
      sont 3 taches developer paralleles.
      T-05 (migration + entity) est SRE parallele.
      T-14 (runbooks) est SRE parallele.

  level_1:
    description: "Services fondation dependant du level 0"
    tasks: [T-03, T-06, T-07, T-08, T-12]
    depends_on: [level_0]
    agents_actifs: [agent-developer]
    note: >
      T-03 depend de T-02. T-06/T-07 dependent de T-01.
      T-08 depend de T-01+T-02+T-04. T-12 depend de T-02.
      Tous parallelisables entre eux une fois level_0 termine.

  level_2:
    description: "Services composites et integration ancrage"
    tasks: [T-09, T-10, T-11]
    depends_on: [level_1]
    agents_actifs: [agent-developer]
    note: >
      T-09 depend de T-06+T-07. T-10 depend de T-05+T-08.
      T-11 depend de T-01+T-05.
      T-09 et T-11 parallelisables. T-10 necessite T-08 (level 1).

  level_3:
    description: "Wiring module final"
    tasks: [T-13]
    depends_on: [level_2]
    agents_actifs: [agent-developer]
    note: >
      T-13 est le cablage final du module NestJS.
      Depend de tous les composants crees.

  level_4:
    description: "Tests complets"
    tasks: [T-15]
    depends_on: [level_3]
    agents_actifs: [agent-qa-unit-integration]
    note: >
      Les tests necessitent tous les composants en place.

Diagramme DAG

Level 0 (parallele):
  T-01 ─┬─────────────────────────────────────┐
  T-02 ─┼──────────────────────┐              │
  T-04 ─┤                      │              │
  T-05 ─┼──────────┐           │              │
  T-14 ─┘(termine)  │           │              │
                    │           │              │
Level 1 (parallele apres L0):  │              │
  T-03 ◄─── T-02   │           │              │
  T-06 ◄─── T-01   │           │              │
  T-07 ◄─── T-01   │           │              │
  T-08 ◄─── T-01+T-02+T-04     │              │
  T-12 ◄─── T-02   │           │              │
                    │           │              │
Level 2 (parallele apres L1):  │              │
  T-09 ◄─── T-06+T-07          │              │
  T-10 ◄─── T-05+T-08          │              │
  T-11 ◄─── T-01+T-05──────────┘              │
Level 3 (apres L2):                            │
  T-13 ◄─── T-03+T-06+T-07+T-08+T-09+T-11+T-12
Level 4 (apres L3): │
  T-15 ◄─── T-01..T-13

3. Detail des taches


T-01 — Ajout error codes PD-177

Champ Valeur
Agent agent-developer
Module blockchain-error-codes
Phase plan Phase 1 (Fondations)
Depends_on (aucune)
Invariants INV-177-17
Tests couverts TC-ERR-01, TC-ERR-02, TC-ERR-03..08 (codes utilises)

Fichiers modifies : - src/modules/blockchain/constants/error-codes.ts

Instructions :

Ajouter 4 nouveaux codes dans l'enum BlockchainErrorCode existant (lignes 7-32). Les codes PD-52 existants sont INCHANGES. Aucune modification de la classe BlockchainError ni de ses methodes sanitizeContext() et toLog().

Codes a ajouter apres la ligne 31 (avant la fermeture de l'enum) :

// PD-177: Custody mode gate (ERR-177-01)
INVALID_CUSTODY_MODE = 'INVALID_CUSTODY_MODE',

// PD-177: Signature failure at custody level (ERR-177-02)
SIGNATURE_FAILED = 'SIGNATURE_FAILED',

// PD-177: Incomplete proof chain (ERR-177-07)
PROOF_LINK_INCOMPLETE = 'PROOF_LINK_INCOMPLETE',

// PD-177: Reorg or abandonment (ERR-177-06)
TRANSACTION_REORGED_OR_ABANDONED = 'TRANSACTION_REORGED_OR_ABANDONED',

Regles : - Ne PAS renommer ni supprimer les codes PD-52 existants - Ne PAS modifier BlockchainError, sanitizeContext(), toLog(), getErrorMessage() - Les nouveaux codes sont des entrees distinctes, pas des aliases vers les codes existants (INVALID_CUSTODY_MODE != CUSTODY_MODE_INVALID) - Ajouter le commentaire PD-177 avec reference ERR-177-XX pour chaque code

Critere de completion : L'enum compile sans erreur TypeScript et contient exactement 19 codes (15 PD-52 + 4 PD-177).


T-02 — Politique de confirmation par reseau

Champ Valeur
Agent agent-developer
Module blockchain-confirmation-policy
Phase plan Phase 1 (Fondations)
Depends_on (aucune)
Invariants INV-177-16
Tests couverts TC-177-05, TC-177-13

Fichiers crees : - src/modules/blockchain/constants/network-confirmation-policy.ts

Instructions :

Creer un fichier de constantes immuables definissant la politique de confirmation contractualisee par reseau.

/**
 * Politique de confirmation par reseau — PD-177
 * INV-177-16 : Confirmation par reseau contractualisee
 *
 * Polygon (mainnet chainId=137, testnet chainId=80002): 12 confirmations, 900s timeout
 * Arbitrum (mainnet chainId=42161, testnet chainId=421614): 30 confirmations, 900s timeout
 */

import { NetworkType } from '../dto/transaction.dto';

export interface ConfirmationPolicy {
  readonly confirmations: number;
  readonly timeoutMs: number;
}

export const NETWORK_CONFIRMATION_POLICY: Readonly<Record<NetworkType, ConfirmationPolicy>> = {
  polygon: { confirmations: 12, timeoutMs: 900_000 },
  arbitrum: { confirmations: 30, timeoutMs: 900_000 },
} as const;

/**
 * Retourne la politique de confirmation pour un reseau donne.
 * @throws Error si le reseau n'est pas supporte
 */
export function getConfirmationPolicy(network: NetworkType): ConfirmationPolicy {
  const policy = NETWORK_CONFIRMATION_POLICY[network];
  if (!policy) {
    throw new Error(`No confirmation policy for network: ${network}`);
  }
  return policy;
}

Regles : - Les valeurs sont des constantes immuables (as const + Readonly) - Ne PAS rendre les seuils configurables par env var (valeurs contractualisees) - Ne PAS ajouter de reseaux non specifies (Ethereum mainnet, etc.) - Importer NetworkType depuis ../dto/transaction.dto

Critere de completion : Le fichier compile, les valeurs correspondent exactement a la spec (polygon 12/900000, arbitrum 30/900000).


T-03 — Enrichissement config et module (partiel)

Champ Valeur
Agent agent-developer
Module blockchain-config-enrichment
Phase plan Phase 1 (Fondations)
Depends_on T-02
Invariants INV-177-16
Tests couverts TC-177-05

Fichiers modifies : - src/modules/blockchain/blockchain.config.ts

Instructions :

Ajouter confirmationTimeoutMs dans l'interface NetworkConfig et les valeurs par defaut dans la factory de config.

  1. Interface NetworkConfig (ligne 7-12) : ajouter confirmationTimeoutMs: number
export interface NetworkConfig {
  chainId: number;
  rpcPrimary: string;
  rpcSecondary: string;
  confirmations: number;
  confirmationTimeoutMs: number; // PD-177: Timeout de confirmation par reseau
}
  1. Factory config : ajouter confirmationTimeoutMs dans les objets polygon et arbitrum :
polygon: {
  // ... existant ...
  confirmationTimeoutMs: Number.parseInt(
    process.env.BLOCKCHAIN_POLYGON_CONFIRMATION_TIMEOUT_MS || '900000',
    10,
  ),
},
arbitrum: {
  // ... existant ...
  confirmationTimeoutMs: Number.parseInt(
    process.env.BLOCKCHAIN_ARBITRUM_CONFIRMATION_TIMEOUT_MS || '900000',
    10,
  ),
},

Regles : - Ne PAS modifier les valeurs par defaut des configs PD-52 existantes (confirmations: 5 pour polygon, confirmations: 1 pour arbitrum restent inchanges — ce sont les configs PD-52 testnet) - La valeur par defaut de confirmationTimeoutMs est 900000 (900s = 15 min) pour les deux reseaux - Ne PAS modifier BlockchainConfig, validateBlockchainConfig() ni la logique de validation existante

Note : Le wiring du BlockchainModule (ajout providers/exports PD-177) est differe a T-13.

Critere de completion : La config compile, les types NetworkConfig et BlockchainConfig sont compatibles.


T-04 — Enrichissement TransactionResult DTO

Champ Valeur
Agent agent-developer
Module blockchain-transaction-enrichment (DTO)
Phase plan Phase 1 (Fondations)
Depends_on (aucune)
Invariants INV-177-02, INV-177-13
Tests couverts TC-177-06, TC-177-08

Fichiers modifies : - src/modules/blockchain/dto/transaction.dto.ts

Instructions :

Ajouter le champ optionnel signerAddress dans l'interface TransactionResult et la classe TransactionResultDto, et le propager dans toTransactionResultDto().

  1. TransactionResultDto (classe, ligne 43-51) : ajouter apres timestamp? :

    signerAddress?: string;
    

  2. TransactionResult (interface, ligne 56-65) : ajouter apres timestamp? :

    signerAddress?: string;
    

  3. toTransactionResultDto() (ligne 70-92) : ajouter avant le return dto :

    dto.signerAddress = result.signerAddress;
    

Regles : - Le champ est optionnel (?) pour ne pas casser les retours PD-52/PD-55 existants - Ne PAS modifier NetworkType, GasMetrics, TransactionStatus, SendAnchorTransactionDto - Le format attendu de signerAddress est une adresse Ethereum checksumee (42 chars, 0x + 40 hex)

Critere de completion : Le fichier compile, les tests PD-52 existants qui utilisent TransactionResult ne cassent pas (champ optionnel).


T-05 — Migration et entity AnchorBatch

Champ Valeur
Agent agent-sre
Module anchor-entity-enrichment + database-migration-pd177
Phase plan Phase 2 (Schema et persistance)
Depends_on (aucune)
Invariants INV-177-13, INV-177-20
Tests couverts TC-177-06, TC-177-18

Fichiers modifies : - src/modules/anchor/entities/anchor-batch.entity.ts

Fichiers crees : - src/database/migrations/1740300000000-PD177-AddSignerAddress.ts

Instructions :

5a. Migration TypeORM

Creer la migration dans src/database/migrations/. Le timestamp 1740300000000 est indicatif — utiliser l'horodatage reel du moment de creation.

import { MigrationInterface, QueryRunner } from 'typeorm';

/**
 * PD-177: Ajout de la colonne signer_address a anchor_batches.
 * INV-177-13: signer_address dans le modele de persistance.
 * INV-177-20: Validation locale obligatoire avant integration.
 *
 * IMPORTANT: La colonne est NULLABLE pour compatibilite avec les batches PD-55 existants.
 * La garantie de non-nullite pour les nouveaux batches PD-177 est APPLICATIVE
 * (AnchorBatchService valide signerAddress != null avant save()).
 */
export class PD177AddSignerAddress1740300000000 implements MigrationInterface {
  public async up(queryRunner: QueryRunner): Promise<void> {
    await queryRunner.query(`
      ALTER TABLE vault_blockchain.anchor_batches
      ADD COLUMN signer_address VARCHAR(42) NULL
    `);
  }

  public async down(queryRunner: QueryRunner): Promise<void> {
    await queryRunner.query(`
      ALTER TABLE vault_blockchain.anchor_batches
      DROP COLUMN signer_address
    `);
  }
}

5b. Entity AnchorBatch

Ajouter la colonne signerAddress dans l'entity AnchorBatch (apres blockNumber, ligne 87) :

/**
 * Adresse Ethereum du wallet signataire de la transaction d'ancrage.
 * Format: 0x + 40 hex (42 chars, EIP-55 checksum).
 * Nullable pour compatibilite avec les batches PD-55 existants.
 * PD-177: INV-177-13 — signer_address dans le modele de persistance.
 */
@Column({ type: 'varchar', length: 42, name: 'signer_address', nullable: true })
signerAddress!: string | null;

Regles : - La colonne est NULLABLE (ne PAS utiliser NOT NULL — les batches PD-55 existants n'ont pas cette valeur) - Ne PAS utiliser d'index partiel PostgreSQL (lecon PD-55) - Ne PAS modifier les colonnes ou triggers existants - Ne PAS ajouter de triggers ni contraintes complexes dans cette migration - La migration doit etre reversible (down() fait DROP COLUMN) - Le nom de la classe de migration doit inclure PD177

Critere de completion : La migration s'execute sans erreur PostgreSQL. L'entity compile. Les batches existants conservent signer_address = NULL.


T-06 — CustodyModeGuard (gate S2)

Champ Valeur
Agent agent-developer
Module blockchain-custody-gate
Phase plan Phase 3 (Services core)
Depends_on T-01
Invariants INV-177-07
Tests couverts TC-177-02, TC-ERR-01

Fichiers crees : - src/modules/blockchain/custody/custody-mode.guard.ts

Instructions :

Creer un guard qui verifie que le mode custody configure est S2. Ce guard est appele lors de l'initialisation de WalletOperationalService (T-09), pas comme un NestJS Guard HTTP classique.

/**
 * CustodyModeGuard — PD-177
 * INV-177-07: Mode S2 uniquement en production PD-177
 *
 * Verifie que le mode custody configure est S2.
 * Throw BlockchainError(INVALID_CUSTODY_MODE) sinon.
 */

import { Injectable, Logger } from '@nestjs/common';
import { BlockchainError, BlockchainErrorCode } from '../constants/error-codes';

export type CustodyMode = 'S1' | 'S2' | 'S3';

@Injectable()
export class CustodyModeGuard {
  private readonly logger = new Logger(CustodyModeGuard.name);

  /**
   * Verifie que le mode custody est S2.
   * @throws BlockchainError(INVALID_CUSTODY_MODE) si mode != S2
   */
  assertS2Mode(configuredMode: CustodyMode): void {
    if (configuredMode !== 'S2') {
      this.logger.error(
        `Custody mode ${configuredMode} is not authorized for PD-177. Only S2 is allowed.`,
      );
      throw new BlockchainError(
        BlockchainErrorCode.INVALID_CUSTODY_MODE,
        `PD-177 requires custody mode S2, got: ${configuredMode}`,
        { configuredMode, requiredMode: 'S2' },
      );
    }
    this.logger.log('Custody mode S2 validated for PD-177');
  }
}

Regles : - Ne PAS modifier CustodyService existant (PD-52) - Ne PAS modifier CustodyStrategy interface (PD-52) - Ne PAS modifier les strategies S1/S2/S3 existantes - Utiliser BlockchainErrorCode.INVALID_CUSTODY_MODE (nouveau code de T-01) - Le guard ne doit PAS accepter de modes autres que S2

Critere de completion : Le guard compile, assertS2Mode('S2') ne throw pas, assertS2Mode('S1') et assertS2Mode('S3') throw BlockchainError avec code INVALID_CUSTODY_MODE.


T-07 — SecretLeakInterceptor

Champ Valeur
Agent agent-developer
Module blockchain-security
Phase plan Phase 3 (Services core)
Depends_on T-01
Invariants INV-177-08, INV-177-09
Tests couverts TC-SEC-01, TC-SEC-02, TC-ERR-08

Fichiers crees : - src/modules/blockchain/security/secret-leak.interceptor.ts

Instructions :

Creer un NestJS Interceptor qui scanne les reponses (via RxJS tap) pour des patterns de secrets. En cas de detection, fail-closed : bloque l'emission + throw BlockchainError(SECRET_LEAK_DETECTED).

Architecture du scan :

[Handler output] → tap(scanForSecrets) → [HTTP response]
                       |
                       ├── Pattern match? → throw BlockchainError(SECRET_LEAK_DETECTED)
                       └── No match? → pass through

Patterns a detecter (extension de BlockchainError.sanitizeContext()) : - Cle privee hex : /^0x[a-fA-F0-9]{64}$/ - Mnemonic (12-24 mots) : /^([a-z]+\s){11,23}[a-z]+$/ - Keywords sensibles : /mnemonic|seed|private.?key|secret|password/i (sur les cles d'objet, pas les valeurs entieres) - KMS key material base64 : /^[A-Za-z0-9+/]{43,}={0,2}$/ (base64 >= 32 bytes)

Comportement fail-closed : 1. Le scan se fait dans le pipe RxJS tap avant serialisation HTTP 2. Si pattern detecte dans la reponse : - throw new BlockchainError(BlockchainErrorCode.SECRET_LEAK_DETECTED, ...) - Le throw remplace la reponse originale (NestJS n'envoie pas le contenu sensible) 3. Le BlockchainError emis ne doit PAS contenir le secret detecte dans son context 4. Les erreurs propagees sont aussi scannees (via catchError)

Scan recursif : La methode detectSecretPattern(value) doit scanner recursivement les objets et tableaux.

Regles : - Ne PAS modifier BlockchainError.sanitizeContext() existant (PD-52) - Ne PAS desactiver l'interceptor via configuration - Ne PAS logger le secret detecte - Utiliser BlockchainErrorCode.SECRET_LEAK_DETECTED (code PD-52 existant, pas un nouveau code) - L'interceptor est @Injectable() pour injection dans le module

Critere de completion : L'interceptor compile. Une reponse contenant 0x + 64 hex est bloquee. Une reponse normale passe sans modification.


T-08 — Enrichissement ConfirmationTracker et TransactionService

Champ Valeur
Agent agent-developer
Module blockchain-transaction-enrichment (services)
Phase plan Phase 3 (Services core)
Depends_on T-01, T-02, T-04
Invariants INV-177-02, INV-177-13, INV-177-16
Tests couverts TC-177-05, TC-177-08, TC-177-13, TC-ERR-06

Fichiers modifies : - src/modules/blockchain/transaction/confirmation.tracker.ts - src/modules/blockchain/transaction/transaction.service.ts

Instructions :

8a. ConfirmationTracker

Probleme actuel : Le tracker utilise MAX_POLLING_ATTEMPTS = 100 avec POLLING_INTERVAL_MS = 3000, soit un timeout effectif de 300s. La spec PD-177 exige 900s.

Modification : Remplacer le calcul statique MAX_POLLING_ATTEMPTS par un calcul dynamique base sur NetworkConfirmationPolicy.

  1. Importer getConfirmationPolicy depuis ../constants/network-confirmation-policy
  2. Dans track(), remplacer la boucle for (let attempts = 0; attempts < MAX_POLLING_ATTEMPTS; ...) par un calcul dynamique :
async track(network: NetworkType, txHash: string): Promise<TransactionResult> {
  const policy = getConfirmationPolicy(network);
  const requiredConfirmations = policy.confirmations;
  const maxPollingAttempts = Math.ceil(policy.timeoutMs / POLLING_INTERVAL_MS);

  this.logger.log(
    `Tracking transaction ${txHash} on ${network}, ` +
    `requiring ${requiredConfirmations} confirmations (timeout: ${policy.timeoutMs}ms)`,
  );

  const state = { lastSeenConfirmations: 0, reorgDetectedAt: null as Date | null };

  for (let attempts = 0; attempts < maxPollingAttempts; attempts++) {
    const pollResult = await this.pollTransaction(network, txHash, state, requiredConfirmations);
    if (pollResult) {
      return pollResult;
    }
    await this.sleep(POLLING_INTERVAL_MS);
  }

  return this.createPendingResult(txHash, network, state.lastSeenConfirmations);
}
  1. Dans getRequiredConfirmations(), utiliser la politique PD-177 :
private getRequiredConfirmations(network: NetworkType): number {
  return getConfirmationPolicy(network).confirmations;
}
  1. Supprimer la constante MAX_POLLING_ATTEMPTS (plus utilisee).

  2. Ajouter le code d'erreur TRANSACTION_REORGED_OR_ABANDONED dans checkAbandonedTransaction() — utiliser le nouveau code a la place de TRANSACTION_ABANDONED quand le reorg est suivi d'un abandon :

throw new BlockchainError(
  BlockchainErrorCode.TRANSACTION_REORGED_OR_ABANDONED,
  `Transaction ${txHash} abandoned after reorg (...)`,
  { txHash, network, elapsedSinceReorg },
);

Note : Conserver aussi TRANSACTION_ABANDONED pour le cas d'abandon sans reorg (si applicable). Le code TRANSACTION_REORGED_OR_ABANDONED est specifique au cas reorg+abandon.

8b. TransactionService

Lire le fichier transaction.service.ts existant. Ajouter signerAddress dans le TransactionResult retourne par sendAnchorTransaction().

Le signerAddress doit etre obtenu via this.walletService.getAddress() (appele lors de la construction de la transaction, avant signature).

Ajouter dans le retour de sendAnchorTransaction() :

signerAddress: walletAddress,  // Adresse du wallet actif au moment de l'emission

Regles : - Ne PAS modifier la signature de track() de maniere non-backward-compatible (meme parametres) - Ne PAS hardcoder de seuils de confirmation (utiliser NetworkConfirmationPolicy) - Ne PAS supprimer la detection de reorg existante - Conserver POLLING_INTERVAL_MS = 3000 et REORG_ABANDON_TIMEOUT_MS = 60000

Critere de completion : Le tracker compile. Le timeout effectif pour Polygon est 900s (300 iterations * 3s). Le timeout pour Arbitrum est 900s egalement. sendAnchorTransaction() retourne signerAddress.


T-09 — WalletOperationalService, AnchorExclusivityGuard, WalletRecoveryService

Champ Valeur
Agent agent-developer
Module blockchain-wallet-operational
Phase plan Phase 3 + Phase 5
Depends_on T-01, T-06, T-07
Invariants INV-177-01, INV-177-02, INV-177-03, INV-177-05, INV-177-06, INV-177-11, INV-177-18, INV-177-19
Tests couverts TC-177-01, TC-177-08, TC-177-09, TC-177-14, TC-177-15, TC-177-16

Fichiers crees : - src/modules/blockchain/wallet/wallet-operational.service.ts - src/modules/blockchain/wallet/anchor-exclusivity.guard.ts - src/modules/blockchain/wallet/wallet-recovery.service.ts

Instructions :

9a. WalletOperationalService

Facade PD-177 pour le wallet. Garantit unicite, restriction ancrage-only, delegation custody, journalisation.

/**
 * WalletOperationalService — PD-177
 * Facade operationnelle du wallet Ethereum pour l'ancrage probatoire.
 *
 * INV-177-01: Un seul wallet actif
 * INV-177-02: Transaction associee au wallet actif
 * INV-177-05: Interface CustodyStrategy respectee
 * INV-177-11: crypto.randomUUID() pour identifiants PD-177
 */

Responsabilites : - initialize() : appelle CustodyModeGuard.assertS2Mode(), puis CustodyService.getStrategy().initialize(), puis getAddress() pour valider le wallet - getOfficialAddress() : delegue a CustodyService.getStrategy().getAddress() - isOperational() : retourne true si le wallet est initialise et l'adresse resolue - getOperationalState() : retourne { address, mode, operational, initializedAt }

Tous les identifiants generes doivent utiliser crypto.randomUUID().

9b. AnchorExclusivityGuard

Garantit que seul le flux d'ancrage peut emettre des transactions.

/**
 * AnchorExclusivityGuard — PD-177
 * INV-177-03: Wallet exclusivement ancrage
 *
 * Verifie que l'appelant est le flux d'ancrage probatoire.
 * Rejette toute tentative non-ancrage.
 */
  • assertAnchorContext(caller: string) : verifie que caller est dans une whitelist de callers autorises ('BlockchainAnchorProcessor', 'BlockchainAdapterService')
  • Si le caller n'est pas autorise, throw BlockchainError(INVALID_CUSTODY_MODE) avec message explicite

9c. WalletRecoveryService

Procedure de reprise documentee et testable.

/**
 * WalletRecoveryService — PD-177
 * INV-177-18: Procedure de reprise testee
 * INV-177-19: Rotation preserve auditabilite
 */
  • executeRecoveryExercise(scenarioId: string) : execute la procedure de reprise en simulation, produit un RecoveryReport avec timestamp ISO 8601 UTC ms, statut (success/failure), etapes executees
  • getRecoveryProcedure() : retourne les metadonnees de la procedure (version, derniere mise a jour, nombre d'exercices)

Le RecoveryReport doit contenir au minimum :

interface RecoveryReport {
  exerciseId: string;       // crypto.randomUUID()
  scenarioId: string;
  startedAt: string;        // ISO 8601 UTC ms
  completedAt: string;      // ISO 8601 UTC ms
  status: 'success' | 'failure';
  steps: RecoveryStep[];
  auditTrail: string;       // Reference audit log entry
}

Regles : - Ne PAS modifier WalletService existant (PD-52) au-dela de l'ajout de signerAddress dans le retour - Ne PAS modifier CustodyStrategy interface - Ne PAS exposer sendRawTransaction ou methode de transaction generique - Generer les UUID UNIQUEMENT via crypto.randomUUID() - Ne PAS stocker de cle privee en memoire applicative

Critere de completion : Les 3 fichiers compilent. WalletOperationalService.initialize() appelle le guard S2. AnchorExclusivityGuard rejette les callers non autorises. WalletRecoveryService produit un rapport horodate.


T-10 — Enrichissement adapter, batch service, processor

Champ Valeur
Agent agent-developer
Module anchor-service-enrichment
Phase plan Phase 4 (Integration ancrage)
Depends_on T-01, T-02, T-04, T-05, T-08
Invariants INV-177-06, INV-177-13, INV-177-14, INV-177-16, INV-177-21
Tests couverts TC-177-04, TC-177-06, TC-177-10, TC-177-11, TC-177-13, TC-177-19

Fichiers modifies : - src/modules/anchor/services/blockchain-adapter.service.ts - src/modules/anchor/services/anchor-batch.service.ts - src/modules/anchor/processors/blockchain-anchor.processor.ts

Instructions :

10a. BlockchainAdapterService

Probleme actuel : 1. waitForConfirmation() hardcode network = 'polygon' (ligne 108) 2. submitMerkleRoot() ne retourne pas signerAddress

Modifications :

  1. Dans submitMerkleRoot() : ajouter signerAddress dans le retour (obtenu via this.walletService.getAddress() deja appele ligne 60) :
    return {
      hash: response.hash,
      network,
      blockNumber: undefined,
      confirmations: 0,
      signerAddress: walletAddress,  // PD-177: INV-177-02
    };
    

Note : Le TransactionResult importe vient du processor (blockchain-anchor.processor.ts), pas du DTO. Il faut aussi ajouter signerAddress?: string dans l'interface TransactionResult du processor (voir 10c).

  1. Dans waitForConfirmation() : ajouter un parametre optionnel network et supprimer le hardcode :
    async waitForConfirmation(
      txHash: string,
      confirmations: number,
      network: NetworkType = 'polygon',  // PD-177: default conserve pour backward-compat
    ): Promise<void> {
      this.logger.log(`Waiting for ${confirmations} confirmations on ${txHash} (${network})`);
      const result = await this.confirmationTracker.track(network, txHash);
      // ... reste inchange ...
    }
    

10b. AnchorBatchService

Modifications :

  1. Dans submitBatch() : ajouter le parametre signerAddress et l'ecrire dans le batch :

    async submitBatch(batchId: string, txHash: string, signerAddress?: string): Promise<void> {
      // ... findOne existant ...
    
      // PD-177: Validation signerAddress pour les nouveaux batches
      // INV-177-13: garantie applicative de non-nullite
      batch.status = AnchorBatchStatus.SUBMITTED;
      batch.txId = txHash;
      batch.submittedAt = new Date();
      if (signerAddress) {
        batch.signerAddress = signerAddress;
      }
    
      await this.batchRepository.save(batch);
      // ... audit existant ...
    }
    

  2. Ajouter une protection append-only applicative pour les batches FINALIZED. Dans toute methode qui modifie un batch, avant la modification, verifier :

    // PD-177: Protection append-only applicative (INV-177-14)
    if (batch.status === AnchorBatchStatus.FINALIZED) {
      throw new ConflictException(
        `Cannot modify batch ${batchId}: batch is FINALIZED (append-only protection)`,
      );
    }
    

Note : Cette protection est en plus du trigger DB PD-55. Elle n'est necessaire que dans les methodes submitBatch et confirmBatch (qui ne devraient pas etre appelees sur un batch FINALIZED de toute facon, mais defense en profondeur).

  1. Dans exportProofArtifact() : ajouter signer_address dans le log d'audit et le champ blockchain dans l'artefact (prepare pour T-11).

  2. Dans finalizeBatch() : ajouter signerAddress dans l'audit event :

    await this.auditService.logEvent('ANCHOR_BATCH_FINALIZED', SYSTEM_USER_ID, batchId, {
      // ... existant ...
      signerAddress: batch.signerAddress,  // PD-177
    });
    

10c. BlockchainAnchorProcessor

Probleme actuel : 1. CONFIRMATION_BLOCKS = 12 hardcode (ligne 66) 2. L'interface locale TransactionResult ne contient pas signerAddress 3. executeConfirmPhase() utilise le hardcode

Modifications :

  1. Ajouter signerAddress?: string dans l'interface locale TransactionResult (ligne 48-53) :

    export interface TransactionResult {
      hash: string;
      blockNumber?: number;
      network?: string;
      confirmations?: number;
      signerAddress?: string;  // PD-177: INV-177-02
    }
    

  2. Importer getConfirmationPolicy et NetworkType :

    import { getConfirmationPolicy } from '../../blockchain/constants/network-confirmation-policy';
    import { NetworkType } from '../../blockchain/dto/transaction.dto';
    

  3. Remplacer CONFIRMATION_BLOCKS = 12 par un calcul dynamique dans executeConfirmPhase() :

    private async executeConfirmPhase(
      job: Job<AnchorJobData>,
      batchId: string,
      txResult: TransactionResult,
    ): Promise<void> {
      const network = (txResult.network ?? 'polygon') as NetworkType;
      const policy = getConfirmationPolicy(network);
    
      this.logger.log(
        `[Job ${job.id}] Phase 3: Attente ${policy.confirmations} confirmations (${network}, timeout: ${policy.timeoutMs}ms)`,
      );
      await job.updateProgress(75);
    
      try {
        await this.blockchainService.waitForConfirmation(
          txResult.hash,
          policy.confirmations,
          network,  // PD-177: propager le network
        );
        // ... reste inchange ...
      }
    }
    

  4. Mettre a jour BlockchainServiceInterface pour propager network :

    export interface BlockchainServiceInterface {
      submitMerkleRoot(merkleRoot: string, chainId: number): Promise<TransactionResult>;
      waitForConfirmation(txHash: string, confirmations: number, network?: NetworkType): Promise<void>;
    }
    

  5. Dans executeAnchorPhase() : propager signerAddress dans submitBatch() :

    await this.anchorBatchService.submitBatch(
      batch.batchId,
      txResult.hash,
      txResult.signerAddress,  // PD-177: INV-177-13
    );
    

  6. Supprimer la constante locale CONFIRMATION_BLOCKS = 12 (remplacee par la politique dynamique).

Regles : - Ne PAS utiliser getRepeatableJobs ou removeRepeatableByKey (APIs BullMQ depreciees) - Ne PAS modifier le mecanisme d'atomicite PD-55 (advisory lock, transaction englobante) - Ne PAS modifier AnchorJobData/AnchorJobResult de maniere non-backward-compatible - Ne PAS creer d'entrees orphelines dans anchor_batches (pas de tx_hash) - Le processor conserve concurrency=1

Critere de completion : Les 3 fichiers compilent. Les confirmations sont dynamiques par reseau. signerAddress est propage dans tout le flow. L'interface BlockchainServiceInterface supporte le parametre network optionnel.


T-11 — AnchorProofValidator et enrichissement ProofArtifactDto

Champ Valeur
Agent agent-developer
Module anchor-proof-validation
Phase plan Phase 4 (Integration ancrage)
Depends_on T-01, T-05
Invariants INV-177-04, INV-177-15
Tests couverts TC-177-07, TC-177-12, TC-ERR-07

Fichiers crees : - src/modules/anchor/validators/anchor-proof.validator.ts

Fichiers modifies : - src/modules/anchor/dto/proof-artifact.dto.ts

Instructions :

11a. ProofArtifactDto

Ajouter le champ optionnel blockchain dans ProofArtifactDto. Apres block_number (ligne 154) :

/**
 * Type de blockchain pour la preuve multi-chain.
 * PD-177: INV-177-04 — ethereum_l2 pour les preuves PD-177.
 * PD-245: Format preuve multi-chain.
 * Optionnel pour backward-compat avec les artefacts PD-55 existants.
 */
@IsOptional()
@IsString({ message: 'blockchain doit etre une chaine de caracteres' })
@IsIn(['ethereum_l2', 'tezos'], { message: 'blockchain doit etre ethereum_l2 ou tezos' })
blockchain?: 'ethereum_l2' | 'tezos';

Ajouter les imports necessaires : IsOptional, IsIn depuis class-validator.

11b. AnchorProofValidator

Creer un validateur qui verifie le lien complet merkle_root <-> tx_hash <-> confirmation.

/**
 * AnchorProofValidator — PD-177
 * INV-177-15: Reconstruction tierce de la chaine de preuve
 *
 * Valide le lien complet merkle_root <-> tx_hash <-> confirmation.
 * Throw BlockchainError(PROOF_LINK_INCOMPLETE) si incomplet.
 */

import { Injectable, Logger } from '@nestjs/common';
import { BlockchainError, BlockchainErrorCode } from '../../blockchain/constants/error-codes';
import { AnchorBatch } from '../entities/anchor-batch.entity';
import { AnchorBatchStatus } from '../enums/anchor-batch-status.enum';

@Injectable()
export class AnchorProofValidator {
  private readonly logger = new Logger(AnchorProofValidator.name);

  /**
   * Valide la chaine de preuve complete pour un batch.
   * @throws BlockchainError(PROOF_LINK_INCOMPLETE) si un maillon manque
   */
  validateProofChain(batch: AnchorBatch): void {
    const missing: string[] = [];

    if (!batch.merkleRoot) missing.push('merkleRoot');
    if (!batch.txId) missing.push('txId');
    if (batch.blockNumber === null || batch.blockNumber === undefined) missing.push('blockNumber');
    if (!batch.signerAddress) missing.push('signerAddress');
    if (batch.status !== AnchorBatchStatus.FINALIZED) missing.push('status=FINALIZED');
    if (!batch.submittedAt) missing.push('submittedAt');
    if (!batch.finalizedAt) missing.push('finalizedAt');

    if (missing.length > 0) {
      throw new BlockchainError(
        BlockchainErrorCode.PROOF_LINK_INCOMPLETE,
        `Proof chain is incomplete for batch ${batch.batchId}: missing ${missing.join(', ')}`,
        { batchId: batch.batchId, missingFields: missing },
      );
    }

    this.logger.debug(`Proof chain validated for batch ${batch.batchId}`);
  }

  /**
   * Verifie si la chaine de preuve est complete (sans throw).
   */
  isProofComplete(batch: AnchorBatch): boolean {
    try {
      this.validateProofChain(batch);
      return true;
    } catch {
      return false;
    }
  }
}

Regles : - Ne PAS modifier les validations existantes de ProofArtifactDto (PD-55) - Ne PAS rendre le champ blockchain obligatoire (casse les artefacts existants) - Ne PAS accepter blockchain='tezos' comme valide pour la generation de preuves PD-177 (seul ethereum_l2 est produit) - Utiliser BlockchainErrorCode.PROOF_LINK_INCOMPLETE (nouveau code de T-01)

Critere de completion : Le validateur compile. Un batch FINALIZED avec tous les champs passe. Un batch sans signerAddress ou sans txId echoue avec PROOF_LINK_INCOMPLETE. Le DTO compile avec le champ blockchain optionnel.


T-12 — Enrichissement anchor.constants.ts

Champ Valeur
Agent agent-developer
Module anchor-constants-enrichment
Phase plan Phase 4 (Integration ancrage)
Depends_on T-02
Invariants INV-177-16
Tests couverts TC-177-19

Fichiers modifies : - src/modules/anchor/constants/anchor.constants.ts

Instructions :

Ajouter un re-export de NetworkConfirmationPolicy pour usage par le processor, sans modifier les constantes PD-55 existantes.

Ajouter a la fin du fichier :

// ========== PD-177: Network Confirmation Policy ==========

/**
 * Re-export de la politique de confirmation par reseau (PD-177).
 * INV-177-16: Confirmation par reseau contractualisee.
 *
 * Usage: les composants anchor/ importent depuis ce fichier
 * plutot que d'acceder directement au module blockchain/constants/.
 */
export {
  NETWORK_CONFIRMATION_POLICY,
  getConfirmationPolicy,
  type ConfirmationPolicy,
} from '../../blockchain/constants/network-confirmation-policy';

Regles : - Ne PAS modifier les constantes PD-55 existantes (ANCHOR_CONFIRMATION_BLOCKS, ANCHOR_JOB_OPTIONS, ANCHOR_QUEUE_NAME, etc.) - C'est un re-export uniquement, pas une redefinition

Critere de completion : Le fichier compile. L'import import { getConfirmationPolicy } from '../constants/anchor.constants' fonctionne depuis le module anchor.


T-13 — Wiring BlockchainModule final

Champ Valeur
Agent agent-developer
Module blockchain-config-enrichment (wiring)
Phase plan Phase 6 (Wiring module)
Depends_on T-03, T-06, T-07, T-08, T-09, T-11, T-12
Invariants INV-177-16
Tests couverts (transversal)

Fichiers modifies : - src/modules/blockchain/blockchain.module.ts

Instructions :

Ajouter les nouveaux providers et exports PD-177 dans le module NestJS.

  1. Imports en tete de fichier :

    // PD-177: Custody Mode Guard
    import { CustodyModeGuard } from './custody/custody-mode.guard';
    
    // PD-177: Security
    import { SecretLeakInterceptor } from './security/secret-leak.interceptor';
    
    // PD-177: Wallet Operational
    import { WalletOperationalService } from './wallet/wallet-operational.service';
    import { AnchorExclusivityGuard } from './wallet/anchor-exclusivity.guard';
    import { WalletRecoveryService } from './wallet/wallet-recovery.service';
    

  2. Ajouter dans providers: (apres les commentaires PD-52 existants) :

    // PD-177: Custody Mode Gate (T-06)
    CustodyModeGuard,
    
    // PD-177: Security (T-07)
    SecretLeakInterceptor,
    
    // PD-177: Wallet Operational (T-09)
    WalletOperationalService,
    AnchorExclusivityGuard,
    WalletRecoveryService,
    

  3. Ajouter dans exports: :

    // PD-177 exports
    CustodyService,              // Expose pour usage par le guard PD-177
    CustodyModeGuard,
    SecretLeakInterceptor,
    WalletOperationalService,
    AnchorExclusivityGuard,
    WalletRecoveryService,
    RpcProviderService,          // Necessaire pour BlockchainAdapterService
    GasEstimatorService,         // Necessaire pour BlockchainAdapterService
    ConfirmationTracker,         // Necessaire pour BlockchainAdapterService
    CustodyService,              // Necessaire pour BlockchainAdapterService
    

Regles : - Ne PAS supprimer de providers ou exports existants - Ne PAS modifier l'ordre des imports PD-52 - Les ajouts sont tous additifs

Critere de completion : Le module compile. Tous les nouveaux services sont injectables depuis les modules qui importent BlockchainModule.


T-14 — Documentation operationnelle

Champ Valeur
Agent agent-sre
Module operational-documentation
Phase plan Phase 5 (Continuite et resilience)
Depends_on (aucune)
Invariants INV-177-18, INV-177-19
Tests couverts TC-177-15

Fichiers crees : - docs/runbooks/wallet-recovery-procedure.md - docs/runbooks/wallet-rotation-log-template.md

Instructions :

14a. Procedure de reprise wallet

Creer docs/runbooks/wallet-recovery-procedure.md :

Le document doit couvrir : 1. Objectif : Procedure de reprise du wallet Ethereum ProbatioVault apres incident custody 2. Prerequis : Acces AWS console, IAM role avec KMS access, identifiants S2 de secours 3. Scenarios couverts : - Indisponibilite temporaire du service KMS - Rotation d'urgence de la cle KMS - Compromission suspectee du serveur applicatif 4. Etapes de reprise (numerotees, avec responsable et verification) : - Isolation du service (arret du worker d'ancrage) - Diagnostic (verification de l'etat KMS, logs, derniere transaction) - Decision (reprise en l'etat OU rotation) - Execution (rotation si necessaire, avec preservation d'auditabilite) - Verification (test de signature, verification des transactions historiques) - Retour en service (redemarrage du worker, monitoring) 5. Post-mortem : Template de rapport d'exercice avec timestamp, statut, etapes executees 6. Historique : Tableau des exercices de reprise (date, scenario, resultat, responsable)

14b. Template rotation

Creer docs/runbooks/wallet-rotation-log-template.md :

Template de consignation de rotation de cle avec : - Date et heure de rotation (ISO 8601 UTC ms) - Ancienne adresse wallet - Nouvelle adresse wallet - Raison de la rotation (planifiee / incident) - Dernier batch signe avec l'ancienne cle (batchId, txHash) - Premier batch signe avec la nouvelle cle (batchId, txHash) - Verification de continuite d'auditabilite (oui/non + preuve) - Responsable de la rotation - Signature du responsable

Regles : - Ne PAS inclure de secrets (pas de cles, pas d'ARN KMS reels, pas de credentials) - Les exemples doivent utiliser des valeurs fictives clairement identifiees - Le document doit etre versionne (header avec version et date)

Critere de completion : Les 2 fichiers existent. La procedure est executee de bout en bout (simulable). Le template de rotation contient tous les champs requis.


T-15 — Tests PD-177

Champ Valeur
Agent agent-qa-unit-integration
Module tests-pd177
Phase plan Phase finale
Depends_on T-01..T-13
Invariants INV-177-11, INV-177-12, INV-177-21
Tests couverts TC-177-01..TC-177-19, TC-SEC-01..TC-SEC-03, TC-ERR-01..TC-ERR-08

Fichiers crees : - src/modules/blockchain/__tests__/pd177/custody-mode-guard.spec.ts - src/modules/blockchain/__tests__/pd177/secret-leak-interceptor.spec.ts - src/modules/blockchain/__tests__/pd177/wallet-operational.service.spec.ts - src/modules/blockchain/__tests__/pd177/anchor-exclusivity-guard.spec.ts - src/modules/blockchain/__tests__/pd177/confirmation-tracker-pd177.spec.ts - src/modules/blockchain/__tests__/pd177/network-confirmation-policy.spec.ts - src/modules/blockchain/__tests__/pd177/wallet-recovery.service.spec.ts - src/modules/anchor/__tests__/pd177/anchor-proof-validator.spec.ts - src/modules/anchor/__tests__/pd177/blockchain-adapter-pd177.spec.ts - src/modules/anchor/__tests__/pd177/anchor-batch-pd177.spec.ts - src/modules/anchor/__tests__/pd177/blockchain-anchor-processor-pd177.spec.ts - src/modules/anchor/__tests__/pd177/proof-artifact-pd177.spec.ts - src/modules/anchor/__tests__/pd177/error-codes-pd177.spec.ts - src/modules/blockchain/__tests__/pd177/static-analysis.spec.ts

Instructions :

Couverture obligatoire (30 tests minimum)

Chaque test doit porter l'ID TC-177-XX correspondant dans sa description.

Tests unitaires (blockchain/) :

Fichier Tests IDs TC
custody-mode-guard.spec.ts S2 passe, S1 echoue, S3 echoue TC-177-02, TC-ERR-01
secret-leak-interceptor.spec.ts Reponse OK passe, private key bloquee, mnemonic bloquee, fail-closed, incident cree TC-SEC-01, TC-SEC-02, TC-ERR-08
wallet-operational.service.spec.ts Unicite adresse, initialisation S2, delegation custody, isOperational TC-177-01, TC-177-03, TC-177-08
anchor-exclusivity-guard.spec.ts Caller ancrage OK, caller non-ancrage rejete TC-177-09
confirmation-tracker-pd177.spec.ts Polygon 12 confirmations, Arbitrum 30 confirmations, timeout 900s, reorg+abandon TC-177-05, TC-177-13, TC-ERR-06
network-confirmation-policy.spec.ts Valeurs polygon, valeurs arbitrum, reseau inconnu TC-177-05
wallet-recovery.service.spec.ts Exercice reussi, rapport horodate TC-177-15

Tests unitaires (anchor/) :

Fichier Tests IDs TC
anchor-proof-validator.spec.ts Preuve complete OK, manque signerAddress, manque txId, preuve incomplete TC-177-07, TC-177-12, TC-ERR-07
blockchain-adapter-pd177.spec.ts signerAddress dans retour, network propage, tezos passthrough TC-177-04, TC-177-10
anchor-batch-pd177.spec.ts submitBatch avec signerAddress, protection append-only FINALIZED, champ blockchain dans export TC-177-06, TC-177-11
blockchain-anchor-processor-pd177.spec.ts Confirmations dynamiques, signerAddress propage, delegation signature TC-177-10, TC-177-16, TC-177-19
proof-artifact-pd177.spec.ts Champ blockchain present, ethereum_l2 valide, tezos ignore TC-177-07
error-codes-pd177.spec.ts 4 nouveaux codes existent, codes PD-52 inchanges TC-ERR-01..08

Tests d'analyse statique :

Fichier Tests IDs TC
static-analysis.spec.ts Grep crypto.randomUUID dans fichiers PD-177, grep APIs BullMQ depreciees, grep prefixe pv-test-* TC-177-16, TC-177-17, TC-177-19

Conventions de test

  • Framework : Jest (existant)
  • Nommage : describe('TC-177-XX - <titre>', () => { ... })
  • Fixtures : Prefixe pv-test-* pour toutes les cles ephemeres
  • UUID : Utiliser crypto.randomUUID() pour les identifiants de test
  • Timestamps : Utiliser Date.now() pour les timestamps dynamiques (pas de collisions)
  • Spread : Copier les objets fixture avec spread operator, jamais muter les references partagees
  • Mocks : jest.fn() pour les services, mockQueryRunner pour TypeORM
  • Coverage : >= 85% sur les fichiers PD-177

Regles : - Ne PAS modifier les tests PD-52 et PD-55 existants - Ne PAS utiliser uuid.v4() ou Math.random() pour les identifiants - Ne PAS hardcoder de seuils de confirmation dans les tests (utiliser les constantes) - Verifier que les tests PD-52/PD-55 existants passent toujours (non-regression)

Critere de completion : Tous les tests passent. Coverage >= 85% sur les fichiers PD-177. Les tests PD-52/PD-55 existants passent (non-regression). Les 30 IDs TC sont couverts.


4. Resume d'execution

Metrique Valeur
Nombre total de taches 15
Taches agent-developer 12 (T-01, T-02, T-03, T-04, T-06, T-07, T-08, T-09, T-10, T-11, T-12, T-13)
Taches agent-sre 2 (T-05, T-14)
Taches agent-qa-unit-integration 1 (T-15)
Niveaux de parallelisation 5 (L0 → L4)
Taches paralleles max (L0) 5 (T-01, T-02, T-04, T-05, T-14)
Taches paralleles L1 5 (T-03, T-06, T-07, T-08, T-12)
Taches paralleles L2 3 (T-09, T-10, T-11)
Fichiers modifies 12
Fichiers crees 12
Invariants couverts 21/21
Criteres d'acceptation couverts 17/17
Cas d'erreur couverts 8/8
Tests de securite couverts 3/3

Chemin critique

T-01 → T-08 → T-10 → T-13 → T-15

Le chemin critique traverse les error codes (T-01), l'enrichissement tracker+transaction (T-08), l'integration ancrage (T-10), le wiring module (T-13), et les tests (T-15).

Ordre d'execution recommande

Iteration 1 (parallele): T-01, T-02, T-04, T-05, T-14
Iteration 2 (parallele): T-03, T-06, T-07, T-08, T-12
Iteration 3 (parallele): T-09, T-10, T-11
Iteration 4 (sequentiel): T-13
Iteration 5 (sequentiel): T-15

Frontiere de non-regression

Les tests PD-52 et PD-55 existants doivent passer apres chaque iteration. Les modifications PD-177 sont toutes additives : - Nouveaux codes dans l'enum (pas de renames) - Colonnes nullable (pas de NOT NULL) - Champs optionnels dans les DTOs - Nouveaux providers/exports dans le module (pas de suppressions)