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.
- Interface
NetworkConfig(ligne 7-12) : ajouterconfirmationTimeoutMs: number
export interface NetworkConfig {
chainId: number;
rpcPrimary: string;
rpcSecondary: string;
confirmations: number;
confirmationTimeoutMs: number; // PD-177: Timeout de confirmation par reseau
}
- Factory config : ajouter
confirmationTimeoutMsdans les objetspolygonetarbitrum:
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().
-
TransactionResultDto(classe, ligne 43-51) : ajouter aprestimestamp?: -
TransactionResult(interface, ligne 56-65) : ajouter aprestimestamp?: -
toTransactionResultDto()(ligne 70-92) : ajouter avant lereturn dto:
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.
- Importer
getConfirmationPolicydepuis../constants/network-confirmation-policy - Dans
track(), remplacer la bouclefor (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);
}
- Dans
getRequiredConfirmations(), utiliser la politique PD-177 :
private getRequiredConfirmations(network: NetworkType): number {
return getConfirmationPolicy(network).confirmations;
}
-
Supprimer la constante
MAX_POLLING_ATTEMPTS(plus utilisee). -
Ajouter le code d'erreur
TRANSACTION_REORGED_OR_ABANDONEDdanscheckAbandonedTransaction()— utiliser le nouveau code a la place deTRANSACTION_ABANDONEDquand 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() :
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 quecallerest 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 unRecoveryReportavec timestamp ISO 8601 UTC ms, statut (success/failure), etapes executeesgetRecoveryProcedure(): 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 :
- Dans
submitMerkleRoot(): ajoutersignerAddressdans le retour (obtenu viathis.walletService.getAddress()deja appele ligne 60) :
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).
- Dans
waitForConfirmation(): ajouter un parametre optionnelnetworket 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 :
-
Dans
submitBatch(): ajouter le parametresignerAddresset 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 ... } -
Ajouter une protection append-only applicative pour les batches FINALIZED. Dans toute methode qui modifie un batch, avant la modification, verifier :
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).
-
Dans
exportProofArtifact(): ajoutersigner_addressdans le log d'audit et le champblockchaindans l'artefact (prepare pour T-11). -
Dans
finalizeBatch(): ajoutersignerAddressdans l'audit event :
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 :
-
Ajouter
signerAddress?: stringdans l'interface localeTransactionResult(ligne 48-53) : -
Importer
getConfirmationPolicyetNetworkType: -
Remplacer
CONFIRMATION_BLOCKS = 12par un calcul dynamique dansexecuteConfirmPhase():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 ... } } -
Mettre a jour
BlockchainServiceInterfacepour propagernetwork: -
Dans
executeAnchorPhase(): propagersignerAddressdanssubmitBatch(): -
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.
-
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'; -
Ajouter dans
providers:(apres les commentaires PD-52 existants) : -
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,mockQueryRunnerpour 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¶
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)