@sigloch/contracts 9.1.0 → 10.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/dist/se/ao-rules.d.ts +12 -12
  2. package/dist/se/ao-rules.js +88 -225
  3. package/dist/se/conformance-rules.d.ts +12 -0
  4. package/dist/se/conformance-rules.js +6 -6
  5. package/dist/se/cr-quality-rules.js +75 -58
  6. package/dist/se/evaluate-all.d.ts +4 -2
  7. package/dist/se/evaluate-all.js +61 -24
  8. package/dist/se/fchain-quality-rules.d.ts +0 -1
  9. package/dist/se/fchain-quality-rules.js +0 -44
  10. package/dist/se/flat-graph.d.ts +9 -1
  11. package/dist/se/flat-graph.js +15 -6
  12. package/dist/se/grammar-snapshot.d.ts +9 -5
  13. package/dist/se/grammar-snapshot.js +250 -15
  14. package/dist/se/index.d.ts +6 -3
  15. package/dist/se/index.js +14 -3
  16. package/dist/se/meta-model.d.ts +47 -0
  17. package/dist/se/meta-model.js +52 -5
  18. package/dist/se/metric-rules.d.ts +20 -1
  19. package/dist/se/metric-rules.js +97 -58
  20. package/dist/se/module-crossings.d.ts +102 -0
  21. package/dist/se/module-crossings.js +196 -0
  22. package/dist/se/near-duplicate-rules.d.ts +16 -24
  23. package/dist/se/near-duplicate-rules.js +21 -92
  24. package/dist/se/ontology.d.ts +0 -22
  25. package/dist/se/ontology.js +0 -2
  26. package/dist/se/policy.d.ts +6 -0
  27. package/dist/se/policy.js +53 -2
  28. package/dist/se/quality-rules.d.ts +18 -0
  29. package/dist/se/quality-rules.js +51 -9
  30. package/dist/se/readiness.d.ts +25 -3
  31. package/dist/se/readiness.js +39 -21
  32. package/dist/se/rule-help.d.ts +52 -0
  33. package/dist/se/rule-help.js +342 -0
  34. package/dist/se/rules.d.ts +6 -0
  35. package/dist/se/rules.js +303 -125
  36. package/dist/se/schema-quality-rules.d.ts +0 -1
  37. package/dist/se/schema-quality-rules.js +7 -28
  38. package/dist/se/similarity.d.ts +61 -0
  39. package/dist/se/similarity.js +116 -0
  40. package/package.json +4 -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": "9.1.0",
3
+ "version": "10.1.0",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -29,7 +29,9 @@
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",
34
+ "report:bestand": "node scripts/bestand-report.mjs"
33
35
  },
34
36
  "dependencies": {
35
37
  "zod": "^4.3.6"