@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.
- package/CHANGELOG.md +136 -0
- package/README.md +103 -0
- package/docs/00-ETAPE-INAUGURALE-KIND-CATALOG.md +105 -0
- package/docs/03bis-PROMPT-IMAGE-OBJECTIF-KIND-CATALOG.md +99 -0
- package/docs/09bis-MATRICE-SOURCING-KIND-CATALOG.md +176 -0
- package/docs/10-PRESENTATION-COMMERCIALE-KIND-CATALOG.md +140 -0
- package/docs/11-SLIDES-COMMERCIAL-KIND-CATALOG.md +154 -0
- package/docs/12-DOC-TECHNIQUE-KIND-CATALOG.md +164 -0
- package/docs/13-SLIDES-DEV-KIND-CATALOG.md +162 -0
- package/docs/14-PROMPT-IMAGE-KIND-CATALOG.md +88 -0
- package/docs/15-REVUE-SECURITE-KIND-CATALOG.md +98 -0
- package/docs/16-DPIA-CONFORMITE-KIND-CATALOG.md +100 -0
- package/docs/ARTICLE-SEO-KIND-CATALOG.md +96 -0
- package/docs/AUDIT-EXISTANT-KIND-CATALOG.md +127 -0
- package/docs/DEVTEST-PLAN.kind-catalog.json +412 -0
- package/docs/ETUDE-ETAT-ART-KIND-CATALOG-02092026.md +207 -0
- package/docs/PLAN-DEV-KIND-CATALOG.md +186 -0
- package/docs/PLAN-PUBLICATION-KIND-CATALOG.md +80 -0
- package/docs/PLAN-SUIVI-MONITORING.md +66 -0
- package/docs/PLAN-TESTS.md +79 -0
- package/docs/SEO-KEYWORDS-KIND-CATALOG.md +46 -0
- package/docs/articles/01-ARTICLE-KIND-CATALOG.md +229 -0
- package/kinds/acces.kind.mjs +48 -0
- package/kinds/apprentissage.kind.mjs +25 -0
- package/kinds/chiffres.kind.mjs +80 -0
- package/kinds/decision.kind.mjs +88 -0
- package/kinds/donnees.kind.mjs +108 -0
- package/kinds/ecran-service.kind.mjs +71 -0
- package/kinds/integration.kind.mjs +83 -0
- package/kinds/metier-optimisation.kind.mjs +210 -0
- package/kinds/traces.kind.mjs +39 -0
- package/llms.txt +34 -0
- package/package.json +46 -0
- package/src/catalogue.js +98 -0
- package/src/index.js +4 -0
- package/src/instance.js +85 -0
- package/src/kind.js +152 -0
- package/src/projection.js +76 -0
package/src/instance.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* L'INSTANCIATION — une application reprend une fiche, telle quelle ou AFFINÉE.
|
|
3
|
+
*
|
|
4
|
+
* ⚠️ CE QUI FAIT VIVRE UN CATALOGUE, CE N'EST PAS LA RÉUTILISATION À L'IDENTIQUE : c'est
|
|
5
|
+
* **l'écart tracé**. L'agronomie adapte l'itinéraire technique au terroir et relève l'écart ; le
|
|
6
|
+
* bâtiment reprend le DTU et amende **par clause dérogatoire écrite** ; le raisonnement à partir de
|
|
7
|
+
* cas fait de la révision une étape du cycle, pas un accident. Les trois disent la même chose.
|
|
8
|
+
*
|
|
9
|
+
* Sans cette trace, « réutiliser » se dégrade en « recopier », et l'on revient à l'état d'avant le
|
|
10
|
+
* module : des plans qui se ressemblent sans jamais se parler.
|
|
11
|
+
*
|
|
12
|
+
* Author: Dr Hamid MADANI <drmdh@msn.com> · AGPL-3.0-or-later
|
|
13
|
+
*/
|
|
14
|
+
import { validateKind } from './kind.js';
|
|
15
|
+
|
|
16
|
+
const propre = (v) => String(v ?? '').trim();
|
|
17
|
+
|
|
18
|
+
/** Les champs qu'une application peut affiner. Le reste appartient à la fiche. */
|
|
19
|
+
export const AFFINABLES = ['enonce', 'besoins', 'utilisation', 'succes', 'erreurs', 'test', 'seProsePose'];
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Instancie une fiche pour une application.
|
|
23
|
+
*
|
|
24
|
+
* @param {object} kind
|
|
25
|
+
* @param {object} o
|
|
26
|
+
* @param {string} o.app l'application qui reprend
|
|
27
|
+
* @param {string} o.prefix préfixe de ses références
|
|
28
|
+
* @param {object} [o.affine] UNIQUEMENT les champs affinés, chacun avec son motif
|
|
29
|
+
* `{ succes: { valeur: [...], motif: '…' } }`
|
|
30
|
+
*/
|
|
31
|
+
export function instantiate(kind, { app, prefix, affine = {} } = {}) {
|
|
32
|
+
if (!kind?.ref) throw new Error('instantiate: fiche requise');
|
|
33
|
+
if (!propre(app)) throw new Error('instantiate: `app` requise — une instance sans application ne se retrouve pas');
|
|
34
|
+
|
|
35
|
+
const affinages = [];
|
|
36
|
+
const resultat = { ...kind };
|
|
37
|
+
for (const [champ, val] of Object.entries(affine)) {
|
|
38
|
+
if (!AFFINABLES.includes(champ)) {
|
|
39
|
+
throw new Error(`instantiate(${kind.ref}) : « ${champ} » n’est pas affinable — affinables : ${AFFINABLES.join(', ')}`);
|
|
40
|
+
}
|
|
41
|
+
// ⚠️ LE MOTIF EST EXIGÉ. Un affinage sans motif est une recopie qui s'ignore : six mois plus
|
|
42
|
+
// tard, personne ne sait si l'écart était une nécessité du métier ou une facilité du moment —
|
|
43
|
+
// et c'est justement cet écart qui doit nourrir la version suivante de la fiche.
|
|
44
|
+
if (!propre(val?.motif)) {
|
|
45
|
+
throw new Error(`instantiate(${kind.ref}) : affinage de « ${champ} » sans \`motif\` — sans lui, « réutiliser » se dégrade en « recopier »`);
|
|
46
|
+
}
|
|
47
|
+
affinages.push({ champ, motif: propre(val.motif) });
|
|
48
|
+
resultat[champ] = val.valeur;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// L'instance AFFINÉE doit rester une fiche valide : on n'affine pas jusqu'à casser l'exigence.
|
|
52
|
+
const refus = validateKind(resultat);
|
|
53
|
+
if (refus.length) throw new Error(`instantiate(${kind.ref}) : l’affinage casse la fiche — ${refus.join(' · ')}`);
|
|
54
|
+
|
|
55
|
+
return Object.freeze({
|
|
56
|
+
kind: kind.ref,
|
|
57
|
+
kindVersion: kind.version,
|
|
58
|
+
app: propre(app),
|
|
59
|
+
prefix: propre(prefix) || propre(app).toUpperCase(),
|
|
60
|
+
affinages,
|
|
61
|
+
fiche: Object.freeze(resultat),
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Ce que cette application a changé, et pourquoi — lisible d'un coup d'œil. */
|
|
66
|
+
export function diffInstance(instance) {
|
|
67
|
+
if (!instance?.affinages?.length) return [];
|
|
68
|
+
return instance.affinages.map((a) => `${a.champ} : ${a.motif}`);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* QUI EMPLOIE QUOI — l'index inverse.
|
|
73
|
+
* C'est ce qui rend l'affinage sûr dans les deux sens : on sait qui prévenir quand une fiche
|
|
74
|
+
* change, et on lit, en face de chaque fiche, tout ce que le terrain a dû lui faire dire.
|
|
75
|
+
*/
|
|
76
|
+
export function emplois(instances = []) {
|
|
77
|
+
const par = new Map();
|
|
78
|
+
for (const i of instances) {
|
|
79
|
+
const e = par.get(i.kind) || { kind: i.kind, apps: [], affinages: [] };
|
|
80
|
+
e.apps.push(i.app);
|
|
81
|
+
for (const a of i.affinages) e.affinages.push({ app: i.app, ...a });
|
|
82
|
+
par.set(i.kind, e);
|
|
83
|
+
}
|
|
84
|
+
return [...par.values()].sort((a, b) => b.apps.length - a.apps.length);
|
|
85
|
+
}
|
package/src/kind.js
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @mostajs/kind-catalog — LA FICHE D'EXIGENCE RÉUTILISABLE.
|
|
3
|
+
*
|
|
4
|
+
* ── CE QU'EST UN KIND ──────────────────────────────────────────────────────
|
|
5
|
+
* Une **question qu'un métier se pose**, avec ce qu'il faut pour y répondre, comment on l'éprouve,
|
|
6
|
+
* à quoi on reconnaît une bonne réponse, et **les façons connues de se tromper**.
|
|
7
|
+
*
|
|
8
|
+
* ⚠️ UN KIND N'EST PAS UN ALGORITHME. `@mostajs/ro-pla` porte 23 dialectes pour 12 familles :
|
|
9
|
+
* plusieurs questions métier partagent le même moteur, et un moteur ne dit rien de la question.
|
|
10
|
+
* Ce qu'un exploitant active, c'est une question ; ce qu'un développeur choisit, c'est un moteur.
|
|
11
|
+
*
|
|
12
|
+
* ── CE QUE CE MODULE NE FAIT PAS ───────────────────────────────────────────
|
|
13
|
+
* Il **n'exécute rien** (ni solveur, ni essai, ni règle) et **ne stocke rien** (aucun schéma,
|
|
14
|
+
* aucun dépôt). Il décrit, valide et PROJETTE. Un essai le compte (`T-KC-12`).
|
|
15
|
+
*
|
|
16
|
+
* ── LA PROVENANCE, ET POURQUOI ELLE N'INTERDIT RIEN ────────────────────────
|
|
17
|
+
* Les grands catalogues du monde n'admettent que des faits : ATT&CK ne consigne que des techniques
|
|
18
|
+
* OBSERVÉES, CWE naît de vulnérabilités RÉELLES. Mais « fait » ne veut pas dire « incident chez
|
|
19
|
+
* nous » : un DTU, une fiche HACCP, un itinéraire technique sont des faits établis par un métier
|
|
20
|
+
* entier, et les ignorer laisserait le catalogue vide là où le savoir existe depuis des décennies.
|
|
21
|
+
*
|
|
22
|
+
* D'où la règle réelle : **toute fiche déclare d'où elle vient**, et son VERDICT dit ce qu'elle a
|
|
23
|
+
* traversé.
|
|
24
|
+
*
|
|
25
|
+
* `reference` → tirée d'un corpus établi (norme, guide de bonnes pratiques, littérature)
|
|
26
|
+
* `terrain` → tirée du dire d'un praticien, nommé et daté
|
|
27
|
+
* `incident` → tirée d'un défaut CONSTATÉ, avec sa date et son fichier
|
|
28
|
+
*
|
|
29
|
+
* Une fiche `propose` peut n'avoir qu'une `reference` — et elle a toute sa place au catalogue.
|
|
30
|
+
* Une fiche `eprouve` ou `retenu` exige au moins un `incident` ou un `terrain` : **on ne se
|
|
31
|
+
* décerne pas l'expérience**. La contrainte ne porte donc pas sur l'ENTRÉE au catalogue, elle
|
|
32
|
+
* porte sur le GALON qu'on s'y donne.
|
|
33
|
+
*
|
|
34
|
+
* Author: Dr Hamid MADANI <drmdh@msn.com> · AGPL-3.0-or-later
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Les états d'une fiche.
|
|
39
|
+
* ⚠️ `ecarte` RESTE au catalogue, avec son motif : une fiche écartée qui disparaît est une fiche
|
|
40
|
+
* qui sera réinventée, avec les mêmes espoirs et le même échec.
|
|
41
|
+
*/
|
|
42
|
+
export const VERDICTS = ['propose', 'eprouve', 'retenu', 'ecarte'];
|
|
43
|
+
|
|
44
|
+
/** Ce qui atteste qu'une fiche décrit quelque chose de réel. */
|
|
45
|
+
export const PROVENANCES = ['reference', 'terrain', 'incident'];
|
|
46
|
+
|
|
47
|
+
/** Les provenances qui autorisent à se dire ÉPROUVÉ : on ne se décerne pas l'expérience. */
|
|
48
|
+
const PROVENANCES_EPROUVANTES = new Set(['incident', 'terrain']);
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Les domaines déclarés. Vocabulaire OUVERT — mais déclaré : un domaine surgi sans déclaration
|
|
52
|
+
* échappe à la revue, et le catalogue se fragmente en synonymes (`acces`, `access`, `droits`…).
|
|
53
|
+
* Tenu au §5.bis de `docs/PLAN-DEV-KIND-CATALOG.md`.
|
|
54
|
+
*/
|
|
55
|
+
export const DOMAINES = [
|
|
56
|
+
// transverses — nés d'incidents de l'écosystème
|
|
57
|
+
'acces', 'donnees', 'apprentissage', 'decision', 'integration',
|
|
58
|
+
// métier
|
|
59
|
+
'btp', 'alimentaire', 'elevage', 'apiculture', 'agronomie', 'electronique',
|
|
60
|
+
];
|
|
61
|
+
|
|
62
|
+
const propre = (v) => String(v ?? '').trim();
|
|
63
|
+
const liste = (v) => (Array.isArray(v) ? v : v == null ? [] : [v]);
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Déclare une fiche. Refuse au montage ce qui la rendrait inutilisable, et NOMME tout ce qui
|
|
67
|
+
* manque d'un seul coup : corriger un manque pour en découvrir un autre fait abandonner.
|
|
68
|
+
*/
|
|
69
|
+
export function defineKind(f = {}) {
|
|
70
|
+
const k = {
|
|
71
|
+
ref: propre(f.ref),
|
|
72
|
+
enonce: propre(f.enonce),
|
|
73
|
+
domaine: propre(f.domaine) || 'decision',
|
|
74
|
+
version: propre(f.version) || '1',
|
|
75
|
+
besoins: liste(f.besoins),
|
|
76
|
+
utilisation: propre(f.utilisation),
|
|
77
|
+
succes: liste(f.succes).map(propre).filter(Boolean),
|
|
78
|
+
erreurs: liste(f.erreurs),
|
|
79
|
+
test: liste(f.test),
|
|
80
|
+
// Renvois — jamais recalculés ici : l'efficacité appartient à @mostajs/skill-library, et
|
|
81
|
+
// deux mémoires d'efficacité finiraient par se contredire.
|
|
82
|
+
efficacite: f.efficacite ?? null,
|
|
83
|
+
evaluation: f.evaluation ?? null,
|
|
84
|
+
verdict: propre(f.verdict) || 'propose',
|
|
85
|
+
motif: propre(f.motif) || null,
|
|
86
|
+
origine: liste(f.origine),
|
|
87
|
+
// OÙ la question se pose, quand ce n'est pas là où elle se calcule. Le corpus transverse n'en
|
|
88
|
+
// avait pas besoin ; le BTP (sur chantier) et l'électronique (sur l'embarqué) l'exigent.
|
|
89
|
+
seProsePose: propre(f.seProsePose) || null,
|
|
90
|
+
};
|
|
91
|
+
const refus = validateKind(k);
|
|
92
|
+
if (refus.length) throw new Error(`defineKind(${k.ref || '?'}) : ${refus.join(' · ')}`);
|
|
93
|
+
return Object.freeze(k);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Tout ce qui cloche, en une fois.
|
|
98
|
+
*
|
|
99
|
+
* ⚠️ APPLIQUE LES MÊMES DÉFAUTS QUE `defineKind`. Sans cela, valider un objet BRUT — celui qu'on
|
|
100
|
+
* vient d'écrire, avant de le passer à `defineKind` — reprochait un `verdict` manquant alors que
|
|
101
|
+
* le champ est facultatif. L'auteur corrigeait un reproche qui n'en était pas un, et la fonction
|
|
102
|
+
* mentait sur son propre contrat (constat du 02/09/2026, par la démonstration `demo-epreuve.mjs`).
|
|
103
|
+
*/
|
|
104
|
+
export function validateKind(brut = {}) {
|
|
105
|
+
const k = { verdict: 'propose', domaine: 'decision', ...brut };
|
|
106
|
+
if (!propre(k.verdict)) k.verdict = 'propose';
|
|
107
|
+
if (!propre(k.domaine)) k.domaine = 'decision';
|
|
108
|
+
const out = [];
|
|
109
|
+
if (!propre(k.ref)) out.push('`ref` requise');
|
|
110
|
+
else if (!/^KIND-[A-Z0-9-]+$/.test(k.ref)) out.push(`\`ref\` doit valoir KIND-… (reçu « ${k.ref} »)`);
|
|
111
|
+
if (!propre(k.enonce)) out.push('`enonce` requis — une fiche sans question n’est pas une exigence');
|
|
112
|
+
|
|
113
|
+
if (!DOMAINES.includes(k.domaine)) {
|
|
114
|
+
out.push(`\`domaine\` non déclaré : ${k.domaine} — déclarés : ${DOMAINES.join(', ')}`);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// ⚠️ `succes` et `erreurs` sont les deux seuls champs exigés en plus de l'énoncé, et c'est
|
|
118
|
+
// délibéré : ce sont ceux qu'on omet, et ce sont ceux qui servent.
|
|
119
|
+
if (!liste(k.succes).length) out.push('`succes` requis — sans critère écrit, tout résultat paraît bon');
|
|
120
|
+
if (!liste(k.erreurs).length) out.push('`erreurs` requis — une fiche sans façon connue de se tromper n’apprend rien, et c’est la seule raison d’en reprendre une');
|
|
121
|
+
|
|
122
|
+
for (const [i, e] of liste(k.erreurs).entries()) {
|
|
123
|
+
if (!propre(e?.titre)) out.push(`erreurs[${i}] : \`titre\` requis`);
|
|
124
|
+
// La CONSÉQUENCE distingue un avertissement d'une consigne : « ne pas oublier le périmètre »
|
|
125
|
+
// ne se retient pas ; « sans lui, tout parent lit le dossier de tous » se retient. CWE/OWASP.
|
|
126
|
+
if (!propre(e?.consequence)) out.push(`erreurs[${i}] : \`consequence\` requise — un avertissement sans sa conséquence ne se retient pas`);
|
|
127
|
+
}
|
|
128
|
+
for (const [i, t] of liste(k.test).entries()) {
|
|
129
|
+
if (!propre(t?.action)) out.push(`test[${i}] : \`action\` requise`);
|
|
130
|
+
if (!propre(t?.attendu)) out.push(`test[${i}] : \`attendu\` requis`);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (!VERDICTS.includes(k.verdict)) out.push(`\`verdict\` inconnu : ${k.verdict} — attendus : ${VERDICTS.join(', ')}`);
|
|
134
|
+
if (k.verdict === 'ecarte' && !propre(k.motif)) {
|
|
135
|
+
out.push('`motif` requis pour un verdict `ecarte` — une fiche écartée sans motif sera réinventée');
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// ── LA PROVENANCE ────────────────────────────────────────────────────────
|
|
139
|
+
const origines = liste(k.origine);
|
|
140
|
+
if (!origines.length) out.push('`origine` requise — une fiche sans provenance est une idée déguisée');
|
|
141
|
+
for (const [i, o] of origines.entries()) {
|
|
142
|
+
if (!PROVENANCES.includes(propre(o?.type))) {
|
|
143
|
+
out.push(`origine[${i}] : \`type\` inconnu — attendus : ${PROVENANCES.join(', ')}`);
|
|
144
|
+
}
|
|
145
|
+
if (!propre(o?.source)) out.push(`origine[${i}] : \`source\` requise — norme, praticien ou fichier`);
|
|
146
|
+
}
|
|
147
|
+
if (['eprouve', 'retenu'].includes(k.verdict)
|
|
148
|
+
&& !origines.some((o) => PROVENANCES_EPROUVANTES.has(propre(o?.type)))) {
|
|
149
|
+
out.push(`verdict \`${k.verdict}\` exige une origine \`incident\` ou \`terrain\` — on ne se décerne pas l’expérience`);
|
|
150
|
+
}
|
|
151
|
+
return out;
|
|
152
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LA PROJECTION — une fiche devient des EXIGENCES et des ESSAIS au format `mostajs-devtest/1`.
|
|
3
|
+
*
|
|
4
|
+
* ⚠️ AUCUN FORMAT NOUVEAU, et c'est la décision structurante du module. `@mostajs/qa-engine` parse
|
|
5
|
+
* déjà `mostajs-devtest/1`, et qatrax l'importe déjà. Inventer un second format aurait demandé un
|
|
6
|
+
* second validateur, un second import, un second écran — et le jour où les deux divergent, c'est
|
|
7
|
+
* le catalogue qui aurait raison contre les faits.
|
|
8
|
+
*
|
|
9
|
+
* ⚠️ C'EST ICI QUE LE CATALOGUE DEVIENT UTILE. L'état de l'art le montre : CWE est branché aux
|
|
10
|
+
* scanners et s'applique ; le catalogue de patrons d'exigences de Withall n'est branché à rien et
|
|
11
|
+
* est resté un livre. **Une fiche qui ne descend pas dans le plan de test du projet ne sera pas
|
|
12
|
+
* appliquée**, quelle que soit sa qualité.
|
|
13
|
+
*
|
|
14
|
+
* Author: Dr Hamid MADANI <drmdh@msn.com> · AGPL-3.0-or-later
|
|
15
|
+
*/
|
|
16
|
+
export const PLAN_VERSION = 'mostajs-devtest/1';
|
|
17
|
+
|
|
18
|
+
const propre = (v) => String(v ?? '').trim();
|
|
19
|
+
/** `KIND-PERIMETRE-01` → `PERIMETRE-01` : la référence projetée porte le préfixe de l'application. */
|
|
20
|
+
const corps = (ref) => propre(ref).replace(/^KIND-/, '');
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Projette des fiches en plan Dev+Test.
|
|
24
|
+
*
|
|
25
|
+
* @param {object[]} kinds
|
|
26
|
+
* @param {object} o
|
|
27
|
+
* @param {{key:string,name:string,description?:string}} o.project le projet, au sens qatrax
|
|
28
|
+
* @param {string} o.prefix préfixe des références — `ATC`, `RESTO`… ISOLE deux applications qui
|
|
29
|
+
* reprennent la même fiche : sans lui, leurs plans se marcheraient dessus dans le même qatrax.
|
|
30
|
+
*/
|
|
31
|
+
export function toDevtest(kinds = [], { project, prefix = 'APP' } = {}) {
|
|
32
|
+
if (!project?.key || !project?.name) throw new Error('toDevtest: `project` { key, name } requis');
|
|
33
|
+
const p = propre(prefix).toUpperCase();
|
|
34
|
+
const plan = { plan: PLAN_VERSION, project: { ...project }, specs: [], realisations: [], tests: [] };
|
|
35
|
+
|
|
36
|
+
for (const k of kinds) {
|
|
37
|
+
const specRef = `SPEC-${p}-${corps(k.ref)}`;
|
|
38
|
+
plan.specs.push({
|
|
39
|
+
ref: specRef,
|
|
40
|
+
title: k.enonce,
|
|
41
|
+
priority: 'critical',
|
|
42
|
+
// Le verdict de la FICHE devient le statut de l'exigence : une fiche seulement `propose`
|
|
43
|
+
// ne doit pas apparaître comme vérifiée dans le suivi du projet.
|
|
44
|
+
status: k.verdict === 'eprouve' || k.verdict === 'retenu' ? 'verified' : 'draft',
|
|
45
|
+
// LA DESCRIPTION PORTE LES ERREURS CONNUES. C'est tout l'apport du catalogue : celui qui lit
|
|
46
|
+
// l'exigence dans qatrax reçoit AUSSI ce qui a mal tourné ailleurs, avec la conséquence.
|
|
47
|
+
description: [
|
|
48
|
+
k.utilisation ? `Emploi : ${k.utilisation}` : null,
|
|
49
|
+
k.succes.length ? `Réussi si : ${k.succes.join(' · ')}` : null,
|
|
50
|
+
...k.erreurs.map((e) => `⚠ ${e.titre} — ${e.consequence}`),
|
|
51
|
+
`Fiche ${k.ref} v${k.version} (${k.verdict})`,
|
|
52
|
+
].filter(Boolean).join('\n'),
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
plan.realisations.push({
|
|
56
|
+
ref: `REL-${p}-${corps(k.ref)}`,
|
|
57
|
+
specRef,
|
|
58
|
+
kind: 'feature',
|
|
59
|
+
title: `Reprise de la fiche ${k.ref}`,
|
|
60
|
+
progress: 0,
|
|
61
|
+
status: 'todo',
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
k.test.forEach((t, i) => {
|
|
65
|
+
plan.tests.push({
|
|
66
|
+
ref: `T-${p}-${corps(k.ref)}-${i + 1}`,
|
|
67
|
+
specRef,
|
|
68
|
+
title: `T-${p}-${corps(k.ref)}-${i + 1} — ${t.action}`,
|
|
69
|
+
type: 'automated',
|
|
70
|
+
priority: 'critical',
|
|
71
|
+
steps: [{ action: t.action, expected: t.attendu }],
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
return plan;
|
|
76
|
+
}
|