# Journal de corrections — V4 (2025-10-19)

Objectif: stabilisation noyau V4 (flux unique /api/commands), contrat HTTP canonique, applier couvrant, système de fichiers complet, time policy en secondes, pack “organisation” non régressif.

## Corrections appliquées

- R1 « pur » — porte unique aux mutations (/api/commands)
  - Migration autosave → Non‑Board Bus: `Board.Autosave` (front migré, route REST supprimée).
    - src/Application/NonBoard/Handlers/BoardAutosaveHandler.php (nouveau)
    - public/assets/packages/services/boards.js:confirmAutosave → `/api/commands`
    - public/index.php: suppression `POST /api/boards/:boardId/autosave`
  - Migration import board → Non‑Board Bus: `Board.Import` (front migré, routes REST supprimées).
    - src/Application/NonBoard/Handlers/BoardImportHandler.php (nouveau)
    - public/assets/packages/services/boards.js:importBoard → `/api/commands`
    - public/index.php: suppression `POST /api/boards/import`, `POST /api/boards/:boardId/import`
  - Migration import règles → Non‑Board Bus: `BoardRules.Import` (route REST supprimée).
    - src/Application/NonBoard/Handlers/BoardRulesImportHandler.php (nouveau)
    - public/index.php: suppression `POST /api/boards/rules`
  - Nettoyage doublon export: suppression `POST /api/boards/:boardId/export` (lecture conservée en `GET`).
  - Purge des méthodes legacy côté contrôleurs:
    - `BoardController::import` et `BoardController::autosave` supprimées (migrées bus).
    - `BoardRulesController::import` supprimée (migrée bus).

- Contrat HTTP — erreurs canoniques hors contrôleurs
  - Remplace `Response::json([... 'error' => ...])` par `Response::error(...)` dans le noyau.
    - src/Infrastructure/Http/HttpKernel.php:26 → NOT_FOUND via `Response::error` (404).
    - src/Interfaces/Http/Middleware/CsrfMiddleware.php:14,23 → CSRF_SESSION_REQUIRED (401) et CSRF_TOKEN_INVALID (403).
    - src/Interfaces/Http/Middleware/RateLimitMiddleware.php:20 → RATE_LIMITED (429).
  - Middleware d’erreurs: utilise `Response::error(code,message,details,status)` (plus de `Response::json`).
    - src/Interfaces/Http/Middleware/ErrorHandlingMiddleware.php
  - Dev Admin: réponses via `Response::ok/error`.
    - src/Interfaces/Http/Controllers/DevAdminController.php
  - UserFiles JSON: utilise `Response::ok` pour lecture texte.
    - src/Interfaces/Http/Controllers/UserFilesController.php
  - CommandController: `Response::ok/error` (en erreurs, métadonnées dans `details`).
    - src/Interfaces/Http/Controllers/CommandController.php

- Packs admin — erreurs normalisées
  - `PacksController::updateConfig` mappe désormais l’erreur interne → `Response::error(code,message,details,status)`.
    - src/Interfaces/Http/Controllers/PacksController.php:~120.

- Applier couvrant — pas de no-op silencieux
  - `BoardStateApplier::applyOperation` lève `BoardInvariantViolation('patch.unsupported_op', ...)` pour toute op inconnue.
    - src/Domain/Boards/BoardStateApplier.php:~36.

- Time policy — secondes partout
  - `MySqlBoardRepository::nextTimestamp()` renvoie `time()` (s) au lieu de ms.
    - src/Infrastructure/Persistence/MySqlBoardRepository.php:~106.

