@sigloch/contracts 6.3.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.
Files changed (41) 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 +28 -0
  4. package/dist/se/conformance-rules.js +40 -17
  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 +40 -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/format-e-parser.d.ts +14 -2
  13. package/dist/se/format-e-parser.js +33 -9
  14. package/dist/se/grammar-snapshot.d.ts +10 -7
  15. package/dist/se/grammar-snapshot.js +172 -27
  16. package/dist/se/index.d.ts +4 -3
  17. package/dist/se/index.js +4 -3
  18. package/dist/se/meta-model.d.ts +99 -7
  19. package/dist/se/meta-model.js +155 -14
  20. package/dist/se/metric-rules.d.ts +20 -1
  21. package/dist/se/metric-rules.js +97 -58
  22. package/dist/se/module-crossings.d.ts +102 -0
  23. package/dist/se/module-crossings.js +196 -0
  24. package/dist/se/near-duplicate-rules.d.ts +16 -24
  25. package/dist/se/near-duplicate-rules.js +21 -92
  26. package/dist/se/ontology.d.ts +32 -47
  27. package/dist/se/ontology.js +56 -13
  28. package/dist/se/policy.d.ts +6 -0
  29. package/dist/se/policy.js +53 -2
  30. package/dist/se/quality-rules.d.ts +18 -0
  31. package/dist/se/quality-rules.js +68 -12
  32. package/dist/se/readiness.d.ts +7 -5
  33. package/dist/se/readiness.js +40 -22
  34. package/dist/se/rules.d.ts +6 -7
  35. package/dist/se/rules.js +358 -128
  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/dist/se/uc-quality-rules.js +31 -12
  41. package/package.json +3 -2
