@sigloch/se-engine 1.2.0 → 1.4.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.d.ts +117 -1
- package/dist/fix-templates.js +380 -22
- package/dist/metrics.d.ts +3 -2
- package/dist/metrics.js +4 -2
- package/dist/readiness-compute.d.ts +35 -1
- package/dist/readiness-compute.js +65 -4
- package/dist/rule-apply.js +10 -2
- package/dist/rule-classify.js +4 -13
- package/dist/steer.d.ts +104 -0
- package/dist/steer.js +89 -0
- package/dist/suggest.d.ts +21 -8
- package/dist/suggest.js +100 -23
- package/package.json +3 -3
|
@@ -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/dist/rule-classify.js
CHANGED
|
@@ -19,7 +19,8 @@
|
|
|
19
19
|
*
|
|
20
20
|
* Delta zum Spike-Original: RD-04 (Dekompositionsbreite, CR-SM-221) ergänzt;
|
|
21
21
|
* SC-01/SC-03 (BOK-CR-026) und MT-03 (CR-SM-223) sind aus dem Katalog gelöscht
|
|
22
|
-
* und daher hier entfernt. CR-SM-226: +R-28/FC-04/SC-04 (neue Regeln; R-28 mit CR-SM-247
|
|
22
|
+
* und daher hier entfernt. CR-SM-226: +R-28/FC-04/SC-04 (neue Regeln; R-28 mit CR-SM-247,
|
|
23
|
+
* SC-04 mit CR-SM-271 wieder entfallen — Untergrenze ist Grammatik/R-18-Bein); IO-01
|
|
23
24
|
* ist nicht mehr cross-module-only (Rationale-Text angepasst, Klasse unverändert).
|
|
24
25
|
* CR-GC-366: +R-30 (Wirkketten-Bindung) und +R-31 (io-Verdrahtung), beide Operator —
|
|
25
26
|
* ihr Fix fügt eine Trace hinzu und bewegt damit die Topologie.
|
|
@@ -55,7 +56,6 @@ export const CLASS_MAP = {
|
|
|
55
56
|
'R-02': { class: 'Operator', rationale: 'add satisfy trace FUNC→REQ' },
|
|
56
57
|
'R-05': { class: 'Operator', rationale: 'add verify trace TEST→REQ' },
|
|
57
58
|
'R-10': { class: 'Operator', rationale: 'add io traces to complete the FLOW' },
|
|
58
|
-
'R-14': { class: 'Operator', rationale: 'add compose trace UC→FCHAIN/REQ' },
|
|
59
59
|
'R-15': { class: 'Operator', rationale: 'add compose trace FCHAIN→FUNC' },
|
|
60
60
|
'R-16': { class: 'Operator', rationale: 'add io trace ACTOR→UC/FLOW' },
|
|
61
61
|
'R-17': { class: 'Operator', rationale: 'add compose trace SYS→child' },
|
|
@@ -70,22 +70,17 @@ export const CLASS_MAP = {
|
|
|
70
70
|
'UC-03': { class: 'Operator', rationale: 'add compose trace UC→FCHAIN (scenario)' },
|
|
71
71
|
'UC-05': { class: 'Operator', rationale: 'add REQ(postcondition) via compose trace' },
|
|
72
72
|
'UC-06': { class: 'Operator', rationale: 'add REQ(precondition) via compose trace' },
|
|
73
|
-
'FC-01': { class: 'Operator', rationale: 'connect chain to ACTOR via FLOW io trace' },
|
|
74
73
|
'FC-02': { class: 'Operator', rationale: 'add FCHAIN via compose trace to leaf UC' },
|
|
75
74
|
'SC-02': { class: 'Operator', rationale: 'link FLOW→SCHEMA via relation trace' },
|
|
76
|
-
'PH-01': { class: 'Operator', rationale: 'add logical MOD via compose trace' },
|
|
77
75
|
'IO-01': { class: 'Operator', rationale: 'add FLOW element + io traces between the FUNC pair' },
|
|
78
76
|
// CR-GC-366: beide Fixes fuegen eine Trace hinzu, also Operator wie R-15 (compose) und R-10 (io).
|
|
79
77
|
'R-30': { class: 'Operator', rationale: 'add compose trace FCHAIN→FUNC to bind the function into a chain' },
|
|
80
78
|
'R-31': { class: 'Operator', rationale: 'add io traces FLOW→FUNC / FUNC→FLOW to wire the function up' },
|
|
81
79
|
'FC-04': { class: 'Operator', rationale: 'add ACTOR→FLOW entry + FUNC→FLOW exit io traces' },
|
|
82
|
-
'SC-04': { class: 'Operator', rationale: 'link FLOW→SCHEMA via relation trace' },
|
|
83
80
|
'CR-R01': { class: 'Operator', rationale: 'add relation traces CR→affected elements' },
|
|
84
|
-
'CR-R04': { class: 'Operator', rationale: 'add relation trace CR→FUNC' },
|
|
85
81
|
'FM-02': { class: 'Operator', rationale: 'create mitigation REQ + compose trace' },
|
|
86
82
|
'FM-03': { class: 'Operator', rationale: 'add TEST(passed) + verify trace to risk REQ' },
|
|
87
83
|
// --- Constraints: remove/repair/reduce, or attribute/text-only -------------
|
|
88
|
-
'R-03': { class: 'Constraint', rationale: 'ASIL isolation — separate mixed levels, non-additive' },
|
|
89
84
|
'R-04': { class: 'Constraint', rationale: 'module too large — split, non-additive' },
|
|
90
85
|
'R-08': { class: 'Constraint', rationale: 'repair dangling trace endpoint' },
|
|
91
86
|
'R-12': { class: 'Constraint', rationale: 'break dependency cycle — remove an edge' },
|
|
@@ -97,10 +92,9 @@ export const CLASS_MAP = {
|
|
|
97
92
|
'R-29': { class: 'Constraint', rationale: 'test file claimed twice — which acceptance owns it is a judgement call' },
|
|
98
93
|
'R-20': { class: 'Constraint', rationale: 'add realRef attribute — no topology change' },
|
|
99
94
|
'R-26': { class: 'Constraint', rationale: 'add realRef attribute — no topology change' },
|
|
100
|
-
'R-27': { class: 'Constraint', rationale: 'physical MOD add realRef attribute — no topology change' },
|
|
101
95
|
'RD-02': { class: 'Constraint', rationale: 'decomposition consistency — repair existing' },
|
|
102
96
|
'RD-03': { class: 'Constraint', rationale: 'premature decomposition — remove children' },
|
|
103
|
-
'RD-04': { class: 'Constraint', rationale: 'decomposition breadth 7
|
|
97
|
+
'RD-04': { class: 'Constraint', rationale: 'decomposition breadth 7±2 — split level, non-additive' },
|
|
104
98
|
'MS-02': { class: 'Constraint', rationale: 'dangling dependency — fix relation target' },
|
|
105
99
|
'UC-04': { class: 'Constraint', rationale: 'goal = description text — no topology change' },
|
|
106
100
|
'FC-03': { class: 'Constraint', rationale: 'flatten chain — move nested funcs' },
|
|
@@ -115,10 +109,8 @@ export const CLASS_MAP = {
|
|
|
115
109
|
'ND-02': { class: 'Constraint', rationale: 'near-duplicate SCHEMA — merge/differentiate' },
|
|
116
110
|
'CR-R02': { class: 'Constraint', rationale: 'done requires commitRef — attribute' },
|
|
117
111
|
'CR-R03': { class: 'Constraint', rationale: 'concurrent mutation — coordinate, non-additive' },
|
|
118
|
-
'
|
|
119
|
-
'AO-D03': { class: 'Constraint', rationale: 'duplicate path — unify, advisory' },
|
|
112
|
+
'BW-02': { class: 'Constraint', rationale: 'whitebox boundary width — consolidate contracts or add a level' },
|
|
120
113
|
'CR-01': { class: 'Constraint', rationale: 'crossing-flow coupling — reduce, advisory' },
|
|
121
|
-
'RT-01': { class: 'Constraint', rationale: 'physical boundary integrity — repair' },
|
|
122
114
|
'NFR-01': { class: 'Constraint', rationale: 'budget overshoot — reduce measured/raise budget' },
|
|
123
115
|
'VR-01': { class: 'Constraint', rationale: 'missing testResult attribute — no topology change' },
|
|
124
116
|
// CR-SM-227 hat AF-01..05 in den Katalog gelegt, ohne sie hier zu klassifizieren;
|
|
@@ -130,7 +122,6 @@ export const CLASS_MAP = {
|
|
|
130
122
|
'AF-04': { class: 'Constraint', rationale: 'FMEA freshness stamp — attribute, no topology change' },
|
|
131
123
|
'AF-05': { class: 'Constraint', rationale: 'Implementation Plan freshness stamp — attribute, no topology change' },
|
|
132
124
|
// --- Ambiguous (mixed-intent fix) ------------------------------------------
|
|
133
|
-
'CA-01': { class: 'Constraint', rationale: 'add capabilities OR move FUNC — mixed', ambiguous: true },
|
|
134
125
|
'CL-01': { class: 'Constraint', rationale: 'ConOps completeness — attribute vs added element unclear', ambiguous: true },
|
|
135
126
|
'FM-01': { class: 'Constraint', rationale: 'FMEA S/O/D attributes vs added mitigation — mixed', ambiguous: true },
|
|
136
127
|
};
|
package/dist/steer.d.ts
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* steer.ts — der Ranking-Score der Familie (CR-SM-292).
|
|
3
|
+
*
|
|
4
|
+
* Er ersetzt `Δm · t̂` aus `metrics()`, das dreimal an gemessenen Gegenbeispielen gefallen ist
|
|
5
|
+
* (CR-SM-281, CR-SM-287 §10/§11): eine gewichtete Summe über sechs Zahlen aus dem UNTYPISIERTEN
|
|
6
|
+
* Graphen kann Enthaltensein nicht von Fluss und nicht von Besitz unterscheiden — und rankte
|
|
7
|
+
* deshalb einen Code-Import mit 306 flachen Wurzeln über jedes strukturierte Modell der Familie.
|
|
8
|
+
*
|
|
9
|
+
* ## Die Rechnung
|
|
10
|
+
*
|
|
11
|
+
* score = max_i( (wert_i − budget_i)/budget_i ) + ε · mittel_i( (wert_i − budget_i)/budget_i )
|
|
12
|
+
*
|
|
13
|
+
* über die fünf messenden Regeln. **Kleiner ist besser**; 0 heisst „jede Blackbox innerhalb
|
|
14
|
+
* ihres Budgets".
|
|
15
|
+
*
|
|
16
|
+
* ## Warum das Maximum und nicht die Summe
|
|
17
|
+
*
|
|
18
|
+
* Summe und Lexikographie sind derselbe Fehler an entgegengesetzten Enden. Die Summe ist
|
|
19
|
+
* KOMPENSATORISCH — eine gute Dimension kauft eine schlechte frei (CR-SM-281: `faultTolerance`
|
|
20
|
+
* +2,11 kaufte drei Regressionen frei). Die Lexikographie ist DIKTATORISCH — die erste Stufe,
|
|
21
|
+
* die sich unterscheidet, entscheidet allein (CR-SM-287 §11: RD-04 entschied, BW-02 kam nie zu
|
|
22
|
+
* Wort, und ein bedeutungsloser Zufallsschnitt gewann gegen den bestätigten).
|
|
23
|
+
*
|
|
24
|
+
* Chebyshev (Wierzbickis achievement scalarizing function) sitzt dazwischen: **man ist so gut wie
|
|
25
|
+
* die eigene schlechteste Stelle.** Kein Freikauf, kein Diktat. Und es ist dieselbe Logik, nach
|
|
26
|
+
* der das Gate schon immer urteilt — „ein neuer `error` blockiert" ist ein Maximum über
|
|
27
|
+
* Severities, nie ein Mittelwert.
|
|
28
|
+
*
|
|
29
|
+
* ## Warum es keine neuen freien Zahlen gibt
|
|
30
|
+
*
|
|
31
|
+
* Normiert wird gegen `context.threshold`, also gegen die **Regelschwelle selbst**
|
|
32
|
+
* (`policy.decompositionBreadth`, `boundaryWidth`, `crossingFlows`, `instability`, `lcom4`) —
|
|
33
|
+
* aus der Verteilung realer Modelle abgelesen, unter `RULES_VERSION` versioniert, an einer Stelle
|
|
34
|
+
* grep-bar. Genau daran ist CR-SM-281 gestorben: sechs Gewichte, die nie bestimmbar waren
|
|
35
|
+
* (R² = 0,91 bei R²_LOO = −40, N = 9 auf 6 Prädiktoren). Hier erbt die Steuerung die Schwellen
|
|
36
|
+
* der Regeln, statt eigene zu führen.
|
|
37
|
+
*
|
|
38
|
+
* `EPS_AUGMENT` ist die einzige eigene Zahl und kein Urteil: ohne sie sind zwei Kandidaten mit
|
|
39
|
+
* gleichem Maximum ununterscheidbar (schwach pareto-optimal) und die Rangfolge hinge an der
|
|
40
|
+
* Sortier-Stabilität.
|
|
41
|
+
*
|
|
42
|
+
* ## Was der Score NICHT kann — gemessen, nicht vermutet (CR-SM-291 §7.2)
|
|
43
|
+
*
|
|
44
|
+
* 1. **Er misst Form, nie Substanz.** Ein Kandidat, der Elemente LÖSCHT, senkt ihn zuverlässig.
|
|
45
|
+
* Dagegen hilft kein besserer Skalar, sondern `beatsBySteer` — die Nebenbedingung unten.
|
|
46
|
+
* 2. **Innerhalb aller Budgets ist er blind** (Score 0 für alles). Dort führt `readiness`, die
|
|
47
|
+
* weiter zählt, was fehlt. Die beiden sind komplementär: readiness misst ABDECKUNG (wie
|
|
48
|
+
* viele Stellen sind erledigt), dieser Score AUSPRÄGUNG (wie schlimm ist die schlimmste
|
|
49
|
+
* offene). Beide lesen denselben Regelstrom.
|
|
50
|
+
* 3. Die MOD-Whitebox hat keine eigene Randbreiten-Regel — R-04 sieht den Rand nur zusammen mit
|
|
51
|
+
* der Grösse, also ist ein kleines Modul mit breitem Rand stumm (gemessen:
|
|
52
|
+
* `MOD-kernel-measure` 10 Verträge, `mod_api_server_ts` 17). Der Score erbt den blinden
|
|
53
|
+
* Fleck; ihn zu schliessen ist ein eigener Grammatik-Vorgang.
|
|
54
|
+
*/
|
|
55
|
+
import type { RuleViolation } from '@sigloch/contracts/se';
|
|
56
|
+
/**
|
|
57
|
+
* Die fünf messenden Regeln — der theoriegestützte Satz, der im Katalog schon stand: Simon
|
|
58
|
+
* (Breite je Container), Parnas (Randbreite je Whitebox), Baldwin/Clark (Kopplung je Modulpaar),
|
|
59
|
+
* Martin (Instabilität), LCOM4 (Kohäsion).
|
|
60
|
+
*
|
|
61
|
+
* Bewusst eine geschlossene Liste und NICHT aus `context.value !== undefined` abgeleitet: eine
|
|
62
|
+
* künftige Regel, die zufällig eine Zahl mitführt, würde sonst still zum Steuersignal.
|
|
63
|
+
*/
|
|
64
|
+
export declare const STEER_RULES: readonly ["RD-04", "BW-02", "CR-01", "MT-02"];
|
|
65
|
+
/** Der Ausgleichsterm. Klein genug, dass er ein echtes Maximum nie überstimmt. */
|
|
66
|
+
export declare const EPS_AUGMENT = 0.001;
|
|
67
|
+
export interface SteerScore {
|
|
68
|
+
/** Der schlimmste normierte Überschuss. 0 = jede Blackbox innerhalb ihres Budgets. */
|
|
69
|
+
worst: number;
|
|
70
|
+
/** Wo er sitzt — die Begründung, nicht nur die Zahl. `null`, wenn nichts überschreitet. */
|
|
71
|
+
worstAt: {
|
|
72
|
+
ruleId: string;
|
|
73
|
+
elementId: string;
|
|
74
|
+
} | null;
|
|
75
|
+
/** Mittel über alle gemessenen Blackboxes; Grundlage des Ausgleichsterms. */
|
|
76
|
+
mean: number;
|
|
77
|
+
/** `worst + EPS_AUGMENT * mean` — der Vergleichswert. **Kleiner ist besser.** */
|
|
78
|
+
score: number;
|
|
79
|
+
/** Zahl der Blackboxes, die in die Rechnung eingegangen sind. */
|
|
80
|
+
measured: number;
|
|
81
|
+
}
|
|
82
|
+
/** Der Chebyshev-Score eines Zustands, aus seinem Regelstrom. Rein. */
|
|
83
|
+
export declare function steerScore(violations: readonly RuleViolation[]): SteerScore;
|
|
84
|
+
/** Ein Kandidat, so weit der Ranker ihn kennen muss. */
|
|
85
|
+
export interface SteerCandidate {
|
|
86
|
+
/** Der Score des Zustands NACH dem Zug. */
|
|
87
|
+
score: SteerScore;
|
|
88
|
+
/**
|
|
89
|
+
* Entfernt der Zug Elemente? Gemessen, nicht deklariert: Elementzahl vorher gegen nachher.
|
|
90
|
+
*/
|
|
91
|
+
removesElements: boolean;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Vergleicht zwei Kandidaten. `< 0` heisst **a ist besser**; direkt sortierbar.
|
|
95
|
+
*
|
|
96
|
+
* Die Zerstörungs-Sperre steht VOR dem Score und ist keine Gewichtung: gemessen (CR-SM-291
|
|
97
|
+
* Satz F) rankt „18 Kinder löschen" unter jeder Massen-Ablesung vor jeder echten Reparatur, weil
|
|
98
|
+
* der ℝ⁵ Form misst und nie Substanz. Ein Score, der Zerstörung „einpreist", bräuchte genau das
|
|
99
|
+
* Gewicht, das niemand bestimmen kann — deshalb eine Nebenbedingung statt einer Zahl.
|
|
100
|
+
*
|
|
101
|
+
* Zwei löschende Kandidaten werden untereinander wieder nach Score verglichen: die Sperre soll
|
|
102
|
+
* Löschen nicht belohnen, aber auch nicht unvergleichbar machen.
|
|
103
|
+
*/
|
|
104
|
+
export declare function beatsBySteer(a: SteerCandidate, b: SteerCandidate): number;
|
package/dist/steer.js
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Die fünf messenden Regeln — der theoriegestützte Satz, der im Katalog schon stand: Simon
|
|
3
|
+
* (Breite je Container), Parnas (Randbreite je Whitebox), Baldwin/Clark (Kopplung je Modulpaar),
|
|
4
|
+
* Martin (Instabilität), LCOM4 (Kohäsion).
|
|
5
|
+
*
|
|
6
|
+
* Bewusst eine geschlossene Liste und NICHT aus `context.value !== undefined` abgeleitet: eine
|
|
7
|
+
* künftige Regel, die zufällig eine Zahl mitführt, würde sonst still zum Steuersignal.
|
|
8
|
+
*/
|
|
9
|
+
/*
|
|
10
|
+
* CR-SM-293: **MT-01 ist raus.** Der Satz oben nennt vier Theorien und beschreibt damit den
|
|
11
|
+
* Stand; Martin fehlt vorerst, und das ist eine Luecke mit Namen, kein stiller Verzicht.
|
|
12
|
+
*
|
|
13
|
+
* Der Grund ist das Kriterium aus CR-SM-287 §2 selbst — lokal, GERICHTET ("weniger ist besser,
|
|
14
|
+
* ohne Diskussion"), BUDGETIERT ("Schwelle aus der Verteilung abgelesen"). MT-01 erfuellt nur
|
|
15
|
+
* das erste. Martins Aussage ist "in Richtung Stabilitaet abhaengen", nicht "I klein halten":
|
|
16
|
+
* ein Blattmodul mit I = 1.0 ist richtig. Und die Verteilung gibt keine Schwelle her (44 Module
|
|
17
|
+
* exakt auf 0.00, 45 exakt auf 1.00 von 149). MT-01 kam ueber die Theorie-TABELLE in §3 herein
|
|
18
|
+
* und wurde nie gegen die drei Kriterien gehalten, die derselbe CR eine Seite vorher aufstellt.
|
|
19
|
+
*
|
|
20
|
+
* Praktisch war die Dimension ohnehin still: `steerScore` liest VERSTOESSE, und graphcodes
|
|
21
|
+
* Config setzt `instability: null` seit CR-GC-329 — auf dem einzigen Repo, das taeglich steuert,
|
|
22
|
+
* lief der R5 laengst als R4. Diese Zeile sagt es jetzt, statt es zu verbergen.
|
|
23
|
+
*
|
|
24
|
+
* Der Nachfolger ist gemessen und angelegt (CR-SM-298): Martins Stable Dependencies Principle
|
|
25
|
+
* als GERICHTETE Groesse — Abhaengigkeiten "bergauf" je Modul. Ueber 19 Familiengraphen 34 von
|
|
26
|
+
* 305 Abhaengigkeiten (11 %), 132 von 146 Modulen bei null, Schwanz bis 6. Lokal, gerichtet,
|
|
27
|
+
* budgetierbar, nicht entartet: drei von drei.
|
|
28
|
+
*/
|
|
29
|
+
export const STEER_RULES = ['RD-04', 'BW-02', 'CR-01', 'MT-02'];
|
|
30
|
+
/** Der Ausgleichsterm. Klein genug, dass er ein echtes Maximum nie überstimmt. */
|
|
31
|
+
export const EPS_AUGMENT = 1e-3;
|
|
32
|
+
const NEUTRAL = { worst: 0, worstAt: null, mean: 0, score: 0, measured: 0 };
|
|
33
|
+
/**
|
|
34
|
+
* Normierter Überschuss je Befund: `(wert − budget) / budget`, dimensionslos.
|
|
35
|
+
*
|
|
36
|
+
* Ein Befund ohne `value`/`threshold` wird ÜBERSPRUNGEN, nicht als 0 gezählt — „keine Messung"
|
|
37
|
+
* ist nicht „keine Überschreitung". Dieselbe Konvention wie `policy.X = null` und
|
|
38
|
+
* `moduleMetrics.instability = null`.
|
|
39
|
+
*/
|
|
40
|
+
function overshootOf(v) {
|
|
41
|
+
const value = v.context?.value;
|
|
42
|
+
const budget = v.context?.threshold;
|
|
43
|
+
if (typeof value !== 'number' || typeof budget !== 'number' || budget <= 0)
|
|
44
|
+
return null;
|
|
45
|
+
return Math.max(0, (value - budget) / budget);
|
|
46
|
+
}
|
|
47
|
+
/** Der Chebyshev-Score eines Zustands, aus seinem Regelstrom. Rein. */
|
|
48
|
+
export function steerScore(violations) {
|
|
49
|
+
const rules = STEER_RULES;
|
|
50
|
+
let worst = 0;
|
|
51
|
+
let worstAt = null;
|
|
52
|
+
let sum = 0;
|
|
53
|
+
let measured = 0;
|
|
54
|
+
for (const v of violations) {
|
|
55
|
+
if (!rules.includes(v.rule_id))
|
|
56
|
+
continue;
|
|
57
|
+
const d = overshootOf(v);
|
|
58
|
+
if (d === null)
|
|
59
|
+
continue;
|
|
60
|
+
measured += 1;
|
|
61
|
+
sum += d;
|
|
62
|
+
// `>` statt `>=`: bei Gleichstand gewinnt der zuerst gesehene, und die Befund-Sequenz ist
|
|
63
|
+
// seit CR-SM-240 kanonisch — damit ist `worstAt` deterministisch.
|
|
64
|
+
if (d > worst) {
|
|
65
|
+
worst = d;
|
|
66
|
+
worstAt = { ruleId: v.rule_id, elementId: v.element_id };
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if (measured === 0)
|
|
70
|
+
return NEUTRAL;
|
|
71
|
+
const mean = sum / measured;
|
|
72
|
+
return { worst, worstAt, mean, score: worst + EPS_AUGMENT * mean, measured };
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Vergleicht zwei Kandidaten. `< 0` heisst **a ist besser**; direkt sortierbar.
|
|
76
|
+
*
|
|
77
|
+
* Die Zerstörungs-Sperre steht VOR dem Score und ist keine Gewichtung: gemessen (CR-SM-291
|
|
78
|
+
* Satz F) rankt „18 Kinder löschen" unter jeder Massen-Ablesung vor jeder echten Reparatur, weil
|
|
79
|
+
* der ℝ⁵ Form misst und nie Substanz. Ein Score, der Zerstörung „einpreist", bräuchte genau das
|
|
80
|
+
* Gewicht, das niemand bestimmen kann — deshalb eine Nebenbedingung statt einer Zahl.
|
|
81
|
+
*
|
|
82
|
+
* Zwei löschende Kandidaten werden untereinander wieder nach Score verglichen: die Sperre soll
|
|
83
|
+
* Löschen nicht belohnen, aber auch nicht unvergleichbar machen.
|
|
84
|
+
*/
|
|
85
|
+
export function beatsBySteer(a, b) {
|
|
86
|
+
if (a.removesElements !== b.removesElements)
|
|
87
|
+
return a.removesElements ? 1 : -1;
|
|
88
|
+
return a.score.score - b.score.score;
|
|
89
|
+
}
|
package/dist/suggest.d.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import type { OntologyGraph } from '@sigloch/contracts/se';
|
|
2
|
-
import { type
|
|
2
|
+
import { type SteerScore } from './steer.js';
|
|
3
3
|
import type { MetricLayer } from './layer.js';
|
|
4
4
|
import { type SuggestedEdit } from './fix-templates.js';
|
|
5
|
-
/** Build a target vector (ℝ⁶, canonical dimension order) from named metric weights. */
|
|
6
|
-
export declare function targetFor(weights: Partial<Record<keyof MetricVector, number>>): number[];
|
|
7
5
|
export interface Suggestion {
|
|
8
6
|
ruleId: string;
|
|
9
7
|
/** Fund: das verletzte Element. */
|
|
@@ -11,10 +9,21 @@ export interface Suggestion {
|
|
|
11
9
|
/** Fund: die Regel-Botschaft (erste Violation der Regel). */
|
|
12
10
|
message: string;
|
|
13
11
|
fixHint?: string;
|
|
14
|
-
/**
|
|
12
|
+
/**
|
|
13
|
+
* Δm der generischen Richtungssonde (ℝ⁶, kanonische Ordnung) — **Ablesung, kein Ranking**.
|
|
14
|
+
* Seit CR-SM-292 rankt niemand mehr danach; die Zahl bleibt im Bericht, weil `graph_metrics`
|
|
15
|
+
* und das Fit-Advisory sie zeigen.
|
|
16
|
+
*/
|
|
15
17
|
delta: number[];
|
|
16
|
-
/**
|
|
18
|
+
/**
|
|
19
|
+
* **Senkung des Chebyshev-Scores** durch diesen Zug: `steer(basis) − steer(nachher)`.
|
|
20
|
+
* Positiv = besser, absteigend sortiert. Ersetzt `Δm · t̂` (CR-SM-292).
|
|
21
|
+
*/
|
|
17
22
|
score: number;
|
|
23
|
+
/** Der Zustands-Score NACH dem Zug — trägt `worstAt`, also das WO, nicht nur das WIEVIEL. */
|
|
24
|
+
steer: SteerScore;
|
|
25
|
+
/** Entfernt der Zug Elemente? Trägt die Zerstörungs-Sperre (`beatsBySteer`). */
|
|
26
|
+
removesElements: boolean;
|
|
18
27
|
/** Rule-spezifischer Template-Edit; fehlt = Fund-Ebene ohne Edit. */
|
|
19
28
|
edit?: SuggestedEdit;
|
|
20
29
|
}
|
|
@@ -25,7 +34,11 @@ export interface SuggestOptions {
|
|
|
25
34
|
layer?: MetricLayer;
|
|
26
35
|
}
|
|
27
36
|
/**
|
|
28
|
-
*
|
|
29
|
-
*
|
|
37
|
+
* Rankt die feuernden Operator-Regeln danach, wie weit ihr Sonden-Edit den **Chebyshev-Score**
|
|
38
|
+
* senkt (CR-SM-292). Deterministisch, score-absteigend, Tiebreak ruleId/elementId.
|
|
39
|
+
*
|
|
40
|
+
* Vorher stand hier `Δm · t̂` gegen einen Zielvektor aus `target-profile.json`. Der Parameter ist
|
|
41
|
+
* mit diesem CR ersatzlos entfallen — dreimal widerlegt, und ein zweiter Pfad daneben wäre genau
|
|
42
|
+
* das, was der Regelsatz verbietet.
|
|
30
43
|
*/
|
|
31
|
-
export declare function suggestEdits(graph: OntologyGraph,
|
|
44
|
+
export declare function suggestEdits(graph: OntologyGraph, opts?: SuggestOptions): Suggestion[];
|