@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,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CR-SM-286 — die Ähnlichkeitsrechnung von ND-01/ND-02/BQ-04, dort wo die Regeln stehen.
|
|
3
|
+
*
|
|
4
|
+
* ## Warum sie hierher gehört
|
|
5
|
+
*
|
|
6
|
+
* Bis hierher lebte die Formel als **Prosa** in `near-duplicate-rules.ts` und als **Code** in
|
|
7
|
+
* `graphcode/src/kernel/measure/nd-similarity.ts`, verbunden durch
|
|
8
|
+
* `setND01SimilarityMatrix()` / `setND02SimilarityMatrix()`. Zwei Pakete mussten sich über
|
|
9
|
+
* etwas einig sein, das nur eines von beiden ausführt.
|
|
10
|
+
*
|
|
11
|
+
* Die Naht kostete vier Dinge, alle gemessen:
|
|
12
|
+
*
|
|
13
|
+
* 1. **Fail-open.** Ohne Injektion gaben ND-01/ND-02 `[]` zurück — bei severity `error` und
|
|
14
|
+
* ununterscheidbar von „keine Duplikate". Gemessen: moneyflow trägt 16 ND-01-Befunde, die
|
|
15
|
+
* für JEDEN Konsumenten ausserhalb graphcodes unsichtbar waren (`report:silence`, die
|
|
16
|
+
* Spike-Skripte, jedes künftige Werkzeug). Die Familie hat für „kann nicht urteilen" eine
|
|
17
|
+
* Konvention — `policy.X = null`, `moduleMetrics.instability = null`, beide sagen es
|
|
18
|
+
* ausdrücklich; ND fiel statt dessen nach grün.
|
|
19
|
+
* 2. **Prozessweiter Modulzustand.** `let _nd01Matrix` ist global. graphcode schützte sich mit
|
|
20
|
+
* der Klammer `withNDMatrices(og, run)` und einem `finally`-Reset; jeder andere Aufrufer
|
|
21
|
+
* nicht. Zwei Graphen nacheinander bewertet — und Graph A urteilt über Graph B.
|
|
22
|
+
* 3. **AO-D01 verstärkte es.** Die Regel liegt IM Gate-Katalog und übersprang ihre
|
|
23
|
+
* Overlap-Prüfung, wenn keine Matrix da war („no matrix → assume pass"): eine Gate-Regel,
|
|
24
|
+
* die sich stillschweigend abschwächt.
|
|
25
|
+
* 4. **BQ-04 ist am selben Muster komplett tot** — `setBQ04SimilarityMatrix()` ruft im ganzen
|
|
26
|
+
* Familienbaum niemand (CR-SM-278, heute erneut geprüft).
|
|
27
|
+
*
|
|
28
|
+
* Präzedenz aus demselben Repo: CR-SM-276 hat `moduleCrossings` genau deshalb NACH contracts
|
|
29
|
+
* gelegt — damit zwei Regelköpfe (CR-01, R-04) EINE Definition von „Kreuzung" teilen. Hier ist
|
|
30
|
+
* es dieselbe Bewegung für drei Regelköpfe und eine Definition von „ähnlich".
|
|
31
|
+
*
|
|
32
|
+
* ## Was sich NICHT ändert
|
|
33
|
+
*
|
|
34
|
+
* Formeln, Gewichte und die Schwelle 0,85 sind zeichengleich aus graphcodes Implementierung
|
|
35
|
+
* übernommen. Das ist kein neues Urteil, es ist derselbe Code an der richtigen Stelle.
|
|
36
|
+
*
|
|
37
|
+
* Rein und deterministisch: kein Store, kein Transport, kein Filesystem, kein Zufall, keine
|
|
38
|
+
* Abhängigkeit ausserhalb der Ontologie. Ergebnisse hängen je Graph-Objekt im WeakMap-Cache,
|
|
39
|
+
* wie `moduleCrossings` — die Matrix ist O(n²) und wird je Voll-Lauf mehrfach gebraucht.
|
|
40
|
+
*/
|
|
41
|
+
import type { OntologyGraph } from './ontology.js';
|
|
42
|
+
/** Wort-Token >= 3 Zeichen, lowercase, Unicode-Buchstaben/Ziffern. */
|
|
43
|
+
export declare function tokens(s: unknown): Set<string>;
|
|
44
|
+
/** Jaccard-Aehnlichkeit; leer/leer = 1 (identisch leer). */
|
|
45
|
+
export declare function jaccard(a: ReadonlySet<string>, b: ReadonlySet<string>): number;
|
|
46
|
+
export interface SimilarityMatrix {
|
|
47
|
+
/** Element-uids in kanonischer Ordnung — der Index in `matrix`. */
|
|
48
|
+
readonly ids: readonly string[];
|
|
49
|
+
/** Symmetrisch, Diagonale 1. */
|
|
50
|
+
readonly matrix: readonly (readonly number[])[];
|
|
51
|
+
}
|
|
52
|
+
/** FUNC-Aehnlichkeit je Graph (ND-01). */
|
|
53
|
+
export declare const funcSimilarity: (graph: OntologyGraph) => SimilarityMatrix;
|
|
54
|
+
/** SCHEMA-Aehnlichkeit je Graph (ND-02, AO-D01). */
|
|
55
|
+
export declare const schemaSimilarity: (graph: OntologyGraph) => SimilarityMatrix;
|
|
56
|
+
/** Paare ueber der Schwelle, kanonisch geordnet (i < j) — die eine Schleife fuer alle drei Regeln. */
|
|
57
|
+
export declare function pairsAbove(sim: SimilarityMatrix, threshold: number): Array<{
|
|
58
|
+
a: string;
|
|
59
|
+
b: string;
|
|
60
|
+
similarity: number;
|
|
61
|
+
}>;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { isValidTrace } from './meta-model.js';
|
|
2
|
+
/** Wort-Token >= 3 Zeichen, lowercase, Unicode-Buchstaben/Ziffern. */
|
|
3
|
+
export function tokens(s) {
|
|
4
|
+
const text = typeof s === 'string' ? s : '';
|
|
5
|
+
return new Set(text
|
|
6
|
+
.toLowerCase()
|
|
7
|
+
.split(/[^\p{L}\p{N}]+/u)
|
|
8
|
+
.filter(w => w.length >= 3));
|
|
9
|
+
}
|
|
10
|
+
/** Jaccard-Aehnlichkeit; leer/leer = 1 (identisch leer). */
|
|
11
|
+
export function jaccard(a, b) {
|
|
12
|
+
if (a.size === 0 && b.size === 0)
|
|
13
|
+
return 1;
|
|
14
|
+
let inter = 0;
|
|
15
|
+
for (const x of a)
|
|
16
|
+
if (b.has(x))
|
|
17
|
+
inter += 1;
|
|
18
|
+
const union = a.size + b.size - inter;
|
|
19
|
+
return union === 0 ? 1 : inter / union;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Partner-uids eines Elements ueber LEGALE Traces der genannten Typen.
|
|
23
|
+
*
|
|
24
|
+
* CR-SM-286: der Legalitaetsfilter ist nicht Kosmetik. Ohne ihn aenderte eine grammatikwidrige
|
|
25
|
+
* Kante das Urteil: `se-rule-pair-legality.test.ts` (Pruefung A, CR-SM-278) meldete **35**
|
|
26
|
+
* Faelle, in denen ND-02 erst durch eine injizierte illegale Kante auf SCHEMA anschlug —
|
|
27
|
+
* `ACTOR -compose-> SCHEMA`, `CR -io-> SCHEMA`, `FLOW -compose-> SCHEMA` und so fort. Eine
|
|
28
|
+
* Regel, deren Verdikt an einer Kante haengt, die R-18 als `error` ablehnt, urteilt ueber einen
|
|
29
|
+
* Graphen, den es nicht geben darf.
|
|
30
|
+
*/
|
|
31
|
+
function partners(graph, id, traceTypes) {
|
|
32
|
+
const byType = new Map(graph.elements.map(e => [e.id, e.type]));
|
|
33
|
+
const out = new Set();
|
|
34
|
+
for (const t of graph.traces) {
|
|
35
|
+
if (!traceTypes.includes(t.type))
|
|
36
|
+
continue;
|
|
37
|
+
if (t.source !== id && t.target !== id)
|
|
38
|
+
continue;
|
|
39
|
+
const src = byType.get(t.source);
|
|
40
|
+
const tgt = byType.get(t.target);
|
|
41
|
+
if (!src || !tgt)
|
|
42
|
+
continue; // haengende Kante -> R-08, nicht hier
|
|
43
|
+
if (!isValidTrace({ source: src, target: tgt, type: t.type }))
|
|
44
|
+
continue;
|
|
45
|
+
out.add(t.source === id ? t.target : t.source);
|
|
46
|
+
}
|
|
47
|
+
return out;
|
|
48
|
+
}
|
|
49
|
+
/** Erstes Wort des Namens als Verb-Naeherung (FUNC-Namen: Verb-Objekt). */
|
|
50
|
+
function firstWord(name) {
|
|
51
|
+
const text = typeof name === 'string' ? name : '';
|
|
52
|
+
return text.toLowerCase().split(/[^\p{L}\p{N}]+/u).find(w => w.length > 0) ?? '';
|
|
53
|
+
}
|
|
54
|
+
const byId = (a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0);
|
|
55
|
+
/** Feld-Menge eines SCHEMA: `attributes.fields` (Array), sonst Beschreibungs-Token. */
|
|
56
|
+
function schemaFields(el) {
|
|
57
|
+
const fields = el.attributes?.fields;
|
|
58
|
+
if (Array.isArray(fields) && fields.length > 0) {
|
|
59
|
+
return new Set(fields.map(f => String(typeof f === 'object' && f !== null ? (f.name ?? JSON.stringify(f)) : f).toLowerCase()));
|
|
60
|
+
}
|
|
61
|
+
return tokens(el.description);
|
|
62
|
+
}
|
|
63
|
+
const CACHE = new WeakMap();
|
|
64
|
+
function build(graph) {
|
|
65
|
+
// ND-01: 0.35*descr_jaccard + 0.25*verb_match + 0.25*io_topology + 0.15*req_overlap
|
|
66
|
+
const funcs = graph.elements.filter(e => e.type === 'FUNC').sort(byId);
|
|
67
|
+
const fDescr = funcs.map(f => tokens(f.description));
|
|
68
|
+
const fVerb = funcs.map(f => firstWord(f.name));
|
|
69
|
+
const fIo = funcs.map(f => partners(graph, f.id, ['io']));
|
|
70
|
+
const fReq = funcs.map(f => new Set(graph.traces.filter(t => t.type === 'satisfy' && t.source === f.id).map(t => t.target)));
|
|
71
|
+
const func = {
|
|
72
|
+
ids: funcs.map(f => f.id),
|
|
73
|
+
matrix: funcs.map((_, i) => funcs.map((_, j) => i === j ? 1
|
|
74
|
+
: 0.35 * jaccard(fDescr[i], fDescr[j])
|
|
75
|
+
+ 0.25 * (fVerb[i] !== '' && fVerb[i] === fVerb[j] ? 1 : 0)
|
|
76
|
+
+ 0.25 * jaccard(fIo[i], fIo[j])
|
|
77
|
+
+ 0.15 * jaccard(fReq[i], fReq[j]))),
|
|
78
|
+
};
|
|
79
|
+
// ND-02: 0.50*field_jaccard + 0.30*descr_jaccard + 0.20*usage_overlap
|
|
80
|
+
const schemas = graph.elements.filter(e => e.type === 'SCHEMA').sort(byId);
|
|
81
|
+
const sFields = schemas.map(schemaFields);
|
|
82
|
+
const sDescr = schemas.map(s => tokens(s.description));
|
|
83
|
+
const sUse = schemas.map(s => partners(graph, s.id, ['relation', 'io', 'compose']));
|
|
84
|
+
const schema = {
|
|
85
|
+
ids: schemas.map(s => s.id),
|
|
86
|
+
matrix: schemas.map((_, i) => schemas.map((_, j) => i === j ? 1
|
|
87
|
+
: 0.5 * jaccard(sFields[i], sFields[j])
|
|
88
|
+
+ 0.3 * jaccard(sDescr[i], sDescr[j])
|
|
89
|
+
+ 0.2 * jaccard(sUse[i], sUse[j]))),
|
|
90
|
+
};
|
|
91
|
+
return { func, schema };
|
|
92
|
+
}
|
|
93
|
+
function similarities(graph) {
|
|
94
|
+
let s = CACHE.get(graph);
|
|
95
|
+
if (s === undefined) {
|
|
96
|
+
s = build(graph);
|
|
97
|
+
CACHE.set(graph, s);
|
|
98
|
+
}
|
|
99
|
+
return s;
|
|
100
|
+
}
|
|
101
|
+
/** FUNC-Aehnlichkeit je Graph (ND-01). */
|
|
102
|
+
export const funcSimilarity = (graph) => similarities(graph).func;
|
|
103
|
+
/** SCHEMA-Aehnlichkeit je Graph (ND-02, AO-D01). */
|
|
104
|
+
export const schemaSimilarity = (graph) => similarities(graph).schema;
|
|
105
|
+
/** Paare ueber der Schwelle, kanonisch geordnet (i < j) — die eine Schleife fuer alle drei Regeln. */
|
|
106
|
+
export function pairsAbove(sim, threshold) {
|
|
107
|
+
const out = [];
|
|
108
|
+
for (let i = 0; i < sim.ids.length; i++) {
|
|
109
|
+
for (let j = i + 1; j < sim.ids.length; j++) {
|
|
110
|
+
const s = sim.matrix[i][j];
|
|
111
|
+
if (s >= threshold)
|
|
112
|
+
out.push({ a: sim.ids[i], b: sim.ids[j], similarity: s });
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return out;
|
|
116
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sigloch/contracts",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "10.0.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -29,7 +29,8 @@
|
|
|
29
29
|
"check:grammar": "node scripts/check-grammar-version.mjs",
|
|
30
30
|
"grammar:snapshot": "SE_WRITE_GRAMMAR_SNAPSHOT=1 vitest run tests/unit/se-grammar-invariant.test.ts",
|
|
31
31
|
"prepublishOnly": "npm run build && npm run test",
|
|
32
|
-
"grammar:measure": "SE_MEASURE=1 vitest run tests/unit/se-grammar-measure.test.ts"
|
|
32
|
+
"grammar:measure": "SE_MEASURE=1 vitest run tests/unit/se-grammar-measure.test.ts",
|
|
33
|
+
"report:silence": "node scripts/rules-silence-report.mjs"
|
|
33
34
|
},
|
|
34
35
|
"dependencies": {
|
|
35
36
|
"zod": "^4.3.6"
|