---
suivi: 1055
date: 2026-07-29
sujet: Étiquettes personnelles / projet + couleur + regroupement Kanban
chantier: taches_suivi
type: feature
statut: poussé
hash: 3b6b7fb6
fichiers:
  - database/migrations/2026_07_29_150000_add_portee_couleur_to_etiquettes_table.php
  - app/Models/Etiquette.php
  - app/Services/EtiquetteLibreService.php
  - app/Http/Controllers/EtiquetteController.php
  - app/Http/Controllers/TacheSuiviEtiquetteController.php
  - app/Services/TacheSuiviService.php
  - app/Support/TacheSuiviPresenter.php
  - app/Support/TacheSuiviPermissions.php
  - app/Http/Controllers/MesTachesController.php
  - app/Http/Controllers/TacheSuiviController.php
  - app/Services/Assistant/AssistantConsolidationTools.php
  - app/Services/Assistant/AssistantTacheSuiviRetrievalService.php
  - routes/web.php
  - resources/js/utils/mesTachesGrouping.js
  - resources/js/Pages/MesTaches/Index.vue
  - resources/js/Components/TachesSuivi/TacheSuiviEtiquettesEditor.vue
  - resources/js/Components/TachesSuivi/TacheSuiviProgrammeTag.vue
  - resources/js/Components/TachesSuivi/TacheSuiviKanbanCard.vue
  - resources/js/Components/TachesSuivi/TacheSuiviDetailModal.vue
  - resources/js/Pages/TachesSuivi/Show.vue
  - resources/css/mes-taches.css
  - database/data/journal_mises_a_jour_post_2026_06_12.php
  - public/build/
---

## PROMPT ENVOYÉ

SUIVI #1055 — Phase 2 après diag #1052. Étiquettes libres personnelles (privées) et
projet (membres), couleur sur l’étiquette, cohabitation Q2A sans fuite, parade C1 sur
regroupement programme, filtre + Kanban par étiquette, drop refusé. Suivi only.

## SYNTHÈSE

### Schéma
Migration `2026_07_29_150000_add_portee_couleur_to_etiquettes_table` : `user_id`, `projet_id`,
`couleur` ; index `(type,user_id)` / `(type,projet_id)` ; unicité `(user_id,libelle)` et
`(projet_id,libelle)` ; FK courtes `etiq_user_fk` / `etiq_projet_fk`. Pas de backfill.
Exclusivité mutuelle des FK + libellé obligatoire pour libres = **garantie applicative**
(`EtiquetteLibreService`), CHECK MySQL non fiable.

Deux régimes `libelle` : NULL OK pour `programme` ; obligatoire pour `personnelle`/`projet`.

### Scope `Etiquette::scopeVisiblesPour(User)`
Programme ∪ personnelles (`user_id = me`) ∪ projet (créateur ∪ `projet_personnes`).

**Appelants lecture :** MesTachesController eager, TacheSuiviController::show,
TacheSuiviPresenter (via relation déjà filtrée), EtiquetteLibreService::listVisibles /
fresh attach-detach-sync, AssistantConsolidationTools, AssistantTacheSuiviRetrievalService.

**Appelants écriture :** EtiquetteLibreService::assertPeutAttacher (via `estVisiblePar`),
normalizeEtiquetteIds / syncLibresVisibles (refus id hors portée).

### Droits
| Action | Personnelle | Projet | Programme | Contrôle |
|---|---|---|---|---|
| Créer / renommer / recolorer / supprimer | propriétaire seul | tout membre du projet | inchangé (auto) | `EtiquetteLibreService::assertPeutGerer` |
| Poser / retirer sur tâche | créateur OU associé (ses propres) | créateur de la tâche | formulaire programmes[] | `assertPeutAttacher` + Policy view tâche |
| Voir dans payload | scope données | scope données | tous (scopé) | `visiblesPour` |

### C1
`mesTachesGrouping.js` : colonnes programme uniquement si `type === 'programme'`.
Tâche avec perso seule → « Sans programme ». Drop `etiq-*` refusé.

### Regroupement N étiquettes
Trello : la tâche apparaît dans **chaque** colonne correspondante. Drop **refusé**.

### N+1
Eager `etiquettes` + `visiblesPour` en une contrainte de relation (1 requête pivot/étiquettes
pour la liste), pas de requête par carte. Chiffre local synthétique (MySQL Cursor down).

## DÉPLOIEMENT-TEST

```bash
git pull origin main
php artisan migrate --force
php artisan route:clear
php artisan journal:sync
php artisan view:clear
find app/ -name "*.php" -exec touch {} +
```

## LEÇON

Une clé de regroupement calculée en `programme_id ?? id` fonctionne aussi longtemps
qu’un seul type d’étiquette existe, et devient silencieusement fausse dès qu’on en ajoute
un second — un défaut de ce genre ne lève aucune erreur, il produit un classement crédible.
