Lot 0 bis, séquence L0-3 — Manifestes et gardes des modules
02/10/2026. Mise en œuvre de la séquence L0-3 du plan d’alignement, d’après la note d’amendement (AM-05). Décisions : EC-092 (précisé), EC-095 (en partie), EC-096, EC-097.
- Chaque module a un manifeste dans le code : ses permissions, ses commandes, ses routes d’API et ses écrans. Chaque élément appartient à exactement un module, et les tests le vérifient.
- Un module désactivé passe en lecture seule. L’API laisse lire ses données et refuse toute écriture (409
module_inactif). - Un fait du terrain n’est jamais rejeté. Une commande d’un module inactif à sa date d’opération est conservée et mise en contrôle
module_inactif, sans écriture métier. - Le back-office masque les écrans d’un module jamais activé et marque « Lecture seule » ceux d’un module désactivé.
- La synchronisation filtrée par module est faite en L1-5 (page).
Manifestes
Section intitulée « Manifestes »packages/domain/src/modules/manifestes.ts est la source de vérité. Les tables app.module, app.module_dependance et app.module_activite la reflètent ; un test d’intégration compare les deux.
| Module | Permissions | Commandes | Routes et écrans |
|---|---|---|---|
noyau |
utilisateurs, appareils, ensembles, paramétrage, modules.manage, review.resolve, dashboard.view |
– | Synchronisation, identité, campagnes, produits, numérotation, certificats, conformité, contrats, activités et modules, file de contrôle |
membres_foncier |
producer.*, plot.*, capacity.approve |
producteur.*, parcelle.* |
Groupements, producteurs (avec fusion), parcelles (avec polygone), capacités (avec validations) |
collecte (requiert membres_foncier) |
collection.*, receipt.reprint, delivery.issue_note |
collecte.*, recu.reimprimer, livraison.etablir_bon |
– |
sci (requiert membres_foncier) |
inspection.record, nonconformity.report, harvest.estimate |
inspection.enregistrer, non_conformite.constater, recolte.estimer |
– |
intrants_avances |
input.issue |
intrant.remettre |
– |
paiements (requiert collecte), planification, reception, preparation, commercialisation, eudr |
– | – | – |
Les identifiants de permission restent en anglais : leur préfixe désigne un domaine, pas le code du module (EC-092 précisé).
Garde de l’API
Section intitulée « Garde de l’API »GardeModule (apps/api/src/auth/module.ts) se place après la garde de permission, sur les contrôleurs du back-office et de l’identité.
- La route est retrouvée dans le registre par sa méthode et son gabarit, puis rattachée à son module par le manifeste. Une route hors registre ou hors manifeste est une erreur de programmation.
GETest toujours permis. Une écriture sur un module inactif reçoit 409{ erreur: "module_inactif", details: { module } }, avant tout autre contrôle du service.- Le noyau n’est jamais gardé.
Garde des commandes (EC-096)
Section intitulée « Garde des commandes (EC-096) »- Au traitement d’une commande, après le contrôle des droits (étape 9), le serveur lit l’état du module à la date d’opération (
app.module_actif_a). - Module inactif : motif
module_inactif, rangé parmi les motifs sans écriture. La commande est conservée, la séquence avance, aucune entité n’est créée. - Levée :
reprise_manuelle, par le support ou le RSCI, commedonnees_illisibles. - Un type de commande hors catalogue (le type technique de test) relève du noyau. Un gestionnaire ne peut pas changer le module d’un type du catalogue.
Navigation et permissions
Section intitulée « Navigation et permissions »GET /v1/moirendmodules: les modules actifs, noyau compris, et ceux qui ont été désactivés (lecture_seule). Un module jamais activé est absent.navigationVisible(permissions, modules)masque les écrans d’un module absent et marque « Lecture seule » ceux d’un module désactivé.GET /v1/permissionsrend aussipar_module, avec l’état de chaque module. Les ensembles par défaut ne changent pas : une organisation neuve n’a aucun module actif, et l’on ne vide pas ses ensembles.
Migration
Section intitulée « Migration »db/migrations/20261002110000_lot0bis_l03_gardes.sql :
- dépendance
collecte → membres_foncier, refusée si une organisation acollecteactif sansmembres_foncier; - fonction
app.module_actif_a(organisation, module, instant); - motif
module_inactifdans la contrainte desync.controle. La descente est refusée si un tel contrôle existe.
Contrat d’API 1.11.0 : modules dans Moi, par_module dans le catalogue des permissions, 409 module_inactif sur les écritures de Membres et foncier.
| Test | Ce qu’il vérifie |
|---|---|
packages/domain/src/modules/manifestes.test.ts |
Chaque permission et chaque type de commande dans exactement un module ; codes uniques ; dépendances existantes et sans cycle ; rattachements EC-095 et EC-097 |
| VT-MOD-016 à 021, VT-CTRL-018 et 019 | Rattachements ; décision et effet bloquant de module_inactif |
apps/api/src/openapi.test.ts |
Chaque route du registre appartient à un manifeste, et chaque route d’un manifeste existe |
db/tests/l0bis-3.sql |
Dépendance collecte → membres_foncier appliquée par la base ; motif module_inactif accepté |
apps/api/test/gardes-modules.test.ts |
Concordance des tables et des manifestes ; lecture permise et écriture refusée sur un module inactif ; moi et par_module ; commande en contrôle module_inactif sans écriture ; état apprécié à la date d’opération ; commande du noyau non concernée |
apps/backoffice/src/mise-en-page/navigation.test.ts |
Écrans masqués sans module, visibles en lecture seule après désactivation |
Le monde de test de l’API active Membres et foncier par défaut, comme le supposent les tests des producteurs, parcelles et capacités ; creerMonde({ modules: [] }) donne une organisation sans module.
Non vérifié dans le navigateur : la connexion au back-office de bout en bout n’est pas encore montée (aucun compte de développement dans Keycloak, réserve du journal).