@@ -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
- * Uses pre-injected similarity matrices (same pattern as BQ-04).
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
- // ND-01: FUNC near-duplicates
4
- // ---------------------------------------------------------------------------
5
- let _nd01Matrix = null;
6
- /**
7
- * Inject pre-computed similarity matrix for FUNC elements.
8
- * Similarity = 0.35 × descr_jaccard + 0.25 × verb_match + 0.25 × io_topology + 0.15 × req_overlap
9
- */
10
- export function setND01SimilarityMatrix(data) {
11
- _nd01Matrix = data;
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 error. */
20
+ /** ND-01: FUNC pairs with similarity >= 0.85 -> error. */
14
21
  export function nd01FuncNearDuplicate(graph) {
15
- if (!_nd01Matrix)
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
- if (!_nd02Matrix)
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'] },
@@ -21,7 +21,6 @@ export declare const ElementType: z.ZodEnum<{
21
21
  TEST: "TEST";
22
22
  MOD: "MOD";
23
23
  SCHEMA: "SCHEMA";
24
- SESSION: "SESSION";
25
24
  CR: "CR";
26
25
  MS: "MS";
27
26
  }>;
@@ -38,28 +37,48 @@ export declare const TraceType: z.ZodEnum<{
38
37
  verify: "verify";
39
38
  allocate: "allocate";
40
39
  relation: "relation";
41
- produces: "produces";
42
40
  }>;
43
41
  export type TraceType = z.infer<typeof TraceType>;
44
- /** REQ kind — 7 values aligned with SysML 2.0 + FMEA (CR-180). */
42
+ /**
43
+ * REQ kind — 6 values aligned with SysML 2.0 + FMEA (CR-180).
44
+ *
45
+ * CR-SM-266 B: `negative` ENTFAELLT. Ein Verbots-REQ ("das System tut X nie") ist auch eine
46
+ * Anforderung — die Implementierung muss Regeln abfragen oder Sicherungen einbauen, und das
47
+ * ist `functional`. Der Wert hatte NULL Leser (kein Regel-, Gate- oder View-Konsument; anders
48
+ * als risk/mitigation → FM-01..03, pre/postcondition → UC-05/06, non-functional → NFR-01) und
49
+ * haette durch die where-Praedikate am satisfy-Pattern per AUSLASSUNG erstmals Wirkung
50
+ * bekommen: er stand in keiner der vier Listen und waere damit nur noch per FCHAIN erfuellbar
51
+ * gewesen. Die sechs verbleibenden Werte partitionieren die where-Listen VOLLSTAENDIG — kein
52
+ * Wert ohne Zuordnung, keine Kante, die durch ein Loch in der Aufzaehlung faellt.
53
+ */
45
54
  export declare const ReqKind: z.ZodEnum<{
46
55
  functional: "functional";
47
56
  "non-functional": "non-functional";
48
57
  risk: "risk";
49
- negative: "negative";
50
58
  mitigation: "mitigation";
51
59
  precondition: "precondition";
52
60
  postcondition: "postcondition";
53
61
  }>;
54
62
  export type ReqKind = z.infer<typeof ReqKind>;
55
- export declare const AsilLevel: z.ZodEnum<{
56
- QM: "QM";
57
- A: "A";
58
- B: "B";
59
- C: "C";
60
- D: "D";
61
- }>;
62
- export type AsilLevel = z.infer<typeof AsilLevel>;
63
+ /**
64
+ * `kinds` in der Form, die WIRKLICH auf Platte liegt — als Liste.
65
+ *
66
+ * Gefunden beim Migrations-Audit zu CR-SM-266 (2026-08-25), nicht von Hand: drei Graphen der
67
+ * Familie tragen `kinds` als blossen STRING statt als Liste (`graph-view-edit`
68
+ * `REQ-edit-genesis: 'functional'`, dazu gc_test-graphview und graphify), einer davon mit dem
69
+ * Wert `'security'`, den das Enum gar nicht kennt. Ursache ist der Format-E-Weg: dort reist
70
+ * das Feld als `@kinds a,b` und wird erst vom Konsumenten gehoben (graph-api-core, CR-195d).
71
+ *
72
+ * Seit die where-Praedikate `kinds` LESEN (CR-SM-266 B), ist diese Drift tragend: ein
73
+ * `kinds.every(...)` auf einem String wirft, und eine geworfene Exception in `isValidTrace`
74
+ * reisst den ganzen Regellauf statt einen Befund zu melden. Die SSOT muss auch bei kaputten
75
+ * Daten ein URTEIL liefern, keinen Absturz.
76
+ *
77
+ * Normalisieren heisst hier ausdruecklich NICHT raten: `'a,b'` wird zu `['a','b']`, weil genau
78
+ * das die Schreibform ist — ein unbekannter Wert wie `'security'` ueberlebt die Normalisierung
79
+ * und faellt danach am Enum-Vergleich durch. Die Drift wird sichtbar, nicht geheilt.
80
+ */
81
+ export declare function normalizeReqKinds(raw: unknown): readonly string[];
63
82
  /** INCOSE TIAD verification method (only relevant for type=TEST). */
64
83
  export declare const VerificationMethod: z.ZodEnum<{
65
84
  test: "test";
@@ -222,19 +241,11 @@ export declare const OntologyElement: z.ZodObject<{
222
241
  TEST: "TEST";
223
242
  MOD: "MOD";
224
243
  SCHEMA: "SCHEMA";
225
- SESSION: "SESSION";
226
244
  CR: "CR";
227
245
  MS: "MS";
228
246
  }>;
229
247
  name: z.ZodString;
230
248
  description: z.ZodString;
231
- asil: z.ZodOptional<z.ZodEnum<{
232
- QM: "QM";
233
- A: "A";
234
- B: "B";
235
- C: "C";
236
- D: "D";
237
- }>>;
238
249
  method: z.ZodOptional<z.ZodEnum<{
239
250
  test: "test";
240
251
  inspection: "inspection";
@@ -245,7 +256,6 @@ export declare const OntologyElement: z.ZodObject<{
245
256
  functional: "functional";
246
257
  "non-functional": "non-functional";
247
258
  risk: "risk";
248
- negative: "negative";
249
259
  mitigation: "mitigation";
250
260
  precondition: "precondition";
251
261
  postcondition: "postcondition";
@@ -265,12 +275,6 @@ export type OntologyElement = z.infer<typeof OntologyElement>;
265
275
  * A trace (edge) in the SE ontology graph.
266
276
  * `label` provides semantic context for 'relation' edges (e.g. 'derives', 'depends-on').
267
277
  */
268
- /** Trace category: modeling traces are user-visible, audit traces are internal. */
269
- export declare const TraceCategory: z.ZodEnum<{
270
- modeling: "modeling";
271
- audit: "audit";
272
- }>;
273
- export type TraceCategory = z.infer<typeof TraceCategory>;
274
278
  export declare const Trace: z.ZodObject<{
275
279
  source: z.ZodString;
276
280
  target: z.ZodString;
@@ -281,12 +285,7 @@ export declare const Trace: z.ZodObject<{
281
285
  verify: "verify";
282
286
  allocate: "allocate";
283
287
  relation: "relation";
284
- produces: "produces";
285
288
  }>;
286
- category: z.ZodOptional<z.ZodEnum<{
287
- modeling: "modeling";
288
- audit: "audit";
289
- }>>;
290
289
  label: z.ZodOptional<z.ZodString>;
291
290
  weight: z.ZodDefault<z.ZodNumber>;
292
291
  attributes: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
@@ -308,19 +307,11 @@ export declare const OntologyGraph: z.ZodObject<{
308
307
  TEST: "TEST";
309
308
  MOD: "MOD";
310
309
  SCHEMA: "SCHEMA";
311
- SESSION: "SESSION";
312
310
  CR: "CR";
313
311
  MS: "MS";
314
312
  }>;
315
313
  name: z.ZodString;
316
314
  description: z.ZodString;
317
- asil: z.ZodOptional<z.ZodEnum<{
318
- QM: "QM";
319
- A: "A";
320
- B: "B";
321
- C: "C";
322
- D: "D";
323
- }>>;
324
315
  method: z.ZodOptional<z.ZodEnum<{
325
316
  test: "test";
326
317
  inspection: "inspection";
@@ -331,7 +322,6 @@ export declare const OntologyGraph: z.ZodObject<{
331
322
  functional: "functional";
332
323
  "non-functional": "non-functional";
333
324
  risk: "risk";
334
- negative: "negative";
335
325
  mitigation: "mitigation";
336
326
  precondition: "precondition";
337
327
  postcondition: "postcondition";
@@ -356,12 +346,7 @@ export declare const OntologyGraph: z.ZodObject<{
356
346
  verify: "verify";
357
347
  allocate: "allocate";
358
348
  relation: "relation";
359
- produces: "produces";
360
349
  }>;
361
- category: z.ZodOptional<z.ZodEnum<{
362
- modeling: "modeling";
363
- audit: "audit";
364
- }>>;
365
350
  label: z.ZodOptional<z.ZodString>;
366
351
  weight: z.ZodDefault<z.ZodNumber>;
367
352
  attributes: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
@@ -372,7 +357,7 @@ export declare const OntologyGraph: z.ZodObject<{
372
357
  export type OntologyGraph = z.infer<typeof OntologyGraph>;
373
358
  /** Human-readable descriptions for each ElementType. */
374
359
  export declare const ELEMENT_DESCRIPTIONS: Record<ElementType, string>;
375
- /** Element types used in modeling (user-visible). Excludes audit types (SESSION, CR). */
360
+ /** Element types used in modeling (user-visible). Excludes the planning type CR. */
376
361
  export declare const MODELING_ELEMENT_TYPES: ElementType[];
377
362
  export interface AttributeSpec {
378
363
  key: string;
@@ -13,7 +13,7 @@ import { z } from 'zod/v4';
13
13
  export const ElementType = z.enum([
14
14
  'SYS', 'UC', 'ACTOR', 'FCHAIN',
15
15
  'FUNC', 'FLOW', 'REQ', 'TEST',
16
- 'MOD', 'SCHEMA', 'SESSION', 'CR',
16
+ 'MOD', 'SCHEMA', 'CR',
17
17
  'MS',
18
18
  ]);
19
19
  /**
@@ -22,14 +22,49 @@ export const ElementType = z.enum([
22
22
  * verify=test coverage, allocate=function-to-module, relation=generic link.
23
23
  */
24
24
  export const TraceType = z.enum([
25
- 'compose', 'io', 'satisfy', 'verify', 'allocate', 'relation', 'produces',
25
+ 'compose', 'io', 'satisfy', 'verify', 'allocate', 'relation',
26
26
  ]);
27
- /** REQ kind — 7 values aligned with SysML 2.0 + FMEA (CR-180). */
27
+ /**
28
+ * REQ kind — 6 values aligned with SysML 2.0 + FMEA (CR-180).
29
+ *
30
+ * CR-SM-266 B: `negative` ENTFAELLT. Ein Verbots-REQ ("das System tut X nie") ist auch eine
31
+ * Anforderung — die Implementierung muss Regeln abfragen oder Sicherungen einbauen, und das
32
+ * ist `functional`. Der Wert hatte NULL Leser (kein Regel-, Gate- oder View-Konsument; anders
33
+ * als risk/mitigation → FM-01..03, pre/postcondition → UC-05/06, non-functional → NFR-01) und
34
+ * haette durch die where-Praedikate am satisfy-Pattern per AUSLASSUNG erstmals Wirkung
35
+ * bekommen: er stand in keiner der vier Listen und waere damit nur noch per FCHAIN erfuellbar
36
+ * gewesen. Die sechs verbleibenden Werte partitionieren die where-Listen VOLLSTAENDIG — kein
37
+ * Wert ohne Zuordnung, keine Kante, die durch ein Loch in der Aufzaehlung faellt.
38
+ */
28
39
  export const ReqKind = z.enum([
29
- 'functional', 'non-functional', 'risk', 'negative',
40
+ 'functional', 'non-functional', 'risk',
30
41
  'mitigation', 'precondition', 'postcondition',
31
42
  ]);
32
- export const AsilLevel = z.enum(['QM', 'A', 'B', 'C', 'D']);
43
+ /**
44
+ * `kinds` in der Form, die WIRKLICH auf Platte liegt — als Liste.
45
+ *
46
+ * Gefunden beim Migrations-Audit zu CR-SM-266 (2026-08-25), nicht von Hand: drei Graphen der
47
+ * Familie tragen `kinds` als blossen STRING statt als Liste (`graph-view-edit`
48
+ * `REQ-edit-genesis: 'functional'`, dazu gc_test-graphview und graphify), einer davon mit dem
49
+ * Wert `'security'`, den das Enum gar nicht kennt. Ursache ist der Format-E-Weg: dort reist
50
+ * das Feld als `@kinds a,b` und wird erst vom Konsumenten gehoben (graph-api-core, CR-195d).
51
+ *
52
+ * Seit die where-Praedikate `kinds` LESEN (CR-SM-266 B), ist diese Drift tragend: ein
53
+ * `kinds.every(...)` auf einem String wirft, und eine geworfene Exception in `isValidTrace`
54
+ * reisst den ganzen Regellauf statt einen Befund zu melden. Die SSOT muss auch bei kaputten
55
+ * Daten ein URTEIL liefern, keinen Absturz.
56
+ *
57
+ * Normalisieren heisst hier ausdruecklich NICHT raten: `'a,b'` wird zu `['a','b']`, weil genau
58
+ * das die Schreibform ist — ein unbekannter Wert wie `'security'` ueberlebt die Normalisierung
59
+ * und faellt danach am Enum-Vergleich durch. Die Drift wird sichtbar, nicht geheilt.
60
+ */
61
+ export function normalizeReqKinds(raw) {
62
+ if (raw == null)
63
+ return [];
64
+ if (Array.isArray(raw))
65
+ return raw.map(k => String(k).trim()).filter(Boolean);
66
+ return String(raw).split(',').map(k => k.trim()).filter(Boolean);
67
+ }
33
68
  /** INCOSE TIAD verification method (only relevant for type=TEST). */
34
69
  export const VerificationMethod = z.enum(['test', 'inspection', 'analysis', 'demonstration']);
35
70
  /** Test execution result (only relevant for type=TEST). */
@@ -161,7 +196,6 @@ export const OntologyElement = z.object({
161
196
  type: ElementType,
162
197
  name: z.string(),
163
198
  description: z.string(),
164
- asil: AsilLevel.optional(),
165
199
  /** INCOSE TIAD verification method — only for TEST elements (CR-057). */
166
200
  method: VerificationMethod.optional(),
167
201
  /** REQ kinds: multi-valued classification (CR-180). Only for REQ elements. */
@@ -175,14 +209,24 @@ export const OntologyElement = z.object({
175
209
  * A trace (edge) in the SE ontology graph.
176
210
  * `label` provides semantic context for 'relation' edges (e.g. 'derives', 'depends-on').
177
211
  */
178
- /** Trace category: modeling traces are user-visible, audit traces are internal. */
179
- export const TraceCategory = z.enum(['modeling', 'audit']);
212
+ /*
213
+ * CR-SM-266 D5: `TraceCategory` und `Trace.category` ENTFALLEN ersatzlos.
214
+ *
215
+ * Die Unterscheidung modeling/audit trug genau EIN Pattern (`SESSION -produces-> *`), und das
216
+ * ist mit D5 weg. Was blieb, waere ein SCHLUPFLOCH gewesen, kein Vertrag: R-08 und R-18 haben
217
+ * `category === 'audit'` uebersprungen (`filter(t => t.category !== 'audit')`), also haette
218
+ * JEDE Kante mit diesem Attribut die komplette Pattern-Matrix umgangen — die
219
+ * Referenzintegritaet gleich mit. Ein selbstgesetztes Attribut, das eine Strukturpruefung
220
+ * abschaltet, ist genau die Klasse, die CR-SM-263 und CR-SM-257 abgelehnt haben, und hier
221
+ * zusaetzlich der billige Zweitweg aus CR-GC-366: die Kante gaebe es weiterhin, nur ungeprueft.
222
+ * Gemessen: 0 Kanten in allen 9 aktiven Familie-Graphen tragen `category` ueberhaupt — die
223
+ * Entfernung nimmt niemandem etwas weg. Provenance lebt seit CR-GC-347 in
224
+ * `.graphcode/audit.jsonl`, ausserhalb des Graphen.
225
+ */
180
226
  export const Trace = z.object({
181
227
  source: z.string(),
182
228
  target: z.string(),
183
229
  type: TraceType,
184
- /** 'modeling' (default, user-visible) or 'audit' (internal, e.g. produces traces). Absent = modeling. */
185
- category: TraceCategory.optional(),
186
230
  label: z.string().optional(),
187
231
  weight: z.number().default(1),
188
232
  attributes: z.record(z.string(), z.unknown()).optional(),
@@ -208,12 +252,11 @@ export const ELEMENT_DESCRIPTIONS = {
208
252
  TEST: 'Testfall',
209
253
  MOD: 'Modul (SW-Paket oder HW-Baugruppe)',
210
254
  SCHEMA: 'Datenschema (Zod)',
211
- SESSION: 'Audit-Session',
212
255
  CR: 'Change Request',
213
256
  MS: 'Meilenstein',
214
257
  };
215
- /** Element types used in modeling (user-visible). Excludes audit types (SESSION, CR). */
216
- export const MODELING_ELEMENT_TYPES = ElementType.options.filter(t => t !== 'SESSION' && t !== 'CR');
258
+ /** Element types used in modeling (user-visible). Excludes the planning type CR. */
259
+ export const MODELING_ELEMENT_TYPES = ElementType.options.filter(t => t !== 'CR');
217
260
  /**
218
261
  * Documented attributes per ElementType.
219
262
  * `attributes` is Record<string,unknown> on the schema level, but these are
@@ -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<{