@sigloch/contracts 5.0.0 → 6.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/analysis-freshness-rules.d.ts +1 -1
- package/dist/se/analysis-freshness-rules.js +2 -2
- package/dist/se/ao-rules.js +103 -62
- package/dist/se/evaluate-all.js +7 -1
- package/dist/se/format-e-parser.d.ts +11 -2
- package/dist/se/format-e-parser.js +33 -3
- package/dist/se/grammar-snapshot.d.ts +23 -0
- package/dist/se/grammar-snapshot.js +184 -0
- package/dist/se/index.d.ts +2 -2
- package/dist/se/index.js +2 -2
- package/dist/se/ontology.d.ts +1 -1
- package/dist/se/ontology.js +1 -1
- package/dist/se/readiness.js +2 -5
- package/dist/se/rules.d.ts +1 -1
- package/dist/se/rules.js +96 -99
- package/package.json +6 -3
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* CR-SM-227: Analysis-Freshness-Legs — the third leg-kind a review gate checks
|
|
3
|
-
* (rule legs, layer-presence legs [R-28], now analysis-freshness legs).
|
|
3
|
+
* (rule legs, layer-presence legs [R-28, entfallen mit CR-SM-247], now analysis-freshness legs).
|
|
4
4
|
* Presence/Resolution-Split (wie R-19/R-20/R-26/R-27 vs. RC-01..05): these five
|
|
5
5
|
* rules are the PRÜFBARE FORM only — does a `graphVersion` stamp exist for the
|
|
6
6
|
* artifact, as a graph attribute. They never compare the stamp against the
|
|
@@ -6,12 +6,12 @@ const ARTIFACT_LABEL = {
|
|
|
6
6
|
fmea: 'FMEA',
|
|
7
7
|
implplan: 'Implementation Plan',
|
|
8
8
|
};
|
|
9
|
-
/** Presence-check for one analysis artifact's freshness stamp, anchored on SYS
|
|
9
|
+
/** Presence-check for one analysis artifact's freshness stamp, anchored on SYS. */
|
|
10
10
|
function analysisFreshnessPresence(ruleId, artifactId) {
|
|
11
11
|
return (graph) => {
|
|
12
12
|
const sys = graph.elements.find(e => e.type === 'SYS');
|
|
13
13
|
if (!sys)
|
|
14
|
-
return []; // nothing to anchor on yet (
|
|
14
|
+
return []; // nothing to anchor on yet (vacuous-complete exemption)
|
|
15
15
|
const stamp = sys.attributes?.analysisFreshness?.[artifactId];
|
|
16
16
|
if (AnalysisFreshnessStampSchema.safeParse(stamp).success)
|
|
17
17
|
return [];
|
package/dist/se/ao-rules.js
CHANGED
|
@@ -263,75 +263,116 @@ export function ca01CapabilityAllocation(graph) {
|
|
|
263
263
|
return violations;
|
|
264
264
|
}
|
|
265
265
|
// ---------------------------------------------------------------------------
|
|
266
|
-
// IO-01:
|
|
267
|
-
//
|
|
268
|
-
//
|
|
269
|
-
//
|
|
270
|
-
//
|
|
271
|
-
//
|
|
266
|
+
// IO-01: die Wirkkette haengt zusammen (CR-192, CR-SM-226, CR-SM-250)
|
|
267
|
+
//
|
|
268
|
+
// Geprueft wird ERREICHBARKEIT im io-Teilgraphen der Kette, nicht Adjazenz. Bis CR-SM-250
|
|
269
|
+
// musste es fuer JEDES Paar einen gemeinsamen FLOW geben — die Regel forderte damit einen
|
|
270
|
+
// vollstaendigen Graphen (n·(n−1)/2 Paarungen), obwohl eine Wirkkette eine Sequenz ist. Der
|
|
271
|
+
// Zaehler wuchs quadratisch mit der Kettenlaenge, der Nenner-Beitrag linear: eine lange, gut
|
|
272
|
+
// modellierte Kette las sich schlechter als eine kurze. Dieselbe Korrektur ist fuer R-21
|
|
273
|
+
// bereits vollzogen (CR-GC-315, "reuse was penalised quadratically").
|
|
274
|
+
//
|
|
275
|
+
// Zwei Praezisierungen gegenueber dem Vorschlag (Review 2026-08-20):
|
|
276
|
+
//
|
|
277
|
+
// 1. EIN BEFUND JE GLIED, nicht je Paar. Auch nach dem Umbau waeren zwei Komponenten der
|
|
278
|
+
// Groesse n/2 wieder n²/4 Befunde. Gemeldet wird je Glied ausserhalb der groessten
|
|
279
|
+
// Komponente — hoechstens ein Befund je FUNC und Kette, damit der Zaehler den
|
|
280
|
+
// Nenner-Beitrag (`domain: ['FUNC']`) nicht uebersteigt (die Fehlmessung aus CR-SM-242).
|
|
281
|
+
// Groesste Komponente bei Gleichstand: die mit der lexikografisch kleinsten FUNC-ID —
|
|
282
|
+
// ein Befund darf nicht an der Array-Reihenfolge haengen (CR-SM-244, Ordnung).
|
|
283
|
+
//
|
|
284
|
+
// 2. GAR NICHT VERDRAHTETE GLIEDER GEHOEREN R-31. Eine FUNC ohne jede io-Kante ist trivial
|
|
285
|
+
// ihre eigene Komponente; sie zu melden waere eine zweite Meldung derselben Ursache. Am
|
|
286
|
+
// sigloch-modules-Selbstmodell waren 6 der 7 IO-01-Elemente zugleich R-31-Elemente —
|
|
287
|
+
// genau die Doppelzaehlung, die Gate 4 verbietet. IO-01 meldet ab jetzt nur, was
|
|
288
|
+
// verdrahtet, aber von seiner Kette abgehaengt ist. Das ist die Aussage, die keine
|
|
289
|
+
// andere Regel trifft.
|
|
272
290
|
// ---------------------------------------------------------------------------
|
|
273
291
|
export function io01CrossModuleCompleteness(graph) {
|
|
274
292
|
const violations = [];
|
|
275
|
-
const
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
293
|
+
const flowIds = new Set(graph.elements.filter(e => e.type === 'FLOW').map(e => e.id));
|
|
294
|
+
const funcIdSet = new Set(graph.elements.filter(e => e.type === 'FUNC').map(e => e.id));
|
|
295
|
+
/** io-Nachbarschaft FUNC <-> FLOW, richtungslos: die Frage ist "haengt das zusammen?". */
|
|
296
|
+
const flowsOfFunc = new Map();
|
|
297
|
+
for (const t of graph.traces) {
|
|
298
|
+
if (t.type !== 'io')
|
|
299
|
+
continue;
|
|
300
|
+
const [f, fl] = funcIdSet.has(t.source) && flowIds.has(t.target) ? [t.source, t.target]
|
|
301
|
+
: funcIdSet.has(t.target) && flowIds.has(t.source) ? [t.target, t.source]
|
|
302
|
+
: [undefined, undefined];
|
|
303
|
+
if (!f || !fl)
|
|
304
|
+
continue;
|
|
305
|
+
if (!flowsOfFunc.has(f))
|
|
306
|
+
flowsOfFunc.set(f, new Set());
|
|
307
|
+
flowsOfFunc.get(f).add(fl);
|
|
308
|
+
}
|
|
309
|
+
for (const fc of graph.elements.filter(e => e.type === 'FCHAIN')) {
|
|
310
|
+
const members = graph.traces
|
|
279
311
|
.filter(t => t.source === fc.id && t.type === 'compose')
|
|
280
312
|
.map(t => t.target)
|
|
281
|
-
.filter(id =>
|
|
282
|
-
|
|
313
|
+
.filter(id => funcIdSet.has(id));
|
|
314
|
+
// Nur verdrahtete Glieder — der Rest ist R-31s Befund (s. Kopf, Praezisierung 2).
|
|
315
|
+
const wired = members.filter(id => (flowsOfFunc.get(id)?.size ?? 0) > 0).sort();
|
|
316
|
+
if (wired.length < 2)
|
|
283
317
|
continue;
|
|
284
|
-
//
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
return false;
|
|
307
|
-
if (t1.source !== fA && t1.source !== fB)
|
|
308
|
-
return false;
|
|
309
|
-
const flowId = t1.target;
|
|
310
|
-
const flowEl = graph.elements.find(e => e.id === flowId && e.type === 'FLOW');
|
|
311
|
-
if (!flowEl)
|
|
312
|
-
return false;
|
|
313
|
-
const otherFunc = t1.source === fA ? fB : fA;
|
|
314
|
-
return graph.traces.some(t2 => t2.type === 'io' &&
|
|
315
|
-
((t2.source === flowId && t2.target === otherFunc) ||
|
|
316
|
-
(t2.target === flowId && t2.source === otherFunc)));
|
|
317
|
-
});
|
|
318
|
-
if (!hasFlow) {
|
|
319
|
-
const elA = graph.elements.find(e => e.id === fA);
|
|
320
|
-
const elB = graph.elements.find(e => e.id === fB);
|
|
321
|
-
violations.push({
|
|
322
|
-
rule_id: 'IO-01',
|
|
323
|
-
severity: 'warning',
|
|
324
|
-
element_id: fA,
|
|
325
|
-
message: `${fA} (${modA}) and ${fB} (${modB}) in FCHAIN ${fc.id} have no IO path — missing FLOW?`,
|
|
326
|
-
fix_hint: 'Add a FLOW element with io traces between these functions',
|
|
327
|
-
context: {
|
|
328
|
-
element_type: 'FUNC',
|
|
329
|
-
element_name: elA?.name ?? fA,
|
|
330
|
-
candidate_targets: elB ? [{ id: elB.id, type: elB.type, name: elB.name }] : undefined,
|
|
331
|
-
},
|
|
332
|
-
});
|
|
333
|
-
}
|
|
318
|
+
// Union-Find ueber die Glieder: zwei Glieder am selben FLOW sind verbunden, Ketten von
|
|
319
|
+
// FLOW-Glied-FLOW verschmelzen dadurch transitiv zu einer Komponente.
|
|
320
|
+
const parent = new Map(wired.map(id => [id, id]));
|
|
321
|
+
const find = (x) => {
|
|
322
|
+
let r = x;
|
|
323
|
+
while (parent.get(r) !== r)
|
|
324
|
+
r = parent.get(r);
|
|
325
|
+
while (parent.get(x) !== r) {
|
|
326
|
+
const nxt = parent.get(x);
|
|
327
|
+
parent.set(x, r);
|
|
328
|
+
x = nxt;
|
|
329
|
+
}
|
|
330
|
+
return r;
|
|
331
|
+
};
|
|
332
|
+
const union = (a, b) => { const ra = find(a), rb = find(b); if (ra !== rb)
|
|
333
|
+
parent.set(ra < rb ? rb : ra, ra < rb ? ra : rb); };
|
|
334
|
+
const byFlow = new Map();
|
|
335
|
+
for (const id of wired)
|
|
336
|
+
for (const fl of flowsOfFunc.get(id)) {
|
|
337
|
+
if (!byFlow.has(fl))
|
|
338
|
+
byFlow.set(fl, []);
|
|
339
|
+
byFlow.get(fl).push(id);
|
|
334
340
|
}
|
|
341
|
+
for (const sharing of byFlow.values())
|
|
342
|
+
for (let i = 1; i < sharing.length; i++)
|
|
343
|
+
union(sharing[0], sharing[i]);
|
|
344
|
+
const components = new Map();
|
|
345
|
+
for (const id of wired) {
|
|
346
|
+
const root = find(id);
|
|
347
|
+
if (!components.has(root))
|
|
348
|
+
components.set(root, []);
|
|
349
|
+
components.get(root).push(id);
|
|
350
|
+
}
|
|
351
|
+
if (components.size < 2)
|
|
352
|
+
continue;
|
|
353
|
+
// Groesste Komponente; bei Gleichstand die mit der kleinsten ID — deterministisch.
|
|
354
|
+
const largest = [...components.values()].sort((a, b) => b.length - a.length || a[0].localeCompare(b[0]))[0];
|
|
355
|
+
const largestSet = new Set(largest);
|
|
356
|
+
for (const id of wired) {
|
|
357
|
+
if (largestSet.has(id))
|
|
358
|
+
continue;
|
|
359
|
+
const el = graph.elements.find(e => e.id === id);
|
|
360
|
+
const mod = graph.traces.find(t => t.source === id && t.type === 'allocate')?.target ?? 'unallocated';
|
|
361
|
+
violations.push({
|
|
362
|
+
rule_id: 'IO-01',
|
|
363
|
+
severity: 'warning',
|
|
364
|
+
element_id: id,
|
|
365
|
+
message: `${id} (${mod}) is io-wired but not connected to the rest of FCHAIN ${fc.id} — no FLOW path to ${largest[0]}`,
|
|
366
|
+
fix_hint: `Add a FLOW (or io traces on an existing one) linking ${id} to a function of the same chain`,
|
|
367
|
+
context: {
|
|
368
|
+
element_type: 'FUNC',
|
|
369
|
+
element_name: el?.name ?? id,
|
|
370
|
+
candidate_targets: largest.slice(0, 3).map(t => {
|
|
371
|
+
const te = graph.elements.find(e => e.id === t);
|
|
372
|
+
return { id: t, type: te?.type ?? 'FUNC', name: te?.name ?? t };
|
|
373
|
+
}),
|
|
374
|
+
},
|
|
375
|
+
});
|
|
335
376
|
}
|
|
336
377
|
}
|
|
337
378
|
return violations;
|
package/dist/se/evaluate-all.js
CHANGED
|
@@ -24,7 +24,13 @@ export const ALL_RULE_DEFS = [
|
|
|
24
24
|
].map(r => ({ id: r.id, name: r.name, severity: r.severity, domain: r.domain }));
|
|
25
25
|
// CR-SM-221: 'RD-' was missing — 'RD-01'.startsWith('R-') is false, so the
|
|
26
26
|
// decomposition rules ran in `default` only and never in the `se` profile.
|
|
27
|
-
|
|
27
|
+
// CR-SM-245: 'MS-' fehlte hier UND in CODING_PREFIXES — MS-01/02/03 fielen aus beiden Profilen
|
|
28
|
+
// und liefen nur im 'default'-Lauf. Die Dimension `ms` wird von genau vier Regeln getragen
|
|
29
|
+
// (MS-01/02/03 + AF-05); AF-05 ist eine `domain: ['graph']`-Regel und traegt per CR-SM-239
|
|
30
|
+
// keinen Nenner bei. Im se-Profil hatte `ms` damit keine einzige zaehlende Regel — ein leerer
|
|
31
|
+
// Topic-Score und drei fehlende SRR-Legs. Zweiter Fall derselben Klasse nach 'RD-' (CR-SM-221),
|
|
32
|
+
// jetzt vom Profil-Test in se-grammar-invariant.test.ts abgedeckt.
|
|
33
|
+
const SE_PREFIXES = ['R-', 'RD-', 'UC-', 'FC-', 'SC-', 'MT-', 'CR-', 'AO-', 'FM-', 'NFR-', 'RT-', 'PH-', 'CA-', 'IO-', 'VR-', 'CL-', 'AF-', 'MS-'];
|
|
28
34
|
const CODING_PREFIXES = ['BQ-', 'ND-'];
|
|
29
35
|
export function getRuleDefsForProfile(profile) {
|
|
30
36
|
if (profile === 'se')
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* @sigloch/contracts/se
|
|
11
11
|
*/
|
|
12
12
|
import { ElementType } from './ontology.js';
|
|
13
|
-
import type { TraceType, OntologyGraph } from './ontology.js';
|
|
13
|
+
import type { TraceType, OntologyGraph, AttributeSpec } from './ontology.js';
|
|
14
14
|
export interface FormatEOperation {
|
|
15
15
|
type: 'add_node' | 'remove_node' | 'update_node' | 'add_edge' | 'remove_edge' | 'strict_add_node' | 'strict_add_edge';
|
|
16
16
|
semanticId: string;
|
|
@@ -50,7 +50,16 @@ export declare function extractFormatE(llmOutput: string): string | null;
|
|
|
50
50
|
* package over. Two hydration rules would drift; there is one, and it lives here with the
|
|
51
51
|
* schemas it feeds.
|
|
52
52
|
*/
|
|
53
|
-
|
|
53
|
+
/**
|
|
54
|
+
* CR-SM-251: der deklarierte Typ des Attributs entscheidet, nicht die Schreibweise des Wertes.
|
|
55
|
+
*
|
|
56
|
+
* `ELEMENT_ATTRIBUTES` sagt fuer 14 Attribute (9 boolean, 5 number), welchen Typ sie tragen.
|
|
57
|
+
* Wer stattdessen raet (`raw === 'true'`, `/^\d+$/`), verschiebt den Defekt nur: ein
|
|
58
|
+
* Freitext-Attribut mit dem Wert "true" oder "8" kippt dann still den Typ. Unbekannte Keys —
|
|
59
|
+
* jeder Graph darf eigene tragen — fallen sauber auf String zurueck.
|
|
60
|
+
*/
|
|
61
|
+
export declare function attributeTypeOf(elementType: string | undefined, key: string): AttributeSpec['type'] | undefined;
|
|
62
|
+
export declare function hydrateAttrValue(raw: string, declaredType?: AttributeSpec['type']): unknown;
|
|
54
63
|
export interface ParseFormatEOptions {
|
|
55
64
|
/**
|
|
56
65
|
* CR-SM-216: resolve the type of a uid that this text does not declare. A mutation
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*
|
|
10
10
|
* @sigloch/contracts/se
|
|
11
11
|
*/
|
|
12
|
-
import { ElementType } from './ontology.js';
|
|
12
|
+
import { ElementType, ELEMENT_ATTRIBUTES } from './ontology.js';
|
|
13
13
|
import { isValidTrace } from './meta-model.js';
|
|
14
14
|
// ---------------------------------------------------------------------------
|
|
15
15
|
// Extraction
|
|
@@ -54,7 +54,37 @@ const ATTR_RE = /^\s*@(\w+)\s+(.+)$/;
|
|
|
54
54
|
* package over. Two hydration rules would drift; there is one, and it lives here with the
|
|
55
55
|
* schemas it feeds.
|
|
56
56
|
*/
|
|
57
|
-
|
|
57
|
+
/**
|
|
58
|
+
* CR-SM-251: der deklarierte Typ des Attributs entscheidet, nicht die Schreibweise des Wertes.
|
|
59
|
+
*
|
|
60
|
+
* `ELEMENT_ATTRIBUTES` sagt fuer 14 Attribute (9 boolean, 5 number), welchen Typ sie tragen.
|
|
61
|
+
* Wer stattdessen raet (`raw === 'true'`, `/^\d+$/`), verschiebt den Defekt nur: ein
|
|
62
|
+
* Freitext-Attribut mit dem Wert "true" oder "8" kippt dann still den Typ. Unbekannte Keys —
|
|
63
|
+
* jeder Graph darf eigene tragen — fallen sauber auf String zurueck.
|
|
64
|
+
*/
|
|
65
|
+
export function attributeTypeOf(elementType, key) {
|
|
66
|
+
if (!elementType)
|
|
67
|
+
return undefined;
|
|
68
|
+
const specs = ELEMENT_ATTRIBUTES[elementType];
|
|
69
|
+
return specs?.find(s => s.key === key)?.type;
|
|
70
|
+
}
|
|
71
|
+
export function hydrateAttrValue(raw, declaredType) {
|
|
72
|
+
// CR-SM-251: `concept:true` kam als String "true" an, und alle 11 Vergleiche in
|
|
73
|
+
// rules.ts/conformance-rules.ts pruefen identitaetsscharf (`=== true`). Damit war jeder
|
|
74
|
+
// Ausnahme-Schalter (concept/external/safety_relevant/spike) ueber Format-E unerreichbar:
|
|
75
|
+
// der Autor befolgt den fix_hint woertlich, und die Warnung bleibt stehen. Ein Export-Reimport
|
|
76
|
+
// entwertete zusaetzlich jede bestehende Ausnahme still.
|
|
77
|
+
if (declaredType === 'boolean') {
|
|
78
|
+
if (raw === 'true')
|
|
79
|
+
return true;
|
|
80
|
+
if (raw === 'false')
|
|
81
|
+
return false;
|
|
82
|
+
return raw; // kein stiller Fallback auf false — ein unlesbarer Wert bleibt sichtbar falsch
|
|
83
|
+
}
|
|
84
|
+
if (declaredType === 'number') {
|
|
85
|
+
const n = Number(raw);
|
|
86
|
+
return raw.trim() !== '' && Number.isFinite(n) ? n : raw;
|
|
87
|
+
}
|
|
58
88
|
if (!/^[{[]/.test(raw))
|
|
59
89
|
return raw;
|
|
60
90
|
try {
|
|
@@ -90,7 +120,7 @@ export function parseFormatE(input, options = {}) {
|
|
|
90
120
|
if (lastOp && (lastOp.type === 'add_node' || lastOp.type === 'update_node' || lastOp.type === 'strict_add_node')) {
|
|
91
121
|
if (!lastOp.attributes)
|
|
92
122
|
lastOp.attributes = {};
|
|
93
|
-
lastOp.attributes[attrMatch[1]] = hydrateAttrValue(attrMatch[2].trim());
|
|
123
|
+
lastOp.attributes[attrMatch[1]] = hydrateAttrValue(attrMatch[2].trim(), attributeTypeOf(lastOp.elementType, attrMatch[1]));
|
|
94
124
|
}
|
|
95
125
|
else {
|
|
96
126
|
errors.push(`@attribute line without preceding node: "${line}"`);
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GENERIERT — nicht von Hand pflegen.
|
|
3
|
+
*
|
|
4
|
+
* Das Golden-File der SE-Grammatik (CR-SM-244). Sein Diff IST der Changelog der Oberflaeche:
|
|
5
|
+
* jede Zeile hier ist ein Vertrag, an dem ein Konsument der Familie haengt. Erzeugt von
|
|
6
|
+
* `npm run grammar:snapshot -w @sigloch/contracts`, geprueft von
|
|
7
|
+
* `tests/unit/se-grammar-invariant.test.ts`.
|
|
8
|
+
*
|
|
9
|
+
* Wer diese Datei aendert, ohne die zugehoerige Version in ./index.ts zu bumpen, hat den Zweck
|
|
10
|
+
* verfehlt — `npm run check:grammar` faellt darueber.
|
|
11
|
+
*/
|
|
12
|
+
export declare const GRAMMAR_SNAPSHOT: {
|
|
13
|
+
readonly versions: {
|
|
14
|
+
readonly ontology: "7.0.0";
|
|
15
|
+
readonly metaModel: "2.0.0";
|
|
16
|
+
readonly rules: "6.0.0";
|
|
17
|
+
};
|
|
18
|
+
readonly elementTypes: readonly ["ACTOR", "CR", "FCHAIN", "FLOW", "FUNC", "MOD", "MS", "REQ", "SCHEMA", "SESSION", "SYS", "TEST", "UC"];
|
|
19
|
+
readonly traceTypes: readonly ["allocate", "compose", "io", "produces", "relation", "satisfy", "verify"];
|
|
20
|
+
readonly patterns: readonly ["ACTOR -io-> FLOW", "ACTOR -io-> UC", "CR -relation-> FUNC", "CR -relation-> MOD", "CR -relation-> MS", "CR -relation-> REQ", "CR -relation-> UC", "FCHAIN -compose-> FUNC [1..*]", "FCHAIN -satisfy-> REQ", "FLOW -io-> ACTOR", "FLOW -io-> FUNC", "FLOW -io-> UC", "FLOW -relation-> SCHEMA", "FUNC -allocate-> MOD", "FUNC -compose-> FUNC [0..*]", "FUNC -io-> FLOW", "FUNC -satisfy-> REQ", "MOD -compose-> MOD [0..*]", "MOD -io-> MOD", "MOD -satisfy-> REQ", "MS -compose-> FUNC", "MS -compose-> MS", "MS -compose-> REQ", "MS -compose-> UC", "MS -relation-> MS", "REQ -compose-> REQ [0..*]", "SESSION -produces-> * (audit)", "SYS -compose-> MOD [0..*]", "SYS -compose-> REQ [0..*]", "SYS -compose-> SYS [0..*]", "SYS -compose-> UC [1..*]", "SYS -satisfy-> REQ", "TEST -verify-> REQ", "UC -compose-> FCHAIN [1..*]", "UC -compose-> REQ [1..*]"];
|
|
21
|
+
readonly attributes: readonly ["CR.rationale: string", "CR.spike: boolean", "CR.status: enum", "FLOW.protocol: string", "FLOW.qos: string", "FUNC.concept: boolean", "FUNC.external: boolean", "FUNC.measuredMs: number", "FUNC.realRef: object", "FUNC.safety_relevant: boolean", "FUNC.sourceFile: string", "FUNC.timingBudgetMs: number", "MOD.concept: boolean", "MOD.external: boolean", "MOD.kind: string", "MOD.path: string", "MOD.realRef: object", "REQ.detection: number", "REQ.occurrence: number", "REQ.severity: number", "SCHEMA.concept: boolean", "SCHEMA.contract: string", "SCHEMA.external: boolean", "SCHEMA.realRef: object", "TEST.concept: boolean", "TEST.sourceFile: string", "TEST.testRefs: array", "UC.operatingMode: string"];
|
|
22
|
+
readonly rules: readonly ["AF-01 (warning) domain=[graph]", "AF-02 (warning) domain=[graph]", "AF-03 (warning) domain=[graph]", "AF-04 (warning) domain=[graph]", "AF-05 (warning) domain=[graph]", "AO-D01 (info) domain=[FUNC]", "AO-D03 (info) domain=[FUNC]", "BQ-01 (warning) domain=[REQ]", "BQ-02 (warning) domain=[REQ]", "BQ-04 (warning) domain=[REQ]", "BQ-06 (warning) domain=[REQ]", "BQ-07 (warning) domain=[REQ]", "CA-01 (error) domain=[FUNC]", "CL-01 (warning) domain=[ACTOR]", "CR-01 (warning) domain=[MOD]", "CR-R01 (error) domain=[CR]", "CR-R02 (error) domain=[CR]", "CR-R03 (warning) domain=[all]", "CR-R04 (warning) domain=[CR]", "FC-01 (warning) domain=[FCHAIN]", "FC-02 (warning) domain=[UC]", "FC-03 (warning) domain=[FUNC]", "FC-04 (warning) domain=[FCHAIN]", "FM-01 (warning) domain=[REQ]", "FM-02 (warning) domain=[REQ]", "FM-03 (error) domain=[REQ]", "IO-01 (warning) domain=[FUNC]", "MS-01 (warning) domain=[MS]", "MS-02 (error) domain=[MS]", "MS-03 (info) domain=[CR]", "MT-01 (warning) domain=[MOD]", "MT-02 (info) domain=[MOD]", "ND-01 (error) domain=[FUNC]", "ND-02 (error) domain=[SCHEMA]", "NFR-01 (warning) domain=[FCHAIN,FUNC,MOD]", "PH-01 (info) domain=[MOD]", "R-01 (error) domain=[REQ]", "R-02 (warning) domain=[FUNC]", "R-03 (error) domain=[MOD]", "R-04 (warning) domain=[MOD]", "R-05 (warning) domain=[TEST]", "R-08 (error) domain=[all]", "R-10 (warning) domain=[FLOW]", "R-12 (warning) domain=[FUNC]", "R-14 (warning) domain=[UC]", "R-15 (warning) domain=[FCHAIN]", "R-16 (warning) domain=[ACTOR]", "R-17 (warning) domain=[SYS]", "R-18 (error) domain=[all]", "R-19 (warning) domain=[TEST]", "R-20 (warning) domain=[FUNC]", "R-21 (warning) domain=[FCHAIN]", "R-22 (warning) domain=[FUNC]", "R-23 (warning) domain=[MOD]", "R-26 (warning) domain=[SCHEMA]", "R-27 (warning) domain=[MOD]", "R-29 (error) domain=[TEST]", "R-30 (warning) domain=[FUNC]", "R-31 (warning) domain=[FUNC]", "RD-01 (warning) domain=[REQ]", "RD-02 (warning) domain=[REQ]", "RD-03 (info) domain=[REQ]", "RD-04 (warning) domain=[FUNC,MOD,SYS]", "RT-01 (error) domain=[FUNC]", "SC-02 (warning) domain=[SCHEMA]", "SC-04 (warning) domain=[FLOW]", "UC-01 (error) domain=[UC]", "UC-02 (error) domain=[UC]", "UC-03 (warning) domain=[UC]", "UC-04 (warning) domain=[UC]", "UC-05 (info) domain=[UC]", "UC-06 (info) domain=[UC]", "VR-01 (info) domain=[TEST]"];
|
|
23
|
+
};
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GENERIERT — nicht von Hand pflegen.
|
|
3
|
+
*
|
|
4
|
+
* Das Golden-File der SE-Grammatik (CR-SM-244). Sein Diff IST der Changelog der Oberflaeche:
|
|
5
|
+
* jede Zeile hier ist ein Vertrag, an dem ein Konsument der Familie haengt. Erzeugt von
|
|
6
|
+
* `npm run grammar:snapshot -w @sigloch/contracts`, geprueft von
|
|
7
|
+
* `tests/unit/se-grammar-invariant.test.ts`.
|
|
8
|
+
*
|
|
9
|
+
* Wer diese Datei aendert, ohne die zugehoerige Version in ./index.ts zu bumpen, hat den Zweck
|
|
10
|
+
* verfehlt — `npm run check:grammar` faellt darueber.
|
|
11
|
+
*/
|
|
12
|
+
export const GRAMMAR_SNAPSHOT = {
|
|
13
|
+
versions: {
|
|
14
|
+
ontology: "7.0.0",
|
|
15
|
+
metaModel: "2.0.0",
|
|
16
|
+
rules: "6.0.0",
|
|
17
|
+
},
|
|
18
|
+
elementTypes: [
|
|
19
|
+
"ACTOR",
|
|
20
|
+
"CR",
|
|
21
|
+
"FCHAIN",
|
|
22
|
+
"FLOW",
|
|
23
|
+
"FUNC",
|
|
24
|
+
"MOD",
|
|
25
|
+
"MS",
|
|
26
|
+
"REQ",
|
|
27
|
+
"SCHEMA",
|
|
28
|
+
"SESSION",
|
|
29
|
+
"SYS",
|
|
30
|
+
"TEST",
|
|
31
|
+
"UC",
|
|
32
|
+
],
|
|
33
|
+
traceTypes: [
|
|
34
|
+
"allocate",
|
|
35
|
+
"compose",
|
|
36
|
+
"io",
|
|
37
|
+
"produces",
|
|
38
|
+
"relation",
|
|
39
|
+
"satisfy",
|
|
40
|
+
"verify",
|
|
41
|
+
],
|
|
42
|
+
patterns: [
|
|
43
|
+
"ACTOR -io-> FLOW",
|
|
44
|
+
"ACTOR -io-> UC",
|
|
45
|
+
"CR -relation-> FUNC",
|
|
46
|
+
"CR -relation-> MOD",
|
|
47
|
+
"CR -relation-> MS",
|
|
48
|
+
"CR -relation-> REQ",
|
|
49
|
+
"CR -relation-> UC",
|
|
50
|
+
"FCHAIN -compose-> FUNC [1..*]",
|
|
51
|
+
"FCHAIN -satisfy-> REQ",
|
|
52
|
+
"FLOW -io-> ACTOR",
|
|
53
|
+
"FLOW -io-> FUNC",
|
|
54
|
+
"FLOW -io-> UC",
|
|
55
|
+
"FLOW -relation-> SCHEMA",
|
|
56
|
+
"FUNC -allocate-> MOD",
|
|
57
|
+
"FUNC -compose-> FUNC [0..*]",
|
|
58
|
+
"FUNC -io-> FLOW",
|
|
59
|
+
"FUNC -satisfy-> REQ",
|
|
60
|
+
"MOD -compose-> MOD [0..*]",
|
|
61
|
+
"MOD -io-> MOD",
|
|
62
|
+
"MOD -satisfy-> REQ",
|
|
63
|
+
"MS -compose-> FUNC",
|
|
64
|
+
"MS -compose-> MS",
|
|
65
|
+
"MS -compose-> REQ",
|
|
66
|
+
"MS -compose-> UC",
|
|
67
|
+
"MS -relation-> MS",
|
|
68
|
+
"REQ -compose-> REQ [0..*]",
|
|
69
|
+
"SESSION -produces-> * (audit)",
|
|
70
|
+
"SYS -compose-> MOD [0..*]",
|
|
71
|
+
"SYS -compose-> REQ [0..*]",
|
|
72
|
+
"SYS -compose-> SYS [0..*]",
|
|
73
|
+
"SYS -compose-> UC [1..*]",
|
|
74
|
+
"SYS -satisfy-> REQ",
|
|
75
|
+
"TEST -verify-> REQ",
|
|
76
|
+
"UC -compose-> FCHAIN [1..*]",
|
|
77
|
+
"UC -compose-> REQ [1..*]",
|
|
78
|
+
],
|
|
79
|
+
attributes: [
|
|
80
|
+
"CR.rationale: string",
|
|
81
|
+
"CR.spike: boolean",
|
|
82
|
+
"CR.status: enum",
|
|
83
|
+
"FLOW.protocol: string",
|
|
84
|
+
"FLOW.qos: string",
|
|
85
|
+
"FUNC.concept: boolean",
|
|
86
|
+
"FUNC.external: boolean",
|
|
87
|
+
"FUNC.measuredMs: number",
|
|
88
|
+
"FUNC.realRef: object",
|
|
89
|
+
"FUNC.safety_relevant: boolean",
|
|
90
|
+
"FUNC.sourceFile: string",
|
|
91
|
+
"FUNC.timingBudgetMs: number",
|
|
92
|
+
"MOD.concept: boolean",
|
|
93
|
+
"MOD.external: boolean",
|
|
94
|
+
"MOD.kind: string",
|
|
95
|
+
"MOD.path: string",
|
|
96
|
+
"MOD.realRef: object",
|
|
97
|
+
"REQ.detection: number",
|
|
98
|
+
"REQ.occurrence: number",
|
|
99
|
+
"REQ.severity: number",
|
|
100
|
+
"SCHEMA.concept: boolean",
|
|
101
|
+
"SCHEMA.contract: string",
|
|
102
|
+
"SCHEMA.external: boolean",
|
|
103
|
+
"SCHEMA.realRef: object",
|
|
104
|
+
"TEST.concept: boolean",
|
|
105
|
+
"TEST.sourceFile: string",
|
|
106
|
+
"TEST.testRefs: array",
|
|
107
|
+
"UC.operatingMode: string",
|
|
108
|
+
],
|
|
109
|
+
rules: [
|
|
110
|
+
"AF-01 (warning) domain=[graph]",
|
|
111
|
+
"AF-02 (warning) domain=[graph]",
|
|
112
|
+
"AF-03 (warning) domain=[graph]",
|
|
113
|
+
"AF-04 (warning) domain=[graph]",
|
|
114
|
+
"AF-05 (warning) domain=[graph]",
|
|
115
|
+
"AO-D01 (info) domain=[FUNC]",
|
|
116
|
+
"AO-D03 (info) domain=[FUNC]",
|
|
117
|
+
"BQ-01 (warning) domain=[REQ]",
|
|
118
|
+
"BQ-02 (warning) domain=[REQ]",
|
|
119
|
+
"BQ-04 (warning) domain=[REQ]",
|
|
120
|
+
"BQ-06 (warning) domain=[REQ]",
|
|
121
|
+
"BQ-07 (warning) domain=[REQ]",
|
|
122
|
+
"CA-01 (error) domain=[FUNC]",
|
|
123
|
+
"CL-01 (warning) domain=[ACTOR]",
|
|
124
|
+
"CR-01 (warning) domain=[MOD]",
|
|
125
|
+
"CR-R01 (error) domain=[CR]",
|
|
126
|
+
"CR-R02 (error) domain=[CR]",
|
|
127
|
+
"CR-R03 (warning) domain=[all]",
|
|
128
|
+
"CR-R04 (warning) domain=[CR]",
|
|
129
|
+
"FC-01 (warning) domain=[FCHAIN]",
|
|
130
|
+
"FC-02 (warning) domain=[UC]",
|
|
131
|
+
"FC-03 (warning) domain=[FUNC]",
|
|
132
|
+
"FC-04 (warning) domain=[FCHAIN]",
|
|
133
|
+
"FM-01 (warning) domain=[REQ]",
|
|
134
|
+
"FM-02 (warning) domain=[REQ]",
|
|
135
|
+
"FM-03 (error) domain=[REQ]",
|
|
136
|
+
"IO-01 (warning) domain=[FUNC]",
|
|
137
|
+
"MS-01 (warning) domain=[MS]",
|
|
138
|
+
"MS-02 (error) domain=[MS]",
|
|
139
|
+
"MS-03 (info) domain=[CR]",
|
|
140
|
+
"MT-01 (warning) domain=[MOD]",
|
|
141
|
+
"MT-02 (info) domain=[MOD]",
|
|
142
|
+
"ND-01 (error) domain=[FUNC]",
|
|
143
|
+
"ND-02 (error) domain=[SCHEMA]",
|
|
144
|
+
"NFR-01 (warning) domain=[FCHAIN,FUNC,MOD]",
|
|
145
|
+
"PH-01 (info) domain=[MOD]",
|
|
146
|
+
"R-01 (error) domain=[REQ]",
|
|
147
|
+
"R-02 (warning) domain=[FUNC]",
|
|
148
|
+
"R-03 (error) domain=[MOD]",
|
|
149
|
+
"R-04 (warning) domain=[MOD]",
|
|
150
|
+
"R-05 (warning) domain=[TEST]",
|
|
151
|
+
"R-08 (error) domain=[all]",
|
|
152
|
+
"R-10 (warning) domain=[FLOW]",
|
|
153
|
+
"R-12 (warning) domain=[FUNC]",
|
|
154
|
+
"R-14 (warning) domain=[UC]",
|
|
155
|
+
"R-15 (warning) domain=[FCHAIN]",
|
|
156
|
+
"R-16 (warning) domain=[ACTOR]",
|
|
157
|
+
"R-17 (warning) domain=[SYS]",
|
|
158
|
+
"R-18 (error) domain=[all]",
|
|
159
|
+
"R-19 (warning) domain=[TEST]",
|
|
160
|
+
"R-20 (warning) domain=[FUNC]",
|
|
161
|
+
"R-21 (warning) domain=[FCHAIN]",
|
|
162
|
+
"R-22 (warning) domain=[FUNC]",
|
|
163
|
+
"R-23 (warning) domain=[MOD]",
|
|
164
|
+
"R-26 (warning) domain=[SCHEMA]",
|
|
165
|
+
"R-27 (warning) domain=[MOD]",
|
|
166
|
+
"R-29 (error) domain=[TEST]",
|
|
167
|
+
"R-30 (warning) domain=[FUNC]",
|
|
168
|
+
"R-31 (warning) domain=[FUNC]",
|
|
169
|
+
"RD-01 (warning) domain=[REQ]",
|
|
170
|
+
"RD-02 (warning) domain=[REQ]",
|
|
171
|
+
"RD-03 (info) domain=[REQ]",
|
|
172
|
+
"RD-04 (warning) domain=[FUNC,MOD,SYS]",
|
|
173
|
+
"RT-01 (error) domain=[FUNC]",
|
|
174
|
+
"SC-02 (warning) domain=[SCHEMA]",
|
|
175
|
+
"SC-04 (warning) domain=[FLOW]",
|
|
176
|
+
"UC-01 (error) domain=[UC]",
|
|
177
|
+
"UC-02 (error) domain=[UC]",
|
|
178
|
+
"UC-03 (warning) domain=[UC]",
|
|
179
|
+
"UC-04 (warning) domain=[UC]",
|
|
180
|
+
"UC-05 (info) domain=[UC]",
|
|
181
|
+
"UC-06 (info) domain=[UC]",
|
|
182
|
+
"VR-01 (info) domain=[TEST]",
|
|
183
|
+
],
|
|
184
|
+
};
|
package/dist/se/index.d.ts
CHANGED
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
/** Ontology schema version (element types + trace types). */
|
|
6
6
|
export declare const ONTOLOGY_VERSION = "7.0.0";
|
|
7
7
|
/** Rules engine version (validation rules incl. RC conformance). */
|
|
8
|
-
export declare const RULES_VERSION = "
|
|
8
|
+
export declare const RULES_VERSION = "6.0.0";
|
|
9
9
|
/** Meta-model version (trace pattern constraints + format-e parser). */
|
|
10
|
-
export declare const META_MODEL_VERSION = "
|
|
10
|
+
export declare const META_MODEL_VERSION = "2.0.0";
|
|
11
11
|
export * from './ontology.js';
|
|
12
12
|
export * from './rules.js';
|
|
13
13
|
export * from './conformance-rules.js';
|
package/dist/se/index.js
CHANGED
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
/** Ontology schema version (element types + trace types). */
|
|
6
6
|
export const ONTOLOGY_VERSION = '7.0.0'; // BREAKING (CR-GC-366): das Trace-Pattern `FUNC -satisfy-> UC` entfaellt ersatzlos. Es war ein zweiter Weg vom Verhalten zum Use Case neben dem einzig tragenden `UC -compose-> FCHAIN -compose-> FUNC` — und der billigere: eine FUNC konnte einen UC bedienen, ohne in dessen Wirkkette zu stehen, womit IO-01 (FLOW-Pfade je Kettenpaar) und R-21 (Integrationstest je Kette) sie nie sahen. Die Abkuerzung umging genau die Pruefungen, die die Anbindung belastbar machen. Am graphcode-Selbstmodell waren 10 der 26 solchen Kanten reine Dopplung (die FUNC stand ohnehin in der FCHAIN), die restlichen 13 sind der Migrationsanlass. Jeder Graph mit solchen Kanten faellt jetzt R-18 (error) zur Last und muss migriert werden: FUNC in die FCHAIN des UC aufnehmen, satisfy-Kante loeschen. Prior: BREAKING (CR-SM-231b): das knotenweite `testResult` entfaellt — das Ergebnis haengt PRO `testRefs`-Eintrag (`result`, `ranAt`, `evidence`), aus demselben Grund wie `tool`. Ein einzelnes Ergebnis am Knoten konnte bei n Laeufen nicht sagen, welcher gemeint ist, und "einer rot, einer gruen" war gar nicht darstellbar — genau der Zustand, den ein Gate wissen muss. FM-03 liest jetzt "jeder Eintrag passed": "irgendeiner gruen" haette einen gruenen Unit-Lauf einen roten Visual-Lauf verdecken lassen. Ein Eintrag ohne Ergebnis ist nicht bestanden. VR-01 meldet die Dateien ohne Ergebnis statt nur den Knoten. Prior: BREAKING (CR-SM-231): `testRef` (Objekt) entfaellt ersatzlos zugunsten von `testRefs` (Array, min 1) — eine Abnahme, n Testdateien. Kein Union, kein Alias: ein Graph mit dem alten Attribut faellt R-19 zur Last und muss migriert werden. Anlass war ein Zaehl-Audit (537 laufende Tests in 67 Dateien, 14 TEST-Knoten mit je genau einer Datei, 53 Dateien an keine Abnahme gebunden) plus ein belegter Ausweichweg: dieselbe Spec-Datei stand im testRef ZWEIER TEST-Knoten, womit die Relation faktisch n:m war — ein roter Lauf keiner Abnahme mehr eindeutig zuordenbar, der TRR-Gate zaehlte dieselbe Evidenz doppelt. `tool` bleibt PRO EINTRAG: eine Abnahme mischt real die Runner (vitest + playwright). +AttributeSpec.type 'array' (testRefs ist das erste Listen-Attribut). Prior: BREAKING: `SemanticId` (`Name.TypeAbbr.Counter`) deleted, `ElementUid` (`<TYPE>-<slug>`) is the family canon — the old canon was used by no production graph of the family while 626 of 1145 elements already carried TYPE-slug (CR-SM-217); realRef unifies codeRef+schemaRef (+physical-MOD CAD ref), symbol optional; testRef stays separate (CR-228 C); +RepoRelativePathSchema on testRef/realRef .file — no absolute/`..` paths (CR-GC-255)
|
|
7
7
|
/** Rules engine version (validation rules incl. RC conformance). */
|
|
8
|
-
export const RULES_VERSION = '3.0.0'; // BREAKING (CR-GC-366): +R-30 Wirkketten-Bindung, +R-31 io-Verdrahtung, R-02 zieltyp-scharf. Die vier Pflichten einer FUNC (ein REQ erfuellen, verdrahtet sein, in einer Wirkkette haengen, in einem MOD wohnen) waren nur zu zweit geprueft: R-02 und R-22. R-30 schliesst die Kette — R-15 verlangte, dass eine FCHAIN Funktionen hat, niemand verlangte, dass eine Funktion zu einer FCHAIN gehoert; daran haengen aber IO-01 und R-21, die beide nur INNERHALB einer Kette pruefen. R-31 schliesst die Verdrahtung: R-10 stellt dieselbe Frage vom FLOW aus und sieht eine FUNC ohne jede io-Kante deshalb nie (sie kommt in der FLOW-Schleife nicht vor); nur ACTOR darf terminieren. Ein Befund je FUNC statt je fehlender Seite, sonst uebersteigt der Zaehler den Nenner-Beitrag (die Fehlmessung aus CR-SM-242). BREAKING ist R-02: der Filter war zieltyp-blind, ein `FUNC -satisfy-> UC` schaltete ihn stumm, obwohl kein Requirement erfuellt war — am Selbstmodell 6 von 33 Faellen verdeckt (gemeldet: 27). Messung am graphcode-Selbstmodell (82 FUNC): R-30 60, R-31 54 (52 ohne Eingang, 50 ohne Ausgang, 48 ohne beides), R-02 27->33, R-22 3, R-18 26 (die Migrationsschuld aus dem gestrichenen Pattern). Prior: CR-SM-242: R-10 (`['FUNC']`→`['FLOW']`), SC-04 (`['SCHEMA']`→`['FLOW']`) und IO-01 (`['MOD']`→`['FUNC']`) deklarieren jetzt den Typ, an dem sie WIRKLICH melden. Alle drei iterierten ueber einen anderen Typ als den deklarierten: ihre Verstoesse erhoehten den Zaehler einer Grundgesamtheit, zu der sie nicht gehoerten. Gemessen am graphcode-Graphen (46 FLOW / 55 FUNC / 20 SCHEMA / 10 MOD) hob das `schema` von 0.713 auf 0.783 — SC-04 zog 46 FLOW-Verstoesse gegen 20 SCHEMA; bei IO-01 uebertraf der Zaehler (21) den eigenen Nenner-Beitrag (10) schon, nur der Clamp verhinderte einen negativen Score. Kein Evaluator, keine Regel-Semantik und kein Gate-Urteil geaendert — nur die deklarierte Grundgesamtheit, wie bei CR-SM-239. Neu erzwungen durch `se-rule-domain-invariant.test.ts`: der Typ jedes gemeldeten Elements liegt in der `domain` der Regel (stumm fuer 29 der 72 Regeln, die auf den Fixtures nicht feuern). CR-SM-243 (dieselbe Auslieferung): genau dieser Test hat vier weitere Faelle gefunden, die von Hand nie auffielen — R-03 (`['FUNC']`→`['MOD']`, meldet am Modul, das ASIL-D und QM mischt), FC-03 (`['FCHAIN']`→`['FUNC']`, meldet am FUNC mit der verschachtelten Zerlegung), NFR-01 (`['FUNC']`→`['MOD','FUNC','FCHAIN']`, Budgets liegen physisch am MOD und verhaltensseitig an FUNC/FCHAIN) und CR-R03 (`['CR']`→`['all']`, meldet am getrackten Zielknoten beliebigen Typs — nach R-08/R-18 die dritte und bewusst begruendete `all`-Regel). Prior: CR-SM-239: AF-01..05 und R-28 deklarieren `domain: ['graph']` statt `['SYS']` — sie pruefen EINMAL den Graphen, nicht die SYS-Elemente. Als Ein-Element-Gruppe waren sie ein Nenner-Beitrag mit fast sicherem Verstoss und zogen jede sonst leere Dimension auf 0 %: ein Graph ohne eine einzige REQ las `req` = 0 % (1 - 2/2 aus AF-01/AF-03) statt „nichts zu messen", und die Fokuswahl schickte den Executor dorthin. Kein Regelverhalten geaendert — nur die deklarierte Grundgesamtheit. Prior: CR-SM-231: +R-29 Testdatei-Exklusivitaet (severity **error**) — jede Testdatei erscheint in hoechstens einem `testRefs`. Das ist die Haelfte, die 1:n ERZWINGT statt es nur zu erlauben; ohne sie driftet das Attribut zurueck nach n:m. Bewusst schaerfer als R-19/R-20 (beide warning): eine doppelt beanspruchte Datei macht Gate-Zahlen nachweislich falsch, das ist eine Fehlmessung und kein Vollstaendigkeits-Signal. Rein graph-strukturell, kein I/O. R-19 und RC-02 lesen `testRefs`; RC-02 iteriert die Eintraege und nennt den konkreten Pfad statt nur der Knoten-ID. Prior: CR-SM-236: die letzten drei Urteilsschwellen sind Policy-Parameter statt Literal — CR-01 (`crossingFlows`, erstmals mit Aus-Zustand: `null` unterdrueckt auch die info je Modulpaar, die bisher die arch-Dimension verduennte), FM-03 (`riskRpn`, vorher inline 100) und R-04 (`moduleSize.{large,coupled,crossings}`, vorher inline 12/8/2 — drei Urteile, nicht eins). R-04 heisst jetzt 'Module size relative to crossing flows': die Regel waegt Groesse GEGEN Kreuzungen ab, der alte Name 'Max module size' verschwieg die zweite Haelfte der Bedingung. `RuleDefinition.evaluate` nimmt die Policy als zweiten Parameter, damit keine Familie einen Sonderweg braucht; einstellige Regelfunktionen bleiben zuweisbar. Bewertung unter DEFAULT_METRIC_POLICY unveraendert. Prior: CR-GC-315: R-12 + R-21 no longer tax FLOW reuse. R-12 restricted to dependency-carrying traces (compose/allocate/relation) — on `io` the only reachable shape was FUNC ─io→ FLOW ─io→ the SAME FUNC, i.e. read-modify-write on a shared FLOW, so the check was pure false-positive there (real io-cycles span ≥2 FUNCs and a 2-cycle test cannot see them); satisfy/verify/produces 2-cycles are pattern-illegal and belong to R-18. Its dedup key is now direction-independent (`type|sorted pair`) — keying on the message never collapsed anything, so every cycle was reported twice. R-21 no longer derives FUNC↔FUNC connections from FLOW co-adjacency alone: a producer/consumer pair with NO shared FCHAIN is silent. One hub FLOW with P producers and C consumers manufactured P·C findings, each demanding its own FCHAIN+integration test including pairs that never interact — reuse was penalised quadratically. The FCHAIN is the declared integration scope; the test is owed on that declared claim only. Rule intent (CR-GC-240 gap: unit/UC tests do not cover FUNC↔FUNC wiring) unchanged. Prior: CR-SM-227: +AF-01..05 Analysis-Freshness-Legs (analysis-freshness-rules.ts) — the third leg-kind a review gate checks (rule legs, layer-presence legs [R-28], now analysis-freshness legs, docs/articles/07-the-scoring-landscape.md); presence-only rules (Presence/Resolution-Split like R-19/R-20/R-26/R-27 vs. RC-01..05) checking a `graphVersion` freshness stamp exists under SYS.attributes.analysisFreshness.<AnalysisArtifactId> for each of the 5 CR-GC-221 judgment artifacts (conops/trade/assumption-review/fmea/implplan) — staleness (stamp vs live graphVersion) stays a consumer/I-O concern (`CreationCurrencyProvider`, @sigloch/graphcode-client), NOT evaluated here; Familie-Review 2026-08-05 gate assignment → RULE_TO_PHASE: ConOps/Trade/Assumption-Review → PDR, FMEA → CDR, Implementation Plan → TRR (SRR gets none); +AnalysisArtifactId enum + AnalysisFreshnessStampSchema (ontology.ts) as the SSOT graphcode-client's ARTIFACT_CATALOG analysis ids must match; grober Graph-Versions-Stempel only, no scope-hash in this first cut (conscious CR-SM-227 limitation).
|
|
8
|
+
export const RULES_VERSION = '6.0.0'; // BREAKING (CR-SM-247): R-28 (Ebenen-Praesenz) entfaellt ersatzlos. Sie schloss das Vacuous-Complete-Loch — mit 0/1 FUNC feuert keine Pro-Element-Regel und jede Vollstaendigkeitsfrage liest 'fertig' —, ist seit CR-GC-366 aber doppelt: die FLOW-Haelfte traegt R-31 (je FUNC statt einmal je Graph) plus IO-01, die SCHEMA-Haelfte SC-04 (je FLOW). Probe (SYS+UC+FCHAIN+3 FUNC, ohne FLOW/SCHEMA): 28 Befunde aus 18 Regeln, ohne R-28 bleiben 17 — das Loch bleibt zu. Gemessen feuerte sie 1x/0x an den Selbstmodellen, dort neben 14x IO-01 und 10x R-31. Dimension und Gate bleiben besetzt (R-30/R-31 sind ebenfalls arch/PDR). Bewusst in Kauf genommen: die SCHEMA-Absenz rutscht von PDR nach CDR, weil SC-04 erst feuert, wenn FLOWs existieren. Als domain:['graph']-Regel trug sie keinen Nenner bei — kein Dimensions-Score verschiebt sich. Prior: MINOR (CR-SM-250): IO-01 prueft ERREICHBARKEIT im io-Teilgraphen der Kette statt Adjazenz jedes Paares. Die Regel forderte einen vollstaendigen Graphen (n·(n−1)/2 Paarungen), obwohl eine Wirkkette eine Sequenz ist — der Zaehler wuchs quadratisch mit der Kettenlaenge, der Nenner-Beitrag linear, und wer sie befriedigen wollte, erfand einen fachlich falschen FLOW oder baute einen Ketten-Bus. Dieselbe Korrektur wie CR-GC-315 fuer R-21, eine Ebene hoeher. Zwei Praezisierungen aus dem Grammar-Review: ein Befund je abgehaengtem Glied statt je Paar (sonst waeren zwei Komponenten der Groesse n/2 wieder n²/4 Befunde, Zaehler > Nenner-Beitrag), und gar nicht verdrahtete Glieder gehoeren R-31 — am sigloch-modules-Selbstmodell waren 6 der 7 IO-01-Elemente zugleich R-31-Elemente, also Doppelzaehlung derselben Ursache. Lockerung, kein heute gruener Graph wird rot. Prior: BREAKING (CR-SM-249): R-15 wird VERSCHAERFT und heisst jetzt 'FCHAIN completeness' — eine Kette braucht Funktionen UNTER sich UND einen UC UEBER sich. Zweites Bein unter derselben rule_id nach dem R-10-Muster (bis zu zwei Meldungen je FCHAIN), bewusst keine neue Regel-ID: eine ID kostet dauerhaft einen Nenner-Anteil, eine Katalogzeile, eine readiness-Zuordnung und einen Golden-File-Eintrag — fuer einen einzigen Befund ueber beide Selbstmodelle (Gate 6 des Grammatik-Reviews). Major, weil ein heute gruener Graph rot werden kann: seit Ontologie 7.0.0 ist `UC -compose-> FCHAIN -compose-> FUNC` der EINZIGE Pfad vom Verhalten zum Zweck, und geprueft war nur sein unteres Bein. Migration: eine Kette ohne UC unterstellen oder loeschen (graphcode: `FCHAIN-doc-export`, 1 Befund). Zweitens R-30: die Grundgesamtheit sind ab jetzt AUSSCHLIESSLICH Blaetter (FUNC ohne FUNC-Kinder), und die Aufwaerts-Vererbung aus CR-GC-366 entfaellt ersatzlos. Ein zerlegter FUNC ist ein Rollup seiner Kinder und DARF gar kein Kettenglied sein — FC-03 ('FCHAIN is flat') verbietet es; die erste Fassung forderte einen Zustand, den eine andere Regel untersagt (am graphcode-Gate quittiert: graphVersion 111, zurueckgenommen in 112). Die Vererbung konnte nur greifen, wenn ein Block Kettenglied ist, und haette sonst ein unverbundenes Blatt stumm geschaltet. R-30 allein waere ein Minor (es meldet weniger: 56 -> 43 am graphcode-Selbstmodell, die 13 Differenz sind exakt die Bloecke). ACHTUNG, Gate-Luecke: `check-grammar-version.mjs` sieht diese Aenderung NICHT — der Snapshot erfasst id/severity/domain, und alle drei bleiben gleich. Eine semantische Verschaerfung ist mit einem Oberflaechen-Abzug nicht greifbar; der Bump hier ist von Hand. Prior: BREAKING (CR-SM-245): MS-01 verliert den Legacy-Fallback `MS -compose-> *` ersatzlos — Scope ist ausschliesslich `CR -relation-> MS`. Der Fallback war ein zweiter Weg zu derselben Aussage: ein Meilenstein galt als befuellt, ohne dass ein CR auf ihn zeigte, womit MS-03 und jede Scope-Auswertung ihn nie sahen. Verschaerfung, deshalb major — gemessen faellt an den Selbstmodellen aber kein einziger Meilenstein (alle 7 in graphcode haben CR-Relationen, vier zusaetzlich compose; MS-01 bleibt 0 Befunde). Zweitens: '+MS-' in SE_PREFIXES — MS-01/02/03 lagen in KEINEM Profil (weder se noch coding) und liefen nur im default-Lauf. Die Dimension `ms` haengt an genau vier Regeln (MS-01/02/03 + AF-05), und AF-05 traegt als `domain: ['graph']`-Regel keinen Nenner bei — im se-Profil war `ms` damit ein strukturell leerer Topic-Score und das SRR-Gate um drei Legs aermer. Zweiter Fall der 'RD-'-Klasse (CR-SM-221), gefunden vom Profil-Test aus CR-SM-244. Prior: BREAKING (CR-GC-366): +R-30 Wirkketten-Bindung, +R-31 io-Verdrahtung, R-02 zieltyp-scharf. Die vier Pflichten einer FUNC (ein REQ erfuellen, verdrahtet sein, in einer Wirkkette haengen, in einem MOD wohnen) waren nur zu zweit geprueft: R-02 und R-22. R-30 schliesst die Kette — R-15 verlangte, dass eine FCHAIN Funktionen hat, niemand verlangte, dass eine Funktion zu einer FCHAIN gehoert; daran haengen aber IO-01 und R-21, die beide nur INNERHALB einer Kette pruefen. R-31 schliesst die Verdrahtung: R-10 stellt dieselbe Frage vom FLOW aus und sieht eine FUNC ohne jede io-Kante deshalb nie (sie kommt in der FLOW-Schleife nicht vor); nur ACTOR darf terminieren. Ein Befund je FUNC statt je fehlender Seite, sonst uebersteigt der Zaehler den Nenner-Beitrag (die Fehlmessung aus CR-SM-242). BREAKING ist R-02: der Filter war zieltyp-blind, ein `FUNC -satisfy-> UC` schaltete ihn stumm, obwohl kein Requirement erfuellt war — am Selbstmodell 6 von 33 Faellen verdeckt (gemeldet: 27). Messung am graphcode-Selbstmodell (82 FUNC): R-30 60, R-31 54 (52 ohne Eingang, 50 ohne Ausgang, 48 ohne beides), R-02 27->33, R-22 3, R-18 26 (die Migrationsschuld aus dem gestrichenen Pattern). Prior: CR-SM-242: R-10 (`['FUNC']`→`['FLOW']`), SC-04 (`['SCHEMA']`→`['FLOW']`) und IO-01 (`['MOD']`→`['FUNC']`) deklarieren jetzt den Typ, an dem sie WIRKLICH melden. Alle drei iterierten ueber einen anderen Typ als den deklarierten: ihre Verstoesse erhoehten den Zaehler einer Grundgesamtheit, zu der sie nicht gehoerten. Gemessen am graphcode-Graphen (46 FLOW / 55 FUNC / 20 SCHEMA / 10 MOD) hob das `schema` von 0.713 auf 0.783 — SC-04 zog 46 FLOW-Verstoesse gegen 20 SCHEMA; bei IO-01 uebertraf der Zaehler (21) den eigenen Nenner-Beitrag (10) schon, nur der Clamp verhinderte einen negativen Score. Kein Evaluator, keine Regel-Semantik und kein Gate-Urteil geaendert — nur die deklarierte Grundgesamtheit, wie bei CR-SM-239. Neu erzwungen durch `se-rule-domain-invariant.test.ts`: der Typ jedes gemeldeten Elements liegt in der `domain` der Regel (stumm fuer 29 der 72 Regeln, die auf den Fixtures nicht feuern). CR-SM-243 (dieselbe Auslieferung): genau dieser Test hat vier weitere Faelle gefunden, die von Hand nie auffielen — R-03 (`['FUNC']`→`['MOD']`, meldet am Modul, das ASIL-D und QM mischt), FC-03 (`['FCHAIN']`→`['FUNC']`, meldet am FUNC mit der verschachtelten Zerlegung), NFR-01 (`['FUNC']`→`['MOD','FUNC','FCHAIN']`, Budgets liegen physisch am MOD und verhaltensseitig an FUNC/FCHAIN) und CR-R03 (`['CR']`→`['all']`, meldet am getrackten Zielknoten beliebigen Typs — nach R-08/R-18 die dritte und bewusst begruendete `all`-Regel). Prior: CR-SM-239: AF-01..05 und R-28 deklarieren `domain: ['graph']` statt `['SYS']` — sie pruefen EINMAL den Graphen, nicht die SYS-Elemente. Als Ein-Element-Gruppe waren sie ein Nenner-Beitrag mit fast sicherem Verstoss und zogen jede sonst leere Dimension auf 0 %: ein Graph ohne eine einzige REQ las `req` = 0 % (1 - 2/2 aus AF-01/AF-03) statt „nichts zu messen", und die Fokuswahl schickte den Executor dorthin. Kein Regelverhalten geaendert — nur die deklarierte Grundgesamtheit. Prior: CR-SM-231: +R-29 Testdatei-Exklusivitaet (severity **error**) — jede Testdatei erscheint in hoechstens einem `testRefs`. Das ist die Haelfte, die 1:n ERZWINGT statt es nur zu erlauben; ohne sie driftet das Attribut zurueck nach n:m. Bewusst schaerfer als R-19/R-20 (beide warning): eine doppelt beanspruchte Datei macht Gate-Zahlen nachweislich falsch, das ist eine Fehlmessung und kein Vollstaendigkeits-Signal. Rein graph-strukturell, kein I/O. R-19 und RC-02 lesen `testRefs`; RC-02 iteriert die Eintraege und nennt den konkreten Pfad statt nur der Knoten-ID. Prior: CR-SM-236: die letzten drei Urteilsschwellen sind Policy-Parameter statt Literal — CR-01 (`crossingFlows`, erstmals mit Aus-Zustand: `null` unterdrueckt auch die info je Modulpaar, die bisher die arch-Dimension verduennte), FM-03 (`riskRpn`, vorher inline 100) und R-04 (`moduleSize.{large,coupled,crossings}`, vorher inline 12/8/2 — drei Urteile, nicht eins). R-04 heisst jetzt 'Module size relative to crossing flows': die Regel waegt Groesse GEGEN Kreuzungen ab, der alte Name 'Max module size' verschwieg die zweite Haelfte der Bedingung. `RuleDefinition.evaluate` nimmt die Policy als zweiten Parameter, damit keine Familie einen Sonderweg braucht; einstellige Regelfunktionen bleiben zuweisbar. Bewertung unter DEFAULT_METRIC_POLICY unveraendert. Prior: CR-GC-315: R-12 + R-21 no longer tax FLOW reuse. R-12 restricted to dependency-carrying traces (compose/allocate/relation) — on `io` the only reachable shape was FUNC ─io→ FLOW ─io→ the SAME FUNC, i.e. read-modify-write on a shared FLOW, so the check was pure false-positive there (real io-cycles span ≥2 FUNCs and a 2-cycle test cannot see them); satisfy/verify/produces 2-cycles are pattern-illegal and belong to R-18. Its dedup key is now direction-independent (`type|sorted pair`) — keying on the message never collapsed anything, so every cycle was reported twice. R-21 no longer derives FUNC↔FUNC connections from FLOW co-adjacency alone: a producer/consumer pair with NO shared FCHAIN is silent. One hub FLOW with P producers and C consumers manufactured P·C findings, each demanding its own FCHAIN+integration test including pairs that never interact — reuse was penalised quadratically. The FCHAIN is the declared integration scope; the test is owed on that declared claim only. Rule intent (CR-GC-240 gap: unit/UC tests do not cover FUNC↔FUNC wiring) unchanged. Prior: CR-SM-227: +AF-01..05 Analysis-Freshness-Legs (analysis-freshness-rules.ts) — the third leg-kind a review gate checks (rule legs, layer-presence legs [R-28], now analysis-freshness legs, docs/articles/07-the-scoring-landscape.md); presence-only rules (Presence/Resolution-Split like R-19/R-20/R-26/R-27 vs. RC-01..05) checking a `graphVersion` freshness stamp exists under SYS.attributes.analysisFreshness.<AnalysisArtifactId> for each of the 5 CR-GC-221 judgment artifacts (conops/trade/assumption-review/fmea/implplan) — staleness (stamp vs live graphVersion) stays a consumer/I-O concern (`CreationCurrencyProvider`, @sigloch/graphcode-client), NOT evaluated here; Familie-Review 2026-08-05 gate assignment → RULE_TO_PHASE: ConOps/Trade/Assumption-Review → PDR, FMEA → CDR, Implementation Plan → TRR (SRR gets none); +AnalysisArtifactId enum + AnalysisFreshnessStampSchema (ontology.ts) as the SSOT graphcode-client's ARTIFACT_CATALOG analysis ids must match; grober Graph-Versions-Stempel only, no scope-hash in this first cut (conscious CR-SM-227 limitation).
|
|
9
9
|
/** Meta-model version (trace pattern constraints + format-e parser). */
|
|
10
|
-
export const META_MODEL_VERSION = '
|
|
10
|
+
export const META_MODEL_VERSION = '2.0.0'; // BREAKING, zwei Anlaesse in einer Zahl. (1) Nachtrag zu CR-GC-366: dort entfiel das Trace-Pattern `FUNC -satisfy-> UC`, gebumpt wurde aber nur ONTOLOGY_VERSION — nach der in CR-SM-244 festgeschriebenen Zustaendigkeit (meta-model.ts + format-e-Parser -> META_MODEL_VERSION) fehlte der Bump hier. Eine Entfernung ist major; dass kein Konsument semver-maessig auf diese Konstante verzweigt (schema-guard.ts keyt bewusst auf einen DDL-Fingerprint), macht sie nicht kleiner, nur folgenlos fuer die Bump-Kette. (2) CR-SM-251: `hydrateAttrValue` hydriert typ-geleitet ueber ELEMENT_ATTRIBUTES — boolean/number-Attribute kamen ueber BEIDE Format-E-Pfade als String an, womit die elf `=== true`-Vergleiche in den Regeln jede Ausnahme (concept/external/safety_relevant/spike) ins Leere laufen liessen und ein Export-Reimport bestehende Ausnahmen still entwertete. Verhaltensaenderung am Parser, also hier verbucht. Prior: -REQ→MOD allocate pattern removed (CR-228 A); +FUNC→FUNC compose (blackbox function decomposition)
|
|
11
11
|
export * from './ontology.js';
|
|
12
12
|
export * from './rules.js';
|
|
13
13
|
export * from './conformance-rules.js';
|
package/dist/se/ontology.d.ts
CHANGED
|
@@ -199,7 +199,7 @@ export type AnalysisArtifactId = z.infer<typeof AnalysisArtifactId>;
|
|
|
199
199
|
* artifact was written — I/O, so writing it stays a consumer (graphcode)
|
|
200
200
|
* concern; contracts only defines the shape.
|
|
201
201
|
* Stored under `OntologyElement.attributes.analysisFreshness.<AnalysisArtifactId>`
|
|
202
|
-
* on the SYS root element (
|
|
202
|
+
* on the SYS root element (the anchor convention of the graph-wide AF-01..05 rules).
|
|
203
203
|
*/
|
|
204
204
|
export declare const AnalysisFreshnessStampSchema: z.ZodObject<{
|
|
205
205
|
graphVersion: z.ZodNumber;
|
package/dist/se/ontology.js
CHANGED
|
@@ -147,7 +147,7 @@ export const AnalysisArtifactId = z.enum(['conops', 'fmea', 'trade', 'implplan',
|
|
|
147
147
|
* artifact was written — I/O, so writing it stays a consumer (graphcode)
|
|
148
148
|
* concern; contracts only defines the shape.
|
|
149
149
|
* Stored under `OntologyElement.attributes.analysisFreshness.<AnalysisArtifactId>`
|
|
150
|
-
* on the SYS root element (
|
|
150
|
+
* on the SYS root element (the anchor convention of the graph-wide AF-01..05 rules).
|
|
151
151
|
*/
|
|
152
152
|
export const AnalysisFreshnessStampSchema = z.object({
|
|
153
153
|
graphVersion: z.number().int().nonnegative(),
|
package/dist/se/readiness.js
CHANGED
|
@@ -58,11 +58,9 @@ export const RULE_TO_DIMENSION = {
|
|
|
58
58
|
// Evidenz-Bindung einer Abnahme, R-19 ihre Praesenz, R-29 ihre Eindeutigkeit.
|
|
59
59
|
'R-29': 'ver',
|
|
60
60
|
'R-22': 'alloc', 'R-23': 'alloc', 'R-26': 'schema', 'R-27': 'arch',
|
|
61
|
-
// CR-SM-226: R-28 Ebenen-Präsenz (>1 FUNC needs FLOW+SCHEMA) is an architecture rule.
|
|
62
|
-
'R-28': 'arch',
|
|
63
61
|
// CR-GC-366: R-30 (Wirkketten-Bindung) und R-31 (io-Verdrahtung) bewerten beide, ob ein
|
|
64
|
-
// Funktionsblock ueberhaupt im Bauplan haengt — dieselbe Dimension wie R-20 (realRef)
|
|
65
|
-
//
|
|
62
|
+
// Funktionsblock ueberhaupt im Bauplan haengt — dieselbe Dimension wie R-20 (realRef).
|
|
63
|
+
// NICHT 'uc': R-14..R-17 fragen, ob ein Behaelter gefuellt ist;
|
|
66
64
|
// diese beiden fragen von der FUNC aus, ob sie angeschlossen ist.
|
|
67
65
|
'R-30': 'arch', 'R-31': 'arch',
|
|
68
66
|
// uc
|
|
@@ -153,7 +151,6 @@ export const RULE_TO_PHASE = {
|
|
|
153
151
|
'R-04': 'PDR', 'R-22': 'PDR', 'R-23': 'PDR',
|
|
154
152
|
'R-15': 'PDR',
|
|
155
153
|
'FC-01': 'PDR', 'FC-03': 'PDR', 'FC-04': 'PDR',
|
|
156
|
-
'R-28': 'PDR', // Ebenen-Präsenz (CR-SM-226)
|
|
157
154
|
// CR-GC-366: Anschluss des Funktionsbaus — gehoert an dasselbe Gate wie R-15/IO-01,
|
|
158
155
|
// die auf derselben Kette aufsetzen.
|
|
159
156
|
'R-30': 'PDR', 'R-31': 'PDR',
|
package/dist/se/rules.d.ts
CHANGED
|
@@ -174,7 +174,7 @@ export interface RuleDefinition {
|
|
|
174
174
|
*
|
|
175
175
|
* **CR-SM-239 — „ganzer Graph" ist etwas anderes als „diese Gruppe".** `['graph']` markiert
|
|
176
176
|
* eine Regel, die **einmal je Graph** prueft statt ueber eine Elementmenge: die
|
|
177
|
-
* Analyse-Freshness-Stempel (AF-01..05)
|
|
177
|
+
* Analyse-Freshness-Stempel (AF-01..05). Sie tragen KEINE
|
|
178
178
|
* Grundgesamtheit bei, denn sie haben keine — der Stempel haengt zwar unter SYS, geprueft
|
|
179
179
|
* wird aber der Graph, nicht das Element.
|
|
180
180
|
*
|
package/dist/se/rules.js
CHANGED
|
@@ -510,25 +510,53 @@ function ucMustHaveCompose(graph) {
|
|
|
510
510
|
}));
|
|
511
511
|
}
|
|
512
512
|
// ---------------------------------------------------------------------------
|
|
513
|
-
// R-15: FCHAIN
|
|
513
|
+
// R-15: FCHAIN completeness — a chain needs functions BELOW it and a use case
|
|
514
|
+
// ABOVE it (CR-117, second leg CR-SM-249).
|
|
515
|
+
//
|
|
516
|
+
// Der tragende Pfad vom Verhalten zum Zweck ist `UC -compose-> FCHAIN
|
|
517
|
+
// -compose-> FUNC` (seit Ontologie 7.0.0 der einzige). Geprueft war nur das
|
|
518
|
+
// untere Bein: R-15 verlangte Funktionen unter der Kette, niemand verlangte
|
|
519
|
+
// einen UC ueber ihr. Eine Kette ohne UC beantwortet die Leitfrage "welche
|
|
520
|
+
// Wirkkette ist betroffen, wenn ich diese Funktion aendere?" auf halbem Weg.
|
|
521
|
+
// Belegt: `FCHAIN-doc-export` (graphcode) traegt zwei Funktionen und dient
|
|
522
|
+
// keinem UC.
|
|
523
|
+
//
|
|
524
|
+
// Dieselbe rule_id fuer beide Beine, nach dem Muster von R-10 (FLOW braucht
|
|
525
|
+
// Produzent UND Konsument): ein Belang, eine ID, bis zu zwei Meldungen je
|
|
526
|
+
// Element. Eine eigene ID kostete dauerhaft einen Nenner-Anteil, eine
|
|
527
|
+
// Katalogzeile, eine readiness-Zuordnung und einen Golden-File-Eintrag — fuer
|
|
528
|
+
// einen einzigen Befund ueber beide Selbstmodelle.
|
|
514
529
|
// ---------------------------------------------------------------------------
|
|
515
|
-
function
|
|
530
|
+
function fchainCompleteness(graph) {
|
|
516
531
|
const fchains = graph.elements.filter(e => e.type === 'FCHAIN');
|
|
517
532
|
const funcs = graph.elements.filter(e => e.type === 'FUNC');
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
533
|
+
const ucs = graph.elements.filter(e => e.type === 'UC');
|
|
534
|
+
const ucIds = new Set(ucs.map(u => u.id));
|
|
535
|
+
const violations = [];
|
|
536
|
+
for (const fc of fchains) {
|
|
537
|
+
const ctx = { element_type: fc.type, element_name: fc.name };
|
|
538
|
+
if (!graph.traces.some(t => t.source === fc.id && t.type === 'compose')) {
|
|
539
|
+
violations.push({
|
|
540
|
+
rule_id: 'R-15',
|
|
541
|
+
severity: 'warning',
|
|
542
|
+
element_id: fc.id,
|
|
543
|
+
message: `${fc.id} has no compose traces (no functions in chain)`,
|
|
544
|
+
fix_hint: 'Add FUNC elements via compose trace',
|
|
545
|
+
context: { ...ctx, candidate_targets: toCandidates(funcs) },
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
if (!graph.traces.some(t => t.target === fc.id && t.type === 'compose' && ucIds.has(t.source))) {
|
|
549
|
+
violations.push({
|
|
550
|
+
rule_id: 'R-15',
|
|
551
|
+
severity: 'warning',
|
|
552
|
+
element_id: fc.id,
|
|
553
|
+
message: `${fc.id} serves no use case (no UC -compose-> FCHAIN)`,
|
|
554
|
+
fix_hint: 'Attach the chain to the use case it realises (UC -compose-> FCHAIN)',
|
|
555
|
+
context: { ...ctx, candidate_targets: toCandidates(ucs, fc) },
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
return violations;
|
|
532
560
|
}
|
|
533
561
|
// ---------------------------------------------------------------------------
|
|
534
562
|
// R-16: ACTOR must have at least 1 io trace (CR-117)
|
|
@@ -571,18 +599,19 @@ function sysMustHaveCompose(graph) {
|
|
|
571
599
|
// ---------------------------------------------------------------------------
|
|
572
600
|
// MS-01: Milestone without scope — no CRs assigned via CR→MS [relation]
|
|
573
601
|
// (CR-207: scope = CRs pointing to this MS, not compose children)
|
|
602
|
+
//
|
|
603
|
+
// CR-SM-245: der Legacy-Fallback (`MS -compose-> *` zaehlt auch als Scope) ist ersatzlos weg.
|
|
604
|
+
// Er war ein zweiter Weg zu derselben Aussage neben der einzig tragenden `CR -relation-> MS` —
|
|
605
|
+
// und der billigere: ein Meilenstein galt als befuellt, ohne dass ein einziger CR auf ihn zeigte,
|
|
606
|
+
// womit MS-03 (CR ohne Milestone) und jede Scope-Auswertung ihn nie sahen. Gemessen am
|
|
607
|
+
// graphcode-Selbstmodell aendert die Entfernung nichts (alle 7 MS haben CR-Relationen, vier
|
|
608
|
+
// zusaetzlich compose) — genau deshalb ist jetzt der Zeitpunkt.
|
|
574
609
|
// ---------------------------------------------------------------------------
|
|
575
610
|
function msEmptyScope(graph) {
|
|
576
|
-
const
|
|
577
|
-
return
|
|
578
|
-
.filter(
|
|
579
|
-
|
|
580
|
-
const hasCr = graph.traces.some(t => t.type === 'relation' && t.target === ms.id &&
|
|
581
|
-
graph.elements.some(e => e.id === t.source && e.type === 'CR'));
|
|
582
|
-
// Fallback: legacy compose traces still count
|
|
583
|
-
const hasCompose = graph.traces.some(t => t.source === ms.id && t.type === 'compose');
|
|
584
|
-
return !hasCr && !hasCompose;
|
|
585
|
-
})
|
|
611
|
+
const crIds = new Set(graph.elements.filter(e => e.type === 'CR').map(e => e.id));
|
|
612
|
+
return graph.elements
|
|
613
|
+
.filter(e => e.type === 'MS')
|
|
614
|
+
.filter(ms => !graph.traces.some(t => t.type === 'relation' && t.target === ms.id && crIds.has(t.source)))
|
|
586
615
|
.map(ms => ({
|
|
587
616
|
rule_id: 'MS-01',
|
|
588
617
|
severity: 'warning',
|
|
@@ -989,92 +1018,61 @@ function physicalModMustHaveRealRef(graph) {
|
|
|
989
1018
|
}));
|
|
990
1019
|
}
|
|
991
1020
|
// ---------------------------------------------------------------------------
|
|
992
|
-
// R-28
|
|
993
|
-
//
|
|
994
|
-
//
|
|
995
|
-
//
|
|
996
|
-
//
|
|
997
|
-
//
|
|
998
|
-
//
|
|
999
|
-
//
|
|
1021
|
+
// R-28 (Ebenen-Präsenz) ist mit CR-SM-247 ersatzlos entfallen — nicht weil sie falsch war,
|
|
1022
|
+
// sondern weil sie seit CR-GC-366 doppelt ist. Sie schloss das Vacuous-Complete-Loch (mit 0/1
|
|
1023
|
+
// FUNC feuert keine Pro-Element-Regel, alles liest "fertig"); beide Hälften trägt heute je eine
|
|
1024
|
+
// präzisere Regel: kein FLOW → R-31 (io-Verdrahtung, je FUNC statt einmal je Graph) + IO-01,
|
|
1025
|
+
// kein SCHEMA → SC-04 (je FLOW). Probe (SYS+UC+FCHAIN+3 FUNC, ohne FLOW/SCHEMA): 28 Befunde aus
|
|
1026
|
+
// 18 Regeln, ohne R-28 bleiben 17 — das Loch bleibt zu. Gemessen feuerte sie 1× / 0× an den
|
|
1027
|
+
// Selbstmodellen, dort neben 14× IO-01 und 10× R-31. Bewusst in Kauf genommen: die
|
|
1028
|
+
// SCHEMA-Absenz rutscht von PDR nach CDR, weil SC-04 erst feuert, wenn FLOWs existieren.
|
|
1000
1029
|
// ---------------------------------------------------------------------------
|
|
1001
|
-
function ebenenPraesenz(graph) {
|
|
1002
|
-
const funcs = graph.elements.filter(e => e.type === 'FUNC');
|
|
1003
|
-
if (funcs.length <= 1)
|
|
1004
|
-
return [];
|
|
1005
|
-
const flows = graph.elements.filter(e => e.type === 'FLOW');
|
|
1006
|
-
const schemas = graph.elements.filter(e => e.type === 'SCHEMA');
|
|
1007
|
-
const missingLevels = [];
|
|
1008
|
-
if (flows.length === 0)
|
|
1009
|
-
missingLevels.push('FLOW');
|
|
1010
|
-
if (schemas.length === 0)
|
|
1011
|
-
missingLevels.push('SCHEMA');
|
|
1012
|
-
if (missingLevels.length === 0)
|
|
1013
|
-
return [];
|
|
1014
|
-
// Anchor the graph-wide violation on SYS (the root) if present, else the
|
|
1015
|
-
// first FUNC — there is no single "population" element this rule checks.
|
|
1016
|
-
const anchor = graph.elements.find(e => e.type === 'SYS') ?? funcs[0];
|
|
1017
|
-
return [{
|
|
1018
|
-
rule_id: 'R-28',
|
|
1019
|
-
severity: 'warning',
|
|
1020
|
-
element_id: anchor.id,
|
|
1021
|
-
message: `Graph has ${funcs.length} FUNCs but no ${missingLevels.join(' and no ')} element — architecture level(s) absent`,
|
|
1022
|
-
fix_hint: `Add at least one ${missingLevels.join(' and one ')} element to bind the functional and data-level architecture`,
|
|
1023
|
-
context: { element_type: anchor.type, element_name: anchor.name },
|
|
1024
|
-
}];
|
|
1025
|
-
}
|
|
1026
1030
|
// ---------------------------------------------------------------------------
|
|
1027
|
-
// R-30:
|
|
1028
|
-
//
|
|
1029
|
-
//
|
|
1030
|
-
//
|
|
1031
|
-
//
|
|
1032
|
-
//
|
|
1033
|
-
//
|
|
1034
|
-
//
|
|
1035
|
-
//
|
|
1031
|
+
// R-30: jedes BLATT gehoert in eine Wirkkette (CR-GC-366, Grundgesamtheit
|
|
1032
|
+
// korrigiert in CR-SM-249).
|
|
1033
|
+
//
|
|
1034
|
+
// Die Ontologie deckte nur die Gegenrichtung ab: R-15 verlangt, dass eine
|
|
1035
|
+
// FCHAIN Funktionen hat — niemand verlangte, dass eine Funktion zu einer
|
|
1036
|
+
// FCHAIN gehoert. Genau daran haengt aber die uebrige Absicherung: IO-01
|
|
1037
|
+
// bildet FLOW-Pfade nur zwischen FUNCs DERSELBEN Kette, R-21 fordert den
|
|
1038
|
+
// Integrationstest je Kette. Eine FUNC ausserhalb jeder Kette faellt durch
|
|
1039
|
+
// beide Netze, ohne dass etwas meldet.
|
|
1040
|
+
//
|
|
1041
|
+
// Grundgesamtheit sind AUSSCHLIESSLICH Blaetter (FUNC ohne FUNC-Kinder). Ein
|
|
1042
|
+
// zerlegter FUNC ist ein Rollup seiner Kinder und hat keinen eigenen
|
|
1043
|
+
// Bindungszustand — er *darf* auch keinen haben: FC-03 ("FCHAIN is flat")
|
|
1044
|
+
// verbietet ein Kettenglied mit verschachtelter Zerlegung. Die erste Fassung
|
|
1045
|
+
// forderte damit einen Zustand, den eine andere Regel untersagt; am
|
|
1046
|
+
// graphcode-Gate quittiert (graphVersion 111, zurueckgenommen in 112).
|
|
1047
|
+
//
|
|
1048
|
+
// Die Aufwaerts-Vererbung aus CR-GC-366 ("ein Vorfahre steht in einer Kette →
|
|
1049
|
+
// Kind frei") entfaellt ersatzlos: sie konnte nur greifen, wenn ein Block
|
|
1050
|
+
// Kettenglied ist, und haette sonst ein unverbundenes Blatt stumm geschaltet.
|
|
1051
|
+
// Die Anforderung liegt jetzt vollstaendig auf den Blaettern — der Block wird
|
|
1052
|
+
// gruen, ohne je eine eigene Kante zu tragen.
|
|
1053
|
+
//
|
|
1054
|
+
// Severity warning, nicht error: am Selbstmodell trifft es 43 von 69 Blaettern
|
|
1055
|
+
// — ein error wuerde ueber das Delta-Gate jede weitere Mutation blockieren und
|
|
1056
|
+
// waere Neubau-Zwang statt Steuerung.
|
|
1036
1057
|
// ---------------------------------------------------------------------------
|
|
1037
1058
|
function funcMustBeInEffectChain(graph) {
|
|
1038
1059
|
const funcs = graph.elements.filter(e => e.type === 'FUNC');
|
|
1039
1060
|
const fchains = graph.elements.filter(e => e.type === 'FCHAIN');
|
|
1040
1061
|
const funcIds = new Set(funcs.map(f => f.id));
|
|
1041
1062
|
const fchainIds = new Set(fchains.map(fc => fc.id));
|
|
1063
|
+
const decomposed = new Set(graph.traces
|
|
1064
|
+
.filter(t => t.type === 'compose' && funcIds.has(t.source) && funcIds.has(t.target))
|
|
1065
|
+
.map(t => t.source));
|
|
1042
1066
|
const inChain = new Set(graph.traces
|
|
1043
1067
|
.filter(t => t.type === 'compose' && fchainIds.has(t.source) && funcIds.has(t.target))
|
|
1044
1068
|
.map(t => t.target));
|
|
1045
|
-
// Zerlegung erbt Mitgliedschaft nach unten: steht der Elternteil in einer Kette,
|
|
1046
|
-
// sind seine Kinder dessen Realisierung eine Ebene tiefer (TRACE_PATTERNS
|
|
1047
|
-
// `FUNC -compose-> FUNC`) und damit mitgebunden. Ohne diesen Schritt meldete die
|
|
1048
|
-
// Regel jede Sub-Funktion eines Kettenmitglieds als Waise — ein Fehlbefund aus
|
|
1049
|
-
// der Ontologie selbst. Am Selbstmodell aendert der Schritt heute nichts
|
|
1050
|
-
// (Zerlegungsbaum und Ketten sind dort disjunkt), er verhindert den Fehlbefund
|
|
1051
|
-
// fuer jedes Modell, das beides kombiniert.
|
|
1052
|
-
const parentOf = new Map();
|
|
1053
|
-
for (const t of graph.traces) {
|
|
1054
|
-
if (t.type === 'compose' && funcIds.has(t.source) && funcIds.has(t.target)) {
|
|
1055
|
-
parentOf.set(t.target, t.source);
|
|
1056
|
-
}
|
|
1057
|
-
}
|
|
1058
|
-
const reachesChain = (start) => {
|
|
1059
|
-
const seen = new Set([start]);
|
|
1060
|
-
let cur = start;
|
|
1061
|
-
while (cur) {
|
|
1062
|
-
if (inChain.has(cur))
|
|
1063
|
-
return true;
|
|
1064
|
-
cur = parentOf.get(cur);
|
|
1065
|
-
if (!cur || seen.has(cur))
|
|
1066
|
-
return false;
|
|
1067
|
-
seen.add(cur);
|
|
1068
|
-
}
|
|
1069
|
-
return false;
|
|
1070
|
-
};
|
|
1071
1069
|
return funcs
|
|
1072
|
-
.filter(fn => !
|
|
1070
|
+
.filter(fn => !decomposed.has(fn.id) && !inChain.has(fn.id))
|
|
1073
1071
|
.map(fn => ({
|
|
1074
1072
|
rule_id: 'R-30',
|
|
1075
1073
|
severity: 'warning',
|
|
1076
1074
|
element_id: fn.id,
|
|
1077
|
-
message: `${fn.id} belongs to no function chain (no FCHAIN compose
|
|
1075
|
+
message: `${fn.id} belongs to no function chain (no FCHAIN -compose-> FUNC)`,
|
|
1078
1076
|
fix_hint: 'Add the FUNC to the FCHAIN of the use case it serves (FCHAIN -compose-> FUNC)',
|
|
1079
1077
|
context: {
|
|
1080
1078
|
element_type: fn.type,
|
|
@@ -1131,7 +1129,7 @@ export const V3_RULES = [
|
|
|
1131
1129
|
{ id: 'R-04', name: 'Module size relative to crossing flows', severity: 'warning', evaluate: maxModuleSize, domain: ['MOD'] },
|
|
1132
1130
|
{ id: 'R-05', name: 'TEST must verify REQ', severity: 'warning', evaluate: testMustVerifyReq, domain: ['TEST'] },
|
|
1133
1131
|
{ id: 'R-14', name: 'UC must have compose', severity: 'warning', evaluate: ucMustHaveCompose, domain: ['UC'] },
|
|
1134
|
-
{ id: 'R-15', name: 'FCHAIN
|
|
1132
|
+
{ id: 'R-15', name: 'FCHAIN completeness', severity: 'warning', evaluate: fchainCompleteness, domain: ['FCHAIN'] },
|
|
1135
1133
|
{ id: 'R-16', name: 'ACTOR must have io', severity: 'warning', evaluate: actorMustHaveTrace, domain: ['ACTOR'] },
|
|
1136
1134
|
{ id: 'R-17', name: 'SYS must have compose', severity: 'warning', evaluate: sysMustHaveCompose, domain: ['SYS'] },
|
|
1137
1135
|
{ id: 'R-08', name: 'Trace consistency', severity: 'error', evaluate: traceConsistency, domain: ['all'] },
|
|
@@ -1155,8 +1153,7 @@ export const V3_RULES = [
|
|
|
1155
1153
|
{ id: 'RD-04', name: 'Decomposition breadth', severity: 'warning', evaluate: decompositionBreadth, domain: ['FUNC', 'MOD', 'SYS'] },
|
|
1156
1154
|
{ id: 'MS-01', name: 'Milestone empty scope', severity: 'warning', evaluate: msEmptyScope, domain: ['MS'] },
|
|
1157
1155
|
{ id: 'MS-02', name: 'Milestone dangling dependency', severity: 'error', evaluate: msDanglingDependency, domain: ['MS'] },
|
|
1158
|
-
{ id: 'R-
|
|
1159
|
-
{ id: 'R-30', name: 'FUNC must belong to a function chain', severity: 'warning', evaluate: funcMustBeInEffectChain, domain: ['FUNC'] },
|
|
1156
|
+
{ id: 'R-30', name: 'FUNC leaf must belong to a function chain', severity: 'warning', evaluate: funcMustBeInEffectChain, domain: ['FUNC'] },
|
|
1160
1157
|
{ id: 'R-31', name: 'FUNC must be wired (io input + output)', severity: 'warning', evaluate: funcMustBeWired, domain: ['FUNC'] },
|
|
1161
1158
|
];
|
|
1162
1159
|
/** Run all rules against a graph. CR-SM-236: `policy` ist Pflicht — wie bei `evaluateAllRules`. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sigloch/contracts",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "6.0.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -24,11 +24,14 @@
|
|
|
24
24
|
},
|
|
25
25
|
"scripts": {
|
|
26
26
|
"build": "rm -rf dist && tsc",
|
|
27
|
+
"pretest": "node scripts/check-grammar-version.mjs",
|
|
27
28
|
"test": "vitest run",
|
|
28
|
-
"
|
|
29
|
+
"check:grammar": "node scripts/check-grammar-version.mjs",
|
|
30
|
+
"grammar:snapshot": "SE_WRITE_GRAMMAR_SNAPSHOT=1 vitest run tests/unit/se-grammar-invariant.test.ts",
|
|
31
|
+
"prepublishOnly": "npm run build && npm run test",
|
|
32
|
+
"grammar:measure": "SE_MEASURE=1 vitest run tests/unit/se-grammar-measure.test.ts"
|
|
29
33
|
},
|
|
30
34
|
"dependencies": {
|
|
31
|
-
"@sigloch/contracts": "^4.1.0",
|
|
32
35
|
"zod": "^4.3.6"
|
|
33
36
|
},
|
|
34
37
|
"license": "MIT",
|