# 02-gestion – Spécifications techniques

Ce document traduit les objectifs fonctionnels du pack Gestion en décisions techniques :
modèle JSON dans le snapshot, tags système, règles d’invariants et points d’intégration
avec le moteur de commandes et de règles V4.

On reste volontairement au niveau design : rien n’est encore “figé” dans le code, mais
les choix décrits ici servent de garde‑fous pour l’implémentation.

## 1. Concepts techniques clés

### 1.1. États de base et états avancés

**Rappel fonctionnel :**

- États principaux exclusifs : `{todo, doing, done, failed}`.
- Flags complémentaires : `late`, `blocked`.
- Un item peut ne porter **aucun** état principal, ou **exactement un** parmi ce set ; il ne doit
  jamais en porter plusieurs en même temps.

**Décision technique : tags système `state/*`**

- Les états sont représentés par des tags système `state/*` déclarés dans un pack MCC Gestion.
- Au minimum :
  - `state/todo`
  - `state/doing`
  - `state/done`
  - `state/failed`
  - `state/late`
  - `state/blocked`
- Ces tags sont ajoutés/retirés via les commandes standard de tags (`AddTagV3Command`, `RemoveTagV3Command`).

**Invariants techniques :**

- **Unicité de l’état principal :**
  - Pour un node donné, l’ensemble `{state/todo, state/doing, state/done, state/failed}` doit contenir **au plus un** élément.
  - Avoir **zéro** état principal est autorisé (un item peut exister sans état explicite).
  - Toute opération qui conduirait à **plus d’un** état principal est refusée (`422 INVALID_STATE_COMBINATION`).
- **Combinaisons interdites / autorisées :**
  - Refusées :
    - `state/done` avec `state/blocked`, `state/failed` ou tout autre état principal,
    - `state/failed` avec `state/todo/doing/done`,
    - `state/blocked` ou `state/late` avec `state/done` ou `state/failed`.
  - Autorisées :
    - `state/blocked` et/ou `state/late` uniquement avec `state/todo` ou `state/doing`.
  - Le handler de tags (ou une règle Gestion) vérifie ces combinaisons et met en veto si interdit.
- **États avancés :**
  - `state/late`, `state/blocked` ne peuvent coexister qu’avec un état principal `todo/doing`.
  - Les incompatibilités supplémentaires (plus fines) peuvent être introduites dans des règles MCC,
    mais ne doivent jamais casser l’invariant “au plus un état principal”.

### 1.2. Progression numérique

**Rappel fonctionnel :**

- Chaque item a une progression effective et maximale, bornée à 10 000.
- Progression agrégée uniquement via Relations + Logique, jamais via les seuls enfants structurels ; agrégation V1 = readiness = `min` des dépendances fonctionnelles (`rel/depends-on` / `rel/blocks`).

**Décision technique : champs `ext.gestion.progress`**

- La progression est stockée dans un bloc d’extension dédié, par exemple :

  ```json
  {
    "ext": {
      "gestion": {
        "progress": {
          "effective": 0,
          "max": 100
        }
      }
    }
  }
  ```

- `effective` et `max` sont des entiers.
- `max` est dans `[0, 10_000]`, `effective` dans `[0, max]`.

**Invariants techniques :**

- Toute commande qui modifie `ext.gestion.progress` doit :
  - valider les bornes (`0 <= max <= 10_000`, `0 <= effective <= max`),
  - en cas de violation des bornes, refuser avec `422 INVALID_PROGRESS_RANGE`.
- Invariant sémantique minimal entre progression et état :
  - si un item possède **à la fois** un état `state/done` et un bloc `ext.gestion.progress` non nul,
    alors on exige `effective == max`.
  - toute commande qui aboutirait à `state/done` avec `effective != max`, ou qui modifierait la
    progression d’un item déjà `state/done` pour la mettre à une valeur différente de `max`,
    est refusée avec un code explicite (par ex. `422 INVALID_DONE_PROGRESS_COMBINATION`).
- Le noyau ne corrige **jamais** silencieusement ces incohérences : il refuse la commande et laisse
  à l’UI le soin d’envoyer des mises à jour cohérentes (par ex. commande composée qui met d’abord
  la progression à `max`, puis applique `state/done`).

**Agrégation :**

- Aucune agrégation “structurelle” n’est codée dans le modèle (pas de champ `aggregatedProgress` calculé
  à partir des enfants).
- La seule agrégation V1 est une **readiness fonctionnelle** :
  - `functionalProgress(node) = min(functionalProgress(dep))` sur les relations `depends=true` (`rel/depends-on` / `rel/blocks`),
  - calculée à la volée par un service de projection (runtime) à partir du graphe de Relations/Logic, jamais par la structure ; aucun champ persisté n’est la “vérité” de cette readiness.

