@mostajs/kind-catalog 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/CHANGELOG.md +136 -0
  2. package/README.md +103 -0
  3. package/docs/00-ETAPE-INAUGURALE-KIND-CATALOG.md +105 -0
  4. package/docs/03bis-PROMPT-IMAGE-OBJECTIF-KIND-CATALOG.md +99 -0
  5. package/docs/09bis-MATRICE-SOURCING-KIND-CATALOG.md +176 -0
  6. package/docs/10-PRESENTATION-COMMERCIALE-KIND-CATALOG.md +140 -0
  7. package/docs/11-SLIDES-COMMERCIAL-KIND-CATALOG.md +154 -0
  8. package/docs/12-DOC-TECHNIQUE-KIND-CATALOG.md +164 -0
  9. package/docs/13-SLIDES-DEV-KIND-CATALOG.md +162 -0
  10. package/docs/14-PROMPT-IMAGE-KIND-CATALOG.md +88 -0
  11. package/docs/15-REVUE-SECURITE-KIND-CATALOG.md +98 -0
  12. package/docs/16-DPIA-CONFORMITE-KIND-CATALOG.md +100 -0
  13. package/docs/ARTICLE-SEO-KIND-CATALOG.md +96 -0
  14. package/docs/AUDIT-EXISTANT-KIND-CATALOG.md +127 -0
  15. package/docs/DEVTEST-PLAN.kind-catalog.json +412 -0
  16. package/docs/ETUDE-ETAT-ART-KIND-CATALOG-02092026.md +207 -0
  17. package/docs/PLAN-DEV-KIND-CATALOG.md +186 -0
  18. package/docs/PLAN-PUBLICATION-KIND-CATALOG.md +80 -0
  19. package/docs/PLAN-SUIVI-MONITORING.md +66 -0
  20. package/docs/PLAN-TESTS.md +79 -0
  21. package/docs/SEO-KEYWORDS-KIND-CATALOG.md +46 -0
  22. package/docs/articles/01-ARTICLE-KIND-CATALOG.md +229 -0
  23. package/kinds/acces.kind.mjs +48 -0
  24. package/kinds/apprentissage.kind.mjs +25 -0
  25. package/kinds/chiffres.kind.mjs +80 -0
  26. package/kinds/decision.kind.mjs +88 -0
  27. package/kinds/donnees.kind.mjs +108 -0
  28. package/kinds/ecran-service.kind.mjs +71 -0
  29. package/kinds/integration.kind.mjs +83 -0
  30. package/kinds/metier-optimisation.kind.mjs +210 -0
  31. package/kinds/traces.kind.mjs +39 -0
  32. package/llms.txt +34 -0
  33. package/package.json +46 -0
  34. package/src/catalogue.js +98 -0
  35. package/src/index.js +4 -0
  36. package/src/instance.js +85 -0
  37. package/src/kind.js +152 -0
  38. package/src/projection.js +76 -0
