@sigloch/contracts 10.4.0 → 10.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/se/conformance-rules.d.ts +4 -0
- package/dist/se/conformance-rules.js +63 -0
- package/dist/se/cr-quality-rules.d.ts +5 -0
- package/dist/se/cr-quality-rules.js +5 -2
- package/dist/se/format-e-parser.d.ts +1 -13
- package/dist/se/format-e-parser.js +5 -28
- package/dist/se/grammar-snapshot.d.ts +5 -5
- package/dist/se/grammar-snapshot.js +10 -1
- package/dist/se/index.d.ts +1 -1
- package/dist/se/index.js +1 -1
- package/dist/se/metric-rules.d.ts +47 -4
- package/dist/se/metric-rules.js +140 -85
- package/dist/se/module-crossings.d.ts +17 -0
- package/dist/se/module-crossings.js +58 -4
- package/dist/se/readiness.js +5 -1
- package/dist/se/rule-help.js +9 -1
- package/package.json +1 -1
|
@@ -50,6 +50,10 @@ export declare const CodeFactsSchema: z.ZodObject<{
|
|
|
50
50
|
to: z.ZodString;
|
|
51
51
|
}, z.core.$strip>>>;
|
|
52
52
|
declaredDependencies: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
53
|
+
crFiles: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodEnum<{
|
|
54
|
+
open: "open";
|
|
55
|
+
done: "done";
|
|
56
|
+
}>>>;
|
|
53
57
|
}, z.core.$strip>;
|
|
54
58
|
export type CodeFacts = z.infer<typeof CodeFactsSchema>;
|
|
55
59
|
/** A conformance rule: pure over (graph, facts) — never touches I/O itself. */
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
*/
|
|
17
17
|
import { z } from 'zod/v4';
|
|
18
18
|
import { RealRefSchema, TestRefsSchema } from './ontology.js';
|
|
19
|
+
import { CLOSED_STATUS } from './cr-quality-rules.js';
|
|
19
20
|
/** Parser facts about one source file (extracted by the executor). */
|
|
20
21
|
export const FileFactsSchema = z.object({
|
|
21
22
|
/** File exists on disk (repo-relative path). */
|
|
@@ -59,6 +60,13 @@ export const CodeFactsSchema = z.object({
|
|
|
59
60
|
* every external binding in the graph at once, which is noise, not a finding.
|
|
60
61
|
*/
|
|
61
62
|
declaredDependencies: z.array(z.string()).optional(),
|
|
63
|
+
/**
|
|
64
|
+
* CR id → the directory its file lives in (`docs/cr/open/` or `docs/cr/done/`), for RC-07
|
|
65
|
+
* (CR-SM-329). The directory is where a CR's state is decided; the node only mirrors it.
|
|
66
|
+
* Optional, ABSENT = silence, same reasoning as `declaredDependencies`: a repo without
|
|
67
|
+
* `docs/cr/` was never looked at, it does not "have no CRs".
|
|
68
|
+
*/
|
|
69
|
+
crFiles: z.record(z.string(), z.enum(['open', 'done'])).optional(),
|
|
62
70
|
});
|
|
63
71
|
const missingFile = (facts, file) => facts.files[file]?.exists !== true;
|
|
64
72
|
// RC-01: every valid FUNC realRef must resolve — file on disk, symbol declared in
|
|
@@ -451,6 +459,60 @@ function externalRefMustNameDependency(graph, facts) {
|
|
|
451
459
|
}
|
|
452
460
|
return violations;
|
|
453
461
|
}
|
|
462
|
+
// ---------------------------------------------------------------------------
|
|
463
|
+
// RC-07: a CR node agrees with its file in docs/cr (CR-SM-329).
|
|
464
|
+
//
|
|
465
|
+
// The CR text lives in `docs/cr/{open,done}/`, the graph carries a lean CR node for scope and
|
|
466
|
+
// planning. The DIRECTORY decides a CR's state — moving the file is the close; the node's
|
|
467
|
+
// `status` mirrors it, because CR-R01/02/03 and the milestone readiness read it. Two findings:
|
|
468
|
+
// 1. a node whose closed-ness (CLOSED_STATUS, the same set CR-R01 uses) contradicts the
|
|
469
|
+
// directory;
|
|
470
|
+
// 2. an OPEN file with no node — an open CR the graph cannot see. The missing node has no
|
|
471
|
+
// element to carry the finding, so it anchors at the first SYS (sorted, Gate 5); without
|
|
472
|
+
// a SYS the branch is silent — R-17 already reports that graph.
|
|
473
|
+
// A node WITHOUT a file is not reported: that is history (archived CRs, pre-docs/cr numbering).
|
|
474
|
+
// ---------------------------------------------------------------------------
|
|
475
|
+
function crNodeMatchesFile(graph, facts) {
|
|
476
|
+
const crFiles = facts.crFiles;
|
|
477
|
+
if (crFiles === undefined)
|
|
478
|
+
return []; // extractor never looked — silence, s. CodeFactsSchema
|
|
479
|
+
const crs = graph.elements.filter((e) => e.type === 'CR').sort((a, b) => a.id.localeCompare(b.id));
|
|
480
|
+
const violations = [];
|
|
481
|
+
for (const cr of crs) {
|
|
482
|
+
const dir = crFiles[cr.id];
|
|
483
|
+
if (dir === undefined)
|
|
484
|
+
continue;
|
|
485
|
+
const status = String(cr.attributes?.status ?? '');
|
|
486
|
+
if (CLOSED_STATUS.has(status) === (dir === 'done'))
|
|
487
|
+
continue;
|
|
488
|
+
violations.push({
|
|
489
|
+
rule_id: 'RC-07',
|
|
490
|
+
severity: 'warning',
|
|
491
|
+
element_id: cr.id,
|
|
492
|
+
message: `${cr.id} status '${status || 'none'}' contradicts docs/cr/${dir}/`,
|
|
493
|
+
fix_hint: dir === 'done'
|
|
494
|
+
? 'The file is in done/ — set status done through graph_mutate'
|
|
495
|
+
: 'The file is in open/ — set status open through graph_mutate, or close the CR by moving its file to done/',
|
|
496
|
+
context: { element_type: cr.type, element_name: cr.name },
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
const sys = graph.elements.filter((e) => e.type === 'SYS').sort((a, b) => a.id.localeCompare(b.id))[0];
|
|
500
|
+
if (sys === undefined)
|
|
501
|
+
return violations;
|
|
502
|
+
const nodeIds = new Set(crs.map((c) => c.id));
|
|
503
|
+
const orphans = Object.keys(crFiles).filter((id) => crFiles[id] === 'open' && !nodeIds.has(id)).sort();
|
|
504
|
+
for (const id of orphans) {
|
|
505
|
+
violations.push({
|
|
506
|
+
rule_id: 'RC-07',
|
|
507
|
+
severity: 'warning',
|
|
508
|
+
element_id: sys.id,
|
|
509
|
+
message: `open CR ${id} (docs/cr/open/) has no CR node`,
|
|
510
|
+
fix_hint: `Add a lean CR node ${id} (id, title, no text) with relation edges to its scope through graph_mutate`,
|
|
511
|
+
context: { element_type: sys.type, element_name: sys.name },
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
return violations;
|
|
515
|
+
}
|
|
454
516
|
/** All RC conformance rules — evaluated by executors that can supply CodeFacts. */
|
|
455
517
|
export const CODE_CONFORMANCE_RULES = [
|
|
456
518
|
{ id: 'RC-01', name: 'FUNC realRef resolves to a declared symbol', severity: 'error', domain: ['FUNC'], evaluate: codeRefMustResolve },
|
|
@@ -459,6 +521,7 @@ export const CODE_CONFORMANCE_RULES = [
|
|
|
459
521
|
{ id: 'RC-04', name: 'SCHEMA realRef is parsed at its interface', severity: 'warning', domain: ['SCHEMA'], evaluate: schemaRefMustBeUsed },
|
|
460
522
|
{ id: 'RC-05', name: 'cross-module import drift', severity: 'warning', domain: ['MOD'], evaluate: importDriftConformance },
|
|
461
523
|
{ id: 'RC-06', name: 'external realRef names a declared dependency', severity: 'warning', domain: ['FUNC', 'MOD', 'SCHEMA'], evaluate: externalRefMustNameDependency },
|
|
524
|
+
{ id: 'RC-07', name: 'CR node agrees with docs/cr', severity: 'warning', domain: ['CR', 'SYS'], evaluate: crNodeMatchesFile },
|
|
462
525
|
];
|
|
463
526
|
/** Run all RC rules against a graph + extracted code facts. */
|
|
464
527
|
export function evaluateConformanceRules(graph, facts) {
|
|
@@ -5,5 +5,10 @@
|
|
|
5
5
|
import type { OntologyGraph } from './ontology.js';
|
|
6
6
|
import type { RuleDefinition, RuleViolation } from './rules.js';
|
|
7
7
|
import type { MetricPolicy } from './policy.js';
|
|
8
|
+
/**
|
|
9
|
+
* Abgeschlossen heisst: nicht mehr steuerbar. Alles andere ist Grundgesamtheit.
|
|
10
|
+
* Exportiert fuer RC-07 (CR-SM-329), das dieselbe Frage gegen `docs/cr/done/` stellt.
|
|
11
|
+
*/
|
|
12
|
+
export declare const CLOSED_STATUS: ReadonlySet<string>;
|
|
8
13
|
export declare const CR_RULES: RuleDefinition[];
|
|
9
14
|
export declare function evaluateCRRules(graph: OntologyGraph, policy: MetricPolicy): RuleViolation[];
|
|
@@ -33,8 +33,11 @@
|
|
|
33
33
|
// ---------------------------------------------------------------------------
|
|
34
34
|
/** Elementtypen, die einen Aenderungsumfang darstellen — MS gehoert bewusst nicht dazu. */
|
|
35
35
|
const SCOPE_TYPES = new Set(['FUNC', 'MOD', 'SCHEMA', 'REQ', 'UC']);
|
|
36
|
-
/**
|
|
37
|
-
|
|
36
|
+
/**
|
|
37
|
+
* Abgeschlossen heisst: nicht mehr steuerbar. Alles andere ist Grundgesamtheit.
|
|
38
|
+
* Exportiert fuer RC-07 (CR-SM-329), das dieselbe Frage gegen `docs/cr/done/` stellt.
|
|
39
|
+
*/
|
|
40
|
+
export const CLOSED_STATUS = new Set(['done', 'dropped', 'rejected']);
|
|
38
41
|
function crMustTrack(graph) {
|
|
39
42
|
const typeOf = new Map(graph.elements.map(e => [e.id, e.type]));
|
|
40
43
|
const crs = graph.elements.filter(e => {
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* @sigloch/contracts/se
|
|
11
11
|
*/
|
|
12
12
|
import { ElementType, TraceType } from './ontology.js';
|
|
13
|
-
import type { OntologyGraph, AttributeSpec
|
|
13
|
+
import type { OntologyGraph, AttributeSpec } from './ontology.js';
|
|
14
14
|
export interface FormatEOperation {
|
|
15
15
|
type: 'add_node' | 'remove_node' | 'update_node' | 'add_edge' | 'remove_edge' | 'strict_add_node' | 'strict_add_edge';
|
|
16
16
|
semanticId: string;
|
|
@@ -68,18 +68,6 @@ export interface ParseFormatEOptions {
|
|
|
68
68
|
* silent skip.
|
|
69
69
|
*/
|
|
70
70
|
resolveType?: (uid: string) => ElementType | undefined;
|
|
71
|
-
/**
|
|
72
|
-
* CR-SM-266 B: die Kinds eines uid, den dieser Text nicht deklariert. Seit die
|
|
73
|
-
* satisfy-Patterns ein `where` tragen, entscheidet nicht mehr das Typ-Paar allein — ein
|
|
74
|
-
* `MOD -satisfy-> REQ` ist nur gueltig, wenn das REQ strukturelle Kinds traegt.
|
|
75
|
-
*
|
|
76
|
-
* Ohne diesen Resolver sieht der Parser die Kinds eines BESTEHENDEN Knotens nicht und lehnt
|
|
77
|
-
* solche Kanten ab. Das ist Absicht: fehlschlagen und es sagen, nicht raten und durchlassen
|
|
78
|
-
* — dieselbe Entscheidung wie bei `resolveType` ("Without it such a diff is an error, never
|
|
79
|
-
* a silent skip"). Knoten, die DIESER Text anlegt, brauchen ihn nicht; ihre Kinds stehen als
|
|
80
|
-
* `@kinds` in denselben Zeilen.
|
|
81
|
-
*/
|
|
82
|
-
resolveKinds?: (uid: string) => readonly ReqKind[] | undefined;
|
|
83
71
|
}
|
|
84
72
|
/** Parse a Format E text block into validated operations. */
|
|
85
73
|
export declare function parseFormatE(input: string, options?: ParseFormatEOptions): FormatEDiff;
|
|
@@ -10,7 +10,6 @@
|
|
|
10
10
|
* @sigloch/contracts/se
|
|
11
11
|
*/
|
|
12
12
|
import { ElementType, ELEMENT_ATTRIBUTES, TraceType, normalizeReqKinds } from './ontology.js';
|
|
13
|
-
import { isValidTrace } from './meta-model.js';
|
|
14
13
|
// ---------------------------------------------------------------------------
|
|
15
14
|
// Extraction
|
|
16
15
|
// ---------------------------------------------------------------------------
|
|
@@ -110,23 +109,6 @@ export function parseFormatE(input, options = {}) {
|
|
|
110
109
|
/** uid → type, from this text's node sections. */
|
|
111
110
|
const declared = new Map();
|
|
112
111
|
const typeOf = (uid) => declared.get(uid) ?? options.resolveType?.(uid);
|
|
113
|
-
/**
|
|
114
|
-
* CR-SM-266 B: die Kinds eines uid — erst aus DIESEM Text, dann aus dem Store.
|
|
115
|
-
*
|
|
116
|
-
* `kinds` reist in Format E als `@kinds a,b` und wird erst vom Konsumenten aufs
|
|
117
|
-
* Top-Level-Feld gehoben (graph-api-core, CR-195d). Hier steht es also noch in
|
|
118
|
-
* `op.attributes.kinds`, und genau dort wird es gelesen — sonst saehe eine Mutation, die
|
|
119
|
-
* REQ und satisfy-Kante in EINEM Block anlegt, die eigenen Kinds nicht. Seit CR-SM-320
|
|
120
|
-
* liegt es dort bereits als Liste (s. Attribut-Zeile unten), also ohne eigene Normalisierung.
|
|
121
|
-
*/
|
|
122
|
-
const kindsOf = (uid) => {
|
|
123
|
-
for (const op of operations) {
|
|
124
|
-
if ('elementType' in op && op.semanticId === uid && Array.isArray(op.attributes?.kinds)) {
|
|
125
|
-
return op.attributes.kinds;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
return options.resolveKinds?.(uid);
|
|
129
|
-
};
|
|
130
112
|
for (const rawLine of input.split('\n')) {
|
|
131
113
|
const line = rawLine.trim();
|
|
132
114
|
if (!line || line.startsWith('//') || line.startsWith('#!'))
|
|
@@ -188,7 +170,7 @@ export function parseFormatE(input, options = {}) {
|
|
|
188
170
|
if (isEdgeLine || section === 'edges') {
|
|
189
171
|
const edgeMatch = isEdgeLine ? EDGE_RE.exec(line) : null;
|
|
190
172
|
if (edgeMatch) {
|
|
191
|
-
parseEdge(edgeMatch, typeOf,
|
|
173
|
+
parseEdge(edgeMatch, typeOf, operations, errors);
|
|
192
174
|
}
|
|
193
175
|
else {
|
|
194
176
|
errors.push(`Invalid edge line: "${line}"`);
|
|
@@ -248,7 +230,7 @@ function parseNode(m, elementType, ops, errors) {
|
|
|
248
230
|
ops.push({ type: 'add_node', semanticId: id, elementType, description: descr });
|
|
249
231
|
}
|
|
250
232
|
}
|
|
251
|
-
function parseEdge(m, typeOf,
|
|
233
|
+
function parseEdge(m, typeOf, ops, errors) {
|
|
252
234
|
const opChar = m[1] || '+';
|
|
253
235
|
const sourceId = m[2];
|
|
254
236
|
const traceType = m[3];
|
|
@@ -277,15 +259,10 @@ function parseEdge(m, typeOf, kindsOf, ops, errors) {
|
|
|
277
259
|
errors.push(`Cannot resolve type of "${targetId}" — not declared under a "### <TYPE>" section and no resolveType provided`);
|
|
278
260
|
continue;
|
|
279
261
|
}
|
|
280
|
-
// CR-148:
|
|
262
|
+
// CR-148: normalize the trace type (e.g. FLOW→SCHEMA io → relation).
|
|
263
|
+
// CR-SM-325: no legality verdict while parsing — the parser knows neither label nor the
|
|
264
|
+
// kinds of existing nodes. The write path judges with the one rule (gate R-18, GraphService).
|
|
281
265
|
const resolvedTraceType = normalizeTraceType(srcType, tgtType, traceType);
|
|
282
|
-
if (!isValidTrace({
|
|
283
|
-
source: srcType, target: tgtType, type: resolvedTraceType,
|
|
284
|
-
sourceKinds: kindsOf(sourceId), targetKinds: kindsOf(targetId),
|
|
285
|
-
})) {
|
|
286
|
-
errors.push(`Meta-model violation: ${srcType} -${traceType}-> ${tgtType} is not valid`);
|
|
287
|
-
continue;
|
|
288
|
-
}
|
|
289
266
|
ops.push({
|
|
290
267
|
type: edgeType,
|
|
291
268
|
semanticId: `${sourceId}->${targetId}`,
|
|
@@ -13,16 +13,16 @@ export declare const GRAMMAR_SNAPSHOT: {
|
|
|
13
13
|
readonly versions: {
|
|
14
14
|
readonly ontology: "9.0.0";
|
|
15
15
|
readonly metaModel: "5.1.0";
|
|
16
|
-
readonly rules: "
|
|
16
|
+
readonly rules: "28.1.0";
|
|
17
17
|
};
|
|
18
18
|
readonly elementTypes: readonly ["ACTOR", "CR", "FCHAIN", "FLOW", "FUNC", "MOD", "MS", "REQ", "SCHEMA", "SYS", "TEST", "UC"];
|
|
19
19
|
readonly traceTypes: readonly ["allocate", "compose", "io", "relation", "satisfy", "verify"];
|
|
20
20
|
readonly patterns: readonly ["ACTOR -io-> FLOW", "CR -relation-> FUNC", "CR -relation-> MOD", "CR -relation-> MS", "CR -relation-> REQ", "CR -relation-> UC", "FCHAIN -compose-> FUNC [1..*]", "FCHAIN -satisfy-> REQ", "FLOW -io-> ACTOR", "FLOW -io-> FUNC", "FLOW -relation-> SCHEMA [1]", "FUNC -allocate-> MOD [0..1]", "FUNC -compose-> FUNC [0..*]", "FUNC -io-> FLOW", "FUNC -satisfy-> REQ where target.kinds in {functional,postcondition,precondition}", "MOD -compose-> MOD [0..*]", "MOD -satisfy-> REQ where target.kinds in {mitigation,non-functional,risk}", "MS -compose-> FUNC", "MS -compose-> REQ", "MS -compose-> UC", "MS -relation-> MS label=depends-on", "REQ -compose-> REQ [0..*]", "SYS -compose-> MOD [0..*]", "SYS -compose-> REQ [0..*]", "SYS -compose-> SYS [0..*]", "SYS -compose-> UC [1..*]", "SYS -satisfy-> REQ where target.kinds in {mitigation,non-functional,risk}", "TEST -verify-> REQ", "TEST -verify-> SCHEMA", "UC -compose-> FCHAIN [1..*]", "UC -compose-> REQ [1..*]"];
|
|
21
21
|
readonly elementColumns: readonly ["OntologyElement.attributes", "OntologyElement.created_at", "OntologyElement.description", "OntologyElement.id", "OntologyElement.kinds", "OntologyElement.method", "OntologyElement.name", "OntologyElement.status", "OntologyElement.type", "OntologyElement.updated_at", "Trace.attributes", "Trace.created_at", "Trace.label", "Trace.source", "Trace.target", "Trace.type", "Trace.verified_at", "Trace.weight"];
|
|
22
22
|
readonly attributes: readonly ["CR.rationale: string", "CR.spike: boolean", "CR.status: enum", "FLOW.protocol: string", "FLOW.qos: string", "FUNC.concept: boolean", "FUNC.external: boolean", "FUNC.measuredMs: number", "FUNC.realRef: object", "FUNC.safety_relevant: boolean", "FUNC.sourceFile: string", "FUNC.timingBudgetMs: number", "MOD.concept: boolean", "MOD.external: boolean", "MOD.kind: string", "MOD.path: string", "MOD.realRef: object", "REQ.detection: number", "REQ.occurrence: number", "REQ.severity: number", "SCHEMA.concept: boolean", "SCHEMA.contract: string", "SCHEMA.external: boolean", "SCHEMA.realRef: object", "TEST.concept: boolean", "TEST.sourceFile: string", "TEST.testRefs: array", "UC.operatingMode: string"];
|
|
23
|
-
readonly rules: readonly ["AF-01 (warning) domain=[graph]", "AF-02 (warning) domain=[graph]", "AF-03 (warning) domain=[graph]", "AF-04 (warning) domain=[graph]", "AF-05 (warning) domain=[graph]", "BQ-01 (warning) domain=[REQ]", "BQ-02 (warning) domain=[REQ]", "BQ-04 (warning) domain=[REQ]", "BQ-06 (warning) domain=[REQ]", "BQ-07 (warning) domain=[REQ]", "BW-02 (warning) domain=[FUNC]", "CL-01 (warning) domain=[ACTOR]", "CR-01 (warning) domain=[MOD]", "CR-R01 (error) domain=[CR]", "CR-R02 (error) domain=[CR]", "CR-R03 (warning) domain=[all]", "FC-02 (warning) domain=[UC]", "FC-03 (warning) domain=[FUNC]", "FC-04 (warning) domain=[FCHAIN]", "FM-01 (warning) domain=[REQ]", "FM-02 (warning) domain=[REQ]", "FM-03 (error) domain=[REQ]", "IO-01 (warning) domain=[FUNC]", "IO-02 (error) domain=[FLOW]", "MS-01 (warning) domain=[MS]", "MS-02 (error) domain=[MS]", "MS-03 (info) domain=[CR]", "MT-01 (warning) domain=[MOD]", "MT-02 (info) domain=[MOD]", "ND-01 (error) domain=[FUNC]", "ND-02 (error) domain=[SCHEMA]", "NFR-01 (warning) domain=[FCHAIN,FUNC,MOD]", "R-01 (error) domain=[REQ]", "R-02 (warning) domain=[FUNC]", "R-04 (warning) domain=[MOD]", "R-05 (warning) domain=[TEST]", "R-08 (error) domain=[all]", "R-10 (warning) domain=[FLOW]", "R-12 (warning) domain=[FUNC]", "R-15 (warning) domain=[FCHAIN]", "R-16 (warning) domain=[ACTOR]", "R-17 (warning) domain=[SYS]", "R-18 (error) domain=[all]", "R-19 (warning) domain=[TEST]", "R-20 (warning) domain=[FUNC]", "R-21 (warning) domain=[FCHAIN,FUNC]", "R-22 (warning) domain=[FUNC]", "R-23 (warning) domain=[MOD]", "R-26 (warning) domain=[SCHEMA]", "R-29 (error) domain=[TEST]", "R-30 (warning) domain=[FUNC]", "R-31 (warning) domain=[FUNC]", "R-32 (warning) domain=[SCHEMA]", "RC-01 (error) domain=[FUNC]", "RC-02 (error) domain=[TEST]", "RC-03 (error) domain=[SCHEMA]", "RC-04 (warning) domain=[SCHEMA]", "RC-05 (warning) domain=[MOD]", "RC-06 (warning) domain=[FUNC,MOD,SCHEMA]", "RD-01 (warning) domain=[REQ]", "RD-02 (warning) domain=[REQ]", "RD-03 (info) domain=[REQ]", "RD-04 (warning) domain=[FUNC,MOD,SYS]", "RD-05 (warning) domain=[FUNC,MOD,SYS]", "SC-02 (warning) domain=[SCHEMA]", "UC-01 (error) domain=[UC]", "UC-02 (error) domain=[UC]", "UC-03 (warning) domain=[UC]", "UC-04 (warning) domain=[UC]", "UC-05 (info) domain=[UC]", "UC-06 (info) domain=[UC]", "VR-01 (info) domain=[TEST]"];
|
|
24
|
-
readonly conformanceRules: readonly ["RC-01 (error)", "RC-02 (error)", "RC-03 (error)", "RC-04 (warning)", "RC-05 (warning)", "RC-06 (warning)"];
|
|
23
|
+
readonly rules: readonly ["AF-01 (warning) domain=[graph]", "AF-02 (warning) domain=[graph]", "AF-03 (warning) domain=[graph]", "AF-04 (warning) domain=[graph]", "AF-05 (warning) domain=[graph]", "BQ-01 (warning) domain=[REQ]", "BQ-02 (warning) domain=[REQ]", "BQ-04 (warning) domain=[REQ]", "BQ-06 (warning) domain=[REQ]", "BQ-07 (warning) domain=[REQ]", "BW-02 (warning) domain=[FUNC]", "CL-01 (warning) domain=[ACTOR]", "CR-01 (warning) domain=[MOD]", "CR-R01 (error) domain=[CR]", "CR-R02 (error) domain=[CR]", "CR-R03 (warning) domain=[all]", "FC-02 (warning) domain=[UC]", "FC-03 (warning) domain=[FUNC]", "FC-04 (warning) domain=[FCHAIN]", "FM-01 (warning) domain=[REQ]", "FM-02 (warning) domain=[REQ]", "FM-03 (error) domain=[REQ]", "IO-01 (warning) domain=[FUNC]", "IO-02 (error) domain=[FLOW]", "MS-01 (warning) domain=[MS]", "MS-02 (error) domain=[MS]", "MS-03 (info) domain=[CR]", "MT-01 (warning) domain=[MOD]", "MT-02 (info) domain=[MOD]", "MT-04 (info) domain=[FUNC]", "ND-01 (error) domain=[FUNC]", "ND-02 (error) domain=[SCHEMA]", "NFR-01 (warning) domain=[FCHAIN,FUNC,MOD]", "R-01 (error) domain=[REQ]", "R-02 (warning) domain=[FUNC]", "R-04 (warning) domain=[MOD]", "R-05 (warning) domain=[TEST]", "R-08 (error) domain=[all]", "R-10 (warning) domain=[FLOW]", "R-12 (warning) domain=[FUNC]", "R-15 (warning) domain=[FCHAIN]", "R-16 (warning) domain=[ACTOR]", "R-17 (warning) domain=[SYS]", "R-18 (error) domain=[all]", "R-19 (warning) domain=[TEST]", "R-20 (warning) domain=[FUNC]", "R-21 (warning) domain=[FCHAIN,FUNC]", "R-22 (warning) domain=[FUNC]", "R-23 (warning) domain=[MOD]", "R-26 (warning) domain=[SCHEMA]", "R-29 (error) domain=[TEST]", "R-30 (warning) domain=[FUNC]", "R-31 (warning) domain=[FUNC]", "R-32 (warning) domain=[SCHEMA]", "RC-01 (error) domain=[FUNC]", "RC-02 (error) domain=[TEST]", "RC-03 (error) domain=[SCHEMA]", "RC-04 (warning) domain=[SCHEMA]", "RC-05 (warning) domain=[MOD]", "RC-06 (warning) domain=[FUNC,MOD,SCHEMA]", "RC-07 (warning) domain=[CR,SYS]", "RD-01 (warning) domain=[REQ]", "RD-02 (warning) domain=[REQ]", "RD-03 (info) domain=[REQ]", "RD-04 (warning) domain=[FUNC,MOD,SYS]", "RD-05 (warning) domain=[FUNC,MOD,SYS]", "SC-02 (warning) domain=[SCHEMA]", "UC-01 (error) domain=[UC]", "UC-02 (error) domain=[UC]", "UC-03 (warning) domain=[UC]", "UC-04 (warning) domain=[UC]", "UC-05 (info) domain=[UC]", "UC-06 (info) domain=[UC]", "VR-01 (info) domain=[TEST]"];
|
|
24
|
+
readonly conformanceRules: readonly ["RC-01 (error)", "RC-02 (error)", "RC-03 (error)", "RC-04 (warning)", "RC-05 (warning)", "RC-06 (warning)", "RC-07 (warning)"];
|
|
25
25
|
readonly policy: readonly ["apTable = null", "boundaryWidth = {warning:5}", "criticality = {infrastructure:3}", "crossingFlows = {warning:3}", "decompositionBreadth = {min:3,warning:9}", "instability = null", "lcom4 = {info:4,warning:6}", "riskRpn = 100"];
|
|
26
|
-
readonly ruleHelp: readonly ["AF-01", "AF-02", "AF-03", "AF-04", "AF-05", "BQ-01", "BQ-02", "BQ-04", "BQ-06", "BQ-07", "BW-02", "CL-01", "CR-01", "CR-R01", "CR-R02", "CR-R03", "FC-02", "FC-03", "FC-04", "FM-01", "FM-02", "FM-03", "IO-01", "IO-02", "MS-01", "MS-02", "MS-03", "MT-01", "MT-02", "ND-01", "ND-02", "NFR-01", "R-01", "R-02", "R-04", "R-05", "R-08", "R-10", "R-12", "R-15", "R-16", "R-17", "R-18", "R-19", "R-20", "R-21", "R-22", "R-23", "R-26", "R-29", "R-30", "R-31", "R-32", "RC-01", "RC-02", "RC-03", "RC-04", "RC-05", "RC-06", "RD-01", "RD-02", "RD-03", "RD-04", "RD-05", "SC-02", "UC-01", "UC-02", "UC-03", "UC-04", "UC-05", "UC-06", "VR-01"];
|
|
27
|
-
readonly exports: readonly ["AF_RULES", "ALL_RULE_DEFS", "AO_RULES", "ActionPriority", "AnalysisArtifactId", "AnalysisFreshnessStampSchema", "ApTableSchema", "BOUNDED_PATTERNS", "BQ_RULES", "CODE_CONFORMANCE_RULES", "CR_RULES", "CodeFactsSchema", "DEFAULT_METRIC_POLICY", "DIMENSION_READINESS_DELTA_NAME", "DIMENSION_READINESS_NAME", "ELEMENT_ATTRIBUTES", "ELEMENT_DESCRIPTIONS", "ElementType", "ElementUid", "FC_RULES", "FM_RULES", "FileFactsSchema", "ImportEdgeSchema", "MAX_SLUG_LENGTH", "META_MODEL_VERSION", "MODELING_ELEMENT_TYPES", "MT_RULES", "MetricPolicySchema", "ND_RULES", "ONTOLOGY_VERSION", "OntologyElement", "OntologyGraph", "PHASE_READINESS_NAME", "PhaseGate", "READINESS_SCORED_PROFILES", "REQUIRED_PATTERNS", "RULES_VERSION", "RULE_HELP", "RULE_TO_DIMENSION", "RULE_TO_PHASE", "ReadinessDimension", "ReadinessReport", "ReadinessScore", "RealRefSchema", "RepoRelativePathSchema", "ReqKind", "RuleSeverity", "RuleViolation", "SC_RULES", "TRACE_PATTERNS", "TestRefSchema", "TestRefsSchema", "TestResult", "Trace", "TraceType", "UC_RULES", "V3_RULES", "VIEW_RULES", "VerificationMethod", "ViolationCandidate", "ViolationContext", "actionPriority", "allocationCohesion", "apMethod", "attributeTypeOf", "bq01Unambiguous", "bq02Verifiable", "bq04Necessary", "bq06Conforming", "bq07Complete", "bw02WhiteboxWidth", "cl01ConopsCompleteness", "cr01CrossingFlowCount", "crossingContractCount", "decomposedFuncs", "evaluateAFRules", "evaluateAORules", "evaluateAllRules", "evaluateBQRules", "evaluateCRRules", "evaluateConformanceRules", "evaluateFCRules", "evaluateFMRules", "evaluateMTRules", "evaluateNDRules", "evaluateRules", "evaluateSCRules", "evaluateUCRules", "evaluateViewRules", "extractFormatE", "fc02LeafUcHasFchain", "fc03FchainFlat", "fc04ActorBounded", "fm01MissingFmeaAttributes", "fm02MissingMitigation", "fm03HighRiskUnverified", "funcSimilarity", "functionCriticality", "getRuleDefsForProfile", "hydrateAttrValue", "importCoverage", "indexOf", "io01CrossModuleCompleteness", "isElementUid", "isValidTrace", "jaccard", "maxOccurs", "minOccurs", "moduleCrossings", "moduleMetrics", "mt01Instability", "mt02Lcom4", "nd01FuncNearDuplicate", "nd02SchemaNearDuplicate", "nfr01BudgetOvershoot", "normalizeReqKinds", "pairsAbove", "parseElementUid", "parseFormatE", "sc02IsReferenced", "schemaSimilarity", "serializeToFormatE", "setBQ04SimilarityMatrix", "subtreeFuncs", "toElementUid", "toEvaluableGraph", "tokens", "traceRejection", "tryParseElementUid", "uc01HasRequirements", "uc02HasActor", "uc03HasScenario", "uc04GoalDefined", "uc05HasPostcondition", "uc06HasPrecondition", "vr01TestNoResult", "whiteboxContractCount"];
|
|
26
|
+
readonly ruleHelp: readonly ["AF-01", "AF-02", "AF-03", "AF-04", "AF-05", "BQ-01", "BQ-02", "BQ-04", "BQ-06", "BQ-07", "BW-02", "CL-01", "CR-01", "CR-R01", "CR-R02", "CR-R03", "FC-02", "FC-03", "FC-04", "FM-01", "FM-02", "FM-03", "IO-01", "IO-02", "MS-01", "MS-02", "MS-03", "MT-01", "MT-02", "MT-04", "ND-01", "ND-02", "NFR-01", "R-01", "R-02", "R-04", "R-05", "R-08", "R-10", "R-12", "R-15", "R-16", "R-17", "R-18", "R-19", "R-20", "R-21", "R-22", "R-23", "R-26", "R-29", "R-30", "R-31", "R-32", "RC-01", "RC-02", "RC-03", "RC-04", "RC-05", "RC-06", "RC-07", "RD-01", "RD-02", "RD-03", "RD-04", "RD-05", "SC-02", "UC-01", "UC-02", "UC-03", "UC-04", "UC-05", "UC-06", "VR-01"];
|
|
27
|
+
readonly exports: readonly ["AF_RULES", "ALL_RULE_DEFS", "AO_RULES", "ActionPriority", "AnalysisArtifactId", "AnalysisFreshnessStampSchema", "ApTableSchema", "BOUNDED_PATTERNS", "BQ_RULES", "CLOSED_STATUS", "CODE_CONFORMANCE_RULES", "CR_RULES", "CodeFactsSchema", "DEFAULT_METRIC_POLICY", "DIMENSION_READINESS_DELTA_NAME", "DIMENSION_READINESS_NAME", "ELEMENT_ATTRIBUTES", "ELEMENT_DESCRIPTIONS", "ElementType", "ElementUid", "FC_RULES", "FM_RULES", "FileFactsSchema", "ImportEdgeSchema", "MAX_SLUG_LENGTH", "META_MODEL_VERSION", "MODELING_ELEMENT_TYPES", "MT_RULES", "MetricPolicySchema", "ND_RULES", "ONTOLOGY_VERSION", "OntologyElement", "OntologyGraph", "PHASE_READINESS_NAME", "PhaseGate", "READINESS_SCORED_PROFILES", "REQUIRED_PATTERNS", "RULES_VERSION", "RULE_HELP", "RULE_TO_DIMENSION", "RULE_TO_PHASE", "ReadinessDimension", "ReadinessReport", "ReadinessScore", "RealRefSchema", "RepoRelativePathSchema", "ReqKind", "RuleSeverity", "RuleViolation", "SC_RULES", "TRACE_PATTERNS", "TestRefSchema", "TestRefsSchema", "TestResult", "Trace", "TraceType", "UC_RULES", "V3_RULES", "VIEW_RULES", "VerificationMethod", "ViolationCandidate", "ViolationContext", "actionPriority", "allocationCohesion", "apMethod", "attributeTypeOf", "boxContracts", "bq01Unambiguous", "bq02Verifiable", "bq04Necessary", "bq06Conforming", "bq07Complete", "bw02WhiteboxWidth", "cl01ConopsCompleteness", "cr01CrossingFlowCount", "crossingContractCount", "decomposedFuncs", "evaluateAFRules", "evaluateAORules", "evaluateAllRules", "evaluateBQRules", "evaluateCRRules", "evaluateConformanceRules", "evaluateFCRules", "evaluateFMRules", "evaluateMTRules", "evaluateNDRules", "evaluateRules", "evaluateSCRules", "evaluateUCRules", "evaluateViewRules", "extractFormatE", "fc02LeafUcHasFchain", "fc03FchainFlat", "fc04ActorBounded", "fm01MissingFmeaAttributes", "fm02MissingMitigation", "fm03HighRiskUnverified", "funcSimilarity", "funcSubtree", "functionCriticality", "getRuleDefsForProfile", "hydrateAttrValue", "importCoverage", "indexOf", "io01CrossModuleCompleteness", "isElementUid", "isValidTrace", "jaccard", "maxOccurs", "minOccurs", "moduleCrossings", "moduleMetrics", "mt01Instability", "mt02Lcom4", "mt04WhiteboxLcom4", "nd01FuncNearDuplicate", "nd02SchemaNearDuplicate", "nfr01BudgetOvershoot", "normalizeReqKinds", "pairsAbove", "parseElementUid", "parseFormatE", "sc02IsReferenced", "schemaSimilarity", "serializeToFormatE", "setBQ04SimilarityMatrix", "subtreeFuncs", "toElementUid", "toEvaluableGraph", "tokens", "traceRejection", "tryParseElementUid", "uc01HasRequirements", "uc02HasActor", "uc03HasScenario", "uc04GoalDefined", "uc05HasPostcondition", "uc06HasPrecondition", "vr01TestNoResult", "whiteboxContractCount"];
|
|
28
28
|
};
|
|
@@ -13,7 +13,7 @@ export const GRAMMAR_SNAPSHOT = {
|
|
|
13
13
|
versions: {
|
|
14
14
|
ontology: "9.0.0",
|
|
15
15
|
metaModel: "5.1.0",
|
|
16
|
-
rules: "
|
|
16
|
+
rules: "28.1.0",
|
|
17
17
|
},
|
|
18
18
|
elementTypes: [
|
|
19
19
|
"ACTOR",
|
|
@@ -150,6 +150,7 @@ export const GRAMMAR_SNAPSHOT = {
|
|
|
150
150
|
"MS-03 (info) domain=[CR]",
|
|
151
151
|
"MT-01 (warning) domain=[MOD]",
|
|
152
152
|
"MT-02 (info) domain=[MOD]",
|
|
153
|
+
"MT-04 (info) domain=[FUNC]",
|
|
153
154
|
"ND-01 (error) domain=[FUNC]",
|
|
154
155
|
"ND-02 (error) domain=[SCHEMA]",
|
|
155
156
|
"NFR-01 (warning) domain=[FCHAIN,FUNC,MOD]",
|
|
@@ -180,6 +181,7 @@ export const GRAMMAR_SNAPSHOT = {
|
|
|
180
181
|
"RC-04 (warning) domain=[SCHEMA]",
|
|
181
182
|
"RC-05 (warning) domain=[MOD]",
|
|
182
183
|
"RC-06 (warning) domain=[FUNC,MOD,SCHEMA]",
|
|
184
|
+
"RC-07 (warning) domain=[CR,SYS]",
|
|
183
185
|
"RD-01 (warning) domain=[REQ]",
|
|
184
186
|
"RD-02 (warning) domain=[REQ]",
|
|
185
187
|
"RD-03 (info) domain=[REQ]",
|
|
@@ -201,6 +203,7 @@ export const GRAMMAR_SNAPSHOT = {
|
|
|
201
203
|
"RC-04 (warning)",
|
|
202
204
|
"RC-05 (warning)",
|
|
203
205
|
"RC-06 (warning)",
|
|
206
|
+
"RC-07 (warning)",
|
|
204
207
|
],
|
|
205
208
|
policy: [
|
|
206
209
|
"apTable = null",
|
|
@@ -242,6 +245,7 @@ export const GRAMMAR_SNAPSHOT = {
|
|
|
242
245
|
"MS-03",
|
|
243
246
|
"MT-01",
|
|
244
247
|
"MT-02",
|
|
248
|
+
"MT-04",
|
|
245
249
|
"ND-01",
|
|
246
250
|
"ND-02",
|
|
247
251
|
"NFR-01",
|
|
@@ -272,6 +276,7 @@ export const GRAMMAR_SNAPSHOT = {
|
|
|
272
276
|
"RC-04",
|
|
273
277
|
"RC-05",
|
|
274
278
|
"RC-06",
|
|
279
|
+
"RC-07",
|
|
275
280
|
"RD-01",
|
|
276
281
|
"RD-02",
|
|
277
282
|
"RD-03",
|
|
@@ -296,6 +301,7 @@ export const GRAMMAR_SNAPSHOT = {
|
|
|
296
301
|
"ApTableSchema",
|
|
297
302
|
"BOUNDED_PATTERNS",
|
|
298
303
|
"BQ_RULES",
|
|
304
|
+
"CLOSED_STATUS",
|
|
299
305
|
"CODE_CONFORMANCE_RULES",
|
|
300
306
|
"CR_RULES",
|
|
301
307
|
"CodeFactsSchema",
|
|
@@ -352,6 +358,7 @@ export const GRAMMAR_SNAPSHOT = {
|
|
|
352
358
|
"allocationCohesion",
|
|
353
359
|
"apMethod",
|
|
354
360
|
"attributeTypeOf",
|
|
361
|
+
"boxContracts",
|
|
355
362
|
"bq01Unambiguous",
|
|
356
363
|
"bq02Verifiable",
|
|
357
364
|
"bq04Necessary",
|
|
@@ -384,6 +391,7 @@ export const GRAMMAR_SNAPSHOT = {
|
|
|
384
391
|
"fm02MissingMitigation",
|
|
385
392
|
"fm03HighRiskUnverified",
|
|
386
393
|
"funcSimilarity",
|
|
394
|
+
"funcSubtree",
|
|
387
395
|
"functionCriticality",
|
|
388
396
|
"getRuleDefsForProfile",
|
|
389
397
|
"hydrateAttrValue",
|
|
@@ -399,6 +407,7 @@ export const GRAMMAR_SNAPSHOT = {
|
|
|
399
407
|
"moduleMetrics",
|
|
400
408
|
"mt01Instability",
|
|
401
409
|
"mt02Lcom4",
|
|
410
|
+
"mt04WhiteboxLcom4",
|
|
402
411
|
"nd01FuncNearDuplicate",
|
|
403
412
|
"nd02SchemaNearDuplicate",
|
|
404
413
|
"nfr01BudgetOvershoot",
|
package/dist/se/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
/** Ontology schema version (element types + trace types). */
|
|
6
6
|
export declare const ONTOLOGY_VERSION = "9.0.0";
|
|
7
7
|
/** Rules engine version (validation rules incl. RC conformance). */
|
|
8
|
-
export declare const RULES_VERSION = "
|
|
8
|
+
export declare const RULES_VERSION = "28.1.0";
|
|
9
9
|
/** Meta-model version (trace pattern constraints + format-e parser). */
|
|
10
10
|
export declare const META_MODEL_VERSION = "5.1.0";
|
|
11
11
|
export * from './ontology.js';
|
package/dist/se/index.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
/** Ontology schema version (element types + trace types). */
|
|
6
6
|
export const ONTOLOGY_VERSION = '9.0.0'; // BREAKING (CR-SM-294): das Feld `asil` entfaellt ersatzlos, zusammen mit dem Enum `AsilLevel`. Sein einziger Leser war R-03 (ASIL isolation), und die Messung ueber 19 Familiengraphen (4540 Elemente) zeigt: 0 von 305 MOD tragen `asil` ueberhaupt. Ein Feld ohne Schreiber ist eine Regel, die nie feuert -- Gate 3 der Grammatik-Review. Mit R-03 fallen im selben Zug RT-01, PH-01, R-27 (alle drei lesen `attributes.kind === 'physical'`, 0 von 305 MOD tragen es) und CA-01 (`attributes.requires` / `attributes.capability`, 0/0). Die physische Ebene der Ontologie war nie in Gebrauch: kein Mitglied der Familie modelliert Hardware oder funktionale Sicherheit. `kind`, `requires` und `capability` brauchen KEINEN Schema-Eingriff -- sie lagen im freien `attributes`-Sack und sind nach der Streichung ihrer Leser schlicht unbenutzte Schluessel. Migration: ein Graph, der `asil` traegt, verliert das Feld beim naechsten Reseed; kein Element und keine Kante faellt (0 -> 0 Befunde ueber alle 19 Graphen). Prior: BREAKING (CR-SM-266a): drei Enums schrumpfen, und alle drei sind ENTFERNUNGEN. (1) ElementType 13 -> 12: `SESSION` entfaellt. (2) TraceType 7 -> 6: `produces` entfaellt. Beide trugen genau EIN Pattern (`SESSION -produces-> *`), und die Messung hat es widerlegt: 0 produces-Kanten und 0 SESSION-Knoten in ALLEN 9 aktiven Familie-Graphen, auch im graphcode-Selbstmodell nach 195 gegateten Versionen. Die reale Provenance lebt seit CR-GC-347 in `.graphcode/audit.jsonl` -- audit_trail/audit_stats lesen die DATEI, nie den Graphen; einziger Schreiber war ein toter Pfad (CR-195d in graph-api-core), Leser mit Wirkung keiner. Der urspruengliche CR-Entwurf hielt das Pattern fuer die Provenance-Kante und wollte es BEHALTEN ('Nicht-Befund'); die Zaehlung hat den Nicht-Befund kassiert -- Gate 1 verlangt fuer das Wiederaufmachen einer Entscheidung eine Messung, und hier war sie es. (3) Mit `produces` faellt `TraceCategory` und `Trace.category`. Das ist die wichtigste Haelfte: R-08 und R-18 haben `category === 'audit'` UEBERSPRUNGEN, also haette nach D5 jede Kante mit diesem selbstgesetzten Attribut die komplette Pattern-Matrix UND die Referenzintegritaet umgangen. Ein Attribut, das eine Strukturpruefung abschaltet, ist die Klasse aus CR-SM-263/-257; hier zusaetzlich der billige Zweitweg aus CR-GC-366, nur ohne Kante. Gemessen: 0 von 0 Traces aller 9 Graphen tragen `category` -- die Entfernung nimmt niemandem etwas weg. (4) ReqKind 7 -> 6: `negative` entfaellt. Ein Verbots-REQ ist auch eine Anforderung -- die Implementierung muss Regeln abfragen oder Sicherungen einbauen, das ist `functional`. Der Wert hatte NULL Leser (anders als risk/mitigation -> FM-01..03, pre/postcondition -> UC-05/06, non-functional -> NFR-01) und haette durch die neuen where-Praedikate per AUSLASSUNG erstmals Wirkung bekommen: er stand in keiner der vier Listen und waere nur noch per FCHAIN erfuellbar gewesen. Die sechs verbleibenden Werte partitionieren die where-Listen VOLLSTAENDIG. Migration `negative`: 3 REQs, alle graphcode, Ziel-kind je REQ statt pauschal -- REQ-dashboard-readonly und REQ-readonly-bridge nach `non-functional` (Architektur-Constraint an der Modulgrenze, konsistent mit D2), REQ-no-extraction nach `functional`. Rest-Reject dabei: genau 1 (`FUNC-serve-sse -satisfy-> REQ-readonly-bridge`), aufzuloesen durch Loeschen oder Heben auf die FCHAIN. Prior: BREAKING (CR-GC-366): das Trace-Pattern `FUNC -satisfy-> UC` entfaellt ersatzlos. Es war ein zweiter Weg vom Verhalten zum Use Case neben dem einzig tragenden `UC -compose-> FCHAIN -compose-> FUNC` — und der billigere: eine FUNC konnte einen UC bedienen, ohne in dessen Wirkkette zu stehen, womit IO-01 (FLOW-Pfade je Kettenpaar) und R-21 (Integrationstest je Kette) sie nie sahen. Die Abkuerzung umging genau die Pruefungen, die die Anbindung belastbar machen. Am graphcode-Selbstmodell waren 10 der 26 solchen Kanten reine Dopplung (die FUNC stand ohnehin in der FCHAIN), die restlichen 13 sind der Migrationsanlass. Jeder Graph mit solchen Kanten faellt jetzt R-18 (error) zur Last und muss migriert werden: FUNC in die FCHAIN des UC aufnehmen, satisfy-Kante loeschen. Prior: BREAKING (CR-SM-231b): das knotenweite `testResult` entfaellt — das Ergebnis haengt PRO `testRefs`-Eintrag (`result`, `ranAt`, `evidence`), aus demselben Grund wie `tool`. Ein einzelnes Ergebnis am Knoten konnte bei n Laeufen nicht sagen, welcher gemeint ist, und "einer rot, einer gruen" war gar nicht darstellbar — genau der Zustand, den ein Gate wissen muss. FM-03 liest jetzt "jeder Eintrag passed": "irgendeiner gruen" haette einen gruenen Unit-Lauf einen roten Visual-Lauf verdecken lassen. Ein Eintrag ohne Ergebnis ist nicht bestanden. VR-01 meldet die Dateien ohne Ergebnis statt nur den Knoten. Prior: BREAKING (CR-SM-231): `testRef` (Objekt) entfaellt ersatzlos zugunsten von `testRefs` (Array, min 1) — eine Abnahme, n Testdateien. Kein Union, kein Alias: ein Graph mit dem alten Attribut faellt R-19 zur Last und muss migriert werden. Anlass war ein Zaehl-Audit (537 laufende Tests in 67 Dateien, 14 TEST-Knoten mit je genau einer Datei, 53 Dateien an keine Abnahme gebunden) plus ein belegter Ausweichweg: dieselbe Spec-Datei stand im testRef ZWEIER TEST-Knoten, womit die Relation faktisch n:m war — ein roter Lauf keiner Abnahme mehr eindeutig zuordenbar, der TRR-Gate zaehlte dieselbe Evidenz doppelt. `tool` bleibt PRO EINTRAG: eine Abnahme mischt real die Runner (vitest + playwright). +AttributeSpec.type 'array' (testRefs ist das erste Listen-Attribut). Prior: BREAKING: `SemanticId` (`Name.TypeAbbr.Counter`) deleted, `ElementUid` (`<TYPE>-<slug>`) is the family canon — the old canon was used by no production graph of the family while 626 of 1145 elements already carried TYPE-slug (CR-SM-217); realRef unifies codeRef+schemaRef (+physical-MOD CAD ref), symbol optional; testRef stays separate (CR-228 C); +RepoRelativePathSchema on testRef/realRef .file — no absolute/`..` paths (CR-GC-255)
|
|
7
7
|
/** Rules engine version (validation rules incl. RC conformance). */
|
|
8
|
-
export const RULES_VERSION = '26.1.0'; // MINOR (CR-SM-321, ITEM-2026-080): R-23 zaehlt den compose-TEILBAUM (subtreeFuncs, dieselbe Zaehlung wie R-04 seit CR-SM-282) statt nur der direkten allocate-Kante. Ein Container-MOD (nur MOD -compose-> MOD, Leitlinie S2) galt als leer und bekam 'remove the module'; jetzt ist leer nur, wessen ganzer Teilbaum keine FUNC traegt. Verengung, kein neuer Befund. Prior: // MAJOR (CR-SM-319, ITEM-2026-064): neue Regel R-32 — jedes realisierte SCHEMA (symbol-realRef, nicht concept) braucht einen Vertragstest (TEST -verify-> SCHEMA); Warnung, Dimension ver, Gate TRR. Prior: // MAJOR (CR-SM-318, ITEM-2026-064): RC-04 prueft auch external-SCHEMAs mit realRef — ein Fremd-API-Vertrag als Zod-Schema im eigenen Code gehoert an seine Grenze; bisher pauschal ausgenommen, jetzt nur noch concept. Prior: // MAJOR (CR-SM-313): R-21 meldet die Uebergabe OHNE gemeinsame Kette -- Befund am SENDER, weil es ohne gemeinsame Kette keinen Kettenanker gibt. CR-GC-315 hatte diesen Fall stillgelegt, weil ein FLOW damals viele Produzenten haben durfte und die P·C-Ableitung Wiederverwendung quadratisch besteuerte; seit IO-02 (CR-SM-307) hat er genau EINEN, die Begruendung ist entfallen. DREI ZWEIGE: (1) ein Ende in GAR KEINER Kette -> still, das ist R-30s Aussage und nicht R-21s -- ohne die Klausel feuert die Regel an einem code-importierten Graphen wie moneyflow 219 von 219 Mal und meldet in Wahrheit 'dieses Repo hat keine Wirkketten', einmal je Kante; (2) gemeinsame Kette, keine davon geprueft -> Befund an der Kette, WOERTLICH unveraendert; (3) beide in Ketten, keine gemeinsame -> Befund am Sender, NEU. AUSNAHME: ist ein Ende Infrastruktur (chains >= policy.criticality.infrastructure, CR-SM-314), schweigt Zweig 3 -- eine Uebergabe an eine Funktion, die quer durch alle Ketten laeuft, ist ein Bus und kein fehlender Integrationstest. GEMESSEN ueber 12 Familiengraphen bei Modellstand 2026-09-12: 500 Uebergabepaare, davon 240 still durch Zweig 1 (219 allein moneyflow -- die Klausel ist der Unterschied zwischen einer Regel und einem Rauschen), 34 Altbefunde, 56 neu roh, 20 durch die Ausnahme freigestellt (alle graphcode, getragen von sechs FUNCs: mutate 11 Ketten, evaluate-rules 6, graph-impact/read-tools/graph-store je 4, list-elements 3), 36 netto neu. R-21 gesamt 34 -> 70. DOMAIN waechst von ['FCHAIN'] auf ['FCHAIN','FUNC']: Zweig 3 verankert am FUNC, und ein Zaehler, der seinen eigenen readiness-Nenner uebersteigt, ist die Fehlmessung aus CR-SM-242. GOLDEN UNVERAENDERT und das ist gemessen: der ssot-Eingang ist docs/graph/sigloch-modules.graph.json mit 0 Uebergabepaaren, n700/n3500 sind Kopien davon. R-21 hatte bis hierher KEINEN Ausloese-Beleg (eine der 42 als unbelegt ausgewiesenen Regeln) -- er kommt mit diesem CR, die Zahl sinkt auf 41. NOCH OFFEN: R-21 haelt fuer die Frage 'teilen diese beiden EINE Kette' weiterhin einen lokalen Ketten-Index, waehrend die ZAHL fuer die Ausnahme aus functionCriticality kommt; ein gemeinsamer Helfer chainsByFunc waere die siebte Datei gewesen und ist ein eigenes Item. Prior: MAJOR (CR-SM-314): neues PFLICHTFELD `criticality` in MetricPolicy (nullable, Default {infrastructure: 3}) und die neue Kennzahl `functionCriticality` -- je FUNC die Zahl der Wirkketten und der Use Cases darueber. MAJOR nach der Praezedenz CR-SM-283 (`boundaryWidth`): ein neues Pflichtfeld laesst JEDEN Konsumenten mit eigener Config hart abbrechen, auch wenn die Oberflaeche nur waechst. KEIN Verstoss aendert sich in dieser Version -- die Kennzahl urteilt nicht, und ihr erster Abnehmer (R-21s Infrastruktur-Ausnahme) landet getrennt als CR-SM-313. DIE DEFINITION IST GELIEHEN: "FUNC in einer Kette" heisst woertlich, was R-30 darunter versteht (direkte `FCHAIN -compose-> FUNC`), ohne Vererbung ueber compose-Vorfahren. Der Rollup wurde gemessen und verworfen -- ueber 17 Familiengraphen haette er die Grundgesamtheit von 224 auf 227 FUNCs gehoben, drei Stueck, und waere ein ZWEITER Kettenbegriff neben R-30s gewesen (die Drift-Klasse aus CR-SM-276). Ein zerlegter Block misst deshalb 0, und das ist richtig: FC-03 verbietet ein Kettenglied mit verschachtelter Zerlegung, er KANN die Kante nicht tragen. KEIN `infrastructure`-Flag an der Kennzahl: ein Flag waere ein Urteil in der Messung, die Schwelle gehoert in die Policy -- dieselbe Trennung, die `moduleMetrics` gegen MT-01/MT-02 haelt. SCHWELLE ABGELESEN, aber NICHT an der Verteilungsform: die ist ein duenner Schwanz (224 FUNCs in 1 Kette, 37 in 2, 6 in 3, 8 in 4, je 1 in 5/6/11) und gibt wie `instability` keine Schwelle her (CR-SM-293). Abgelesen ist die WIRKUNG: auf graphcodes 31 Uebergaben ohne gemeinsame Kette stellen die Schwellen 3 und 4 identisch 19 frei, 5 und 6 identisch 6 -- ein Plateau 3..4, innerhalb dessen die Wahl wirkungsfrei ist; der Startwert ist seine Untergrenze. Prior: MAJOR (CR-SM-312): R-04 misst nur noch die Randbreite des Moduls (verschiedene Vertraege ueber den Modulrand, Schwelle `boundaryWidth` wie BW-02) und steht in STEER_RULES; `policy.moduleSize` entfaellt ersatzlos -- Pflichtfeld weg, Befunde verschieben sich. Prior: MAJOR (CR-SM-311): RD-04 misst die Breite je Ebene als BAND -- neues Pflichtfeld `decompositionBreadth.min` (Default 3, Z3) und die neue Regel RD-05 fuer die Untergrenze (Katalog 70 -> 71), und ein MOD zaehlt Untermodule PLUS eigene Blatt-FUNC (das Allokations-Bein aus CR-SM-296 kommt als Breite zurueck; R-04 wird Kopplung, CR-SM-312). Breaking fuer jede Config (Pflichtfeld) und fuer Befunde (Untergrenze neu, MOD-Breite neu). Prior: MAJOR (CR-SM-307): IO-02 -- ein FLOW hat genau EINEN Produzenten, severity `error`. Katalog 69 -> 70. Reine Ergaenzung im Umfang, MAJOR nach Gate 8: eine neue error-Regel KANN fremde Graphen fallen lassen, und diese tut es -- gemessen ueber fuenf Systeme sind es graphcode 20/42 FLOWs, graph-view-edit 5/8, bok 3/15, waehrend moneyflow (0/220) und test_karp (0/21) sauber sind. Die beiden Referenzen erfuellen sogar die STRENGERE beidseitige Fassung; die Lockerung rettet sie nicht, sie brauchen sie nicht. WARUM NUR DIE PRODUZENTENSEITE: der Inhalt eines FLOW kommt von einer Stelle -- haengen zwei Quellen dran, sind es zwei Fluesse unter einem Namen und kein Leser weiss, welche Fassung er bekommt (Beleg aus dem Bestand: graphcodes FLOW-metric-policy trug ACTOR-owner UND FUNC-load-config, und seine eigene Beschreibung sagte es woertlich -- 'Dieselbe Form in zwei Fassungen, deshalb ein Vertrag'). Mehrere KONSUMENTEN sind dagegen die normale Gestalt eines geteilten Vertrags: eine Quelle, viele Leser, genau wie eine zentrale Konfiguration aussieht. Die beidseitige Fassung wurde GEMESSEN und VERWORFEN -- sie haette allein in graphcode 8 saubere 1:N-Muster bestraft (FLOW-dimension-readiness 1x7, FLOW-steering-snapshot 1x3, sechs weitere) und keinen einzigen zusaetzlichen echten Fehler gefunden; tests/unit/se-rule-trigger-proof.test.ts haelt die Gegenprobe fest, damit sie nicht unbemerkt zurueckkehrt. GATE 4, keine Doppelzaehlung: R-10 prueft die UNTERgrenze derselben Achse (mindestens ein Produzent, mindestens ein Konsument), IO-02 die OBERgrenze der Produzentenseite -- |P| = 0 und |P| > 1 sind disjunkt, die beiden koennen am selben FLOW nie zugleich feuern. Getrennte IDs, weil es getrennte Fragen sind (CR-SM-296): R-10 fragt 'hat dieser Fluss ueberhaupt Enden?', IO-02 'kommt sein Inhalt von einer Stelle?'. WARUM ES BIS HIERHER KEIN GATE PRUEFTE: die vier io-Zeilen in TRACE_PATTERNS tragen kein cardinality-Feld und fallen durch REQUIRED_PATTERNS (= TRACE_PATTERNS.filter(p => p.cardinality === '1')); die SCHEMA-Haelfte IST Grammatik (FLOW -relation-> SCHEMA [1..1], R-18 seit CR-SM-271 Teil 2), die io-Haelfte war nie deklariert -- kein wieder aufgerissenes Loch, die Frage wurde nie gestellt. EIN Befund je FLOW, nicht je ueberzaehliger Kante (Klasse CR-SM-242), value = Zahl der Produzenten, threshold 1 (CR-SM-288); dieselbe Quelle zweimal verdrahtet ist EINE Quelle. NICHT in STEER_RULES, und das ist die wichtigste Abgrenzung: das ist Hygiene, keine Steuerdimension, und der Unterschied ist GEGENLAEUFIG gemessen -- die Reparatur in graphcode (CR-GC-499/500, IO-02 20 -> 9) verbesserte fitAdvisory (coherence +0,111, modifiability +0,072) und verschlechterte den Chebyshev-Abstand um 0,25. KORREKTUR (CR-SM-308): nicht, weil ein aufgetrennter Fluss BW-02 hebt -- moduleCrossings zaehlt verschiedene SCHEMA je Rand, reines Auftrennen laesst BW-02 gleich (gemessen graphcode v257: 15 -> 15); der Anstieg kam von drei neuen SCHEMA und umgelegten Ketten. Die Einordnung als Hygiene statt Steuerdimension bleibt eine Entscheidung, kein gemessener Zielkonflikt. Die neun Befunde, die in graphcode bleiben, sind Busse mit 3 bis 19 Produzenten -- un-modellierte Entwurfsarbeit, kein Regelproblem, eigener Spike. Prior: MINOR (CR-SM-305): die sechs RC-Regeln stehen im KATALOG -- ALL_RULE_DEFS 63 -> 69, mit eigenem Profil `conformance`. Reine Ergaenzung: keine bestehende Regel, Severity oder Schwelle aendert sich, kein Graph faellt, das Golden-File der Regelausgabe steht zeichengleich. ROOT CAUSE: das Kongruenz-Urteil existierte, war getestet und erreichte KEINEN Produktionspfad. Gate (`harness.mutate`), Steuerung (fit-advisory, steering-snapshot) und `GET /api/graph/readiness` lesen alle ALL_RULE_DEFS; RC stand nicht darin, weil seine Signatur `(graph, facts: CodeFacts)` statt `(graph, policy)` ist -- der einzige Aufrufer von `scoreReadinessWithConformance` war ein Test. Ein Modell-Zug, der die Bindung an den Code bricht, passierte damit jedes Gate lautlos. `evaluateAllRules` fuehrt RC weiterhin NICHT aus (es hat keine CodeFacts), und genau diese Differenz ist das Signal: deklariert und nicht ausgewertet heisst NICHT GEPRUEFT; die vorhandene Ableitung `ALL_RULE_DEFS minus ausgewertet` macht es von selbst laut -- kein zweiter Zweig, die ND-fail-open-Lehre aus CR-SM-286. `domain` ist NEU an `ConformanceRuleDefinition` und fuer Konstruierer breaking; die Werte sind GEMESSEN statt gesetzt (der Entwurf des CR schlug `['code']`, `['code','interface']` vor -- `domain` ist aber die Grundgesamtheit, und 'code' ist kein Elementtyp): RC-01 10x FUNC, RC-04 4x SCHEMA, RC-05 5x MOD ueber fuenf Familien-Repos, RC-02/RC-03 aus der Schleife gelesen (TEST/SCHEMA), RC-06 aus ELEMENT_ATTRIBUTES -- nur FUNC/MOD/SCHEMA duerfen `external` + `realRef` tragen. NICHT in RULE_TO_DIMENSION/RULE_TO_PHASE, und das ist die zweite Korrektur am eigenen Entwurf: der readiness-Nenner summiert die Grundgesamtheiten der Regeln einer Dimension, eine deklarierte und nicht ausgewertete Regel HEBT also den Score. Gemessen ueber vier Repos, was das geschenkt haette: graph-view-edit `schema` +5,0 Punkte (Nenner 24 -> 40), graphify `arch` +2,1 und `ver` +1,8, alles Uebrige <= 0,2 -- der Ausschlag sitzt genau in der kleinen Dimension, wo die Zahl am meisten wiegt. `computeApplicable` ueberspringt eine Regel ohne Dimension bereits von selbst; die beiden Vollstaendigkeitstests sind auf die AUSGEWERTETEN Profile eingeengt (READINESS_SCORED_PROFILES) und haben das seit CR-228 in ihrem eigenen Kopf behauptet -- nur war der Katalog bis hierher mit den ausgewerteten Regeln identisch. Der Ausloese-Beleg (CR-SM-285) gilt auch fuer RC: sechs synthetische CodeFacts-Fixturen, kein Checkout, wie `CodeFactsSchema` es seit CR-211 zusagt ('testable without any filesystem') -- die Zahl der unbelegten Regeln bleibt 42 und ist nicht um sechs gestiegen. In se-engine deckt CLASS_MAP die sechs als Constraint ab: wohin eine verwaiste realRef zeigen SOLL, steht in keinem Feld, ein Operator daraus waere geraten. Prior: MINOR (CR-SM-300): `RULE_HELP` -- die Regelhilfe liegt neben der Regel. Reine ERGAENZUNG: kein Katalogeintrag, keine Severity, keine Schwelle aendert sich, kein bestehender Graph faellt. Warum sie umzieht: ein Hilfeeintrag ist keine Annotation UEBER einer Regel, er ist Teil ihrer LIEFERUNG -- eine Regel, deren Befund niemand lesen kann, ist nicht fertig. CR-GC-227 hat die Schicht 2025 bewusst in graphcode gelassen (Tempo: kein Bump, kein Familien-Review) und die Verschiebung ausdruecklich als spaetere Entscheidung notiert; Gate 1 verlangt fuer das Wiederaufmachen eine MESSUNG, und die lag am 2026-09-08 vor: von 73 Eintraegen in graphcodes `HELP_CONTENT` zeigten 11 auf Regel-IDs, die es nicht mehr gab (R-03, R-14, R-27, FC-01, SC-04, CR-R04, AO-D01, AO-D03, RT-01, PH-01, CA-01 -- AO-D03 seit drei Wochen), waehrend 8 Katalogregeln ganz ohne Eintrag dastanden (BW-02, BQ-01, BQ-02, BQ-04, BQ-06, BQ-07, ND-01, ND-02) -- darunter mit BW-02 eine der vier STEUERDIMENSIONEN: eine Regel, die rankt und sich nicht erklaert. Die Drift lief in beide Richtungen und ueber vier Releases, obwohl graphcode sehr wohl eine Vorwaertspruefung hat (`tests/help-content.test.ts`) -- sie war rot und blieb es, weil niemand graphcodes Suite las (CR-GC-488). Eine Pruefung im ungelesenen Repo ist keine Pruefung, und hinter einem Symlink gilt kein Versionsbereich (CR-GC-488 Paragraph 1a). Co-located zwingt die Grammatik selbst: eine Streichung nimmt den Eintrag mit, eine Neuanlage kann ohne ihn nicht landen. Umfang: 69 Eintraege (63 Katalog- + 6 Conformance-Regeln), 18,6 KB reine Strings -- der ./browser-Eintrag bleibt transitiv `node:`-frei (CR-SM-230). Im Golden-File steht nur die SCHLUESSELMENGE (`ruleHelp`), nicht die Prosa: 'Prosa ist ein Patch-Bump, keine Grammatik' (CR-SM-244) -- ein umformulierter Satz darf keinen Bump ausloesen, ein weggefallener Eintrag muss. Abgrenzung zu `fix_hint`, das an jeder Regeldefinition schon steht (Gate 3): `fix_hint` ist die Anweisung an den AGENTEN, `plain`/`se` die Erklaerung fuer den MENSCHEN -- verschiedene Leser, verschiedene Laenge, verschiedene Sprache. NICHT mitgezogen und bewusst in graphcode geblieben: Dashboard-Panels, Artefakte, das Vokabular und die sechs Metrik-Dimensionen (`HELP_METRICS`, CR-GC-458) -- sie haengen an graphcodes Oberflaeche, nicht am Regelkatalog. Prior: BREAKING (CR-SM-293): MT-01 zaehlt KOPPLUNG statt aller Kanten, dreht die Richtung um und urteilt per Default nicht mehr. (1) fan_in/fan_out lasen bis hierher JEDE Kante, deren eines Ende einem Modul gehoerte. Gemessen ueber 19 Familiengraphen: von 3281 gezaehlten Kanten sind nur 1117 (34 %) Kopplung. fan_in 2164 = 674 `allocate` -- das ist die MODULGROESSE, ein Modul wurde also stabiler, indem es Funktionen bekam -- plus 610 `FLOW -io-> FUNC`, 348 `FCHAIN -compose-> FUNC`, 255 `CR -relation-> FUNC`, 277 Rest. fan_out 1117 = 507 `FUNC -io-> FLOW` plus 501 `FUNC -satisfy-> REQ`; fast die halbe fan_out war SPEZIFIKATION, dieselbe Begruendung, mit der CR-SM-297 `satisfy` aus LCOM4 genommen hat. Dazu 70 Kanten ueber `MOD -io-> MOD`, ein Muster, das CR-SM-266 D2 aus TRACE_PATTERNS geloescht hat. MT-01 liest jetzt `moduleCrossings` -- DIESELBE Definition von Rand und dieselbe Zaehlbasis (verschiedene Vertraege), die CR-01, R-04 und BW-02 seit CR-SM-274/276 benutzen; MT-01 hatte bis hierher noch eine dritte. (2) RICHTUNG: der KONSUMENT haengt ab. Wer nach draussen liefert, wird gebraucht (afferent, fan_in); wer von draussen bezieht, haengt ab (efferent, fan_out). Das ist Martins Ca/Ce; die alte Lesart 'Kante zeigt weg = Abhaengigkeit' drehte jedes reine Verbrauchermodul auf I = 0 (maximal stabil), wo es 1 sein muss. Die Konvention stand vorher NIRGENDS im Repo -- sie war nie entschieden, nur codiert. (3) `DEFAULT_METRIC_POLICY.instability` 0.7 -> null, messen statt urteilen. Die Verteilung ist zweigipflig -- 44 von 149 messbaren Modulen exakt auf 0.00, 45 exakt auf 1.00, und 45 der 51 Befunde bei 0.7 sind genau diese 1.00 -- aus ihr laesst sich keine Schwelle ABLESEN (Praezedenz CR-SM-283/BW-02). Tiefer: I ist eine KOORDINATE, kein Defekt; Martins Satz lautet 'in Richtung Stabilitaet abhaengen', nicht 'I klein halten'. Damit fehlt MT-01 die Eigenschaft, die CR-SM-287 §2 von einer Steuerdimension verlangt (gerichtet, 'weniger ist besser, ohne Diskussion'), und (4) `STEER_RULES` schrumpft auf VIER. Praktisch war die Dimension ohnehin still: `steerScore` liest Verstoesse, und graphcodes Config setzt `instability: null` seit CR-GC-329 -- auf dem einzigen Repo, das taeglich steuert, lief der R5 laengst als R4. Der Nachfolger ist gemessen und angelegt (CR-SM-298): Martins Stable Dependencies Principle als gerichtete Groesse, Abhaengigkeiten 'bergauf' je Modul -- 34 von 305 (11 %), 132 von 146 Modulen bei null, Schwanz bis 6; lokal, gerichtet, budgetierbar, nicht entartet. Die Zahl `instability` bleibt vollstaendig erhalten: graph_metrics, Export und das gve-Dashboard rendern sie weiter, letzteres samt Hinweis 'Instabilitaet wird hier nur gemessen'. Prior: BREAKING (CR-SM-297, Teil 1): `satisfy` faellt aus der LCOM4-Verbindung von MT-02. Die Regel misst KOHAESION, und Kohaesion ist Datenkopplung -- geteilte `io`-Ziele und geteilte FLOW. `satisfy` ist dagegen eine SPEZIFIKATIONS-Beziehung: zwei FUNCs, die dasselbe REQ erfuellen, koennen zur Laufzeit vollstaendig entkoppelt sein. Die Wirkung ging dabei nur in EINE Richtung -- `satisfy` verbindet Gruppen, senkte also LCOM4 und liess Module kohaesiver aussehen, als ihr Datenfluss hergibt. Der Modellierungsgrund wiegt schwerer als der Messgrund: teilen sich zwei Funktionen ein Requirement, ist das REQUIREMENT zu zerlegen, nicht die Kohaesionsmessung zu beschoenigen; RD-02 sagt bereits das Verwandte. Gemessen ueber 19 Familiengraphen: MT-02 23 -> 24 Befunde. `satisfy` hat also fast nie zwei Gruppen verbunden -- die Aenderung ist prinzipiell richtig und praktisch billig. Golden neu verankert OHNE Zahlenaenderung: Totals und Reihenfolge bleiben an allen drei Eingaengen gleich (ssot 99, n700 1475, n3500 17495), nur die MT-02-shas wandern, weil die Meldung den gemessenen LCOM4-Wert woertlich nennt. NOCH OFFEN aus CR-SM-297: die beiden Container-Masse fuer den zerlegten FUNC (Kohaesion analog MT-02, Paar-Kopplung analog CR-01) -- ihre Schwellen sind aus der Verteilung ABZULESEN, nicht zu setzen (Praezedenz CR-SM-283/BW-02). Prior: BREAKING (CR-SM-296): je Frage eine Regel, je Schwelle ein Wert. (1) RD-04 gibt das Bein `FUNC -allocate-> MOD` an R-04 ab. Es zaehlte die allozierten FUNCs je Modul -- also die MODULGROESSE, und die misst R-04 auch. Beide feuerten am selben Modul im selben Batch, mit zwei Schwellen aus zwei Policy-Feldern (RD-04 > 11 gegen R-04 > 12), und welche recht hat, stand nirgends; dieselbe Ursache ging doppelt in den readiness-Nenner. R-04 ist die reichere Aussage -- Groesse GEGEN Kreuzungen, drei Urteile -- und liest ueber moduleCrossings ohnehin dieselben Daten. RD-04 bleibt fuer ZERLEGUNGSBREITE zustaendig: FUNC compose FUNC, SYS/MOD compose MOD und der FUNC-Wurzelwald am SYS (CR-SM-282). (2) Beide Schwellen sind an die Doktrin 7+-2 gebunden statt danebengesetzt: decompositionBreadth.warning 11 -> 9 (die Obergrenze der Doktrin) und moduleSize large 12 -> 9, coupled 8 -> 7. CR-SM-282 hatte gegen die Doktrin-Zahl 5 entschieden, weil `info` bei 5 den readiness-Nenner verwaessert haette -- das Argument traegt fuer die OBERgrenze nicht, denn RD-04 meldet als warning. Wirkung ueber 19 Familiengraphen, gemessen: RD-04 17 -> 15, R-04 9 -> 11, Summe 26 -> 26. Die Zahl bleibt, die Zustaendigkeit wird eindeutig. Prior: BREAKING (CR-SM-295): drei Regeln entfallen, eine wird neu gefasst -- Katalog 66 -> 63. (1) R-14 (UC must have compose) kann NIE ALLEIN feuern: sie prueft nur, ob ein UC irgendeine ausgehende compose-Kante hat, ohne den Zieltyp anzusehen, waehrend UC-01 den REQ und UC-03 die FCHAIN einzeln verlangen. Auf ELEMENTEBENE ueber 19 Familiengraphen gemessen, nicht auf Summen: R-14 (2 Befunde) ist eine echte Teilmenge von UC-01 vereinigt UC-03 (23 Befunde). Ihr Fixhinweis ('Add FCHAIN or REQ') versprach zudem eine Pruefung, die der Code nicht macht. (2) FC-01 (FCHAIN has actor boundary) ist von FC-04 gedeckt -- gemessen FC-01 (5) Teilmenge von FC-04 (27); FC-04 verlangt Eingang UND Ausgang, FC-01 nur eine Beruehrung. Dazu trug FC-01 einen TOTEN Ersatzweg: `hasDirectActorIO` prueft, ob der Eltern-UC actor-adjazent ist, und `ACTOR -io-> UC` hat CR-SM-266 (D1/D4) aus TRACE_PATTERNS entfernt -- der Zweig konnte seit drei Versionen nicht mehr wahr werden. (3) CR-R04 entfaellt und CR-R01 wird NEU GEFASST. Beide zielten daneben: CR-R01 akzeptierte jede relation-Kante, also auch einen CR, der nur an einem Meilenstein haengt (Termin, kein Umfang) -- 5 Befunde bei 49 CRs, die ausschliesslich auf ein MS zeigen. CR-R04 verlangte einen FUNC und war damit zu eng: 21 Befunde, darunter Daten-, Doku- und Konfigurationsaenderungen, die zu Recht keine Funktion beruehren. Die neue Fassung fragt nach dem UMFANG: mindestens eine relation auf FUNC, MOD, SCHEMA, REQ oder UC. Grundgesamtheit woertlich die von CR-R04 (CR-SM-255, nur offene CRs) -- an einem geschlossenen CR ist die Frage Archaeologie. Gemessen an 514 CR-Knoten: 54 ohne Umfangsbezug, davon 0 OFFEN, 52 done, 2 ohne Status. Die Verschaerfung laesst die Historie damit unangetastet und wirkt ab dem naechsten CR. MS-03 (CR ohne MS) bleibt unberuehrt -- sie stellt die komplementaere Frage. (4) R-17 behaelt ihre Pruefung und verliert ihren falschen Fixhinweis, der drei Zieltypen nannte, die sie nie angesehen hat. Prior: BREAKING (CR-SM-294): sechs Regeln entfallen ersatzlos, Katalog 72 -> 66. AO-D01 (RelayNodeDetection) ist STRUKTURELL unmoeglich -- sie verlangt `>= 2` ausgehende `io`-Kanten auf andere FUNC, und `FUNC -io-> FUNC` steht nicht in TRACE_PATTERNS; R-18 lehnt es als error ab. Das ist zeichengleich der Fall, fuer den CR-SM-283 AO-D03 geloescht hat -- AO-D03 fiel, AO-D01 blieb stehen. CA-01, RT-01, PH-01, R-27 und R-03 sind GEGENSTANDSLOS: sie lesen `asil`, `attributes.kind === 'physical'`, `attributes.requires` und `attributes.capability`, und ueber 19 Familiengraphen ist jedes dieser Attribute 0-mal gesetzt (305 MOD, 696 FUNC). Beleg beidseitig: `report:silence` meldet alle sechs mit 0 Befunden ueber den gesamten Bestand, UND keine von ihnen hat eine Ausloese-Fixture in se-rule-trigger-proof.test.ts (CR-SM-285) -- also weder feuert sie irgendwo, noch ist belegt, dass sie ueberhaupt feuern KANN. Genau die Doppelbedingung, die Gate 7 fuer eine Streichung verlangt. NICHT gestrichen wurde CL-01, obwohl derselbe Verdacht bestand: sie feuert 2x in 1 von 19 Graphen. Ihre Aussage waere anderswo besser aufgehoben -- jeder UC muss hinreichend distinct sein, und das saehe man an der FCHAIN -- aber diese Regel EXISTIERT NICHT: CR-SM-283 hat AO-D03 geloescht und dabei festgehalten, dass ihre Aufgabe ('zwei Wirkketten, die auf einer Ebene mitgliedsgleich werden') ungeloest bleibt. CL-01 zu streichen hiesse, eine feuernde Pruefung gegen eine nicht existierende zu tauschen. Wirkung auf die Bestandsgraphen: 0 -> 0 Befunde, kein Graph faellt. Die einzige messbare Folge ist der kleinere NENNER jeder readiness-Dimension -- sechs stumme Regeln hoben bisher jeden Score. Prior: BREAKING (CR-SM-286): ND-01/ND-02 rechnen ihre Aehnlichkeit SELBST; setND01SimilarityMatrix/setND02SimilarityMatrix/getND02SimilarityMatrix entfallen ersatzlos. Die Formel stand als Prosa in contracts und als Code in graphcode/src/kernel/measure/nd-similarity.ts, verbunden durch eine Injektionsnaht. Vier gemessene Kosten: (1) FAIL-OPEN -- ohne Injektion gaben zwei `error`-Regeln [] zurueck, ununterscheidbar von 'keine Duplikate'; moneyflow trug 16 ND-01-Befunde, die ausserhalb graphcodes NIEMAND sah (report:silence, die Spike-Skripte, jedes kuenftige Werkzeug). Die Familie hat fuer 'kann nicht urteilen' eine Konvention (policy.X = null, moduleMetrics.instability = null); ND fiel statt dessen nach gruen. (2) PROZESSWEITER MODULZUSTAND -- `let _nd01Matrix` ist global, graphcode brauchte die Klammer withNDMatrices mit finally-Reset, jeder andere Aufrufer nicht: Graph A urteilte ueber Graph B. (3) AO-D01 liegt IM Gate-Katalog und uebersprang seine Overlap-Pruefung ohne Matrix ('no matrix -> assume pass') -- eine Gate-Regel, die sich stillschweigend abschwaecht. (4) BQ-04 haengt am selben Muster und ist komplett tot. Jetzt: similarity.ts, je Graph WeakMap-gecacht, Formeln und Schwelle 0,85 zeichengleich uebernommen -- dasselbe Urteil an der richtigen Stelle, Praezedenz CR-SM-276 (moduleCrossings). DABEI GEFUNDEN UND BEHOBEN: die portierte Formel las Partner ueber ALLE Traces; se-rule-pair-legality (Pruefung A, CR-SM-278) meldete 35 Faelle, in denen ND-02 erst durch eine grammatikwidrige Kante auf SCHEMA anschlug (ACTOR -compose-> SCHEMA und Verwandte). `partners()` filtert jetzt ueber isValidTrace -- eine Regel darf ihr Verdikt nicht an einer Kante haengen, die R-18 als error ablehnt. Golden bewusst neu verankert und im Test-Header aufgeschluesselt: ssot 98 -> 98 UNVERAENDERT, n700 1038 -> 1478, n3500 5176 -> 17496, der gesamte Zuwachs ist ND-01 auf den synthetischen Eingaengen -- buildFixedSizeGraph KOPIERT den Basisgraphen 11x bzw. 56x, dort hat jedes Element 10 bzw. 55 exakte Duplikate; eine Duplikatsregel MUSS das melden. ND-02 bleibt ueberall 0. BQ-04 ausdruecklich NICHT angeschlossen: es verlangt 'pre-computed EMBEDDING similarity', und Embeddings kann ein reines Vertragspaket nicht rechnen; ein Ersatzmass haette ich erfinden muessen, und der Versuch (0.7*Beschreibung + 0.3*Name) lieferte 0 Befunde an allen neun Familiengraphen und 4950 an einer templatierten Fixture -- beide Gate-7-Ausreisser zugleich. BQ-04 ist damit der naechste AO-D03-Fall und ein eigener se-grammar-review-Vorgang. Prior: BREAKING (CR-SM-285): R-12 findet Zyklen JEDER Laenge statt nur Rueckkanten. Die Regel heisst 'No circular dependencies' und prueft bis hierher nur, ob eine Kante DIREKT zurueckliuft (A->B und B->A). Ein Dreierzyklus A->B->C->A ging vollstaendig durch das Gate, mit null Fehlern. Das ist die gefaehrlichere Haelfte der Fehlerklasse dieser Session: ein Absturz meldet sich, eine Pruefung die ihren Gegenstand nicht erreicht BESTAETIGT. Warum es zaehlt: jeder Rollup der Familie setzt Azyklizitaet voraus -- chainOf/funcChainOf (CR-SM-282/-283) tragen seen-Guards gegen genau diese Endlosschleife, moduleMetrics, die RD-04-Breitenzaehlung und gves Container-Sicht ebenso; unter einem Zyklus ist ein Rollup nicht falsch sondern UNDEFINIERT. Das R-18-Bein aus CR-SM-283 deckt es NICHT: bei A->B->C->A hat jeder Knoten genau EINEN compose-Elternteil, die Baum-Eigenschaft ist formal erfuellt und die Struktur trotzdem kein Baum. Gemessen ueber NEUN Familiengraphen: 0 Zyklen jeder Laenge, also 0 -> 0 Befunde -- die Schaerfung laesst keinen bestehenden Graphen fallen. Major nach Gate 8 trotzdem, weil eine verschaerfte Regel fremde Graphen fallen lassen KANN. EIN Befund je Zyklus statt je Kante (Klasse CR-SM-242), verankert am kleinsten Knoten des Rings und damit unabhaengig vom DFS-Einstieg (Gate 5); die Zweierring-Meldung ist woertlich unveraendert. Dazu zwei Haertungen OHNE Regelwirkung: descriptionOf() gab `el.description ?? ''` und starb an jedem Nicht-String (42/{}/[]/true -> '.trim is not a function', vier von fuenf Typen) -- am Gate hiess das Stacktrace statt block-Verdict; jetzt zaehlt nur ein echter String, alles andere ist 'keine Beschreibung'. Und tests/unit/se-rule-trigger-proof.test.ts ist die Umkehrung von report:silence: je Regel ein Graph der sie feuern LASSEN MUSS, 22 belegt, 50 als unbelegt ausgewiesen mit einer Zahl die nur sinken darf. Beim ersten Lauf fand er sofort einen Fall: ND-01/ND-02 sind `error` und beginnen mit `if (!_nd01Matrix) return []` -- ohne injizierte Aehnlichkeitsmatrix fallen sie nach GRUEN, ununterscheidbar von 'keine Duplikate'. Dokumentiert und eingefroren, nicht behoben: die Severity-/Signalfrage ist ein eigener se-grammar-review-Vorgang. Prior: BREAKING (CR-SM-283): AO-D03 entfaellt ERSATZLOS, BW-02 kommt -- Katalog 74 -> 74, kein Wachstum im Komplexitaetsbudget. (1) AO-D03 (`DuplicatePathDetection`) suchte `FUNC -io-> FUNC`, ein Paar das TRACE_PATTERNS nicht kennt und das R-18 als error ablehnt: die Regel konnte STRUKTURELL nicht feuern und hat es an keinem der fuenf Familiengraphen je getan. Eine Regel die nie feuert kostet trotzdem Nenner-Anteil, Katalogzeile, readiness-Zuordnung, Golden-File-Eintrag und einen Leser der sie unterscheiden muss. Die Aufgabe die sie tragen SOLLTE -- zwei Wirkketten die auf einer Ebene mitgliedsgleich werden -- bleibt ungeloest und ist ein eigener CR; BW-02 ersetzt sie nicht. (2) BW-02 misst die Randbreite der FUNC-WHITEBOX: verschiedene SCHEMA-Vertraege auf `FUNC -io-> FLOW -io-> FUNC` mit einem Endpunkt im compose-Teilbaum und einem ausserhalb. Gerechnet im selben Durchlauf wie `byModule` (module-crossings.ts), damit es EINE Definition von Rand gibt. Der Rollup ist hier nicht Genauigkeit sondern Existenz: ein zerlegter FUNC traegt selbst keine io-Kanten (die liegen an seinen Blaettern), ohne Teilbaum-Aufloesung waere die Zahl strukturell immer 0 -- gemessen FUNC-block-grounding 0 -> 19. Gate 6 des Grammatik-Reviews: 12 Befunde an graphcode, davon 0 in Ueberlappung mit irgendeiner FUNC-Regel (AO-D01 misst Relay-Knoten, IO-01 Kettenkohaerenz, R-31 blosse Verdrahtung). Schwelle 5 aus der Verteilung ABGELESEN, nicht gewaehlt: bok und graph-view-edit enden beide bei 3, graphcode geht bis 19, moneyflow bis 17. Kein info-Level -- bei Schwelle 3 truege bok 4 Befunde auf 9 Blackboxes, und readiness zaehlt info ungefiltert in den Nenner (MT-03-Praezedenz). (3) VIERTES BEIN von R-18: ein Element hat hoechstens einen compose-Elternteil (FUNC/FUNC und MOD/MOD), error. TRACE_PATTERNS begrenzt heute nur die KINDER einer Kante, nie die ELTERN -- graph_mutate legt eine zweite compose-Kante anstandslos an. 0 Verstoesse ueber fuenf Graphen, und trotzdem hart, weil jeder Rollup der Familie unter Verletzung nicht falsch sondern UNDEFINIERT ist (byModule/byFunc, RD-04-Breite, die Container-Sicht von gve). Keine eigene Regel-ID, Praezedenz CR-SM-271 Teil 2: dieselbe Aussage, derselbe naechste Schritt. EIN Befund je KIND, nicht je ueberzaehliger Kante. (4) `policy.boundaryWidth` neu (nullable, Default {warning: 5}) -- fuer Konstruierer einer MetricPolicy breaking. Prior: BREAKING (CR-SM-282): zwei Blackbox-Luecken geschlossen, und beide lassen bestehende Graphen fallen. (1) RD-04 zaehlt den FUNC-WURZELWALD, verankert am SYS-Knoten. Die Regel zaehlt Kinder je Parent; die Wurzeln des `FUNC -compose-> FUNC`-Waldes haben keinen, also war die oberste Funktionsebene unsichtbar -- gemessen: moneyflow hat 306 Wurzel-FUNCs und RD-04 meldete 0. Die MOD-Seite hatte das Problem nie, weil `SYS -compose-> MOD` eine echte Kante ist; die FUNC-Seite hat keine (`se:top-level`: der obere FUNC-Satz ist eine PROJEKTION, keine Kante). Anker ist der einzige SYS-Knoten; gibt es nicht genau einen, schweigt der Zweig statt zu raten (R-17 deckt den Fall). (2) `moduleCrossings.byModule` und die Groesse in R-04 lesen jetzt die BESITZKETTE (direkt alloziertes MOD + compose-Vorfahren) statt nur der direkten Allokation. Ein Eltern-MOD hat keine direkt allozierten FUNCs -- es kam in `byModule` nicht vor, `funcCount` war 0, und R-04 schwieg per `funcCount <= coupled` an genau der Whitebox, in die der Leser hineinklickt. `pairs` (CR-01) bleibt BEWUSST auf der direkten Zuordnung: 'wie stark haengen DIESE zwei Module aneinander' ist eine Blattebenen-Frage, ein Elternpaar meldete dieselbe Kopplung ein zweites Mal -- CR-01 ist an allen fuenf Familiengraphen zeichengleich geblieben (5/3/15/0/63). Delta gesamt: moneyflow RD-04 +1, sirail R-04 +1, bok/gve/graphcode unveraendert. (3) Die Schwelle steht in `policy.decompositionBreadth` statt als `DECOMPOSITION_BREADTH_MAX = 11` inline in rules.ts -- dieselbe Klasse, die CR-SM-236 fuer R-04 aufgeloest hat; `null` schaltet die Regel ab, Default 11 = verhaltensgleich. Das neue Pflichtfeld in MetricPolicy ist fuer Konstruierer breaking. Die Doktrin-Zahl 5 (`se:top-level`) ist bewusst NICHT der Default: `info` bei 5 haette an graphcode 20 zusaetzliche Befunde erzeugt, und readiness zaehlt `info` ungefiltert in den Nenner (`score = 1 - Verstoesse / applicable`) -- genau der Grund, aus dem MT-03 zur Messung statt zur Regel wurde (readiness.ts). Wer doktrin-streng fahren will, setzt 5. Prior: BREAKING (CR-SM-271 Teil 2): SC-04 entfaellt ersatzlos. Ein FLOW ohne SCHEMA ist kein schwaecherer Typ, sondern untypisiert — die Untergrenze ist Grammatik geworden (`FLOW -relation-> SCHEMA [1..1]`, META_MODEL 5.0.0) und meldet als drittes Bein von R-18 (error, mit candidate_targets im Kontext wie zuvor SC-04). Kein Regel-Loch: derselbe Sachverhalt, EIN Befund, nur haerter und am Gate durchgesetzt statt nur gemeldet. Der Katalog schrumpft um eine Zeile (Nenner, readiness-Zuordnung 'schema'/CDR und Golden-File-Eintrag fallen mit); der Fall zaehlt jetzt unter R-18/'arch'/PDR.
|
|
8
|
+
export const RULES_VERSION = '28.1.0'; // MINOR (CR-SM-329, ITEM-2026-154): +RC-07 'CR node agrees with docs/cr' (warning) und +`CodeFacts.crFiles`. Der CR-Status bleibt am Knoten (drei Regeln und die Meilenstein-Readiness lesen ihn); RC-07 prueft ihn gegen das Verzeichnis und meldet offene CR-Dateien ohne Knoten. Reine Ergaenzung, RC laeuft nicht in evaluateAllRules, Golden unberuehrt. Bestand vorab: 1 Status-Widerspruch (graphcode CR-GC-261), 11 offene Dateien ohne Knoten (bok 9, sigloch-modules 1, graph-view-edit 1). Prior: // MAJOR (CR-SM-327, ITEM-2026-151): neue Regel MT-04 — LCOM4 an der FUNC-WHITEBOX, dieselbe Rechnung wie MT-02 seit CR-SM-326 (Boxen = compose-Kinder, jede mit den Vertraegen ihres Teilbaums, verbunden ueber den geteilten Vertrag). Eigene ID statt domain ['MOD','FUNC'] an MT-02, weil die Grundgesamtheit den Readiness-Nenner bestimmt: an MT-02 gehaengt stiege alloc um bis zu 0,027 ohne Verbesserung am Graphen (Klasse CR-SM-235/270), als eigene Regel in arch <= 0,008. Praezedenz R-20/R-26/R-27. Schwellen geteilt (policy.lcom4). Bestand: MT-04 0 -> 2 (graphcode block-gedaechtnis 9 Boxen/5 Gruppen, block-host-sitzung 6/5), MT-02 unveraendert 11. Prior: // MAJOR (CR-SM-326, ITEM-2026-029): MT-02 urteilt ueber die BOXEN einer Modul-Whitebox (Sub-MODs + direkt allozierte FUNC), jede Box mit den Vertraegen ihres Teilbaums (Besitzkette wie R-04/CR-SM-282, BW-02/CR-SM-283, R-23/CR-SM-321), verbunden ueber den VERTRAG statt ueber FLOW-Identitaet (eine Zaehlbasis wie CR-01/R-04/MT-01 seit CR-SM-274/276). `null` jetzt auch ohne einen einzigen Vertrag im Modul — die unverdrahtete FUNC ist R-31s Aussage. Die Ausnahme aus CR-SM-263 entfaellt, `rollupContainer` bleibt Messung. Bestand ueber 7 Familiengraphen: MT-02 20 -> 11 (-5 graphcode Artefakte, -1 graphify, -4 moneyflow, +1 sirail MOD-in-MOD). MAJOR, weil Befunde nicht nur wegfallen, sondern an anderer Stelle neu entstehen. Prior: // MINOR (CR-SM-321, ITEM-2026-080): R-23 zaehlt den compose-TEILBAUM (subtreeFuncs, dieselbe Zaehlung wie R-04 seit CR-SM-282) statt nur der direkten allocate-Kante. Ein Container-MOD (nur MOD -compose-> MOD, Leitlinie S2) galt als leer und bekam 'remove the module'; jetzt ist leer nur, wessen ganzer Teilbaum keine FUNC traegt. Verengung, kein neuer Befund. Prior: // MAJOR (CR-SM-319, ITEM-2026-064): neue Regel R-32 — jedes realisierte SCHEMA (symbol-realRef, nicht concept) braucht einen Vertragstest (TEST -verify-> SCHEMA); Warnung, Dimension ver, Gate TRR. Prior: // MAJOR (CR-SM-318, ITEM-2026-064): RC-04 prueft auch external-SCHEMAs mit realRef — ein Fremd-API-Vertrag als Zod-Schema im eigenen Code gehoert an seine Grenze; bisher pauschal ausgenommen, jetzt nur noch concept. Prior: // MAJOR (CR-SM-313): R-21 meldet die Uebergabe OHNE gemeinsame Kette -- Befund am SENDER, weil es ohne gemeinsame Kette keinen Kettenanker gibt. CR-GC-315 hatte diesen Fall stillgelegt, weil ein FLOW damals viele Produzenten haben durfte und die P·C-Ableitung Wiederverwendung quadratisch besteuerte; seit IO-02 (CR-SM-307) hat er genau EINEN, die Begruendung ist entfallen. DREI ZWEIGE: (1) ein Ende in GAR KEINER Kette -> still, das ist R-30s Aussage und nicht R-21s -- ohne die Klausel feuert die Regel an einem code-importierten Graphen wie moneyflow 219 von 219 Mal und meldet in Wahrheit 'dieses Repo hat keine Wirkketten', einmal je Kante; (2) gemeinsame Kette, keine davon geprueft -> Befund an der Kette, WOERTLICH unveraendert; (3) beide in Ketten, keine gemeinsame -> Befund am Sender, NEU. AUSNAHME: ist ein Ende Infrastruktur (chains >= policy.criticality.infrastructure, CR-SM-314), schweigt Zweig 3 -- eine Uebergabe an eine Funktion, die quer durch alle Ketten laeuft, ist ein Bus und kein fehlender Integrationstest. GEMESSEN ueber 12 Familiengraphen bei Modellstand 2026-09-12: 500 Uebergabepaare, davon 240 still durch Zweig 1 (219 allein moneyflow -- die Klausel ist der Unterschied zwischen einer Regel und einem Rauschen), 34 Altbefunde, 56 neu roh, 20 durch die Ausnahme freigestellt (alle graphcode, getragen von sechs FUNCs: mutate 11 Ketten, evaluate-rules 6, graph-impact/read-tools/graph-store je 4, list-elements 3), 36 netto neu. R-21 gesamt 34 -> 70. DOMAIN waechst von ['FCHAIN'] auf ['FCHAIN','FUNC']: Zweig 3 verankert am FUNC, und ein Zaehler, der seinen eigenen readiness-Nenner uebersteigt, ist die Fehlmessung aus CR-SM-242. GOLDEN UNVERAENDERT und das ist gemessen: der ssot-Eingang ist docs/graph/sigloch-modules.graph.json mit 0 Uebergabepaaren, n700/n3500 sind Kopien davon. R-21 hatte bis hierher KEINEN Ausloese-Beleg (eine der 42 als unbelegt ausgewiesenen Regeln) -- er kommt mit diesem CR, die Zahl sinkt auf 41. NOCH OFFEN: R-21 haelt fuer die Frage 'teilen diese beiden EINE Kette' weiterhin einen lokalen Ketten-Index, waehrend die ZAHL fuer die Ausnahme aus functionCriticality kommt; ein gemeinsamer Helfer chainsByFunc waere die siebte Datei gewesen und ist ein eigenes Item. Prior: MAJOR (CR-SM-314): neues PFLICHTFELD `criticality` in MetricPolicy (nullable, Default {infrastructure: 3}) und die neue Kennzahl `functionCriticality` -- je FUNC die Zahl der Wirkketten und der Use Cases darueber. MAJOR nach der Praezedenz CR-SM-283 (`boundaryWidth`): ein neues Pflichtfeld laesst JEDEN Konsumenten mit eigener Config hart abbrechen, auch wenn die Oberflaeche nur waechst. KEIN Verstoss aendert sich in dieser Version -- die Kennzahl urteilt nicht, und ihr erster Abnehmer (R-21s Infrastruktur-Ausnahme) landet getrennt als CR-SM-313. DIE DEFINITION IST GELIEHEN: "FUNC in einer Kette" heisst woertlich, was R-30 darunter versteht (direkte `FCHAIN -compose-> FUNC`), ohne Vererbung ueber compose-Vorfahren. Der Rollup wurde gemessen und verworfen -- ueber 17 Familiengraphen haette er die Grundgesamtheit von 224 auf 227 FUNCs gehoben, drei Stueck, und waere ein ZWEITER Kettenbegriff neben R-30s gewesen (die Drift-Klasse aus CR-SM-276). Ein zerlegter Block misst deshalb 0, und das ist richtig: FC-03 verbietet ein Kettenglied mit verschachtelter Zerlegung, er KANN die Kante nicht tragen. KEIN `infrastructure`-Flag an der Kennzahl: ein Flag waere ein Urteil in der Messung, die Schwelle gehoert in die Policy -- dieselbe Trennung, die `moduleMetrics` gegen MT-01/MT-02 haelt. SCHWELLE ABGELESEN, aber NICHT an der Verteilungsform: die ist ein duenner Schwanz (224 FUNCs in 1 Kette, 37 in 2, 6 in 3, 8 in 4, je 1 in 5/6/11) und gibt wie `instability` keine Schwelle her (CR-SM-293). Abgelesen ist die WIRKUNG: auf graphcodes 31 Uebergaben ohne gemeinsame Kette stellen die Schwellen 3 und 4 identisch 19 frei, 5 und 6 identisch 6 -- ein Plateau 3..4, innerhalb dessen die Wahl wirkungsfrei ist; der Startwert ist seine Untergrenze. Prior: MAJOR (CR-SM-312): R-04 misst nur noch die Randbreite des Moduls (verschiedene Vertraege ueber den Modulrand, Schwelle `boundaryWidth` wie BW-02) und steht in STEER_RULES; `policy.moduleSize` entfaellt ersatzlos -- Pflichtfeld weg, Befunde verschieben sich. Prior: MAJOR (CR-SM-311): RD-04 misst die Breite je Ebene als BAND -- neues Pflichtfeld `decompositionBreadth.min` (Default 3, Z3) und die neue Regel RD-05 fuer die Untergrenze (Katalog 70 -> 71), und ein MOD zaehlt Untermodule PLUS eigene Blatt-FUNC (das Allokations-Bein aus CR-SM-296 kommt als Breite zurueck; R-04 wird Kopplung, CR-SM-312). Breaking fuer jede Config (Pflichtfeld) und fuer Befunde (Untergrenze neu, MOD-Breite neu). Prior: MAJOR (CR-SM-307): IO-02 -- ein FLOW hat genau EINEN Produzenten, severity `error`. Katalog 69 -> 70. Reine Ergaenzung im Umfang, MAJOR nach Gate 8: eine neue error-Regel KANN fremde Graphen fallen lassen, und diese tut es -- gemessen ueber fuenf Systeme sind es graphcode 20/42 FLOWs, graph-view-edit 5/8, bok 3/15, waehrend moneyflow (0/220) und test_karp (0/21) sauber sind. Die beiden Referenzen erfuellen sogar die STRENGERE beidseitige Fassung; die Lockerung rettet sie nicht, sie brauchen sie nicht. WARUM NUR DIE PRODUZENTENSEITE: der Inhalt eines FLOW kommt von einer Stelle -- haengen zwei Quellen dran, sind es zwei Fluesse unter einem Namen und kein Leser weiss, welche Fassung er bekommt (Beleg aus dem Bestand: graphcodes FLOW-metric-policy trug ACTOR-owner UND FUNC-load-config, und seine eigene Beschreibung sagte es woertlich -- 'Dieselbe Form in zwei Fassungen, deshalb ein Vertrag'). Mehrere KONSUMENTEN sind dagegen die normale Gestalt eines geteilten Vertrags: eine Quelle, viele Leser, genau wie eine zentrale Konfiguration aussieht. Die beidseitige Fassung wurde GEMESSEN und VERWORFEN -- sie haette allein in graphcode 8 saubere 1:N-Muster bestraft (FLOW-dimension-readiness 1x7, FLOW-steering-snapshot 1x3, sechs weitere) und keinen einzigen zusaetzlichen echten Fehler gefunden; tests/unit/se-rule-trigger-proof.test.ts haelt die Gegenprobe fest, damit sie nicht unbemerkt zurueckkehrt. GATE 4, keine Doppelzaehlung: R-10 prueft die UNTERgrenze derselben Achse (mindestens ein Produzent, mindestens ein Konsument), IO-02 die OBERgrenze der Produzentenseite -- |P| = 0 und |P| > 1 sind disjunkt, die beiden koennen am selben FLOW nie zugleich feuern. Getrennte IDs, weil es getrennte Fragen sind (CR-SM-296): R-10 fragt 'hat dieser Fluss ueberhaupt Enden?', IO-02 'kommt sein Inhalt von einer Stelle?'. WARUM ES BIS HIERHER KEIN GATE PRUEFTE: die vier io-Zeilen in TRACE_PATTERNS tragen kein cardinality-Feld und fallen durch REQUIRED_PATTERNS (= TRACE_PATTERNS.filter(p => p.cardinality === '1')); die SCHEMA-Haelfte IST Grammatik (FLOW -relation-> SCHEMA [1..1], R-18 seit CR-SM-271 Teil 2), die io-Haelfte war nie deklariert -- kein wieder aufgerissenes Loch, die Frage wurde nie gestellt. EIN Befund je FLOW, nicht je ueberzaehliger Kante (Klasse CR-SM-242), value = Zahl der Produzenten, threshold 1 (CR-SM-288); dieselbe Quelle zweimal verdrahtet ist EINE Quelle. NICHT in STEER_RULES, und das ist die wichtigste Abgrenzung: das ist Hygiene, keine Steuerdimension, und der Unterschied ist GEGENLAEUFIG gemessen -- die Reparatur in graphcode (CR-GC-499/500, IO-02 20 -> 9) verbesserte fitAdvisory (coherence +0,111, modifiability +0,072) und verschlechterte den Chebyshev-Abstand um 0,25. KORREKTUR (CR-SM-308): nicht, weil ein aufgetrennter Fluss BW-02 hebt -- moduleCrossings zaehlt verschiedene SCHEMA je Rand, reines Auftrennen laesst BW-02 gleich (gemessen graphcode v257: 15 -> 15); der Anstieg kam von drei neuen SCHEMA und umgelegten Ketten. Die Einordnung als Hygiene statt Steuerdimension bleibt eine Entscheidung, kein gemessener Zielkonflikt. Die neun Befunde, die in graphcode bleiben, sind Busse mit 3 bis 19 Produzenten -- un-modellierte Entwurfsarbeit, kein Regelproblem, eigener Spike. Prior: MINOR (CR-SM-305): die sechs RC-Regeln stehen im KATALOG -- ALL_RULE_DEFS 63 -> 69, mit eigenem Profil `conformance`. Reine Ergaenzung: keine bestehende Regel, Severity oder Schwelle aendert sich, kein Graph faellt, das Golden-File der Regelausgabe steht zeichengleich. ROOT CAUSE: das Kongruenz-Urteil existierte, war getestet und erreichte KEINEN Produktionspfad. Gate (`harness.mutate`), Steuerung (fit-advisory, steering-snapshot) und `GET /api/graph/readiness` lesen alle ALL_RULE_DEFS; RC stand nicht darin, weil seine Signatur `(graph, facts: CodeFacts)` statt `(graph, policy)` ist -- der einzige Aufrufer von `scoreReadinessWithConformance` war ein Test. Ein Modell-Zug, der die Bindung an den Code bricht, passierte damit jedes Gate lautlos. `evaluateAllRules` fuehrt RC weiterhin NICHT aus (es hat keine CodeFacts), und genau diese Differenz ist das Signal: deklariert und nicht ausgewertet heisst NICHT GEPRUEFT; die vorhandene Ableitung `ALL_RULE_DEFS minus ausgewertet` macht es von selbst laut -- kein zweiter Zweig, die ND-fail-open-Lehre aus CR-SM-286. `domain` ist NEU an `ConformanceRuleDefinition` und fuer Konstruierer breaking; die Werte sind GEMESSEN statt gesetzt (der Entwurf des CR schlug `['code']`, `['code','interface']` vor -- `domain` ist aber die Grundgesamtheit, und 'code' ist kein Elementtyp): RC-01 10x FUNC, RC-04 4x SCHEMA, RC-05 5x MOD ueber fuenf Familien-Repos, RC-02/RC-03 aus der Schleife gelesen (TEST/SCHEMA), RC-06 aus ELEMENT_ATTRIBUTES -- nur FUNC/MOD/SCHEMA duerfen `external` + `realRef` tragen. NICHT in RULE_TO_DIMENSION/RULE_TO_PHASE, und das ist die zweite Korrektur am eigenen Entwurf: der readiness-Nenner summiert die Grundgesamtheiten der Regeln einer Dimension, eine deklarierte und nicht ausgewertete Regel HEBT also den Score. Gemessen ueber vier Repos, was das geschenkt haette: graph-view-edit `schema` +5,0 Punkte (Nenner 24 -> 40), graphify `arch` +2,1 und `ver` +1,8, alles Uebrige <= 0,2 -- der Ausschlag sitzt genau in der kleinen Dimension, wo die Zahl am meisten wiegt. `computeApplicable` ueberspringt eine Regel ohne Dimension bereits von selbst; die beiden Vollstaendigkeitstests sind auf die AUSGEWERTETEN Profile eingeengt (READINESS_SCORED_PROFILES) und haben das seit CR-228 in ihrem eigenen Kopf behauptet -- nur war der Katalog bis hierher mit den ausgewerteten Regeln identisch. Der Ausloese-Beleg (CR-SM-285) gilt auch fuer RC: sechs synthetische CodeFacts-Fixturen, kein Checkout, wie `CodeFactsSchema` es seit CR-211 zusagt ('testable without any filesystem') -- die Zahl der unbelegten Regeln bleibt 42 und ist nicht um sechs gestiegen. In se-engine deckt CLASS_MAP die sechs als Constraint ab: wohin eine verwaiste realRef zeigen SOLL, steht in keinem Feld, ein Operator daraus waere geraten. Prior: MINOR (CR-SM-300): `RULE_HELP` -- die Regelhilfe liegt neben der Regel. Reine ERGAENZUNG: kein Katalogeintrag, keine Severity, keine Schwelle aendert sich, kein bestehender Graph faellt. Warum sie umzieht: ein Hilfeeintrag ist keine Annotation UEBER einer Regel, er ist Teil ihrer LIEFERUNG -- eine Regel, deren Befund niemand lesen kann, ist nicht fertig. CR-GC-227 hat die Schicht 2025 bewusst in graphcode gelassen (Tempo: kein Bump, kein Familien-Review) und die Verschiebung ausdruecklich als spaetere Entscheidung notiert; Gate 1 verlangt fuer das Wiederaufmachen eine MESSUNG, und die lag am 2026-09-08 vor: von 73 Eintraegen in graphcodes `HELP_CONTENT` zeigten 11 auf Regel-IDs, die es nicht mehr gab (R-03, R-14, R-27, FC-01, SC-04, CR-R04, AO-D01, AO-D03, RT-01, PH-01, CA-01 -- AO-D03 seit drei Wochen), waehrend 8 Katalogregeln ganz ohne Eintrag dastanden (BW-02, BQ-01, BQ-02, BQ-04, BQ-06, BQ-07, ND-01, ND-02) -- darunter mit BW-02 eine der vier STEUERDIMENSIONEN: eine Regel, die rankt und sich nicht erklaert. Die Drift lief in beide Richtungen und ueber vier Releases, obwohl graphcode sehr wohl eine Vorwaertspruefung hat (`tests/help-content.test.ts`) -- sie war rot und blieb es, weil niemand graphcodes Suite las (CR-GC-488). Eine Pruefung im ungelesenen Repo ist keine Pruefung, und hinter einem Symlink gilt kein Versionsbereich (CR-GC-488 Paragraph 1a). Co-located zwingt die Grammatik selbst: eine Streichung nimmt den Eintrag mit, eine Neuanlage kann ohne ihn nicht landen. Umfang: 69 Eintraege (63 Katalog- + 6 Conformance-Regeln), 18,6 KB reine Strings -- der ./browser-Eintrag bleibt transitiv `node:`-frei (CR-SM-230). Im Golden-File steht nur die SCHLUESSELMENGE (`ruleHelp`), nicht die Prosa: 'Prosa ist ein Patch-Bump, keine Grammatik' (CR-SM-244) -- ein umformulierter Satz darf keinen Bump ausloesen, ein weggefallener Eintrag muss. Abgrenzung zu `fix_hint`, das an jeder Regeldefinition schon steht (Gate 3): `fix_hint` ist die Anweisung an den AGENTEN, `plain`/`se` die Erklaerung fuer den MENSCHEN -- verschiedene Leser, verschiedene Laenge, verschiedene Sprache. NICHT mitgezogen und bewusst in graphcode geblieben: Dashboard-Panels, Artefakte, das Vokabular und die sechs Metrik-Dimensionen (`HELP_METRICS`, CR-GC-458) -- sie haengen an graphcodes Oberflaeche, nicht am Regelkatalog. Prior: BREAKING (CR-SM-293): MT-01 zaehlt KOPPLUNG statt aller Kanten, dreht die Richtung um und urteilt per Default nicht mehr. (1) fan_in/fan_out lasen bis hierher JEDE Kante, deren eines Ende einem Modul gehoerte. Gemessen ueber 19 Familiengraphen: von 3281 gezaehlten Kanten sind nur 1117 (34 %) Kopplung. fan_in 2164 = 674 `allocate` -- das ist die MODULGROESSE, ein Modul wurde also stabiler, indem es Funktionen bekam -- plus 610 `FLOW -io-> FUNC`, 348 `FCHAIN -compose-> FUNC`, 255 `CR -relation-> FUNC`, 277 Rest. fan_out 1117 = 507 `FUNC -io-> FLOW` plus 501 `FUNC -satisfy-> REQ`; fast die halbe fan_out war SPEZIFIKATION, dieselbe Begruendung, mit der CR-SM-297 `satisfy` aus LCOM4 genommen hat. Dazu 70 Kanten ueber `MOD -io-> MOD`, ein Muster, das CR-SM-266 D2 aus TRACE_PATTERNS geloescht hat. MT-01 liest jetzt `moduleCrossings` -- DIESELBE Definition von Rand und dieselbe Zaehlbasis (verschiedene Vertraege), die CR-01, R-04 und BW-02 seit CR-SM-274/276 benutzen; MT-01 hatte bis hierher noch eine dritte. (2) RICHTUNG: der KONSUMENT haengt ab. Wer nach draussen liefert, wird gebraucht (afferent, fan_in); wer von draussen bezieht, haengt ab (efferent, fan_out). Das ist Martins Ca/Ce; die alte Lesart 'Kante zeigt weg = Abhaengigkeit' drehte jedes reine Verbrauchermodul auf I = 0 (maximal stabil), wo es 1 sein muss. Die Konvention stand vorher NIRGENDS im Repo -- sie war nie entschieden, nur codiert. (3) `DEFAULT_METRIC_POLICY.instability` 0.7 -> null, messen statt urteilen. Die Verteilung ist zweigipflig -- 44 von 149 messbaren Modulen exakt auf 0.00, 45 exakt auf 1.00, und 45 der 51 Befunde bei 0.7 sind genau diese 1.00 -- aus ihr laesst sich keine Schwelle ABLESEN (Praezedenz CR-SM-283/BW-02). Tiefer: I ist eine KOORDINATE, kein Defekt; Martins Satz lautet 'in Richtung Stabilitaet abhaengen', nicht 'I klein halten'. Damit fehlt MT-01 die Eigenschaft, die CR-SM-287 §2 von einer Steuerdimension verlangt (gerichtet, 'weniger ist besser, ohne Diskussion'), und (4) `STEER_RULES` schrumpft auf VIER. Praktisch war die Dimension ohnehin still: `steerScore` liest Verstoesse, und graphcodes Config setzt `instability: null` seit CR-GC-329 -- auf dem einzigen Repo, das taeglich steuert, lief der R5 laengst als R4. Der Nachfolger ist gemessen und angelegt (CR-SM-298): Martins Stable Dependencies Principle als gerichtete Groesse, Abhaengigkeiten 'bergauf' je Modul -- 34 von 305 (11 %), 132 von 146 Modulen bei null, Schwanz bis 6; lokal, gerichtet, budgetierbar, nicht entartet. Die Zahl `instability` bleibt vollstaendig erhalten: graph_metrics, Export und das gve-Dashboard rendern sie weiter, letzteres samt Hinweis 'Instabilitaet wird hier nur gemessen'. Prior: BREAKING (CR-SM-297, Teil 1): `satisfy` faellt aus der LCOM4-Verbindung von MT-02. Die Regel misst KOHAESION, und Kohaesion ist Datenkopplung -- geteilte `io`-Ziele und geteilte FLOW. `satisfy` ist dagegen eine SPEZIFIKATIONS-Beziehung: zwei FUNCs, die dasselbe REQ erfuellen, koennen zur Laufzeit vollstaendig entkoppelt sein. Die Wirkung ging dabei nur in EINE Richtung -- `satisfy` verbindet Gruppen, senkte also LCOM4 und liess Module kohaesiver aussehen, als ihr Datenfluss hergibt. Der Modellierungsgrund wiegt schwerer als der Messgrund: teilen sich zwei Funktionen ein Requirement, ist das REQUIREMENT zu zerlegen, nicht die Kohaesionsmessung zu beschoenigen; RD-02 sagt bereits das Verwandte. Gemessen ueber 19 Familiengraphen: MT-02 23 -> 24 Befunde. `satisfy` hat also fast nie zwei Gruppen verbunden -- die Aenderung ist prinzipiell richtig und praktisch billig. Golden neu verankert OHNE Zahlenaenderung: Totals und Reihenfolge bleiben an allen drei Eingaengen gleich (ssot 99, n700 1475, n3500 17495), nur die MT-02-shas wandern, weil die Meldung den gemessenen LCOM4-Wert woertlich nennt. NOCH OFFEN aus CR-SM-297: die beiden Container-Masse fuer den zerlegten FUNC (Kohaesion analog MT-02, Paar-Kopplung analog CR-01) -- ihre Schwellen sind aus der Verteilung ABZULESEN, nicht zu setzen (Praezedenz CR-SM-283/BW-02). Prior: BREAKING (CR-SM-296): je Frage eine Regel, je Schwelle ein Wert. (1) RD-04 gibt das Bein `FUNC -allocate-> MOD` an R-04 ab. Es zaehlte die allozierten FUNCs je Modul -- also die MODULGROESSE, und die misst R-04 auch. Beide feuerten am selben Modul im selben Batch, mit zwei Schwellen aus zwei Policy-Feldern (RD-04 > 11 gegen R-04 > 12), und welche recht hat, stand nirgends; dieselbe Ursache ging doppelt in den readiness-Nenner. R-04 ist die reichere Aussage -- Groesse GEGEN Kreuzungen, drei Urteile -- und liest ueber moduleCrossings ohnehin dieselben Daten. RD-04 bleibt fuer ZERLEGUNGSBREITE zustaendig: FUNC compose FUNC, SYS/MOD compose MOD und der FUNC-Wurzelwald am SYS (CR-SM-282). (2) Beide Schwellen sind an die Doktrin 7+-2 gebunden statt danebengesetzt: decompositionBreadth.warning 11 -> 9 (die Obergrenze der Doktrin) und moduleSize large 12 -> 9, coupled 8 -> 7. CR-SM-282 hatte gegen die Doktrin-Zahl 5 entschieden, weil `info` bei 5 den readiness-Nenner verwaessert haette -- das Argument traegt fuer die OBERgrenze nicht, denn RD-04 meldet als warning. Wirkung ueber 19 Familiengraphen, gemessen: RD-04 17 -> 15, R-04 9 -> 11, Summe 26 -> 26. Die Zahl bleibt, die Zustaendigkeit wird eindeutig. Prior: BREAKING (CR-SM-295): drei Regeln entfallen, eine wird neu gefasst -- Katalog 66 -> 63. (1) R-14 (UC must have compose) kann NIE ALLEIN feuern: sie prueft nur, ob ein UC irgendeine ausgehende compose-Kante hat, ohne den Zieltyp anzusehen, waehrend UC-01 den REQ und UC-03 die FCHAIN einzeln verlangen. Auf ELEMENTEBENE ueber 19 Familiengraphen gemessen, nicht auf Summen: R-14 (2 Befunde) ist eine echte Teilmenge von UC-01 vereinigt UC-03 (23 Befunde). Ihr Fixhinweis ('Add FCHAIN or REQ') versprach zudem eine Pruefung, die der Code nicht macht. (2) FC-01 (FCHAIN has actor boundary) ist von FC-04 gedeckt -- gemessen FC-01 (5) Teilmenge von FC-04 (27); FC-04 verlangt Eingang UND Ausgang, FC-01 nur eine Beruehrung. Dazu trug FC-01 einen TOTEN Ersatzweg: `hasDirectActorIO` prueft, ob der Eltern-UC actor-adjazent ist, und `ACTOR -io-> UC` hat CR-SM-266 (D1/D4) aus TRACE_PATTERNS entfernt -- der Zweig konnte seit drei Versionen nicht mehr wahr werden. (3) CR-R04 entfaellt und CR-R01 wird NEU GEFASST. Beide zielten daneben: CR-R01 akzeptierte jede relation-Kante, also auch einen CR, der nur an einem Meilenstein haengt (Termin, kein Umfang) -- 5 Befunde bei 49 CRs, die ausschliesslich auf ein MS zeigen. CR-R04 verlangte einen FUNC und war damit zu eng: 21 Befunde, darunter Daten-, Doku- und Konfigurationsaenderungen, die zu Recht keine Funktion beruehren. Die neue Fassung fragt nach dem UMFANG: mindestens eine relation auf FUNC, MOD, SCHEMA, REQ oder UC. Grundgesamtheit woertlich die von CR-R04 (CR-SM-255, nur offene CRs) -- an einem geschlossenen CR ist die Frage Archaeologie. Gemessen an 514 CR-Knoten: 54 ohne Umfangsbezug, davon 0 OFFEN, 52 done, 2 ohne Status. Die Verschaerfung laesst die Historie damit unangetastet und wirkt ab dem naechsten CR. MS-03 (CR ohne MS) bleibt unberuehrt -- sie stellt die komplementaere Frage. (4) R-17 behaelt ihre Pruefung und verliert ihren falschen Fixhinweis, der drei Zieltypen nannte, die sie nie angesehen hat. Prior: BREAKING (CR-SM-294): sechs Regeln entfallen ersatzlos, Katalog 72 -> 66. AO-D01 (RelayNodeDetection) ist STRUKTURELL unmoeglich -- sie verlangt `>= 2` ausgehende `io`-Kanten auf andere FUNC, und `FUNC -io-> FUNC` steht nicht in TRACE_PATTERNS; R-18 lehnt es als error ab. Das ist zeichengleich der Fall, fuer den CR-SM-283 AO-D03 geloescht hat -- AO-D03 fiel, AO-D01 blieb stehen. CA-01, RT-01, PH-01, R-27 und R-03 sind GEGENSTANDSLOS: sie lesen `asil`, `attributes.kind === 'physical'`, `attributes.requires` und `attributes.capability`, und ueber 19 Familiengraphen ist jedes dieser Attribute 0-mal gesetzt (305 MOD, 696 FUNC). Beleg beidseitig: `report:silence` meldet alle sechs mit 0 Befunden ueber den gesamten Bestand, UND keine von ihnen hat eine Ausloese-Fixture in se-rule-trigger-proof.test.ts (CR-SM-285) -- also weder feuert sie irgendwo, noch ist belegt, dass sie ueberhaupt feuern KANN. Genau die Doppelbedingung, die Gate 7 fuer eine Streichung verlangt. NICHT gestrichen wurde CL-01, obwohl derselbe Verdacht bestand: sie feuert 2x in 1 von 19 Graphen. Ihre Aussage waere anderswo besser aufgehoben -- jeder UC muss hinreichend distinct sein, und das saehe man an der FCHAIN -- aber diese Regel EXISTIERT NICHT: CR-SM-283 hat AO-D03 geloescht und dabei festgehalten, dass ihre Aufgabe ('zwei Wirkketten, die auf einer Ebene mitgliedsgleich werden') ungeloest bleibt. CL-01 zu streichen hiesse, eine feuernde Pruefung gegen eine nicht existierende zu tauschen. Wirkung auf die Bestandsgraphen: 0 -> 0 Befunde, kein Graph faellt. Die einzige messbare Folge ist der kleinere NENNER jeder readiness-Dimension -- sechs stumme Regeln hoben bisher jeden Score. Prior: BREAKING (CR-SM-286): ND-01/ND-02 rechnen ihre Aehnlichkeit SELBST; setND01SimilarityMatrix/setND02SimilarityMatrix/getND02SimilarityMatrix entfallen ersatzlos. Die Formel stand als Prosa in contracts und als Code in graphcode/src/kernel/measure/nd-similarity.ts, verbunden durch eine Injektionsnaht. Vier gemessene Kosten: (1) FAIL-OPEN -- ohne Injektion gaben zwei `error`-Regeln [] zurueck, ununterscheidbar von 'keine Duplikate'; moneyflow trug 16 ND-01-Befunde, die ausserhalb graphcodes NIEMAND sah (report:silence, die Spike-Skripte, jedes kuenftige Werkzeug). Die Familie hat fuer 'kann nicht urteilen' eine Konvention (policy.X = null, moduleMetrics.instability = null); ND fiel statt dessen nach gruen. (2) PROZESSWEITER MODULZUSTAND -- `let _nd01Matrix` ist global, graphcode brauchte die Klammer withNDMatrices mit finally-Reset, jeder andere Aufrufer nicht: Graph A urteilte ueber Graph B. (3) AO-D01 liegt IM Gate-Katalog und uebersprang seine Overlap-Pruefung ohne Matrix ('no matrix -> assume pass') -- eine Gate-Regel, die sich stillschweigend abschwaecht. (4) BQ-04 haengt am selben Muster und ist komplett tot. Jetzt: similarity.ts, je Graph WeakMap-gecacht, Formeln und Schwelle 0,85 zeichengleich uebernommen -- dasselbe Urteil an der richtigen Stelle, Praezedenz CR-SM-276 (moduleCrossings). DABEI GEFUNDEN UND BEHOBEN: die portierte Formel las Partner ueber ALLE Traces; se-rule-pair-legality (Pruefung A, CR-SM-278) meldete 35 Faelle, in denen ND-02 erst durch eine grammatikwidrige Kante auf SCHEMA anschlug (ACTOR -compose-> SCHEMA und Verwandte). `partners()` filtert jetzt ueber isValidTrace -- eine Regel darf ihr Verdikt nicht an einer Kante haengen, die R-18 als error ablehnt. Golden bewusst neu verankert und im Test-Header aufgeschluesselt: ssot 98 -> 98 UNVERAENDERT, n700 1038 -> 1478, n3500 5176 -> 17496, der gesamte Zuwachs ist ND-01 auf den synthetischen Eingaengen -- buildFixedSizeGraph KOPIERT den Basisgraphen 11x bzw. 56x, dort hat jedes Element 10 bzw. 55 exakte Duplikate; eine Duplikatsregel MUSS das melden. ND-02 bleibt ueberall 0. BQ-04 ausdruecklich NICHT angeschlossen: es verlangt 'pre-computed EMBEDDING similarity', und Embeddings kann ein reines Vertragspaket nicht rechnen; ein Ersatzmass haette ich erfinden muessen, und der Versuch (0.7*Beschreibung + 0.3*Name) lieferte 0 Befunde an allen neun Familiengraphen und 4950 an einer templatierten Fixture -- beide Gate-7-Ausreisser zugleich. BQ-04 ist damit der naechste AO-D03-Fall und ein eigener se-grammar-review-Vorgang. Prior: BREAKING (CR-SM-285): R-12 findet Zyklen JEDER Laenge statt nur Rueckkanten. Die Regel heisst 'No circular dependencies' und prueft bis hierher nur, ob eine Kante DIREKT zurueckliuft (A->B und B->A). Ein Dreierzyklus A->B->C->A ging vollstaendig durch das Gate, mit null Fehlern. Das ist die gefaehrlichere Haelfte der Fehlerklasse dieser Session: ein Absturz meldet sich, eine Pruefung die ihren Gegenstand nicht erreicht BESTAETIGT. Warum es zaehlt: jeder Rollup der Familie setzt Azyklizitaet voraus -- chainOf/funcChainOf (CR-SM-282/-283) tragen seen-Guards gegen genau diese Endlosschleife, moduleMetrics, die RD-04-Breitenzaehlung und gves Container-Sicht ebenso; unter einem Zyklus ist ein Rollup nicht falsch sondern UNDEFINIERT. Das R-18-Bein aus CR-SM-283 deckt es NICHT: bei A->B->C->A hat jeder Knoten genau EINEN compose-Elternteil, die Baum-Eigenschaft ist formal erfuellt und die Struktur trotzdem kein Baum. Gemessen ueber NEUN Familiengraphen: 0 Zyklen jeder Laenge, also 0 -> 0 Befunde -- die Schaerfung laesst keinen bestehenden Graphen fallen. Major nach Gate 8 trotzdem, weil eine verschaerfte Regel fremde Graphen fallen lassen KANN. EIN Befund je Zyklus statt je Kante (Klasse CR-SM-242), verankert am kleinsten Knoten des Rings und damit unabhaengig vom DFS-Einstieg (Gate 5); die Zweierring-Meldung ist woertlich unveraendert. Dazu zwei Haertungen OHNE Regelwirkung: descriptionOf() gab `el.description ?? ''` und starb an jedem Nicht-String (42/{}/[]/true -> '.trim is not a function', vier von fuenf Typen) -- am Gate hiess das Stacktrace statt block-Verdict; jetzt zaehlt nur ein echter String, alles andere ist 'keine Beschreibung'. Und tests/unit/se-rule-trigger-proof.test.ts ist die Umkehrung von report:silence: je Regel ein Graph der sie feuern LASSEN MUSS, 22 belegt, 50 als unbelegt ausgewiesen mit einer Zahl die nur sinken darf. Beim ersten Lauf fand er sofort einen Fall: ND-01/ND-02 sind `error` und beginnen mit `if (!_nd01Matrix) return []` -- ohne injizierte Aehnlichkeitsmatrix fallen sie nach GRUEN, ununterscheidbar von 'keine Duplikate'. Dokumentiert und eingefroren, nicht behoben: die Severity-/Signalfrage ist ein eigener se-grammar-review-Vorgang. Prior: BREAKING (CR-SM-283): AO-D03 entfaellt ERSATZLOS, BW-02 kommt -- Katalog 74 -> 74, kein Wachstum im Komplexitaetsbudget. (1) AO-D03 (`DuplicatePathDetection`) suchte `FUNC -io-> FUNC`, ein Paar das TRACE_PATTERNS nicht kennt und das R-18 als error ablehnt: die Regel konnte STRUKTURELL nicht feuern und hat es an keinem der fuenf Familiengraphen je getan. Eine Regel die nie feuert kostet trotzdem Nenner-Anteil, Katalogzeile, readiness-Zuordnung, Golden-File-Eintrag und einen Leser der sie unterscheiden muss. Die Aufgabe die sie tragen SOLLTE -- zwei Wirkketten die auf einer Ebene mitgliedsgleich werden -- bleibt ungeloest und ist ein eigener CR; BW-02 ersetzt sie nicht. (2) BW-02 misst die Randbreite der FUNC-WHITEBOX: verschiedene SCHEMA-Vertraege auf `FUNC -io-> FLOW -io-> FUNC` mit einem Endpunkt im compose-Teilbaum und einem ausserhalb. Gerechnet im selben Durchlauf wie `byModule` (module-crossings.ts), damit es EINE Definition von Rand gibt. Der Rollup ist hier nicht Genauigkeit sondern Existenz: ein zerlegter FUNC traegt selbst keine io-Kanten (die liegen an seinen Blaettern), ohne Teilbaum-Aufloesung waere die Zahl strukturell immer 0 -- gemessen FUNC-block-grounding 0 -> 19. Gate 6 des Grammatik-Reviews: 12 Befunde an graphcode, davon 0 in Ueberlappung mit irgendeiner FUNC-Regel (AO-D01 misst Relay-Knoten, IO-01 Kettenkohaerenz, R-31 blosse Verdrahtung). Schwelle 5 aus der Verteilung ABGELESEN, nicht gewaehlt: bok und graph-view-edit enden beide bei 3, graphcode geht bis 19, moneyflow bis 17. Kein info-Level -- bei Schwelle 3 truege bok 4 Befunde auf 9 Blackboxes, und readiness zaehlt info ungefiltert in den Nenner (MT-03-Praezedenz). (3) VIERTES BEIN von R-18: ein Element hat hoechstens einen compose-Elternteil (FUNC/FUNC und MOD/MOD), error. TRACE_PATTERNS begrenzt heute nur die KINDER einer Kante, nie die ELTERN -- graph_mutate legt eine zweite compose-Kante anstandslos an. 0 Verstoesse ueber fuenf Graphen, und trotzdem hart, weil jeder Rollup der Familie unter Verletzung nicht falsch sondern UNDEFINIERT ist (byModule/byFunc, RD-04-Breite, die Container-Sicht von gve). Keine eigene Regel-ID, Praezedenz CR-SM-271 Teil 2: dieselbe Aussage, derselbe naechste Schritt. EIN Befund je KIND, nicht je ueberzaehliger Kante. (4) `policy.boundaryWidth` neu (nullable, Default {warning: 5}) -- fuer Konstruierer einer MetricPolicy breaking. Prior: BREAKING (CR-SM-282): zwei Blackbox-Luecken geschlossen, und beide lassen bestehende Graphen fallen. (1) RD-04 zaehlt den FUNC-WURZELWALD, verankert am SYS-Knoten. Die Regel zaehlt Kinder je Parent; die Wurzeln des `FUNC -compose-> FUNC`-Waldes haben keinen, also war die oberste Funktionsebene unsichtbar -- gemessen: moneyflow hat 306 Wurzel-FUNCs und RD-04 meldete 0. Die MOD-Seite hatte das Problem nie, weil `SYS -compose-> MOD` eine echte Kante ist; die FUNC-Seite hat keine (`se:top-level`: der obere FUNC-Satz ist eine PROJEKTION, keine Kante). Anker ist der einzige SYS-Knoten; gibt es nicht genau einen, schweigt der Zweig statt zu raten (R-17 deckt den Fall). (2) `moduleCrossings.byModule` und die Groesse in R-04 lesen jetzt die BESITZKETTE (direkt alloziertes MOD + compose-Vorfahren) statt nur der direkten Allokation. Ein Eltern-MOD hat keine direkt allozierten FUNCs -- es kam in `byModule` nicht vor, `funcCount` war 0, und R-04 schwieg per `funcCount <= coupled` an genau der Whitebox, in die der Leser hineinklickt. `pairs` (CR-01) bleibt BEWUSST auf der direkten Zuordnung: 'wie stark haengen DIESE zwei Module aneinander' ist eine Blattebenen-Frage, ein Elternpaar meldete dieselbe Kopplung ein zweites Mal -- CR-01 ist an allen fuenf Familiengraphen zeichengleich geblieben (5/3/15/0/63). Delta gesamt: moneyflow RD-04 +1, sirail R-04 +1, bok/gve/graphcode unveraendert. (3) Die Schwelle steht in `policy.decompositionBreadth` statt als `DECOMPOSITION_BREADTH_MAX = 11` inline in rules.ts -- dieselbe Klasse, die CR-SM-236 fuer R-04 aufgeloest hat; `null` schaltet die Regel ab, Default 11 = verhaltensgleich. Das neue Pflichtfeld in MetricPolicy ist fuer Konstruierer breaking. Die Doktrin-Zahl 5 (`se:top-level`) ist bewusst NICHT der Default: `info` bei 5 haette an graphcode 20 zusaetzliche Befunde erzeugt, und readiness zaehlt `info` ungefiltert in den Nenner (`score = 1 - Verstoesse / applicable`) -- genau der Grund, aus dem MT-03 zur Messung statt zur Regel wurde (readiness.ts). Wer doktrin-streng fahren will, setzt 5. Prior: BREAKING (CR-SM-271 Teil 2): SC-04 entfaellt ersatzlos. Ein FLOW ohne SCHEMA ist kein schwaecherer Typ, sondern untypisiert — die Untergrenze ist Grammatik geworden (`FLOW -relation-> SCHEMA [1..1]`, META_MODEL 5.0.0) und meldet als drittes Bein von R-18 (error, mit candidate_targets im Kontext wie zuvor SC-04). Kein Regel-Loch: derselbe Sachverhalt, EIN Befund, nur haerter und am Gate durchgesetzt statt nur gemeldet. Der Katalog schrumpft um eine Zeile (Nenner, readiness-Zuordnung 'schema'/CDR und Golden-File-Eintrag fallen mit); der Fall zaehlt jetzt unter R-18/'arch'/PDR.
|
|
9
9
|
/** Meta-model version (trace pattern constraints + format-e parser). */
|
|
10
10
|
export const META_MODEL_VERSION = '5.1.0'; // MINOR (CR-SM-317, ITEM-2026-064): neues Muster `TEST -verify-> SCHEMA` — der Vertragstest haengt am Vertrag, nicht an einer Hilfs-REQ; additiv, kein bestehender Graph faellt. Prior: // BREAKING (CR-SM-271 Teil 2): `FLOW -relation-> SCHEMA` wird `1..1` — die erste DURCHGESETZTE Untergrenze (minOccurs/REQUIRED_PATTERNS; nur exakt-`1`, die `1..*`-compose-Untergrenzen bleiben Vollstaendigkeits-Warnungen ihrer Regeln). Migrationsbestand vorab abgetragen: graphcode 2, sigloch-modules 4, graph-view-edit 5 (je durchs Gate), urbanmobility-Fixture geloescht (39, Spike ohne Referenzen); sirail 4 offen (EXPORT_PENDING-Divergenz seit 2026-08-18).
|
|
11
11
|
export * from './ontology.js';
|
|
@@ -24,6 +24,29 @@ export declare function mt01Instability(graph: OntologyGraph, policy: MetricPoli
|
|
|
24
24
|
* CR-SM-233: die Stufen sind Eingabe, `null` heißt messen statt urteilen.
|
|
25
25
|
*/
|
|
26
26
|
export declare function mt02Lcom4(graph: OntologyGraph, policy: MetricPolicy): RuleViolation[];
|
|
27
|
+
/**
|
|
28
|
+
* MT-04: LCOM4 der FUNC-WHITEBOX (CR-SM-327).
|
|
29
|
+
*
|
|
30
|
+
* Dasselbe Prinzip wie MT-02, im anderen Baum: die Kinder eines zerlegten FUNC sind die Boxen
|
|
31
|
+
* seiner Ebene, jede mit den Vertraegen ihres Teilbaums, verbunden ueber den geteilten Vertrag.
|
|
32
|
+
* BW-02 misst am selben Element den RAND (CR-SM-283) — wie breit die Whitebox nach aussen ist;
|
|
33
|
+
* hier steht, ob sie innen zusammenhaengt. Zwei Aussagen, zwei Fixes: BW-02 heisst „schneide
|
|
34
|
+
* die Schnittstelle", MT-04 heisst „das sind zwei Bloecke".
|
|
35
|
+
*
|
|
36
|
+
* EIGENE REGEL statt `domain: ['MOD','FUNC']` an MT-02, und das ist keine Geschmacksfrage: die
|
|
37
|
+
* Grundgesamtheit einer Regel geht in den NENNER ihrer Readiness-Dimension (CR-SM-235/239).
|
|
38
|
+
* MT-02 liegt in `alloc` (Kern MOD); haengte man FUNC an sie, waechse `alloc.applicable` um
|
|
39
|
+
* jede FUNC des Graphen — gemessen ueber sieben Familiengraphen steigt der alloc-Score dadurch
|
|
40
|
+
* um bis zu 0,027 (graphcode 0,932 -> 0,955), ohne dass sich am Graphen etwas verbessert haette.
|
|
41
|
+
* Genau der Nenner-Defekt der Klasse CR-SM-235/270. Als eigene Regel in `arch` (Kern FUNC) ist
|
|
42
|
+
* die Bewegung <= 0,008, und die Befunde landen dort, wo FUNC der Gegenstand ist.
|
|
43
|
+
* Praezedenz fuer den Schnitt: R-20/R-26/R-27 — dieselbe Aussage („braucht einen realRef"),
|
|
44
|
+
* drei Populationen, drei IDs.
|
|
45
|
+
*
|
|
46
|
+
* Die Schwellen sind dieselben (`policy.lcom4`): dieselbe Masszahl, dieselbe Skala. Ein eigenes
|
|
47
|
+
* Policy-Feld waere eine zweite Schwelle fuer dieselbe Frage.
|
|
48
|
+
*/
|
|
49
|
+
export declare function mt04WhiteboxLcom4(graph: OntologyGraph, policy: MetricPolicy): RuleViolation[];
|
|
27
50
|
/** One module's allocation-cohesion measurement (CR-SM-223). */
|
|
28
51
|
export interface AllocationCohesion {
|
|
29
52
|
moduleId: string;
|
|
@@ -69,14 +92,28 @@ export interface ModuleMetrics {
|
|
|
69
92
|
fanOut: number;
|
|
70
93
|
/** null when fanIn + fanOut === 0 — no signal, no substitute value. */
|
|
71
94
|
instability: number | null;
|
|
72
|
-
/**
|
|
95
|
+
/**
|
|
96
|
+
* MT-02 core: connected-component count ueber die BOXEN dieser Modul-Whitebox (CR-SM-326).
|
|
97
|
+
* `null`, wo nichts zu messen ist: unter zwei Boxen, oder wenn keine einzige Box einen
|
|
98
|
+
* Vertrag traegt — dann meldet R-31 die unverdrahteten Funktionen am Element, und eine Zahl
|
|
99
|
+
* hier waere keine Aussage ueber Kohaesion, sondern dieselbe Aussage ein zweites Mal.
|
|
100
|
+
*/
|
|
73
101
|
lcom4: number | null;
|
|
102
|
+
/**
|
|
103
|
+
* CR-SM-326: die Zahl der BOXEN, ueber die `lcom4` gerechnet wurde — Sub-MODs
|
|
104
|
+
* (`MOD -compose-> MOD`) plus die direkt allozierten FUNC. Sie ist der Nenner der MT-02-Meldung
|
|
105
|
+
* und NICHT `allocatedFuncs`: ein Eltern-MOD hat Boxen ohne eine einzige eigene Allokation.
|
|
106
|
+
*/
|
|
107
|
+
lcom4Boxes: number;
|
|
74
108
|
/**
|
|
75
109
|
* CR-SM-263: JEDE allozierte FUNC ist ein zerlegter Block (Rollup), das MOD also ein
|
|
76
|
-
* Container statt eines Implementierungsmoduls.
|
|
77
|
-
* garantiert durch CR-SM-256 — eine wahre Zahl ohne Aussage ueber Kohaesion. MT-02
|
|
78
|
-
* urteilt hier nicht; der Wert steht trotzdem da, damit der Leser ihn deuten kann.
|
|
110
|
+
* Container statt eines Implementierungsmoduls.
|
|
79
111
|
* `false` unterhalb von 2 allozierten FUNCs (kein Container, nur zu klein zum Messen).
|
|
112
|
+
*
|
|
113
|
+
* CR-SM-326: seither eine reine MESSUNG. Das Urteil haengt nicht mehr daran — eine Box erbt
|
|
114
|
+
* die Vertraege ihres Teilbaums, also ist LCOM4 an einem Container genauso aussagekraeftig
|
|
115
|
+
* wie an einem Implementierungsmodul. Der Wert steht hier, weil er die Lesart der Zeile
|
|
116
|
+
* erklaert (Bloecke statt Blaetter), nicht weil er etwas stilllegt.
|
|
80
117
|
*/
|
|
81
118
|
rollupContainer: boolean;
|
|
82
119
|
/** The CR-SM-223 measurement, deliberately threshold-free. null where contracts omits it. */
|
|
@@ -132,5 +169,11 @@ export declare const MT_RULES: readonly [{
|
|
|
132
169
|
readonly severity: "info";
|
|
133
170
|
readonly evaluate: typeof mt02Lcom4;
|
|
134
171
|
readonly domain: readonly ["MOD"];
|
|
172
|
+
}, {
|
|
173
|
+
readonly id: "MT-04";
|
|
174
|
+
readonly name: "Whitebox cohesion (LCOM4)";
|
|
175
|
+
readonly severity: "info";
|
|
176
|
+
readonly evaluate: typeof mt04WhiteboxLcom4;
|
|
177
|
+
readonly domain: readonly ["FUNC"];
|
|
135
178
|
}];
|
|
136
179
|
export declare function evaluateMTRules(graph: OntologyGraph, policy: MetricPolicy): RuleViolation[];
|
package/dist/se/metric-rules.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { decomposedFuncs } from './rules.js';
|
|
2
2
|
import { indexOf } from './graph-index.js';
|
|
3
|
-
import { moduleCrossings } from './module-crossings.js';
|
|
3
|
+
import { moduleCrossings, subtreeFuncs, funcSubtree, boxContracts } from './module-crossings.js';
|
|
4
4
|
/** Geteilte leere Menge — spart je Modul zwei Allokationen in `measureModulesUncached`. */
|
|
5
5
|
const EMPTY_SET = new Set();
|
|
6
6
|
/**
|
|
@@ -53,16 +53,13 @@ export function mt02Lcom4(graph, policy) {
|
|
|
53
53
|
return violations;
|
|
54
54
|
// CR-SM-232: die Union-Find-Rechnung steht in `measureModules`; hier nur die Stufen.
|
|
55
55
|
for (const m of measureModules(graph)) {
|
|
56
|
+
// CR-SM-326: `null` deckt jetzt beide Faelle, in denen nichts zu urteilen ist — unter zwei
|
|
57
|
+
// Boxen, und ohne einen einzigen Vertrag im ganzen Modul. Die Ausnahme aus CR-SM-263
|
|
58
|
+
// (reiner Container-MOD) ist damit erledigt: seit die Box ihren Teilbaum erbt, traegt auch
|
|
59
|
+
// ein Rollup Vertraege, und die Regel ist dort erfuellbar statt still.
|
|
56
60
|
if (m.lcom4 === null)
|
|
57
61
|
continue;
|
|
58
|
-
|
|
59
|
-
// LCOM4 verbindet ueber geteilte io-ZIELE, und CR-SM-256 hat festgehalten, dass
|
|
60
|
-
// ein Rollup genau die nicht traegt. Die Regel war dort nicht streng, sondern
|
|
61
|
-
// unerfuellbar: gruen wird sie erst mit der Kante, die CR-SM-256 fuer falsch erklaert.
|
|
62
|
-
// Die MESSUNG bleibt (`moduleMetrics` liefert `lcom4` unveraendert) — nur das Urteil faellt.
|
|
63
|
-
if (m.rollupContainer)
|
|
64
|
-
continue;
|
|
65
|
-
const message = `${m.moduleName} has LCOM4=${m.lcom4} (${m.allocatedFuncs} FUNCs in ${m.lcom4} disconnected groups)`;
|
|
62
|
+
const message = `${m.moduleName} has LCOM4=${m.lcom4} (${m.lcom4Boxes} boxes in ${m.lcom4} disconnected groups)`;
|
|
66
63
|
// CR-SM-288: EIN Budget fuer beide Stufen — `steps.warning - 1` ist der groesste LCOM4,
|
|
67
64
|
// der noch drin liegt. Die `info`-Stufe meldet frueher, aber sie meldet keine Ueberschreitung;
|
|
68
65
|
// ihre Masse ist 0.
|
|
@@ -76,6 +73,65 @@ export function mt02Lcom4(graph, policy) {
|
|
|
76
73
|
}
|
|
77
74
|
return violations;
|
|
78
75
|
}
|
|
76
|
+
/**
|
|
77
|
+
* MT-04: LCOM4 der FUNC-WHITEBOX (CR-SM-327).
|
|
78
|
+
*
|
|
79
|
+
* Dasselbe Prinzip wie MT-02, im anderen Baum: die Kinder eines zerlegten FUNC sind die Boxen
|
|
80
|
+
* seiner Ebene, jede mit den Vertraegen ihres Teilbaums, verbunden ueber den geteilten Vertrag.
|
|
81
|
+
* BW-02 misst am selben Element den RAND (CR-SM-283) — wie breit die Whitebox nach aussen ist;
|
|
82
|
+
* hier steht, ob sie innen zusammenhaengt. Zwei Aussagen, zwei Fixes: BW-02 heisst „schneide
|
|
83
|
+
* die Schnittstelle", MT-04 heisst „das sind zwei Bloecke".
|
|
84
|
+
*
|
|
85
|
+
* EIGENE REGEL statt `domain: ['MOD','FUNC']` an MT-02, und das ist keine Geschmacksfrage: die
|
|
86
|
+
* Grundgesamtheit einer Regel geht in den NENNER ihrer Readiness-Dimension (CR-SM-235/239).
|
|
87
|
+
* MT-02 liegt in `alloc` (Kern MOD); haengte man FUNC an sie, waechse `alloc.applicable` um
|
|
88
|
+
* jede FUNC des Graphen — gemessen ueber sieben Familiengraphen steigt der alloc-Score dadurch
|
|
89
|
+
* um bis zu 0,027 (graphcode 0,932 -> 0,955), ohne dass sich am Graphen etwas verbessert haette.
|
|
90
|
+
* Genau der Nenner-Defekt der Klasse CR-SM-235/270. Als eigene Regel in `arch` (Kern FUNC) ist
|
|
91
|
+
* die Bewegung <= 0,008, und die Befunde landen dort, wo FUNC der Gegenstand ist.
|
|
92
|
+
* Praezedenz fuer den Schnitt: R-20/R-26/R-27 — dieselbe Aussage („braucht einen realRef"),
|
|
93
|
+
* drei Populationen, drei IDs.
|
|
94
|
+
*
|
|
95
|
+
* Die Schwellen sind dieselben (`policy.lcom4`): dieselbe Masszahl, dieselbe Skala. Ein eigenes
|
|
96
|
+
* Policy-Feld waere eine zweite Schwelle fuer dieselbe Frage.
|
|
97
|
+
*/
|
|
98
|
+
export function mt04WhiteboxLcom4(graph, policy) {
|
|
99
|
+
const violations = [];
|
|
100
|
+
const steps = policy.lcom4;
|
|
101
|
+
if (steps === null)
|
|
102
|
+
return violations;
|
|
103
|
+
const idx = indexOf(graph);
|
|
104
|
+
const decomposed = decomposedFuncs(graph);
|
|
105
|
+
// In GRAPH-Reihenfolge, nicht in der Reihenfolge der `compose`-Kanten: der Anker eines
|
|
106
|
+
// Befundes darf nicht an der Kantenreihenfolge haengen (Gate 5).
|
|
107
|
+
for (const fn of idx.elementsOfType('FUNC')) {
|
|
108
|
+
if (!decomposed.has(fn.id))
|
|
109
|
+
continue;
|
|
110
|
+
const kids = idx.out(fn.id, 'compose')
|
|
111
|
+
.filter(t => idx.typeOf(t.target) === 'FUNC')
|
|
112
|
+
.map(t => t.target);
|
|
113
|
+
const boxes = kids.map(kid => boxContracts(graph, funcSubtree(graph, kid)));
|
|
114
|
+
const lcom4 = lcom4OfBoxes(boxes);
|
|
115
|
+
if (lcom4 === null || lcom4 < steps.info)
|
|
116
|
+
continue;
|
|
117
|
+
const message = `${fn.name} has LCOM4=${lcom4} (${boxes.length} boxes in ${lcom4} disconnected groups)`;
|
|
118
|
+
const context = {
|
|
119
|
+
element_type: fn.type,
|
|
120
|
+
element_name: fn.name,
|
|
121
|
+
value: lcom4,
|
|
122
|
+
threshold: steps.warning - 1,
|
|
123
|
+
};
|
|
124
|
+
violations.push({
|
|
125
|
+
rule_id: 'MT-04',
|
|
126
|
+
severity: lcom4 >= steps.warning ? 'warning' : 'info',
|
|
127
|
+
element_id: fn.id,
|
|
128
|
+
message,
|
|
129
|
+
fix_hint: 'Split the whitebox along its groups, or wire the groups together where they really share data — a block whose children share no contract is two blocks',
|
|
130
|
+
context,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
return violations;
|
|
134
|
+
}
|
|
79
135
|
/**
|
|
80
136
|
* CR-SM-221: connection pairs between non-FLOW elements, FLOW-transitive.
|
|
81
137
|
*
|
|
@@ -196,6 +252,7 @@ function measureModulesUncached(graph) {
|
|
|
196
252
|
for (const mod of mods) {
|
|
197
253
|
const allocated = idx.in(mod.id, 'allocate').map(t => t.source);
|
|
198
254
|
const modFuncIds = new Set(allocated);
|
|
255
|
+
const boxes = boxesOfModule(graph, mod.id, allocated);
|
|
199
256
|
// --- MT-01: fan-in / fan-out als querende VERTRAEGE je Richtung (CR-SM-293) ---
|
|
200
257
|
const fanIn = (crossings.afferentContracts.get(mod.id) ?? EMPTY_SET).size;
|
|
201
258
|
const fanOut = (crossings.efferentContracts.get(mod.id) ?? EMPTY_SET).size;
|
|
@@ -207,7 +264,8 @@ function measureModulesUncached(graph) {
|
|
|
207
264
|
fanIn,
|
|
208
265
|
fanOut,
|
|
209
266
|
instability,
|
|
210
|
-
lcom4:
|
|
267
|
+
lcom4: lcom4OfBoxes(boxes),
|
|
268
|
+
lcom4Boxes: boxes.length,
|
|
211
269
|
rollupContainer: allocated.length >= 2 && allocated.every(f => decomposed.has(f)),
|
|
212
270
|
cohesion: cohesionOf(pairs, modFuncIds),
|
|
213
271
|
// CR-SM-301: braucht die Instabilitaet ALLER Module, also zweiter Durchgang unten.
|
|
@@ -256,88 +314,82 @@ function measureModulesUncached(graph) {
|
|
|
256
314
|
}
|
|
257
315
|
return rows;
|
|
258
316
|
}
|
|
259
|
-
/**
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
// RD-02 sagt bereits das Verwandte (ein Parent-REQ mit direktem FUNC-satisfy; der satisfy
|
|
274
|
-
// gehoert an die Kinder).
|
|
275
|
-
//
|
|
276
|
-
// CR-SM-264: die ausgehenden Kanten kommen aus dem Index. Vorher lief je alloziertem FUNC
|
|
277
|
-
// ein Vollscan ueber alle Traces — MOD x FUNC x T.
|
|
317
|
+
/**
|
|
318
|
+
* CR-SM-326 — die BOXEN einer Modul-Whitebox: ihre Sub-MODs plus ihre direkt allozierten FUNC.
|
|
319
|
+
*
|
|
320
|
+
* Die Blackbox-Sicht verlangt genau das: ein zerlegtes Element steht nicht NEBEN seinen Kindern,
|
|
321
|
+
* es IST sie, von aussen gesehen — und es traegt selbst keine Kante (CR-SM-256). Wer es als
|
|
322
|
+
* gleichrangigen Nachbarn mit leerer Kantenmenge zaehlt, misst die Zerlegungstiefe statt der
|
|
323
|
+
* Kohaesion: je besser zerlegt, desto schlechter die Zahl. Dieselbe Besitzkette benutzen R-04
|
|
324
|
+
* (CR-SM-282), BW-02 (CR-SM-283) und R-23 (CR-SM-321).
|
|
325
|
+
*
|
|
326
|
+
* Ein Sub-MOD bringt die FUNCs seines ganzen Modul-Teilbaums mit (`subtreeFuncs`), jede davon
|
|
327
|
+
* mit ihrem eigenen compose-Teilbaum — sonst fehlten die Blaetter, die gar nicht eigens
|
|
328
|
+
* alloziert sind.
|
|
329
|
+
*/
|
|
330
|
+
function boxesOfModule(graph, modId, allocated) {
|
|
278
331
|
const idx = indexOf(graph);
|
|
279
|
-
const
|
|
280
|
-
for (const
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
332
|
+
const boxes = [];
|
|
333
|
+
for (const t of idx.out(modId, 'compose')) {
|
|
334
|
+
if (idx.typeOf(t.target) !== 'MOD')
|
|
335
|
+
continue;
|
|
336
|
+
const funcs = new Set();
|
|
337
|
+
for (const f of subtreeFuncs(graph, t.target))
|
|
338
|
+
for (const x of funcSubtree(graph, f))
|
|
339
|
+
funcs.add(x);
|
|
340
|
+
boxes.push(boxContracts(graph, funcs));
|
|
285
341
|
}
|
|
286
|
-
const
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
342
|
+
for (const func of allocated)
|
|
343
|
+
boxes.push(boxContracts(graph, funcSubtree(graph, func)));
|
|
344
|
+
return boxes;
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* MT-02 core: die Zahl der Komponenten ueber den Boxen. Verbunden sind zwei Boxen, wenn sie
|
|
348
|
+
* einen VERTRAG teilen — dieselbe Zaehlbasis, mit der CR-01, R-04, BW-02 und MT-01 seit
|
|
349
|
+
* CR-SM-274/276/293 Kopplung zaehlen. Vorher lief die Verbindung ueber FLOW-Identitaet, also
|
|
350
|
+
* ueber einen zweiten Kopplungsbegriff im selben Katalog.
|
|
351
|
+
*
|
|
352
|
+
* CR-SM-297 bleibt in Kraft: `satisfy` verbindet nicht. LCOM4 misst Datenkopplung; teilen sich
|
|
353
|
+
* zwei Funktionen ein Requirement, gehoert das REQUIREMENT zerlegt.
|
|
354
|
+
*
|
|
355
|
+
* `null` heisst messen, nicht urteilen (CR-SM-233) — hier zweimal: unter zwei Boxen gibt es
|
|
356
|
+
* keine Zerfallenheit, und ohne einen einzigen Vertrag gibt es keine Kopplung, ueber die man
|
|
357
|
+
* urteilen koennte. Der zweite Fall gehoert R-31 (`FUNC must be wired`), das ihn am Element
|
|
358
|
+
* meldet; eine Zahl hier waere dieselbe Aussage ein zweites Mal.
|
|
359
|
+
*/
|
|
360
|
+
function lcom4OfBoxes(boxes) {
|
|
361
|
+
if (boxes.length < 2)
|
|
362
|
+
return null;
|
|
363
|
+
if (boxes.every(b => b.size === 0))
|
|
364
|
+
return null;
|
|
365
|
+
const parent = boxes.map((_, i) => i);
|
|
366
|
+
const find = (x) => {
|
|
367
|
+
let cur = x;
|
|
368
|
+
while (parent[cur] !== cur) {
|
|
369
|
+
parent[cur] = parent[parent[cur]];
|
|
370
|
+
cur = parent[cur];
|
|
292
371
|
}
|
|
293
|
-
return
|
|
294
|
-
}
|
|
295
|
-
|
|
372
|
+
return cur;
|
|
373
|
+
};
|
|
374
|
+
const union = (a, b) => {
|
|
296
375
|
const ra = find(a), rb = find(b);
|
|
297
376
|
if (ra !== rb)
|
|
298
|
-
parent
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
}
|
|
312
|
-
// CR-165/CR-171: FUNCs sharing the same FLOW (via io in either direction) are connected.
|
|
313
|
-
const funcIdSet = new Set(funcIds);
|
|
314
|
-
const flowToFuncs = new Map();
|
|
315
|
-
for (const t of graph.traces) {
|
|
316
|
-
if (t.type !== 'io')
|
|
317
|
-
continue;
|
|
318
|
-
if (funcIdSet.has(t.target)) {
|
|
319
|
-
const srcEl = graph.elements.find(e => e.id === t.source);
|
|
320
|
-
if (srcEl?.type === 'FLOW') {
|
|
321
|
-
if (!flowToFuncs.has(t.source))
|
|
322
|
-
flowToFuncs.set(t.source, new Set());
|
|
323
|
-
flowToFuncs.get(t.source).add(t.target);
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
if (funcIdSet.has(t.source)) {
|
|
327
|
-
const tgtEl = graph.elements.find(e => e.id === t.target);
|
|
328
|
-
if (tgtEl?.type === 'FLOW') {
|
|
329
|
-
if (!flowToFuncs.has(t.target))
|
|
330
|
-
flowToFuncs.set(t.target, new Set());
|
|
331
|
-
flowToFuncs.get(t.target).add(t.source);
|
|
332
|
-
}
|
|
377
|
+
parent[ra] = rb;
|
|
378
|
+
};
|
|
379
|
+
// Ein Durchgang ueber alle Vertraege statt des Paarvergleichs: der erste Traeger eines
|
|
380
|
+
// Vertrags zieht jeden weiteren an sich. Linear in der Summe der Boxgroessen — und das
|
|
381
|
+
// Ergebnis haengt nicht an der Reihenfolge, weil die Komponentenzahl es nicht tut.
|
|
382
|
+
const firstHolder = new Map();
|
|
383
|
+
for (let i = 0; i < boxes.length; i++) {
|
|
384
|
+
for (const contract of boxes[i]) {
|
|
385
|
+
const first = firstHolder.get(contract);
|
|
386
|
+
if (first === undefined)
|
|
387
|
+
firstHolder.set(contract, i);
|
|
388
|
+
else
|
|
389
|
+
union(first, i);
|
|
333
390
|
}
|
|
334
391
|
}
|
|
335
|
-
|
|
336
|
-
const arr = [...funcsInFlow];
|
|
337
|
-
for (let i = 1; i < arr.length; i++)
|
|
338
|
-
union(arr[0], arr[i]);
|
|
339
|
-
}
|
|
340
|
-
return new Set(funcIds.map(find)).size;
|
|
392
|
+
return new Set(boxes.map((_, i) => find(i))).size;
|
|
341
393
|
}
|
|
342
394
|
/** CR-SM-223 core: internal vs. external connection pairs. null where there is no signal. */
|
|
343
395
|
function cohesionOf(pairs, funcIds) {
|
|
@@ -400,6 +452,9 @@ export function allocationCohesion(graph) {
|
|
|
400
452
|
export const MT_RULES = [
|
|
401
453
|
{ id: 'MT-01', name: 'Module instability', severity: 'warning', evaluate: mt01Instability, domain: ['MOD'] },
|
|
402
454
|
{ id: 'MT-02', name: 'Module cohesion (LCOM4)', severity: 'info', evaluate: mt02Lcom4, domain: ['MOD'] },
|
|
455
|
+
// CR-SM-327: dieselbe Masszahl an der FUNC-Whitebox. Eigene ID, weil die Grundgesamtheit
|
|
456
|
+
// die Readiness-Dimension bestimmt (Begruendung an `mt04WhiteboxLcom4`).
|
|
457
|
+
{ id: 'MT-04', name: 'Whitebox cohesion (LCOM4)', severity: 'info', evaluate: mt04WhiteboxLcom4, domain: ['FUNC'] },
|
|
403
458
|
// MT-03 retired as a rule (CR-SM-223) — see `allocationCohesion` above.
|
|
404
459
|
];
|
|
405
460
|
export function evaluateMTRules(graph, policy) {
|
|
@@ -100,3 +100,20 @@ export declare function crossingContractCount(graph: OntologyGraph, modId: strin
|
|
|
100
100
|
export declare function whiteboxContractCount(graph: OntologyGraph, funcId: string): number;
|
|
101
101
|
/** Die FUNCs im Teilbaum dieses Moduls (direkt alloziert + die seiner Sub-MODs) — CR-SM-282. */
|
|
102
102
|
export declare function subtreeFuncs(graph: OntologyGraph, modId: string): ReadonlySet<string>;
|
|
103
|
+
/**
|
|
104
|
+
* CR-SM-326: `funcId` plus seine `compose`-Nachfahren im FUNC-Baum — der TEILBAUM, an dessen
|
|
105
|
+
* Blaettern die io-Kanten liegen (CR-SM-256). Fuer ein Blatt die Einermenge, also derselbe
|
|
106
|
+
* Aufruf fuer Box und Blatt statt einer Fallunterscheidung beim Aufrufer.
|
|
107
|
+
*/
|
|
108
|
+
export declare function funcSubtree(graph: OntologyGraph, funcId: string): ReadonlySet<string>;
|
|
109
|
+
/**
|
|
110
|
+
* CR-SM-326: die Vertraege, die eine FUNC-Menge BERUEHRT — dieselbe Zaehlbasis wie
|
|
111
|
+
* `byModule`/`byFunc` (SCHEMA, sonst `UNBOUND:<flow>`), nur ohne die Rand-Frage.
|
|
112
|
+
*
|
|
113
|
+
* Warum ohne Rand: MT-02 fragt nicht, was die Box verlaesst, sondern ob zwei Boxen einen
|
|
114
|
+
* Vertrag TEILEN. Fuer diese Frage sind Rand- und Beruehrmenge dasselbe — ein Vertrag, den
|
|
115
|
+
* nur eine Box beruehrt, liegt in keiner zweiten und verbindet niemanden. Die Beruehrmenge
|
|
116
|
+
* ist die billigere von beiden und braucht die Whitebox-Bedingung aus `byFunc` nicht, die
|
|
117
|
+
* ein Blatt ausschliesst.
|
|
118
|
+
*/
|
|
119
|
+
export declare function boxContracts(graph: OntologyGraph, funcIds: Iterable<string>): ReadonlySet<string>;
|
|
@@ -1,4 +1,16 @@
|
|
|
1
1
|
import { indexOf } from './graph-index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Die Vertraege EINES Flusses: seine SCHEMAs, sonst er selbst als untypisierter Vertrag.
|
|
4
|
+
*
|
|
5
|
+
* Eine Stelle fuer die Zaehlbasis aus CR-SM-274 — `build()` und `boxContracts()` lesen
|
|
6
|
+
* dieselbe Funktion, damit es nicht zwei Vorstellungen davon gibt, was ein Vertrag ist.
|
|
7
|
+
*/
|
|
8
|
+
function contractsOfFlow(idx, flowId) {
|
|
9
|
+
const schemas = idx.out(flowId, 'relation')
|
|
10
|
+
.filter(t => idx.typeOf(t.target) === 'SCHEMA')
|
|
11
|
+
.map(t => t.target);
|
|
12
|
+
return schemas.length > 0 ? schemas : [`UNBOUND:${flowId}`];
|
|
13
|
+
}
|
|
2
14
|
const CACHE = new WeakMap();
|
|
3
15
|
function build(graph) {
|
|
4
16
|
const idx = indexOf(graph);
|
|
@@ -85,10 +97,7 @@ function build(graph) {
|
|
|
85
97
|
.filter(t => idx.typeOf(t.target) === 'FUNC')
|
|
86
98
|
.map(t => modOfFunc.get(t.target))
|
|
87
99
|
.filter((m) => !!m));
|
|
88
|
-
const
|
|
89
|
-
.filter(t => idx.typeOf(t.target) === 'SCHEMA')
|
|
90
|
-
.map(t => t.target);
|
|
91
|
-
const contracts = schemas.length > 0 ? schemas : [`UNBOUND:${flow.id}`];
|
|
100
|
+
const contracts = contractsOfFlow(idx, flow.id);
|
|
92
101
|
// CR-SM-283: derselbe Test auf dem compose-Baum. `allEndpoints` statt `endpoints` —
|
|
93
102
|
// die FUNC-Whitebox kennt keine Modul-Zugehoerigkeit, eine unallozierte FUNC liegt
|
|
94
103
|
// trotzdem drinnen oder draussen.
|
|
@@ -194,3 +203,48 @@ export function whiteboxContractCount(graph, funcId) {
|
|
|
194
203
|
export function subtreeFuncs(graph, modId) {
|
|
195
204
|
return moduleCrossings(graph).funcsByModule.get(modId) ?? new Set();
|
|
196
205
|
}
|
|
206
|
+
/**
|
|
207
|
+
* CR-SM-326: `funcId` plus seine `compose`-Nachfahren im FUNC-Baum — der TEILBAUM, an dessen
|
|
208
|
+
* Blaettern die io-Kanten liegen (CR-SM-256). Fuer ein Blatt die Einermenge, also derselbe
|
|
209
|
+
* Aufruf fuer Box und Blatt statt einer Fallunterscheidung beim Aufrufer.
|
|
210
|
+
*/
|
|
211
|
+
export function funcSubtree(graph, funcId) {
|
|
212
|
+
const idx = indexOf(graph);
|
|
213
|
+
const out = new Set();
|
|
214
|
+
const stack = [funcId];
|
|
215
|
+
while (stack.length > 0) {
|
|
216
|
+
const cur = stack.pop();
|
|
217
|
+
if (out.has(cur))
|
|
218
|
+
continue;
|
|
219
|
+
out.add(cur);
|
|
220
|
+
for (const t of idx.out(cur, 'compose')) {
|
|
221
|
+
if (idx.typeOf(t.target) === 'FUNC')
|
|
222
|
+
stack.push(t.target);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return out;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* CR-SM-326: die Vertraege, die eine FUNC-Menge BERUEHRT — dieselbe Zaehlbasis wie
|
|
229
|
+
* `byModule`/`byFunc` (SCHEMA, sonst `UNBOUND:<flow>`), nur ohne die Rand-Frage.
|
|
230
|
+
*
|
|
231
|
+
* Warum ohne Rand: MT-02 fragt nicht, was die Box verlaesst, sondern ob zwei Boxen einen
|
|
232
|
+
* Vertrag TEILEN. Fuer diese Frage sind Rand- und Beruehrmenge dasselbe — ein Vertrag, den
|
|
233
|
+
* nur eine Box beruehrt, liegt in keiner zweiten und verbindet niemanden. Die Beruehrmenge
|
|
234
|
+
* ist die billigere von beiden und braucht die Whitebox-Bedingung aus `byFunc` nicht, die
|
|
235
|
+
* ein Blatt ausschliesst.
|
|
236
|
+
*/
|
|
237
|
+
export function boxContracts(graph, funcIds) {
|
|
238
|
+
const idx = indexOf(graph);
|
|
239
|
+
const out = new Set();
|
|
240
|
+
for (const func of funcIds) {
|
|
241
|
+
const flows = [
|
|
242
|
+
...idx.out(func, 'io').filter(t => idx.typeOf(t.target) === 'FLOW').map(t => t.target),
|
|
243
|
+
...idx.in(func, 'io').filter(t => idx.typeOf(t.source) === 'FLOW').map(t => t.source),
|
|
244
|
+
];
|
|
245
|
+
for (const flow of flows)
|
|
246
|
+
for (const c of contractsOfFlow(idx, flow))
|
|
247
|
+
out.add(c);
|
|
248
|
+
}
|
|
249
|
+
return out;
|
|
250
|
+
}
|
package/dist/se/readiness.js
CHANGED
|
@@ -131,6 +131,10 @@ export const RULE_TO_DIMENSION = {
|
|
|
131
131
|
// MT-03 is no longer here: it became a measurement (`allocationCohesion`), not a
|
|
132
132
|
// rule (CR-SM-223) — a per-module advisory would depress this score permanently.
|
|
133
133
|
'MT-01': 'alloc', 'MT-02': 'alloc',
|
|
134
|
+
// CR-SM-327: MT-04 misst die FUNC-Whitebox, nicht das Modul — Grundgesamtheit FUNC,
|
|
135
|
+
// also `arch` (Kern FUNC). In `alloc` waere sie eine Fremdtyp-Regel und wuerde deren
|
|
136
|
+
// Nenner um jede FUNC des Graphen aufblaehen (Klasse CR-SM-235/270, gemessen im CR).
|
|
137
|
+
'MT-04': 'arch',
|
|
134
138
|
// CR traceability
|
|
135
139
|
'CR-R01': 'cr', 'CR-R02': 'cr', 'CR-R03': 'cr', // architecture optimization
|
|
136
140
|
// CR-SM-283: BW-02 (Whitebox-Randbreite) ersetzt AO-D03 an dieser Stelle — dieselbe
|
|
@@ -199,7 +203,7 @@ export const RULE_TO_PHASE = {
|
|
|
199
203
|
// CR-GC-366: Anschluss des Funktionsbaus — gehoert an dasselbe Gate wie R-15/IO-01,
|
|
200
204
|
// die auf derselben Kette aufsetzen.
|
|
201
205
|
'R-30': 'PDR', 'R-31': 'PDR',
|
|
202
|
-
'MT-01': 'PDR', 'MT-02': 'PDR',
|
|
206
|
+
'MT-01': 'PDR', 'MT-02': 'PDR', 'MT-04': 'PDR', // CR-SM-327: dieselbe Reifephase wie MT-02
|
|
203
207
|
'ND-01': 'PDR',
|
|
204
208
|
'BW-02': 'PDR', 'CR-01': 'PDR', 'IO-01': 'PDR', 'IO-02': 'PDR', // CR-SM-226: extended to all FCHAIN FUNC-pairs, added to the phase axis.
|
|
205
209
|
// CDR — critical design/schema completeness.
|
package/dist/se/rule-help.js
CHANGED
|
@@ -170,7 +170,11 @@ export const RULE_HELP = {
|
|
|
170
170
|
},
|
|
171
171
|
'MT-02': {
|
|
172
172
|
plain: "The parts inside this module never talk to each other → it is really several modules in one.",
|
|
173
|
-
se: "LCOM4
|
|
173
|
+
se: "LCOM4 over the BOXES of this module whitebox: its sub-MODs (`MOD -compose-> MOD`) plus its directly allocated `FUNC`s. A box owns its whole subtree, so a decomposed block carries the contracts of its leaves — the same ownership chain R-04 (CR-SM-282), BW-02 (CR-SM-283) and R-23 (CR-SM-321) use; without it a container counted as an empty group of its own and the number grew with decomposition DEPTH (CR-SM-326). Connected means a shared CONTRACT (`FLOW -relation-> SCHEMA`, else the untyped flow) — the same counting base as CR-01/R-04/MT-01 since CR-SM-274/276, not FLOW identity. CR-SM-297 dropped `satisfy`: two FUNCs meeting the same requirement can be fully decoupled at runtime, and if they share one, the REQUIREMENT is what needs decomposing. `null` (no judgement) below two boxes and where no box carries a contract at all — an unwired FUNC is R-31's statement, at the element. `info` from `metricPolicy.lcom4.info`, `warning` from `.warning` (CR-GC-329).",
|
|
174
|
+
},
|
|
175
|
+
'MT-04': {
|
|
176
|
+
plain: "This block's parts never talk to each other → it is really two blocks that happen to sit under one name.",
|
|
177
|
+
se: "LCOM4 of a decomposed FUNC: its `compose` children are the boxes of that level, each owning its whole subtree, connected when they share a CONTRACT (`FLOW -relation-> SCHEMA`, else the untyped flow) — the same computation MT-02 runs on the module whitebox (CR-SM-326). BW-02 measures the OUTSIDE of the same element (how wide the interface is, CR-SM-283); this one says whether the inside hangs together. `null` (no judgement) below two boxes and where no box carries a contract. Thresholds shared with MT-02 (`metricPolicy.lcom4`) — same measure, same scale.",
|
|
174
178
|
},
|
|
175
179
|
'ND-01': {
|
|
176
180
|
plain: "Two functions look like the same function written twice → merge them, or make clear what each one does differently.",
|
|
@@ -294,6 +298,10 @@ export const RULE_HELP = {
|
|
|
294
298
|
plain: "This element points at code in a package your project does not actually install → either add the package, or point at the one that owns the symbol now.",
|
|
295
299
|
se: "An `external: true` `realRef` names a package that is in neither `dependencies` nor `devDependencies` of the consumer. Absent dependency data is SILENCE, not a violation — the extractor never looked, and treating that as \"declares nothing\" would report every external binding at once (CR-SM-262).",
|
|
296
300
|
},
|
|
301
|
+
'RC-07': {
|
|
302
|
+
plain: "A change request is marked done in the model but still open in the folder (or the other way round), or an open change request has no entry in the model → the folder decides; fix the model.",
|
|
303
|
+
se: "`CR` node whose `status` (closed = done/dropped/rejected) contradicts `docs/cr/open|done/`, or an OPEN CR file with no `CR` node (anchored at the first `SYS`). Nodes without a file are not reported — history. Absent `crFiles` is SILENCE (CR-SM-329).",
|
|
304
|
+
},
|
|
297
305
|
'RD-01': {
|
|
298
306
|
plain: "A smallest-piece feature has nothing built to fulfil it → add what implements it.",
|
|
299
307
|
se: "Leaf `REQ` (no `compose`→`REQ` children) with no `satisfy` from a `FUNC`/`FCHAIN`/`MOD`/`SYS`.",
|