@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
|
@@ -1,64 +1,98 @@
|
|
|
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: "
|
|
62
|
+
readonly name: "Unbuilt elements have an open CR";
|
|
39
63
|
readonly severity: "warning";
|
|
40
|
-
readonly
|
|
64
|
+
readonly stage: 10;
|
|
65
|
+
readonly role: "existence";
|
|
66
|
+
readonly evaluate: typeof buildPlanDue;
|
|
41
67
|
readonly domain: readonly ["graph"];
|
|
42
68
|
}];
|
|
43
69
|
/** TR-01: a stamped trade study names >=1 existing CR, each carrying a relation(label: decides) edge. */
|
|
44
70
|
export declare function tradeDecisionRecorded(graph: OntologyGraph): RuleViolation[];
|
|
45
|
-
/**
|
|
46
|
-
|
|
47
|
-
|
|
71
|
+
/**
|
|
72
|
+
* IR-01 (CR-SM-397): every REQ named by a stamped assumption review exists; an empty list is a
|
|
73
|
+
* legitimate outcome. That the REQ carries a verifying TEST is R-01's finding, not repeated here.
|
|
74
|
+
*/
|
|
75
|
+
export declare function assumptionReviewRequirementsExist(graph: OntologyGraph): RuleViolation[];
|
|
76
|
+
/** CR-SM-355: the outcome rules of trade / irr — silent until the analysis is stamped. */
|
|
48
77
|
export declare const TASK_OUTCOME_RULES: readonly [{
|
|
49
78
|
readonly id: "TR-01";
|
|
50
79
|
readonly name: "Trade decision recorded as CR";
|
|
51
80
|
readonly severity: "warning";
|
|
81
|
+
readonly stage: 7;
|
|
82
|
+
readonly role: "analysis";
|
|
52
83
|
readonly evaluate: typeof tradeDecisionRecorded;
|
|
53
84
|
readonly domain: readonly ["graph"];
|
|
54
85
|
}, {
|
|
55
86
|
readonly id: "IR-01";
|
|
56
|
-
readonly name: "Assumption review
|
|
87
|
+
readonly name: "Assumption review requirements exist";
|
|
57
88
|
readonly severity: "warning";
|
|
58
|
-
readonly
|
|
89
|
+
readonly stage: 7;
|
|
90
|
+
readonly role: "analysis";
|
|
91
|
+
readonly evaluate: typeof assumptionReviewRequirementsExist;
|
|
59
92
|
readonly domain: readonly ["graph"];
|
|
60
93
|
}];
|
|
61
94
|
/** Evaluate the task-outcome rules (CR-SM-355). */
|
|
62
95
|
export declare function evaluateTaskOutcomeRules(graph: OntologyGraph): RuleViolation[];
|
|
63
96
|
/** Evaluate all analysis-freshness-presence rules. */
|
|
64
97
|
export declare function evaluateAFRules(graph: OntologyGraph): RuleViolation[];
|
|
98
|
+
export {};
|
|
@@ -1,10 +1,45 @@
|
|
|
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
|
-
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* CR-SM-382: was der Hinweis nennt — den Task, seinen Skill und den ersten Arbeitsschritt.
|
|
12
|
+
*
|
|
13
|
+
* Bis hierher lautete der fix_hint "Set attributes.analysisFreshness.<id>.graphVersion …". Gemessen im
|
|
14
|
+
* lokalen AgentDiary-Lauf (local-1, 2026-09-30): das Modell setzte fuenf Stempel in einem Zug, ohne ein
|
|
15
|
+
* einziges Artefakt, und meldete die Analysen als erledigt — der Hinweis nannte die Metrik als Handlung.
|
|
16
|
+
* Dazu wich die Artefakt-id vom Task-Namen ab (`implplan` gegen `plan`), der Aufruf
|
|
17
|
+
* `graph_generate {task:'implplan'}` schlug fehl. Der Hinweis nannte darauf den Aufruf woertlich und die
|
|
18
|
+
* Arbeit; den Stempel schreibt der letzte Schritt des Skills, zusammen mit den Funden.
|
|
19
|
+
*
|
|
20
|
+
* CR-SM-387: der Hinweis nennt keinen Werkzeugnamen mehr. `graph_generate` hat nur der Autopilot; der
|
|
21
|
+
* interaktive Agent (todo-local, 2026-10-03) zitierte den Aufruf zweimal als fehlendes Werkzeug und
|
|
22
|
+
* liess die Analyse liegen. Der Skill fuehrt die Analyse in beiden Betriebsarten — er ist der Name, der
|
|
23
|
+
* stimmen muss (`skill` folgt RULE_HELP[…].prompt, der Test haelt beide zusammen); damit entfaellt auch
|
|
24
|
+
* die Fehlerquelle `task` gegen Artefakt-id.
|
|
25
|
+
*/
|
|
26
|
+
const ANALYSIS_WORK = {
|
|
27
|
+
conops: {
|
|
28
|
+
skill: 'se-conops',
|
|
29
|
+
firstStep: 'walk configuration, credentials, user management, deployment, observability and data lifecycle, and write each concern the system must satisfy as a non-functional REQ with its TEST at SYS scope',
|
|
30
|
+
},
|
|
31
|
+
trade: {
|
|
32
|
+
skill: 'se-trade',
|
|
33
|
+
firstStep: 'name the open decision with its options and criteria, then record the chosen option as a CR with relation(label: decides) edges to what it decides',
|
|
34
|
+
},
|
|
35
|
+
'assumption-review': {
|
|
36
|
+
skill: 'se-irr',
|
|
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',
|
|
38
|
+
},
|
|
39
|
+
fmea: {
|
|
40
|
+
skill: 'se-fmea',
|
|
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',
|
|
42
|
+
},
|
|
8
43
|
};
|
|
9
44
|
/** Presence-check for one analysis artifact's freshness stamp, anchored on SYS. */
|
|
10
45
|
function analysisFreshnessPresence(ruleId, artifactId) {
|
|
@@ -12,16 +47,16 @@ function analysisFreshnessPresence(ruleId, artifactId) {
|
|
|
12
47
|
const sys = graph.elements.find(e => e.type === 'SYS');
|
|
13
48
|
if (!sys)
|
|
14
49
|
return []; // nothing to anchor on yet (vacuous-complete exemption)
|
|
15
|
-
|
|
16
|
-
if (AnalysisFreshnessStampSchema.safeParse(stamp).success)
|
|
50
|
+
if (readAnalysisStamp(graph, artifactId).state === 'bound')
|
|
17
51
|
return [];
|
|
18
52
|
const label = ARTIFACT_LABEL[artifactId];
|
|
53
|
+
const work = ANALYSIS_WORK[artifactId];
|
|
19
54
|
return [{
|
|
20
55
|
rule_id: ruleId,
|
|
21
56
|
severity: 'warning',
|
|
22
57
|
element_id: sys.id,
|
|
23
|
-
message: `${label}
|
|
24
|
-
fix_hint: `
|
|
58
|
+
message: `${label} has not been carried out — no analysis is on record for this system`,
|
|
59
|
+
fix_hint: `Carry out the ${label}: work through the skill ${work.skill} step by step. First step: ${work.firstStep}. The closing step of the skill records the analysis as done, in the same batch as its findings`,
|
|
25
60
|
context: { element_type: sys.type, element_name: sys.name },
|
|
26
61
|
}];
|
|
27
62
|
};
|
|
@@ -30,36 +65,73 @@ const conopsFreshnessPresent = analysisFreshnessPresence('AF-01', 'conops');
|
|
|
30
65
|
const tradeFreshnessPresent = analysisFreshnessPresence('AF-02', 'trade');
|
|
31
66
|
const assumptionReviewFreshnessPresent = analysisFreshnessPresence('AF-03', 'assumption-review');
|
|
32
67
|
const fmeaFreshnessPresent = analysisFreshnessPresence('AF-04', 'fmea');
|
|
33
|
-
|
|
68
|
+
/**
|
|
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.
|
|
80
|
+
*/
|
|
81
|
+
function buildPlanDue(graph) {
|
|
82
|
+
const sys = graph.elements.find(e => e.type === 'SYS');
|
|
83
|
+
if (!sys)
|
|
84
|
+
return []; // kein Anker — R-33 meldet das fehlende System
|
|
85
|
+
if (hasOpenOrder(graph))
|
|
86
|
+
return [];
|
|
87
|
+
const unbuilt = unbuiltElementIds(graph);
|
|
88
|
+
if (unbuilt.length === 0)
|
|
89
|
+
return [];
|
|
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
|
+
}];
|
|
99
|
+
}
|
|
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.
|
|
34
104
|
export const AF_RULES = [
|
|
35
|
-
{ id: 'AF-01', name: 'ConOps freshness stamp present', severity: 'warning', evaluate: conopsFreshnessPresent, domain: ['graph'] },
|
|
36
|
-
{ id: 'AF-02', name: 'Trade Study freshness stamp present', severity: 'warning', evaluate: tradeFreshnessPresent, domain: ['graph'] },
|
|
37
|
-
{ id: 'AF-03', name: 'Assumption Review freshness stamp present', severity: 'warning', evaluate: assumptionReviewFreshnessPresent, domain: ['graph'] },
|
|
38
|
-
{ id: 'AF-04', name: 'FMEA freshness stamp present', severity: 'warning', evaluate: fmeaFreshnessPresent, domain: ['graph'] },
|
|
39
|
-
{ id: 'AF-05', name: '
|
|
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'] },
|
|
40
110
|
];
|
|
41
111
|
/**
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
112
|
+
* Task-Ausgangsregeln fuer trade und irr (CR-SM-355, CR-SM-397): was der Task hinterlassen hat, ist
|
|
113
|
+
* im Frischestempel genannt und existiert. Ein Variantenvergleich hinterlaesst Entscheidungs-CRs
|
|
114
|
+
* (`crRefs`, TR-01), ein Annahmen-Review Anforderungen (`reqRefs`, IR-01) — eine Analyse legt keinen
|
|
115
|
+
* Bauauftrag an. Ohne Stempel feuern die Regeln nicht (dann feuert der Eintritt AF-02/AF-03).
|
|
116
|
+
* `label` wird als `Trace.label` gelesen wie bei MS-02 — `decides` ist kein TraceType, sondern das
|
|
117
|
+
* SP-1-Label einer relation-Kante.
|
|
46
118
|
*/
|
|
47
|
-
function
|
|
119
|
+
function stampedOutcome(graph, artifactId) {
|
|
48
120
|
const sys = graph.elements.find(e => e.type === 'SYS');
|
|
49
121
|
if (!sys)
|
|
50
122
|
return null;
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
if (!parsed.success)
|
|
123
|
+
const stamp = readAnalysisStamp(graph, artifactId);
|
|
124
|
+
if (stamp.state !== 'bound')
|
|
54
125
|
return null;
|
|
55
|
-
return { sys,
|
|
126
|
+
return { sys, stamp: stamp.value };
|
|
56
127
|
}
|
|
57
128
|
/** TR-01: a stamped trade study names >=1 existing CR, each carrying a relation(label: decides) edge. */
|
|
58
129
|
export function tradeDecisionRecorded(graph) {
|
|
59
|
-
const stamped =
|
|
130
|
+
const stamped = stampedOutcome(graph, 'trade');
|
|
60
131
|
if (!stamped)
|
|
61
132
|
return [];
|
|
62
|
-
const { sys
|
|
133
|
+
const { sys } = stamped;
|
|
134
|
+
const crRefs = stamped.stamp.crRefs ?? [];
|
|
63
135
|
const base = { rule_id: 'TR-01', severity: 'warning', context: { element_type: sys.type, element_name: sys.name } };
|
|
64
136
|
if (crRefs.length === 0) {
|
|
65
137
|
return [{
|
|
@@ -82,28 +154,31 @@ export function tradeDecisionRecorded(graph) {
|
|
|
82
154
|
return [];
|
|
83
155
|
});
|
|
84
156
|
}
|
|
85
|
-
/**
|
|
86
|
-
|
|
87
|
-
|
|
157
|
+
/**
|
|
158
|
+
* IR-01 (CR-SM-397): every REQ named by a stamped assumption review exists; an empty list is a
|
|
159
|
+
* legitimate outcome. That the REQ carries a verifying TEST is R-01's finding, not repeated here.
|
|
160
|
+
*/
|
|
161
|
+
export function assumptionReviewRequirementsExist(graph) {
|
|
162
|
+
const stamped = stampedOutcome(graph, 'assumption-review');
|
|
88
163
|
if (!stamped)
|
|
89
164
|
return [];
|
|
90
|
-
const { sys
|
|
165
|
+
const { sys } = stamped;
|
|
91
166
|
const byId = new Map(graph.elements.map(e => [e.id, e]));
|
|
92
|
-
return
|
|
93
|
-
.filter(id => byId.get(id)?.type !== '
|
|
167
|
+
return (stamped.stamp.reqRefs ?? [])
|
|
168
|
+
.filter(id => byId.get(id)?.type !== 'REQ')
|
|
94
169
|
.map(id => ({
|
|
95
170
|
rule_id: 'IR-01',
|
|
96
171
|
severity: 'warning',
|
|
97
172
|
element_id: sys.id,
|
|
98
|
-
message: `Assumption Review names ${id} as a
|
|
99
|
-
fix_hint: 'Point analysisFreshness.assumption-review.
|
|
173
|
+
message: `Assumption Review names ${id} as the requirement of a load-bearing assumption, but no REQ with that id exists`,
|
|
174
|
+
fix_hint: 'Point analysisFreshness.assumption-review.reqRefs at an existing REQ, or add the REQ with its verifying TEST through graph_mutate (se-irr)',
|
|
100
175
|
context: { element_type: sys.type, element_name: sys.name },
|
|
101
176
|
}));
|
|
102
177
|
}
|
|
103
|
-
/** CR-SM-355: the
|
|
178
|
+
/** CR-SM-355: the outcome rules of trade / irr — silent until the analysis is stamped. */
|
|
104
179
|
export const TASK_OUTCOME_RULES = [
|
|
105
|
-
{ id: 'TR-01', name: 'Trade decision recorded as CR', severity: 'warning', evaluate: tradeDecisionRecorded, domain: ['graph'] },
|
|
106
|
-
{ id: 'IR-01', name: 'Assumption review
|
|
180
|
+
{ id: 'TR-01', name: 'Trade decision recorded as CR', severity: 'warning', stage: 7, role: 'analysis', evaluate: tradeDecisionRecorded, domain: ['graph'] },
|
|
181
|
+
{ id: 'IR-01', name: 'Assumption review requirements exist', severity: 'warning', stage: 7, role: 'analysis', evaluate: assumptionReviewRequirementsExist, domain: ['graph'] },
|
|
107
182
|
];
|
|
108
183
|
/** Evaluate the task-outcome rules (CR-SM-355). */
|
|
109
184
|
export function evaluateTaskOutcomeRules(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));
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
import { z } from 'zod/v4';
|
|
18
18
|
import type { OntologyGraph } from './ontology.js';
|
|
19
19
|
import type { RuleSeverity, RuleViolation } from './rules.js';
|
|
20
|
+
import type { RuleRole, RuleStage } from './readiness.js';
|
|
20
21
|
/** Parser facts about one source file (extracted by the executor). */
|
|
21
22
|
export declare const FileFactsSchema: z.ZodObject<{
|
|
22
23
|
exists: z.ZodBoolean;
|
|
@@ -67,6 +68,9 @@ export interface ConformanceRuleDefinition {
|
|
|
67
68
|
id: string;
|
|
68
69
|
name: string;
|
|
69
70
|
severity: RuleSeverity;
|
|
71
|
+
/** CR-SM-395: Stufe und Rolle wie an jeder Katalogregel (`RuleDefinition`). */
|
|
72
|
+
stage: RuleStage;
|
|
73
|
+
role?: RuleRole;
|
|
70
74
|
/**
|
|
71
75
|
* CR-SM-305: die Grundgesamtheit, ueber die die Regel meldet — dieselbe Bedeutung wie an
|
|
72
76
|
* jeder Katalogregel (CR-SM-235), damit RC in `ALL_RULE_DEFS` stehen kann, ohne dass ein
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
import { z } from 'zod/v4';
|
|
18
18
|
import { readTestRefs, readRealRef } from './ontology.js';
|
|
19
19
|
import { CLOSED_STATUS } from './cr-quality-rules.js';
|
|
20
|
+
import { whenDue } from './rule-due.js';
|
|
20
21
|
/** Parser facts about one source file (extracted by the executor). */
|
|
21
22
|
export const FileFactsSchema = z.object({
|
|
22
23
|
/** File exists on disk (repo-relative path). */
|
|
@@ -83,7 +84,7 @@ export const CodeFactsSchema = z.object({
|
|
|
83
84
|
});
|
|
84
85
|
const missingFile = (facts, file) => facts.files[file]?.exists !== true;
|
|
85
86
|
// RC-01: every valid FUNC realRef must resolve — file on disk, symbol declared in
|
|
86
|
-
// it. Presence is R-20's concern (incl.
|
|
87
|
+
// it. Presence is R-20's concern (incl. external/decomposition-parent
|
|
87
88
|
// exemptions, CR-GC-244); RC-01 only judges bindings that exist. `lang:'prompt'`
|
|
88
89
|
// is realized by a skill file — file-exists is the whole binding. A realRef with
|
|
89
90
|
// no `symbol` (CR-228) is also file-only: resolution stops at file-exists.
|
|
@@ -92,7 +93,7 @@ function codeRefMustResolve(graph, facts) {
|
|
|
92
93
|
for (const el of graph.elements) {
|
|
93
94
|
if (el.type !== 'FUNC')
|
|
94
95
|
continue;
|
|
95
|
-
if (el.attributes?.
|
|
96
|
+
if (el.attributes?.external === true)
|
|
96
97
|
continue;
|
|
97
98
|
const parsed = readRealRef(el.attributes);
|
|
98
99
|
if (parsed.state !== 'bound')
|
|
@@ -136,8 +137,6 @@ function testRefMustResolve(graph, facts) {
|
|
|
136
137
|
for (const el of graph.elements) {
|
|
137
138
|
if (el.type !== 'TEST')
|
|
138
139
|
continue;
|
|
139
|
-
if (el.attributes?.concept === true)
|
|
140
|
-
continue;
|
|
141
140
|
const parsed = readTestRefs(el.attributes);
|
|
142
141
|
if (parsed.state !== 'bound')
|
|
143
142
|
continue; // no/invalid binding → R-19 territory
|
|
@@ -172,7 +171,7 @@ function testRefMustResolve(graph, facts) {
|
|
|
172
171
|
}
|
|
173
172
|
// RC-03: every valid SCHEMA realRef must resolve — file on disk, symbol declared
|
|
174
173
|
// in it (CR-211, unified CR-228). Presence (a SCHEMA with NO realRef) is the
|
|
175
|
-
// concern of the R-26 presence rule, not RC-03;
|
|
174
|
+
// concern of the R-26 presence rule, not RC-03; external SCHEMAs are exempt
|
|
176
175
|
// there and here. Severity error, like RC-01: a bound-but-broken schema IS a defect.
|
|
177
176
|
// A symbol-less realRef stops at file-exists. Fires 0× until realRefs exist.
|
|
178
177
|
function schemaRefMustResolve(graph, facts) {
|
|
@@ -180,7 +179,7 @@ function schemaRefMustResolve(graph, facts) {
|
|
|
180
179
|
for (const el of graph.elements) {
|
|
181
180
|
if (el.type !== 'SCHEMA')
|
|
182
181
|
continue;
|
|
183
|
-
if (el.attributes?.
|
|
182
|
+
if (el.attributes?.external === true)
|
|
184
183
|
continue;
|
|
185
184
|
const parsed = readRealRef(el.attributes);
|
|
186
185
|
if (parsed.state !== 'bound')
|
|
@@ -274,7 +273,7 @@ function boundToNonZod(facts, ref) {
|
|
|
274
273
|
// but NONE of their realRef files import AND parse (`.parse`/`.safeParse`) the
|
|
275
274
|
// schema symbol, the modelled validation is missing. Severity warn (the parse may
|
|
276
275
|
// legitimately sit in a framework layer, not the FUNC's own file). Skips when the
|
|
277
|
-
// SCHEMA
|
|
276
|
+
// SCHEMA has no realRef (or a symbol-less one), or no io-connected
|
|
278
277
|
// realized FUNC. `external` is NOT exempt (CR-SM-318, ITEM-2026-064): an external SCHEMA
|
|
279
278
|
// with a realRef is a foreign-API contract held as a Zod schema in OUR code — the
|
|
280
279
|
// boundary to the foreign system is exactly where that parse belongs.
|
|
@@ -284,8 +283,6 @@ function schemaRefMustBeUsed(graph, facts) {
|
|
|
284
283
|
for (const el of graph.elements) {
|
|
285
284
|
if (el.type !== 'SCHEMA')
|
|
286
285
|
continue;
|
|
287
|
-
if (el.attributes?.concept === true)
|
|
288
|
-
continue;
|
|
289
286
|
const parsed = readRealRef(el.attributes);
|
|
290
287
|
if (parsed.state !== 'bound' || parsed.value.symbol === undefined)
|
|
291
288
|
continue;
|
|
@@ -308,7 +305,7 @@ function schemaRefMustBeUsed(graph, facts) {
|
|
|
308
305
|
(actual.length > 0 ? ` — it is parsed in: ${actual.join(', ')}` : ''),
|
|
309
306
|
fix_hint: actual.length > 0
|
|
310
307
|
? `The parse sits in ${actual.join(', ')}: point the FUNC realRef at that file, or move the parse into the modelled FUNC`
|
|
311
|
-
: `Import and call ${ref.symbol}.parse()/.safeParse() in one of the io-connected FUNC's code files
|
|
308
|
+
: `Import and call ${ref.symbol}.parse()/.safeParse() in one of the io-connected FUNC's code files`,
|
|
312
309
|
context: { element_type: el.type, element_name: el.name },
|
|
313
310
|
});
|
|
314
311
|
}
|
|
@@ -490,8 +487,7 @@ function externalRefMustNameDependency(graph, facts) {
|
|
|
490
487
|
const known = new Set(declared);
|
|
491
488
|
const violations = [];
|
|
492
489
|
for (const el of graph.elements) {
|
|
493
|
-
|
|
494
|
-
if (el.attributes?.external !== true || el.attributes?.concept === true)
|
|
490
|
+
if (el.attributes?.external !== true)
|
|
495
491
|
continue;
|
|
496
492
|
const parsed = readRealRef(el.attributes);
|
|
497
493
|
if (parsed.state !== 'bound')
|
|
@@ -523,8 +519,12 @@ function externalRefMustNameDependency(graph, facts) {
|
|
|
523
519
|
// directory;
|
|
524
520
|
// 2. an OPEN file with no node — an open CR the graph cannot see. The missing node has no
|
|
525
521
|
// element to carry the finding, so it anchors at the first SYS (sorted, Gate 5); without
|
|
526
|
-
// a SYS the branch is silent — R-
|
|
522
|
+
// a SYS the branch is silent — R-33 already reports that graph.
|
|
527
523
|
// A node WITHOUT a file is not reported: that is history (archived CRs, pre-docs/cr numbering).
|
|
524
|
+
//
|
|
525
|
+
// CR-SM-396: stage 10 (plan), not 12. The rule needs the ORDERS and the directory, not a binding —
|
|
526
|
+
// it holds always, also in a draft: an open CR file without a node must be reported before the
|
|
527
|
+
// build is opened, not after. Its findings hold the mark Bau like every finding of stages 10–12.
|
|
528
528
|
// ---------------------------------------------------------------------------
|
|
529
529
|
function crNodeMatchesFile(graph, facts) {
|
|
530
530
|
const crFiles = facts.crFiles;
|
|
@@ -585,8 +585,6 @@ function schemaRefMustBeZod(graph, facts) {
|
|
|
585
585
|
for (const el of graph.elements) {
|
|
586
586
|
if (el.type !== 'SCHEMA')
|
|
587
587
|
continue;
|
|
588
|
-
if (el.attributes?.concept === true)
|
|
589
|
-
continue;
|
|
590
588
|
const parsed = readRealRef(el.attributes);
|
|
591
589
|
if (parsed.state !== 'bound' || parsed.value.symbol === undefined)
|
|
592
590
|
continue;
|
|
@@ -620,8 +618,6 @@ function schemaParsedOnlyAtInterface(graph, facts) {
|
|
|
620
618
|
for (const el of graph.elements) {
|
|
621
619
|
if (el.type !== 'SCHEMA')
|
|
622
620
|
continue;
|
|
623
|
-
if (el.attributes?.concept === true)
|
|
624
|
-
continue;
|
|
625
621
|
const parsed = readRealRef(el.attributes);
|
|
626
622
|
if (parsed.state !== 'bound' || parsed.value.symbol === undefined)
|
|
627
623
|
continue;
|
|
@@ -660,7 +656,7 @@ function schemaParsedOnlyAtInterface(graph, facts) {
|
|
|
660
656
|
// boundary measurement are blind to it BY CONSTRUCTION. Measured at sigllm: 0 RC-05
|
|
661
657
|
// findings at 19 % import coverage, 8 at 81 % — the zero was the absence of a question.
|
|
662
658
|
// The cost in the message comes from `importCoverage` (the same resolution), not from a
|
|
663
|
-
// second count. `
|
|
659
|
+
// second count. `external` is exempt — the same exemption as R-20.
|
|
664
660
|
// ABSENT `importEdges` = silence, like RC-05: without an import graph RC-05 has nothing to
|
|
665
661
|
// judge, so there is no blindness to report — and the cost could not be named.
|
|
666
662
|
// ---------------------------------------------------------------------------
|
|
@@ -673,7 +669,7 @@ function modMustBeResolvable(graph, facts) {
|
|
|
673
669
|
.filter(t => t.type === 'allocate' && boundFuncs.has(t.source) && typeOf.get(t.target) === 'MOD')
|
|
674
670
|
.map(t => t.target));
|
|
675
671
|
const blind = graph.elements.filter(e => e.type === 'MOD' &&
|
|
676
|
-
e.attributes?.
|
|
672
|
+
e.attributes?.external !== true &&
|
|
677
673
|
!(typeof e.attributes?.path === 'string' && e.attributes.path.length > 0) &&
|
|
678
674
|
!resolvedByFunc.has(e.id));
|
|
679
675
|
if (blind.length === 0)
|
|
@@ -685,22 +681,31 @@ function modMustBeResolvable(graph, facts) {
|
|
|
685
681
|
severity: 'warning',
|
|
686
682
|
element_id: el.id,
|
|
687
683
|
message: `${el.id} has neither a path nor an allocated FUNC with a realRef — no file can resolve to it, RC-05 is blind here (${cost})`,
|
|
688
|
-
fix_hint: `Set the module's source directory via graph_mutate Format-E \`~ ${el.id} @path <dir>\` — or bind one of its allocated FUNCs (\`~ FUNC-x @realRef {"file":…,"symbol":…}\`)
|
|
684
|
+
fix_hint: `Set the module's source directory via graph_mutate Format-E \`~ ${el.id} @path <dir>\` — or bind one of its allocated FUNCs (\`~ FUNC-x @realRef {"file":…,"symbol":…}\`)`,
|
|
689
685
|
context: { element_type: el.type, element_name: el.name },
|
|
690
686
|
}));
|
|
691
687
|
}
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
{ id: 'RC-
|
|
703
|
-
]
|
|
688
|
+
// CR-SM-395: Stufe 12 — der Abgleich ist faellig, sobald der Bau eroeffnet ist (s. rule-due.ts).
|
|
689
|
+
// Zwei Regeln warten nicht auf den Bau und liegen deshalb auf Stufe 10, der letzten Stufe, deren
|
|
690
|
+
// Faelligkeit die `domain` ist; ihre Befunde halten weiter die Marke Bau:
|
|
691
|
+
// - RC-07 (CR-SM-396) braucht die Auftraege und `docs/cr`, keine Bindung.
|
|
692
|
+
// - RC-05 (CR-SM-397, Entscheidung des Autors 2026-10-06) braucht Module und den Import-Graphen.
|
|
693
|
+
// Ein Modul ist schon ueber `path` aufloesbar, ohne Bindung und ohne Auftrag. Die Stufenregel
|
|
694
|
+
// ergaebe 7 (Modul) — dort hielte eine Warnung KEINE Marke mehr (nur Fehler und Existenz-Regeln
|
|
695
|
+
// halten vor TRR). Stufe 10 ist die benannte Ausnahme: der Befund bleibt ein Befund des Baus.
|
|
696
|
+
// Ohne `importEdges` und ohne aufloesbares Modul schweigt die Regel von selbst.
|
|
697
|
+
export const CODE_CONFORMANCE_RULES = whenDue([
|
|
698
|
+
{ id: 'RC-01', name: 'FUNC realRef resolves to a declared symbol', severity: 'warning', stage: 12, domain: ['FUNC'], evaluate: codeRefMustResolve },
|
|
699
|
+
{ id: 'RC-02', name: 'testRefs entries resolve to runnable tests', severity: 'warning', stage: 12, domain: ['TEST'], evaluate: testRefMustResolve },
|
|
700
|
+
{ id: 'RC-03', name: 'SCHEMA realRef resolves to a declared export', severity: 'warning', stage: 12, domain: ['SCHEMA'], evaluate: schemaRefMustResolve },
|
|
701
|
+
{ id: 'RC-04', name: 'SCHEMA realRef is parsed at its interface', severity: 'warning', stage: 12, domain: ['SCHEMA'], evaluate: schemaRefMustBeUsed },
|
|
702
|
+
{ id: 'RC-05', name: 'cross-module import drift', severity: 'warning', stage: 10, domain: ['MOD'], evaluate: importDriftConformance },
|
|
703
|
+
{ id: 'RC-06', name: 'external realRef names a declared dependency', severity: 'warning', stage: 12, domain: ['FUNC', 'MOD', 'SCHEMA'], evaluate: externalRefMustNameDependency },
|
|
704
|
+
{ id: 'RC-07', name: 'CR node agrees with docs/cr', severity: 'warning', stage: 10, domain: ['CR', 'SYS'], evaluate: crNodeMatchesFile },
|
|
705
|
+
{ id: 'RC-08', name: 'SCHEMA realRef is a Zod schema', severity: 'warning', stage: 12, domain: ['SCHEMA'], evaluate: schemaRefMustBeZod },
|
|
706
|
+
{ id: 'RC-09', name: 'SCHEMA is parsed only at its modelled interface', severity: 'warning', stage: 12, domain: ['SCHEMA'], evaluate: schemaParsedOnlyAtInterface },
|
|
707
|
+
{ id: 'RC-10', name: 'MOD has resolvable files', severity: 'warning', stage: 12, domain: ['MOD'], evaluate: modMustBeResolvable },
|
|
708
|
+
]);
|
|
704
709
|
/** Run all RC rules against a graph + extracted code facts. */
|
|
705
710
|
export function evaluateConformanceRules(graph, facts) {
|
|
706
711
|
return CODE_CONFORMANCE_RULES.flatMap((rule) => rule.evaluate(graph, facts));
|