@sigloch/contracts 6.3.0 → 9.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/se/conformance-rules.d.ts +28 -0
- package/dist/se/conformance-rules.js +40 -17
- package/dist/se/format-e-parser.d.ts +14 -2
- package/dist/se/format-e-parser.js +33 -9
- package/dist/se/grammar-snapshot.d.ts +6 -6
- package/dist/se/grammar-snapshot.js +9 -16
- package/dist/se/index.d.ts +3 -3
- package/dist/se/index.js +3 -3
- package/dist/se/meta-model.d.ts +52 -7
- package/dist/se/meta-model.js +108 -14
- package/dist/se/ontology.d.ts +32 -25
- package/dist/se/ontology.js +56 -11
- package/dist/se/quality-rules.js +19 -5
- package/dist/se/readiness.d.ts +4 -2
- package/dist/se/readiness.js +23 -1
- package/dist/se/rules.d.ts +0 -7
- package/dist/se/rules.js +60 -8
- package/dist/se/uc-quality-rules.js +31 -12
- package/package.json +1 -1
package/dist/se/rules.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { z } from 'zod/v4';
|
|
7
7
|
import { ElementType, TraceType, TestRefsSchema, RealRefSchema } from './ontology.js';
|
|
8
|
-
import { isValidTrace } from './meta-model.js';
|
|
8
|
+
import { isValidTrace, BOUNDED_PATTERNS, maxOccurs } from './meta-model.js';
|
|
9
9
|
import { indexOf } from './graph-index.js';
|
|
10
10
|
export const RuleSeverity = z.enum(['error', 'warning', 'info']);
|
|
11
11
|
/** Candidate target for resolving a violation (e.g. a REQ to satisfy, a TEST to link). */
|
|
@@ -333,8 +333,9 @@ function testMustVerifyReq(graph) {
|
|
|
333
333
|
// ---------------------------------------------------------------------------
|
|
334
334
|
function traceConsistency(graph) {
|
|
335
335
|
const idx = indexOf(graph);
|
|
336
|
+
// CR-SM-266 D5: kein `category !== 'audit'`-Filter mehr — das Attribut ist weg, und mit ihm
|
|
337
|
+
// die Moeglichkeit, eine Kante per Selbstauskunft aus der Referenzintegritaet zu nehmen.
|
|
336
338
|
return graph.traces
|
|
337
|
-
.filter(t => t.category !== 'audit')
|
|
338
339
|
.filter(t => {
|
|
339
340
|
const sourceExists = idx.byId.has(t.source);
|
|
340
341
|
const targetExists = idx.byId.has(t.target);
|
|
@@ -361,7 +362,9 @@ function flowCompleteness(graph) {
|
|
|
361
362
|
const idx = indexOf(graph);
|
|
362
363
|
const flows = idx.elementsOfType('FLOW');
|
|
363
364
|
// Producers/consumers of a FLOW are FUNCs or ACTORs (io endpoints on the
|
|
364
|
-
// upstream/downstream side)
|
|
365
|
+
// upstream/downstream side). CR-SM-266 D4: UC ist KEIN Konsument mehr — das Pattern
|
|
366
|
+
// `FLOW -io-> UC` ist entfallen, ein FLOW erreicht einen UC nur noch ueber ein Kettenglied.
|
|
367
|
+
// Der fix_hint hat den UC vorher noch angeboten und haette in eine R-18-Ablehnung gefuehrt.
|
|
365
368
|
const sources = graph.elements.filter(e => e.type === 'FUNC' || e.type === 'ACTOR'); // Mehrtyp: ein Durchlauf ist hier billiger als vier Index-Listen zu mischen (Reihenfolge!)
|
|
366
369
|
const io = idx.tracesOfType('io');
|
|
367
370
|
const violations = [];
|
|
@@ -383,7 +386,7 @@ function flowCompleteness(graph) {
|
|
|
383
386
|
severity: 'warning',
|
|
384
387
|
element_id: f.id,
|
|
385
388
|
message: `${f.id} has no consumer (outgoing io)`,
|
|
386
|
-
fix_hint: 'Link a FUNC
|
|
389
|
+
fix_hint: 'Link a FUNC or ACTOR as the target via io trace',
|
|
387
390
|
context: ctx,
|
|
388
391
|
});
|
|
389
392
|
}
|
|
@@ -721,23 +724,72 @@ function validTracePattern(graph) {
|
|
|
721
724
|
const idx = indexOf(graph);
|
|
722
725
|
// CR-SM-264: die Typ-Map kommt aus dem Index statt je Aufruf neu gebaut zu werden.
|
|
723
726
|
const typeById = { get: (id) => idx.typeOf(id) };
|
|
727
|
+
// CR-SM-266 D5: der `category !== 'audit'`-Filter ist WEG. Er war die Ausnahme fuer das
|
|
728
|
+
// einzige audit-Pattern (`SESSION -produces-> *`); mit dessen Entfernung waere er zum
|
|
729
|
+
// Schlupfloch geworden — eine Kante mit `category: 'audit'` haette die Matrix komplett
|
|
730
|
+
// umgangen. Das Attribut existiert nicht mehr (ontology.ts), hier faellt der Leser mit.
|
|
724
731
|
return graph.traces
|
|
725
|
-
.filter(t => t.category !== 'audit')
|
|
726
732
|
.filter(t => {
|
|
727
733
|
const src = typeById.get(t.source);
|
|
728
734
|
const tgt = typeById.get(t.target);
|
|
729
735
|
if (!src || !tgt)
|
|
730
736
|
return false; // dangling endpoint → R-08, not R-18
|
|
731
|
-
|
|
737
|
+
// CR-SM-266 B: die where-Patterns lesen die Kinds des betroffenen Endes mit.
|
|
738
|
+
return !isValidTrace({
|
|
739
|
+
source: src, target: tgt, type: t.type, label: t.label,
|
|
740
|
+
sourceKinds: idx.byId.get(t.source)?.kinds,
|
|
741
|
+
targetKinds: idx.byId.get(t.target)?.kinds,
|
|
742
|
+
});
|
|
732
743
|
})
|
|
733
|
-
.map(t => ({
|
|
744
|
+
.map((t) => ({
|
|
734
745
|
rule_id: 'R-18',
|
|
735
746
|
severity: 'error',
|
|
736
747
|
element_id: t.source,
|
|
737
748
|
message: `Invalid trace ${t.source} -${t.type}-> ${t.target}: ` +
|
|
738
749
|
`${typeById.get(t.source)} → ${typeById.get(t.target)} is not a valid ${t.type} pattern`,
|
|
739
750
|
fix_hint: 'Use a trace type whose TRACE_PATTERNS allows this source/target element-type pair',
|
|
740
|
-
}))
|
|
751
|
+
}))
|
|
752
|
+
.concat(cardinalityViolations(graph));
|
|
753
|
+
}
|
|
754
|
+
/**
|
|
755
|
+
* CR-SM-266b — das ZWEITE BEIN von R-18: die Kardinalitaets-Obergrenzen.
|
|
756
|
+
*
|
|
757
|
+
* Bewusst dieselbe `rule_id` und keine eigene, nach dem Muster von R-10 (Produzent/Konsument)
|
|
758
|
+
* und R-15 seit CR-SM-249: die Aussage ist dieselbe — "diese Kante ist nach dem Meta-Modell
|
|
759
|
+
* nicht zulaessig" —, nur ist die Bedingung hier eine ueber die MENGE statt ueber das Paar.
|
|
760
|
+
* Eine eigene ID kostete dauerhaft einen Nenner-Anteil, eine Katalogzeile, eine
|
|
761
|
+
* readiness-Zuordnung und einen Golden-File-Eintrag (Gate 6), und ein Leser muesste sie von
|
|
762
|
+
* R-18 unterscheiden, obwohl der naechste Schritt derselbe ist: Kante weg.
|
|
763
|
+
*
|
|
764
|
+
* EIN Befund je Quellknoten, nicht je ueberzaehliger Kante. Bei n Allokationen sind nicht
|
|
765
|
+
* n-1 Kanten "falsch" — es ist EINE Entscheidung offen (welches Modul), und n-1 Befunde
|
|
766
|
+
* wuerden den Zaehler ueber den Nenner-Beitrag treiben, die Fehlmessung aus CR-SM-242.
|
|
767
|
+
*/
|
|
768
|
+
function cardinalityViolations(graph) {
|
|
769
|
+
const idx = indexOf(graph);
|
|
770
|
+
const violations = [];
|
|
771
|
+
for (const p of BOUNDED_PATTERNS) {
|
|
772
|
+
const max = maxOccurs(p.cardinality);
|
|
773
|
+
for (const el of idx.elementsOfType(p.source)) {
|
|
774
|
+
const matching = idx.out(el.id, p.type).filter(t => idx.typeOf(t.target) === p.target);
|
|
775
|
+
if (matching.length <= max)
|
|
776
|
+
continue;
|
|
777
|
+
violations.push({
|
|
778
|
+
rule_id: 'R-18',
|
|
779
|
+
severity: 'error',
|
|
780
|
+
element_id: el.id,
|
|
781
|
+
message: `${el.id} has ${matching.length} ${p.type} traces to ${p.target} — ` +
|
|
782
|
+
`the meta-model allows at most ${max} (${matching.map(t => t.target).join(', ')})`,
|
|
783
|
+
fix_hint: `Keep exactly one ${p.type} trace from ${el.id} to a ${p.target} and delete the others`,
|
|
784
|
+
context: {
|
|
785
|
+
element_type: el.type,
|
|
786
|
+
element_name: el.name,
|
|
787
|
+
existing_traces: matching.map(t => ({ source: t.source, target: t.target, type: t.type })),
|
|
788
|
+
},
|
|
789
|
+
});
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
return violations;
|
|
741
793
|
}
|
|
742
794
|
// ---------------------------------------------------------------------------
|
|
743
795
|
// R-19: Runnable TEST binding (CR-GC-205 Item 4) — a TEST that is not explicitly
|
|
@@ -32,26 +32,45 @@ export function uc02HasActor(graph) {
|
|
|
32
32
|
// ueber Elemente und Traces), dann je UC nur noch Mitgliedschaft pruefen. Dieselbe Aussage,
|
|
33
33
|
// dieselben Befunde in derselben Reihenfolge — die Iteration ueber `graph.elements` bleibt
|
|
34
34
|
// die aeussere Schleife.
|
|
35
|
+
// CR-SM-266 D1/D4: die Regel prueft ERREICHBARKEIT statt Adjazenz.
|
|
36
|
+
//
|
|
37
|
+
// Bis hierher suchte sie einen io-Ziel-Treffer direkt am UC oder an dessen compose-Kind —
|
|
38
|
+
// also genau die beiden Kanten `ACTOR -io-> UC` und (ueber die FCHAIN) den Direktweg. Beide
|
|
39
|
+
// Patterns sind mit D1/D4 entfallen; die Regel waere damit UNERFUELLBAR geworden: sie haette
|
|
40
|
+
// einen Zustand verlangt, den R-18 im selben Lauf als ungueltige Kante ablehnt — der
|
|
41
|
+
// gegenlaeufige Fall aus Gate 4 des Grammatik-Reviews, und fuer den Nutzer eine Sackgasse
|
|
42
|
+
// statt eines Fehlers.
|
|
43
|
+
//
|
|
44
|
+
// Der tragende Pfad ist jetzt der einzige: `ACTOR -io-> FLOW -io-> FUNC`, FUNC in einer
|
|
45
|
+
// FCHAIN des UC. Zwei Sprungweiten (Actor→FLOW, FLOW→FUNC), danach Mengen-Mitgliedschaft —
|
|
46
|
+
// die CR-SM-261-Optimierung bleibt damit erhalten (kein Scan je UC, alles ueber den Index
|
|
47
|
+
// aus CR-SM-264), und die Iteration ueber `elementsOfType('UC')` bleibt die aeussere
|
|
48
|
+
// Schleife, also ist die Befundreihenfolge weiter die Graph-Reihenfolge (Gate 5).
|
|
35
49
|
const idx = indexOf(graph);
|
|
36
50
|
const actorIds = idx.idsOfType('ACTOR');
|
|
37
|
-
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
51
|
+
// Schritt 1: die FLOWs, die ein ACTOR speist.
|
|
52
|
+
const actorFlows = new Set(idx.tracesOfType('io').filter(t => actorIds.has(t.source)).map(t => t.target));
|
|
53
|
+
// Schritt 2: die FUNCs, die an einem solchen FLOW haengen — in BEIDE Richtungen. Ein UC ist
|
|
54
|
+
// auch dann an einen Actor angebunden, wenn die Kette IHM etwas liefert (FUNC -io-> FLOW
|
|
55
|
+
// -io-> ACTOR), nicht nur wenn sie von ihm getriggert wird. Die alte Fassung sah nur die
|
|
56
|
+
// Trigger-Richtung, weil `ACTOR -io-> UC` nur so herum existierte.
|
|
57
|
+
const actorFacingFuncs = new Set();
|
|
58
|
+
for (const t of idx.tracesOfType('io')) {
|
|
59
|
+
if (actorFlows.has(t.source))
|
|
60
|
+
actorFacingFuncs.add(t.target);
|
|
61
|
+
if (actorIds.has(t.target) && idx.typeOf(t.source) === 'FLOW') {
|
|
62
|
+
for (const producer of idx.in(t.source, 'io'))
|
|
63
|
+
actorFacingFuncs.add(producer.source);
|
|
64
|
+
}
|
|
45
65
|
}
|
|
46
66
|
return idx.elementsOfType('UC')
|
|
47
|
-
.filter(uc => !
|
|
48
|
-
!(composeTargets.get(uc.id) ?? []).some(target => actorIoTargets.has(target)))
|
|
67
|
+
.filter(uc => !idx.out(uc.id, 'compose').some(chain => idx.out(chain.target, 'compose').some(member => actorFacingFuncs.has(member.target))))
|
|
49
68
|
.map(uc => ({
|
|
50
69
|
rule_id: 'UC-02',
|
|
51
70
|
severity: 'error',
|
|
52
71
|
element_id: uc.id,
|
|
53
|
-
message: `${uc.id}
|
|
54
|
-
fix_hint: '
|
|
72
|
+
message: `${uc.id} is not reachable from any ACTOR`,
|
|
73
|
+
fix_hint: 'Wire an ACTOR to a FLOW that feeds (or is fed by) a FUNC in one of this UC\'s function chains',
|
|
55
74
|
context: { element_type: uc.type, element_name: uc.name },
|
|
56
75
|
}));
|
|
57
76
|
}
|