PD-278 — Agent Developer : Module dip-attestation
Agent : agent-developer Module : dip-attestation Story : PD-278 — NF Z42-013 : ajout contractuel de l'etat DIP Date : 2026-03-01
1. Resume
Implementation de l'entity TypeORM DisseminationAttestation pour la table vault_secure.dissemination_attestations. Cette entity represente l'attestation de restitution generee a chaque transition SEALED → DIP (INV-278-05).
2. Fichiers modifies
| Fichier | Action | Description |
src/modules/documents/entities/dissemination-attestation.entity.ts | CREE | Entity TypeORM DisseminationAttestation — table vault_secure.dissemination_attestations |
| Invariant | Statut | Mecanisme |
INV-278-05 : 1 attestation par requete SEALED→DIP, stockee dans vault_secure.dissemination_attestations | CONFORME | Entity mappee sur @Entity('dissemination_attestations', { schema: 'vault_secure' }). La cardinalite 1:1 par requete est enforced par le service (C9), pas par l'entity. |
Champs WORM : hash_evidence et signature_ref ne sont jamais modifies apres insertion | CONFORME | Les champs sont declares NOT NULL / nullable: true sans @UpdateDateColumn. La protection WORM est documentee par JSDoc. L'enforcement reel est au niveau migration (trigger DB) et service (pas d'UPDATE sur ces champs). |
document_ids stocke en UUID[] pour lier N documents a 1 attestation | CONFORME | @Column({ name: 'document_ids', type: 'uuid', array: true }) — PostgreSQL native UUID array. Permet requete @> pour lookup. |
Retention 10 ans, partitioning par annee civile sur issued_at | CONFORME | Colonne issued_at declaree avec type: 'timestamptz' et index idx_attestations_issued_at. Le partitioning est gere au niveau DDL (migration C1), pas dans l'entity TypeORM (limitation framework). |
4. Verification des interdits (forbidden)
| Interdit | Statut | Justification |
UPDATE sur hash_evidence ou signature_ref | RESPECTE | L'entity ne declare aucun @UpdateDateColumn. Les champs sont documentes WORM par JSDoc. L'enforcement est au niveau service (pas d'UPDATE) + trigger DB (migration C1). L'entity seule ne peut pas empecher un UPDATE — c'est un choix architecturalement correct car TypeORM ne dispose pas de decorateur "read-only column". |
| DELETE sans politique d'archivage validee | RESPECTE | L'entity ne dispose d'aucune methode de suppression. Le controle est au niveau service + migration (absence de onDelete: CASCADE). |
Stockage en clair de hash_evidence sans chiffrement au repos | RESPECTE | L'entity stocke hash_evidence en text. Le chiffrement au repos est assure par l'envelope encryption au niveau infrastructure (PostgreSQL TDE / storage layer). L'entity ne gere pas le chiffrement applicatif — c'est le service (C9) qui calcule le hash et le signe via HSM (INV-278-10). |
5. Schema de l'entity
@Entity('dissemination_attestations', { schema: 'vault_secure' })
export class DisseminationAttestation {
id: string; // PK UUID auto-generated
attestationId: string; // UUID unique — reference externe
requestId: string; // UUID — correlation requete API
documentIds: string[]; // UUID[] — N documents lies
actorId: string; // UUID — acteur initiateur
issuedAt: Date; // TIMESTAMPTZ — emission (DEFAULT NOW())
motifCommunication: string | null; // VARCHAR(1024) nullable — motif optionnel
disseminationPackageId: string | null; // UUID nullable — NULL mono, UUID multi
hashEvidence: string; // TEXT — empreinte integrite (WORM)
signatureRef: string | null; // TEXT nullable — ref HSM (WORM, STUB PD-37)
createdAt: Date; // TIMESTAMPTZ — CreateDateColumn
}
6. Index declares
| Index | Colonnes | Condition | Justification |
idx_attestations_attestation_id | attestation_id | UNIQUE | Lookup par reference externe (spec §5.13) |
idx_attestations_actor | actor_id | — | Requetes par acteur (audit, quota) |
idx_attestations_issued_at | issued_at | — | Partitioning + requetes temporelles (retention 10 ans) |
idx_attestations_package | dissemination_package_id | WHERE dissemination_package_id IS NOT NULL | Correlation package multi-documents (index partiel, spec §5.1) |
7. Conventions respectees
| Convention | Application |
PostgreSQL columns snake_case | Tous les name: sont en snake_case (attestation_id, request_id, etc.) |
TypeScript properties camelCase | attestationId, requestId, documentIds, etc. |
Schema vault_secure | @Entity('dissemination_attestations', { schema: 'vault_secure' }) |
@PrimaryGeneratedColumn('uuid') | Pattern standard du projet |
@CreateDateColumn avec timestamptz | created_at avec type explicite |
| JSDoc sur chaque colonne | Documentation complete avec references spec/INV |
! definite assignment assertion | Coherent avec DocumentSecure, Deposit, etc. |
| Import minimal TypeORM | Uniquement les decorateurs utilises |
8. Decisions architecturales
architectural_decisions:
- decision: "UUID[] natif PostgreSQL pour document_ids (pas JSONB)"
rationale: "Coherent avec le pattern keyword_deterministic dans DocumentSecure. Permet operateur @> pour lookup natif PostgreSQL et typage strict UUID."
alternatives_considered: ["JSONB array", "Table de jointure N:N"]
trade_offs: ["UUID[] = requetes simples + index GIN possible, mais pas de FK vers documents (intégrité applicative)"]
- decision: "Index partiel sur dissemination_package_id WHERE NOT NULL"
rationale: "Les attestations mono-document (package_id NULL) representent potentiellement la majorite. L'index partiel evite l'indexation inutile des NULL (REX PD-55: index partiels simples)."
alternatives_considered: ["Index complet", "Pas d'index"]
trade_offs: ["Index partiel = performance requetes package + economie stockage"]
9. Hypotheses
| ID | Hypothese | Impact si faux |
| H-ATT-01 | La migration (C1) cree la table vault_secure.dissemination_attestations avec le partitioning par annee sur issued_at | Si la table n'existe pas, l'entity ne peut pas etre utilisee |
| H-ATT-02 | hash_evidence est calcule par le service (C9) en SHA-256 des document_ids + actor_id + timestamp, pas par l'entity | Si le calcul est attendu dans l'entity, l'architecture est incorrecte |
| H-ATT-03 | signature_ref est un stub PD-37 (HSM non disponible), valeur NULL acceptee | Si HSM est obligatoire, la colonne nullable: true est incorrecte |
| H-ATT-04 | Le chiffrement au repos de hash_evidence est assure par l'infrastructure (PostgreSQL TDE), pas par chiffrement applicatif dans l'entity | Si chiffrement applicatif requis, le type text est insuffisant — il faudrait bytea avec AES-256-GCM |
10. Dependances inter-modules
| Composant | Direction | Detail |
| C1 (Migration DDL) | PREREQUIS | La table vault_secure.dissemination_attestations doit exister avant toute utilisation de l'entity |
| C9 (DisseminationService) | CONSOMMATEUR | Le service INSERT les attestations dans cette entity via queryRunner.manager.save() ou queryRunner.query() dans la transaction ACID |
| C10 (DisseminationController) | INDIRECT | Le controller retourne la reponse attestation generee par C9 |
11. Matrice de couverture tests
Les tests de l'entity DisseminationAttestation sont couverts par les tests d'integration du service (C9) :
| Test ID | Fichier de test | Couverture entity |
| TC-NOM-01 | dissemination.service.spec.ts | INSERT attestation mono-document, verification champs |
| TC-NOM-02 | dissemination.service.spec.ts | INSERT attestation multi-documents, verification document_ids[] et dissemination_package_id |
| TC-NOM-06 | dissemination.service.spec.ts | Verification motif_communication dans attestation |
| TC-NOM-07 | dissemination.service.spec.ts | dissemination_package_id NULL (mono) vs non-NULL (multi) |
| TC-INV-05 | dissemination.service.spec.ts | Exactement 1 attestation par requete, liee aux bons document_ids et actor_id |
| TC-ERR-09 | dissemination.service.spec.ts | Echec INSERT attestation → rollback atomique, status reste SEALED |
12. Type-check
npx tsc --noEmit : 0 erreur (verification projet complet)
13. Points de vigilance
| ID | Point | Mitigation |
| V-ATT-01 | TypeORM ne dispose pas de decorateur "read-only column" pour enforcer WORM au niveau entity | WORM enforce par : (1) service C9 qui ne fait jamais UPDATE, (2) trigger DB dans migration C1, (3) documentation JSDoc |
| V-ATT-02 | Le partitioning RANGE(issued_at) n'est pas declarable dans TypeORM entity | Gere exclusivement dans la migration DDL (C1). L'entity fonctionne de maniere transparente sur tables partitionnees. |
| V-ATT-03 | document_ids UUID[] n'a pas de FK vers vault_secure.documents | Integrite referentielle assuree au niveau applicatif (service C9 verifie l'existence des documents avant INSERT). Les FK sur arrays ne sont pas supportees par PostgreSQL. |