@@ -0,0 +1,71 @@
1
+ // Domaine `donnees` — l'écart entre ce que l'écran envoie et ce que le service attend.
2
+ // @author Dr Hamid MADANI <drmdh@msn.com> · AGPL-3.0-or-later
3
+ import { defineKind } from '../src/kind.js';
4
+
5
+ export const kinds = [
6
+ defineKind({
7
+ ref: 'KIND-FORMULAIRE-ROUTE-01',
8
+ domaine: 'donnees',
9
+ enonce: 'Le formulaire RENDU et la route qui le reçoit s’accordent — et cela se vérifie sur l’écran TEL QU’IL EST SERVI.',
10
+ utilisation: 'Toute application où un écran alimente un service : formulaire HTML, appel d’interface, message.',
11
+ besoins: [{ nom: 'écran servi', forme: 'le HTML réellement rendu, pas la fonction qui le compose' }],
12
+ succes: [
13
+ 'chaque formulaire rendu est ACCEPTÉ par sa propre route',
14
+ 'ce que le service exige, l’écran l’envoie — ou le service le DÉRIVE d’une donnée qu’il possède déjà',
15
+ 'l’essai part du rendu, jamais d’un appel direct au service',
16
+ ],
17
+ erreurs: [
18
+ { titre: 'l’essai appelle le service en lui fournissant ce que l’écran ne fournit pas',
19
+ consequence: 'la suite est verte et l’écran écrit des données amputées. C’est le pire écart possible, parce que la couverture rassure exactement là où le trou se trouve' },
20
+ { titre: 'le champ manquant est FACULTATIF côté service',
21
+ consequence: 'aucune erreur nulle part : la donnée naît incomplète, et le défaut se découvre à l’autre bout de l’application — parfois douze écrans plus loin, sans qu’on puisse relier les deux' },
22
+ { titre: 'le service comble le manque par une valeur par défaut',
23
+ consequence: 'le défaut devient la valeur réelle, et il a l’air d’une saisie' },
24
+ { titre: 'la route évolue, le formulaire non — ou l’inverse',
25
+ consequence: 'rien ne casse tant que le champ reste facultatif ; tout casse le jour où il ne l’est plus, et l’on cherche du côté de la modification récente, pas du formulaire ancien' },
26
+ ],
27
+ test: [
28
+ { action: 'rendre le formulaire, poster EXACTEMENT les champs qu’il porte', attendu: 'la route accepte' },
29
+ { action: 'relire la donnée écrite par ce chemin', attendu: 'elle est complète — tous les champs que la suite du parcours exigera' },
30
+ ],
31
+ verdict: 'eprouve',
32
+ origine: [
33
+ { type: 'incident', source: 'ATC — l’écran de notation appelait `noter()` avec la seule séance : `courseId` et `levelId` restaient nuls, et le certificat répondait « aucun niveau atteint » douze écrans plus loin', date: '2026-09-01' },
34
+ { type: 'incident', source: 'LabTrax — « VERIFIER L IHM TELLE QU ELLE EST SERVIE : chaque formulaire rendu doit être accepté par SA route »', date: '2026-08-31' },
35
+ ],
36
+ }),
37
+
38
+ defineKind({
39
+ ref: 'KIND-CONSIGNER-APRES-SUCCES-01',
40
+ domaine: 'donnees',
41
+ enonce: 'On ne date, ne numérote et n’annonce qu’APRÈS le succès de l’acte — et l’on relit ce que l’on annonce.',
42
+ utilisation: 'Envoi de courriel ou de message, attribution d’un numéro officiel, écriture en base, publication d’une pièce.',
43
+ besoins: [{ nom: 'confirmation', forme: 'le retour de l’acte, attendu — jamais supposé' }],
44
+ succes: [
45
+ 'la date d’envoi est celle de l’envoi RÉUSSI',
46
+ 'un numéro n’est attribué que si l’enregistrement a abouti',
47
+ 'ce qui est annoncé à l’utilisateur a été RELU depuis la source, pas depuis la mémoire de l’appelant',
48
+ ],
49
+ erreurs: [
50
+ { titre: 'on date avant d’envoyer',
51
+ consequence: 'un envoi échoué laisse une date qui atteste d’une notification qui n’a pas eu lieu — et comme la date existe, personne ne relance jamais' },
52
+ { titre: 'la valeur annoncée n’est pas relue',
53
+ consequence: 'on rend un numéro de pièce à quelqu’un, et la pièce n’existe pas. La page publique répond « introuvable » sur un numéro que le système a pourtant produit' },
54
+ { titre: 'une promesse non attendue est stockée telle quelle',
55
+ consequence: 'le champ vaut `{}` ou `[object Promise]` ; la signature couvre ce vide, et la pièce devient invérifiable sans qu’aucune erreur ne soit levée' },
56
+ { titre: 'le journal consigne l’INTENTION, pas le RÉSULTAT',
57
+ consequence: 'on relit six mois plus tard une trace d’actions qui n’ont pas eu lieu, et l’on en tire des conclusions' },
58
+ ],
59
+ test: [
60
+ { action: 'faire échouer l’envoi', attendu: 'aucune date d’envoi n’est posée, et l’échec est dit' },
61
+ { action: 'interrompre la persistance après l’attribution du numéro', attendu: 'aucun numéro n’est rendu à l’utilisateur' },
62
+ { action: 'relire dans un NOUVEAU processus ce qui vient d’être annoncé', attendu: 'la chose existe' },
63
+ ],
64
+ verdict: 'eprouve',
65
+ origine: [
66
+ { type: 'incident', source: 'ATC — `certificates` n’attendait pas `numbering.next()` : le numéro valait `{}`, la signature couvrait ce vide, et aucun certificat n’était vérifiable', date: '2026-08-31' },
67
+ { type: 'incident', source: 'ATC — un certificat annoncé (CERT-2026-0001) n’avait jamais été persisté : la page publique répondait « introuvable » sur un numéro rendu par le système', date: '2026-09-02' },
68
+ { type: 'incident', source: 'LabTrax — « NOTIFIER RÉELLEMENT le candidat au scellement, ne dater qu’APRÈS l’envoi »', date: '2026-08-31' },
69
+ ],
70
+ }),
71
+ ];
@@ -0,0 +1,83 @@
1
+ // Domaine `integration` — ce qui casse au bout d'une clé d'API, et comment le nommer.
2
+ // @author Dr Hamid MADANI <drmdh@msn.com> · AGPL-3.0-or-later
3
+ import { defineKind } from '../src/kind.js';
4
+
5
+ export const kinds = [
6
+ defineKind({
7
+ ref: 'KIND-ECHEC-FOURNISSEUR-01',
8
+ domaine: 'integration',
9
+ enonce: 'Les échecs d’un fournisseur externe se CLASSENT : authentification, ressource, quota, solde, injoignable.',
10
+ utilisation: 'Tout service tiers à clé : modèle de langage, courriel, messagerie, paiement, stockage, cartographie.',
11
+ besoins: [
12
+ { nom: 'sonde', forme: '(fournisseur) => { classe, message, action conseillée }' },
13
+ { nom: 'fournisseurs configurés', forme: '[{ id, clé, point d’entrée }]' },
14
+ ],
15
+ succes: [
16
+ 'chaque échec est rangé dans une classe NOMMÉE, avec l’action à faire',
17
+ 'un diagnostic dit l’état de CHAQUE fournisseur configuré, pas seulement celui qui vient d’échouer',
18
+ 'un échec temporaire (quota) se distingue d’un échec définitif (clé révoquée)',
19
+ ],
20
+ erreurs: [
21
+ { titre: 'l’échec est rendu comme « ça ne marche pas »',
22
+ consequence: 'on renouvelle une clé parfaitement valide pendant des heures, alors qu’il manquait du crédit' },
23
+ { titre: 'une clé valide SANS CRÉDIT est confondue avec une clé invalide',
24
+ consequence: '400 (solde) et 401 (authentification) demandent deux actions opposées : recharger, ou régénérer. Confondre les deux fait perdre une journée' },
25
+ { titre: 'un quota temporaire est traité comme une panne définitive',
26
+ consequence: 'on bascule sur un repli de moindre qualité — et l’on n’y revient jamais, parce que rien ne resonde' },
27
+ { titre: 'l’ordre de repli est figé dans la configuration',
28
+ consequence: 'il ne tient aucun compte de ce qui marche réellement ; le premier de la liste est réessayé indéfiniment' },
29
+ { titre: 'le diagnostic est fait par l’appel métier lui-même',
30
+ consequence: 'on n’apprend l’état d’un fournisseur qu’en échouant devant un utilisateur' },
31
+ ],
32
+ test: [
33
+ { action: 'simuler successivement 401, 404, 400-solde et 429', attendu: 'quatre classes distinctes, quatre actions conseillées distinctes' },
34
+ { action: 'sonder l’ensemble des fournisseurs configurés', attendu: 'un état par fournisseur, y compris ceux qu’on n’a pas appelés' },
35
+ { action: 'lever un quota temporaire', attendu: 'le fournisseur redevient éligible sans intervention' },
36
+ ],
37
+ verdict: 'eprouve',
38
+ origine: [
39
+ { type: 'incident', source: 'CRM/TRADING — essai en direct : une clé valide sans crédit (400 billing) prise pour une clé invalide (401)', date: '2026-06-13' },
40
+ { type: 'incident', source: 'CRM/TRADING — docs/CARNET-IDEES-TRANSVERSALES.md motif #4', date: '2026-06-13' },
41
+ ],
42
+ }),
43
+
44
+ defineKind({
45
+ ref: 'KIND-DEPENDANCE-DISTANTE-01',
46
+ domaine: 'integration',
47
+ enonce: 'La perte du lien avec un service distant n’empêche AUCUN geste local.',
48
+ utilisation: 'Toute instance qui dépend d’un central : licence, métriques, référentiel, annuaire, télémétrie.',
49
+ besoins: [
50
+ { nom: 'ce qui est vérifiable hors ligne', forme: 'jeton ou acte SIGNÉ, opposable sans réseau' },
51
+ { nom: 'ce qui peut attendre', forme: 'file d’attente locale, rejouée au retour du lien' },
52
+ ],
53
+ succes: [
54
+ 'central injoignable : le geste local se fait, et il est daté',
55
+ 'ce qui doit être vérifié l’est par une SIGNATURE, pas par un appel',
56
+ 'l’état dégradé est DIT à l’écran, jamais masqué',
57
+ 'le retour du lien rattrape ce qui attendait, sans intervention',
58
+ ],
59
+ erreurs: [
60
+ { titre: 'le geste local attend une réponse distante',
61
+ consequence: 'une panne de réseau arrête le travail réel — le pointage à la porte, la délibération, l’encaissement. La dépendance devient une panne métier' },
62
+ { titre: 'l’autorisation est vérifiée par un APPEL au lieu d’une signature',
63
+ consequence: 'qui coupe le lien ouvre la porte, ou la ferme — dans les deux cas, c’est le réseau qui décide du droit' },
64
+ { titre: 'la révocation n’est pas opposable hors ligne',
65
+ consequence: 'un droit retiré au central reste actif sur l’instance tant qu’elle ne rappelle pas — et rien ne dit quand elle rappellera' },
66
+ { titre: 'le mode dégradé est silencieux',
67
+ consequence: 'on croit travailler normalement ; on découvre au retour du lien que la moitié n’est pas partie' },
68
+ { titre: 'la file d’attente n’est pas bornée',
69
+ consequence: 'une coupure longue remplit le disque, et la panne de réseau devient une panne de stockage' },
70
+ ],
71
+ test: [
72
+ { action: 'couper le lien, puis accomplir le geste métier', attendu: 'il aboutit, daté, et l’écran dit l’état dégradé' },
73
+ { action: 'révoquer au central, lien coupé', attendu: 'la révocation signée est opposable sans appel' },
74
+ { action: 'rétablir le lien', attendu: 'ce qui attendait part, sans geste humain' },
75
+ ],
76
+ verdict: 'eprouve',
77
+ origine: [
78
+ { type: 'incident', source: 'LabTrax — « Fonctionner central injoignable : la perte du lien n’empêche NI le pointage, NI la délibération »', date: '2026-08-31' },
79
+ { type: 'incident', source: 'SofTrax — « La révocation est signée donc opposable hors ligne, et coupe l’activation »', date: '2026-08-20' },
80
+ { type: 'incident', source: 'LabTraxAdmin — même exigence, côté central : la licence ne se demande pas depuis l’instance', date: '2026-08-25' },
81
+ ],
82
+ }),
83
+ ];
@@ -0,0 +1,210 @@
1
+ // Fiches MÉTIER — écrites d'après les corpus établis de chaque profession.
2
+ //
3
+ // ⚠️ TOUTES EN `propose`, ET C'EST LA PART HONNÊTE DU CATALOGUE. Leur provenance est une
4
+ // `reference` : une norme, un guide de bonnes pratiques, un problème classique de la littérature.
5
+ // Elles ne passeront à `eprouve` qu'avec un `incident` ou un `terrain` — c'est-à-dire un défaut
6
+ // constaté ou le dire d'un praticien nommé et daté. On n'entre pas au catalogue sur un galon
7
+ // qu'on ne s'est pas gagné, mais on y entre.
8
+ // @author Dr Hamid MADANI <drmdh@msn.com> · AGPL-3.0-or-later
9
+ import { defineKind } from '../src/kind.js';
10
+
11
+ export const kinds = [
12
+ defineKind({
13
+ ref: 'KIND-FORMULATION-MOINDRE-COUT-01',
14
+ domaine: 'elevage',
15
+ enonce: 'Composer une ration au moindre coût, sous contraintes nutritionnelles.',
16
+ utilisation: 'Élevage (ration du troupeau) et agro-alimentaire (formulation d’un produit) — c’est le même problème.',
17
+ besoins: [
18
+ { nom: 'ingrédients', forme: '[{ id, coût unitaire, apports par nutriment }]' },
19
+ { nom: 'contraintes', forme: '[{ nutriment, min?, max? }]' },
20
+ ],
21
+ succes: [
22
+ 'la ration respecte TOUTES les contraintes, ou l’infaisabilité est déclarée',
23
+ 'le coût rendu est celui de la ration proposée, à la même unité que les prix saisis',
24
+ 'les contraintes SERRÉES sont nommées — ce sont elles qui font le prix',
25
+ ],
26
+ erreurs: [
27
+ { titre: 'l’infaisabilité est rendue comme une ration',
28
+ consequence: 'on distribue une ration qui ne couvre pas les besoins, et le déficit ne se voit qu’à la production, des semaines plus tard' },
29
+ { titre: 'seul le coût est optimisé, sans borne haute',
30
+ consequence: 'la solution mathématique concentre un ingrédient bon marché à une dose que l’animal ne supporte pas — le simplexe ne connaît pas la physiologie' },
31
+ { titre: 'les unités se mélangent (kg, %, MS, brut)',
32
+ consequence: 'un facteur mille passe inaperçu ; la ration est chiffrée et fausse' },
33
+ { titre: 'les prix sont figés',
34
+ consequence: 'une formule optimale en janvier devient la plus chère en juin, et personne ne la recalcule' },
35
+ ],
36
+ test: [
37
+ { action: 'poser des contraintes contradictoires', attendu: 'infaisabilité DÉCLARÉE, pas une ration approchée' },
38
+ { action: 'omettre une borne haute sur un ingrédient bon marché', attendu: 'la concentration excessive est signalée' },
39
+ { action: 'faire varier un prix', attendu: 'la formule change, et les contraintes serrées sont nommées' },
40
+ ],
41
+ verdict: 'propose',
42
+ origine: [
43
+ { type: 'reference', source: 'Problème du régime (Stigler 1945 ; résolu par le simplexe de Dantzig) — l’un des premiers problèmes de programmation linéaire' },
44
+ { type: 'reference', source: 'Tables de rationnement et besoins nutritionnels par espèce' },
45
+ ],
46
+ }),
47
+
48
+ defineKind({
49
+ ref: 'KIND-TRANSHUMANCE-01',
50
+ domaine: 'apiculture',
51
+ enonce: 'Placer les ruches là où la miellée les attend — et savoir qu’on ne le saura pas d’avance.',
52
+ utilisation: 'Conduite d’un rucher : quels lots vers quels emplacements, à quelle date.',
53
+ besoins: [
54
+ { nom: 'emplacements', forme: '[{ id, floraison attendue, capacité en ruches, distance }]' },
55
+ { nom: 'lots', forme: '[{ id, nombre de ruches, état sanitaire }]' },
56
+ ],
57
+ succes: [
58
+ 'l’affectation respecte la capacité de chaque emplacement',
59
+ 'le coût de déplacement est rendu séparément du gain espéré',
60
+ 'l’INCERTITUDE de la miellée est dite, jamais absorbée dans le chiffre',
61
+ ],
62
+ erreurs: [
63
+ { titre: 'une prévision de récolte est calculée sur l’historique',
64
+ consequence: 'dix ans d’exploitation font DIX points, dont aucun n’est comparable à l’autre — floraison, météo et état du cheptel changent tout. Un chiffre y est plus faux qu’ailleurs, et plus crédible' },
65
+ { titre: 'le sanitaire est traité comme une contrainte souple',
66
+ consequence: 'déplacer un lot atteint contamine l’emplacement, et la perte dépasse toute optimisation' },
67
+ { titre: 'le coût de déplacement est fondu dans l’objectif',
68
+ consequence: 'l’apiculteur ne peut plus arbitrer entre ce qu’il gagne et ce qu’il dépense' },
69
+ ],
70
+ test: [
71
+ { action: 'demander une prévision de récolte sur moins de trois miellées', attendu: 'refus chiffrant ce qui manque' },
72
+ { action: 'affecter un lot atteint à un emplacement sain', attendu: 'refus, non pénalisation' },
73
+ ],
74
+ verdict: 'propose',
75
+ origine: [
76
+ { type: 'reference', source: 'Guides de bonnes pratiques apicoles — conduite du rucher et prophylaxie' },
77
+ { type: 'reference', source: 'Affectation sous capacité (kind `assignment` de @mostajs/ro-pla)' },
78
+ ],
79
+ }),
80
+
81
+ defineKind({
82
+ ref: 'KIND-PLANNING-CHANTIER-01',
83
+ domaine: 'btp',
84
+ enonce: 'Ordonnancer un chantier sous ressources FINIES, et nommer ce qui tient le délai.',
85
+ utilisation: 'Conduite de travaux : tâches, prédécesseurs, équipes et engins en nombre limité.',
86
+ besoins: [
87
+ { nom: 'tâches', forme: '[{ id, durée, prédécesseurs, ressources }]' },
88
+ { nom: 'capacités', forme: '{ ressource: nombre }' },
89
+ ],
90
+ succes: [
91
+ 'le chemin critique est nommé, avec les marges',
92
+ 'la contrainte de ressource est respectée, pas seulement l’enchaînement',
93
+ 'la règle de priorité employée est RENDUE — elle change le résultat',
94
+ ],
95
+ erreurs: [
96
+ { titre: 'le chemin critique est calculé à ressources INFINIES',
97
+ consequence: 'le planning est juste sur le papier et faux sur le chantier : un seul grue, quatre tâches qui la demandent' },
98
+ { titre: 'la règle de priorité est traitée comme un réglage',
99
+ consequence: 'c’est une DÉCISION : minimiser le retard global, équilibrer la charge ou libérer vite ne donnent pas le même chantier, et aucune ne domine les autres' },
100
+ { titre: 'la question se pose sur le chantier, la réponse arrive au bureau',
101
+ consequence: 'un ordonnancement recalculé sans le conducteur de travaux n’est pas appliqué' },
102
+ ],
103
+ test: [
104
+ { action: 'ordonnancer avec une ressource unique demandée par plusieurs tâches', attendu: 'le makespan tient compte de la capacité' },
105
+ { action: 'changer la règle de priorité', attendu: 'le résultat change, et la règle employée est rendue' },
106
+ ],
107
+ verdict: 'propose',
108
+ seProsePose: 'sur le chantier — la réponse se calcule au bureau, et cet écart fait partie de la fiche',
109
+ origine: [
110
+ { type: 'reference', source: 'CPM/PERT ; ordonnancement sous contraintes de ressources (RCPSP)' },
111
+ { type: 'reference', source: 'DTU et CCTG — clauses types reprises et amendées par dérogation écrite' },
112
+ ],
113
+ }),
114
+
115
+ defineKind({
116
+ ref: 'KIND-TRACABILITE-LOT-01',
117
+ domaine: 'alimentaire',
118
+ enonce: 'Un lot se remonte et se redescend — de la matière au client, et l’inverse.',
119
+ utilisation: 'Toute production alimentaire soumise à traçabilité amont/aval.',
120
+ besoins: [{ nom: 'liens de lot', forme: 'matière → fabrication → lot fini → destinataire' }],
121
+ succes: [
122
+ 'depuis un lot fini, on nomme toutes les matières qui y sont entrées',
123
+ 'depuis une matière, on nomme tous les lots finis et tous les destinataires',
124
+ 'le retrait se calcule sur ces deux chemins, pas sur une estimation',
125
+ ],
126
+ erreurs: [
127
+ { titre: 'la traçabilité descendante seule est tenue',
128
+ consequence: 'on sait ce qu’un lot contient, mais pas OÙ il est parti : le retrait devient un rappel général, et le coût est sans commune mesure' },
129
+ { titre: 'un mélange de lots casse la chaîne',
130
+ consequence: 'un lot fini issu de trois lots de matière n’en désigne qu’un — les deux autres circulent encore' },
131
+ { titre: 'la DLC est calculée à la fabrication, pas au lot de matière le plus ancien',
132
+ consequence: 'la date affichée est plus longue que la réalité' },
133
+ ],
134
+ test: [
135
+ { action: 'depuis un lot fini, remonter aux matières', attendu: 'toutes, y compris en cas de mélange' },
136
+ { action: 'depuis une matière, descendre aux destinataires', attendu: 'tous' },
137
+ ],
138
+ verdict: 'propose',
139
+ origine: [
140
+ { type: 'reference', source: 'HACCP et paquet hygiène — traçabilité amont/aval, procédures de retrait-rappel' },
141
+ ],
142
+ }),
143
+
144
+ defineKind({
145
+ ref: 'KIND-ROTATION-CULTURALE-01',
146
+ domaine: 'agronomie',
147
+ enonce: 'Affecter les cultures aux parcelles dans le respect des rotations pluriannuelles.',
148
+ utilisation: 'Assolement d’une exploitation, sur plusieurs campagnes.',
149
+ besoins: [
150
+ { nom: 'parcelles', forme: '[{ id, surface, historique des cultures }]' },
151
+ { nom: 'règles de retour', forme: '[{ culture, délai minimal de retour, précédents interdits }]' },
152
+ ],
153
+ succes: [
154
+ 'aucun délai de retour n’est enfreint',
155
+ 'les précédents interdits sont respectés',
156
+ 'le plan couvre PLUSIEURS campagnes, pas la prochaine seule',
157
+ ],
158
+ erreurs: [
159
+ { titre: 'l’assolement est optimisé campagne par campagne',
160
+ consequence: 'chaque année est optimale et la rotation est ruinée — l’optimum local sur trois ans coûte plus que ce qu’il a rapporté sur un' },
161
+ { titre: 'l’historique de la parcelle est incomplet',
162
+ consequence: 'un délai de retour est calculé sur ce qu’on sait, et le pathogène, lui, se souvient de tout' },
163
+ { titre: 'le seuil de données est exprimé en jours',
164
+ consequence: 'un cycle est ANNUEL : deux saisons complètes, ce sont deux ans — un seuil en jours autorise à conclure sur une seule' },
165
+ ],
166
+ test: [
167
+ { action: 'proposer un assolement sur trois campagnes', attendu: 'aucun délai de retour enfreint sur l’ensemble' },
168
+ { action: 'fournir un historique incomplet', attendu: 'l’incertitude est dite, la parcelle n’est pas traitée comme vierge' },
169
+ ],
170
+ verdict: 'propose',
171
+ origine: [
172
+ { type: 'reference', source: 'Itinéraires techniques et règles de rotation — délais de retour par culture' },
173
+ { type: 'reference', source: 'Satisfaction de contraintes (kind `csp` de @mostajs/ro-pla)' },
174
+ ],
175
+ }),
176
+
177
+ defineKind({
178
+ ref: 'KIND-DIAGNOSTIC-PANNE-01',
179
+ domaine: 'electronique',
180
+ enonce: 'Ordonner les tests par ce qu’ils ÉLIMINENT, pas par ce qu’ils coûtent.',
181
+ utilisation: 'Diagnostic d’une carte ou d’un équipement, en production ou en maintenance.',
182
+ besoins: [
183
+ { nom: 'pannes possibles', forme: '[{ id, probabilité a priori }]' },
184
+ { nom: 'tests', forme: '[{ id, coût, pannes discriminées }]' },
185
+ ],
186
+ succes: [
187
+ 'la séquence proposée réduit l’incertitude au coût le plus bas',
188
+ 'la probabilité résiduelle est rendue après chaque test',
189
+ 'un test non discriminant est écarté, et on dit pourquoi',
190
+ ],
191
+ erreurs: [
192
+ { titre: 'les tests sont ordonnés par coût croissant',
193
+ consequence: 'on enchaîne des tests bon marché qui n’éliminent rien — la séquence est la moins chère par étape et la plus chère au total' },
194
+ { titre: 'les probabilités a priori sont supposées uniformes',
195
+ consequence: 'le diagnostic ignore que 80 % des pannes viennent de trois causes, et cherche partout' },
196
+ { titre: 'la question se pose sur l’équipement, le calcul tourne ailleurs',
197
+ consequence: 'une séquence optimale que le technicien ne reçoit pas au poste n’est jamais suivie' },
198
+ ],
199
+ test: [
200
+ { action: 'ordonner des tests dont le moins cher ne discrimine rien', attendu: 'il n’est pas placé en tête' },
201
+ { action: 'fournir des probabilités très inégales', attendu: 'la séquence en tient compte' },
202
+ ],
203
+ verdict: 'propose',
204
+ seProsePose: 'au poste de test ou sur l’équipement — le calcul est ailleurs',
205
+ origine: [
206
+ { type: 'reference', source: 'Diagnostic séquentiel ; arbres de décision et théorie de l’information' },
207
+ { type: 'reference', source: 'Chaînes de Markov et décision (kinds `markov`, `decision` de @mostajs/ro-pla)' },
208
+ ],
209
+ }),
210
+ ];
@@ -0,0 +1,39 @@
1
+ // Domaine `donnees` — ce qu'on retire, et ce qu'il en reste.
2
+ // @author Dr Hamid MADANI <drmdh@msn.com> · AGPL-3.0-or-later
3
+ import { defineKind } from '../src/kind.js';
4
+
5
+ export default defineKind({
6
+ ref: 'KIND-SUPPRESSION-DATEE-01',
7
+ domaine: 'donnees',
8
+ enonce: 'Retirer une chose, c’est la DATER — jamais la détruire.',
9
+ utilisation: 'Toute entité qu’un utilisateur peut « supprimer » : dossier, projet, compte, rôle, activation, solveur.',
10
+ besoins: [{ nom: 'champs de retrait', forme: '{ retireA, retirePar, motif }' }],
11
+ succes: [
12
+ 'la ligne SURVIT au retrait et porte sa date, son auteur et son motif',
13
+ 'ce qui est retiré n’apparaît plus dans les listes courantes',
14
+ 'on peut répondre : « cette chose a-t-elle servi, et jusqu’à quand ? »',
15
+ ],
16
+ erreurs: [
17
+ { titre: 'la ligne est effacée',
18
+ consequence: 'on ne distingue plus « n’a jamais existé » de « a servi trois mois puis a été retiré ». La seconde information est celle qui explique les chiffres du passé, et elle est perdue pour toujours' },
19
+ { titre: 'le retrait n’est pas motivé',
20
+ consequence: 'six mois plus tard, personne ne sait si c’était une décision ou une fausse manœuvre — et l’on refait la chose retirée' },
21
+ { titre: 'le retrait n’est pas attribué',
22
+ consequence: 'un retrait contesté n’a pas d’auteur : la discussion porte sur le fait, pas sur la décision' },
23
+ { titre: 'la restauration recrée un objet NEUF',
24
+ consequence: 'elle perd l’historique de l’ancien, et les rapports antérieurs ne se rattachent plus à rien' },
25
+ { titre: 'le retrait est un simple drapeau, sans date',
26
+ consequence: 'on sait que c’est retiré, pas depuis quand : impossible de reconstituer un état passé' },
27
+ ],
28
+ test: [
29
+ { action: 'retirer, puis compter les lignes en base', attendu: 'le compte est INCHANGÉ ; la ligne porte sa date, son auteur et son motif' },
30
+ { action: 'retirer puis remettre en service', attendu: 'l’historique du retrait subsiste — on lit les deux décisions' },
31
+ { action: 'lister', attendu: 'ce qui est retiré n’y figure pas' },
32
+ ],
33
+ verdict: 'eprouve',
34
+ origine: [
35
+ { type: 'incident', source: 'LabTrax — « POUVOIR RETIRER UN DOSSIER sans destruction : pierre tombale datée, attribuée et motivée »', date: '2026-08-31' },
36
+ { type: 'incident', source: 'qatrax — « supprimer un projet pose une pierre tombale : rien n’est détruit, et la restauration est possible »', date: '2026-08-15' },
37
+ { type: 'incident', source: '@mostajs/assistant-pilote — la désactivation d’un solveur est datée, la ligne n’est pas effacée (T-AP-8)', date: '2026-09-01' },
38
+ ],
39
+ });
package/llms.txt ADDED
@@ -0,0 +1,34 @@
1
+ # @mostajs/kind-catalog — fiche LLM
2
+ RÔLE
3
+ Catalogue de KINDS : fiches d'exigence REUTILISABLES, projetables en plan `mostajs-devtest/1`
4
+ (lu par @mostajs/qa-engine, importé par qatrax). Un kind = une QUESTION qu'un métier se pose,
5
+ avec ses besoins, son epreuve, ses criteres de succes et ses ERREURS CONNUES (chacune avec sa
6
+ consequence). N'EXECUTE RIEN, NE STOCKE RIEN — il decrit, valide et projette.
7
+ EXPORTS
8
+ defineKind(fiche) -> Kind gele ; validateKind(fiche) -> string[] ; VERDICTS, PROVENANCES, DOMAINES
9
+ toDevtest(kinds, { project, prefix }) -> plan mostajs-devtest/1 ; PLAN_VERSION
10
+ instantiate(kind, { app, prefix, affine }) -> Instance ; diffInstance(i) ; emplois(instances) ; AFFINABLES
11
+ loadCatalogue(dir) ; findKinds(kinds, {domaine,verdict,texte}) ; auditCatalogue(kinds) ; statsCatalogue(kinds)
12
+ CHAMPS DE LA FICHE
13
+ ref (KIND-…) · enonce · domaine · version · besoins · utilisation · succes* · erreurs* · test
14
+ efficacite · evaluation · verdict · motif · origine* · seProsePose (* = requis)
15
+ VERDICTS propose | eprouve | retenu | ecarte
16
+ PROVENANCES reference (norme, litterature) | terrain (praticien nomme) | incident (defaut constate)
17
+ DOMAINES acces donnees apprentissage decision integration · btp alimentaire elevage apiculture agronomie electronique
18
+ PIÈGES
19
+ - `succes` ET `erreurs` sont REQUIS : ce sont les champs qu'on omet, et ceux qui servent.
20
+ - Chaque erreur porte sa CONSEQUENCE : « ne pas oublier X » ne se retient pas (lecon CWE/OWASP).
21
+ - `eprouve`/`retenu` EXIGENT une origine `incident` ou `terrain` : on ne se decerne pas
22
+ l'experience. Une `reference` suffit pour ENTRER au catalogue en `propose` — la contrainte
23
+ porte sur le galon, jamais sur l'entree.
24
+ - `ecarte` exige un `motif` et RESTE au catalogue : une fiche ecartee qui disparait sera reinventee.
25
+ - `instantiate` REFUSE un affinage sans motif : sans lui, « reutiliser » se degrade en « recopier ».
26
+ - Le `prefix` ISOLE deux applications reprenant la meme fiche dans le meme qatrax.
27
+ - Une fiche `propose` se projette en statut `draft`, jamais `verified`.
28
+ - Les fiches sont des FICHIERS VERSIONNES (kinds/*.kind.mjs). Ce qui va en base, c'est l'USAGE —
29
+ et cela appartient a @mostajs/skill-library et au journal d'@mostajs/assistant-pilote.
30
+ - AUCUN FORMAT NOUVEAU : la projection produit du mostajs-devtest/1, valide par le parseur officiel.
31
+ - loadCatalogue() fait un import() : charger un corpus, c'est EXECUTER du code. Une fiche se revoit
32
+ comme du code, jamais comme un document (docs/15-REVUE-SECURITE).
33
+ - validateKind() applique les memes defauts que defineKind : on peut lui passer un objet BRUT.
34
+ CORPUS LIVRE 21 fiches · 15 eprouvees (71 %) · 84 erreurs cataloguees · 35 incidents · 11 domaines.
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "@mostajs/kind-catalog",
3
+ "version": "0.1.0",
4
+ "description": "Catalogue de KINDS — fiches d'exigence réutilisables, projetables en plan mostajs-devtest/1 lisible par qatrax. N'exécute rien, ne stocke rien.",
5
+ "type": "module",
6
+ "license": "AGPL-3.0-or-later",
7
+ "author": "Dr Hamid MADANI <drmdh@msn.com>",
8
+ "exports": {
9
+ ".": "./src/index.js",
10
+ "./kinds": "./src/kinds/index.js"
11
+ },
12
+ "files": [
13
+ "src",
14
+ "kinds",
15
+ "docs",
16
+ "llms.txt",
17
+ "README.md",
18
+ "CHANGELOG.md",
19
+ "!docs/**/*.pdf",
20
+ "!docs/**/*.pptx",
21
+ "!docs/**/*.png",
22
+ "!docs/**/*.html",
23
+ "!docs/MONITORING-*"
24
+ ],
25
+ "scripts": {
26
+ "test": "bash test-scripts/run-tests.sh"
27
+ },
28
+ "devDependencies": {
29
+ "@mostajs/doc-driver-chrome": "^0.1.0",
30
+ "@mostajs/doc-driver-pandoc": "^0.1.0",
31
+ "@mostajs/doc-export": "^0.2.1",
32
+ "@mostajs/doc-themes": "^0.1.1",
33
+ "@mostajs/markdown-html": "^0.1.2",
34
+ "@mostajs/mjs-unit": "^0.6.0",
35
+ "@mostajs/qa-engine": "^0.7.2"
36
+ },
37
+ "keywords": [
38
+ "mostajs",
39
+ "kind",
40
+ "catalogue",
41
+ "exigence",
42
+ "devtest",
43
+ "qatrax",
44
+ "reuse"
45
+ ]
46
+ }
@@ -0,0 +1,98 @@
1
+ /**
2
+ * LE CORPUS — charger, chercher, contrôler.
3
+ *
4
+ * ⚠️ SEULE I/O DU MODULE, et elle reçoit son chemin : elle ne le devine pas. Un module qui devine
5
+ * où sont ses données est un module qu'on ne peut pas monter deux fois.
6
+ *
7
+ * ⚠️ LES FICHES SONT DES FICHIERS VERSIONNÉS, pas des lignes en base. Une fiche est un document
8
+ * éditorial : elle se lit, se discute, se relit six mois plus tard, et son historique est celui
9
+ * d'un fichier. Ce qui va en base, c'est ce que produit son USAGE — exécutions, indices, retours —
10
+ * et cela appartient à @mostajs/skill-library et au journal d'@mostajs/assistant-pilote.
11
+ * Les deux ne se rangent pas au même endroit parce qu'ils n'ont pas la même durée de vie : la
12
+ * fiche se relit, le journal s'entasse.
13
+ *
14
+ * Author: Dr Hamid MADANI <drmdh@msn.com> · AGPL-3.0-or-later
15
+ */
16
+ import { readdir } from 'node:fs/promises';
17
+ import { join } from 'node:path';
18
+ import { pathToFileURL } from 'node:url';
19
+ import { validateKind } from './kind.js';
20
+
21
+ const propre = (v) => String(v ?? '').trim().toLowerCase();
22
+
23
+ /**
24
+ * Charge un corpus depuis un dossier de `*.kind.mjs`.
25
+ * Chaque fichier exporte `default` (une fiche) ou `kinds` (plusieurs).
26
+ */
27
+ export async function loadCatalogue(dir) {
28
+ if (!dir) throw new Error('loadCatalogue: dossier requis');
29
+ const fichiers = (await readdir(dir)).filter((f) => f.endsWith('.kind.mjs')).sort();
30
+ const kinds = [];
31
+ const vues = new Map();
32
+
33
+ for (const f of fichiers) {
34
+ const mod = await import(pathToFileURL(join(dir, f)).href);
35
+ const lot = mod.kinds ?? (mod.default ? [mod.default] : []);
36
+ for (const k of lot) {
37
+ // ⚠️ L'IDENTITÉ EST UNIQUE. `CWE-79` désigne la même chose pour mille projets depuis dix
38
+ // ans : c'est ce qui rend une référence citable. Deux fiches sous une même `ref`
39
+ // divergeraient sans que personne ne sache laquelle fait foi.
40
+ if (vues.has(k.ref)) {
41
+ throw new Error(`loadCatalogue : \`${k.ref}\` est déclarée deux fois — ${vues.get(k.ref)} et ${f}`);
42
+ }
43
+ vues.set(k.ref, f);
44
+ kinds.push(k);
45
+ }
46
+ }
47
+ return kinds;
48
+ }
49
+
50
+ /** Chercher — par domaine, par verdict, ou par mot dans l'énoncé et les erreurs. */
51
+ export function findKinds(kinds = [], { domaine = null, verdict = null, texte = null } = {}) {
52
+ const t = propre(texte);
53
+ return kinds.filter((k) => {
54
+ if (domaine && k.domaine !== propre(domaine)) return false;
55
+ if (verdict && k.verdict !== propre(verdict)) return false;
56
+ if (!t) return true;
57
+ const foin = [k.ref, k.enonce, k.utilisation, ...k.succes,
58
+ ...k.erreurs.flatMap((e) => [e.titre, e.consequence])].join(' ').toLowerCase();
59
+ return foin.includes(t);
60
+ });
61
+ }
62
+
63
+ /**
64
+ * CONTRÔLE DU CORPUS — ce qui doit être vrai de l'ensemble, et pas seulement de chaque fiche.
65
+ * Rendu comme liste de reproches : on corrige tout d'un coup.
66
+ */
67
+ export function auditCatalogue(kinds = []) {
68
+ const out = [];
69
+ const vues = new Set();
70
+ for (const k of kinds) {
71
+ for (const r of validateKind(k)) out.push(`${k.ref} : ${r}`);
72
+ if (vues.has(k.ref)) out.push(`${k.ref} : référence en double`);
73
+ vues.add(k.ref);
74
+ }
75
+ return out;
76
+ }
77
+
78
+ /** L'état du corpus, en un coup d'œil — c'est ce qu'on montre, et c'est ce qui doit être vrai. */
79
+ export function statsCatalogue(kinds = []) {
80
+ const parVerdict = {};
81
+ const parDomaine = {};
82
+ const parProvenance = {};
83
+ for (const k of kinds) {
84
+ parVerdict[k.verdict] = (parVerdict[k.verdict] ?? 0) + 1;
85
+ parDomaine[k.domaine] = (parDomaine[k.domaine] ?? 0) + 1;
86
+ for (const o of k.origine) parProvenance[o.type] = (parProvenance[o.type] ?? 0) + 1;
87
+ }
88
+ const eprouvees = (parVerdict.eprouve ?? 0) + (parVerdict.retenu ?? 0);
89
+ return {
90
+ total: kinds.length,
91
+ eprouvees,
92
+ // ⚠️ LE TAUX ÉPROUVÉ EST LE SEUL CHIFFRE QUI ENGAGE. Un catalogue se vante de son volume ;
93
+ // ce qui se vérifie, c'est la part qui a traversé un fait réel.
94
+ tauxEprouve: kinds.length ? Math.round((eprouvees / kinds.length) * 100) : 0,
95
+ parVerdict, parDomaine, parProvenance,
96
+ erreursCataloguees: kinds.reduce((n, k) => n + k.erreurs.length, 0),
97
+ };
98
+ }
package/src/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export { defineKind, validateKind, VERDICTS, PROVENANCES, DOMAINES } from './kind.js';
2
+ export { toDevtest, PLAN_VERSION } from './projection.js';
3
+ export { instantiate, diffInstance, emplois, AFFINABLES } from './instance.js';
4
+ export { loadCatalogue, findKinds, auditCatalogue, statsCatalogue } from './catalogue.js';