Historique du projet (V2→V4) + Plan Pack Progression

⚠️ MODE DEV STRICT (pas PROD) — 0 migration depuis l’ancien, 0 rétro‑compat, 0 fallback, 0 zone grise. Une règle violée ⇒ pas de merge.

0) Objectif du document

Donner une vue chronologique des décisions pour comprendre l’origine de chaque choix.

Servir de référence rapide quand un agent tombe sur un comportement “bizarre” → retrouver pourquoi il existe (ou pourquoi il a été supprimé).

Définir le plan d’atterrissage vers un noyau stable + premier pack ‘Progression’ proprement branché.

1) Chronologie haute (phases)

Phase V2 — Héritage à éradiquer (passé)

Listes en entités dédiées (API/handlers séparés) ⇒ double logique.

Heuristiques UI (ex. inbox auto, “première liste”).

Métier dispersé (UI/registre/services), REST hétérogène.

Phase V3 — Transition conceptuelle (passé)

Tout est un node ; une liste = item taggué système type=list (skin, pas structure).

Opérations génériques node.* / tag.* ; invariants : parent unique, ordre exact, pas de cycles.

Coexistence de flux (REST direct vs commandes), deux systèmes de slots, restes d’heuristiques.

Phase V4 — Socle unifié (cap actuel, non négociable)

Une seule porte d’entrée : POST /api/commands (CommandBus).

Moteur logique unique (arbitre par strates Système > Packs > Utilisateur).

Source de vérité : snapshot JSON v4 versionné ; Apply = tout ou rien.

Full JSON (succès & erreurs) ; UI neutre (intentions + rendu).

Packs MCC : tags système catégorisés, datasets, règles, slots UI — aucun métier fondu dans le noyau.

Note historique importante : l’invariant “inbox en tête” a été retiré en V4 (héritage V2) ; la structure découle uniquement de node.sys.shape + ordre explicite.

2) Journal des sessions (résumé chronologique)

Session A — Consolidation V4 (Pass 1 → Pass 2)

Porte unique : toutes les mutations passées via /api/commands ; suppression du flux REST ad hoc.

Contrat HTTP : Full‑JSON (jamais de 204 si body attendu) ; client vérifie Content‑Type et remonte COMMAND_RESPONSE_INVALID si non‑JSON.

Applier : couverture des opérations (workspace.rename, column.rename, tagFilter.set, node.*, tag.*).

DnD : seuil de drag, indicateurs drop-indicator--{item|list|column}, previews/drops recâblés, feedback drop interdit.

Slots UI (Option A) : renderSlot() avec { state, item, commands, datasets, tags }; gating strict des packs.

Éditeur : retour à la modale (clic header, overlay, focus‑trap, Esc).

Tags / Pack “organisation” : picker groupé par catégories système ; aucun system:* en UI ; Réacteur structurel aligné sur type/list (idempotent, OFF par défaut en prod).

Nettoyage : retrait de panneaux Relations/Progression hardcodés du noyau ; CTA “+ nouveau board”.

Session B — Normalisation HTTP & Sécurité

Response helpers : Response::ok/error → enveloppe { ok, code, message } partout ; sérialisation robuste.

CSRF / Rate‑limit : erreurs enveloppées cohérentes avec les statuts ; parsing homogène côté client.

Licences/activation : codes/messages unifiés.

404 & packs : HttpKernel 404 + /api/packs/datasets/:id renvoient NOT_FOUND / DATASET_NOT_FOUND enveloppés ; gestion ETag.

Règles : l’exécuteur ignore les agrégations sans effet ; chemins imbriqués sûrs ; traçabilité claire.

Tests passés : CsrfMiddlewareTest, RateLimitMiddlewareTest, BoardStateTest, BoardStateApplierTest, RulesEngineTest.

Session C — Élagage legacy & MCC

Suppression : DynamicModuleRulesProvider, ModuleRegistry, OperationRegistry, /api/modules, public/modules/.

MCC : mcc.schema.json, MccValidator, PackLoader, SystemTagRegistry, UiSlotRegistry, DatasetStore, RulesCatalog.

Endpoints /api/packs/ : enabled, slots, manifest, config, datasets/:id.

Pack org : tags state/{todo,doing,done}, type/{note,image,list}, categories (dataset), règles d’exclusivité.

Front : fetchModules() → fetchPacks() ; loadPackUIModules() → loadPackSlots() ; ESM contributions.

Question UI : Option A confirmée (manifest MCC → composant ESM qui s’enregistre via registerSlotContribution).

Session D — V3 vs V2 : clarification / clôture

V3 : listes = nodes tagués type=list ; opérations node.*/tag.* ; colonnes restent dédiées ; gate version < 3 refusée.

