Décomposition multi-agents — PD-63
Story : PD-63 — GET /documents/:id/download Date : 2026-02-20 Branche : feature/PD-63-document-download
Vue d'ensemble
| Métrique | Valeur |
| Total tâches | 20 (+ 4 vérifications Phase 0) |
| Phases | 7 |
| Agents impliqués | 2 (agent-developer, agent-qa-unit-integration) |
| Code contracts | 8 |
| Base | PD-46 (existant) |
Phase 0 — Vérifications préalables (go/no-go)
Ces vérifications sont exécutées par l'orchestrateur (Claude) avant de lancer les agents.
| # | Vérification | Critère | Action si no-go |
| 0a | Table document_shares | Existe dans DB | Créer ticket + migration |
| 0b | Claims JWT tenant_id/roles | Présents dans token | Ticket config Keycloak |
| 0c | RLS vault_secure.documents | Active | Documenter migration |
| 0d | Schéma audit enrichi | Service compatible | Adapter AuditService |
Règle : Si un critère échoue → escalade humaine avant Phase 1.
Phase 1 — Migration et Entity (Δ6)
Tâche 1 — Migration add-document-status-fields
| Attribut | Valeur |
| ID | TASK-01 |
| Agent | agent-developer |
| Contract | CC-63-06 |
| Fichiers | src/database/migrations/YYYYMMDDHHMMSS-add-document-status-fields.ts |
| Dépend de | Phase 0 (go/no-go) |
| INV/CA | INV-63-10 (erreurs 410/423) |
Spécification :
// Migration TypeORM
// Ajouter 5 colonnes à vault_secure.documents :
// - deleted_at: TIMESTAMP NULL
// - deletion_reason: VARCHAR(50) NULL
// - legal_lock: BOOLEAN DEFAULT false
// - legal_lock_reason: VARCHAR(50) NULL
// - legal_lock_until: TIMESTAMP NULL
Tâche 2 — Entity DocumentSecure (champs statut)
| Attribut | Valeur |
| ID | TASK-02 |
| Agent | agent-developer |
| Contract | CC-63-05 |
| Fichiers | src/modules/documents/entities/document-secure.entity.ts |
| Dépend de | TASK-01 |
| INV/CA | INV-63-10 |
Spécification :
// Ajouter les propriétés TypeORM :
@Column({ type: 'timestamp', nullable: true })
deletedAt?: Date;
@Column({ type: 'varchar', length: 50, nullable: true })
deletionReason?: string;
@Column({ type: 'boolean', default: false })
legalLock: boolean;
@Column({ type: 'varchar', length: 50, nullable: true })
legalLockReason?: string;
@Column({ type: 'timestamp', nullable: true })
legalLockUntil?: Date;
Tâche 3 — Tests unitaires Entity
| Attribut | Valeur |
| ID | TASK-03 |
| Agent | agent-qa-unit-integration |
| Contract | CC-63-05 |
| Fichiers | src/modules/documents/entities/document-secure.entity.spec.ts |
| Dépend de | TASK-02 |
| Tests | Validation des champs, valeurs par défaut |
Phase 2 — Codes d'erreur (Δ2, Δ8)
Tâche 4 — Codes d'erreur ERR-63-*
| Attribut | Valeur |
| ID | TASK-04 |
| Agent | agent-developer |
| Contract | CC-63-03 |
| Fichiers | src/modules/documents/download/download.errors.ts |
| Dépend de | - |
| INV/CA | INV-63-10, CA-63-07 |
Spécification :
// Ajouter ERR-63-11 (audit indisponible)
// Mapper ERR-46-* vers ERR-63-*
// Codes : 400, 401, 403, 404, 410, 423, 429, 500, 503
Tâche 5 — Tests codes d'erreur
| Attribut | Valeur |
| ID | TASK-05 |
| Agent | agent-qa-unit-integration |
| Contract | CC-63-03 |
| Fichiers | src/modules/documents/download/download.errors.spec.ts |
| Dépend de | TASK-04 |
| Tests | TC-ERR-01 |
Phase 3 — Guard enrichi (Δ3, Δ4, Δ5)
Tâche 6 — Check partage actif
| Attribut | Valeur |
| ID | TASK-06 |
| Agent | agent-developer |
| Contract | CC-63-01 |
| Fichiers | src/modules/documents/download/guards/document-access.guard.ts |
| Dépend de | TASK-02 |
| INV/CA | INV-63-06, INV-63-08, CA-63-09 |
Spécification :
// Vérifier table document_shares
// Check: share_type, expires_at, revoked_at
// Si partage valide → ALLOW avec contexte délégation
Tâche 7 — Check B2B co-détention
| Attribut | Valeur |
| ID | TASK-07 |
| Agent | agent-developer |
| Contract | CC-63-01 |
| Fichiers | src/modules/documents/download/guards/document-access.guard.ts |
| Dépend de | TASK-06 |
| INV/CA | INV-63-06, INV-63-08, CA-63-09 |
Spécification :
// Vérifier claims JWT tenant_id + roles
// Check: co_holders table ou claims B2B
// Si co-détention valide → ALLOW avec contexte B2B
Tâche 8 — Check statut document
| Attribut | Valeur |
| ID | TASK-08 |
| Agent | agent-developer |
| Contract | CC-63-01 |
| Fichiers | src/modules/documents/download/guards/document-access.guard.ts |
| Dépend de | TASK-07 |
| INV/CA | INV-63-10, CA-63-03, CA-63-04 |
Spécification :
// Vérifier status document selon §3.1 spec :
// - PENDING → 403
// - SEALED → OK
// - EXPIRED → 410
// - ARCHIVED → HEAD Object S3, si non accessible → 503
// Vérifier deleted_at → 410 (ERR-63-06)
// Vérifier legal_lock → 423 (ERR-63-07)
Tâche 9 — Tests unitaires guard
| Attribut | Valeur |
| ID | TASK-09 |
| Agent | agent-qa-unit-integration |
| Contract | CC-63-01 |
| Fichiers | src/modules/documents/download/guards/document-access.guard.spec.ts |
| Dépend de | TASK-08 |
| Tests | TC-NOM-02/03/04, TC-ERR-02/03 |
Phase 4 — Audit fail-closed (Δ1, Δ7)
Tâche 10 — Schéma événement audit enrichi
| Attribut | Valeur |
| ID | TASK-10 |
| Agent | agent-developer |
| Contract | CC-63-04 |
| Fichiers | src/modules/documents/audit/audit-download.service.ts |
| Dépend de | - |
| INV/CA | INV-63-05, CA-63-07 |
Spécification :
// Enrichir schéma événement audit :
interface DownloadAuditEvent {
actor_id: string;
document_id: string;
tenant_id: string;
decision: 'ALLOW' | 'DENY';
reason: string;
timestamp_utc: string;
correlation_id: string;
// Nouveaux champs :
access_type: 'OWNER' | 'SHARE' | 'B2B';
share_id?: string;
co_holder_id?: string;
}
Tâche 11 — Fail-closed dans service
| Attribut | Valeur |
| ID | TASK-11 |
| Agent | agent-developer |
| Contract | CC-63-02 |
| Fichiers | src/modules/documents/download/download.service.ts |
| Dépend de | TASK-10 |
| INV/CA | INV-63-05 |
Spécification :
// AVANT de délivrer l'URL pré-signée :
// 1. Appeler auditService.logDownloadAttempt()
// 2. Si audit échoue (exception ou timeout) → throw ERR-63-11
// 3. Sinon → générer URL pré-signée
// FAIL-CLOSED STRICT : pas de retry applicatif
Tâche 12 — Tests fail-closed
| Attribut | Valeur |
| ID | TASK-12 |
| Agent | agent-qa-unit-integration |
| Contract | CC-63-02 |
| Fichiers | src/modules/documents/download/download.service.spec.ts |
| Dépend de | TASK-11 |
| Tests | TC-INT-14, TC-NOM-06 |
Phase 5 — Intercepteur audit global (Δ9)
Tâche 13 — AuditRejectInterceptor
| Attribut | Valeur |
| ID | TASK-13 |
| Agent | agent-developer |
| Contract | CC-63-08 |
| Fichiers | src/modules/documents/download/interceptors/audit-reject.interceptor.ts |
| Dépend de | TASK-10 |
| INV/CA | INV-63-05, CA-63-07 |
Spécification :
// Intercepteur NestJS qui :
// 1. Capture les réponses 401/403/404
// 2. Log événement audit avec decision=DENY
// 3. Propage la réponse originale
// Ne bloque PAS la réponse (audit best-effort pour rejets précoces)
Tâche 14 — Enregistrer intercepteur
| Attribut | Valeur |
| ID | TASK-14 |
| Agent | agent-developer |
| Contract | CC-63-08 |
| Fichiers | src/modules/documents/download/download.controller.ts |
| Dépend de | TASK-13 |
| INV/CA | INV-63-05 |
Spécification :
// Ajouter @UseInterceptors(AuditRejectInterceptor) sur le controller
// OU enregistrer globalement dans le module
Tâche 15 — Tests intercepteur
| Attribut | Valeur |
| ID | TASK-15 |
| Agent | agent-qa-unit-integration |
| Contract | CC-63-08 |
| Fichiers | src/modules/documents/download/interceptors/audit-reject.interceptor.spec.ts |
| Dépend de | TASK-14 |
| Tests | ST-63-12 (audit rejets précoces) |
Phase 6 — Tests d'intégration
Tâche 16 — Tests E2E endpoint
| Attribut | Valeur |
| ID | TASK-16 |
| Agent | agent-qa-unit-integration |
| Contract | CC-63-07 |
| Fichiers | src/modules/documents/download/download.controller.e2e-spec.ts |
| Dépend de | TASK-09, TASK-12, TASK-15 |
| Tests | TC-INT-01 à TC-INT-15, TC-E2E-01 à TC-E2E-08 |
Graphe de dépendances
Phase 0 (go/no-go)
│
▼
TASK-01 (migration)
│
▼
TASK-02 (entity)
│
├──────────────────┐
▼ ▼
TASK-03 TASK-06 (partage)
(tests entity) │
▼
TASK-04 ───────► TASK-07 (B2B)
│ │
▼ ▼
TASK-05 TASK-08 (statut)
(tests errors) │
▼
TASK-09 (tests guard)
│
TASK-10 ──────────────┼──────────────┐
│ │ │
▼ │ ▼
TASK-11 (fail-closed) │ TASK-13 (interceptor)
│ │ │
▼ │ ▼
TASK-12 │ TASK-14 (register)
(tests fail-closed) │ │
│ │ ▼
│ │ TASK-15 (tests interceptor)
│ │ │
└──────────────────┴──────────────┘
│
▼
TASK-16 (E2E)
Ordre d'exécution
| Ordre | Tâche | Agent | Durée estimée |
| 1 | TASK-01 | agent-developer | 10 min |
| 2 | TASK-02 | agent-developer | 10 min |
| 3 | TASK-03 | agent-qa-unit-integration | 15 min |
| 4 | TASK-04 | agent-developer | 15 min |
| 5 | TASK-05 | agent-qa-unit-integration | 10 min |
| 6 | TASK-06 | agent-developer | 20 min |
| 7 | TASK-07 | agent-developer | 20 min |
| 8 | TASK-08 | agent-developer | 25 min |
| 9 | TASK-09 | agent-qa-unit-integration | 25 min |
| 10 | TASK-10 | agent-developer | 15 min |
| 11 | TASK-11 | agent-developer | 20 min |
| 12 | TASK-12 | agent-qa-unit-integration | 20 min |
| 13 | TASK-13 | agent-developer | 20 min |
| 14 | TASK-14 | agent-developer | 5 min |
| 15 | TASK-15 | agent-qa-unit-integration | 15 min |
| 16 | TASK-16 | agent-qa-unit-integration | 45 min |
Total estimé : ~5h
Mapping Code Contracts
| Contract | Module | Owner | Tâches |
| CC-63-01 | document-access-guard | agent-developer | 6, 7, 8, 9 |
| CC-63-02 | download-service | agent-developer | 11, 12 |
| CC-63-03 | download-errors | agent-developer | 4, 5 |
| CC-63-04 | audit-download | agent-developer | 10 |
| CC-63-05 | document-secure-entity | agent-developer | 2, 3 |
| CC-63-06 | migration-status-fields | agent-developer | 1 |
| CC-63-07 | tests-integration | agent-qa-unit-integration | 16 |
| CC-63-08 | audit-reject-interceptor | agent-developer | 13, 14, 15 |
Généré par : Claude (Orchestrateur) Date : 2026-02-20