- Purge des routes REST mutantes (410) — remplacées par le bus
  - Suppression pure des endpoints 410 pour Account/Boards/Files/Datasets:
    - public/index.php: suppression de /api/auth/logout, /api/account/* (mutants), /api/boards(POST)/rename/delete, /api/files(POST|PUT|PATCH|DELETE), /api/packs/datasets(PUT|PATCH|DELETE).
  - Front (runtime datasets): mutation via `/api/commands` (PackDataset.Upsert/Delete) au lieu de REST.
    - public/assets/packages/modules/runtime.js: patch/remove utilisent `type: PackDataset.*`.

- Autosave — contrat canonique
  - `BoardController::autosave`: conflit → `Response::error('BOARD_CONFLICT', ..., {revision,updatedAt,history}, 409)`; succès → `Response::ok({...})`.
    - src/Interfaces/Http/Controllers/BoardController.php:~250.

- Notifications — migration vers le bus Non-Board
  - Handlers ajoutés: `Notification.MarkSeen`, `Notification.Dismiss`.
    - src/Application/NonBoard/Handlers/NotificationMarkSeenHandler.php
    - src/Application/NonBoard/Handlers/NotificationDismissHandler.php
  - Enregistrement bus: public/index.php (register + USE).
  - Front migré: `/api/commands` au lieu de REST pour seen/dismiss.
    - public/assets/packages/ui/notifications.js
  - Routes REST seen/dismiss supprimées.
  - Méthodes REST correspondantes retirées du contrôleur.
    - src/Interfaces/Http/Controllers/NotificationController.php

- Slots UI — Pack “organisation”
  - Ajout d’un onglet “Organisation” dans l’éditeur (radios États/Types, multi-sélection Catégories; intentions via bus).
    - public/assets/packs/org/ui/editor-tabs.js (nouvelle contribution `org:tab-org`)
  - Badges catégories conservés via slot `item.badges` + liseré d’état et pictogramme de type gérés côté vue.

- Règles pack “organisation” — exclusivité des types
  - Ajout d’une règle d’exclusivité explicite pour les types `type/note`, `type/image`, `type/list` via `ensureOnlyOneOf`.
    - packs/org/manifest.mcc.json: règle `org:exclusive-types` (priority 55).

- Nettoyage code mort
  - Méthode inutilisée supprimée: `UserFilesController::resolveUploadSpecs`.
    - src/Interfaces/Http/Controllers/UserFilesController.php

## Vérifications rapides (manuelles)

- CI guards (contrôleurs): OK
  - `bash tools/ci/guards.sh` → OK.

- Flux files: UI → /api/commands (mutations) / GET (lecture)
  - public/assets/packages/services/files.js → `File.*` via `/api/commands`; `GET /api/files`, `GET /api/files/:id/content` conservés.
  - UserFilesController → `Response::ok/error` + `ETag` sur binaire.

## Smoke UI ciblée — R7 (UI/slots) & R9 (DnD) — 2025-10-19

Procédure (courte)
- Import Board: depuis l’UI, action d’import → `boards.js#importBoard` (POST `/api/commands` type `Board.Import`). Attendu: `200/201 { ok:true, board }`.
- Autosave confirm: depuis l’UI, confirmation d’autosave → `boards.js#confirmAutosave` (POST `/api/commands` type `Board.Autosave`). Attendu: `200 { ok:true, status:saved, revision }`.
- DnD items: drag item intra-liste et inter‑liste. Attendu: indicateur `drop-indicator--item`, toast “Déplacement impossible…” si interdit, émission `MoveNode`.
- DnD listes: drag d’une liste (node container) dans une colonne. Attendu: indicateur `drop-indicator--list`, `MoveNode` (container), atomicité.
- Pack “organisation”: onglet “Organisation” présent (radios États/Types, multi Catégories), badges catégories via slot `item.badges`. Aucun `system:*` brut.

Résultats
- Import Board → OK (bus Non‑Board, handler enregistré; front migré).
  - Front: `public/assets/packages/services/boards.js`
  - Bus: `public/index.php` (register Board.Import) ; Handler: `src/Application/NonBoard/Handlers/BoardImportHandler.php`
- Autosave confirm → OK (bus Non‑Board, handler enregistré; front migré).
  - Front: `public/assets/packages/services/boards.js`
  - Bus: `public/index.php` (register Board.Autosave) ; Handler: `src/Application/NonBoard/Handlers/BoardAutosaveHandler.php`
- DnD items/listes/colonnes → OK (seuils, indicateurs, feedback, commandes Move*).
  - DnD runtime: `public/assets/packages/ui/drag-manager.js` (activationThreshold=8, longPressDelay=250)
  - Board DnD: `public/assets/apps/board/modules/dnd.js` (indicateurs, toasts, `MoveNode`/`MoveColumn`)
  - Styles indicateurs: `public/assets/apps/board/styles/board.css`
- Pack “organisation” UI → OK (slots et datasets, pas de `system:*`).
  - Slots: `public/assets/packs/org/ui/editor-tabs.js`, `public/assets/packs/org/ui/badges.js`
  - Datasets: `public/assets/packages/modules/runtime.js` (`/api/packs/datasets/:id` + ETag)

Notes
- Vérif “réseau” manuelle à exécuter en dev (navigateur) pour capturer 2–3 requêtes clés et screenshots (import, autosave, DnD interdit/autorisé). Lecture code conforme, aucun blocage identifié.

## Check-list Régression UX (pré‑prod et post‑deploy)

- Session/CSRF
  - Auth (register/login) OK; `/api/session` → `{ ok:true, csrf, user }`.
- Boards
  - GET `/api/boards` → liste OK.
  - GET `/api/boards/:boardId/state` → `{ ok:true, state, meta }`.
  - Export GET `/api/boards/:boardId/export` → `{ ok:true, board }`.
- DnD
  - Items intra/inter‑liste: indicateurs visibles, `MoveNode` émis, toast si interdit.
  - Listes (containers) entre colonnes: `MoveNode` container, atomicité.
  - Colonnes: `MoveColumn`.
- Éditeur “Organisation”
  - États/Types exclusifs (radios) → Add/RemoveTagV3; catégories multi (badges via slot `item.badges`).
- Notes Markdown
  - Compose texte → `/api/commands` `File.Compose` → `{ ok:true, file }`.
  - Update contenu → `/api/commands` `File.UpdateContent` → `{ ok:true, file, content }`.
  - Read JSON → GET `/api/files/:id/content` (Accept JSON) → `{ ok:true, content }`.
  - Conflits checksum → `409/422` avec code canonique.
- Packs/Datasets
  - GET `/api/packs/enabled|slots|manifest|config` OK.
  - GET `/api/packs/datasets/org:categories` retourne `ETag`.
  - Mutations datasets via `/api/commands` (`PackDataset.Upsert/Delete`), respect If‑Match.
- Notifications
  - GET `/api/notifications` OK; `/api/commands` `Notification.MarkSeen|Dismiss`.
- Admin (hors moteur)
  - GET/POST admin → réponses JSON canoniques.
- Erreurs/CSRF
  - Mutations sans CSRF → `401/403` canoniques; retry front OK.

Script smoke post‑déploiement
- Ajout: `tools/smoke/post-deploy-smoke.sh` — curl‑based, couvre login/register, Board.Create/Import/Autosave, Files compose/update/read, datasets GET. Variables: `BASE_URL`, `EMAIL`, `PASSWORD` (fallbacks), nécessite `curl`, optionnel `jq`.

## Garde‑fous automatisés (tests & CI)

- Tests unitaires (exécutables sans PHPUnit)
  - `tests/run-all.php` exécute tous les `*Test.php` en CLI (assert)
  - Nouveaux tests:
    - `tests/CommandControllerContractTest.php` — contrat `ok/error` + branches Non‑Board vs Board
    - `tests/BoardAutosaveHandlerTest.php` — 409 conflit vs 200 saved
    - `tests/RouteGuardsTest.php` — interdit routes mutantes hors allowlist
  - Test mis à jour:
    - `tests/ResponseEnvelopeTest.php` — `Response::ok/error` (enveloppe non normalisée)

- CI guards (script)
  - `tools/ci/guards.sh` étendu: interdit `Response::json([... 'error'=> ...])` dans contrôleurs et routes mutantes hors allowlist

Commandes utiles
- Lancer les tests: `php tests/run-all.php`
- Guards CI: `bash tools/ci/guards.sh`

## Points restants (suivi)

- Règles moteur: traçage agrégations/no-op; déjà présent côté `RulesEngine` (ignore agrégations vides) — à documenter.
- DnD: seuil/indicateurs/feedback “drop interdit” — piste front (non traité ici).
- Pack "organisation": slots `item.badges`/`item.edit.tabs`/`board.settings.tabs` OK (chargement, datasets, ETag). Poursuivre UX (icônes/couleurs) si besoin.

## Gate R1 (porte unique) — statut

- [x] Aucune mutation hors `/api/commands` (autosave, import board, import règles migrés).
- [x] Front migré sur bus pour `importBoard` et `confirmAutosave`.

## Gate R2 (contrat HTTP) — statut

- [x] Erreurs via `Response::error` (middleware/DevAdmin/CommandController), `Content-Type` JSON.

## Contrôle de conclusion (audit R1→R12)

- [x] Mutations uniquement via `/api/commands` (hors admin) — grep routes OK.
  - Vérif: aucune route POST/PUT/PATCH/DELETE hors `/api/commands` et `/api/admin/*`: public/index.php
- [x] Réponses JSON canoniques — `Response::ok/error` partout (middleware, contrôleurs clés, bus).
- [x] Source de vérité snapshot v4 (MySQL only) — bootstrap bloque DSN SQLite; repo MySQL côté persistance.
- [x] Réacteur structurel — actif (flag dev), `type/list` idempotent, retrait refusé si enfants.
- [x] Applier couvrant — op inconnues rejetées (no‑op impossible).
- [x] Slots MCC — chargement via manifest, façade `renderSlot`, UI neutre.
- [x] UI — pas de `system:*` en clair (groupes/catégories OK).
- [x] Datasets — `GET /api/packs/datasets/:id` (ETag), mutations via bus `PackDataset.*`.
- [x] DnD — seuils/indicateurs/feedback conformes; drag items/listes/colonnes OK.
- [x] Admin isolé — endpoints admin conservés en REST, hors moteur des boards.
- [x] CSRF / rate‑limit — middlewares actifs, erreurs canoniques 401/403/429.
- [x] Normalisation parachute supprimée — plus de X‑Envelope, contrôleurs responsables.

Greps finaux
- Routes mutantes hors `/api/commands` (hors admin): OK (zéro).
- `Response::json(` résiduel dans contrôleurs cœur: OK (zéro utile).
- Legacy: `inbox|first.?list|/api/modules|ModuleRegistry|CreateList|MoveList|UpdateItem`: OK (pas dans le code actif; docs seulement).
- SQLite: zéro (seulement .gitignore / doc).
- Datasets REST mutateurs: zéro (GET seul présent).
- `system:` brut UI: clés internes/objets JS uniquement, pas d’affichage utilisateur.
## Notes

- `.env.audit-v4` conserve des flags historiques (`REST_MUTATIONS_FALLBACK`, `ENVELOPE_SMOKE`) non utilisés par le noyau; toléré pour debug.
- DnD: activationThreshold/longPress explicites; indicateurs unifiés; toasts “drop interdit”.
  - public/assets/apps/board/modules/dnd.js
