@sigloch/contracts 9.1.0 → 10.0.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/se/ao-rules.d.ts +12 -12
- package/dist/se/ao-rules.js +88 -225
- package/dist/se/cr-quality-rules.js +75 -58
- package/dist/se/evaluate-all.d.ts +4 -2
- package/dist/se/evaluate-all.js +40 -24
- package/dist/se/fchain-quality-rules.d.ts +0 -1
- package/dist/se/fchain-quality-rules.js +0 -44
- package/dist/se/flat-graph.d.ts +9 -1
- package/dist/se/flat-graph.js +15 -6
- package/dist/se/grammar-snapshot.d.ts +8 -5
- package/dist/se/grammar-snapshot.js +167 -15
- package/dist/se/index.d.ts +4 -3
- package/dist/se/index.js +4 -3
- package/dist/se/meta-model.d.ts +47 -0
- package/dist/se/meta-model.js +52 -5
- package/dist/se/metric-rules.d.ts +20 -1
- package/dist/se/metric-rules.js +97 -58
- package/dist/se/module-crossings.d.ts +102 -0
- package/dist/se/module-crossings.js +196 -0
- package/dist/se/near-duplicate-rules.d.ts +16 -24
- package/dist/se/near-duplicate-rules.js +21 -92
- package/dist/se/ontology.d.ts +0 -22
- package/dist/se/ontology.js +0 -2
- package/dist/se/policy.d.ts +6 -0
- package/dist/se/policy.js +53 -2
- package/dist/se/quality-rules.d.ts +18 -0
- package/dist/se/quality-rules.js +51 -9
- package/dist/se/readiness.d.ts +3 -3
- package/dist/se/readiness.js +17 -21
- package/dist/se/rules.d.ts +6 -0
- package/dist/se/rules.js +303 -125
- package/dist/se/schema-quality-rules.d.ts +0 -1
- package/dist/se/schema-quality-rules.js +7 -28
- package/dist/se/similarity.d.ts +61 -0
- package/dist/se/similarity.js +116 -0
- package/package.json +3 -2
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CR-SM-276 — EIN Begriff „kreuzender Fluss", EINE Berechnung.
|
|
3
|
+
*
|
|
4
|
+
* Zwei Katalogeintraege messen Modulrand-Kopplung: **CR-01** je Modul*paar* und **R-04** je
|
|
5
|
+
* Modul (Groesse GEGEN Kreuzungen). Bis CR-SM-274/276 hatte jeder seine eigene, jeweils
|
|
6
|
+
* falsche Vorstellung davon, was eine Kreuzung ist:
|
|
7
|
+
*
|
|
8
|
+
* - CR-01 suchte `FUNC -io-> FUNC` — ein Paar, das `TRACE_PATTERNS` nicht kennt (R-18 lehnt es
|
|
9
|
+
* ab). Die Regel konnte strukturell nicht feuern (CR-SM-274).
|
|
10
|
+
* - R-04 zaehlte „jede io-Kante einer Modul-FUNC mit einem Endpunkt ausserhalb der FUNC-Menge".
|
|
11
|
+
* FLOWs liegen NIE in der FUNC-Menge — also zaehlte jede io-Kante zu jedem FLOW, auch dem
|
|
12
|
+
* modul*internen*. Das war der io-Grad des Moduls, nicht seine Randkopplung: fuer jedes Modul
|
|
13
|
+
* mit irgendeinem Datenfluss trivial > 2, womit R-04 faktisch wieder „max module size" war —
|
|
14
|
+
* genau der Name, den CR-SM-236 verworfen hat.
|
|
15
|
+
*
|
|
16
|
+
* Hier steht der Pfad, den beide Regelkoepfe meinen: **`FUNC -io-> FLOW -io-> FUNC` ueber eine
|
|
17
|
+
* Modulgrenze**. MOD-Zugehoerigkeit einer FUNC = **direkte** `allocate`-Kante (`FUNC -allocate->
|
|
18
|
+
* MOD` ist `0..1`) — dieselbe Aufloesung wie `moduleMetrics` und die RC-05-Adjazenz.
|
|
19
|
+
*
|
|
20
|
+
* ## CR-SM-282: `byModule` rollt auf, `pairs` nicht
|
|
21
|
+
*
|
|
22
|
+
* Bis hierher las AUCH `byModule` nur die direkte Zuordnung. Ein Eltern-MOD (`MOD -compose-> MOD`)
|
|
23
|
+
* hat keine direkt allozierten FUNCs — es kam in `byModule` also gar nicht vor, und R-04 sah an ihm
|
|
24
|
+
* **null Kreuzungen**. Genau die Whitebox, in die der Leser hineinklickt, war unsichtbar. Jetzt ist
|
|
25
|
+
* die Zugehoerigkeit die **Besitzkette**: das direkt allozierte MOD plus seine compose-Vorfahren.
|
|
26
|
+
* Ein Vertrag quert den Rand von `m`, wenn ein Endpunkt in `m`s Teilbaum liegt und einer nicht.
|
|
27
|
+
*
|
|
28
|
+
* `pairs` (CR-01) bleibt bewusst auf der direkten Zuordnung: „wie stark haengen DIESE zwei Module
|
|
29
|
+
* aneinander" ist eine Frage der Blattebene; ein Elternpaar meldete dieselbe Kopplung ein zweites
|
|
30
|
+
* Mal. Ohne `MOD -compose-> MOD` im Graphen ist das Ergebnis Zeichen fuer Zeichen das bisherige.
|
|
31
|
+
*
|
|
32
|
+
* ## Zaehlbasis: verschiedene SCHEMA, nicht Einzelquerungen
|
|
33
|
+
*
|
|
34
|
+
* Begruendung in CR-SM-274 (dort familienweit gemessen): Kopplung ist die Zahl der **Vertraege**,
|
|
35
|
+
* auf die sich zwei Module einigen muessen — ein FLOW mit 3 Produzenten und 3 Konsumenten ist
|
|
36
|
+
* EIN Vertrag, nicht 9. Roh ist hub-empfindlich und durch FLOW-Splitting manipulierbar;
|
|
37
|
+
* `FLOW -relation-> SCHEMA` ist seit CR-SM-271 Teil 2 `1..1` und per R-18 erzwungen, die
|
|
38
|
+
* Zaehlbasis also garantiert vorhanden. Ein FLOW ohne SCHEMA zaehlt als eigener, untypisierter
|
|
39
|
+
* Vertrag (`UNBOUND:<flow>`) — er darf nicht stillschweigend mit anderen verschmelzen.
|
|
40
|
+
*
|
|
41
|
+
* ## Warum `byModule` die VEREINIGUNG ist und nicht die Summe ueber die Paare
|
|
42
|
+
*
|
|
43
|
+
* R-04 meldet an EINEM MOD. Quert derselbe Vertrag zu drei Nachbarn, besitzt das Modul trotzdem
|
|
44
|
+
* einen Vertrag — die Summe ueber die Paare brächte die Hub-Empfindlichkeit zurueck, die die
|
|
45
|
+
* distinct-Zaehlung gerade beseitigt, und zaehlte doppelt, was CR-01 je Paar ohnehin meldet.
|
|
46
|
+
* `byModule` ist damit die **Vertragsflaeche** des Modulrandes.
|
|
47
|
+
*/
|
|
48
|
+
import type { OntologyGraph } from './ontology.js';
|
|
49
|
+
export interface ModulePairCrossing {
|
|
50
|
+
readonly modA: string;
|
|
51
|
+
readonly modB: string;
|
|
52
|
+
/** SCHEMA-ids bzw. `UNBOUND:<flow>` — die verschiedenen Vertraege an dieser Grenze. */
|
|
53
|
+
readonly contracts: ReadonlySet<string>;
|
|
54
|
+
}
|
|
55
|
+
export interface ModuleCrossings {
|
|
56
|
+
/** Je sortiertem Modulpaar (Schluessel `modA::modB`) die querenden Vertraege. */
|
|
57
|
+
readonly pairs: ReadonlyMap<string, ModulePairCrossing>;
|
|
58
|
+
/** Je MOD die Vereinigung aller Vertraege, die seine Grenze queren. */
|
|
59
|
+
readonly byModule: ReadonlyMap<string, ReadonlySet<string>>;
|
|
60
|
+
/**
|
|
61
|
+
* CR-SM-283: je ZERLEGTER FUNC die Vertraege, die den Rand seiner Whitebox queren —
|
|
62
|
+
* dieselbe Definition und derselbe Durchlauf wie `byModule`, nur ist die Innen-Menge der
|
|
63
|
+
* `compose`-Teilbaum statt des Modul-Teilbaums. Ein Blatt-FUNC steht NICHT drin: es ist
|
|
64
|
+
* keine Whitebox, seine io-Kanten sind kein Rand.
|
|
65
|
+
*
|
|
66
|
+
* Der Rollup ist hier nicht Genauigkeit, sondern Existenz: ein zerlegter FUNC traegt selbst
|
|
67
|
+
* keine io-Kanten (die liegen an seinen Blaettern), ohne Teilbaum-Aufloesung waere die Zahl
|
|
68
|
+
* strukturell immer 0. Gemessen: `FUNC-block-grounding` 0 -> 19 (Spike CR-SM-282).
|
|
69
|
+
*/
|
|
70
|
+
readonly byFunc: ReadonlyMap<string, ReadonlySet<string>>;
|
|
71
|
+
/**
|
|
72
|
+
* CR-SM-282: je MOD die FUNCs seines TEILBAUMS — direkt alloziert plus die seiner
|
|
73
|
+
* compose-Sub-MODs. Fuer ein Blatt-MOD identisch zur direkten Allokation; fuer ein
|
|
74
|
+
* Eltern-MOD ist es die Groesse, die R-04 gegen die Kreuzungen abwaegt. Ohne diese
|
|
75
|
+
* Zahl war ein Eltern-MOD fuer R-04 leer und die Regel schwieg per `funcCount <= coupled`.
|
|
76
|
+
*/
|
|
77
|
+
readonly funcsByModule: ReadonlyMap<string, ReadonlySet<string>>;
|
|
78
|
+
/**
|
|
79
|
+
* CR-SM-293: dieselbe Grenze, jetzt mit RICHTUNG — Martins Ca/Ce, gerechnet im selben
|
|
80
|
+
* Durchlauf und auf derselben Zaehlbasis (verschiedene Vertraege) wie `pairs`/`byModule`.
|
|
81
|
+
*
|
|
82
|
+
* `afferentContracts` = Vertraege, die dieses Modul ueber seinen Rand LIEFERT: andere
|
|
83
|
+
* haengen von ihm ab. `efferentContracts` = Vertraege, die es von draussen BEZIEHT: es
|
|
84
|
+
* haengt von anderen ab. Ein Vertrag, dessen Produzent und Konsument beide im Teilbaum
|
|
85
|
+
* liegen, steht in keiner der beiden Mengen — er quert den Rand nicht.
|
|
86
|
+
*
|
|
87
|
+
* Die Richtung ist die Entscheidung, die bis hierher nirgends im Repo stand: der
|
|
88
|
+
* KONSUMENT haengt vom Vertrag des Produzenten ab, nicht umgekehrt. `moduleMetrics`
|
|
89
|
+
* las vorher „Kante zeigt weg = Abhaengigkeit" und drehte damit jedes reine
|
|
90
|
+
* Verbrauchermodul auf `I = 0` (maximal stabil), wo es `1` sein muss.
|
|
91
|
+
*/
|
|
92
|
+
readonly afferentContracts: ReadonlyMap<string, ReadonlySet<string>>;
|
|
93
|
+
readonly efferentContracts: ReadonlyMap<string, ReadonlySet<string>>;
|
|
94
|
+
}
|
|
95
|
+
/** Die querenden Vertraege dieses Graphen — einmal je Graph-Objekt berechnet. */
|
|
96
|
+
export declare function moduleCrossings(graph: OntologyGraph): ModuleCrossings;
|
|
97
|
+
/** Zahl der verschiedenen Vertraege, die den Rand dieses Moduls queren. */
|
|
98
|
+
export declare function crossingContractCount(graph: OntologyGraph, modId: string): number;
|
|
99
|
+
/** Zahl der verschiedenen Vertraege, die den Rand dieser FUNC-Whitebox queren — CR-SM-283. */
|
|
100
|
+
export declare function whiteboxContractCount(graph: OntologyGraph, funcId: string): number;
|
|
101
|
+
/** Die FUNCs im Teilbaum dieses Moduls (direkt alloziert + die seiner Sub-MODs) — CR-SM-282. */
|
|
102
|
+
export declare function subtreeFuncs(graph: OntologyGraph, modId: string): ReadonlySet<string>;
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { indexOf } from './graph-index.js';
|
|
2
|
+
const CACHE = new WeakMap();
|
|
3
|
+
function build(graph) {
|
|
4
|
+
const idx = indexOf(graph);
|
|
5
|
+
// FUNC → MOD (`FUNC -allocate-> MOD` ist `0..1`, also hoechstens ein Modul je FUNC).
|
|
6
|
+
const modOfFunc = new Map();
|
|
7
|
+
for (const mod of idx.elementsOfType('MOD')) {
|
|
8
|
+
for (const t of idx.in(mod.id, 'allocate')) {
|
|
9
|
+
if (idx.typeOf(t.source) === 'FUNC')
|
|
10
|
+
modOfFunc.set(t.source, mod.id);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
// CR-SM-282: die Besitzkette eines MODs — es selbst plus seine `compose`-Vorfahren.
|
|
14
|
+
// `byModule` braucht sie, `pairs` nicht (s. Kopf).
|
|
15
|
+
const parentMod = new Map();
|
|
16
|
+
for (const mod of idx.elementsOfType('MOD')) {
|
|
17
|
+
for (const t of idx.in(mod.id, 'compose')) {
|
|
18
|
+
if (idx.typeOf(t.source) === 'MOD')
|
|
19
|
+
parentMod.set(mod.id, t.source);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
const chainOf = (modId) => {
|
|
23
|
+
const out = [];
|
|
24
|
+
const seen = new Set();
|
|
25
|
+
let cur = modId;
|
|
26
|
+
while (cur !== undefined && !seen.has(cur)) {
|
|
27
|
+
seen.add(cur);
|
|
28
|
+
out.push(cur);
|
|
29
|
+
cur = parentMod.get(cur);
|
|
30
|
+
}
|
|
31
|
+
return out;
|
|
32
|
+
};
|
|
33
|
+
const ownersOfFunc = new Map();
|
|
34
|
+
for (const [func, mod] of modOfFunc)
|
|
35
|
+
ownersOfFunc.set(func, chainOf(mod));
|
|
36
|
+
// CR-SM-283: dieselbe Konstruktion auf dem FUNC-compose-Baum. `funcChainOf(f)` ist f selbst
|
|
37
|
+
// plus seine Vorfahren; nur ZERLEGTE FUNCs sind Whiteboxes und kommen als Kandidat in Frage.
|
|
38
|
+
const parentFunc = new Map();
|
|
39
|
+
const decomposed = new Set();
|
|
40
|
+
for (const t of graph.traces) {
|
|
41
|
+
if (t.type !== 'compose')
|
|
42
|
+
continue;
|
|
43
|
+
if (idx.typeOf(t.source) !== 'FUNC' || idx.typeOf(t.target) !== 'FUNC')
|
|
44
|
+
continue;
|
|
45
|
+
parentFunc.set(t.target, t.source);
|
|
46
|
+
decomposed.add(t.source);
|
|
47
|
+
}
|
|
48
|
+
const funcChain = new Map();
|
|
49
|
+
const funcChainOf = (funcId) => {
|
|
50
|
+
const cached = funcChain.get(funcId);
|
|
51
|
+
if (cached)
|
|
52
|
+
return cached;
|
|
53
|
+
const out = [];
|
|
54
|
+
const seen = new Set();
|
|
55
|
+
let cur = funcId;
|
|
56
|
+
while (cur !== undefined && !seen.has(cur)) {
|
|
57
|
+
seen.add(cur);
|
|
58
|
+
out.push(cur);
|
|
59
|
+
cur = parentFunc.get(cur);
|
|
60
|
+
}
|
|
61
|
+
funcChain.set(funcId, out);
|
|
62
|
+
return out;
|
|
63
|
+
};
|
|
64
|
+
const pairs = new Map();
|
|
65
|
+
const byModule = new Map();
|
|
66
|
+
const byFunc = new Map();
|
|
67
|
+
const afferentContracts = new Map();
|
|
68
|
+
const efferentContracts = new Map();
|
|
69
|
+
const addInto = (m, key, contracts) => {
|
|
70
|
+
let set = m.get(key);
|
|
71
|
+
if (!set) {
|
|
72
|
+
set = new Set();
|
|
73
|
+
m.set(key, set);
|
|
74
|
+
}
|
|
75
|
+
for (const c of contracts)
|
|
76
|
+
set.add(c);
|
|
77
|
+
};
|
|
78
|
+
const addTo = (mod, contracts) => addInto(byModule, mod, contracts);
|
|
79
|
+
for (const flow of idx.elementsOfType('FLOW')) {
|
|
80
|
+
const producerMods = new Set(idx.in(flow.id, 'io')
|
|
81
|
+
.filter(t => idx.typeOf(t.source) === 'FUNC')
|
|
82
|
+
.map(t => modOfFunc.get(t.source))
|
|
83
|
+
.filter((m) => !!m));
|
|
84
|
+
const consumerMods = new Set(idx.out(flow.id, 'io')
|
|
85
|
+
.filter(t => idx.typeOf(t.target) === 'FUNC')
|
|
86
|
+
.map(t => modOfFunc.get(t.target))
|
|
87
|
+
.filter((m) => !!m));
|
|
88
|
+
const schemas = idx.out(flow.id, 'relation')
|
|
89
|
+
.filter(t => idx.typeOf(t.target) === 'SCHEMA')
|
|
90
|
+
.map(t => t.target);
|
|
91
|
+
const contracts = schemas.length > 0 ? schemas : [`UNBOUND:${flow.id}`];
|
|
92
|
+
// CR-SM-283: derselbe Test auf dem compose-Baum. `allEndpoints` statt `endpoints` —
|
|
93
|
+
// die FUNC-Whitebox kennt keine Modul-Zugehoerigkeit, eine unallozierte FUNC liegt
|
|
94
|
+
// trotzdem drinnen oder draussen.
|
|
95
|
+
const allEndpoints = [
|
|
96
|
+
...idx.in(flow.id, 'io').filter(t => idx.typeOf(t.source) === 'FUNC').map(t => t.source),
|
|
97
|
+
...idx.out(flow.id, 'io').filter(t => idx.typeOf(t.target) === 'FUNC').map(t => t.target),
|
|
98
|
+
];
|
|
99
|
+
const funcCandidates = new Set(allEndpoints.flatMap(f => funcChainOf(f)).filter(f => decomposed.has(f)));
|
|
100
|
+
for (const w of funcCandidates) {
|
|
101
|
+
const inside = allEndpoints.some(f => funcChainOf(f).includes(w));
|
|
102
|
+
const outside = allEndpoints.some(f => !funcChainOf(f).includes(w));
|
|
103
|
+
if (inside && outside) {
|
|
104
|
+
let set = byFunc.get(w);
|
|
105
|
+
if (!set) {
|
|
106
|
+
set = new Set();
|
|
107
|
+
byFunc.set(w, set);
|
|
108
|
+
}
|
|
109
|
+
for (const c of contracts)
|
|
110
|
+
set.add(c);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// Der MOD-Teil ab hier: ohne Zuordnung auf BEIDEN Seiten gibt es keine Modulgrenze.
|
|
114
|
+
// Der FUNC-Teil oben haengt bewusst NICHT daran — eine Whitebox ist eine Whitebox,
|
|
115
|
+
// auch wenn ihre Blaetter noch keinem Modul zugewiesen sind (R-22 meldet das separat).
|
|
116
|
+
if (producerMods.size === 0 || consumerMods.size === 0)
|
|
117
|
+
continue;
|
|
118
|
+
for (const a of producerMods) {
|
|
119
|
+
for (const b of consumerMods) {
|
|
120
|
+
if (a === b)
|
|
121
|
+
continue;
|
|
122
|
+
const [modA, modB] = a < b ? [a, b] : [b, a];
|
|
123
|
+
const key = `${modA}::${modB}`;
|
|
124
|
+
let entry = pairs.get(key);
|
|
125
|
+
if (!entry) {
|
|
126
|
+
entry = { modA, modB, contracts: new Set() };
|
|
127
|
+
pairs.set(key, entry);
|
|
128
|
+
}
|
|
129
|
+
for (const c of contracts)
|
|
130
|
+
entry.contracts.add(c);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
// byModule MIT Rollup (CR-SM-282): ein Vertrag quert den Rand von `m`, wenn ein Endpunkt
|
|
134
|
+
// in `m`s Besitzkette liegt und einer nicht. Ohne Eltern-MODs faellt das exakt auf die
|
|
135
|
+
// frueheren `addTo(a)/addTo(b)` zurueck — das ist die Regressions-Invariante.
|
|
136
|
+
const endpoints = [
|
|
137
|
+
...idx.in(flow.id, 'io').filter(t => idx.typeOf(t.source) === 'FUNC').map(t => t.source),
|
|
138
|
+
...idx.out(flow.id, 'io').filter(t => idx.typeOf(t.target) === 'FUNC').map(t => t.target),
|
|
139
|
+
].filter(f => ownersOfFunc.has(f));
|
|
140
|
+
const candidates = new Set(endpoints.flatMap(f => ownersOfFunc.get(f)));
|
|
141
|
+
for (const m of candidates) {
|
|
142
|
+
const inside = endpoints.some(f => ownersOfFunc.get(f).includes(m));
|
|
143
|
+
const outside = endpoints.some(f => !ownersOfFunc.get(f).includes(m));
|
|
144
|
+
if (inside && outside)
|
|
145
|
+
addTo(m, contracts);
|
|
146
|
+
}
|
|
147
|
+
// CR-SM-293: dieselben Endpunkte, nach Seite getrennt. Ein Modul, das auf BEIDEN
|
|
148
|
+
// Seiten steht (es produziert und konsumiert diesen Vertrag im eigenen Teilbaum),
|
|
149
|
+
// faellt aus beiden Mengen — der Vertrag quert seinen Rand nicht.
|
|
150
|
+
const producerChain = new Set(idx.in(flow.id, 'io')
|
|
151
|
+
.filter(t => idx.typeOf(t.source) === 'FUNC' && ownersOfFunc.has(t.source))
|
|
152
|
+
.flatMap(t => ownersOfFunc.get(t.source)));
|
|
153
|
+
const consumerChain = new Set(idx.out(flow.id, 'io')
|
|
154
|
+
.filter(t => idx.typeOf(t.target) === 'FUNC' && ownersOfFunc.has(t.target))
|
|
155
|
+
.flatMap(t => ownersOfFunc.get(t.target)));
|
|
156
|
+
for (const m of producerChain)
|
|
157
|
+
if (!consumerChain.has(m))
|
|
158
|
+
addInto(afferentContracts, m, contracts);
|
|
159
|
+
for (const m of consumerChain)
|
|
160
|
+
if (!producerChain.has(m))
|
|
161
|
+
addInto(efferentContracts, m, contracts);
|
|
162
|
+
}
|
|
163
|
+
const funcsByModule = new Map();
|
|
164
|
+
for (const [func, owners] of ownersOfFunc) {
|
|
165
|
+
for (const m of owners) {
|
|
166
|
+
let set = funcsByModule.get(m);
|
|
167
|
+
if (!set) {
|
|
168
|
+
set = new Set();
|
|
169
|
+
funcsByModule.set(m, set);
|
|
170
|
+
}
|
|
171
|
+
set.add(func);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return { pairs, byModule, byFunc, funcsByModule, afferentContracts, efferentContracts };
|
|
175
|
+
}
|
|
176
|
+
/** Die querenden Vertraege dieses Graphen — einmal je Graph-Objekt berechnet. */
|
|
177
|
+
export function moduleCrossings(graph) {
|
|
178
|
+
let result = CACHE.get(graph);
|
|
179
|
+
if (result === undefined) {
|
|
180
|
+
result = build(graph);
|
|
181
|
+
CACHE.set(graph, result);
|
|
182
|
+
}
|
|
183
|
+
return result;
|
|
184
|
+
}
|
|
185
|
+
/** Zahl der verschiedenen Vertraege, die den Rand dieses Moduls queren. */
|
|
186
|
+
export function crossingContractCount(graph, modId) {
|
|
187
|
+
return moduleCrossings(graph).byModule.get(modId)?.size ?? 0;
|
|
188
|
+
}
|
|
189
|
+
/** Zahl der verschiedenen Vertraege, die den Rand dieser FUNC-Whitebox queren — CR-SM-283. */
|
|
190
|
+
export function whiteboxContractCount(graph, funcId) {
|
|
191
|
+
return moduleCrossings(graph).byFunc.get(funcId)?.size ?? 0;
|
|
192
|
+
}
|
|
193
|
+
/** Die FUNCs im Teilbaum dieses Moduls (direkt alloziert + die seiner Sub-MODs) — CR-SM-282. */
|
|
194
|
+
export function subtreeFuncs(graph, modId) {
|
|
195
|
+
return moduleCrossings(graph).funcsByModule.get(modId) ?? new Set();
|
|
196
|
+
}
|
|
@@ -1,33 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* CR-121 P2: Near-Duplicate Detection for FUNC and SCHEMA elements.
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
|
+
* CR-SM-286: **die Aehnlichkeit wird HIER gerechnet, nicht injiziert.**
|
|
5
|
+
*
|
|
6
|
+
* Bis hierher trugen beide Regeln `if (!_ndXXMatrix) return [];` und warteten auf
|
|
7
|
+
* `setND01SimilarityMatrix()` / `setND02SimilarityMatrix()`. Die Formel stand als Prosa in
|
|
8
|
+
* diesem Kopf, die Implementierung in `graphcode/src/kernel/measure/nd-similarity.ts`. Folgen,
|
|
9
|
+
* alle gemessen: bei severity `error` ununterscheidbares Fail-open (moneyflow trug 16
|
|
10
|
+
* ND-01-Befunde, die ausserhalb graphcodes niemand sah), prozessweiter Modulzustand (Graph A
|
|
11
|
+
* urteilte ueber Graph B, wogegen graphcode eine `finally`-Klammer brauchte), und AO-D01 —
|
|
12
|
+
* eine GATE-Regel — schwaechte sich stillschweigend ab ("no matrix → assume pass").
|
|
13
|
+
*
|
|
14
|
+
* Jetzt: `funcSimilarity(graph)` / `schemaSimilarity(graph)` aus `similarity.ts`, je Graph
|
|
15
|
+
* gecacht. Kein Setter, kein Modulzustand, kein Fail-open. Formeln und Schwelle 0,85 sind
|
|
16
|
+
* zeichengleich uebernommen — dasselbe Urteil an der richtigen Stelle.
|
|
4
17
|
*/
|
|
5
18
|
import type { OntologyGraph } from './ontology.js';
|
|
6
19
|
import type { RuleViolation } from './rules.js';
|
|
7
|
-
/**
|
|
8
|
-
* Inject pre-computed similarity matrix for FUNC elements.
|
|
9
|
-
* Similarity = 0.35 × descr_jaccard + 0.25 × verb_match + 0.25 × io_topology + 0.15 × req_overlap
|
|
10
|
-
*/
|
|
11
|
-
export declare function setND01SimilarityMatrix(data: {
|
|
12
|
-
funcIds: string[];
|
|
13
|
-
matrix: number[][];
|
|
14
|
-
} | null): void;
|
|
15
|
-
/** ND-01: FUNC pairs with similarity >= 0.85 → error. */
|
|
20
|
+
/** ND-01: FUNC pairs with similarity >= 0.85 -> error. */
|
|
16
21
|
export declare function nd01FuncNearDuplicate(graph: OntologyGraph): RuleViolation[];
|
|
17
|
-
/**
|
|
18
|
-
* Inject pre-computed similarity matrix for SCHEMA elements.
|
|
19
|
-
* Similarity = 0.50 × field_jaccard + 0.30 × descr_jaccard + 0.20 × usage_overlap
|
|
20
|
-
*/
|
|
21
|
-
export declare function setND02SimilarityMatrix(data: {
|
|
22
|
-
schemaIds: string[];
|
|
23
|
-
matrix: number[][];
|
|
24
|
-
} | null): void;
|
|
25
|
-
/** Read-only access to current ND-02 matrix (used by AO-D01 for schema overlap). */
|
|
26
|
-
export declare function getND02SimilarityMatrix(): {
|
|
27
|
-
schemaIds: string[];
|
|
28
|
-
matrix: number[][];
|
|
29
|
-
} | null;
|
|
30
|
-
/** ND-02: SCHEMA pairs with similarity >= 0.85 → error. */
|
|
22
|
+
/** ND-02: SCHEMA pairs with similarity >= 0.85 -> error. */
|
|
31
23
|
export declare function nd02SchemaNearDuplicate(graph: OntologyGraph): RuleViolation[];
|
|
32
24
|
export declare const ND_RULES: readonly [{
|
|
33
25
|
readonly id: "ND-01";
|
|
@@ -1,101 +1,30 @@
|
|
|
1
|
+
import { funcSimilarity, schemaSimilarity, pairsAbove } from './similarity.js';
|
|
1
2
|
const ND_SIMILARITY_THRESHOLD = 0.85;
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
3
|
+
/** Der gemeinsame Rumpf beider Regeln — eine Schleife, zwei Meldungstexte. */
|
|
4
|
+
function nearDuplicates(graph, sim, ruleId, what, fixHint) {
|
|
5
|
+
const byId = new Map(graph.elements.map(e => [e.id, e]));
|
|
6
|
+
return pairsAbove(sim, ND_SIMILARITY_THRESHOLD).map(({ a, b, similarity }) => {
|
|
7
|
+
const elA = byId.get(a);
|
|
8
|
+
const elB = byId.get(b);
|
|
9
|
+
const pct = Math.round(similarity * 100);
|
|
10
|
+
return {
|
|
11
|
+
rule_id: ruleId,
|
|
12
|
+
severity: 'error',
|
|
13
|
+
element_id: b,
|
|
14
|
+
message: `${b}${elB?.name ? ' (' + elB.name + ')' : ''} is ${pct}% similar to ${a}${elA?.name ? ' (' + elA.name + ')' : ''} — potential duplicate ${what}`,
|
|
15
|
+
fix_hint: fixHint,
|
|
16
|
+
context: { element_type: elB?.type, element_name: elB?.name },
|
|
17
|
+
};
|
|
18
|
+
});
|
|
12
19
|
}
|
|
13
|
-
/** ND-01: FUNC pairs with similarity >= 0.85
|
|
20
|
+
/** ND-01: FUNC pairs with similarity >= 0.85 -> error. */
|
|
14
21
|
export function nd01FuncNearDuplicate(graph) {
|
|
15
|
-
|
|
16
|
-
return [];
|
|
17
|
-
const { funcIds, matrix } = _nd01Matrix;
|
|
18
|
-
const violations = [];
|
|
19
|
-
const seen = new Set();
|
|
20
|
-
for (let i = 0; i < funcIds.length; i++) {
|
|
21
|
-
for (let j = i + 1; j < funcIds.length; j++) {
|
|
22
|
-
const sim = matrix[i][j];
|
|
23
|
-
if (sim < ND_SIMILARITY_THRESHOLD)
|
|
24
|
-
continue;
|
|
25
|
-
const pairKey = `${funcIds[i]}:${funcIds[j]}`;
|
|
26
|
-
if (seen.has(pairKey))
|
|
27
|
-
continue;
|
|
28
|
-
seen.add(pairKey);
|
|
29
|
-
const pct = Math.round(sim * 100);
|
|
30
|
-
const elI = graph.elements.find(e => e.id === funcIds[i]);
|
|
31
|
-
const elJ = graph.elements.find(e => e.id === funcIds[j]);
|
|
32
|
-
violations.push({
|
|
33
|
-
rule_id: 'ND-01',
|
|
34
|
-
severity: 'error',
|
|
35
|
-
element_id: funcIds[j],
|
|
36
|
-
message: `${funcIds[j]}${elJ?.name ? ' (' + elJ.name + ')' : ''} is ${pct}% similar to ${funcIds[i]}${elI?.name ? ' (' + elI.name + ')' : ''} — potential duplicate function`,
|
|
37
|
-
fix_hint: 'Merge into single FUNC or differentiate responsibilities',
|
|
38
|
-
context: {
|
|
39
|
-
element_type: elJ?.type,
|
|
40
|
-
element_name: elJ?.name,
|
|
41
|
-
},
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
return violations;
|
|
22
|
+
return nearDuplicates(graph, funcSimilarity(graph), 'ND-01', 'function', 'Merge into single FUNC or differentiate responsibilities');
|
|
46
23
|
}
|
|
47
|
-
|
|
48
|
-
// ND-02: SCHEMA near-duplicates
|
|
49
|
-
// ---------------------------------------------------------------------------
|
|
50
|
-
let _nd02Matrix = null;
|
|
51
|
-
/**
|
|
52
|
-
* Inject pre-computed similarity matrix for SCHEMA elements.
|
|
53
|
-
* Similarity = 0.50 × field_jaccard + 0.30 × descr_jaccard + 0.20 × usage_overlap
|
|
54
|
-
*/
|
|
55
|
-
export function setND02SimilarityMatrix(data) {
|
|
56
|
-
_nd02Matrix = data;
|
|
57
|
-
}
|
|
58
|
-
/** Read-only access to current ND-02 matrix (used by AO-D01 for schema overlap). */
|
|
59
|
-
export function getND02SimilarityMatrix() {
|
|
60
|
-
return _nd02Matrix;
|
|
61
|
-
}
|
|
62
|
-
/** ND-02: SCHEMA pairs with similarity >= 0.85 → error. */
|
|
24
|
+
/** ND-02: SCHEMA pairs with similarity >= 0.85 -> error. */
|
|
63
25
|
export function nd02SchemaNearDuplicate(graph) {
|
|
64
|
-
|
|
65
|
-
return [];
|
|
66
|
-
const { schemaIds, matrix } = _nd02Matrix;
|
|
67
|
-
const violations = [];
|
|
68
|
-
const seen = new Set();
|
|
69
|
-
for (let i = 0; i < schemaIds.length; i++) {
|
|
70
|
-
for (let j = i + 1; j < schemaIds.length; j++) {
|
|
71
|
-
const sim = matrix[i][j];
|
|
72
|
-
if (sim < ND_SIMILARITY_THRESHOLD)
|
|
73
|
-
continue;
|
|
74
|
-
const pairKey = `${schemaIds[i]}:${schemaIds[j]}`;
|
|
75
|
-
if (seen.has(pairKey))
|
|
76
|
-
continue;
|
|
77
|
-
seen.add(pairKey);
|
|
78
|
-
const pct = Math.round(sim * 100);
|
|
79
|
-
const elI = graph.elements.find(e => e.id === schemaIds[i]);
|
|
80
|
-
const elJ = graph.elements.find(e => e.id === schemaIds[j]);
|
|
81
|
-
violations.push({
|
|
82
|
-
rule_id: 'ND-02',
|
|
83
|
-
severity: 'error',
|
|
84
|
-
element_id: schemaIds[j],
|
|
85
|
-
message: `${schemaIds[j]}${elJ?.name ? ' (' + elJ.name + ')' : ''} is ${pct}% similar to ${schemaIds[i]}${elI?.name ? ' (' + elI.name + ')' : ''} — potential duplicate schema`,
|
|
86
|
-
fix_hint: 'Merge schemas or differentiate field sets',
|
|
87
|
-
context: {
|
|
88
|
-
element_type: elJ?.type,
|
|
89
|
-
element_name: elJ?.name,
|
|
90
|
-
},
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
return violations;
|
|
26
|
+
return nearDuplicates(graph, schemaSimilarity(graph), 'ND-02', 'schema', 'Merge schemas or differentiate field sets');
|
|
95
27
|
}
|
|
96
|
-
// ---------------------------------------------------------------------------
|
|
97
|
-
// Aggregate
|
|
98
|
-
// ---------------------------------------------------------------------------
|
|
99
28
|
export const ND_RULES = [
|
|
100
29
|
{ id: 'ND-01', name: 'FuncNearDuplicate', severity: 'error', evaluate: nd01FuncNearDuplicate, domain: ['FUNC'] },
|
|
101
30
|
{ id: 'ND-02', name: 'SchemaNearDuplicate', severity: 'error', evaluate: nd02SchemaNearDuplicate, domain: ['SCHEMA'] },
|
package/dist/se/ontology.d.ts
CHANGED
|
@@ -79,14 +79,6 @@ export type ReqKind = z.infer<typeof ReqKind>;
|
|
|
79
79
|
* und faellt danach am Enum-Vergleich durch. Die Drift wird sichtbar, nicht geheilt.
|
|
80
80
|
*/
|
|
81
81
|
export declare function normalizeReqKinds(raw: unknown): readonly string[];
|
|
82
|
-
export declare const AsilLevel: z.ZodEnum<{
|
|
83
|
-
QM: "QM";
|
|
84
|
-
A: "A";
|
|
85
|
-
B: "B";
|
|
86
|
-
C: "C";
|
|
87
|
-
D: "D";
|
|
88
|
-
}>;
|
|
89
|
-
export type AsilLevel = z.infer<typeof AsilLevel>;
|
|
90
82
|
/** INCOSE TIAD verification method (only relevant for type=TEST). */
|
|
91
83
|
export declare const VerificationMethod: z.ZodEnum<{
|
|
92
84
|
test: "test";
|
|
@@ -254,13 +246,6 @@ export declare const OntologyElement: z.ZodObject<{
|
|
|
254
246
|
}>;
|
|
255
247
|
name: z.ZodString;
|
|
256
248
|
description: z.ZodString;
|
|
257
|
-
asil: z.ZodOptional<z.ZodEnum<{
|
|
258
|
-
QM: "QM";
|
|
259
|
-
A: "A";
|
|
260
|
-
B: "B";
|
|
261
|
-
C: "C";
|
|
262
|
-
D: "D";
|
|
263
|
-
}>>;
|
|
264
249
|
method: z.ZodOptional<z.ZodEnum<{
|
|
265
250
|
test: "test";
|
|
266
251
|
inspection: "inspection";
|
|
@@ -327,13 +312,6 @@ export declare const OntologyGraph: z.ZodObject<{
|
|
|
327
312
|
}>;
|
|
328
313
|
name: z.ZodString;
|
|
329
314
|
description: z.ZodString;
|
|
330
|
-
asil: z.ZodOptional<z.ZodEnum<{
|
|
331
|
-
QM: "QM";
|
|
332
|
-
A: "A";
|
|
333
|
-
B: "B";
|
|
334
|
-
C: "C";
|
|
335
|
-
D: "D";
|
|
336
|
-
}>>;
|
|
337
315
|
method: z.ZodOptional<z.ZodEnum<{
|
|
338
316
|
test: "test";
|
|
339
317
|
inspection: "inspection";
|
package/dist/se/ontology.js
CHANGED
|
@@ -65,7 +65,6 @@ export function normalizeReqKinds(raw) {
|
|
|
65
65
|
return raw.map(k => String(k).trim()).filter(Boolean);
|
|
66
66
|
return String(raw).split(',').map(k => k.trim()).filter(Boolean);
|
|
67
67
|
}
|
|
68
|
-
export const AsilLevel = z.enum(['QM', 'A', 'B', 'C', 'D']);
|
|
69
68
|
/** INCOSE TIAD verification method (only relevant for type=TEST). */
|
|
70
69
|
export const VerificationMethod = z.enum(['test', 'inspection', 'analysis', 'demonstration']);
|
|
71
70
|
/** Test execution result (only relevant for type=TEST). */
|
|
@@ -197,7 +196,6 @@ export const OntologyElement = z.object({
|
|
|
197
196
|
type: ElementType,
|
|
198
197
|
name: z.string(),
|
|
199
198
|
description: z.string(),
|
|
200
|
-
asil: AsilLevel.optional(),
|
|
201
199
|
/** INCOSE TIAD verification method — only for TEST elements (CR-057). */
|
|
202
200
|
method: VerificationMethod.optional(),
|
|
203
201
|
/** REQ kinds: multi-valued classification (CR-180). Only for REQ elements. */
|
package/dist/se/policy.d.ts
CHANGED
|
@@ -24,6 +24,12 @@ export declare const MetricPolicySchema: z.ZodObject<{
|
|
|
24
24
|
crossingFlows: z.ZodNullable<z.ZodObject<{
|
|
25
25
|
warning: z.ZodNumber;
|
|
26
26
|
}, z.core.$strip>>;
|
|
27
|
+
decompositionBreadth: z.ZodNullable<z.ZodObject<{
|
|
28
|
+
warning: z.ZodNumber;
|
|
29
|
+
}, z.core.$strip>>;
|
|
30
|
+
boundaryWidth: z.ZodNullable<z.ZodObject<{
|
|
31
|
+
warning: z.ZodNumber;
|
|
32
|
+
}, z.core.$strip>>;
|
|
27
33
|
riskRpn: z.ZodNullable<z.ZodNumber>;
|
|
28
34
|
apTable: z.ZodNullable<z.ZodObject<{
|
|
29
35
|
severityBands: z.ZodArray<z.ZodObject<{
|
package/dist/se/policy.js
CHANGED
|
@@ -34,6 +34,27 @@ export const MetricPolicySchema = z.object({
|
|
|
34
34
|
* in die `arch`-Dimension ein (CR-SM-236).
|
|
35
35
|
*/
|
|
36
36
|
crossingFlows: z.object({ warning: z.number().int().min(1) }).nullable(),
|
|
37
|
+
/**
|
|
38
|
+
* CR-SM-282: Kinderzahl je Blackbox (RD-04). Stand bis hierher als
|
|
39
|
+
* `DECOMPOSITION_BREADTH_MAX = 11` inline in `rules.ts` — eine Urteilsschwelle, die sich
|
|
40
|
+
* weder per grep noch aus dem Regelnamen ablesen liess (dieselbe Klasse, die CR-SM-236 fuer
|
|
41
|
+
* R-04 aufgeloest hat). `null` -> messen, nicht urteilen: die Regel schweigt.
|
|
42
|
+
*
|
|
43
|
+
* Der Default bleibt 11 und ist damit verhaltensgleich zum Bisherigen. Die Doktrin
|
|
44
|
+
* (`se:top-level`: „max 5 per level") ist bewusst NICHT der Default: `info` bei 5 haette am
|
|
45
|
+
* graphcode-Selbstmodell 20 zusaetzliche Befunde erzeugt, und `readiness` zaehlt `info`
|
|
46
|
+
* ungefiltert in den Nenner (`score = 1 - Verstoesse / applicable`) — genau der Grund, aus dem
|
|
47
|
+
* MT-03 zur Messung statt zur Regel wurde. Wer doktrin-streng fahren will, setzt 5.
|
|
48
|
+
*/
|
|
49
|
+
decompositionBreadth: z.object({ warning: z.number().int().min(2) }).nullable(),
|
|
50
|
+
/**
|
|
51
|
+
* CR-SM-283: Randbreite je Blackbox (BW-02) — verschiedene Vertraege, die den Rand queren.
|
|
52
|
+
* Bewusst OHNE `info`-Stufe: die Verteilung gibt sie nicht her. bok und graph-view-edit enden
|
|
53
|
+
* beide bei 3; eine info-Schwelle dort truege bok 4 Befunde auf 9 Blackboxes, und `readiness`
|
|
54
|
+
* zaehlt `info` ungefiltert in den Nenner (`score = 1 - Verstoesse / applicable`) — der Grund,
|
|
55
|
+
* aus dem MT-03 zur Messung statt zur Regel wurde. `null` -> messen, nicht urteilen.
|
|
56
|
+
*/
|
|
57
|
+
boundaryWidth: z.object({ warning: z.number().int().min(1) }).nullable(),
|
|
37
58
|
/**
|
|
38
59
|
* FM-03: RPN (severity · occurrence · detection), ab dem ein Risiko-REQ eine bestandene
|
|
39
60
|
* Verifikation braucht (exklusiv). null → FM-03 feuert nie.
|
|
@@ -84,15 +105,45 @@ export const MetricPolicySchema = z.object({
|
|
|
84
105
|
* `> 5` → warning, also warning ab 6.
|
|
85
106
|
*/
|
|
86
107
|
export const DEFAULT_METRIC_POLICY = {
|
|
87
|
-
|
|
108
|
+
// CR-SM-293: `null` — MESSEN, NICHT URTEILEN. Die 0.7 stand hier als Startwert und war laut
|
|
109
|
+
// CR-SM-223 ausdruecklich "gesetzt, nicht gemessen". Nachdem MT-01 nur noch Kopplung zaehlt,
|
|
110
|
+
// ist die Verteilung ueber 149 messbare Module ZWEIGIPFLIG: 44 liegen exakt auf 0.00 (reine
|
|
111
|
+
// Produzenten), 45 exakt auf 1.00 (reine Konsumenten), und 45 der 51 Befunde bei 0.7 sind
|
|
112
|
+
// genau die 1.00. Aus einer solchen Verteilung laesst sich keine Schwelle ABLESEN (Praezedenz
|
|
113
|
+
// CR-SM-283/BW-02), und der Grund ist Struktur, nicht Zaehlweise: die meisten Module tragen
|
|
114
|
+
// zwei bis drei kreuzende Vertraege, da ist das Verhaeltnis 0 oder 1.
|
|
115
|
+
//
|
|
116
|
+
// Tiefer liegt der Einwand von Martin selbst: I ist eine KOORDINATE, kein Defekt. Sein Satz
|
|
117
|
+
// lautet "in Richtung Stabilitaet abhaengen", nicht "I klein halten" — ein Blattmodul mit
|
|
118
|
+
// I = 1.0 ist richtig, nicht kaputt. Damit fehlt MT-01 die Eigenschaft, die CR-SM-287 §2 von
|
|
119
|
+
// einer Steuerdimension verlangt: GERICHTET, "weniger ist besser, ohne Diskussion".
|
|
120
|
+
//
|
|
121
|
+
// Die Zahl bleibt vollstaendig erhalten — in jeder Modulzeile von `graph_metrics`, im Export
|
|
122
|
+
// und im gve-Dashboard, das den Zustand bereits rendert ("Instabilitaet wird hier nur
|
|
123
|
+
// gemessen"). Es entsteht nur kein Verstoss. graphcode faehrt das seit CR-GC-329 in seiner
|
|
124
|
+
// eigenen Config; dieser Default macht aus der Konfigurationszufaelligkeit eine Entscheidung.
|
|
125
|
+
instability: null,
|
|
88
126
|
lcom4: { info: 4, warning: 6 },
|
|
89
127
|
// CR-SM-236: die bisherigen Literale, jetzt an einer grep-baren Stelle.
|
|
90
128
|
// `crossingFlows.warning = 3` ist die unveränderte Alt-Semantik: vorher `count > 2` → warning.
|
|
91
129
|
crossingFlows: { warning: 3 },
|
|
130
|
+
// CR-SM-282: unveraenderte Alt-Semantik (`DECOMPOSITION_BREADTH_MAX = 11`, `> max` meldet).
|
|
131
|
+
// CR-SM-296: 7+-2 ist die Doktrin (`se:top-level`), 9 ist ihre Obergrenze. Bis hierher stand
|
|
132
|
+
// hier 11 -- gesetzt, nicht abgelesen. CR-SM-282 hatte gegen die Doktrin-Zahl 5 entschieden,
|
|
133
|
+
// weil `info` bei 5 den readiness-Nenner verwaessert haette; das Argument traegt fuer die
|
|
134
|
+
// OBERgrenze nicht, denn RD-04 meldet als `warning`. Die Zahl steht jetzt an EINER Stelle und
|
|
135
|
+
// gilt fuer beide Zerlegungsfragen -- Sub-FUNC und Sub-MOD.
|
|
136
|
+
decompositionBreadth: { warning: 9 },
|
|
137
|
+
// CR-SM-283: aus der Verteilung abgelesen — bok/gve max 3, graphcode 19, moneyflow 17.
|
|
138
|
+
boundaryWidth: { warning: 5 },
|
|
92
139
|
riskRpn: 100,
|
|
93
140
|
// `large`/`coupled`/`crossings` = die drei Literale aus `maxModuleSize`: vorher
|
|
94
141
|
// `funcCount <= 8` → skip, `funcCount > 12` → groß, `crossings > 2` → gekoppelt.
|
|
95
|
-
|
|
142
|
+
// CR-SM-296: an dieselbe Doktrin gebunden statt danebengesetzt. R-04 ist seit diesem CR ALLEIN
|
|
143
|
+
// fuer die Modulgroesse zustaendig (RD-04 hat ihr Allokations-Bein abgegeben), also gilt hier
|
|
144
|
+
// dieselbe 7+-2: `large` = 9 (Obergrenze), `coupled` = 7 (Untergrenze, ab der die Regel
|
|
145
|
+
// hinsieht). Vorher 12/8 -- zwei Zahlen fuer dieselbe Frage, unverbunden mit den 11 darueber.
|
|
146
|
+
moduleSize: { large: 9, coupled: 7, crossings: 2 },
|
|
96
147
|
// CR-SM-229: kein Startwert möglich und keiner gewollt — die Tabelle steht nur im Handbook.
|
|
97
148
|
// `null` heißt hier nicht „aus", sondern „markierter Übergang": bestätigte AP-Invarianten,
|
|
98
149
|
// sonst RPN-Bänder. `apMethod()` macht das für den Leser sichtbar.
|
|
@@ -20,6 +20,24 @@ export declare function setBQ04SimilarityMatrix(data: {
|
|
|
20
20
|
/**
|
|
21
21
|
* BQ-04 checks for duplicate / near-duplicate requirements using
|
|
22
22
|
* pre-computed embedding similarity. Returns [] when no matrix is set.
|
|
23
|
+
*
|
|
24
|
+
* CR-SM-286: **bewusst NICHT angeschlossen — anders als ND-01/ND-02.**
|
|
25
|
+
*
|
|
26
|
+
* Die Naht ist dieselbe und sie ist genauso tot: `setBQ04SimilarityMatrix()` ruft im gesamten
|
|
27
|
+
* Familienbaum niemand (CR-SM-278, erneut geprueft). Der Unterschied liegt in der Eingabe. ND-01
|
|
28
|
+
* und ND-02 nennen deterministische Formeln ueber Graphinhalt (Jaccard ueber Beschreibung,
|
|
29
|
+
* Verb, io-Topologie, Felder) — die konnten nach `similarity.ts` wandern und tun dort dasselbe.
|
|
30
|
+
* BQ-04 verlangt laut eigener Zeile "pre-computed EMBEDDING similarity", und Embeddings kann ein
|
|
31
|
+
* reines Vertragspaket nicht berechnen: kein Modell, kein Netz, kein Zufall.
|
|
32
|
+
*
|
|
33
|
+
* Ein Ersatzmass haette ich erfinden muessen. Ein Versuch mit 0.7*Beschreibung + 0.3*Name lief:
|
|
34
|
+
* **0 Befunde an allen neun Familiengraphen** und 4950 an einer templatierten Fixture — also
|
|
35
|
+
* genau die zwei Gate-7-Ausreisser zugleich ("0 Befunde am Selbstmodell" und "quadratisch mit
|
|
36
|
+
* der Graphgroesse"). Erfundene Gewichte ohne Messung sind keine Reparatur.
|
|
37
|
+
*
|
|
38
|
+
* Damit ist BQ-04 der naechste AO-D03-Fall: entweder eine Eingabe, die der Host liefern MUSS
|
|
39
|
+
* (dann gehoert die Regel nicht in ein reines Paket), oder streichen. Das ist eine
|
|
40
|
+
* Grammatik-Entscheidung — `se-grammar-review`, eigener CR, nicht hier nebenbei.
|
|
23
41
|
*/
|
|
24
42
|
export declare function bq04Necessary(graph: OntologyGraph): RuleViolation[];
|
|
25
43
|
export declare function bq06Conforming(graph: OntologyGraph): RuleViolation[];
|