### 1.3. Dates & échéances

**Rappel fonctionnel :**

- Un item peut avoir plusieurs échéances structurées, une par catégorie (`start`, `due`, `review`, `deadline`…).
- `state/late` doit être cohérent avec au moins une échéance dépassée.

**Décision technique : bloc `ext.gestion.deadlines`**

- Les échéances sont stockées sous forme d’objet indexé par catégorie :

  ```json
  {
    "ext": {
      "gestion": {
        "deadlines": {
          "start": { "at": 1735689600, "label": "Début prévu" },
          "due": { "at": 1736377200, "label": "À rendre" },
          "deadline": { "at": 1736463600, "label": "Date limite" }
        }
      }
    }
  }
  ```

- `at` est un timestamp en secondes (UTC).
- `label` est optionnel, purement descriptif.

**Invariants techniques :**

- Pour une catégorie donnée :
  - au plus une entrée (`deadlines[category]`),
  - aucune duplication sous un autre champ.
- Les commandes de mise à jour d’échéances :
  - créent ou remplacent l’entrée pour la catégorie concernée,
  - ne laissent jamais de structure invalide (pas de `deadlines = []` mélangé à des objets).
- Un tag `state/late` ne peut être appliqué que si au moins une échéance pertinente est présente et dépassée ; sinon veto ou warning configurable.
- Politique par board :
  - `manual-only` : aucune auto-application,
  - `auto-tag` : ajout automatique de `state/late` si `deadline` dépassée et pas `done`,
  - `eligible-only` : l’item est étiqueté “éligible” côté UI/Activity, sans appliquer le tag.

**Lien avec `state/late` :**

- La détection des retards est gérée par des règles :
  - soit des règles Gestion “standards” (pack MCC),
  - soit des règles Logique spécifiques au board.
- Côté technique :
  - une règle périodique (scheduler) ou dérivée des mutations compare `now` aux `deadlines.*.at`,
  - si `now > deadline.at` et que l’item n’a pas `state/done`, il est marqué comme “éligible à late”.
- L’application effective de `state/late` se fait :
  - soit via une règle auto (ajout du tag),
  - soit via un warning UI qui invite l’utilisateur à appliquer `late`.

### 1.4. Ressources matérielles

**Rappel fonctionnel :**

- Certains items représentent des ressources (eau, bois, argent, etc.) avec un type, une quantité, une unité.
- Les agrégations ne combinent que des ressources de même type et même unité.

**Décision technique : bloc `ext.gestion.resource`**

- Un item ressource possède un bloc d’extension :

  ```json
  {
    "ext": {
      "gestion": {
        "resource": {
          "resourceId": "resource/water",
          "unitId": "unit/litre",
          "quantity": 1200
        }
      }
    }
  }
  ```

- `resourceId` est un identifiant de ressource `resource/*` déclaré dans un dataset MCC.
- `unitId` est un identifiant d’unité `unit/*` déclaré dans un dataset MCC (ou `null`/absent si non agrégé).
- `quantity` est un entier non négatif (V1 n’autorise pas les décimales).

**Invariants techniques :**

- Si `ext.gestion.resource` est présent :
  - `resourceId` DOIT être non vide et commencer par `resource/`, et figurer dans le dataset MCC actif,
  - `quantity` DOIT être un entier >= 0 (aucun stock négatif par défaut),
  - `unitId` peut être nullable/omise ; si deux ressources sont agrégées, leurs `(resourceId, unitId)`
    DOIVENT être identiques.
- Les commandes “Gestion Ressource” (à définir) :
  - effectuent des opérations atomiques (`add`, `subtract`, `set`) sur `quantity`,
  - refusent de descendre sous 0 sauf si une règle Logique/board l’autorise explicitement.

**Agrégation de ressources :**

- Toute vue “stock global” ou “total par type” :
  - regroupe les items par `(resourceId, unitId)`,
  - somme `quantity` uniquement à l’intérieur d’un même groupe `(resourceId, unitId)` dont
    les identifiants proviennent des datasets MCC.
- Les ressources dont `resourceId` ou `unitId` ne figurent pas dans les datasets MCC peuvent être
  affichées individuellement, mais NE DOIVENT PAS être agrégées dans les vues “officielles”.
- Side‑effect : une règle ou un service peut calculer des synthèses dérivées dans un dataset séparé,
  mais le snapshot de base ne stocke pas un “total global” mélangeant des types ou des unités.

## 2. Points d’intégration

### 2.1. CommandBus & handlers

- La Gestion n’introduit pas de nouveau bus :
  - toutes les mutations passent par `POST /api/commands`.
