@sigloch/contracts 10.13.0 → 11.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 +49 -15
- package/dist/se/analysis-freshness-rules.js +109 -34
- package/dist/se/ao-rules.js +3 -3
- package/dist/se/conformance-rules.d.ts +4 -0
- package/dist/se/conformance-rules.js +37 -32
- package/dist/se/cr-quality-rules.js +4 -76
- package/dist/se/evaluate-all.d.ts +5 -0
- package/dist/se/evaluate-all.js +2 -1
- package/dist/se/fchain-quality-rules.js +4 -4
- package/dist/se/fmea-rules.js +4 -4
- package/dist/se/format-e-parser.js +1 -1
- package/dist/se/grammar-snapshot.d.ts +6 -6
- package/dist/se/grammar-snapshot.js +91 -90
- package/dist/se/index.d.ts +3 -2
- package/dist/se/index.js +4 -2
- package/dist/se/metric-rules.d.ts +3 -0
- package/dist/se/metric-rules.js +3 -3
- package/dist/se/near-duplicate-rules.d.ts +2 -0
- package/dist/se/near-duplicate-rules.js +2 -2
- package/dist/se/ontology.d.ts +23 -4
- package/dist/se/ontology.js +37 -14
- package/dist/se/placeholder.d.ts +1 -0
- package/dist/se/placeholder.js +13 -0
- package/dist/se/quality-rules.js +8 -8
- package/dist/se/readiness.d.ts +45 -47
- package/dist/se/readiness.js +57 -120
- package/dist/se/rule-due.d.ts +35 -0
- package/dist/se/rule-due.js +83 -0
- package/dist/se/rule-help.d.ts +19 -3
- package/dist/se/rule-help.js +97 -26
- package/dist/se/rules.d.ts +18 -0
- package/dist/se/rules.js +119 -73
- package/dist/se/schema-quality-rules.d.ts +1 -1
- package/dist/se/schema-quality-rules.js +1 -1
- package/dist/se/uc-quality-rules.js +6 -6
- package/dist/se/view-rules.js +6 -4
- package/package.json +1 -1
package/dist/se/rules.js
CHANGED
|
@@ -7,6 +7,7 @@ import { z } from 'zod/v4';
|
|
|
7
7
|
import { ElementType, TraceType, readTestRefs, readRealRef } from './ontology.js';
|
|
8
8
|
import { traceRejection, admittingAlternatives, isValidTrace, BOUNDED_PATTERNS, REQUIRED_PATTERNS, maxOccurs } from './meta-model.js';
|
|
9
9
|
import { indexOf } from './graph-index.js';
|
|
10
|
+
import { whenDue } from './rule-due.js';
|
|
10
11
|
import { moduleCrossings, subtreeFuncs } from './module-crossings.js';
|
|
11
12
|
import { functionCriticality, chainsByFunc } from './function-criticality.js';
|
|
12
13
|
export const RuleSeverity = z.enum(['error', 'warning', 'info']);
|
|
@@ -200,6 +201,10 @@ elements, ref) {
|
|
|
200
201
|
}
|
|
201
202
|
// ---------------------------------------------------------------------------
|
|
202
203
|
// R-01: Every REQ must have at least one verify trace
|
|
204
|
+
//
|
|
205
|
+
// CR-SM-393: wieder genau das. CR-SM-391 hatte einen zweiten Befund angehaengt (Nicht-Konzept-REQ
|
|
206
|
+
// braucht einen Nicht-Konzept-TEST); mit dem Attribut `concept` ist sein Gegenstand entfallen.
|
|
207
|
+
// Ob der verifizierende TEST an eine Datei gebunden ist, fragt R-19 — ab Beginn der Realisierung.
|
|
203
208
|
// ---------------------------------------------------------------------------
|
|
204
209
|
function reqMustHaveVerification(graph) {
|
|
205
210
|
const idx = indexOf(graph);
|
|
@@ -746,6 +751,12 @@ function decompositionBreadth(graph, policy) {
|
|
|
746
751
|
// deshalb keine Zahl im Kontext und steuert nicht — sie ist Lesbarkeit, nicht Randbreite.
|
|
747
752
|
// Eine Ebene ohne jedes Kind ist nicht ihr Fall: ein leeres MOD meldet R-23, eine FUNC ohne Kinder
|
|
748
753
|
// ist ein Blatt.
|
|
754
|
+
//
|
|
755
|
+
// CR-SM-388 (ITEM-2026-731): die WURZEL ist ausgenommen — die Ebene direkt unter SYS, also beide
|
|
756
|
+
// Zaehlungen mit SYS als Eltern (`SYS compose MOD` und der FUNC-Wurzelwald). Der Rat „in die Ebene
|
|
757
|
+
// darueber aufloesen" ist dort nicht ausfuehrbar, ueber SYS liegt keine; und ein System aus einem
|
|
758
|
+
// Modul oder einer Wurzelfunktion ist eine Architekturentscheidung, keine leere Abstraktion.
|
|
759
|
+
// RD-04 (Obergrenze) gilt an der Wurzel weiter.
|
|
749
760
|
// ---------------------------------------------------------------------------
|
|
750
761
|
function decompositionNarrow(graph, policy) {
|
|
751
762
|
const band = policy.decompositionBreadth;
|
|
@@ -753,7 +764,7 @@ function decompositionNarrow(graph, policy) {
|
|
|
753
764
|
return [];
|
|
754
765
|
const byId = indexOf(graph).byId;
|
|
755
766
|
return breadthCounts(graph)
|
|
756
|
-
.filter(c => c.n < band.min)
|
|
767
|
+
.filter(c => c.n < band.min && byId.get(c.parentId)?.type !== 'SYS')
|
|
757
768
|
.map(c => {
|
|
758
769
|
const parent = byId.get(c.parentId);
|
|
759
770
|
return {
|
|
@@ -839,19 +850,24 @@ function actorMustHaveTrace(graph) {
|
|
|
839
850
|
}));
|
|
840
851
|
}
|
|
841
852
|
// ---------------------------------------------------------------------------
|
|
842
|
-
// R-17: SYS must have at least
|
|
853
|
+
// R-17: SYS must have at least one use case (CR-117; neu gefasst CR-SM-396).
|
|
854
|
+
//
|
|
855
|
+
// Die Existenz-Regel der Stufe 2. Bis CR-SM-396 schwieg sie, sobald das SYS IRGENDETWAS komponierte
|
|
856
|
+
// (UC, REQ oder MOD) — ein Code-Import (SYS -compose-> MOD, kein UC) las damit SRR als erreicht,
|
|
857
|
+
// „0 von 0" an der Stufe Anwendungsfall. Verlangt ist der Anwendungsfall selbst: `SYS -compose-> UC`
|
|
858
|
+
// (im Meta-Modell `1..*`). Ein UC, der nicht am System haengt, genuegt nicht.
|
|
843
859
|
// ---------------------------------------------------------------------------
|
|
844
|
-
function
|
|
860
|
+
function sysMustHaveUseCase(graph) {
|
|
845
861
|
const idx = indexOf(graph);
|
|
846
862
|
const systems = idx.elementsOfType('SYS');
|
|
847
863
|
return systems
|
|
848
|
-
.filter(sys => idx.out(sys.id, 'compose').
|
|
864
|
+
.filter(sys => !idx.out(sys.id, 'compose').some(t => idx.typeOf(t.target) === 'UC'))
|
|
849
865
|
.map(sys => ({
|
|
850
866
|
rule_id: 'R-17',
|
|
851
867
|
severity: 'warning',
|
|
852
868
|
element_id: sys.id,
|
|
853
|
-
message: `${sys.id} has no
|
|
854
|
-
fix_hint:
|
|
869
|
+
message: `${sys.id} has no use case (no SYS -compose-> UC)`,
|
|
870
|
+
fix_hint: `Add at least one UC and a compose trace from ${sys.id} to it — a use case says who uses the system for what`,
|
|
855
871
|
context: {
|
|
856
872
|
element_type: sys.type,
|
|
857
873
|
element_name: sys.name,
|
|
@@ -1126,19 +1142,18 @@ function cardinalityViolations(graph) {
|
|
|
1126
1142
|
return violations;
|
|
1127
1143
|
}
|
|
1128
1144
|
// ---------------------------------------------------------------------------
|
|
1129
|
-
// R-19: Runnable TEST binding (CR-GC-205 Item 4) — a TEST
|
|
1130
|
-
//
|
|
1131
|
-
//
|
|
1132
|
-
//
|
|
1133
|
-
//
|
|
1134
|
-
//
|
|
1135
|
-
//
|
|
1136
|
-
//
|
|
1145
|
+
// R-19: Runnable TEST binding (CR-GC-205 Item 4) — a TEST should carry a valid testRefs
|
|
1146
|
+
// runnable binding so the deduced selective test set is trustworthy. WARNING (a completeness
|
|
1147
|
+
// signal like R-05, not a hard gate). File EXISTENCE is out of scope here (rules are pure, no
|
|
1148
|
+
// I/O — that is RC-02); this guards presence/shape only.
|
|
1149
|
+
//
|
|
1150
|
+
// CR-SM-393: keine Ausnahme je Element mehr (`concept` ist entfallen). „Noch nicht faellig" ist
|
|
1151
|
+
// eine Eigenschaft des Graphen: die Regel ist Stufe 11 und erst faellig, wenn der Bau eroeffnet
|
|
1152
|
+
// ist (`isDue`, CR-SM-395).
|
|
1137
1153
|
// ---------------------------------------------------------------------------
|
|
1138
1154
|
function testMustHaveRunnableBinding(graph) {
|
|
1139
1155
|
const idx = indexOf(graph);
|
|
1140
1156
|
return idx.elementsOfType('TEST')
|
|
1141
|
-
.filter(e => e.attributes?.concept !== true) // concept-only TESTs are exempt
|
|
1142
1157
|
// CR-SM-231: `testRefs` (Array, min 1) statt `testRef` (Objekt). Semantik unveraendert —
|
|
1143
1158
|
// Praesenz und Form, keine Datei-Existenz; die pruefst RC-02.
|
|
1144
1159
|
.filter(e => readTestRefs(e.attributes).state !== 'bound')
|
|
@@ -1147,7 +1162,7 @@ function testMustHaveRunnableBinding(graph) {
|
|
|
1147
1162
|
severity: 'warning',
|
|
1148
1163
|
element_id: test.id,
|
|
1149
1164
|
message: `${test.id} is a runnable TEST without a valid testRefs binding`,
|
|
1150
|
-
fix_hint: 'Add attributes.testRefs [{file, case?, tool, level?}, …] with at least one entry
|
|
1165
|
+
fix_hint: 'Add attributes.testRefs [{file, case?, tool, level?}, …] with at least one entry',
|
|
1151
1166
|
context: { element_type: test.type, element_name: test.name },
|
|
1152
1167
|
}));
|
|
1153
1168
|
}
|
|
@@ -1206,8 +1221,8 @@ function testFileExclusivity(graph) {
|
|
|
1206
1221
|
}
|
|
1207
1222
|
// ---------------------------------------------------------------------------
|
|
1208
1223
|
// R-20: FUNC realRef binding (CR-GC-205 Item 5, extended by CR-210, unified CR-228)
|
|
1209
|
-
// — a FUNC that is not
|
|
1210
|
-
//
|
|
1224
|
+
// — a FUNC that is not externally realized (attributes.external === true) counts as
|
|
1225
|
+
// BOUND when it EITHER
|
|
1211
1226
|
// carries a valid realRef {file, symbol?} OR is a blackbox parent whose compose→FUNC
|
|
1212
1227
|
// children are ALL (recursively) bound — the parent is realized by its children
|
|
1213
1228
|
// (e.g. FUNC-gesture-capture, gve CR-GVE-150). A partially-bound parent fires
|
|
@@ -1224,7 +1239,7 @@ function funcMustHaveCodeBinding(graph) {
|
|
|
1224
1239
|
.filter(t => t.source === id && t.type === 'compose' && funcById.has(t.target))
|
|
1225
1240
|
.map(t => t.target);
|
|
1226
1241
|
const hasCodeRef = (el) => readRealRef(el.attributes).state === 'bound';
|
|
1227
|
-
const isExempt = (el) => el.attributes?.
|
|
1242
|
+
const isExempt = (el) => el.attributes?.external === true;
|
|
1228
1243
|
// A FUNC is bound iff it is exempt, has a codeRef, or is a parent whose
|
|
1229
1244
|
// compose→FUNC children are all bound (recursive, cycle-safe).
|
|
1230
1245
|
const isBound = (id, stack) => {
|
|
@@ -1288,7 +1303,7 @@ function funcMustHaveCodeBinding(graph) {
|
|
|
1288
1303
|
severity: 'warning',
|
|
1289
1304
|
element_id: fn.id,
|
|
1290
1305
|
message: `${fn.id} is a FUNC without a valid realRef binding`,
|
|
1291
|
-
fix_hint: 'Add attributes.realRef {file, symbol, lang?}, or set attributes.
|
|
1306
|
+
fix_hint: 'Add attributes.realRef {file, symbol, lang?}, or set attributes.external:true if it is realized in another package',
|
|
1292
1307
|
context: { element_type: fn.type, element_name: fn.name },
|
|
1293
1308
|
});
|
|
1294
1309
|
}
|
|
@@ -1490,7 +1505,7 @@ function modMustHaveAllocatedFunc(graph) {
|
|
|
1490
1505
|
// REQ→MOD allocate edge as an invalid trace pattern.
|
|
1491
1506
|
// ---------------------------------------------------------------------------
|
|
1492
1507
|
// R-26: SCHEMA realRef presence (CR-211, unified CR-228) — a SCHEMA that is not
|
|
1493
|
-
//
|
|
1508
|
+
// external should carry a valid realRef {file, symbol?}
|
|
1494
1509
|
// so its Zod definition is machine-resolvable (RC-03 then checks it resolves, RC-04
|
|
1495
1510
|
// that it is parsed at the interface). WARNING, not error: currently-unbound SCHEMAs
|
|
1496
1511
|
// on the reference model must not turn readiness red before any binding exists — the
|
|
@@ -1500,54 +1515,45 @@ function modMustHaveAllocatedFunc(graph) {
|
|
|
1500
1515
|
function schemaMustHaveSchemaRef(graph) {
|
|
1501
1516
|
const idx = indexOf(graph);
|
|
1502
1517
|
return idx.elementsOfType('SCHEMA')
|
|
1503
|
-
.filter(e => e.attributes?.
|
|
1518
|
+
.filter(e => e.attributes?.external !== true)
|
|
1504
1519
|
.filter(e => readRealRef(e.attributes).state !== 'bound')
|
|
1505
1520
|
.map(sc => ({
|
|
1506
1521
|
rule_id: 'R-26',
|
|
1507
1522
|
severity: 'warning',
|
|
1508
1523
|
element_id: sc.id,
|
|
1509
1524
|
message: `${sc.id} is a SCHEMA without a valid realRef binding`,
|
|
1510
|
-
fix_hint: 'Add attributes.realRef {file, symbol, lang?}, or set attributes.
|
|
1525
|
+
fix_hint: 'Add attributes.realRef {file, symbol, lang?}, or set attributes.external:true if it is a foreign-API contract',
|
|
1511
1526
|
context: { element_type: sc.type, element_name: sc.name },
|
|
1512
1527
|
}));
|
|
1513
1528
|
}
|
|
1514
1529
|
// ---------------------------------------------------------------------------
|
|
1515
|
-
// R-32: Every
|
|
1516
|
-
// A SCHEMA
|
|
1517
|
-
//
|
|
1518
|
-
//
|
|
1519
|
-
//
|
|
1520
|
-
//
|
|
1521
|
-
//
|
|
1530
|
+
// R-32: Every SCHEMA must have a contract TEST (CR-SM-319, ITEM-2026-064; alle Schemas CR-SM-396).
|
|
1531
|
+
// A SCHEMA is a contract. RC-04 checks that it is PARSED at its boundary; this rule checks that it
|
|
1532
|
+
// is TESTED as a contract — every variant incl. rejection, independent of any scenario — via
|
|
1533
|
+
// `TEST -verify-> SCHEMA` (CR-SM-317). Chain tests (R-21) cover meaning end-to-end; they do not
|
|
1534
|
+
// cover the contract's variants. Owner's decision 2026-09-11: no exception, no KPI — a rule.
|
|
1535
|
+
//
|
|
1536
|
+
// CR-SM-396 (Entscheidung des Autors 2026-10-06): die Regel gilt fuer JEDES Schema. Bis hierher
|
|
1537
|
+
// pruefte sie nur Schemas mit Symbol-`realRef` — als Existenz-Regel der Stufe 9 brauchte sie damit
|
|
1538
|
+
// die Bindung (Stufe 11), und im Entwurf verlangte keine Regel den Vertragstest: TRR las erreicht,
|
|
1539
|
+
// ohne dass ein Schema getestet war. Der Test ist Teil der Spezifikation, nicht des Baus.
|
|
1522
1540
|
// ---------------------------------------------------------------------------
|
|
1523
1541
|
function schemaMustHaveContractTest(graph) {
|
|
1524
1542
|
const idx = indexOf(graph);
|
|
1525
1543
|
const typeOf = new Map(graph.elements.map(e => [e.id, e.type]));
|
|
1526
1544
|
const tests = idx.elementsOfType('TEST');
|
|
1527
1545
|
return idx.elementsOfType('SCHEMA')
|
|
1528
|
-
.filter(sc => sc.attributes?.concept !== true)
|
|
1529
|
-
.filter(sc => { const r = readRealRef(sc.attributes); return r.state === 'bound' && r.value.symbol !== undefined; })
|
|
1530
1546
|
.filter(sc => !graph.traces.some(t => t.type === 'verify' && t.target === sc.id && typeOf.get(t.source) === 'TEST'))
|
|
1531
1547
|
.map(sc => ({
|
|
1532
1548
|
rule_id: 'R-32',
|
|
1533
1549
|
severity: 'warning',
|
|
1534
1550
|
element_id: sc.id,
|
|
1535
|
-
message: `${sc.id} is a
|
|
1551
|
+
message: `${sc.id} is a SCHEMA without a contract TEST (no TEST -verify-> SCHEMA)`,
|
|
1536
1552
|
fix_hint: 'Add a TEST that exercises every variant of the contract incl. rejection, and link it via verify trace to this SCHEMA',
|
|
1537
1553
|
context: { element_type: sc.type, element_name: sc.name, candidate_targets: toCandidates(tests) },
|
|
1538
1554
|
}));
|
|
1539
1555
|
}
|
|
1540
1556
|
// ---------------------------------------------------------------------------
|
|
1541
|
-
// R-27: physical MOD realRef presence (CR-228) — the MOD arm of the unified
|
|
1542
|
-
// "element must have a realRef" rule (FUNC=R-20, SCHEMA=R-26, physical MOD=R-27).
|
|
1543
|
-
// A physical MOD (kind='physical') is a Bauteil realized by a CAD/geometry
|
|
1544
|
-
// artefact, not by code (RT-01: FUNCs never allocate directly to it). Logical/SW
|
|
1545
|
-
// MODs are realized through their FUNCs' code (R-20) and are NOT in scope here.
|
|
1546
|
-
// WARNING (presence signal like R-20/R-26); concept/external MODs are exempt.
|
|
1547
|
-
// Symbol is optional on a realRef (a geometry file has none) — presence = a valid
|
|
1548
|
-
// realRef with a file. RESOLUTION (file on disk) is a consumer/RC concern.
|
|
1549
|
-
// ---------------------------------------------------------------------------
|
|
1550
|
-
// ---------------------------------------------------------------------------
|
|
1551
1557
|
// R-28 (Ebenen-Präsenz) ist mit CR-SM-247 ersatzlos entfallen — nicht weil sie falsch war,
|
|
1552
1558
|
// sondern weil sie seit CR-GC-366 doppelt ist. Sie schloss das Vacuous-Complete-Loch (mit 0/1
|
|
1553
1559
|
// FUNC feuert keine Pro-Element-Regel, alles liest "fertig"); beide Hälften trägt heute je eine
|
|
@@ -1657,42 +1663,82 @@ function funcMustBeWired(graph) {
|
|
|
1657
1663
|
},
|
|
1658
1664
|
}));
|
|
1659
1665
|
}
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1666
|
+
// ---------------------------------------------------------------------------
|
|
1667
|
+
// R-33 (CR-SM-395): die Existenz-Regel des SYSTEMS (Stufe 1).
|
|
1668
|
+
//
|
|
1669
|
+
// Jede Pflichtmenge wird von einer Existenz-Regel der Stufe davor verlangt; eine leere Pflichtmenge
|
|
1670
|
+
// ist deshalb immer ein Befund und nie „0 von 0 bestanden". „Kein System" stand als Code im
|
|
1671
|
+
// Kaltstart von graphcode (`graph_generate`), nicht im Katalog. Ein Graph ohne SYS hat kein Element,
|
|
1672
|
+
// an dem der Befund haengen koennte — er traegt den Anker `GRAPH_ANCHOR`.
|
|
1673
|
+
//
|
|
1674
|
+
// R-34 („FLOW hat ein SCHEMA", Stufe 8) ist mit CR-SM-396 wieder gestrichen: deckungsgleich mit der
|
|
1675
|
+
// R-18-Untergrenze `FLOW -relation-> SCHEMA [1]` — alle 20 Befunde des Bestands standen an FLOWs
|
|
1676
|
+
// mit R-18-Fehler, R-34 konnte nie allein melden. Die Stufe 8 traegt keine eigene Existenz-Regel:
|
|
1677
|
+
// R-18 ist `'immer'` und haelt als Fehler jede Marke, das Gate lehnt den untypisierten FLOW schon
|
|
1678
|
+
// beim Schreiben ab; null Datenfluesse haelt R-31 (Stufe 6).
|
|
1679
|
+
// ---------------------------------------------------------------------------
|
|
1680
|
+
/** Der Anker eines Befunds an einem Graphen ohne SYS — kein Element traegt diese Id. */
|
|
1681
|
+
export const GRAPH_ANCHOR = 'graph';
|
|
1682
|
+
function graphMustHaveSystem(graph) {
|
|
1683
|
+
if (indexOf(graph).elementsOfType('SYS').length > 0)
|
|
1684
|
+
return [];
|
|
1685
|
+
return [{
|
|
1686
|
+
rule_id: 'R-33',
|
|
1687
|
+
severity: 'warning',
|
|
1688
|
+
element_id: GRAPH_ANCHOR,
|
|
1689
|
+
message: 'The graph has no SYS element — nothing names the system and its boundary',
|
|
1690
|
+
fix_hint: 'Add one SYS element that names the system; use cases, requirements and modules are composed below it',
|
|
1691
|
+
}];
|
|
1692
|
+
}
|
|
1693
|
+
/**
|
|
1694
|
+
* CR-SM-395: das UNGEBAUTE — die Elemente, an denen die Praesenzregeln der Bindung melden wuerden
|
|
1695
|
+
* (R-19 TEST, R-20 FUNC, R-26 SCHEMA), unabhaengig davon, ob sie schon faellig sind. EINE
|
|
1696
|
+
* Definition samt Ausnahmen (`external`, Zerlegungs-Eltern mit gebundenen Kindern): AF-05 fragt
|
|
1697
|
+
* damit „gibt es etwas zu bauen?", ohne die Ausnahmen ein zweites Mal zu schreiben.
|
|
1698
|
+
*/
|
|
1699
|
+
export function unbuiltElementIds(graph) {
|
|
1700
|
+
return [...testMustHaveRunnableBinding(graph), ...funcMustHaveCodeBinding(graph), ...schemaMustHaveSchemaRef(graph)]
|
|
1701
|
+
.map(v => v.element_id);
|
|
1702
|
+
}
|
|
1703
|
+
// CR-SM-395: die Faelligkeit haengt am KATALOG (`whenDue`), nicht am Auswerter und nicht im
|
|
1704
|
+
// Regelkoerper — die Regeln der Stufe 11 (R-19, R-20, R-26, R-29) schweigen, bis der Bau eroeffnet ist.
|
|
1705
|
+
export const V3_RULES = whenDue([
|
|
1706
|
+
{ id: 'R-33', name: 'Graph must have a SYS', severity: 'warning', stage: 1, role: 'existence', evaluate: graphMustHaveSystem, domain: ['graph'] },
|
|
1707
|
+
{ id: 'R-01', name: 'REQ must have verification', severity: 'error', stage: 9, role: 'existence', evaluate: reqMustHaveVerification, domain: ['REQ'] },
|
|
1708
|
+
{ id: 'R-02', name: 'FUNC must satisfy REQ', severity: 'warning', stage: 5, evaluate: funcMustSatisfyReq, domain: ['FUNC'] },
|
|
1663
1709
|
// CR-SM-243: domain ist MOD, nicht FUNC — die Regel iteriert Module und meldet am Modul,
|
|
1664
1710
|
// das ASIL-D und QM mischt. Die FUNCs sind der Anlass des Urteils, nicht seine Traeger.
|
|
1665
|
-
{ id: 'R-04', name: 'Module boundary width', severity: 'warning', evaluate: moduleBoundaryWidth, domain: ['MOD'] },
|
|
1666
|
-
{ id: 'R-05', name: 'TEST must verify REQ', severity: 'warning', evaluate: testMustVerifyReq, domain: ['TEST'] },
|
|
1667
|
-
{ id: 'R-15', name: 'FCHAIN completeness', severity: 'warning', evaluate: fchainCompleteness, domain: ['FCHAIN'] },
|
|
1668
|
-
{ id: 'R-16', name: 'ACTOR must have io', severity: 'warning', evaluate: actorMustHaveTrace, domain: ['ACTOR'] },
|
|
1669
|
-
{ id: 'R-17', name: 'SYS must have
|
|
1670
|
-
{ id: 'R-08', name: 'Trace consistency', severity: 'error', evaluate: traceConsistency, domain: ['all'] },
|
|
1711
|
+
{ id: 'R-04', name: 'Module boundary width', severity: 'warning', stage: 7, evaluate: moduleBoundaryWidth, domain: ['MOD'] },
|
|
1712
|
+
{ id: 'R-05', name: 'TEST must verify REQ', severity: 'warning', stage: 9, evaluate: testMustVerifyReq, domain: ['TEST'] },
|
|
1713
|
+
{ id: 'R-15', name: 'FCHAIN completeness', severity: 'warning', stage: 5, role: 'existence', evaluate: fchainCompleteness, domain: ['FCHAIN'] },
|
|
1714
|
+
{ id: 'R-16', name: 'ACTOR must have io', severity: 'warning', stage: 2, evaluate: actorMustHaveTrace, domain: ['ACTOR'] },
|
|
1715
|
+
{ id: 'R-17', name: 'SYS must have a use case', severity: 'warning', stage: 2, role: 'existence', evaluate: sysMustHaveUseCase, domain: ['SYS'] },
|
|
1716
|
+
{ id: 'R-08', name: 'Trace consistency', severity: 'error', stage: 'immer', evaluate: traceConsistency, domain: ['all'] },
|
|
1671
1717
|
// CR-SM-242: domain ist FLOW, nicht FUNC — die Regel iteriert ueber FLOWs und meldet am FLOW.
|
|
1672
1718
|
// Als ['FUNC'] deklariert erhoehte sie den Zaehler einer Grundgesamtheit, zu der ihre
|
|
1673
1719
|
// Verstoesse nicht gehoerten (bis zu 2 Legs je FLOW gegen einen FUNC-Nenner).
|
|
1674
|
-
{ id: 'R-10', name: 'FLOW completeness', severity: 'warning', evaluate: flowCompleteness, domain: ['FLOW'] },
|
|
1720
|
+
{ id: 'R-10', name: 'FLOW completeness', severity: 'warning', stage: 6, evaluate: flowCompleteness, domain: ['FLOW'] },
|
|
1675
1721
|
// Obergrenze zu R-10s Untergrenze — disjunkt, deshalb zwei IDs (CR-SM-307).
|
|
1676
|
-
{ id: 'IO-02', name: 'FLOW single producer', severity: 'error', evaluate: flowSingleProducer, domain: ['FLOW'] },
|
|
1677
|
-
{ id: 'R-12', name: 'No circular dependencies', severity: 'warning', evaluate: noDirectCircular, domain: ['FUNC'] },
|
|
1678
|
-
{ id: 'R-18', name: 'Valid trace pattern', severity: 'error', evaluate: validTracePattern, domain: ['all'] },
|
|
1679
|
-
{ id: 'R-19', name: 'Runnable TEST binding', severity: 'warning', evaluate: testMustHaveRunnableBinding, domain: ['TEST'] },
|
|
1680
|
-
{ id: 'R-29', name: 'Test file exclusivity', severity: 'error', evaluate: testFileExclusivity, domain: ['TEST'] },
|
|
1681
|
-
{ id: 'R-20', name: 'FUNC realRef binding', severity: 'warning', evaluate: funcMustHaveCodeBinding, domain: ['FUNC'] },
|
|
1682
|
-
{ id: 'R-21', name: 'FUNC↔FUNC handover needs a shared chain covered by member REQs or an integration test', severity: 'warning', evaluate: fchainMustHaveIntegrationTest, domain: ['FCHAIN', 'FUNC'] },
|
|
1683
|
-
{ id: 'R-22', name: 'FUNC must be allocated to MOD', severity: 'warning', evaluate: funcMustBeAllocated, domain: ['FUNC'] },
|
|
1684
|
-
{ id: 'R-23', name: 'MOD must have allocated FUNC', severity: 'warning', evaluate: modMustHaveAllocatedFunc, domain: ['MOD'] },
|
|
1685
|
-
{ id: 'R-26', name: 'SCHEMA must have realRef', severity: 'warning', evaluate: schemaMustHaveSchemaRef, domain: ['SCHEMA'] },
|
|
1686
|
-
{ id: 'R-32', name: 'SCHEMA must have contract TEST', severity: 'warning', evaluate: schemaMustHaveContractTest, domain: ['SCHEMA'] },
|
|
1687
|
-
{ id: 'RD-01', name: 'Unresolved requirement', severity: 'warning', evaluate: unresolvedRequirement, domain: ['REQ'] },
|
|
1688
|
-
{ id: 'RD-02', name: 'Decomposition consistency', severity: 'warning', evaluate: decompositionConsistency, domain: ['REQ'] },
|
|
1689
|
-
{ id: 'RD-04', name: 'Decomposition breadth', severity: 'warning', evaluate: decompositionBreadth, domain: ['FUNC', 'MOD', 'SYS'] },
|
|
1690
|
-
{ id: 'RD-05', name: 'Decomposition too narrow', severity: 'warning', evaluate: decompositionNarrow, domain: ['FUNC', 'MOD'
|
|
1691
|
-
{ id: 'MS-01', name: 'Milestone empty scope', severity: 'warning', evaluate: msEmptyScope, domain: ['MS'] },
|
|
1692
|
-
{ id: 'MS-02', name: 'Milestone dangling dependency', severity: 'warning', evaluate: msDanglingDependency, domain: ['MS'] },
|
|
1693
|
-
{ id: 'R-30', name: 'FUNC leaf must belong to a function chain', severity: 'warning', evaluate: funcMustBeInEffectChain, domain: ['FUNC'] },
|
|
1694
|
-
{ id: 'R-31', name: 'FUNC must be wired (io input + output)', severity: 'warning', evaluate: funcMustBeWired, domain: ['FUNC'] },
|
|
1695
|
-
];
|
|
1722
|
+
{ id: 'IO-02', name: 'FLOW single producer', severity: 'error', stage: 6, evaluate: flowSingleProducer, domain: ['FLOW'] },
|
|
1723
|
+
{ id: 'R-12', name: 'No circular dependencies', severity: 'warning', stage: 5, evaluate: noDirectCircular, domain: ['FUNC'] },
|
|
1724
|
+
{ id: 'R-18', name: 'Valid trace pattern', severity: 'error', stage: 'immer', evaluate: validTracePattern, domain: ['all'] },
|
|
1725
|
+
{ id: 'R-19', name: 'Runnable TEST binding', severity: 'warning', stage: 11, evaluate: testMustHaveRunnableBinding, domain: ['TEST'] },
|
|
1726
|
+
{ id: 'R-29', name: 'Test file exclusivity', severity: 'error', stage: 11, evaluate: testFileExclusivity, domain: ['TEST'] },
|
|
1727
|
+
{ id: 'R-20', name: 'FUNC realRef binding', severity: 'warning', stage: 11, evaluate: funcMustHaveCodeBinding, domain: ['FUNC'] },
|
|
1728
|
+
{ id: 'R-21', name: 'FUNC↔FUNC handover needs a shared chain covered by member REQs or an integration test', severity: 'warning', stage: 9, evaluate: fchainMustHaveIntegrationTest, domain: ['FCHAIN', 'FUNC'] },
|
|
1729
|
+
{ id: 'R-22', name: 'FUNC must be allocated to MOD', severity: 'warning', stage: 7, role: 'existence', evaluate: funcMustBeAllocated, domain: ['FUNC'] },
|
|
1730
|
+
{ id: 'R-23', name: 'MOD must have allocated FUNC', severity: 'warning', stage: 7, evaluate: modMustHaveAllocatedFunc, domain: ['MOD'] },
|
|
1731
|
+
{ id: 'R-26', name: 'SCHEMA must have realRef', severity: 'warning', stage: 11, evaluate: schemaMustHaveSchemaRef, domain: ['SCHEMA'] },
|
|
1732
|
+
{ id: 'R-32', name: 'SCHEMA must have contract TEST', severity: 'warning', stage: 9, role: 'existence', evaluate: schemaMustHaveContractTest, domain: ['SCHEMA'] },
|
|
1733
|
+
{ id: 'RD-01', name: 'Unresolved requirement', severity: 'warning', stage: 3, evaluate: unresolvedRequirement, domain: ['REQ'] },
|
|
1734
|
+
{ id: 'RD-02', name: 'Decomposition consistency', severity: 'warning', stage: 3, evaluate: decompositionConsistency, domain: ['REQ'] },
|
|
1735
|
+
{ id: 'RD-04', name: 'Decomposition breadth', severity: 'warning', stage: 7, evaluate: decompositionBreadth, domain: ['FUNC', 'MOD', 'SYS'] },
|
|
1736
|
+
{ id: 'RD-05', name: 'Decomposition too narrow', severity: 'warning', stage: 7, evaluate: decompositionNarrow, domain: ['FUNC', 'MOD'] },
|
|
1737
|
+
{ id: 'MS-01', name: 'Milestone empty scope', severity: 'warning', stage: 10, evaluate: msEmptyScope, domain: ['MS'] },
|
|
1738
|
+
{ id: 'MS-02', name: 'Milestone dangling dependency', severity: 'warning', stage: 10, evaluate: msDanglingDependency, domain: ['MS'] },
|
|
1739
|
+
{ id: 'R-30', name: 'FUNC leaf must belong to a function chain', severity: 'warning', stage: 5, evaluate: funcMustBeInEffectChain, domain: ['FUNC'] },
|
|
1740
|
+
{ id: 'R-31', name: 'FUNC must be wired (io input + output)', severity: 'warning', stage: 6, role: 'existence', evaluate: funcMustBeWired, domain: ['FUNC'] },
|
|
1741
|
+
]);
|
|
1696
1742
|
/** Run all rules against a graph. CR-SM-236: `policy` ist Pflicht — wie bei `evaluateAllRules`. */
|
|
1697
1743
|
export function evaluateRules(graph, policy) {
|
|
1698
1744
|
return V3_RULES.flatMap(rule => rule.evaluate(graph, policy));
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* attributes and fired on exactly the same elements (Jaccard 1.0 vs R-26). SC-03 had
|
|
7
7
|
* additionally become wrong — a SCHEMA correctly bound via `realRef` still fired it.
|
|
8
8
|
* `realRef` is the single SCHEMA binding truth (R-26 presence, RC-03/RC-04 resolution);
|
|
9
|
-
* a spec-only SCHEMA
|
|
9
|
+
* a spec-only SCHEMA simply has none yet, it does NOT carry a Zod body copy in the graph
|
|
10
10
|
* (same rule as TEST: bindings point at code, they never mirror it).
|
|
11
11
|
*
|
|
12
12
|
* What remains here is the one SCHEMA check that is NOT about binding: is the interface
|
|
@@ -32,7 +32,7 @@ export const SC_RULES = [
|
|
|
32
32
|
// Die Asymmetrie zur FLOW-Seite ist gewollt (CR-SM-242): SC-02 iteriert ueber SCHEMA
|
|
33
33
|
// („wird dieses SCHEMA benutzt"); die Gegenrichtung je FLOW („hat dieser FLOW einen
|
|
34
34
|
// Datenvertrag") traegt seit CR-SM-271 das R-18-Untergrenzen-Bein.
|
|
35
|
-
{ id: 'SC-02', name: 'Schema referenced by FLOW', severity: 'warning', evaluate: sc02IsReferenced, domain: ['SCHEMA'] },
|
|
35
|
+
{ id: 'SC-02', name: 'Schema referenced by FLOW', severity: 'warning', stage: 8, evaluate: sc02IsReferenced, domain: ['SCHEMA'] },
|
|
36
36
|
];
|
|
37
37
|
// CR-SM-236: `policy` wird durchgereicht, auch wo diese Familie heute keine Schwelle hat —
|
|
38
38
|
// ein Sonderweg je Familie waere genau der zweite Pfad, den der Regelsatz verbietet.
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { indexOf } from './graph-index.js';
|
|
2
|
+
import { hasPlaceholder } from './placeholder.js';
|
|
2
3
|
// ---------------------------------------------------------------------------
|
|
3
4
|
// UC-01: UC must have compose→REQ trace (CR-165: satisfy UC→REQ is invalid per meta-model)
|
|
4
5
|
// ---------------------------------------------------------------------------
|
|
@@ -94,13 +95,12 @@ export function uc03HasScenario(graph) {
|
|
|
94
95
|
// UC-04: UC must have meaningful description (CR-150: description = goal)
|
|
95
96
|
// ---------------------------------------------------------------------------
|
|
96
97
|
const UC_DESC_MIN_LEN = 10;
|
|
97
|
-
const PLACEHOLDER_RE = /\b(tbd|todo|fixme|placeholder|xxx)\b/i;
|
|
98
98
|
export function uc04GoalDefined(graph) {
|
|
99
99
|
return graph.elements
|
|
100
100
|
.filter(e => e.type === 'UC')
|
|
101
101
|
.filter(uc => {
|
|
102
102
|
const d = uc.description?.trim() ?? '';
|
|
103
|
-
return d.length < UC_DESC_MIN_LEN ||
|
|
103
|
+
return d.length < UC_DESC_MIN_LEN || hasPlaceholder(d);
|
|
104
104
|
})
|
|
105
105
|
.map(uc => ({
|
|
106
106
|
rule_id: 'UC-04',
|
|
@@ -117,10 +117,10 @@ export function uc04GoalDefined(graph) {
|
|
|
117
117
|
// CR-SM-357: UC-05/UC-06 (Pre-/Postcondition-REQ) sind gestrichen — Schreibregel in se:author-uc,
|
|
118
118
|
// wie BQ in author-req (CR-GC-602): sie feuerten an fast jedem UC, und niemand handelte darauf.
|
|
119
119
|
export const UC_RULES = [
|
|
120
|
-
{ id: 'UC-01', name: 'UC has requirements', severity: 'warning', evaluate: uc01HasRequirements, domain: ['UC'] },
|
|
121
|
-
{ id: 'UC-02', name: 'UC has actor', severity: 'warning', evaluate: uc02HasActor, domain: ['UC'] },
|
|
122
|
-
{ id: 'UC-03', name: 'UC has scenario', severity: 'warning', evaluate: uc03HasScenario, domain: ['UC'] },
|
|
123
|
-
{ id: 'UC-04', name: 'UC has goal', severity: 'warning', evaluate: uc04GoalDefined, domain: ['UC'] },
|
|
120
|
+
{ id: 'UC-01', name: 'UC has requirements', severity: 'warning', stage: 3, role: 'existence', evaluate: uc01HasRequirements, domain: ['UC'] },
|
|
121
|
+
{ id: 'UC-02', name: 'UC has actor', severity: 'warning', stage: 2, role: 'existence', evaluate: uc02HasActor, domain: ['UC'] },
|
|
122
|
+
{ id: 'UC-03', name: 'UC has scenario', severity: 'warning', stage: 4, role: 'existence', evaluate: uc03HasScenario, domain: ['UC'] },
|
|
123
|
+
{ id: 'UC-04', name: 'UC has goal', severity: 'warning', stage: 2, evaluate: uc04GoalDefined, domain: ['UC'] },
|
|
124
124
|
];
|
|
125
125
|
// CR-SM-236: `policy` wird durchgereicht, auch wo diese Familie heute keine Schwelle hat —
|
|
126
126
|
// ein Sonderweg je Familie waere genau der zweite Pfad, den der Regelsatz verbietet.
|
package/dist/se/view-rules.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* CL-01: ACTOR without UCs in at least 2 distinct operatingModes (warning).
|
|
5
5
|
*/
|
|
6
6
|
import { readTestRefs } from './ontology.js';
|
|
7
|
+
import { whenDue } from './rule-due.js';
|
|
7
8
|
// ---------------------------------------------------------------------------
|
|
8
9
|
// VR-01: ein testRefs-Eintrag ohne Ergebnis
|
|
9
10
|
//
|
|
@@ -103,10 +104,11 @@ export function cl01ConopsCompleteness(graph) {
|
|
|
103
104
|
// ---------------------------------------------------------------------------
|
|
104
105
|
// Aggregated array & convenience runner
|
|
105
106
|
// ---------------------------------------------------------------------------
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
{ id: '
|
|
109
|
-
]
|
|
107
|
+
// CR-SM-395: VR-01 ist Stufe 11 — ein Testergebnis gibt es erst, wenn der Bau eroeffnet ist (`whenDue`).
|
|
108
|
+
export const VIEW_RULES = whenDue([
|
|
109
|
+
{ id: 'VR-01', name: 'TestNoResult', severity: 'info', stage: 11, evaluate: vr01TestNoResult, domain: ['TEST'] },
|
|
110
|
+
{ id: 'CL-01', name: 'ConopsCompleteness', severity: 'warning', stage: 2, evaluate: cl01ConopsCompleteness, domain: ['ACTOR'] },
|
|
111
|
+
]);
|
|
110
112
|
// CR-SM-236: `policy` wird durchgereicht, auch wo diese Familie heute keine Schwelle hat —
|
|
111
113
|
// ein Sonderweg je Familie waere genau der zweite Pfad, den der Regelsatz verbietet.
|
|
112
114
|
export function evaluateViewRules(graph, policy) {
|