# Audit V4 — 17/10/2025 (Finale)

## Résumé exécutif
- **Porte d’entrée mutante encore duale** : 39 routes REST mutantes restent actives côté serveur (boards, files, packs, admin…) et 15 appels front les consomment directement, maintenant un flux parallèle au bus `/api/commands`.【F:public/index.php†L215-L315】【F:public/assets/packages/services/files.js†L3-L108】【F:reports/audit-v4/traces/r1_routes_mutations.txt†L1-L39】【F:reports/audit-v4/traces/r1_front_mutations.txt†L1-L15】
- **Contrat JSON ≠ natif, observabilité séparée** : 90 réponses d’erreur issues de 14 contrôleurs reposent sur des champs `error`/`status` sans `code`, tandis que la normalisation continue d’ajouter `X-Envelope-Normalized` et de logger `RESPONSE_NORMALIZED` côté serveur.【F:reports/audit-v4/traces/r2_contract_gaps.txt†L1-L90】【F:src/Infrastructure/Http/Response.php†L16-L70】【F:reports/audit-v4/traces/r12_observability.txt†L1-L2】
- **Arbitre conditionnel & slots prescripteurs** : le flag `STRUCTURE_TAG_REACTIONS` reste optionnel et les slots MCC pilotent structure, fichiers et datasets via services REST (`sendCommandRef`, `requestJson`, `registries.datasets.patch`).【F:bootstrap.php†L72-L88】【F:src/Application/Services/StructureTagReactor.php†L13-L86】【F:reports/audit-v4/traces/r6_slots_logic.txt†L1-L16】【F:reports/audit-v4/traces/r8_datasets_flow.txt†L1-L9】
- **Applier partiel, temps mixtes** : la matrice `matrix-applier-ops.csv` confirme que seules les opérations v3 historiques sont supportées, toute opération inconnue tombant dans `default => $state`, alors que la persistance continue d’utiliser des millisecondes côté révision.【F:reports/audit-v4/matrix-applier-ops.csv†L1-L13】【F:src/Domain/Boards/BoardStateApplier.php†L20-L60】【F:src/Infrastructure/Persistence/MySqlBoardRepository.php†L150-L152】

## Synthèse par règle
| Règle | Statut | Commentaire |
|-------|--------|-------------|
| R1 | À RÉPARER | 39 routes POST/PUT/PATCH/DELETE hors `/api/commands` + 15 appels front REST. |
| R2 | À RÉPARER | 14 contrôleurs renvoient `error`/`status` au lieu de `code` natif. |
| R3 | À RÉPARER | Horodatages mixtes (s/ms) et applier permissif. |
| R4 | À RÉPARER | Réacteur structurel optionnel, clé `type/list` dispersée. |
| R5 | À RÉPARER | Matrice confirme absence de garde pour ops inconnues. |
| R6 | À RÉPARER | Slots MCC orchestrent structure, tags, fichiers via REST. |
| R7 | À RÉPARER | L’UI référence `system:*` directement. |
| R8 | À RÉPARER | Dataset `org:categories` manipulé côté client sans validations. |
| R9 | OK | DnD atomique via commandes `Move*`. |
| R10 | OK | Endpoints admin isolés. |
| R11 | OK | CSRF & rate-limit → enveloppes JSON canoniques. |
| R12 | À RÉPARER | Normalisation automatique encore active (en-tête + log). |

## Détails par règle
### R1 — Porte d’entrée mutations unique
- **Constat** : 39 routes mutantes hors `/api/commands` couvrent boards, fichiers, packs, admin, notifications…【F:reports/audit-v4/traces/r1_routes_mutations.txt†L1-L39】 Le front conserve 15 appels `requestJson`/`fetch` mutateurs (logout, files, datasets, account…).【F:reports/audit-v4/traces/r1_front_mutations.txt†L1-L15】
- **Impact** : **S (élevé)** — deux moteurs concurrents, invariants contournés.
- **Preuves** : `traces/r1_routes_mutations.txt`, `traces/r1_front_mutations.txt`.
- **Recommandation** : migrer chaque route vers une commande dédiée (`Board.Create`, `File.Compose`, `Dataset.Upsert`), couper les services REST côté front et rediriger tous les intents vers `/api/commands`.