Front : rendu piloté par node.sys.shape ; “+” d’une colonne ajoute toujours à la racine ; import/export JSON OK.

CI/Docs : grep anti‑terme “inbox” ; checklist merge.

3) Fonctionnement des Packs (vérification de couverture)

3.1 Principes couverts

Le noyau n’embarque aucun pack ; il sait distinguer tag simple / tag système, expose des points d’entrée UI (badges, onglets, panneaux), et un moteur unique (arbitre + applier).

Les packs déposent :

Tags système rangés par catégories (ex. states, categories, types, …). Les packs créent la catégorie absente si besoin.

Datasets (ex. table catégorie → icône/couleur).

Règles (non éditables par défaut, toggleables dans Config générale).

Slots UI (composants ESM via manifest).

Dégradation gracieuse : sans un pack, ses tags retombent en tags simples (rien ne casse).

Jamais de system:* en clair dans l’UI ; le Picker regroupe par catégories système.

3.2 Réacteur structurel et “Type”

type est une catégorie de tags système (ex.: type/note, type/image, type/list).

Seul type/list est structurel par défaut :

TagAdded(system:list) ⇒ sys.shape=container (idempotent) ;

TagRemoved(system:list) ⇒ sys.shape=leaf si pas d’enfants, sinon 422.

Réacteur piloté par STRUCTURE_TAG_REACTIONS (ON dev/tests, OFF prod).

3.3 Packs prévus (après noyau)

Organisation : états {todo,doing,done}, catégories (thématiques/couleurs), type/{note,image,list} ; dataset icône/couleur ; panneau Config générale.

Progression : tag système de progression (points/poids/barre), règles d’affichage/validation.

Relations : relation entre items ; prévention des cycles via moteur (invariant).

Logique (utilisateur) : règles utilisateur (déclencheurs/conditions/actions) sur même moteur.

✅ Couverture documentaire : OK. Les parties ci‑dessus correspondent à ce qui est déjà écrit et à ce qu’on a convergé pendant les sessions.

4) DnD — exigences d’UX (rappel consolidé)

Drag d’un item d’une colonne à l’autre ; drag intra‑liste ; drag d’une liste (container) dans une autre liste (déplace son contenu).

Indicateurs visuels : drop-indicator--item|list|column ; feedback explicite sur “drop interdit”.

Le moteur décide du patch ; l’UI ne déduit aucune structure implicite.

5) Zones grises supprimées / ambigüités résolues

Inbox / première liste : supprimé ; pas d’heuristiques structurelles.

“Fallback” : remplacé par “valeur par défaut” (docs & code).

“Whitelist” : remplacé par “liste d’autorisation des routes”.

Normatif : usage de DOIT/NE DOIT PAS (RFC 2119/8174) ; proscrire “DEVRAIT” dans les règles.

Docs : bannière DEV STRICT en tête de toutes les pages.

6) Plan d’atterrissage — Noyau stable + Pack “Progression” (v1)

6.1 Étapes noyau (obligatoires)

Applier couverture totale (toutes ops exposées par les handlers).

Invariants ré‑testés : parent unique, ordre sans trous, acyclicité, leaf sans enfants.

Contrat HTTP vérifié (schema + Content‑Type), middleware catch‑all actif.

Réacteur : ON en dev/tests, OFF en prod ; tests idempotence/échec si enfants.

CI blocante : greps anti‑legacy (inbox, modules, REST mutantes boards), bannière DEV STRICT, Response::json(['status'…])/['error'…] interdits.

6.2 Pack “Progression” (MVP fonctionnel)

Tags système : progress/<scale> (p.ex. progress/1, progress/2, …, progress/5) ou un champ progress.points (à trancher). Recommandation : tags discrets (plus simple pour filtres & règles).

Catégorie : progress dans categories de tags système (créée si absente).

Dataset : mapping progress → icône/barre (ex. 1..5 → largeur/aria‑label).

Règles :