- Les handlers concernés sont :
  - des handlers existants de tags (pour `state/*`),
  - de nouveaux handlers Gestion pour :
    - la mise à jour de `ext.gestion.progress`,
    - la mise à jour de `ext.gestion.deadlines`,
    - la mise à jour de `ext.gestion.resource`.
- Chaque handler :
  - valide les invariants décrits ci‑dessus,
  - émet des événements métier (ex. `ProgressUpdated`, `DeadlineChanged`, `ResourceAdjusted`)
    consommables par Logique/Interactions.

### 2.2. RulesEngine & packs MCC

- Le pack MCC Gestion :
  - déclare les tags système `state/*` et `resource/*`,
  - définit des règles génériques, par exemple :
    - empêcher les combinaisons d’états incohérentes,
    - imposer les transitions d’état standard (todo → doing → done) ainsi que la cohérence
      `state/done` ⇒ `effective == max` lorsque `ext.gestion.progress` est présent,
    - marquer les retards “simples” (si board configuré en auto‑late).
- Logique peut :
  - désactiver certaines règles par défaut,
  - les remplacer par des règles custom, dans le respect des invariants structurels.

### 2.3. UI & datasets

- Les datasets fournis au front :
  - listent les états `state/*` avec labels, couleurs, icônes,
  - exposent les catégories d’échéances (`start/due/review/deadline`) avec libellés,
  - exposent les types de ressources `resource/*` avec unités suggérées.
- L’UI :
  - utilise ces datasets pour construire :
    - le panneau Gestion (états, progression, échéances, ressources),
    - les filtres de board (retards, blocages, etc.),
  - ne crée jamais de logique implicite à partir des champs `ext.*` sans règle explicite.

## 3. Flux types

### 3.1. Mise à jour de progression sur une tâche

1. L’utilisateur modifie la progression effective/maximale dans le panneau d’édition.
2. Le front envoie une commande `Gestion.SetProgress` avec :
   - `boardId`, `nodeId`,
   - `effective`, `max`.
3. Le handler :
   - vérifie les bornes (`0 <= max <= 10_000`, `0 <= effective <= max`),
   - vérifie, si l’item possède déjà `state/done`, que `effective == max` (sinon refuse avec
     `INVALID_DONE_PROGRESS_COMBINATION`),
   - met à jour `ext.gestion.progress` si les invariants sont respectés,
   - émet un événement `ProgressUpdated`.
4. Si l’UX souhaite offrir un raccourci “Marquer comme terminé” :
   - l’UI envoie une **séquence de commandes cohérente** (par ex. d’abord `Gestion.SetProgress` avec
     `effective = max`, puis une commande de tag pour appliquer `state/done`),
   - chaque commande isolée reste conforme aux invariants noyau.
5. Des règles Logique supplémentaires peuvent :
   - sur‑spécifier ce comportement (alertes, validations supplémentaires),
   - déclencher des actions Interactions à partir de ces changements cohérents.

### 3.2. Marquage automatique d’un retard

1. Un scheduler déclenche une règle quotidienne “CheckDeadlines(boardId)”.
2. La règle parcourt les nodes avec `ext.gestion.deadlines.deadline` non null.
3. Pour chaque node :
   - si `now > deadline.at` et absence de `state/done`,
   - appliquer ou proposer `state/late` selon la configuration du board.
4. En cas d’application :
   - une commande standard de tag est émise (`AddTagV3Command` pour `state/late`),
   - les invariants d’états sont re‑vérifiés.

### 3.3. Ressource consommée par une action

1. Une action Interactions est exécutée avec un effet “consommer 10 unités de telle ressource”.
2. L’effet se traduit par une commande `Gestion.AdjustResource` :
   - `boardId`, `nodeId` de l’item ressource,
   - `delta = -10`.
3. Le handler :
   - lit `ext.gestion.resource`,
   - vérifie que `quantity + delta >= 0` (ou laisse la règle décider),
   - met à jour `quantity`,
   - émet un événement `ResourceAdjusted`.

## 4. Questions ouvertes / risques

- **Performance sur gros boards :**
  - les règles de retard et de progression agrégée doivent éviter de parcourir tout le board
    à chaque mutation ; privilégier les évaluations incrémentales ou périodiques.
- **Rétro‑compatibilité :**
  - les boards existants sans `ext.gestion.*` doivent être considérés comme valides ;
    les handlers doivent initialiser prudemment les blocs manquants.
- **Types de ressources & unités :**
  - le catalogue des ressources (`resource/*`) et des unités (`unit/*`) DOIT être centralisé dans
    des datasets MCC ; seules les ressources dont `(resourceId, unitId)` figurent dans ces datasets
    sont agrégées dans les vues standards.
  - les extensions de ce catalogue (nouveaux types/unité) sont possibles, mais toujours via MCC,
    pas via des chaînes libres arbitraires dans le snapshot.