### R2 — Enveloppe JSON canonique
- **Constat** : 90 occurrences d’`error`/`status` couvrent 14 contrôleurs (`BoardController`, `UserFilesController`, `AccountController`, admin…). Aucun ne publie `code`/`message` stables, la MCC dépend donc de la normalisation.【F:reports/audit-v4/traces/r2_contract_gaps.txt†L1-L90】
- **Impact** : **M** — impossibilité de mapper les erreurs métier (toast, retry, i18n) et d’arrêter la normalisation.
- **Preuves** : `traces/r2_contract_gaps.txt`.
- **Recommandation** : introduire `ResponsePayload::success/error` ou équivalent, ajouter `code`/`message` explicites (ex. `BOARD_CONFLICT`, `FILE_CONFLICT`, `ACCOUNT_TOKEN_REQUIRED`), supprimer `status`.

### R3 — Snapshot JSON & politique temps
- **Constat** : `BoardStateApplier` timestamp en secondes (`time()`), la persistance MySQL incrémente `revision` en millisecondes (`microtime(true)*1000`), et les slots org utilisent `nowSeconds()` pour bricoler les props fichiers.【F:reports/audit-v4/traces/r3_time_usage.csv†L1-L144】【F:src/Infrastructure/Persistence/MySqlBoardRepository.php†L150-L152】【F:public/assets/packs/org/ui/editor-tabs.js†L17-L120】 L’applier accepte toute opération inconnue (`default => $state`).【F:src/Domain/Boards/BoardStateApplier.php†L33-L60】
- **Impact** : **M** — dérive temporelle et impossibilité de détecter les patches illégitimes.
- **Recommandation** : fixer la politique “secondes partout”, version-gater sur `revision`, lever une exception `UNKNOWN_OPERATION`.

### R4 — Arbitre & réacteur structurel
- **Constat** : le flag `STRUCTURE_TAG_REACTIONS` reste désactivable ; à `false`, la structure dépend des slots (`board-structure.js` force `sys.shape`, `type/list`).【F:bootstrap.php†L72-L88】【F:src/Application/Services/StructureTagReactor.php†L13-L86】【F:public/assets/apps/board/modules/board-structure.js†L112-L188】 Les occurrences `type/list` sont dispersées UI/packs.【F:reports/audit-v4/traces/r4_structural_tags.txt†L1-L9】
- **Impact** : **S** — invariants structurels non garantis, rollback incertain.
- **Recommandation** : rendre le réacteur obligatoire, déclarer une clé système unique (`system:list`) gérée par l’arbitre, documenter la strate Système > Packs > Utilisateur.

### R5 — Applier couvrant & zéro no-op
- **Constat** : la matrice `matrix-applier-ops.csv` montre que seules les opérations v3 (`node.*`, `tag.*`, `column.rename`, `workspace.rename`, `tagFilter.set`) sont supportées. Toute autre opération tombe dans `default => $state` sans log.【F:reports/audit-v4/matrix-applier-ops.csv†L1-L13】【F:src/Domain/Boards/BoardStateApplier.php†L30-L38】
- **Impact** : **M** — les packs/handlers peuvent accepter silencieusement des patches inconnus.
- **Preuves** : `traces/r5_applier_coverage.txt`, `matrix-applier-ops.csv`.
- **Recommandation** : lever une exception sur les ops non mappées, compléter l’applier (props, datasets, attachments…), ajouter des tests contractuels.

### R6 — UI neutre (slots MCC)
- **Constat** : 16 appels `sendCommandRef`/`requestJson` dans les modules MCC pilotent structure, tags système, datasets, y compris `requestJson('/api/packs/datasets/…')` et `sendCommandRef('CreateNode')`.【F:reports/audit-v4/traces/r6_slots_logic.txt†L1-L16】【F:public/assets/apps/board/modules/board-structure.js†L112-L214】【F:public/assets/apps/board/modules/tags.js†L53-L848】
- **Impact** : **S** — logique métier en UI, divergence avec l’arbitre/packs.
- **Recommandation** : encapsuler ces intentions dans des commandes (`List.Create`, `Tag.Resolve`, `Dataset.Update`), limiter les slots au rendu.

### R7 — Tags système catégorisés
- **Constat** : l’UI manipule `system:*`/`type/list` en clair pour filtrer/afficher les badges.【F:public/assets/apps/board/modules/ui/board-view.js†L162-L244】【F:public/assets/apps/board/modules/tags.js†L405-L606】【F:reports/audit-v4/traces/r7_system_tag_refs.txt†L1-L25】
- **Impact** : **M** — dépendance forte aux clés brutes, impossibilité de plugger d’autres packs.
- **Recommandation** : exposer des helpers pack → UI (`resolveTypeTag`, `listCategories()`), ne plus afficher `system:*`.

