@sigloch/se-engine 1.1.0 → 1.3.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/dist/fix-templates.js +50 -14
- package/dist/readiness-compute.d.ts +35 -1
- package/dist/readiness-compute.js +65 -4
- package/dist/rule-apply.js +10 -2
- package/package.json +4 -3
package/dist/fix-templates.js
CHANGED
|
@@ -69,7 +69,10 @@ function relationToMentioned(types) {
|
|
|
69
69
|
continue;
|
|
70
70
|
if (hasTrace(g, el.id, target.id, 'relation'))
|
|
71
71
|
continue;
|
|
72
|
-
if (!isValidTrace({
|
|
72
|
+
if (!isValidTrace({
|
|
73
|
+
source: el.type, target: target.type, type: 'relation',
|
|
74
|
+
sourceKinds: el.kinds, targetKinds: target.kinds,
|
|
75
|
+
}))
|
|
73
76
|
continue;
|
|
74
77
|
return {
|
|
75
78
|
op: 'add-trace',
|
|
@@ -109,7 +112,10 @@ function edgeToMentioned(opts) {
|
|
|
109
112
|
const target = opts.direction === 'out' ? other : el;
|
|
110
113
|
if (hasTrace(g, source.id, target.id, opts.traceType))
|
|
111
114
|
continue;
|
|
112
|
-
if (!isValidTrace({
|
|
115
|
+
if (!isValidTrace({
|
|
116
|
+
source: source.type, target: target.type, type: opts.traceType,
|
|
117
|
+
sourceKinds: source.kinds, targetKinds: target.kinds,
|
|
118
|
+
}))
|
|
113
119
|
continue;
|
|
114
120
|
return {
|
|
115
121
|
op: 'add-trace',
|
|
@@ -150,7 +156,10 @@ export const FIX_TEMPLATES = {
|
|
|
150
156
|
const ms = mentioned[0] ?? (milestones.length === 1 ? milestones[0] : undefined);
|
|
151
157
|
if (!ms || hasTrace(g, cr.id, ms.id, 'relation'))
|
|
152
158
|
return null;
|
|
153
|
-
if (!isValidTrace({
|
|
159
|
+
if (!isValidTrace({
|
|
160
|
+
source: cr.type, target: ms.type, type: 'relation',
|
|
161
|
+
sourceKinds: cr.kinds, targetKinds: ms.kinds,
|
|
162
|
+
}))
|
|
154
163
|
return null;
|
|
155
164
|
return {
|
|
156
165
|
op: 'add-trace',
|
|
@@ -162,23 +171,50 @@ export const FIX_TEMPLATES = {
|
|
|
162
171
|
: `${ms.id} ist der einzige Milestone im Graphen`,
|
|
163
172
|
};
|
|
164
173
|
},
|
|
174
|
+
/**
|
|
175
|
+
* CR-SM-266 D1: der Vorschlag geht an den FLOW, nicht mehr an den UC.
|
|
176
|
+
*
|
|
177
|
+
* Die alte Fassung schlug `ACTOR -io-> UC` vor. Mit dem Wegfall des Patterns waere sie
|
|
178
|
+
* still gestorben — `isValidTrace` haette jeden Kandidaten verworfen, die Schleife immer
|
|
179
|
+
* `null` geliefert, und UC-02 haette einen Fix-Template-Eintrag OHNE Wirkung behalten: ein
|
|
180
|
+
* Vorschlagspfad, der nichts mehr vorschlaegt, ist schlimmer als keiner, weil niemand ihn
|
|
181
|
+
* vermisst.
|
|
182
|
+
*
|
|
183
|
+
* Der tragende Pfad ist jetzt `ACTOR -io-> FLOW`, wobei der FLOW ein Kettenglied des UC
|
|
184
|
+
* speisen muss — sonst ist die Kante zwar gueltig, macht UC-02 aber nicht gruen und der
|
|
185
|
+
* Vorschlag waere eine Kante ins Leere. Deshalb wird der FLOW nicht geraten, sondern aus
|
|
186
|
+
* dem Graphen HERGELEITET: die FLOWs, die in eine FUNC einer FCHAIN dieses UC laufen.
|
|
187
|
+
* Ist keiner da, gibt das Template `null` — der fehlende FLOW ist dann die eigentliche
|
|
188
|
+
* Luecke und gehoert R-31, nicht einer erfundenen Actor-Kante.
|
|
189
|
+
*/
|
|
165
190
|
'UC-02': (v, g) => {
|
|
166
191
|
const uc = byId(g, v.element_id);
|
|
167
192
|
if (!uc)
|
|
168
193
|
return null;
|
|
194
|
+
const chainMembers = new Set(g.traces
|
|
195
|
+
.filter((t) => t.type === 'compose' && t.source === uc.id)
|
|
196
|
+
.flatMap((chain) => g.traces.filter((t) => t.type === 'compose' && t.source === chain.target).map((t) => t.target)));
|
|
197
|
+
const feedingFlows = g.traces
|
|
198
|
+
.filter((t) => t.type === 'io' && chainMembers.has(t.target))
|
|
199
|
+
.map((t) => byId(g, t.source))
|
|
200
|
+
.filter((e) => e?.type === 'FLOW');
|
|
201
|
+
if (feedingFlows.length === 0)
|
|
202
|
+
return null;
|
|
169
203
|
const actors = mentionedElements(`${uc.name} ${uc.description ?? ''}`, g, ['ACTOR']);
|
|
170
204
|
for (const actor of actors) {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
205
|
+
for (const flow of feedingFlows) {
|
|
206
|
+
if (hasTrace(g, actor.id, flow.id, 'io'))
|
|
207
|
+
continue;
|
|
208
|
+
if (!isValidTrace({ source: actor.type, target: flow.type, type: 'io' }))
|
|
209
|
+
continue;
|
|
210
|
+
return {
|
|
211
|
+
op: 'add-trace',
|
|
212
|
+
source: actor.id,
|
|
213
|
+
target: flow.id,
|
|
214
|
+
type: 'io',
|
|
215
|
+
rationale: `${actor.id} ist im Text von ${uc.id} genannt, und ${flow.id} speist dessen Wirkkette`,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
182
218
|
}
|
|
183
219
|
return null;
|
|
184
220
|
},
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Relocated from aimpro (learning-engine/graph/readiness.ts) into @sigloch/se-engine (CR-SM-248).
|
|
5
5
|
*/
|
|
6
6
|
import type { OntologyGraph, MetricPolicy } from '@sigloch/contracts/se';
|
|
7
|
-
import { type ReadinessReportType } from '@sigloch/contracts/se';
|
|
7
|
+
import { type ReadinessDimensionType, type ReadinessReportType } from '@sigloch/contracts/se';
|
|
8
8
|
/**
|
|
9
9
|
* Compute readiness report from the current graph state.
|
|
10
10
|
*
|
|
@@ -25,3 +25,37 @@ import { type ReadinessReportType } from '@sigloch/contracts/se';
|
|
|
25
25
|
* (CR-GC-329); diese Funktion macht nur die Zweitmeinung unmoeglich.
|
|
26
26
|
*/
|
|
27
27
|
export declare function computeReadiness(graph: OntologyGraph, policy: MetricPolicy, readyThreshold: number): ReadinessReportType;
|
|
28
|
+
/**
|
|
29
|
+
* CR-146 / CR-SM-235: Anzahl anwendbarer Pruefungen je Dimension.
|
|
30
|
+
*
|
|
31
|
+
* `applicable = Σ (Elemente der Grundgesamtheit)` ueber die Regeln der Dimension. Die
|
|
32
|
+
* Grundgesamtheit steht seit CR-SM-235 an der Regel selbst (`ALL_RULE_DEFS[].domain`) —
|
|
33
|
+
* vorher lag hier eine Handtabelle `RULE_ELEMENT_TYPE`, in der **18 von 71 Regeln** fehlten.
|
|
34
|
+
* Deren Verstoesse erhoehten den Zaehler, nie den Nenner; auf graph-view-edit waren das 38
|
|
35
|
+
* von 415 Verstoessen. Jede neue Regel senkte den Score automatisch, bis jemand die zweite
|
|
36
|
+
* Tabelle nachzog, und kein Test erzwang das.
|
|
37
|
+
*
|
|
38
|
+
* Eine Regel mit mehreren Typen (RD-04: FUNC, MOD, SYS) traegt deren Summe bei — dieselben
|
|
39
|
+
* Elemente, die sie auch pruefen kann.
|
|
40
|
+
*/
|
|
41
|
+
/**
|
|
42
|
+
* CR-SM-270: die KERNMENGE je Dimension — die Elemente der Typen, um die es der Dimension
|
|
43
|
+
* wirklich geht.
|
|
44
|
+
*
|
|
45
|
+
* Der Anlass, gemessen: `moneyflow` ist ein reiner Code-Import (0 UC, 0 FCHAIN, 0 ACTOR,
|
|
46
|
+
* 306 FUNC) und meldete `uc: 0,997, ready: true`. Von den 15 uc-Regeln haben dreizehn eine
|
|
47
|
+
* leere Grundgesamtheit — sie schweigen mangels Gegenstand —, R-17 traegt 1 (SYS) und FC-03
|
|
48
|
+
* traegt 306 (FUNC). 99,7 % des Nenners kamen also aus EINER Fremdtyp-Regel, und das Schweigen
|
|
49
|
+
* der dreizehn las sich als Erfolg. Je mehr importierte FUNCs ein Repo ohne Wozu-Ebene hat,
|
|
50
|
+
* desto besser sah seine uc-Readiness aus — der Score zeigte in die falsche Richtung, und
|
|
51
|
+
* `dimension_readiness` ist Steuergroesse fuer `graph_next_step`.
|
|
52
|
+
*
|
|
53
|
+
* ABGELEITET, nicht deklariert: der Kern sind die Typen, die die MEHRHEIT der Dimensionsregeln
|
|
54
|
+
* prueft. Eine zweite Tabelle neben `RULE_TO_DIMENSION` waere ein zweiter Speicher derselben
|
|
55
|
+
* Wahrheit und liefe auseinander, sobald jemand nur eine von beiden pflegt — genau der Defekt,
|
|
56
|
+
* den CR-SM-235 hier schon einmal beseitigt hat (`RULE_ELEMENT_TYPE`, 18 von 71 Regeln
|
|
57
|
+
* fehlten). Ein Attribut waere zusaetzlich eine Selbstauskunft (abgelehnt in CR-SM-263).
|
|
58
|
+
*
|
|
59
|
+
* Graph-Regeln zaehlen auch hier nicht mit: sie haben keine Gruppe (CR-SM-239).
|
|
60
|
+
*/
|
|
61
|
+
export declare function coreTypesOf(dim: ReadinessDimensionType): string[];
|
|
@@ -43,7 +43,8 @@ export function computeReadiness(graph, policy, readyThreshold) {
|
|
|
43
43
|
// Die Verstoesse sind nicht verloren: sie stehen im Regelstrom und auf der Gate-Achse.
|
|
44
44
|
const graphLevelRules = new Set(ALL_RULE_DEFS.filter(d => d.domain.includes('graph')).map(d => d.id));
|
|
45
45
|
// Count elements by type — used for applicable counts AND phase gate checks
|
|
46
|
-
|
|
46
|
+
// CR-SM-266 D5: kein SESSION-Ausschluss mehr — den Elementtyp gibt es nicht.
|
|
47
|
+
const els = graph.elements;
|
|
47
48
|
const countByType = {};
|
|
48
49
|
for (const e of els) {
|
|
49
50
|
countByType[e.type] = (countByType[e.type] ?? 0) + 1;
|
|
@@ -59,16 +60,31 @@ export function computeReadiness(graph, policy, readyThreshold) {
|
|
|
59
60
|
continue;
|
|
60
61
|
violationsByDim.get(dim).push(v);
|
|
61
62
|
}
|
|
63
|
+
const coreCounts = computeCoreApplicable(countByType);
|
|
62
64
|
const scores = ReadinessDimension.options.map(dim => {
|
|
63
65
|
const violations = violationsByDim.get(dim).length;
|
|
64
66
|
const applicable = applicableCounts[dim];
|
|
65
|
-
const
|
|
67
|
+
const coreApplicable = coreCounts[dim];
|
|
68
|
+
// CR-SM-270: der Interpretierbarkeits-Waechter VOR der Rechnung. Ist die Kernmenge leer,
|
|
69
|
+
// wird nicht gerechnet — `null`, nicht 0,997 und nicht 0,0. Der Nenner selbst bleibt
|
|
70
|
+
// unangetastet: wo die Kernmenge da ist, zaehlen die Fremdtyp-Beitraege weiter mit.
|
|
71
|
+
//
|
|
72
|
+
// Die Reihenfolge ist Absicht: `applicable === 0` behaelt seine 0,0. Der Fall ist mit
|
|
73
|
+
// demselben Argument angreifbar ("nichts zu pruefen ist nicht 0 %"), aber CR-SM-270
|
|
74
|
+
// verlangt ihn ausdruecklich UNVERAENDERT, und ein stiller Mitnahmeeffekt waere hier die
|
|
75
|
+
// schlechtere Wahl als eine eigene Entscheidung dazu. Der Waechter greift also nur im
|
|
76
|
+
// wirklich neuen Fall: Kernmenge leer, Fremdtyp-Menge gross.
|
|
77
|
+
const score = applicable === 0 ? 0.0
|
|
78
|
+
: coreApplicable === 0 ? null
|
|
79
|
+
: Math.max(0, 1 - violations / applicable);
|
|
66
80
|
return {
|
|
67
81
|
dimension: dim,
|
|
68
|
-
score: Math.round(score * 1000) / 1000, // 3 decimal precision
|
|
82
|
+
score: score === null ? null : Math.round(score * 1000) / 1000, // 3 decimal precision
|
|
69
83
|
violations,
|
|
70
84
|
applicable,
|
|
71
|
-
|
|
85
|
+
coreApplicable,
|
|
86
|
+
// Nicht messbar ist nicht bereit — nie `true` bei `null`.
|
|
87
|
+
ready: score !== null && score >= readyThreshold,
|
|
72
88
|
};
|
|
73
89
|
});
|
|
74
90
|
// CR-SM-237: `overallScore` faellt — das ungewichtete Mittel dieser Scores, von keinem
|
|
@@ -94,6 +110,51 @@ export function computeReadiness(graph, policy, readyThreshold) {
|
|
|
94
110
|
* Eine Regel mit mehreren Typen (RD-04: FUNC, MOD, SYS) traegt deren Summe bei — dieselben
|
|
95
111
|
* Elemente, die sie auch pruefen kann.
|
|
96
112
|
*/
|
|
113
|
+
/**
|
|
114
|
+
* CR-SM-270: die KERNMENGE je Dimension — die Elemente der Typen, um die es der Dimension
|
|
115
|
+
* wirklich geht.
|
|
116
|
+
*
|
|
117
|
+
* Der Anlass, gemessen: `moneyflow` ist ein reiner Code-Import (0 UC, 0 FCHAIN, 0 ACTOR,
|
|
118
|
+
* 306 FUNC) und meldete `uc: 0,997, ready: true`. Von den 15 uc-Regeln haben dreizehn eine
|
|
119
|
+
* leere Grundgesamtheit — sie schweigen mangels Gegenstand —, R-17 traegt 1 (SYS) und FC-03
|
|
120
|
+
* traegt 306 (FUNC). 99,7 % des Nenners kamen also aus EINER Fremdtyp-Regel, und das Schweigen
|
|
121
|
+
* der dreizehn las sich als Erfolg. Je mehr importierte FUNCs ein Repo ohne Wozu-Ebene hat,
|
|
122
|
+
* desto besser sah seine uc-Readiness aus — der Score zeigte in die falsche Richtung, und
|
|
123
|
+
* `dimension_readiness` ist Steuergroesse fuer `graph_next_step`.
|
|
124
|
+
*
|
|
125
|
+
* ABGELEITET, nicht deklariert: der Kern sind die Typen, die die MEHRHEIT der Dimensionsregeln
|
|
126
|
+
* prueft. Eine zweite Tabelle neben `RULE_TO_DIMENSION` waere ein zweiter Speicher derselben
|
|
127
|
+
* Wahrheit und liefe auseinander, sobald jemand nur eine von beiden pflegt — genau der Defekt,
|
|
128
|
+
* den CR-SM-235 hier schon einmal beseitigt hat (`RULE_ELEMENT_TYPE`, 18 von 71 Regeln
|
|
129
|
+
* fehlten). Ein Attribut waere zusaetzlich eine Selbstauskunft (abgelehnt in CR-SM-263).
|
|
130
|
+
*
|
|
131
|
+
* Graph-Regeln zaehlen auch hier nicht mit: sie haben keine Gruppe (CR-SM-239).
|
|
132
|
+
*/
|
|
133
|
+
export function coreTypesOf(dim) {
|
|
134
|
+
const defs = ALL_RULE_DEFS.filter(d => RULE_TO_DIMENSION[d.id] === dim && !d.domain.includes('graph'));
|
|
135
|
+
if (defs.length === 0)
|
|
136
|
+
return [];
|
|
137
|
+
const seenIn = {};
|
|
138
|
+
for (const d of defs) {
|
|
139
|
+
// Ein Typ zaehlt EINMAL je Regel, auch wenn die Regel mehrere Typen prueft (RD-04).
|
|
140
|
+
for (const type of new Set(d.domain))
|
|
141
|
+
seenIn[type] = (seenIn[type] ?? 0) + 1;
|
|
142
|
+
}
|
|
143
|
+
const majority = defs.length / 2;
|
|
144
|
+
const core = Object.keys(seenIn).filter(type => seenIn[type] > majority).sort();
|
|
145
|
+
// Keine Mehrheit ableitbar (die Regeln verteilen sich gleichmaessig ueber mehrere Typen):
|
|
146
|
+
// dann ist der Kern ALLES, was die Dimension prueft. Der Waechter greift damit nur noch,
|
|
147
|
+
// wenn auch `applicable` 0 waere — das Verhalten bleibt exakt wie vor CR-SM-270. Lieber
|
|
148
|
+
// nicht greifen als am falschen Ort greifen.
|
|
149
|
+
return core.length > 0 ? core : [...new Set(defs.flatMap(d => d.domain))].sort();
|
|
150
|
+
}
|
|
151
|
+
function computeCoreApplicable(countByType) {
|
|
152
|
+
const result = {};
|
|
153
|
+
for (const dim of ReadinessDimension.options) {
|
|
154
|
+
result[dim] = coreTypesOf(dim).reduce((sum, type) => sum + (countByType[type] ?? 0), 0);
|
|
155
|
+
}
|
|
156
|
+
return result;
|
|
157
|
+
}
|
|
97
158
|
function computeApplicable(countByType) {
|
|
98
159
|
const result = {};
|
|
99
160
|
for (const dim of ReadinessDimension.options) {
|
package/dist/rule-apply.js
CHANGED
|
@@ -65,6 +65,10 @@ export function applyRule(rule, graph, knownViolations) {
|
|
|
65
65
|
// elements' stored types (isValidTrace) and (b) links two currently-
|
|
66
66
|
// unconnected nodes, so it genuinely moves the topology.
|
|
67
67
|
const typeById = new Map(graph.elements.map((e) => [e.id, e.type]));
|
|
68
|
+
// CR-SM-266 B: die where-Patterns lesen die Kinds des Zielknotens mit. Ohne sie wuerde
|
|
69
|
+
// `isValidTrace` jede satisfy-Kante ablehnen und der Operator griffe stumm auf einen anderen
|
|
70
|
+
// Kantentyp aus — ein falscher Vorschlag statt keinem.
|
|
71
|
+
const kindsById = new Map(graph.elements.map((e) => [e.id, e.kinds]));
|
|
68
72
|
const v = violations[0];
|
|
69
73
|
const source = v.element_id;
|
|
70
74
|
const srcType = typeById.get(source);
|
|
@@ -94,7 +98,10 @@ export function applyRule(rule, graph, knownViolations) {
|
|
|
94
98
|
[source, srcType, partner, tgtType],
|
|
95
99
|
[partner, tgtType, source, srcType],
|
|
96
100
|
]) {
|
|
97
|
-
if (isValidTrace({
|
|
101
|
+
if (isValidTrace({
|
|
102
|
+
source: sType, target: tType, type: tt,
|
|
103
|
+
sourceKinds: kindsById.get(s), targetKinds: kindsById.get(t),
|
|
104
|
+
})) {
|
|
98
105
|
return { graph: addTrace(graph, s, t, tt), applied: true, op: `+ ${s} -${tt}-> ${t}` };
|
|
99
106
|
}
|
|
100
107
|
}
|
|
@@ -120,7 +127,8 @@ export function markEmptyDelta(graph, measure) {
|
|
|
120
127
|
});
|
|
121
128
|
}
|
|
122
129
|
/** Trace types the meta-model accepts (ontology TraceType enum). */
|
|
123
|
-
|
|
130
|
+
// CR-SM-266 D5: `produces` entfaellt — der TraceType existiert nicht mehr.
|
|
131
|
+
const TRACE_KEYWORDS = ['verify', 'satisfy', 'allocate', 'compose', 'io', 'relation'];
|
|
124
132
|
/**
|
|
125
133
|
* Best-effort trace type from the violation's fix_hint (topology-only — the
|
|
126
134
|
* exact type does not affect `metrics`, but a plausible one keeps the edit
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sigloch/se-engine",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"scripts": {
|
|
18
18
|
"build": "rm -rf dist && tsc",
|
|
19
19
|
"test": "vitest run",
|
|
20
|
+
"profile:rules": "SE_PROFILE=1 vitest run tests/perf/rule-profile.test.ts",
|
|
20
21
|
"prepublishOnly": "npm run build && npm run test"
|
|
21
22
|
},
|
|
22
23
|
"dependencies": {
|
|
@@ -41,9 +42,9 @@
|
|
|
41
42
|
"access": "public"
|
|
42
43
|
},
|
|
43
44
|
"peerDependencies": {
|
|
44
|
-
"@sigloch/contracts": ">=5 <
|
|
45
|
+
"@sigloch/contracts": ">=5 <10"
|
|
45
46
|
},
|
|
46
47
|
"devDependencies": {
|
|
47
|
-
"@sigloch/contracts": "
|
|
48
|
+
"@sigloch/contracts": ">=6 <10"
|
|
48
49
|
}
|
|
49
50
|
}
|