exclusivité progress/* (un seul niveau à la fois) ;

éventuelle auto‑normalisation (remplace progress/3 par progress/4 si commande set(4)).

Slots UI :

item.badges → badge + barre ;

item.edit.tabs → onglet “Progression” (picker niveaux + aide).

Dégradation : sans le pack, les tags progress/* deviennent simples (pas de casse, pas d’UI spécifique).

Tests :

ajout/remplacement progress/* (exclusivité) ;

rendu badge/barre ;

filtres ET avec state/* ;

non‑régression invariants (aucune transformation structurelle).

6.3 Critères d’acceptation (merge gate)

100% des mutations boards via /api/commands (grep CI).

Enveloppe JSON valide (tests schema) succès/erreur.

system:* jamais en clair dans l’UI (grep CI + test UI).

Réacteur : idempotence OK ; TagRemoved(system:list) échoue si enfants (test).

Pack Progression : règles d’exclusivité OK ; dataset chargé ; UI visible seulement si pack activé.

7) FAQ rapide pour agents (onboarding express)

Q: Puis‑je créer un endpoint REST pour une mutation boards ?R: Non. Uniquement POST /api/commands.

Q: Je dois afficher “system:*” pour debug UI ?R: Non. Utilise badges/groupes ; jamais system:* en clair.

Q: Puis‑je migrer progressivement en gardant un peu de legacy ?R: Non. Mode DEV STRICT : zéro rétro‑compat / zéro fallback.

Q: Le tag type/list casse sans le pack ?R: Non. Sans pack, il redevient tag simple, et le réacteur est OFF en prod.

8) Checklists (copier dans PRs)

8.1 PR Noyau



8.2 PR Pack Progression



9) Éléments explicitement legacy (pour éviter les confusions)

“Inbox en tête” (héritage V2) → supprimé en V4.

Registres/Modules historiques (ModuleRegistry, OperationRegistry, /api/modules) → supprimés.

Mutations REST boards hors bus → supprimées (grep CI bloque toute réintroduction).

10) Références rapides

CommandBus → POST /api/commands

Contrat HTTP → enveloppe { ok, … } / { ok:false, code, message, details? }, Content‑Type: application/json; charset=utf‑8

Réacteur → TagAdded/Removed(system:list) ; flag STRUCTURE_TAG_REACTIONS

Packs → tags système catégorisés, datasets, règles, slots ; dégradation gracieuse sans pack
## 2025-10-21 — Cutover Notifications V4 (Activité + Notifications riches)

Objectif: livrer un système propre et aligné V4, en séparant clairement Activité (événements boards) et Notifications riches (éditorial admin), supprimer l’héritage legacy et garantir un contrat HTTP/JSON canonique.

Décisions clés
- Cutover net: abandon complet de l’ancien système de notifications (code, routes, tables)
- Lecture en GET: `/api/activity`, `/api/notifications/rich` (+ `/:id`)
- Mutations: uniquement via `/api/commands` (handlers NonBoard)
- Admin: endpoints dédiés pour catégories et notifications riches (whitelist)
- Sécurité: rendu riche via iframe `srcdoc` sandbox (sans `allow-same-origin`), CSP stricte, `img-src` basé sur l’origin
- Zéro logique métier en UI; pas d’API de pack; compat strict V4

Backend livré
- Tables: `activity_events` + `activity_user_state` (status généré); `notification_categories`, `notifications_rich`, `notification_user_state`
- Handlers NonBoard: `Activity.MarkSeen`, `NotificationRich.MarkSeen`, `Notification.Archive`, `Notification.ArchiveByType`, `Notification.DeleteForUser`
- Contrôleurs/Services lecture: ActivityReadService + NotificationRichReadService
- Admin (CRUD): catégories + notifications riches

Front livré
- Modale “Notifications riches” (cloche):
  - Onglets par catégories avec badge non lues
  - Liste (droite), aperçu (gauche) en iframe `srcdoc`
  - Actions: marquer vu, archiver item, archiver catégorie, Historique, supprimer en Historique
- Overlay “Activité” (icône ⟳): affichage des événements récents, auto mark-seen
- Admin: Vue Notifications refondue (onglets catégories, formulaire HTML/CSS/JS, preview inline `srcdoc`)

Nettoyage legacy
- Code supprimé: services/contrôleurs legacy (user & admin), handlers NonBoard legacy MarkSeen/Dismiss, UI overlay legacy
- Routes retirées: `/api/notifications`, `/api/admin/notifications*`
- Tables legacy: `notifications`, `notification_seen`, `notification_dismissed` — supprimées à la main (DROP TABLE) après cutover

Changement de cap: suppression totale du “layout”
- Le champ `layout` (ex "full-text" / "side-image") a été retiré du modèle, des services et de l’UI
- Justification: l’éditeur HTML/CSS/JS donne toute la liberté nécessaire; le “layout” n’apportait qu’un preset cosmétique
- Impact: simplification du schéma et du code; le rendu correspond exactement au HTML/CSS/JS saisis (toujours en iframe `srcdoc` avec CSP)

Contrat V4 — conformité
- Mutations: bus unique `/api/commands` (NonBoard); admin `/api/admin/*` (liste blanche)
- Réponses: `Response::ok/error` (enveloppe canonique JSON)
- UI: aucune logique métier, pas d’endpoints packs
- Sécurité: CSRF, rate-limit, CSP stricte pour le rendu riche

Étapes futures envisagées
- Abonnements/push par catégorie (scheduler), sans toucher au modèle d’état
- Édition admin plus riche (modale d’édition avec preview), snippets de gabarits HTML/CSS optionnels
- Ajustement CSP si assets servis sur un domaine statique distinct