### R8 — Pack “organisation” (datasets, ETag)
- **Constat** : 5 appels front (`registries.datasets.patch/get`) pilotent entièrement `org:categories`, tandis que le contrôleur `PacksController` accepte PUT/PATCH/DELETE sans validations slug/ETag spécifiques.【F:reports/audit-v4/traces/r8_datasets_flow.txt†L1-L9】【F:public/assets/packs/org/ui/settings-panel.js†L74-L135】【F:src/Interfaces/Http/Controllers/PacksController.php†L181-L253】
- **Impact** : **M** — risques de collisions, absence d’autorisations dédiées.
- **Recommandation** : introduire des commandes `PackDataset.Upsert/Delete`, valider unicité slug, vérifier l’ETag côté moteur et retourner des codes (`DATASET_CONFLICT`, `DATASET_INVALID`).

### R9 — DnD atomique
- **Constat** : les interactions DnD utilisent `sendCommandRef('MoveNode'|'MoveColumn')`, aucun accès REST direct.【F:public/assets/apps/board/modules/dnd.js†L92-L577】【F:reports/audit-v4/traces/r9_dnd_handlers.txt†L1-L126】
- **Impact** : **Faible** — conforme.
- **Recommandation** : garder les tests E2E/observabilité.

### R10 — Séparation Admin / Boards
- **Constat** : `/api/admin/*` reste servi par des contrôleurs dédiés (UserAdmin, LicenseAdmin…) sans appel au moteur board.【F:public/index.php†L242-L262】【F:src/Interfaces/Http/Controllers/Admin/UserAdminController.php†L12-L90】【F:reports/audit-v4/traces/r10_admin_endpoints.txt†L1-L36】
- **Impact** : **Faible** — séparation maintenue.
- **Recommandation** : prévoir des commandes admin spécifiques lors de la migration.

### R11 — CSRF & rate-limit
- **Constat** : le middleware CSRF impose `X-CSRF-Token`, renvoie `CSRF_*`; la rate-limit produit `RATE_LIMITED`, et le client vérifie `Content-Type` avant parsing.【F:src/Interfaces/Http/Middleware/CsrfMiddleware.php†L12-L37】【F:src/Interfaces/Http/Middleware/RateLimitMiddleware.php†L12-L34】【F:public/assets/packages/services/http.js†L10-L96】【F:reports/audit-v4/traces/r11_csrf_rate.txt†L1-L11】
- **Impact** : **Faible** — conforme.
- **Recommandation** : maintenir les greps anti-legacy.

### R12 — Hygiène & observabilité
- **Constat** : la normalisation ajoute toujours `X-Envelope-Normalized` et logue `RESPONSE_NORMALIZED`, preuve que R2 n’est pas corrigée.【F:reports/audit-v4/traces/r12_observability.txt†L1-L2】【F:src/Infrastructure/Http/Response.php†L16-L70】
- **Impact** : **M** — bruit dans les logs, difficile de détecter les vrais écarts.
- **Recommandation** : une fois R2 corrigée, retirer la normalisation et ses logs.

## Annexes
### A. CommandBus ↔ Commandes front
- **Handlers serveur** : `CommandController` mappe `CreateNode`, `UpdateNode`, `MoveNode`, `DeleteNode`, `AddTagV3`, `RemoveTagV3`, `CreateColumn`, `RenameColumn`, `MoveColumn`, `CreateWorkspace`, `RenameWorkspace`, `UpdateTagFilter`.【F:src/Interfaces/Http/Controllers/CommandController.php†L95-L134】
- **Appels front** : `board-structure.js`, `dnd.js`, `tags.js` s’appuient sur `sendCommandRef/execute` pour les intentions structurelles, tandis que les services REST couvrent boards/files/datasets (`traces/r1_front_mutations.txt`, `traces/r6_slots_logic.txt`).

### B. Inventaire mutations REST
- Listing exhaustif : `traces/r1_routes_mutations.txt` (serveur) et `traces/r1_front_mutations.txt` (client) — inclut boards, files, packs, admin, notifications.

### C. Politique temps & applier
- `traces/r3_time_usage.csv` + `matrix-applier-ops.csv` servent de base aux migrations (secondes partout, couverture applier).

### D. Packs / datasets organisation
- Flux complet UI → REST → stockage dans `traces/r8_datasets_flow.txt`; validations slug/version/autorisation absentes dans `PacksController`.【F:src/Interfaces/Http/Controllers/PacksController.php†L181-L253】
