diff --git a/docs/superpowers/specs/2026-03-15-task-documents-design.md b/docs/superpowers/specs/2026-03-15-task-documents-design.md
new file mode 100644
index 0000000..2515f15
--- /dev/null
+++ b/docs/superpowers/specs/2026-03-15-task-documents-design.md
@@ -0,0 +1,171 @@
+# Task Documents — Design Spec
+
+## Overview
+
+Ajout d'un système de documents attachés aux tickets (tasks). Les utilisateurs peuvent uploader des fichiers via drag & drop ou sélection, les visualiser (images, PDF) dans une modale plein écran, et les télécharger.
+
+## Contraintes
+
+- **Taille max par fichier** : 50 Mo
+- **Types acceptés** : tous types de fichiers
+- **Nombre par ticket** : illimité
+- **Stockage** : filesystem local (`var/uploads/documents/`)
+- **Permissions** : ROLE_ADMIN pour créer/supprimer, ROLE_USER pour lire
+
+## Backend
+
+### Entité `TaskDocument`
+
+| Champ | Type | Description |
+|-------|------|-------------|
+| `id` | int (auto) | Clé primaire |
+| `task` | ManyToOne → Task | Ticket parent (CASCADE on delete) |
+| `originalName` | string (255) | Nom original du fichier uploadé |
+| `fileName` | string (255) | Nom unique sur disque (`{uuid}.{extension}`) |
+| `mimeType` | string (100) | Type MIME (ex: `image/png`, `application/pdf`) |
+| `size` | int | Taille en octets |
+| `createdAt` | DateTimeImmutable | Date d'upload |
+| `uploadedBy` | ManyToOne → User | Utilisateur ayant uploadé (SET NULL on delete) |
+
+### Relation inverse sur Task
+
+- `Task.documents` : OneToMany → TaskDocument
+- Sérialisé dans le groupe `task:read` pour charger les documents avec le ticket
+
+### Stockage filesystem
+
+- Répertoire : `var/uploads/documents/`
+- Nommage : `{uuid}.{extension}` — évite les collisions et les caractères spéciaux
+- Volume Docker dédié pour persister les uploads
+- Servir les fichiers via Nginx (`/uploads/{fileName}`) ou un endpoint Symfony dédié pour contrôle d'accès
+
+### API Endpoints
+
+| Méthode | Route | Description | Accès |
+|---------|-------|-------------|-------|
+| `POST` | `/api/task_documents` | Upload multipart/form-data | ROLE_ADMIN |
+| `GET` | `/api/task_documents?task=/api/tasks/{id}` | Liste documents d'un ticket | ROLE_USER |
+| `GET` | `/api/task_documents/{id}` | Métadonnées d'un document | ROLE_USER |
+| `DELETE` | `/api/task_documents/{id}` | Supprime document + fichier | ROLE_ADMIN |
+
+### State Processor — POST (`TaskDocumentProcessor`)
+
+1. Reçoit le fichier via multipart/form-data + IRI de la task
+2. Valide : fichier non vide, taille ≤ 50 Mo
+3. Génère un UUID v4, extrait l'extension du nom original
+4. Déplace le fichier uploadé dans `var/uploads/documents/{uuid}.{ext}`
+5. Crée et persiste l'entité `TaskDocument` avec toutes les métadonnées
+6. Set `uploadedBy` depuis le token JWT courant
+
+### State Processor — DELETE
+
+1. Récupère le `fileName` de l'entité
+2. Supprime le fichier du filesystem (`var/uploads/documents/{fileName}`)
+3. Supprime l'entité de la base de données
+
+### Validation
+
+- Contrainte sur `originalName` : NotBlank
+- Contrainte sur `task` : NotNull
+- Validation dans le Processor : taille fichier ≤ 50 Mo, fichier présent dans la requête
+- PHP `upload_max_filesize` et `post_max_size` à configurer ≥ 50 Mo
+
+### Configuration PHP/Nginx
+
+- `php.ini` : `upload_max_filesize = 50M`, `post_max_size = 55M`
+- Nginx : `client_max_body_size 55m;`
+
+## Frontend
+
+### Placement dans l'UI
+
+La zone de documents est placée **sous la description** dans le `TaskModal`, visible en mode édition.
+
+### Composants à créer
+
+Tous dans `frontend/components/task/` :
+
+#### `TaskDocumentUpload.vue`
+
+- Zone drag & drop avec bordure pointillée
+- Texte : "Glisser des fichiers ici ou cliquer pour sélectionner"
+- Input file caché (`multiple`, `accept="*"`)
+- Événements : `dragover`, `dragleave`, `drop` pour le feedback visuel
+- Barre de progression par fichier pendant l'upload
+- Upload séquentiel ou parallèle (un POST multipart par fichier)
+- Émet un événement quand l'upload est terminé pour rafraîchir la liste
+
+#### `TaskDocumentList.vue`
+
+- Grille de cartes compactes pour chaque document
+- **Images** (`image/*`) : miniature 64x64 en `object-fit: cover`, chargée depuis l'URL du fichier
+- **Autres fichiers** : icône selon le type MIME :
+ - PDF → icône PDF
+ - Word/Excel → icônes Office
+ - Archives → icône archive
+ - Défaut → icône fichier générique
+- Informations affichées : nom original (tronqué si > ~30 chars), taille formatée (Ko/Mo)
+- Clic sur un document → ouvre `TaskDocumentPreview`
+- Bouton supprimer (visible uniquement pour ROLE_ADMIN, avec confirmation)
+
+#### `TaskDocumentPreview.vue`
+
+- Modale plein écran (overlay sombre semi-transparent)
+- Contenu selon le type :
+ - **Images** (`image/*`) : `
` centré, taille adaptative
+ - **PDF** (`application/pdf`) : `