Paramètres contextuels et résolution multi-facteurs
Version 1, 29 septembre 2026. Décisions PC-01 à PC-08 validées le 30/09/2026. Complète les pages « Paquet de règles packages/domain » et « Schéma de la base — socle ». Précise les décisions « Paquets filières » et « Paramétrage » du découpage, ainsi que EC-004, EC-007 et EC-011.
- Une formule est du code, écrit une fois dans
packages/domain. Ses valeurs (seuils, références, tolérances) sont des données, dans les packs et les paramètres. - Une valeur n’est pas un simple chiffre, mais une liste de règles avec conditions : « pour l’ananas séché, au Togo, en culture irriguée : 22 % ».
- Avant chaque calcul, une fonction partagée résout la valeur applicable au cas traité. La règle la plus précise l’emporte.
- Les ambiguïtés sont refusées à la publication du pack ou du paramètre, jamais découvertes sur le terrain.
- Chaque opération enregistre la règle appliquée, la version et les facteurs utilisés. Un auditeur peut toujours savoir pourquoi un seuil valait ce qu’il valait.
1. Vocabulaire
Section intitulée « 1. Vocabulaire »| Terme | Définition | Exemple |
|---|---|---|
| Clé de paramètre | Nom d’une valeur utilisée par une formule | rendement.reference |
| Facteur | Critère dont la valeur peut changer un seuil | produit, pays, type_sol |
| Contexte | Valeurs des facteurs pour le cas traité | { produit: "ananas", pays: "TG", … } |
| Règle | Une condition sur des facteurs et la valeur qui s’applique | R2 : ananas + séchage + TG + irrigué → 2 200 cp |
| Précision | Nombre de facteurs dans la condition d’une règle | R2 : précision 4 |
| Couche | Origine d’un jeu de règles : pack BioTrace ou organisation | – |
| Résolution | Choix de la règle applicable à un contexte | R2 pour un ananas irrigué du Togo |
2. Catalogue des facteurs
Section intitulée « 2. Catalogue des facteurs »2.1 Principe
Section intitulée « 2.1 Principe »Un facteur ne peut servir de condition que s’il existe comme donnée saisie, avec une liste de valeurs fermée. Un facteur qui n’est pas saisi ne peut pas être utilisé.
2.2 Facteurs v1
Section intitulée « 2.2 Facteurs v1 »| Facteur | Porté par | Valeurs | Saisi au |
|---|---|---|---|
produit |
Lot, collecte, contrat | Produits de l’organisation (Lot 1) | Lot 1 |
operation |
Transformation | Types d’opération du pack (sechage, extraction_huile, pressage…) |
Lot 2 |
pays |
Organisation, parcelle | ISO 3166-1 alpha-2 | Lot 0 (organisation), Lot 1 (parcelle) |
region |
Parcelle | Liste par pays | Lot 1 |
mode_culture |
Parcelle, campagne | pluvial, irrigue |
Lot 1 |
type_sol |
Parcelle | Liste du pack (lateritique, sableux, argileux…) |
Lot 1 |
referentiel |
Contrôle de qualité | UE, NOP… |
Lot 1 |
classe_altitude |
Parcelle (calculée) | Classes définies par le pack | Lot 3 |
La liste est fermée. Ajouter un facteur demande :
- une colonne sur l’entité qui le porte ;
- une entrée dans le catalogue ;
- une nouvelle version du schéma des packs.
2.3 Facteurs numériques
Section intitulée « 2.3 Facteurs numériques »Pas de condition sur une plage de valeurs en v1 (« altitude entre 500 et 800 m »). Un facteur numérique est découpé en classes, définies par le pack, et une fonction de packages/domain calcule la classe à partir de la valeur mesurée :
"classes": { "classe_altitude": { "unite": "m", "classes": [ { "code": "basse", "min": 0, "max": 500 }, { "code": "moyenne", "min": 500, "max": 1000 }, { "code": "haute", "min": 1000, "max": null } ] }}L’unité est portée une fois par facteur ; les bornes s’appellent min et max pour que le code reste indépendant de l’unité. Schéma : DefinitionsClasses dans packages/schemas. Fonctions : validerClasses (publication) et classe (résolution) dans packages/domain.
Bornes : minimum inclus, maximum exclu. Les classes doivent se suivre sans trou ni chevauchement, ce qui est vérifié à la publication.
Ce découpage garde la résolution simple, identique sur le téléphone et le serveur, et permet de vérifier l’absence d’ambiguïté.
2.4 Pas de hiérarchie en v1
Section intitulée « 2.4 Pas de hiérarchie en v1 »Une région appartient à un pays, mais la résolution ne l’infère pas. Une condition sur region doit aussi porter pays, ce qui est vérifié à la publication. On évite ainsi les règles implicites.
3. Format des règles
Section intitulée « 3. Format des règles »{ "cle": "rendement.reference", "unite": "cp", "surchargeable": true, "bornes_surcharge": { "min": 500, "max": 9500 }, "regles": [ { "id": "R0", "quand": {}, "valeur": null }, { "id": "R1", "quand": { "produit": ["ananas"], "operation": ["sechage"] }, "valeur": 2000 }, { "id": "R2", "quand": { "produit": ["ananas"], "operation": ["sechage"], "pays": ["TG"], "mode_culture": ["irrigue"] }, "valeur": 2200 }, { "id": "R3", "quand": { "produit": ["avocat"], "operation": ["extraction_huile"] }, "valeur": 1000 }, { "id": "R4", "quand": { "produit": ["avocat"], "operation": ["extraction_huile"], "type_sol": ["lateritique"] }, "valeur": 900 }, { "id": "R5", "quand": { "produit": ["mangue", "papaye"], "operation": ["sechage"] }, "valeur": 1500 } ]}quandassocie à chaque facteur une liste de valeurs acceptées. La condition est satisfaite si le contexte a l’une d’elles, pour chaque facteur cité.- Une condition vide (
{}) est la règle par défaut. Elle est facultative. - Une valeur
nullsignifie « pas de valeur » : la résolution renvoieparametre_introuvable. C’est une façon explicite de dire « il n’y a pas de référence pour ce cas ». surchargeableetbornes_surchargelimitent ce qu’une organisation peut modifier (section 5).idest unique dans la clé et stable d’une version à l’autre : il sert à la traçabilité.
4. Algorithme de résolution
Section intitulée « 4. Algorithme de résolution »- Filtrer : garder les règles dont toutes les conditions sont satisfaites. Si un facteur de la condition est absent du contexte, la règle ne s’applique pas.
- Choisir : garder la règle de plus grande précision. La validation (section 6) garantit qu’elle est unique.
- Rendre : la valeur, l’
idde la règle, la couche et la version. - Aucune règle, ou valeur
null: erreurparametre_introuvable. La fonction n’invente jamais de valeur.
Exemples avec le jeu ci-dessus :
| Contexte | Règles satisfaites | Résultat |
|---|---|---|
| ananas, séchage, TG, irrigué | R0, R1, R2 | R2 → 2 200 cp |
| ananas, séchage, BJ, pluvial | R0, R1 | R1 → 2 000 cp |
| avocat, extraction d’huile, sol latéritique | R0, R3, R4 | R4 → 900 cp |
| papaye, séchage | R0, R5 | R5 → 1 500 cp |
| ananas, pressage | R0 | R0 → parametre_introuvable |
5. Couches : pack et organisation
Section intitulée « 5. Couches : pack et organisation »5.1 Deux couches
Section intitulée « 5.1 Deux couches »| Couche | Source | Qui la modifie |
|---|---|---|
| 1. Pack | app.pack_version, dossier packs/ du dépôt |
Équipe BioTrace, par publication d’une nouvelle version |
| 2. Organisation | app.parametre (ajout seul, versionné) |
Administrateur client, dans le back-office |
La campagne n’est pas une couche. Elle fige la version du pack et la date d’application des paramètres de l’organisation.
5.2 Priorité : la couche d’abord
Section intitulée « 5.2 Priorité : la couche d’abord »- On résout d’abord dans la couche organisation.
- Si une règle de l’organisation s’applique, elle l’emporte, quelle que soit la précision des règles du pack.
- Sinon, on résout dans la couche pack.
Pourquoi la couche d’abord. Une surcharge de l’organisation exprime une réalité locale, connue du client. La règle est simple à expliquer : « si votre organisation a défini une valeur pour ce cas, c’est elle qui compte ».
Contrepartie. Une surcharge générale de l’organisation (« ananas : 18 % ») masque les règles plus précises du pack (R2). À la publication d’une surcharge, le back-office affiche la liste des règles du pack qu’elle masque, et demande une confirmation.
5.3 Limites de la surcharge
Section intitulée « 5.3 Limites de la surcharge »- Une clé
surchargeable: falsene peut pas être modifiée par l’organisation. C’est le cas des seuils réglementaires et des tolérances de bilan de masse. - Une surcharge doit rester dans
bornes_surcharge. - Toute surcharge exige un motif écrit, comme tout paramètre.
- Une référence de rendement surchargée par l’organisation est considérée comme non calibrée (EC-007) tant que l’équipe BioTrace ne l’a pas validée. Hors tolérance, elle produit donc une alerte, pas un blocage.
6. Validation à la publication
Section intitulée « 6. Validation à la publication »Un jeu de règles est refusé à la publication, pack ou surcharge, s’il présente l’une de ces anomalies.
| Contrôle | Refus si… |
|---|---|
| Facteurs connus | Un facteur n’est pas dans le catalogue |
| Valeurs connues | Une valeur n’est pas dans la liste du facteur |
| Région sans pays | Une condition porte region sans pays |
| Identifiants | Deux règles ont le même id, ou un id d’une version précédente change de sens sans changer d’identifiant |
| Unité et bornes | Une valeur n’est pas un entier de l’unité déclarée, ou sort des bornes |
| Classes | Classes d’un facteur numérique avec trou ou chevauchement |
| Ambiguïté | Voir ci-dessous |
| Couverture | Une règle n’est gagnante dans aucun vecteur de tests (section 8) |
6.1 Règle anti-ambiguïté
Section intitulée « 6.1 Règle anti-ambiguïté »Deux règles de même précision peuvent s’appliquer au même contexte si, pour chaque facteur qu’elles citent toutes les deux, leurs listes de valeurs ont au moins une valeur en commun. On dit alors qu’elles se recouvrent. Pour chaque paire de règles de même précision qui se recouvrent :
- si elles citent exactement les mêmes facteurs : refus. Les listes doivent être disjointes ;
- sinon : refus, sauf si le jeu contient une règle égale à leur conjonction, c’est-à-dire l’union de leurs facteurs, avec l’intersection des valeurs pour les facteurs communs.
La conjonction s’applique à tous les contextes où les deux règles s’appliquent, et elle est plus précise. Elle tranche donc toujours. Elle est elle-même soumise au même contrôle.
Exemple :
| Règle | Condition | Précision |
|---|---|---|
| A | ananas + TG | 2 |
| B | ananas + irrigué | 2 |
A et B se recouvrent pour un ananas irrigué du Togo. Le pack n’est accepté que s’il contient aussi C : ananas + TG + irrigué (précision 3).
Ce contrôle est fait par validerJeuDeRegles (section 7), dans le pipeline de publication des packs et dans l’API pour les surcharges. Pour une surcharge, l’appelant reporte bornes_surcharge du pack dans le jeu à valider ; le refus d’une surcharge sur une clé surchargeable: false reste à écrire avec la route de surcharge (Lot 1).
7. Interfaces dans packages/domain
Section intitulée « 7. Interfaces dans packages/domain »Nouveau module parametres :
export type CodeFacteur = string; // entrée du catalogueexport type Contexte = Readonly<Record<CodeFacteur, string>>;
export interface Regle<V> { readonly id: string; readonly quand: Readonly<Record<CodeFacteur, readonly string[]>>; readonly valeur: V | null;}
export interface JeuDeRegles<V> { readonly cle: string; readonly unite: string; readonly surchargeable: boolean; readonly bornes_surcharge?: { readonly min: number; readonly max: number }; readonly regles: readonly Regle<V>[];}
export interface Couche<V> { readonly origine: 'organisation' | 'pack'; readonly version: string; // version du pack ou du paramètre readonly jeu: JeuDeRegles<V>;}
export interface Resolution<V> { readonly cle: string; readonly valeur: V; readonly regle_id: string; readonly origine: 'organisation' | 'pack'; readonly version: string; readonly facteurs_utilises: Contexte; // seulement les facteurs de la règle retenue}
export function resoudre<V>( couches: readonly Couche<V>[], // dans l'ordre de priorité contexte: Contexte,): Resultat<Resolution<V>, 'parametre_introuvable'>;
export function validerJeuDeRegles<V>( jeu: JeuDeRegles<V>, catalogue: CatalogueFacteurs, precedent?: JeuDeRegles<V>,): Resultat<void, 'facteur_inconnu' | 'valeur_inconnue' | 'region_sans_pays' | 'identifiant_duplique' | 'identifiant_redefini' | 'valeur_hors_bornes' | 'ambiguite'>;
export function reglesMasquees<V>(surcharge: JeuDeRegles<V>, pack: JeuDeRegles<V>): readonly string[];
export function classe(facteur: CodeFacteur, valeur: number, classes: DefinitionClasses): Resultat<string, 'hors_classes'>;Les fonctions de calcul ne changent pas. Elles continuent de recevoir leurs valeurs en arguments. L’appelant résout d’abord, puis calcule :
const ref = resoudre(couches.rendementReference, contexte);if (!ref.ok) return ref; // parametre_introuvableconst tol = resoudre(couches.rendementTolerance, contexte);if (!tol.ok) return tol;return evaluerRendement(entree, sortie, { reference_cp: cp(ref.valeur.valeur), tolerance_relative_cp: cp(tol.valeur.valeur), calibre: ref.valeur.origine === 'pack' && packCalibre,});8. Vecteurs de tests des packs
Section intitulée « 8. Vecteurs de tests des packs »Chaque pack est livré avec ses vecteurs de résolution, dans testdata/packs/{pack}/resolution/.
{ "id": "VT-FRUITS-REND-002", "description": "Ananas séché irrigué au Togo : règle spécifique", "cle": "rendement.reference", "contexte": { "produit": "ananas", "operation": "sechage", "pays": "TG", "mode_culture": "irrigue" }, "attendu": { "ok": true, "regle_id": "R2", "valeur": 2200 }}Règles de couverture, vérifiées à la publication :
- chaque règle est gagnante dans au moins un vecteur ;
- chaque paire de règles qui se recouvrent a un vecteur sur leur zone commune ;
- si la clé n’a pas de règle par défaut, au moins un vecteur attend
parametre_introuvable.
Les vecteurs de calcul existants peuvent aussi partir d’un contexte au lieu d’une valeur écrite en dur. L’exécuteur résout alors avant de calculer, ce qui teste la chaîne complète.
9. Traçabilité
Section intitulée « 9. Traçabilité »Chaque opération qui utilise une valeur résolue enregistre :
| Donnée | Où |
|---|---|
| Clé, règle retenue, origine, version | Colonne resolutions (jsonb) de l’entité (transformation, collecte…) |
| Facteurs utilisés | Même colonne : instantané des valeurs, pas une référence à la parcelle |
| Valeur | Colonne typée de l’entité (rendement_reference_cp…) |
Pourquoi un instantané. Si le type de sol d’une parcelle est corrigé plus tard, le calcul d’origine reste explicable et rejouable à l’identique.
Sur le terrain, la commande porte les résolutions faites par le téléphone (payload.resolutions). Au rejeu, le serveur résout à nouveau avec la version figée par la campagne. Une différence de règle ou de valeur ouvre un contrôle ecart_calcul (protocole, étape 11).
10. Comportement en cas de valeur introuvable
Section intitulée « 10. Comportement en cas de valeur introuvable »| Où | Comportement |
|---|---|
| Terrain (hors-ligne) | L’opération est enregistrée. Le serveur ouvre un contrôle parametre_introuvable |
| Back-office | Opération bloquée, avec le message : « Aucune référence de rendement pour l’ananas en pressage. Demandez à l’administrateur de la définir. » |
| Publication d’une campagne | Avertissement listant les combinaisons produit / opération de la campagne sans valeur résolue |
11. Décisions
Section intitulée « 11. Décisions »| id | Décision | Statut |
|---|---|---|
| PC-01 | Formules dans le code, valeurs dans des jeux de règles avec conditions | Validé le 30/09/2026 |
| PC-02 | Catalogue de facteurs fermé, chaque facteur est une donnée saisie | Validé le 30/09/2026 |
| PC-03 | Facteurs numériques découpés en classes, pas de plages dans les conditions | Validé le 30/09/2026 |
| PC-04 | La règle la plus précise l’emporte à l’intérieur d’une couche | Validé le 30/09/2026 |
| PC-05 | La couche organisation l’emporte sur la couche pack, avec affichage des règles masquées | Validé le 30/09/2026 |
| PC-06 | Refus des ambiguïtés à la publication, par la règle de conjonction. Étendu le 02/10/2026 aux lignes des tables de décision : une ligne sans source, sans issue, sans date d’effet ou mal typée est refusée (regles.validerLigne, L0-4) |
Validé le 30/09/2026 |
| PC-07 | Aucune valeur inventée : parametre_introuvable, puis contrôle ou blocage |
Validé le 30/09/2026 |
| PC-08 | Instantané des facteurs et de la règle appliquée sur chaque opération. Étendu le 02/10/2026 aux règles évaluées : chaque constat recopie la règle, sa version, sa source et les valeurs lues (AM-15, L0-4) | Validé le 30/09/2026 |
12. Mises à jour induites
Section intitulée « 12. Mises à jour induites »| Document | Modification |
|---|---|
Paquet packages/domain |
Module parametres : fait. Exécuteur capable de résoudre avant de calculer : à faire |
| Schéma de la base | Tables globales app.facteur et app.facteur_valeur, chargées avec les packs ; au Lot 1, colonnes region, mode_culture, type_sol sur la parcelle, avec clé étrangère vers facteur_valeur ; colonne resolutions sur les entités calculées ; motif parametre_introuvable dans sync.controle |
| Protocole de synchronisation | payload.resolutions dans les commandes qui utilisent une valeur résolue ; motif parametre_introuvable (fait : migration 20260930130000) |
| Découpage | Décisions PC-01 à PC-08 à reporter dans les décisions « Paquets filières » et « Paramétrage » |
| JSON Schema des packs | Format des jeux de règles et des classes (sections 2.3 et 3) : fait, JeuDeRegles et DefinitionsClasses dans packages/schemas |