@sigloch/contracts 3.2.0 → 4.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/harness/index.d.ts +2 -2
- package/dist/se/action-priority.d.ts +101 -0
- package/dist/se/action-priority.js +124 -0
- package/dist/se/analysis-freshness-rules.d.ts +5 -0
- package/dist/se/analysis-freshness-rules.js +5 -5
- package/dist/se/ao-rules.d.ts +5 -39
- package/dist/se/ao-rules.js +20 -13
- package/dist/se/conformance-rules.d.ts +2 -2
- package/dist/se/conformance-rules.js +36 -30
- package/dist/se/cr-quality-rules.d.ts +2 -1
- package/dist/se/cr-quality-rules.js +9 -7
- package/dist/se/evaluate-all.d.ts +18 -3
- package/dist/se/evaluate-all.js +29 -26
- package/dist/se/fchain-quality-rules.d.ts +2 -1
- package/dist/se/fchain-quality-rules.js +8 -6
- package/dist/se/fmea-rules.d.ts +3 -11
- package/dist/se/fmea-rules.js +53 -16
- package/dist/se/format-e-parser.d.ts +14 -1
- package/dist/se/format-e-parser.js +8 -3
- package/dist/se/index.d.ts +4 -2
- package/dist/se/index.js +4 -2
- package/dist/se/metric-rules.d.ts +47 -5
- package/dist/se/metric-rules.js +199 -171
- 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 +55 -4
- package/dist/se/ontology.js +46 -6
- package/dist/se/policy.d.ts +65 -0
- package/dist/se/policy.js +100 -0
- package/dist/se/quality-rules.d.ts +2 -1
- package/dist/se/quality-rules.js +9 -7
- package/dist/se/readiness.d.ts +9 -1
- package/dist/se/readiness.js +18 -2
- package/dist/se/rules.d.ts +25 -5
- package/dist/se/rules.js +112 -47
- package/dist/se/schema-quality-rules.d.ts +2 -1
- package/dist/se/schema-quality-rules.js +6 -4
- package/dist/se/uc-quality-rules.d.ts +2 -1
- package/dist/se/uc-quality-rules.js +10 -8
- package/dist/se/view-rules.d.ts +2 -6
- package/dist/se/view-rules.js +40 -13
- package/package.json +2 -1
package/dist/harness/index.d.ts
CHANGED
|
@@ -118,8 +118,8 @@ export declare const RuleViolationSchema: z.ZodObject<{
|
|
|
118
118
|
ruleId: z.ZodString;
|
|
119
119
|
severity: z.ZodEnum<{
|
|
120
120
|
error: "error";
|
|
121
|
-
warning: "warning";
|
|
122
121
|
info: "info";
|
|
122
|
+
warning: "warning";
|
|
123
123
|
}>;
|
|
124
124
|
message: z.ZodString;
|
|
125
125
|
elementId: z.ZodOptional<z.ZodString>;
|
|
@@ -142,8 +142,8 @@ export declare const MutateResultSchema: z.ZodObject<{
|
|
|
142
142
|
ruleId: z.ZodString;
|
|
143
143
|
severity: z.ZodEnum<{
|
|
144
144
|
error: "error";
|
|
145
|
-
warning: "warning";
|
|
146
145
|
info: "info";
|
|
146
|
+
warning: "warning";
|
|
147
147
|
}>;
|
|
148
148
|
message: z.ZodString;
|
|
149
149
|
elementId: z.ZodOptional<z.ZodString>;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CR-SM-229 — Action Priority (AP) ersetzt RPN, ohne die geschützte Tabelle auszuliefern.
|
|
3
|
+
*
|
|
4
|
+
* Das AIAG-VDA FMEA Handbook (1st Edition, 2019) hat RPN **ersetzt**, nicht ergänzt. Der Skill
|
|
5
|
+
* `se-fmea` sagt das in Step 5 wörtlich („Risk Analysis → Action Priority (AP), not RPN"); der
|
|
6
|
+
* Regelcode folgte ihm bis hierher nicht.
|
|
7
|
+
*
|
|
8
|
+
* **RPN irrt gerichtet.** S 10 / O 2 / D 2 ergibt RPN 40 → „niedriges Risiko", während AP dort
|
|
9
|
+
* immer `High` sagt: ein gutes Detection-Rating rechnet eine sicherheitskritische Schwere weg.
|
|
10
|
+
* Genau dafür wurde AP eingeführt.
|
|
11
|
+
*
|
|
12
|
+
* ## Warum die Tabelle hier nicht steht
|
|
13
|
+
*
|
|
14
|
+
* Die normative AP-Tabelle (5 Severity- × 5 Occurrence- × 4 Detection-Bänder = 100 Zellen) steht
|
|
15
|
+
* ausschließlich im Handbook. Urheberrecht schützt keine Verfahren — **die Methode anzuwenden ist
|
|
16
|
+
* frei**. Die 100 Zellwerte sind aber normative Wertung (jemand hat entschieden, dass S 7 / O 4 /
|
|
17
|
+
* D 6 „Medium" ist), keine Fakten; sie in ein publiziertes npm-Paket zu legen wäre die verbotene
|
|
18
|
+
* Reproduktion.
|
|
19
|
+
*
|
|
20
|
+
* Deshalb: **die Logik wird ausgeliefert, die Zellwerte nie.** Eine Funktion, zwei Konsumenten
|
|
21
|
+
* (FM-03 und der graphcode-Exporter) — kein zweiter Klassifikationspfad. Die Tabelle wandert auch
|
|
22
|
+
* nicht in den Graphen (SYS-Attribute o. ä.): der Graph wird exportiert und verteilt, das wäre
|
|
23
|
+
* dieselbe Reproduktion durch die Hintertür.
|
|
24
|
+
*
|
|
25
|
+
* > Keine Rechtsberatung. Vor einem kommerziellen Release anwaltlich bestätigen lassen; in DE/EU
|
|
26
|
+
* > kommt zusätzlich das Datenbankherstellerrecht (§ 87a UrhG) in Betracht.
|
|
27
|
+
*
|
|
28
|
+
* ## Offene Fachfrage, hier nicht entschieden
|
|
29
|
+
*
|
|
30
|
+
* DFMEA und FMEA-MSR haben verschiedene AP-Tabellen. `ApTableSchema` muss die Variante
|
|
31
|
+
* mitführen, sobald MSR relevant wird.
|
|
32
|
+
*/
|
|
33
|
+
import { z } from 'zod/v4';
|
|
34
|
+
/** Die drei Stufen des AIAG-VDA-Verfahrens. */
|
|
35
|
+
export declare const ActionPriority: z.ZodEnum<{
|
|
36
|
+
High: "High";
|
|
37
|
+
Medium: "Medium";
|
|
38
|
+
Low: "Low";
|
|
39
|
+
}>;
|
|
40
|
+
export type ActionPriority = z.infer<typeof ActionPriority>;
|
|
41
|
+
/**
|
|
42
|
+
* Form einer AP-Tabelle, **nicht ihr Inhalt**.
|
|
43
|
+
*
|
|
44
|
+
* Ein Konsument lädt sie zur Laufzeit aus einer lokalen, nie eingecheckten Datei (z. B.
|
|
45
|
+
* `.graphcode/ap-table.json`, dort mit `.gitignore`-Eintrag). Das Schema erzwingt, dass sie
|
|
46
|
+
* vollständig ist: 5 × 5 × 4 Bänder und für jede der 100 Kombinationen eine Stufe. Eine
|
|
47
|
+
* unvollständige Tabelle wird abgelehnt, statt still auf die Interimslogik zurückzufallen —
|
|
48
|
+
* ein halb geladenes Urteil ist schlimmer als ein bekanntermaßen ersatzweises.
|
|
49
|
+
*/
|
|
50
|
+
export declare const ApTableSchema: z.ZodObject<{
|
|
51
|
+
severityBands: z.ZodArray<z.ZodObject<{
|
|
52
|
+
from: z.ZodNumber;
|
|
53
|
+
to: z.ZodNumber;
|
|
54
|
+
}, z.core.$strip>>;
|
|
55
|
+
occurrenceBands: z.ZodArray<z.ZodObject<{
|
|
56
|
+
from: z.ZodNumber;
|
|
57
|
+
to: z.ZodNumber;
|
|
58
|
+
}, z.core.$strip>>;
|
|
59
|
+
detectionBands: z.ZodArray<z.ZodObject<{
|
|
60
|
+
from: z.ZodNumber;
|
|
61
|
+
to: z.ZodNumber;
|
|
62
|
+
}, z.core.$strip>>;
|
|
63
|
+
cells: z.ZodArray<z.ZodArray<z.ZodArray<z.ZodEnum<{
|
|
64
|
+
High: "High";
|
|
65
|
+
Medium: "Medium";
|
|
66
|
+
Low: "Low";
|
|
67
|
+
}>>>>;
|
|
68
|
+
}, z.core.$strip>;
|
|
69
|
+
export type ApTable = z.infer<typeof ApTableSchema>;
|
|
70
|
+
/**
|
|
71
|
+
* Welches Verfahren die Einstufung gerade trägt.
|
|
72
|
+
*
|
|
73
|
+
* Treibt die Kennzeichnung beim Konsumenten (graphcode rendert die AP-Spalte als `AP*` mit
|
|
74
|
+
* generierter Fußnote, solange `'rpn-interim'` gilt). Hinweis und Berechnung können nicht
|
|
75
|
+
* auseinanderlaufen, weil beide aus derselben Quelle kommen.
|
|
76
|
+
*/
|
|
77
|
+
export declare function apMethod(table?: ApTable): 'aiag-vda' | 'rpn-interim';
|
|
78
|
+
/**
|
|
79
|
+
* Action Priority für ein S/O/D-Tripel.
|
|
80
|
+
*
|
|
81
|
+
* **Mit `table`** gilt die echte Zuordnung des Handbooks.
|
|
82
|
+
*
|
|
83
|
+
* **Ohne `table`** gilt der *markierte Übergang* — nur die von allen geprüften Quellen
|
|
84
|
+
* bestätigten Invarianten, sonst RPN-Bänder:
|
|
85
|
+
*
|
|
86
|
+
* ```
|
|
87
|
+
* s >= 9 → 'High' // bestätigt
|
|
88
|
+
* s === 1 → 'Low' // bestätigt
|
|
89
|
+
* sonst rpn > interimRpn → 'High' // übernimmt die bisherige FM-03-Grenze
|
|
90
|
+
* rpn >= 40 → 'Medium'
|
|
91
|
+
* sonst → 'Low'
|
|
92
|
+
* ```
|
|
93
|
+
*
|
|
94
|
+
* Das ist „bestätigtes AP, wo bekannt; RPN, wo nicht" — **nicht geratenes AP**. Die
|
|
95
|
+
* S-9-10-Klausel entfernt die gefährliche Irrtumsrichtung (hohe Schwere, gutes Detection) und
|
|
96
|
+
* kostet nichts.
|
|
97
|
+
*
|
|
98
|
+
* `interimRpn` ist die Schwelle aus `MetricPolicy.riskRpn` (CR-SM-236) — dieselbe Zahl, die
|
|
99
|
+
* FM-03 vorher direkt benutzt hat. Sie steht damit weiter an genau einer Stelle.
|
|
100
|
+
*/
|
|
101
|
+
export declare function actionPriority(s: number, o: number, d: number, interimRpn: number, table?: ApTable): ActionPriority;
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CR-SM-229 — Action Priority (AP) ersetzt RPN, ohne die geschützte Tabelle auszuliefern.
|
|
3
|
+
*
|
|
4
|
+
* Das AIAG-VDA FMEA Handbook (1st Edition, 2019) hat RPN **ersetzt**, nicht ergänzt. Der Skill
|
|
5
|
+
* `se-fmea` sagt das in Step 5 wörtlich („Risk Analysis → Action Priority (AP), not RPN"); der
|
|
6
|
+
* Regelcode folgte ihm bis hierher nicht.
|
|
7
|
+
*
|
|
8
|
+
* **RPN irrt gerichtet.** S 10 / O 2 / D 2 ergibt RPN 40 → „niedriges Risiko", während AP dort
|
|
9
|
+
* immer `High` sagt: ein gutes Detection-Rating rechnet eine sicherheitskritische Schwere weg.
|
|
10
|
+
* Genau dafür wurde AP eingeführt.
|
|
11
|
+
*
|
|
12
|
+
* ## Warum die Tabelle hier nicht steht
|
|
13
|
+
*
|
|
14
|
+
* Die normative AP-Tabelle (5 Severity- × 5 Occurrence- × 4 Detection-Bänder = 100 Zellen) steht
|
|
15
|
+
* ausschließlich im Handbook. Urheberrecht schützt keine Verfahren — **die Methode anzuwenden ist
|
|
16
|
+
* frei**. Die 100 Zellwerte sind aber normative Wertung (jemand hat entschieden, dass S 7 / O 4 /
|
|
17
|
+
* D 6 „Medium" ist), keine Fakten; sie in ein publiziertes npm-Paket zu legen wäre die verbotene
|
|
18
|
+
* Reproduktion.
|
|
19
|
+
*
|
|
20
|
+
* Deshalb: **die Logik wird ausgeliefert, die Zellwerte nie.** Eine Funktion, zwei Konsumenten
|
|
21
|
+
* (FM-03 und der graphcode-Exporter) — kein zweiter Klassifikationspfad. Die Tabelle wandert auch
|
|
22
|
+
* nicht in den Graphen (SYS-Attribute o. ä.): der Graph wird exportiert und verteilt, das wäre
|
|
23
|
+
* dieselbe Reproduktion durch die Hintertür.
|
|
24
|
+
*
|
|
25
|
+
* > Keine Rechtsberatung. Vor einem kommerziellen Release anwaltlich bestätigen lassen; in DE/EU
|
|
26
|
+
* > kommt zusätzlich das Datenbankherstellerrecht (§ 87a UrhG) in Betracht.
|
|
27
|
+
*
|
|
28
|
+
* ## Offene Fachfrage, hier nicht entschieden
|
|
29
|
+
*
|
|
30
|
+
* DFMEA und FMEA-MSR haben verschiedene AP-Tabellen. `ApTableSchema` muss die Variante
|
|
31
|
+
* mitführen, sobald MSR relevant wird.
|
|
32
|
+
*/
|
|
33
|
+
import { z } from 'zod/v4';
|
|
34
|
+
/** Die drei Stufen des AIAG-VDA-Verfahrens. */
|
|
35
|
+
export const ActionPriority = z.enum(['High', 'Medium', 'Low']);
|
|
36
|
+
/** Ein Rating von 1 bis 10 — die Skala, auf der S, O und D erhoben werden. */
|
|
37
|
+
const RatingSchema = z.number().int().min(1).max(10);
|
|
38
|
+
/**
|
|
39
|
+
* Ein Band der Tabelle: ein Rating-Bereich und die Zuordnung darunter.
|
|
40
|
+
*
|
|
41
|
+
* Die Bandgrenzen selbst sind KEINE geschützte Wertung, sondern die Achsenbeschriftung des
|
|
42
|
+
* Verfahrens (S 9–10 / 7–8 / 4–6 / 2–3 / 1 usw.). Geschützt ist, welche Stufe in einer Zelle
|
|
43
|
+
* steht — und genau das steht hier nicht.
|
|
44
|
+
*/
|
|
45
|
+
const BandSchema = z.object({
|
|
46
|
+
from: RatingSchema,
|
|
47
|
+
to: RatingSchema,
|
|
48
|
+
}).refine((b) => b.from <= b.to, { message: 'from must be <= to' });
|
|
49
|
+
/**
|
|
50
|
+
* Form einer AP-Tabelle, **nicht ihr Inhalt**.
|
|
51
|
+
*
|
|
52
|
+
* Ein Konsument lädt sie zur Laufzeit aus einer lokalen, nie eingecheckten Datei (z. B.
|
|
53
|
+
* `.graphcode/ap-table.json`, dort mit `.gitignore`-Eintrag). Das Schema erzwingt, dass sie
|
|
54
|
+
* vollständig ist: 5 × 5 × 4 Bänder und für jede der 100 Kombinationen eine Stufe. Eine
|
|
55
|
+
* unvollständige Tabelle wird abgelehnt, statt still auf die Interimslogik zurückzufallen —
|
|
56
|
+
* ein halb geladenes Urteil ist schlimmer als ein bekanntermaßen ersatzweises.
|
|
57
|
+
*/
|
|
58
|
+
export const ApTableSchema = z.object({
|
|
59
|
+
severityBands: z.array(BandSchema).length(5),
|
|
60
|
+
occurrenceBands: z.array(BandSchema).length(5),
|
|
61
|
+
detectionBands: z.array(BandSchema).length(4),
|
|
62
|
+
/**
|
|
63
|
+
* `cells[si][oi][di]` — Index in die drei Bandlisten, Wert ist die Stufe.
|
|
64
|
+
* 5 × 5 × 4 = 100 Zellen, alle Pflicht.
|
|
65
|
+
*/
|
|
66
|
+
cells: z.array(z.array(z.array(ActionPriority).length(4)).length(5)).length(5),
|
|
67
|
+
});
|
|
68
|
+
/**
|
|
69
|
+
* Welches Verfahren die Einstufung gerade trägt.
|
|
70
|
+
*
|
|
71
|
+
* Treibt die Kennzeichnung beim Konsumenten (graphcode rendert die AP-Spalte als `AP*` mit
|
|
72
|
+
* generierter Fußnote, solange `'rpn-interim'` gilt). Hinweis und Berechnung können nicht
|
|
73
|
+
* auseinanderlaufen, weil beide aus derselben Quelle kommen.
|
|
74
|
+
*/
|
|
75
|
+
export function apMethod(table) {
|
|
76
|
+
return table ? 'aiag-vda' : 'rpn-interim';
|
|
77
|
+
}
|
|
78
|
+
function bandIndex(bands, rating) {
|
|
79
|
+
return bands.findIndex((b) => rating >= b.from && rating <= b.to);
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Action Priority für ein S/O/D-Tripel.
|
|
83
|
+
*
|
|
84
|
+
* **Mit `table`** gilt die echte Zuordnung des Handbooks.
|
|
85
|
+
*
|
|
86
|
+
* **Ohne `table`** gilt der *markierte Übergang* — nur die von allen geprüften Quellen
|
|
87
|
+
* bestätigten Invarianten, sonst RPN-Bänder:
|
|
88
|
+
*
|
|
89
|
+
* ```
|
|
90
|
+
* s >= 9 → 'High' // bestätigt
|
|
91
|
+
* s === 1 → 'Low' // bestätigt
|
|
92
|
+
* sonst rpn > interimRpn → 'High' // übernimmt die bisherige FM-03-Grenze
|
|
93
|
+
* rpn >= 40 → 'Medium'
|
|
94
|
+
* sonst → 'Low'
|
|
95
|
+
* ```
|
|
96
|
+
*
|
|
97
|
+
* Das ist „bestätigtes AP, wo bekannt; RPN, wo nicht" — **nicht geratenes AP**. Die
|
|
98
|
+
* S-9-10-Klausel entfernt die gefährliche Irrtumsrichtung (hohe Schwere, gutes Detection) und
|
|
99
|
+
* kostet nichts.
|
|
100
|
+
*
|
|
101
|
+
* `interimRpn` ist die Schwelle aus `MetricPolicy.riskRpn` (CR-SM-236) — dieselbe Zahl, die
|
|
102
|
+
* FM-03 vorher direkt benutzt hat. Sie steht damit weiter an genau einer Stelle.
|
|
103
|
+
*/
|
|
104
|
+
export function actionPriority(s, o, d, interimRpn, table) {
|
|
105
|
+
if (table) {
|
|
106
|
+
const si = bandIndex(table.severityBands, s);
|
|
107
|
+
const oi = bandIndex(table.occurrenceBands, o);
|
|
108
|
+
const di = bandIndex(table.detectionBands, d);
|
|
109
|
+
// Ein Rating ausserhalb aller Baender ist ein Datenfehler, kein 'Low'. Der markierte
|
|
110
|
+
// Uebergang ist die ehrlichere Antwort als eine erfundene Zelle.
|
|
111
|
+
if (si >= 0 && oi >= 0 && di >= 0)
|
|
112
|
+
return table.cells[si][oi][di];
|
|
113
|
+
}
|
|
114
|
+
if (s >= 9)
|
|
115
|
+
return 'High';
|
|
116
|
+
if (s === 1)
|
|
117
|
+
return 'Low';
|
|
118
|
+
const rpn = s * o * d;
|
|
119
|
+
if (rpn > interimRpn)
|
|
120
|
+
return 'High';
|
|
121
|
+
if (rpn >= 40)
|
|
122
|
+
return 'Medium';
|
|
123
|
+
return 'Low';
|
|
124
|
+
}
|
|
@@ -14,26 +14,31 @@ export declare const AF_RULES: readonly [{
|
|
|
14
14
|
readonly name: "ConOps freshness stamp present";
|
|
15
15
|
readonly severity: "warning";
|
|
16
16
|
readonly evaluate: (graph: OntologyGraph) => RuleViolation[];
|
|
17
|
+
readonly domain: readonly ["SYS"];
|
|
17
18
|
}, {
|
|
18
19
|
readonly id: "AF-02";
|
|
19
20
|
readonly name: "Trade Study freshness stamp present";
|
|
20
21
|
readonly severity: "warning";
|
|
21
22
|
readonly evaluate: (graph: OntologyGraph) => RuleViolation[];
|
|
23
|
+
readonly domain: readonly ["SYS"];
|
|
22
24
|
}, {
|
|
23
25
|
readonly id: "AF-03";
|
|
24
26
|
readonly name: "Assumption Review freshness stamp present";
|
|
25
27
|
readonly severity: "warning";
|
|
26
28
|
readonly evaluate: (graph: OntologyGraph) => RuleViolation[];
|
|
29
|
+
readonly domain: readonly ["SYS"];
|
|
27
30
|
}, {
|
|
28
31
|
readonly id: "AF-04";
|
|
29
32
|
readonly name: "FMEA freshness stamp present";
|
|
30
33
|
readonly severity: "warning";
|
|
31
34
|
readonly evaluate: (graph: OntologyGraph) => RuleViolation[];
|
|
35
|
+
readonly domain: readonly ["SYS"];
|
|
32
36
|
}, {
|
|
33
37
|
readonly id: "AF-05";
|
|
34
38
|
readonly name: "Implementation Plan freshness stamp present";
|
|
35
39
|
readonly severity: "warning";
|
|
36
40
|
readonly evaluate: (graph: OntologyGraph) => RuleViolation[];
|
|
41
|
+
readonly domain: readonly ["SYS"];
|
|
37
42
|
}];
|
|
38
43
|
/** Evaluate all analysis-freshness-presence rules. */
|
|
39
44
|
export declare function evaluateAFRules(graph: OntologyGraph): RuleViolation[];
|
|
@@ -32,11 +32,11 @@ const assumptionReviewFreshnessPresent = analysisFreshnessPresence('AF-03', 'ass
|
|
|
32
32
|
const fmeaFreshnessPresent = analysisFreshnessPresence('AF-04', 'fmea');
|
|
33
33
|
const implplanFreshnessPresent = analysisFreshnessPresence('AF-05', 'implplan');
|
|
34
34
|
export const AF_RULES = [
|
|
35
|
-
{ id: 'AF-01', name: 'ConOps freshness stamp present', severity: 'warning', evaluate: conopsFreshnessPresent },
|
|
36
|
-
{ id: 'AF-02', name: 'Trade Study freshness stamp present', severity: 'warning', evaluate: tradeFreshnessPresent },
|
|
37
|
-
{ id: 'AF-03', name: 'Assumption Review freshness stamp present', severity: 'warning', evaluate: assumptionReviewFreshnessPresent },
|
|
38
|
-
{ id: 'AF-04', name: 'FMEA freshness stamp present', severity: 'warning', evaluate: fmeaFreshnessPresent },
|
|
39
|
-
{ id: 'AF-05', name: 'Implementation Plan freshness stamp present', severity: 'warning', evaluate: implplanFreshnessPresent },
|
|
35
|
+
{ id: 'AF-01', name: 'ConOps freshness stamp present', severity: 'warning', evaluate: conopsFreshnessPresent, domain: ['SYS'] },
|
|
36
|
+
{ id: 'AF-02', name: 'Trade Study freshness stamp present', severity: 'warning', evaluate: tradeFreshnessPresent, domain: ['SYS'] },
|
|
37
|
+
{ id: 'AF-03', name: 'Assumption Review freshness stamp present', severity: 'warning', evaluate: assumptionReviewFreshnessPresent, domain: ['SYS'] },
|
|
38
|
+
{ id: 'AF-04', name: 'FMEA freshness stamp present', severity: 'warning', evaluate: fmeaFreshnessPresent, domain: ['SYS'] },
|
|
39
|
+
{ id: 'AF-05', name: 'Implementation Plan freshness stamp present', severity: 'warning', evaluate: implplanFreshnessPresent, domain: ['SYS'] },
|
|
40
40
|
];
|
|
41
41
|
/** Evaluate all analysis-freshness-presence rules. */
|
|
42
42
|
export function evaluateAFRules(graph) {
|
package/dist/se/ao-rules.d.ts
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
* Severity: info (optimization hints [OH], not blocking violations).
|
|
4
4
|
*/
|
|
5
5
|
import type { OntologyGraph } from './ontology.js';
|
|
6
|
-
import type { RuleViolation } from './rules.js';
|
|
6
|
+
import type { RuleDefinition, RuleViolation } from './rules.js';
|
|
7
|
+
import type { MetricPolicy } from './policy.js';
|
|
7
8
|
/**
|
|
8
9
|
* AO-D01: Relay Node Detection.
|
|
9
10
|
* A FUNC is a relay if it (a) satisfies no REQ, (b) has >=2 outgoing io to FUNCs,
|
|
@@ -15,45 +16,10 @@ export declare function aoD01RelayNode(graph: OntologyGraph): RuleViolation[];
|
|
|
15
16
|
* FUNC A sends io to both B and C, where B and C connect to the same SCHEMA elements.
|
|
16
17
|
*/
|
|
17
18
|
export declare function aoD03DuplicatePath(graph: OntologyGraph): RuleViolation[];
|
|
18
|
-
export declare function cr01CrossingFlowCount(graph: OntologyGraph): RuleViolation[];
|
|
19
|
+
export declare function cr01CrossingFlowCount(graph: OntologyGraph, policy: MetricPolicy): RuleViolation[];
|
|
19
20
|
export declare function rt01PhysicalBoundaryIntegrity(graph: OntologyGraph): RuleViolation[];
|
|
20
21
|
export declare function ph01PhysicalModCompleteness(graph: OntologyGraph): RuleViolation[];
|
|
21
22
|
export declare function ca01CapabilityAllocation(graph: OntologyGraph): RuleViolation[];
|
|
22
23
|
export declare function io01CrossModuleCompleteness(graph: OntologyGraph): RuleViolation[];
|
|
23
|
-
export declare const AO_RULES:
|
|
24
|
-
|
|
25
|
-
readonly name: "RelayNodeDetection";
|
|
26
|
-
readonly severity: "info";
|
|
27
|
-
readonly evaluate: typeof aoD01RelayNode;
|
|
28
|
-
}, {
|
|
29
|
-
readonly id: "AO-D03";
|
|
30
|
-
readonly name: "DuplicatePathDetection";
|
|
31
|
-
readonly severity: "info";
|
|
32
|
-
readonly evaluate: typeof aoD03DuplicatePath;
|
|
33
|
-
}, {
|
|
34
|
-
readonly id: "CR-01";
|
|
35
|
-
readonly name: "CrossingFlowCount";
|
|
36
|
-
readonly severity: "warning";
|
|
37
|
-
readonly evaluate: typeof cr01CrossingFlowCount;
|
|
38
|
-
}, {
|
|
39
|
-
readonly id: "RT-01";
|
|
40
|
-
readonly name: "PhysicalBoundaryIntegrity";
|
|
41
|
-
readonly severity: "error";
|
|
42
|
-
readonly evaluate: typeof rt01PhysicalBoundaryIntegrity;
|
|
43
|
-
}, {
|
|
44
|
-
readonly id: "PH-01";
|
|
45
|
-
readonly name: "PhysicalModCompleteness";
|
|
46
|
-
readonly severity: "info";
|
|
47
|
-
readonly evaluate: typeof ph01PhysicalModCompleteness;
|
|
48
|
-
}, {
|
|
49
|
-
readonly id: "CA-01";
|
|
50
|
-
readonly name: "CapabilityAllocation";
|
|
51
|
-
readonly severity: "error";
|
|
52
|
-
readonly evaluate: typeof ca01CapabilityAllocation;
|
|
53
|
-
}, {
|
|
54
|
-
readonly id: "IO-01";
|
|
55
|
-
readonly name: "FuncPairIOCompleteness";
|
|
56
|
-
readonly severity: "warning";
|
|
57
|
-
readonly evaluate: typeof io01CrossModuleCompleteness;
|
|
58
|
-
}];
|
|
59
|
-
export declare function evaluateAORules(graph: OntologyGraph): RuleViolation[];
|
|
24
|
+
export declare const AO_RULES: RuleDefinition[];
|
|
25
|
+
export declare function evaluateAORules(graph: OntologyGraph, policy: MetricPolicy): RuleViolation[];
|
package/dist/se/ao-rules.js
CHANGED
|
@@ -135,9 +135,16 @@ export function aoD03DuplicatePath(graph) {
|
|
|
135
135
|
// ---------------------------------------------------------------------------
|
|
136
136
|
// CR-01: Crossing Flow Count (CR-191)
|
|
137
137
|
// For each module pair (A, B): count io paths FUNC_A → FUNC_B
|
|
138
|
+
//
|
|
139
|
+
// CR-SM-236: die Schwelle ist Eingabe. `policy.crossingFlows === null` heisst messen statt
|
|
140
|
+
// urteilen — die Regel schweigt dann vollstaendig, auch unterhalb der Schwelle. Vorher gab es
|
|
141
|
+
// diesen Aus-Zustand nicht: jedes Modulpaar mit >= 1 Kreuzung erzeugte eine info-Meldung.
|
|
138
142
|
// ---------------------------------------------------------------------------
|
|
139
|
-
export function cr01CrossingFlowCount(graph) {
|
|
143
|
+
export function cr01CrossingFlowCount(graph, policy) {
|
|
140
144
|
const violations = [];
|
|
145
|
+
const steps = policy.crossingFlows;
|
|
146
|
+
if (steps === null)
|
|
147
|
+
return violations;
|
|
141
148
|
const mods = graph.elements.filter(e => e.type === 'MOD');
|
|
142
149
|
// Build MOD → Set<FUNC-id> mapping
|
|
143
150
|
const modFuncs = new Map();
|
|
@@ -168,13 +175,13 @@ export function cr01CrossingFlowCount(graph) {
|
|
|
168
175
|
for (const { modA, modB, count } of pairCounts.values()) {
|
|
169
176
|
if (count <= 0)
|
|
170
177
|
continue;
|
|
171
|
-
const
|
|
178
|
+
const over = count >= steps.warning;
|
|
172
179
|
violations.push({
|
|
173
180
|
rule_id: 'CR-01',
|
|
174
|
-
severity,
|
|
181
|
+
severity: over ? 'warning' : 'info',
|
|
175
182
|
element_id: modA,
|
|
176
183
|
message: `${modA} ↔ ${modB}: ${count} crossing io flow(s)`,
|
|
177
|
-
fix_hint:
|
|
184
|
+
fix_hint: over ? 'Reduce coupling between modules or introduce a mediator' : 'Consider if coupling is intentional',
|
|
178
185
|
});
|
|
179
186
|
}
|
|
180
187
|
return violations;
|
|
@@ -330,14 +337,14 @@ export function io01CrossModuleCompleteness(graph) {
|
|
|
330
337
|
return violations;
|
|
331
338
|
}
|
|
332
339
|
export const AO_RULES = [
|
|
333
|
-
{ id: 'AO-D01', name: 'RelayNodeDetection', severity: 'info', evaluate: aoD01RelayNode },
|
|
334
|
-
{ id: 'AO-D03', name: 'DuplicatePathDetection', severity: 'info', evaluate: aoD03DuplicatePath },
|
|
335
|
-
{ id: 'CR-01', name: 'CrossingFlowCount', severity: 'warning', evaluate: cr01CrossingFlowCount },
|
|
336
|
-
{ id: 'RT-01', name: 'PhysicalBoundaryIntegrity', severity: 'error', evaluate: rt01PhysicalBoundaryIntegrity },
|
|
337
|
-
{ id: 'PH-01', name: 'PhysicalModCompleteness', severity: 'info', evaluate: ph01PhysicalModCompleteness },
|
|
338
|
-
{ id: 'CA-01', name: 'CapabilityAllocation', severity: 'error', evaluate: ca01CapabilityAllocation },
|
|
339
|
-
{ id: 'IO-01', name: 'FuncPairIOCompleteness', severity: 'warning', evaluate: io01CrossModuleCompleteness },
|
|
340
|
+
{ id: 'AO-D01', name: 'RelayNodeDetection', severity: 'info', evaluate: aoD01RelayNode, domain: ['FUNC'] },
|
|
341
|
+
{ id: 'AO-D03', name: 'DuplicatePathDetection', severity: 'info', evaluate: aoD03DuplicatePath, domain: ['FUNC'] },
|
|
342
|
+
{ id: 'CR-01', name: 'CrossingFlowCount', severity: 'warning', evaluate: cr01CrossingFlowCount, domain: ['MOD'] },
|
|
343
|
+
{ id: 'RT-01', name: 'PhysicalBoundaryIntegrity', severity: 'error', evaluate: rt01PhysicalBoundaryIntegrity, domain: ['FUNC'] },
|
|
344
|
+
{ id: 'PH-01', name: 'PhysicalModCompleteness', severity: 'info', evaluate: ph01PhysicalModCompleteness, domain: ['MOD'] },
|
|
345
|
+
{ id: 'CA-01', name: 'CapabilityAllocation', severity: 'error', evaluate: ca01CapabilityAllocation, domain: ['FUNC'] },
|
|
346
|
+
{ id: 'IO-01', name: 'FuncPairIOCompleteness', severity: 'warning', evaluate: io01CrossModuleCompleteness, domain: ['MOD'] },
|
|
340
347
|
];
|
|
341
|
-
export function evaluateAORules(graph) {
|
|
342
|
-
return AO_RULES.flatMap(r => r.evaluate(graph));
|
|
348
|
+
export function evaluateAORules(graph, policy) {
|
|
349
|
+
return AO_RULES.flatMap(r => r.evaluate(graph, policy));
|
|
343
350
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* RC conformance rules (CR-GC-253) — graph↔code resolution, the RESOLUTION
|
|
3
|
-
* twins of the presence rules R-20 (FUNC realRef) / R-19 (
|
|
3
|
+
* twins of the presence rules R-20 (FUNC realRef) / R-19 (testRefs).
|
|
4
4
|
*
|
|
5
5
|
* Rules stay in THIS library (one rule base per onto set — no rule definitions
|
|
6
6
|
* in executor codebases). They are pure functions over (graph, facts): all
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* from the repo), so this module stays I/O-free and browser-bundlable.
|
|
9
9
|
*
|
|
10
10
|
* CodeFacts semantics: `files` is keyed by repo-relative path and MUST contain
|
|
11
|
-
* an entry for every file referenced by a realRef/
|
|
11
|
+
* an entry for every file referenced by a realRef/testRefs entry the extractor saw.
|
|
12
12
|
* A MISSING key is treated like `exists:false` — an extractor gap must surface
|
|
13
13
|
* loudly as a violation, never as a silent pass.
|
|
14
14
|
*
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* RC conformance rules (CR-GC-253) — graph↔code resolution, the RESOLUTION
|
|
3
|
-
* twins of the presence rules R-20 (FUNC realRef) / R-19 (
|
|
3
|
+
* twins of the presence rules R-20 (FUNC realRef) / R-19 (testRefs).
|
|
4
4
|
*
|
|
5
5
|
* Rules stay in THIS library (one rule base per onto set — no rule definitions
|
|
6
6
|
* in executor codebases). They are pure functions over (graph, facts): all
|
|
@@ -8,14 +8,14 @@
|
|
|
8
8
|
* from the repo), so this module stays I/O-free and browser-bundlable.
|
|
9
9
|
*
|
|
10
10
|
* CodeFacts semantics: `files` is keyed by repo-relative path and MUST contain
|
|
11
|
-
* an entry for every file referenced by a realRef/
|
|
11
|
+
* an entry for every file referenced by a realRef/testRefs entry the extractor saw.
|
|
12
12
|
* A MISSING key is treated like `exists:false` — an extractor gap must surface
|
|
13
13
|
* loudly as a violation, never as a silent pass.
|
|
14
14
|
*
|
|
15
15
|
* @sigloch/contracts/se — single source of truth for SE validation rules.
|
|
16
16
|
*/
|
|
17
17
|
import { z } from 'zod/v4';
|
|
18
|
-
import { RealRefSchema,
|
|
18
|
+
import { RealRefSchema, TestRefsSchema } from './ontology.js';
|
|
19
19
|
/** Parser facts about one source file (extracted by the executor). */
|
|
20
20
|
export const FileFactsSchema = z.object({
|
|
21
21
|
/** File exists on disk (repo-relative path). */
|
|
@@ -92,8 +92,13 @@ function codeRefMustResolve(graph, facts) {
|
|
|
92
92
|
}
|
|
93
93
|
return violations;
|
|
94
94
|
}
|
|
95
|
-
// RC-02: every valid
|
|
95
|
+
// RC-02: every valid testRefs entry must resolve — file on disk and, when `case` is
|
|
96
96
|
// set, the case name declared as an it/test/describe in that file.
|
|
97
|
+
//
|
|
98
|
+
// CR-SM-231: iteriert die Eintraege. Ein TEST mit zwei Dateien, von denen eine fehlt, ist
|
|
99
|
+
// **ein** Befund an **einer** Adresse — die Meldung nennt deshalb den konkreten Pfad und
|
|
100
|
+
// nicht nur die Knoten-ID. Ohne das waere „TEST-x hat eine kaputte Bindung" bei n Dateien
|
|
101
|
+
// nicht handhabbar.
|
|
97
102
|
function testRefMustResolve(graph, facts) {
|
|
98
103
|
const violations = [];
|
|
99
104
|
for (const el of graph.elements) {
|
|
@@ -101,33 +106,34 @@ function testRefMustResolve(graph, facts) {
|
|
|
101
106
|
continue;
|
|
102
107
|
if (el.attributes?.concept === true)
|
|
103
108
|
continue;
|
|
104
|
-
const parsed =
|
|
109
|
+
const parsed = TestRefsSchema.safeParse(el.attributes?.testRefs);
|
|
105
110
|
if (!parsed.success)
|
|
106
111
|
continue; // no/invalid binding → R-19 territory
|
|
107
|
-
const ref
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
112
|
+
for (const ref of parsed.data) {
|
|
113
|
+
if (missingFile(facts, ref.file)) {
|
|
114
|
+
violations.push({
|
|
115
|
+
rule_id: 'RC-02',
|
|
116
|
+
severity: 'error',
|
|
117
|
+
element_id: el.id,
|
|
118
|
+
message: `${el.id} testRefs entry file '${ref.file}' does not exist on disk`,
|
|
119
|
+
fix_hint: 'The test file was moved or deleted — rebind the TEST (graph_realize) to the current file, or drop the entry',
|
|
120
|
+
context: { element_type: el.type, element_name: el.name },
|
|
121
|
+
});
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
// `case` is a name FILTER (vitest -t semantics): it matches by substring,
|
|
125
|
+
// so it must occur within some declared it/test/describe name — not equal it.
|
|
126
|
+
const caseName = ref.case;
|
|
127
|
+
if (caseName !== undefined && !facts.files[ref.file].testCases.some((name) => name.includes(caseName))) {
|
|
128
|
+
violations.push({
|
|
129
|
+
rule_id: 'RC-02',
|
|
130
|
+
severity: 'error',
|
|
131
|
+
element_id: el.id,
|
|
132
|
+
message: `${el.id} testRefs entry case '${ref.case}' is not declared in '${ref.file}'`,
|
|
133
|
+
fix_hint: 'The test case was renamed or removed — rebind the TEST case name',
|
|
134
|
+
context: { element_type: el.type, element_name: el.name },
|
|
135
|
+
});
|
|
136
|
+
}
|
|
131
137
|
}
|
|
132
138
|
}
|
|
133
139
|
return violations;
|
|
@@ -356,7 +362,7 @@ function importDriftConformance(graph, facts) {
|
|
|
356
362
|
/** All RC conformance rules — evaluated by executors that can supply CodeFacts. */
|
|
357
363
|
export const CODE_CONFORMANCE_RULES = [
|
|
358
364
|
{ id: 'RC-01', name: 'FUNC realRef resolves to a declared symbol', severity: 'error', evaluate: codeRefMustResolve },
|
|
359
|
-
{ id: 'RC-02', name: '
|
|
365
|
+
{ id: 'RC-02', name: 'testRefs entries resolve to runnable tests', severity: 'error', evaluate: testRefMustResolve },
|
|
360
366
|
{ id: 'RC-03', name: 'SCHEMA realRef resolves to a declared export', severity: 'error', evaluate: schemaRefMustResolve },
|
|
361
367
|
{ id: 'RC-04', name: 'SCHEMA realRef is parsed at its interface', severity: 'warning', evaluate: schemaRefMustBeUsed },
|
|
362
368
|
{ id: 'RC-05', name: 'cross-module import drift', severity: 'warning', evaluate: importDriftConformance },
|
|
@@ -4,5 +4,6 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import type { OntologyGraph } from './ontology.js';
|
|
6
6
|
import type { RuleDefinition, RuleViolation } from './rules.js';
|
|
7
|
+
import type { MetricPolicy } from './policy.js';
|
|
7
8
|
export declare const CR_RULES: RuleDefinition[];
|
|
8
|
-
export declare function evaluateCRRules(graph: OntologyGraph): RuleViolation[];
|
|
9
|
+
export declare function evaluateCRRules(graph: OntologyGraph, policy: MetricPolicy): RuleViolation[];
|
|
@@ -130,12 +130,14 @@ function crShouldHaveMilestone(graph) {
|
|
|
130
130
|
// Exports
|
|
131
131
|
// ---------------------------------------------------------------------------
|
|
132
132
|
export const CR_RULES = [
|
|
133
|
-
{ id: 'CR-R01', name: 'CR must track', severity: 'error', evaluate: crMustTrack },
|
|
134
|
-
{ id: 'CR-R02', name: 'Done requires commit', severity: 'error', evaluate: crDoneRequiresCommit },
|
|
135
|
-
{ id: 'CR-R03', name: 'No concurrent mutation', severity: 'warning', evaluate: noConcurrentMutation },
|
|
136
|
-
{ id: 'CR-R04', name: 'CR must have FUNC', severity: 'warning', evaluate: crMustHaveFunc },
|
|
137
|
-
{ id: 'MS-03', name: 'CR without milestone', severity: 'info', evaluate: crShouldHaveMilestone },
|
|
133
|
+
{ id: 'CR-R01', name: 'CR must track', severity: 'error', evaluate: crMustTrack, domain: ['CR'] },
|
|
134
|
+
{ id: 'CR-R02', name: 'Done requires commit', severity: 'error', evaluate: crDoneRequiresCommit, domain: ['CR'] },
|
|
135
|
+
{ id: 'CR-R03', name: 'No concurrent mutation', severity: 'warning', evaluate: noConcurrentMutation, domain: ['CR'] },
|
|
136
|
+
{ id: 'CR-R04', name: 'CR must have FUNC', severity: 'warning', evaluate: crMustHaveFunc, domain: ['CR'] },
|
|
137
|
+
{ id: 'MS-03', name: 'CR without milestone', severity: 'info', evaluate: crShouldHaveMilestone, domain: ['CR'] },
|
|
138
138
|
];
|
|
139
|
-
|
|
140
|
-
|
|
139
|
+
// CR-SM-236: `policy` wird durchgereicht, auch wo diese Familie heute keine Schwelle hat —
|
|
140
|
+
// ein Sonderweg je Familie waere genau der zweite Pfad, den der Regelsatz verbietet.
|
|
141
|
+
export function evaluateCRRules(graph, policy) {
|
|
142
|
+
return CR_RULES.flatMap(rule => rule.evaluate(graph, policy));
|
|
141
143
|
}
|
|
@@ -4,14 +4,29 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import type { OntologyGraph } from './ontology.js';
|
|
6
6
|
import type { RuleViolation } from './rules.js';
|
|
7
|
-
|
|
7
|
+
import type { MetricPolicy } from './policy.js';
|
|
8
|
+
/**
|
|
9
|
+
* All prescribed rule definitions (single source of truth for the catalog).
|
|
10
|
+
*
|
|
11
|
+
* CR-SM-235: `domain` reicht mit durch — die Grundgesamtheit, ueber die eine Regel feuert.
|
|
12
|
+
* Konsumenten, die einen Anteil bilden (`se-steering`s `computeApplicable`), lesen den Nenner
|
|
13
|
+
* hier statt eine eigene Tabelle zu fuehren. Eine zweite Tabelle kann nicht hinterherhinken,
|
|
14
|
+
* wenn es keine zweite gibt.
|
|
15
|
+
*/
|
|
8
16
|
export declare const ALL_RULE_DEFS: ReadonlyArray<{
|
|
9
17
|
id: string;
|
|
10
18
|
name: string;
|
|
11
19
|
severity: 'error' | 'warning' | 'info';
|
|
20
|
+
domain: readonly string[];
|
|
12
21
|
}>;
|
|
13
22
|
/** Profile groupings derived from rule prefixes. */
|
|
14
23
|
export type ProfileId = 'default' | 'se' | 'coding';
|
|
15
24
|
export declare function getRuleDefsForProfile(profile: ProfileId): typeof ALL_RULE_DEFS;
|
|
16
|
-
/**
|
|
17
|
-
|
|
25
|
+
/**
|
|
26
|
+
* Evaluate all rules against a graph. Single call replaces the individual evaluator calls.
|
|
27
|
+
*
|
|
28
|
+
* CR-SM-233: `policy` ist **Pflicht und ohne Fallback** — ein Aufruf ohne Policy ist ein
|
|
29
|
+
* Typfehler, keine stille 0.7. Wer keine eigene Quelle hat (Konfiguration, Host), nimmt
|
|
30
|
+
* `DEFAULT_METRIC_POLICY` sichtbar an der Aufrufstelle.
|
|
31
|
+
*/
|
|
32
|
+
export declare function evaluateAllRules(graph: OntologyGraph, policy: MetricPolicy): RuleViolation[];
|