@sigloch/contracts 10.14.0 → 12.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 +37 -30
- package/dist/se/analysis-freshness-rules.js +41 -81
- package/dist/se/ao-rules.js +3 -3
- package/dist/se/chain-metrics.d.ts +116 -0
- package/dist/se/chain-metrics.js +311 -0
- 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 +7 -3
- package/dist/se/evaluate-all.js +3 -4
- 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 +90 -103
- package/dist/se/index.d.ts +4 -2
- package/dist/se/index.js +6 -2
- package/dist/se/metric-rules.d.ts +9 -5
- package/dist/se/metric-rules.js +10 -9
- package/dist/se/module-crossings.d.ts +10 -0
- package/dist/se/module-crossings.js +3 -1
- 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 -7
- package/dist/se/ontology.js +29 -26
- package/dist/se/quality-rules.js +5 -5
- package/dist/se/readiness.d.ts +41 -149
- package/dist/se/readiness.js +50 -280
- package/dist/se/rule-due.d.ts +26 -0
- package/dist/se/rule-due.js +72 -0
- package/dist/se/rule-help.d.ts +3 -1
- package/dist/se/rule-help.js +47 -31
- package/dist/se/rules.d.ts +23 -13
- package/dist/se/rules.js +112 -72
- package/dist/se/schema-quality-rules.d.ts +1 -1
- package/dist/se/schema-quality-rules.js +1 -1
- package/dist/se/similarity.js +4 -10
- package/dist/se/uc-quality-rules.js +4 -4
- package/dist/se/view-rules.js +6 -4
- package/package.json +1 -1
|
@@ -1,64 +1,71 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* CR-SM-227:
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
2
|
+
* CR-SM-227: the analysis rules — was each judgment-work artifact carried out?
|
|
3
|
+
*
|
|
4
|
+
* AF-01..04 ask for the stamp `SYS.attributes.analysisFreshness.<artifact>`: its presence is the
|
|
5
|
+
* only record that the analysis was done (an FMEA may legitimately end without a single risk, so
|
|
6
|
+
* the result cannot stand in for it). They never compare the stamp against the live graphVersion.
|
|
7
|
+
*
|
|
8
|
+
* AF-05 is different since CR-SM-395: the implementation plan is not a stamp but the set of open
|
|
9
|
+
* CRs. The rule reads "there is something unbuilt, but no open order" and falls due again with
|
|
10
|
+
* every change — which a one-time stamp could not express.
|
|
9
11
|
*/
|
|
10
12
|
import type { OntologyGraph } from './ontology.js';
|
|
11
|
-
import type
|
|
13
|
+
import { type RuleViolation } from './rules.js';
|
|
14
|
+
/**
|
|
15
|
+
* AF-05 (CR-SM-395): es gibt Ungebautes, aber keinen offenen Auftrag → der Bauplan ist faellig.
|
|
16
|
+
*
|
|
17
|
+
* Ungebaut ist, woran die Bindungsregeln melden wuerden (`unbuiltElementIds`: TEST, FUNC, SCHEMA
|
|
18
|
+
* ohne Bindung, `external` und Zerlegungs-Eltern ausgenommen wie dort). Es reicht, dass EIN offener
|
|
19
|
+
* Auftrag existiert; er muss nicht auf die Elemente zeigen — ob er die richtigen trifft, zeigt der
|
|
20
|
+
* Git-Diff. Mit dem Auftrag verstummt diese Regel, und die Bindungsregeln der Stufe 11 werden
|
|
21
|
+
* faellig: ihre Befunde sind die Arbeitsliste. Beim ersten Bau sind das alle Elemente, bei einer
|
|
22
|
+
* Aenderung die neuen.
|
|
23
|
+
*
|
|
24
|
+
* EIN Befund je Graph, am SYS: dort wird er abgenommen („Bau nicht beauftragt"), und eine Abnahme
|
|
25
|
+
* je Element waere mit jedem neuen Element wieder offen. Die Meldung nennt die ungebauten Elemente.
|
|
26
|
+
*/
|
|
27
|
+
declare function buildPlanDue(graph: OntologyGraph): RuleViolation[];
|
|
12
28
|
export declare const AF_RULES: readonly [{
|
|
13
29
|
readonly id: "AF-01";
|
|
14
30
|
readonly name: "ConOps freshness stamp present";
|
|
15
31
|
readonly severity: "warning";
|
|
32
|
+
readonly stage: 3;
|
|
33
|
+
readonly role: "analysis";
|
|
16
34
|
readonly evaluate: (graph: OntologyGraph) => RuleViolation[];
|
|
17
35
|
readonly domain: readonly ["graph"];
|
|
18
36
|
}, {
|
|
19
37
|
readonly id: "AF-02";
|
|
20
38
|
readonly name: "Trade Study freshness stamp present";
|
|
21
39
|
readonly severity: "warning";
|
|
40
|
+
readonly stage: 7;
|
|
41
|
+
readonly role: "analysis";
|
|
22
42
|
readonly evaluate: (graph: OntologyGraph) => RuleViolation[];
|
|
23
43
|
readonly domain: readonly ["graph"];
|
|
24
44
|
}, {
|
|
25
45
|
readonly id: "AF-03";
|
|
26
46
|
readonly name: "Assumption Review freshness stamp present";
|
|
27
47
|
readonly severity: "warning";
|
|
48
|
+
readonly stage: 7;
|
|
49
|
+
readonly role: "analysis";
|
|
28
50
|
readonly evaluate: (graph: OntologyGraph) => RuleViolation[];
|
|
29
51
|
readonly domain: readonly ["graph"];
|
|
30
52
|
}, {
|
|
31
53
|
readonly id: "AF-04";
|
|
32
54
|
readonly name: "FMEA freshness stamp present";
|
|
33
55
|
readonly severity: "warning";
|
|
56
|
+
readonly stage: 8;
|
|
57
|
+
readonly role: "analysis";
|
|
34
58
|
readonly evaluate: (graph: OntologyGraph) => RuleViolation[];
|
|
35
59
|
readonly domain: readonly ["graph"];
|
|
36
60
|
}, {
|
|
37
61
|
readonly id: "AF-05";
|
|
38
|
-
readonly name: "
|
|
39
|
-
readonly severity: "warning";
|
|
40
|
-
readonly evaluate: (graph: OntologyGraph) => RuleViolation[];
|
|
41
|
-
readonly domain: readonly ["graph"];
|
|
42
|
-
}];
|
|
43
|
-
/** TR-01: a stamped trade study names >=1 existing CR, each carrying a relation(label: decides) edge. */
|
|
44
|
-
export declare function tradeDecisionRecorded(graph: OntologyGraph): RuleViolation[];
|
|
45
|
-
/** IR-01: every CR named by a stamped assumption review exists; an empty list is a legitimate outcome. */
|
|
46
|
-
export declare function assumptionReviewPromoted(graph: OntologyGraph): RuleViolation[];
|
|
47
|
-
/** CR-SM-355: the task-outcome rules — owned by trade / irr (TASK_OWNED_RULES), never in the kern focus. */
|
|
48
|
-
export declare const TASK_OUTCOME_RULES: readonly [{
|
|
49
|
-
readonly id: "TR-01";
|
|
50
|
-
readonly name: "Trade decision recorded as CR";
|
|
51
|
-
readonly severity: "warning";
|
|
52
|
-
readonly evaluate: typeof tradeDecisionRecorded;
|
|
53
|
-
readonly domain: readonly ["graph"];
|
|
54
|
-
}, {
|
|
55
|
-
readonly id: "IR-01";
|
|
56
|
-
readonly name: "Assumption review promoted to CR";
|
|
62
|
+
readonly name: "Unbuilt elements have an open CR";
|
|
57
63
|
readonly severity: "warning";
|
|
58
|
-
readonly
|
|
64
|
+
readonly stage: 10;
|
|
65
|
+
readonly role: "existence";
|
|
66
|
+
readonly evaluate: typeof buildPlanDue;
|
|
59
67
|
readonly domain: readonly ["graph"];
|
|
60
68
|
}];
|
|
61
|
-
/** Evaluate the task-outcome rules (CR-SM-355). */
|
|
62
|
-
export declare function evaluateTaskOutcomeRules(graph: OntologyGraph): RuleViolation[];
|
|
63
69
|
/** Evaluate all analysis-freshness-presence rules. */
|
|
64
70
|
export declare function evaluateAFRules(graph: OntologyGraph): RuleViolation[];
|
|
71
|
+
export {};
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { readAnalysisStamp } from './ontology.js';
|
|
2
|
+
import { unbuiltElementIds } from './rules.js';
|
|
3
|
+
import { hasOpenOrder } from './rule-due.js';
|
|
2
4
|
const ARTIFACT_LABEL = {
|
|
3
5
|
conops: 'Concept of Operations',
|
|
4
6
|
trade: 'Trade Study',
|
|
5
7
|
'assumption-review': 'Assumption Review',
|
|
6
8
|
fmea: 'FMEA',
|
|
7
|
-
implplan: 'Implementation Plan',
|
|
8
9
|
};
|
|
9
10
|
/**
|
|
10
11
|
* CR-SM-382: was der Hinweis nennt — den Task, seinen Skill und den ersten Arbeitsschritt.
|
|
@@ -29,20 +30,16 @@ const ANALYSIS_WORK = {
|
|
|
29
30
|
},
|
|
30
31
|
trade: {
|
|
31
32
|
skill: 'se-trade',
|
|
32
|
-
firstStep: 'name the open decision with its options and criteria, then
|
|
33
|
+
firstStep: 'name the open decision with its options and criteria, then apply the chosen option to the model and record the decision as a done CR with relation edges to what was decided',
|
|
33
34
|
},
|
|
34
35
|
'assumption-review': {
|
|
35
36
|
skill: 'se-irr',
|
|
36
|
-
firstStep: 'list every unproven assumption the model rests on (unmeasured numbers, unverified external dependencies) in a record under docs/records/, then
|
|
37
|
+
firstStep: 'list every unproven assumption the model rests on (unmeasured numbers, unverified external dependencies) in a record under docs/records/, then write each load-bearing one as a REQ with its verifying TEST',
|
|
37
38
|
},
|
|
38
39
|
fmea: {
|
|
39
40
|
skill: 'se-fmea',
|
|
40
41
|
firstStep: 'for each FCHAIN name its failure modes and add each one as a REQ with role risk (severity, occurrence, detection) plus a mitigation REQ and its TEST',
|
|
41
42
|
},
|
|
42
|
-
implplan: {
|
|
43
|
-
skill: 'se-plan',
|
|
44
|
-
firstStep: 'cut one CR per leaf REQ at its carrier and group the CRs into MS milestones in dependency order',
|
|
45
|
-
},
|
|
46
43
|
};
|
|
47
44
|
/** Presence-check for one analysis artifact's freshness stamp, anchored on SYS. */
|
|
48
45
|
function analysisFreshnessPresence(ruleId, artifactId) {
|
|
@@ -50,8 +47,7 @@ function analysisFreshnessPresence(ruleId, artifactId) {
|
|
|
50
47
|
const sys = graph.elements.find(e => e.type === 'SYS');
|
|
51
48
|
if (!sys)
|
|
52
49
|
return []; // nothing to anchor on yet (vacuous-complete exemption)
|
|
53
|
-
|
|
54
|
-
if (AnalysisFreshnessStampSchema.safeParse(stamp).success)
|
|
50
|
+
if (readAnalysisStamp(graph, artifactId).state === 'bound')
|
|
55
51
|
return [];
|
|
56
52
|
const label = ARTIFACT_LABEL[artifactId];
|
|
57
53
|
const work = ANALYSIS_WORK[artifactId];
|
|
@@ -69,85 +65,49 @@ const conopsFreshnessPresent = analysisFreshnessPresence('AF-01', 'conops');
|
|
|
69
65
|
const tradeFreshnessPresent = analysisFreshnessPresence('AF-02', 'trade');
|
|
70
66
|
const assumptionReviewFreshnessPresent = analysisFreshnessPresence('AF-03', 'assumption-review');
|
|
71
67
|
const fmeaFreshnessPresent = analysisFreshnessPresence('AF-04', 'fmea');
|
|
72
|
-
const implplanFreshnessPresent = analysisFreshnessPresence('AF-05', 'implplan');
|
|
73
|
-
export const AF_RULES = [
|
|
74
|
-
{ id: 'AF-01', name: 'ConOps freshness stamp present', severity: 'warning', evaluate: conopsFreshnessPresent, domain: ['graph'] },
|
|
75
|
-
{ id: 'AF-02', name: 'Trade Study freshness stamp present', severity: 'warning', evaluate: tradeFreshnessPresent, domain: ['graph'] },
|
|
76
|
-
{ id: 'AF-03', name: 'Assumption Review freshness stamp present', severity: 'warning', evaluate: assumptionReviewFreshnessPresent, domain: ['graph'] },
|
|
77
|
-
{ id: 'AF-04', name: 'FMEA freshness stamp present', severity: 'warning', evaluate: fmeaFreshnessPresent, domain: ['graph'] },
|
|
78
|
-
{ id: 'AF-05', name: 'Implementation Plan freshness stamp present', severity: 'warning', evaluate: implplanFreshnessPresent, domain: ['graph'] },
|
|
79
|
-
];
|
|
80
68
|
/**
|
|
81
|
-
* CR-SM-
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
69
|
+
* AF-05 (CR-SM-395): es gibt Ungebautes, aber keinen offenen Auftrag → der Bauplan ist faellig.
|
|
70
|
+
*
|
|
71
|
+
* Ungebaut ist, woran die Bindungsregeln melden wuerden (`unbuiltElementIds`: TEST, FUNC, SCHEMA
|
|
72
|
+
* ohne Bindung, `external` und Zerlegungs-Eltern ausgenommen wie dort). Es reicht, dass EIN offener
|
|
73
|
+
* Auftrag existiert; er muss nicht auf die Elemente zeigen — ob er die richtigen trifft, zeigt der
|
|
74
|
+
* Git-Diff. Mit dem Auftrag verstummt diese Regel, und die Bindungsregeln der Stufe 11 werden
|
|
75
|
+
* faellig: ihre Befunde sind die Arbeitsliste. Beim ersten Bau sind das alle Elemente, bei einer
|
|
76
|
+
* Aenderung die neuen.
|
|
77
|
+
*
|
|
78
|
+
* EIN Befund je Graph, am SYS: dort wird er abgenommen („Bau nicht beauftragt"), und eine Abnahme
|
|
79
|
+
* je Element waere mit jedem neuen Element wieder offen. Die Meldung nennt die ungebauten Elemente.
|
|
85
80
|
*/
|
|
86
|
-
function
|
|
81
|
+
function buildPlanDue(graph) {
|
|
87
82
|
const sys = graph.elements.find(e => e.type === 'SYS');
|
|
88
83
|
if (!sys)
|
|
89
|
-
return
|
|
90
|
-
|
|
91
|
-
const parsed = AnalysisFreshnessStampSchema.safeParse(raw);
|
|
92
|
-
if (!parsed.success)
|
|
93
|
-
return null;
|
|
94
|
-
return { sys, crRefs: parsed.data.crRefs ?? [] };
|
|
95
|
-
}
|
|
96
|
-
/** TR-01: a stamped trade study names >=1 existing CR, each carrying a relation(label: decides) edge. */
|
|
97
|
-
export function tradeDecisionRecorded(graph) {
|
|
98
|
-
const stamped = stampedCrRefs(graph, 'trade');
|
|
99
|
-
if (!stamped)
|
|
84
|
+
return []; // kein Anker — R-33 meldet das fehlende System
|
|
85
|
+
if (hasOpenOrder(graph))
|
|
100
86
|
return [];
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
if (crRefs.length === 0) {
|
|
104
|
-
return [{
|
|
105
|
-
...base,
|
|
106
|
-
element_id: sys.id,
|
|
107
|
-
message: 'Trade Study is stamped but names no decision CR (analysisFreshness.trade.crRefs is empty) — a trade without a recorded decision is a list, not a trade',
|
|
108
|
-
fix_hint: 'Record the decision as a CR with relation(label: decides) edges and list its id in analysisFreshness.trade.crRefs (se-trade)',
|
|
109
|
-
}];
|
|
110
|
-
}
|
|
111
|
-
const byId = new Map(graph.elements.map(e => [e.id, e]));
|
|
112
|
-
const decidesFrom = new Set(graph.traces.filter(t => t.type === 'relation' && t.label === 'decides').map(t => t.source));
|
|
113
|
-
return crRefs.flatMap(id => {
|
|
114
|
-
const cr = byId.get(id);
|
|
115
|
-
if (!cr || cr.type !== 'CR') {
|
|
116
|
-
return [{ ...base, element_id: sys.id, message: `Trade Study names ${id} as its decision, but no CR with that id exists`, fix_hint: 'Point analysisFreshness.trade.crRefs at an existing CR, or add the CR through graph_mutate (se-trade)' }];
|
|
117
|
-
}
|
|
118
|
-
if (!decidesFrom.has(id)) {
|
|
119
|
-
return [{ ...base, element_id: id, message: `${id} is named as a trade decision but carries no relation(label: decides) edge`, fix_hint: 'Add a relation edge from the CR to the element it decides, with label "decides" (se-trade)' }];
|
|
120
|
-
}
|
|
121
|
-
return [];
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
/** IR-01: every CR named by a stamped assumption review exists; an empty list is a legitimate outcome. */
|
|
125
|
-
export function assumptionReviewPromoted(graph) {
|
|
126
|
-
const stamped = stampedCrRefs(graph, 'assumption-review');
|
|
127
|
-
if (!stamped)
|
|
87
|
+
const unbuilt = unbuiltElementIds(graph);
|
|
88
|
+
if (unbuilt.length === 0)
|
|
128
89
|
return [];
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
fix_hint: 'Point analysisFreshness.assumption-review.crRefs at an existing CR, or add the CR through graph_mutate (se-irr)',
|
|
139
|
-
context: { element_type: sys.type, element_name: sys.name },
|
|
140
|
-
}));
|
|
90
|
+
const shown = unbuilt.slice(0, 3).join(', ') + (unbuilt.length > 3 ? ` and ${unbuilt.length - 3} more` : '');
|
|
91
|
+
return [{
|
|
92
|
+
rule_id: 'AF-05',
|
|
93
|
+
severity: 'warning',
|
|
94
|
+
element_id: sys.id,
|
|
95
|
+
message: `${unbuilt.length} ${unbuilt.length === 1 ? 'element is' : 'elements are'} not built yet (${shown}) and no open CR orders the work — the implementation plan is due`,
|
|
96
|
+
fix_hint: 'Carry out the Implementation Plan: work through the skill se-plan step by step. First step: cut one CR per leaf REQ at its carrier and group the CRs into MS milestones in dependency order. The open CRs are the plan — with the first one the binding rules list what to build',
|
|
97
|
+
context: { element_type: sys.type, element_name: sys.name },
|
|
98
|
+
}];
|
|
141
99
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
100
|
+
// CR-SM-396: AF-01 liegt auf Stufe 3, HINTER den Anforderungen — eine Ausnahme von „Stufe = spaeteste
|
|
101
|
+
// Menge, die die Regel braucht" (das Einsatzkonzept braucht nur die Anwendungsfaelle, Stufe 2).
|
|
102
|
+
// Entscheidung des Autors 2026-10-06: zuerst sagt der Anwendungsfall, was verlangt ist (UC-01), dann
|
|
103
|
+
// folgt die Analyse des Betriebs. In einer Stufe steht die Existenz-Regel vor der Analyse.
|
|
104
|
+
export const AF_RULES = [
|
|
105
|
+
{ id: 'AF-01', name: 'ConOps freshness stamp present', severity: 'warning', stage: 3, role: 'analysis', evaluate: conopsFreshnessPresent, domain: ['graph'] },
|
|
106
|
+
{ id: 'AF-02', name: 'Trade Study freshness stamp present', severity: 'warning', stage: 7, role: 'analysis', evaluate: tradeFreshnessPresent, domain: ['graph'] },
|
|
107
|
+
{ id: 'AF-03', name: 'Assumption Review freshness stamp present', severity: 'warning', stage: 7, role: 'analysis', evaluate: assumptionReviewFreshnessPresent, domain: ['graph'] },
|
|
108
|
+
{ id: 'AF-04', name: 'FMEA freshness stamp present', severity: 'warning', stage: 8, role: 'analysis', evaluate: fmeaFreshnessPresent, domain: ['graph'] },
|
|
109
|
+
{ id: 'AF-05', name: 'Unbuilt elements have an open CR', severity: 'warning', stage: 10, role: 'existence', evaluate: buildPlanDue, domain: ['graph'] },
|
|
146
110
|
];
|
|
147
|
-
/** Evaluate the task-outcome rules (CR-SM-355). */
|
|
148
|
-
export function evaluateTaskOutcomeRules(graph) {
|
|
149
|
-
return TASK_OUTCOME_RULES.flatMap(r => r.evaluate(graph));
|
|
150
|
-
}
|
|
151
111
|
/** Evaluate all analysis-freshness-presence rules. */
|
|
152
112
|
export function evaluateAFRules(graph) {
|
|
153
113
|
return AF_RULES.flatMap(r => r.evaluate(graph));
|
package/dist/se/ao-rules.js
CHANGED
|
@@ -260,12 +260,12 @@ export function io01CrossModuleCompleteness(graph) {
|
|
|
260
260
|
return violations;
|
|
261
261
|
}
|
|
262
262
|
export const AO_RULES = [
|
|
263
|
-
{ id: 'BW-02', name: 'Whitebox boundary width', severity: 'warning', evaluate: bw02WhiteboxWidth, domain: ['FUNC'] },
|
|
264
|
-
{ id: 'CR-01', name: 'CrossingFlowCount', severity: 'warning', evaluate: cr01CrossingFlowCount, domain: ['MOD'] },
|
|
263
|
+
{ id: 'BW-02', name: 'Whitebox boundary width', severity: 'warning', stage: 5, evaluate: bw02WhiteboxWidth, domain: ['FUNC'] },
|
|
264
|
+
{ id: 'CR-01', name: 'CrossingFlowCount', severity: 'warning', stage: 7, evaluate: cr01CrossingFlowCount, domain: ['MOD'] },
|
|
265
265
|
// CR-SM-242: domain ist FUNC, nicht MOD — die Regel laeuft ueber FUNC-Paare in FCHAINs und
|
|
266
266
|
// meldet am FUNC. Mit ['MOD'] uebertraf ihr Zaehler (21) ihren eigenen Nenner-Beitrag (10);
|
|
267
267
|
// nur der Clamp verhinderte einen negativen Score.
|
|
268
|
-
{ id: 'IO-01', name: 'FuncPairIOCompleteness', severity: 'warning', evaluate: io01CrossModuleCompleteness, domain: ['FUNC'] },
|
|
268
|
+
{ id: 'IO-01', name: 'FuncPairIOCompleteness', severity: 'warning', stage: 5, evaluate: io01CrossModuleCompleteness, domain: ['FUNC'] },
|
|
269
269
|
];
|
|
270
270
|
export function evaluateAORules(graph, policy) {
|
|
271
271
|
return AO_RULES.flatMap(r => r.evaluate(graph, policy));
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CR-SM-404 — Kennzahlen der Wirkkette: je FCHAIN eine Zahl, oder der Grund, warum es keine gibt.
|
|
3
|
+
*
|
|
4
|
+
* Die Leitlinie verlangt, die Realisierungsarchitektur AUF DER WIRKKETTE zu bewerten (graphcode
|
|
5
|
+
* `docs/graphcode_architektur_konzept.md`, „Kennzahlen der Wirkkette"). Bis hierher rechnete das
|
|
6
|
+
* nur ein Spike in graphcode (`scripts/spike-kettenkennzahlen.mjs`); von dort ist die Rechnung
|
|
7
|
+
* portiert, nicht neu erfunden. Gemessen 2026-10-09 ueber 10 Familie-Graphen: 76 Ketten, 31
|
|
8
|
+
* bewertbar.
|
|
9
|
+
*
|
|
10
|
+
* ## Diese Datei URTEILT NICHT
|
|
11
|
+
*
|
|
12
|
+
* Keine Regel, keine Schwelle, kein Katalogeintrag — dieselbe Trennung wie `moduleMetrics` und
|
|
13
|
+
* `functionCriticality`. Ob eine Laenge von 6 gut ist, sagt erst ein Profil.
|
|
14
|
+
*
|
|
15
|
+
* ## Nicht bewertbar heisst: KEINE Zahl
|
|
16
|
+
*
|
|
17
|
+
* Eine FCHAIN ist grammatisch eine Menge; die Reihenfolge entsteht erst aus Erzeuger ->
|
|
18
|
+
* Verbraucher (`FUNC -io-> FLOW -io-> FUNC`, beide Enden Glied). Zerfaellt die Kette, beschriebe
|
|
19
|
+
* jede Kennzahl etwas, das es nicht gibt. Eine solche Kette traegt deshalb ihre Gruende und kein
|
|
20
|
+
* einziges Zahlenfeld — das Schema laesst es nicht zu. Sie zaehlt im NENNER der Quote
|
|
21
|
+
* (`measurability`), die neben jedem Wert stehen muss wie die Bindungsquote neben RC-*.
|
|
22
|
+
*
|
|
23
|
+
* ## Wer urteilt, ob eine Kette eine Kette ist
|
|
24
|
+
*
|
|
25
|
+
* - `FC-05`: die Regel FC-05, aufgerufen, nicht nachgebaut. Ursache (fehlendes Glied, Sack) und
|
|
26
|
+
* die Nebenkomponenten stehen in ihrem Befund (`context.side_components`), nicht hier.
|
|
27
|
+
* - `loose-member`: ein Glied ohne io-Eingang oder -Ausgang. Das ist die Bedingung, unter der
|
|
28
|
+
* FC-05 sich ENTHAELT (R-31 meldet das Glied) — ohne diesen Grund haette eine solche Kette weder
|
|
29
|
+
* einen FC-05-Befund noch einen Grund und bekaeme Zahlen.
|
|
30
|
+
* - `no-entry` / `no-exit`: kein FLOW von bzw. zu einem Knoten AUSSERHALB der Kette. Weiter als
|
|
31
|
+
* FC-04, das nur einen ACTOR gelten laesst: eine Kette, die eine andere fortsetzt, ist messbar.
|
|
32
|
+
* - `empty`: keine FUNC (R-15).
|
|
33
|
+
*
|
|
34
|
+
* ## Ein Kettenbegriff, ein Modulbegriff
|
|
35
|
+
*
|
|
36
|
+
* Glied = direkte `FCHAIN -compose-> FUNC`-Kante, ueber `chainsByFunc` (R-30s Definition).
|
|
37
|
+
* Modul einer FUNC = direkte `allocate`-Kante, wie `module-crossings.ts` und `moduleMetrics`.
|
|
38
|
+
* Der Spike erbte das Modul ueber compose-Vorfahren; gemessen sind 326 von 326 Kettengliedern
|
|
39
|
+
* des Korpus direkt alloziert — die Vererbung kauft nichts und waere eine zweite Vorstellung von
|
|
40
|
+
* Zugehoerigkeit im selben Paket.
|
|
41
|
+
*
|
|
42
|
+
* ## CR-SM-406 — die ORTE je Kette, nur erkannt
|
|
43
|
+
*
|
|
44
|
+
* Neben jeder Zahl steht, WO sie herkommt: die Mitglieder je Rueckkopplung, die geteilten
|
|
45
|
+
* Funktionen, die Grenzen je gerichtetem Modulpaar mit ihren Vertraegen, dazu Zulauf, Importe und
|
|
46
|
+
* Uebergaben. Reine Zusatzfelder — die Zahlen aus CR-SM-404 bleiben und sind die Laenge ihrer
|
|
47
|
+
* Liste. Weiter kein Urteil.
|
|
48
|
+
*
|
|
49
|
+
* - Import und Uebergabe zaehlen nur an Gliedern, die ALLEIN dieser Kette gehoeren. Was an einer
|
|
50
|
+
* geteilten Funktion haengt, steht an der geteilten Funktion — sie ist ein eigener Ort. Ohne
|
|
51
|
+
* diese Grenze meldete jede Kette durch Gate und Store deren Zufluesse als ihre eigenen
|
|
52
|
+
* (gemessen am graphcode-Modell 2026-10-10: 333 statt 16).
|
|
53
|
+
* - Ein ACTOR als Erzeuger oder Verbraucher ist der Ein- und Ausgang der Kette, kein Ort.
|
|
54
|
+
* - Vertrag = `contractsOfFlow` aus `module-crossings.ts` (SCHEMA, sonst `UNBOUND:<flow>`), hier je
|
|
55
|
+
* Kettenkante gelesen. `moduleCrossings().pairs` selbst traegt die Vertraege des GANZEN Graphen
|
|
56
|
+
* je ungerichtetem Paar und kann „elf Kanten auf einem Vertrag" in EINER Kette nicht zeigen.
|
|
57
|
+
*
|
|
58
|
+
* @author andreas@siglochconsulting
|
|
59
|
+
*/
|
|
60
|
+
import { z } from 'zod/v4';
|
|
61
|
+
import type { OntologyGraph } from './ontology.js';
|
|
62
|
+
export declare const ChainMetrics: z.ZodObject<{
|
|
63
|
+
chains: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
64
|
+
measurable: z.ZodLiteral<true>;
|
|
65
|
+
length: z.ZodNumber;
|
|
66
|
+
branching: z.ZodNumber;
|
|
67
|
+
moduleBoundaries: z.ZodNullable<z.ZodNumber>;
|
|
68
|
+
feedbackLoops: z.ZodNumber;
|
|
69
|
+
sharedFuncs: z.ZodNumber;
|
|
70
|
+
bottlenecks: z.ZodNumber;
|
|
71
|
+
syncDepth: z.ZodNull;
|
|
72
|
+
errorPathDepth: z.ZodNull;
|
|
73
|
+
memberCount: z.ZodNumber;
|
|
74
|
+
fanIn: z.ZodNumber;
|
|
75
|
+
loops: z.ZodArray<z.ZodObject<{
|
|
76
|
+
id: z.ZodString;
|
|
77
|
+
members: z.ZodArray<z.ZodString>;
|
|
78
|
+
}, z.core.$strict>>;
|
|
79
|
+
shared: z.ZodArray<z.ZodString>;
|
|
80
|
+
boundaries: z.ZodNullable<z.ZodArray<z.ZodObject<{
|
|
81
|
+
from: z.ZodString;
|
|
82
|
+
to: z.ZodString;
|
|
83
|
+
edges: z.ZodNumber;
|
|
84
|
+
contracts: z.ZodArray<z.ZodString>;
|
|
85
|
+
}, z.core.$strict>>>;
|
|
86
|
+
imports: z.ZodArray<z.ZodObject<{
|
|
87
|
+
flow: z.ZodString;
|
|
88
|
+
func: z.ZodString;
|
|
89
|
+
}, z.core.$strict>>;
|
|
90
|
+
handovers: z.ZodArray<z.ZodObject<{
|
|
91
|
+
flow: z.ZodString;
|
|
92
|
+
func: z.ZodString;
|
|
93
|
+
}, z.core.$strict>>;
|
|
94
|
+
chainId: z.ZodString;
|
|
95
|
+
chainName: z.ZodString;
|
|
96
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
97
|
+
measurable: z.ZodLiteral<false>;
|
|
98
|
+
reasons: z.ZodArray<z.ZodEnum<{
|
|
99
|
+
"FC-05": "FC-05";
|
|
100
|
+
"loose-member": "loose-member";
|
|
101
|
+
"no-entry": "no-entry";
|
|
102
|
+
"no-exit": "no-exit";
|
|
103
|
+
empty: "empty";
|
|
104
|
+
}>>;
|
|
105
|
+
chainId: z.ZodString;
|
|
106
|
+
chainName: z.ZodString;
|
|
107
|
+
}, z.core.$strict>], "measurable">>;
|
|
108
|
+
measurability: z.ZodObject<{
|
|
109
|
+
chains: z.ZodNumber;
|
|
110
|
+
measurable: z.ZodNumber;
|
|
111
|
+
ratio: z.ZodNullable<z.ZodNumber>;
|
|
112
|
+
}, z.core.$strip>;
|
|
113
|
+
}, z.core.$strip>;
|
|
114
|
+
export type ChainMetrics = z.infer<typeof ChainMetrics>;
|
|
115
|
+
/** Die Kennzahlen aller FCHAINs dieses Graphen und die Quote der bewertbaren. */
|
|
116
|
+
export declare function chainMetrics(graph: OntologyGraph): ChainMetrics;
|