diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml
index 06a5dc3..12bf7a8 100644
--- a/.github/workflows/continuous-integration.yml
+++ b/.github/workflows/continuous-integration.yml
@@ -20,7 +20,7 @@ jobs:
name: "Generate CI matrix"
uses: "glpi-project/plugin-ci-workflows/.github/workflows/generate-ci-matrix.yml@v1"
with:
- glpi-version: "11.0.x"
+ glpi-version: "12.0.x"
ci:
name: "GLPI ${{ matrix.glpi-version }} - php:${{ matrix.php-version }} - ${{ matrix.db-image }}"
needs: "generate-ci-matrix"
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d834ef4..e9d233f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).
+## [1.21.0] - 2026-09-07
+
+### Added
+
+- GLPI 12 compatibility
+
## [1.20.3] - 2026-09-04
### Fixed
diff --git a/MIGRATION_NOTES.md b/MIGRATION_NOTES.md
new file mode 100644
index 0000000..8c26398
--- /dev/null
+++ b/MIGRATION_NOTES.md
@@ -0,0 +1,172 @@
+# Notes de migration GLPI 12 — plugin TreeView
+
+Migration réalisée sur la branche `feature/glpi-12.0`. Environnement : GLPI
+core `main` (`12.0.0-dev`, hôte `glpi-db-v12`), PHP 8.4, plugin
+`feature/glpi-12.0` rebasé sur `origin/main` (`722fbf2`, « 1.20.3 » #127).
+
+**Aucun environnement de test / migration Docker disponible** pour cette
+session : la Phase 2 (migration réelle 11 → 12 avec BDD isolée) et
+l'exécution de la Phase 3 (`phpunit`, qui exige une BDD de test avec le
+plugin installé + actif) n'ont **pas** pu être déroulées. Les fichiers de
+tests ont été validés statiquement (lint, PHPStan, cohérence avec les
+helpers `Glpi\Tests\*` du core 12). Tout le reste (Phases 1, 4, 5) a été
+mené à bien.
+
+## Points d'attention rencontrés
+
+- **`vendor/` local requis** : `plugin_treeview_check_prerequisites()`
+ échoue tant que `plugins/treeview/vendor/autoload.php` n'existe pas →
+ `composer install --no-dev` obligatoire après chaque clone / rebuild.
+ `vendor/` est git-ignoré. TreeView n'a aucune dépendance runtime
+ (`composer.json require` = `php` seul), `composer install` ne fait que
+ générer l'autoloader.
+- **`require-dev: glpi-project/tools`** retiré de `composer.json` par la
+ branche `feature/glpi-12.0` (évite la collision `symfony/console` /
+ `Cannot redeclare CommandLoaderInterface` avec le core). `composer.lock`
+ allégé en conséquence (869 lignes de packages-dev supprimées).
+- **Protection CSRF sans token en GLPI 12** : `Session::getNewCSRFToken()`
+ et la fonction Twig `csrf_token()` sont **dépréciées** (« Csrf protection
+ is now handled without tokens », `src/Session.php`). Le core n'utilise
+ plus `csrf_token()` dans aucun template. Appeler ces API émettrait une
+ déprécation → échec des tests via `GLPITestCase::tearDown()`. D'où la
+ suppression des `` des 3
+ templates et du token manuel dans l'URL de recherche
+ (`inc/config.class.php`).
+- **`CacheManager->clearSymfonyCache()`** émet un `CRITICAL` non bloquant en
+ fin de `database:update` — bug d'environnement core, indépendant du
+ plugin, à ne pas porter dans la PR.
+- **Suspension des plugins après MAJ majeure** (non observé ici, pas de
+ bascule réelle) : une vraie migration 11 → 12 met
+ `core.plugins_execution_mode = suspended_by_update` → toutes les URL
+ `/plugins/*/front/*.php` renvoient **404** jusqu'à
+ `php bin/console plugin:resume_execution`.
+
+## Phase 1 — Rebase de `feature/glpi-12.0` sur `main`
+
+`feature/glpi-12.0` (`ced8c55`) partait de `b2b679a` ; `main` avait 3
+commits d'avance (#123 « item validation when loading trees », #125
+« enforce preference check », #127 « 1.20.3 ») → **rebase non trivial**.
+
+Conflits résolus :
+
+| Fichier | Résolution |
+|---|---|
+| `CHANGELOG.md` | nouvelle section `## [1.21.0]` (Added: GLPI 12 compatibility) au-dessus de `## [1.20.3]` |
+| `setup.php` | `VERSION` = `1.20.3` (côté main) puis bumpé `1.21.0` en Phase 5 ; `MIN`/`MAX_GLPI` = `12.0.0` / `12.0.99` (côté feature) |
+| `templates/preference.html.twig` | `#125` retire `` (l'`id` est désormais résolu côté serveur), `ced8c55` retire `` → **les deux lignes supprimées** |
+| `inc/config.class.php` | auto-merge OK : refactor sécurité de `#123` + suppression du token CSRF de l'URL de recherche |
+
+Le commit `ced8c55` rebasé apporte : contraintes GLPI
+`12.0.0` / `12.0.99` (`setup.php`), matrice CI `12.0.x`, `Makefile`,
+suppression `require-dev` tools + `composer.lock` allégé, retrait des
+tokens CSRF manuels (3 templates + `inc/config.class.php`).
+
+## Phase 2 — Migration GLPI 11 → 12
+
+**Non réalisée** (pas d'environnement Docker / BDD isolée). Le plugin crée
+3 tables (`glpi_plugin_treeview_configs`, `_profiles`, `_preferences`) via
+`hook.php` ; aucun secret chiffré, aucune tâche cron, aucune notification,
+et `plugin_treeview_install()` porte déjà toute la chaîne d'upgrade
+historique (1.0 → 1.4, renommages de tables inclus). Le risque de
+régression de données sur une bascule 11 → 12 est faible, mais **reste à
+valider** dans un environnement dédié avant publication — la Phase 1 du
+schéma est désormais couverte par `InstallationTest` en CI.
+
+## Phase 3 — Tests fonctionnels automatisés
+
+Harnais **déjà correct** sur `main` (repris tel quel par le rebase), aux
+standards GLPI 12 :
+
+- `tests/bootstrap.php` → `require ../../../tests/bootstrap.php` (bootstrap
+ core, expose `Glpi\Tests\*`) + `require_once TreeviewTestCase.php` +
+ garde `Plugin::isPluginActive('treeview')`.
+- `phpunit.xml` → suite unique `