@sigloch/contracts 10.13.0 → 10.14.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.
@@ -6,6 +6,44 @@ const ARTIFACT_LABEL = {
6
6
  fmea: 'FMEA',
7
7
  implplan: 'Implementation Plan',
8
8
  };
9
+ /**
10
+ * CR-SM-382: was der Hinweis nennt — den Task, seinen Skill und den ersten Arbeitsschritt.
11
+ *
12
+ * Bis hierher lautete der fix_hint "Set attributes.analysisFreshness.<id>.graphVersion …". Gemessen im
13
+ * lokalen AgentDiary-Lauf (local-1, 2026-09-30): das Modell setzte fuenf Stempel in einem Zug, ohne ein
14
+ * einziges Artefakt, und meldete die Analysen als erledigt — der Hinweis nannte die Metrik als Handlung.
15
+ * Dazu wich die Artefakt-id vom Task-Namen ab (`implplan` gegen `plan`), der Aufruf
16
+ * `graph_generate {task:'implplan'}` schlug fehl. Der Hinweis nannte darauf den Aufruf woertlich und die
17
+ * Arbeit; den Stempel schreibt der letzte Schritt des Skills, zusammen mit den Funden.
18
+ *
19
+ * CR-SM-387: der Hinweis nennt keinen Werkzeugnamen mehr. `graph_generate` hat nur der Autopilot; der
20
+ * interaktive Agent (todo-local, 2026-10-03) zitierte den Aufruf zweimal als fehlendes Werkzeug und
21
+ * liess die Analyse liegen. Der Skill fuehrt die Analyse in beiden Betriebsarten — er ist der Name, der
22
+ * stimmen muss (`skill` folgt RULE_HELP[…].prompt, der Test haelt beide zusammen); damit entfaellt auch
23
+ * die Fehlerquelle `task` gegen Artefakt-id.
24
+ */
25
+ const ANALYSIS_WORK = {
26
+ conops: {
27
+ skill: 'se-conops',
28
+ firstStep: 'walk configuration, credentials, user management, deployment, observability and data lifecycle, and write each concern the system must satisfy as a non-functional REQ with its TEST at SYS scope',
29
+ },
30
+ trade: {
31
+ skill: 'se-trade',
32
+ firstStep: 'name the open decision with its options and criteria, then record the chosen option as a CR with relation(label: decides) edges to what it decides',
33
+ },
34
+ 'assumption-review': {
35
+ skill: 'se-irr',
36
+ firstStep: 'list every unproven assumption the model rests on (unmeasured numbers, unverified external dependencies) in a record under docs/records/, then promote the load-bearing ones to CRs',
37
+ },
38
+ fmea: {
39
+ skill: 'se-fmea',
40
+ firstStep: 'for each FCHAIN name its failure modes and add each one as a REQ with role risk (severity, occurrence, detection) plus a mitigation REQ and its TEST',
41
+ },
42
+ implplan: {
43
+ skill: 'se-plan',
44
+ firstStep: 'cut one CR per leaf REQ at its carrier and group the CRs into MS milestones in dependency order',
45
+ },
46
+ };
9
47
  /** Presence-check for one analysis artifact's freshness stamp, anchored on SYS. */
10
48
  function analysisFreshnessPresence(ruleId, artifactId) {
11
49
  return (graph) => {
@@ -16,12 +54,13 @@ function analysisFreshnessPresence(ruleId, artifactId) {
16
54
  if (AnalysisFreshnessStampSchema.safeParse(stamp).success)
17
55
  return [];
18
56
  const label = ARTIFACT_LABEL[artifactId];
57
+ const work = ANALYSIS_WORK[artifactId];
19
58
  return [{
20
59
  rule_id: ruleId,
21
60
  severity: 'warning',
22
61
  element_id: sys.id,
23
- message: `${label} (${artifactId}) has no freshness stamp — was it ever written with a graphVersion stamp?`,
24
- fix_hint: `Set attributes.analysisFreshness.${artifactId}.graphVersion to the current graphVersion() when writing/refreshing the ${label} artifact`,
62
+ message: `${label} has not been carried out — no analysis is on record for this system`,
63
+ fix_hint: `Carry out the ${label}: work through the skill ${work.skill} step by step. First step: ${work.firstStep}. The closing step of the skill records the analysis as done, in the same batch as its findings`,
25
64
  context: { element_type: sys.type, element_name: sys.name },
26
65
  }];
27
66
  };
@@ -11,16 +11,16 @@
11
11
  */
12
12
  export declare const GRAMMAR_SNAPSHOT: {
13
13
  readonly versions: {
14
- readonly ontology: "10.1.0";
14
+ readonly ontology: "10.2.0";
15
15
  readonly metaModel: "6.0.0";
16
- readonly rules: "34.0.0";
16
+ readonly rules: "34.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-> SCHEMA", "CR -relation-> UC", "FCHAIN -compose-> FUNC [1..*]", "FCHAIN -satisfy-> REQ where target.kinds in {non-functional}", "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}", "MOD -compose-> MOD [0..*]", "MOD -satisfy-> REQ where target.kinds in {non-functional}", "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 {non-functional}", "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
- readonly attributes: readonly ["CR.rationale: string", "CR.spike: boolean", "CR.status: enum", "FLOW.sync: enum", "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.role: enum", "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 (warning) domain=[CR]", "CR-R02 (warning) domain=[CR]", "CR-R03 (warning) domain=[all]", "CR-R05 (warning) domain=[REQ]", "FC-02 (warning) domain=[UC]", "FC-03 (warning) domain=[FUNC]", "FC-04 (warning) domain=[FCHAIN]", "FC-05 (warning) domain=[FCHAIN]", "FM-01 (warning) domain=[REQ]", "FM-02 (warning) domain=[REQ]", "FM-03 (warning) domain=[REQ]", "IO-01 (warning) domain=[FUNC]", "IO-02 (error) domain=[FLOW]", "IR-01 (warning) domain=[graph]", "MS-01 (warning) domain=[MS]", "MS-02 (warning) domain=[MS]", "MS-03 (info) domain=[CR]", "MT-01 (warning) domain=[MOD]", "MT-02 (warning) domain=[MOD]", "MT-04 (info) domain=[FUNC]", "ND-01 (warning) domain=[FUNC]", "ND-02 (warning) 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 (warning) domain=[FUNC]", "RC-02 (warning) domain=[TEST]", "RC-03 (warning) 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]", "RC-08 (warning) domain=[SCHEMA]", "RC-09 (warning) domain=[SCHEMA]", "RC-10 (warning) domain=[MOD]", "RD-01 (warning) domain=[REQ]", "RD-02 (warning) domain=[REQ]", "RD-04 (warning) domain=[FUNC,MOD,SYS]", "RD-05 (warning) domain=[FUNC,MOD,SYS]", "SC-02 (warning) domain=[SCHEMA]", "TR-01 (warning) domain=[graph]", "UC-01 (warning) domain=[UC]", "UC-02 (warning) domain=[UC]", "UC-03 (warning) domain=[UC]", "UC-04 (warning) domain=[UC]", "VR-01 (info) domain=[TEST]"];
22
+ readonly attributes: readonly ["CR.architectureOnly: boolean", "CR.rationale: string", "CR.spike: boolean", "CR.status: enum", "FLOW.sync: enum", "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.role: enum", "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 (warning) domain=[CR]", "CR-R02 (warning) domain=[CR]", "CR-R03 (warning) domain=[all]", "CR-R05 (warning) domain=[REQ]", "FC-02 (warning) domain=[UC]", "FC-03 (warning) domain=[FUNC]", "FC-04 (warning) domain=[FCHAIN]", "FC-05 (warning) domain=[FCHAIN]", "FM-01 (warning) domain=[REQ]", "FM-02 (warning) domain=[REQ]", "FM-03 (warning) domain=[REQ]", "IO-01 (warning) domain=[FUNC]", "IO-02 (error) domain=[FLOW]", "IR-01 (warning) domain=[graph]", "MS-01 (warning) domain=[MS]", "MS-02 (warning) domain=[MS]", "MS-03 (info) domain=[CR]", "MT-01 (warning) domain=[MOD]", "MT-02 (warning) domain=[MOD]", "MT-04 (info) domain=[FUNC]", "ND-01 (warning) domain=[FUNC]", "ND-02 (warning) 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 (warning) domain=[FUNC]", "RC-02 (warning) domain=[TEST]", "RC-03 (warning) 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]", "RC-08 (warning) domain=[SCHEMA]", "RC-09 (warning) domain=[SCHEMA]", "RC-10 (warning) domain=[MOD]", "RD-01 (warning) domain=[REQ]", "RD-02 (warning) domain=[REQ]", "RD-04 (warning) domain=[FUNC,MOD,SYS]", "RD-05 (warning) domain=[FUNC,MOD]", "SC-02 (warning) domain=[SCHEMA]", "TR-01 (warning) domain=[graph]", "UC-01 (warning) domain=[UC]", "UC-02 (warning) domain=[UC]", "UC-03 (warning) domain=[UC]", "UC-04 (warning) domain=[UC]", "VR-01 (info) domain=[TEST]"];
24
24
  readonly conformanceRules: readonly ["RC-01 (warning)", "RC-02 (warning)", "RC-03 (warning)", "RC-04 (warning)", "RC-05 (warning)", "RC-06 (warning)", "RC-07 (warning)", "RC-08 (warning)", "RC-09 (warning)", "RC-10 (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
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", "CR-R05", "FC-02", "FC-03", "FC-04", "FC-05", "FM-01", "FM-02", "FM-03", "IO-01", "IO-02", "IR-01", "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", "RC-08", "RC-09", "RC-10", "RD-01", "RD-02", "RD-04", "RD-05", "SC-02", "TR-01", "UC-01", "UC-02", "UC-03", "UC-04", "VR-01"];
@@ -11,9 +11,9 @@
11
11
  */
12
12
  export const GRAMMAR_SNAPSHOT = {
13
13
  versions: {
14
- ontology: "10.1.0",
14
+ ontology: "10.2.0",
15
15
  metaModel: "6.0.0",
16
- rules: "34.0.0",
16
+ rules: "34.1.0",
17
17
  },
18
18
  elementTypes: [
19
19
  "ACTOR",
@@ -92,6 +92,7 @@ export const GRAMMAR_SNAPSHOT = {
92
92
  "Trace.weight",
93
93
  ],
94
94
  attributes: [
95
+ "CR.architectureOnly: boolean",
95
96
  "CR.rationale: string",
96
97
  "CR.spike: boolean",
97
98
  "CR.status: enum",
@@ -192,7 +193,7 @@ export const GRAMMAR_SNAPSHOT = {
192
193
  "RD-01 (warning) domain=[REQ]",
193
194
  "RD-02 (warning) domain=[REQ]",
194
195
  "RD-04 (warning) domain=[FUNC,MOD,SYS]",
195
- "RD-05 (warning) domain=[FUNC,MOD,SYS]",
196
+ "RD-05 (warning) domain=[FUNC,MOD]",
196
197
  "SC-02 (warning) domain=[SCHEMA]",
197
198
  "TR-01 (warning) domain=[graph]",
198
199
  "UC-01 (warning) domain=[UC]",
@@ -3,9 +3,9 @@
3
3
  * Single source of truth for SE ontology schemas across all projects.
4
4
  */
5
5
  /** Ontology schema version (element types + trace types). */
6
- export declare const ONTOLOGY_VERSION = "10.1.0";
6
+ export declare const ONTOLOGY_VERSION = "10.2.0";
7
7
  /** Rules engine version (validation rules incl. RC conformance). */
8
- export declare const RULES_VERSION = "34.0.0";
8
+ export declare const RULES_VERSION = "34.1.0";
9
9
  /** Meta-model version (trace pattern constraints + format-e parser). */
10
10
  export declare const META_MODEL_VERSION = "6.0.0";
11
11
  export * from './ontology.js';
package/dist/se/index.js CHANGED
@@ -3,9 +3,9 @@
3
3
  * Single source of truth for SE ontology schemas across all projects.
4
4
  */
5
5
  /** Ontology schema version (element types + trace types). */
6
- export const ONTOLOGY_VERSION = '10.1.0'; // BREAKING innerhalb des unveroeffentlichten 10.x (CR-SM-366): `ReqKind` 6 -> 2 Werte {functional, non-functional}, genau EIN Wert je REQ. Entfallen: precondition/postcondition (Vorbedingung = Eingangs-FLOW der Kette, Nachbedingung = UC-Ziel, geprueft ueber R-21; Leser UC-05/06 seit CR-SM-357 weg) und risk/mitigation (-> `REQ.attributes.role`, CR-SM-365). Die Zeichenkette bleibt 10.1.0: die Enum-WERTE stehen in keiner Oberflaeche von grammar-snapshot.ts (nur die Spalte `OntologyElement.kinds`), `check:grammar` verlangt also keine Bewegung; gegen die Registry (9.1.0) ist 10.x der eine Major dieses Zugs. Migration (graphcode CR-GC-669): pre/post-REQ je REQ auf functional|non-functional oder aufloesen, risk/mitigation -> role. Prior: // MINOR (CR-SM-365): +`REQ.attributes.role` (Enum `ReqRole` risk|mitigation, Leser `readReqRole`) — die FMEA-Rolle einer Anforderung, satisfy-neutral. Reine Ergaenzung an der Oberflaeche (ReqKind unveraendert; die Entfernung von risk/mitigation aus `kinds` ist CR-SM-366). Kein eigener Major: 10.0.0 ist unveroeffentlicht (Registry 9.1.0), 10.x ist EIN Major gegen die Registry; `check:grammar` vergleicht je Commit gegen HEAD und erzwingt daher eine Bewegung der Zeichenkette. Prior: // MAJOR (CR-SM-364): FLOW traegt genau ein Realisierungsattribut `sync` (Enum sync|async, fehlend = unbekannt, kein Default; Leser `readFlowSync`, Enum `FlowSync`). `protocol` und `qos` entfallen — formal breaking, am Bestand folgenlos: 0 von 186 Familien-FLOWs (bok, SM, GVE, GC, graphify) setzen eines davon, kein Leser ausser ontology.ts/grammar-snapshot.ts. Prior: // MINOR (CR-SM-355): `AnalysisFreshnessStampSchema.crRefs?: string[]` — die aus einem Task hervorgegangenen CRs, atomar mit dem Stempel; TR-01/IR-01 lesen es. Prior: // 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)
6
+ export const ONTOLOGY_VERSION = '10.2.0'; // MINOR (CR-SM-386): +`CR.attributes.architectureOnly: boolean`. CR-R02 las den Schalter identitaetsscharf (`=== true`), er stand aber nicht in ELEMENT_ATTRIBUTES — die typ-geleitete Hydrierung (CR-SM-251) liess `@architectureOnly true` als String "true" stehen, die Ausnahme war ueber Format-E unerreichbar. Bestand: bereits gespeicherte Strings "true" bleiben im Store Strings, bis der Knoten neu geschrieben oder per Export-Reimport hydriert wird; keine Migration. Prior: // BREAKING innerhalb des unveroeffentlichten 10.x (CR-SM-366): `ReqKind` 6 -> 2 Werte {functional, non-functional}, genau EIN Wert je REQ. Entfallen: precondition/postcondition (Vorbedingung = Eingangs-FLOW der Kette, Nachbedingung = UC-Ziel, geprueft ueber R-21; Leser UC-05/06 seit CR-SM-357 weg) und risk/mitigation (-> `REQ.attributes.role`, CR-SM-365). Die Zeichenkette bleibt 10.1.0: die Enum-WERTE stehen in keiner Oberflaeche von grammar-snapshot.ts (nur die Spalte `OntologyElement.kinds`), `check:grammar` verlangt also keine Bewegung; gegen die Registry (9.1.0) ist 10.x der eine Major dieses Zugs. Migration (graphcode CR-GC-669): pre/post-REQ je REQ auf functional|non-functional oder aufloesen, risk/mitigation -> role. Prior: // MINOR (CR-SM-365): +`REQ.attributes.role` (Enum `ReqRole` risk|mitigation, Leser `readReqRole`) — die FMEA-Rolle einer Anforderung, satisfy-neutral. Reine Ergaenzung an der Oberflaeche (ReqKind unveraendert; die Entfernung von risk/mitigation aus `kinds` ist CR-SM-366). Kein eigener Major: 10.0.0 ist unveroeffentlicht (Registry 9.1.0), 10.x ist EIN Major gegen die Registry; `check:grammar` vergleicht je Commit gegen HEAD und erzwingt daher eine Bewegung der Zeichenkette. Prior: // MAJOR (CR-SM-364): FLOW traegt genau ein Realisierungsattribut `sync` (Enum sync|async, fehlend = unbekannt, kein Default; Leser `readFlowSync`, Enum `FlowSync`). `protocol` und `qos` entfallen — formal breaking, am Bestand folgenlos: 0 von 186 Familien-FLOWs (bok, SM, GVE, GC, graphify) setzen eines davon, kein Leser ausser ontology.ts/grammar-snapshot.ts. Prior: // MINOR (CR-SM-355): `AnalysisFreshnessStampSchema.crRefs?: string[]` — die aus einem Task hervorgegangenen CRs, atomar mit dem Stempel; TR-01/IR-01 lesen es. Prior: // 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 = '34.0.0'; // MAJOR (CR-SM-365/366): im selben unveroeffentlichten Major — CR-SM-377: CR-R05 wechselt in TASK_OWNED_RULES.plan (Kern-Fokus → Plan-Task wie CR-R01..03); kein eigener Bump: Oberflaeche (ID/Severity/Domain/Hilfe-Schluessel) unveraendert, 33.0.0 ist das letzte Release. CR-SM-376: R-21 wertet eine Kette auch als gedeckt, wenn JEDE Glied-FUNC eine REQ erfuellt (zweiter Weg neben der verifizierten Ketten-NFR); Meldung nennt die Glieder ohne REQ, fix_hint/Hilfetext beide Wege, Regelname nachgezogen. Kein eigener Bump: Oberflaeche (ID/Severity/Domain/Hilfe-Schluessel) unveraendert, `check:grammar` still, 33.0.0 ist das letzte Release. CR-SM-375: CR-R05 zaehlt eine gebaute FUNC (realRef/external, nicht concept) als Deckung, kein eigener Bump (CR-R05 ist seit dem letzten Release 33.0.0 neu, Oberflaeche unveraendert). CR-SM-366: R-18-Hinweis nennt beim kinds-mismatch die zulaessigen Erfueller statt "FCHAIN carries every kind"; R-02 verliert den toten matchingConstraints-Zweig (non-functional-REQ konnten den FUNC-Filter nie passieren). CR-SM-365: FM-01..03 erkennen Risiko/Gegenmassnahme an `REQ.attributes.role` (readReqRole), nicht mehr an `kinds ∋ risk|mitigation` — kein zweiter Pfad. Ein nicht migrierter Graph verliert seine FM-Befunde, bis `kinds` risk/mitigation in `role` umgezogen ist (graphcode CR-GC-669, Migrationswerkzeug). Oberflaeche (IDs/Severity/Domain/Hilfe-Schluessel) unveraendert; Hilfetexte FM-01..03 nennen die Rolle. Prior: // MINOR (CR-SM-344/343/363, Zug 2026-09-27): reine Ergaenzungen im selben Minor — RC-10 (MOD ohne aufloesbare Dateien, CR-SM-344), CR-R05 (Blatt-REQ ohne Bauauftrag, mit Vorbedingung RULE_PRECONDITION: ohne CR-Knoten nicht ausgewertet, CR-SM-343), FC-05 (FCHAIN gerichtet zusammenhaengend, CR-SM-363; ViolationContext.side_components optional). Prior: // MAJOR (CR-SM-362, ITEM-2026-536): CLOSED_STATUS = {done} — `dropped`/`rejected` stehen nicht im Element-Vertrag (OntologyElement.status) und zaehlen nicht mehr als abgeschlossen; CR-R01 und RC-07 urteilen an solchen CRs jetzt wie an offenen. Bestand (Familie, 2026-09-24): dropped 18, rejected 2 — Migration auf done. Golden unveraendert. Prior: // MINOR (CR-SM-358, ITEM-2026-527): +RC-08 (SCHEMA-realRef ist ein Zod-Schema, kein Typ) und +RC-09 (ein Zod-Vertrag wird nur in den modellierten Erzeuger-/Uebersetzer-Dateien geparst — die erste Regel Code → Modell; ersetzt ND-01 in der Sache, Spike CR-GC-637: parallele Pfade sind nicht aehnlich, sie teilen einen Vertrag). CodeFacts: optional `zodSymbols` je Datei und `fileScope` je Lauf, ABSENT = Stille. RC-04 schweigt an Typ-gebundenen SCHEMA (Ursache gehoert RC-08) — Lockerung, keine Verschaerfung. Beide warning. Prior: // MAJOR (CR-SM-357, ITEM-2026-477): drei Regeln entfallen — UC-05/UC-06 (Pre-/Postcondition-REQ; feuerten in 14 von 21 Familiengraphen an fast jedem UC, 2 Leser, niemand handelte; jetzt Schreibregel in se:author-uc) und RD-03 (keine vorzeitige Zerlegung; 0 Leser, 1 Fund in 21 Graphen — Gate 3 der Grammatik-Review). MT-02 info → warning: eine Steuerregel (STEER_RULES) darf nicht info sein; gemessen 0–1 Fund je Graph, moneyflow 9/155 MOD. Prior: // MAJOR (CR-SM-353/354/355, ITEM-2026-461/465/468): Schwere = Gate-Wirkung — `error` heisst blockt, das Flag `gating` (graph-api-core) entfaellt; elf Regeln ohne Gate-Wirkung werden `warning` (UC-01, UC-02, MS-02, CR-R01, CR-R02, FM-03, ND-01, ND-02, RC-01, RC-02, RC-03); error-Menge = {IO-02, R-01, R-08, R-18, R-29}. Phase gegen NASA NPR 7123.1D App. G: AF-01 → SRR, AF-05 → PDR, R-26 → TRR (64/67 passten). R-27 aus TASK_OWNED_RULES (seit CR-SM-294 gestrichen). +TR-01 / IR-01 (Task-Ausgaenge trade/irr ueber `crRefs` im Frischestempel). Prior: // MAJOR (CR-SM-352, ITEM-2026-459): BQ-02 urteilt sprachsymmetrisch — `must`/`shall` zaehlen nicht mehr als messbares Kriterium (jede englische Anforderung bestand durch ihr Modalverb, jede deutsche fiel ohne Einheit durch; opus5-14), Einheiten und Vergleichswoerter deutsch wie englisch, Einheit mit Wortgrenze. Golden: nur BQ-02 (ssot 10 -> 11). Bestand gemessen (REQ ohne Kriterium, alt -> neu): graphcode 121 -> 120 von 143, sigloch-modules 10 -> 11 von 11, bok 24 -> 21 von 31, sigllm-Golden 70 -> 70 von 80 — kaum Wirkung, niemand schrieb `shall`. BQ-02 bleibt eine lexikalische Heuristik, die an 80-90 % aller REQs feuert; BQ ist Task-Regel (anforderungsqualitaet), nicht im Kern-Fokus. Prior: // MAJOR (CR-SM-351, ITEM-2026-458): zwei Urteile aendern sich. CL-01 liest die UCs eines ACTOR ueber den typisierten Pfad ACTOR-io-FLOW-io-FUNC (beide Richtungen), FUNC in der FCHAIN des UC — vorher `ACTOR io UC`, seit CR-SM-266 verboten; die Regel feuerte damit an jedem Actor, sobald ein UC operatingMode trug (opus5-14: 7 Abnahmen, ITEM-2026-379). CR-R03 zaehlt `CR -relation-> MS` nicht mehr als Kollision — das ist die Meilenstein-Zuordnung, die MS-03 verlangt (opus5-14: 7 Funde an jedem Meilenstein, Plan-Task festgefahren). Prior: // MINOR (CR-SM-350, ITEM-2026-452): +RULE_TASKS, TASK_OWNED_RULES, TASK_ENTRY, taskOf() — die Eigentuemer-Spalte der Regeln (Kern vs. Task: conops, trade, irr, fmea, plan, anforderungsqualitaet, realisierung). Keine Regel aendert ihr Verhalten; wer danach filtert, ist der Konsument (graphcode, CR-GC-600/601). Prior: // MINOR (CR-SM-349, ITEM-2026-443): +AcceptedFindingSchema, acceptedRuleIds(), ACCEPTED_FINDINGS_ATTRIBUTE — die benannte Abweichung als Attribut acceptedFindings [{ruleId, reason}]. Keine Regel aendert ihr Verhalten; wer filtert, ist der Konsument (graphcode-Fokus, CR-GC-593). Prior: // 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.
8
+ export const RULES_VERSION = '34.1.0'; // MINOR (CR-SM-388): RD-05 schweigt an der Wurzel (Ebene direkt unter SYS: `SYS compose MOD` und FUNC-Wurzelwald), domain [FUNC,MOD,SYS] -> [FUNC,MOD]. Weniger Befunde, kein neuer; RD-04 unveraendert. Prior: MAJOR (CR-SM-365/366): im selben unveroeffentlichten Major — CR-SM-377: CR-R05 wechselt in TASK_OWNED_RULES.plan (Kern-Fokus → Plan-Task wie CR-R01..03); kein eigener Bump: Oberflaeche (ID/Severity/Domain/Hilfe-Schluessel) unveraendert, 33.0.0 ist das letzte Release. CR-SM-376: R-21 wertet eine Kette auch als gedeckt, wenn JEDE Glied-FUNC eine REQ erfuellt (zweiter Weg neben der verifizierten Ketten-NFR); Meldung nennt die Glieder ohne REQ, fix_hint/Hilfetext beide Wege, Regelname nachgezogen. Kein eigener Bump: Oberflaeche (ID/Severity/Domain/Hilfe-Schluessel) unveraendert, `check:grammar` still, 33.0.0 ist das letzte Release. CR-SM-375: CR-R05 zaehlt eine gebaute FUNC (realRef/external, nicht concept) als Deckung, kein eigener Bump (CR-R05 ist seit dem letzten Release 33.0.0 neu, Oberflaeche unveraendert). CR-SM-366: R-18-Hinweis nennt beim kinds-mismatch die zulaessigen Erfueller statt "FCHAIN carries every kind"; R-02 verliert den toten matchingConstraints-Zweig (non-functional-REQ konnten den FUNC-Filter nie passieren). CR-SM-365: FM-01..03 erkennen Risiko/Gegenmassnahme an `REQ.attributes.role` (readReqRole), nicht mehr an `kinds ∋ risk|mitigation` — kein zweiter Pfad. Ein nicht migrierter Graph verliert seine FM-Befunde, bis `kinds` risk/mitigation in `role` umgezogen ist (graphcode CR-GC-669, Migrationswerkzeug). Oberflaeche (IDs/Severity/Domain/Hilfe-Schluessel) unveraendert; Hilfetexte FM-01..03 nennen die Rolle. Prior: // MINOR (CR-SM-344/343/363, Zug 2026-09-27): reine Ergaenzungen im selben Minor — RC-10 (MOD ohne aufloesbare Dateien, CR-SM-344), CR-R05 (Blatt-REQ ohne Bauauftrag, mit Vorbedingung RULE_PRECONDITION: ohne CR-Knoten nicht ausgewertet, CR-SM-343), FC-05 (FCHAIN gerichtet zusammenhaengend, CR-SM-363; ViolationContext.side_components optional). Prior: // MAJOR (CR-SM-362, ITEM-2026-536): CLOSED_STATUS = {done} — `dropped`/`rejected` stehen nicht im Element-Vertrag (OntologyElement.status) und zaehlen nicht mehr als abgeschlossen; CR-R01 und RC-07 urteilen an solchen CRs jetzt wie an offenen. Bestand (Familie, 2026-09-24): dropped 18, rejected 2 — Migration auf done. Golden unveraendert. Prior: // MINOR (CR-SM-358, ITEM-2026-527): +RC-08 (SCHEMA-realRef ist ein Zod-Schema, kein Typ) und +RC-09 (ein Zod-Vertrag wird nur in den modellierten Erzeuger-/Uebersetzer-Dateien geparst — die erste Regel Code → Modell; ersetzt ND-01 in der Sache, Spike CR-GC-637: parallele Pfade sind nicht aehnlich, sie teilen einen Vertrag). CodeFacts: optional `zodSymbols` je Datei und `fileScope` je Lauf, ABSENT = Stille. RC-04 schweigt an Typ-gebundenen SCHEMA (Ursache gehoert RC-08) — Lockerung, keine Verschaerfung. Beide warning. Prior: // MAJOR (CR-SM-357, ITEM-2026-477): drei Regeln entfallen — UC-05/UC-06 (Pre-/Postcondition-REQ; feuerten in 14 von 21 Familiengraphen an fast jedem UC, 2 Leser, niemand handelte; jetzt Schreibregel in se:author-uc) und RD-03 (keine vorzeitige Zerlegung; 0 Leser, 1 Fund in 21 Graphen — Gate 3 der Grammatik-Review). MT-02 info → warning: eine Steuerregel (STEER_RULES) darf nicht info sein; gemessen 0–1 Fund je Graph, moneyflow 9/155 MOD. Prior: // MAJOR (CR-SM-353/354/355, ITEM-2026-461/465/468): Schwere = Gate-Wirkung — `error` heisst blockt, das Flag `gating` (graph-api-core) entfaellt; elf Regeln ohne Gate-Wirkung werden `warning` (UC-01, UC-02, MS-02, CR-R01, CR-R02, FM-03, ND-01, ND-02, RC-01, RC-02, RC-03); error-Menge = {IO-02, R-01, R-08, R-18, R-29}. Phase gegen NASA NPR 7123.1D App. G: AF-01 → SRR, AF-05 → PDR, R-26 → TRR (64/67 passten). R-27 aus TASK_OWNED_RULES (seit CR-SM-294 gestrichen). +TR-01 / IR-01 (Task-Ausgaenge trade/irr ueber `crRefs` im Frischestempel). Prior: // MAJOR (CR-SM-352, ITEM-2026-459): BQ-02 urteilt sprachsymmetrisch — `must`/`shall` zaehlen nicht mehr als messbares Kriterium (jede englische Anforderung bestand durch ihr Modalverb, jede deutsche fiel ohne Einheit durch; opus5-14), Einheiten und Vergleichswoerter deutsch wie englisch, Einheit mit Wortgrenze. Golden: nur BQ-02 (ssot 10 -> 11). Bestand gemessen (REQ ohne Kriterium, alt -> neu): graphcode 121 -> 120 von 143, sigloch-modules 10 -> 11 von 11, bok 24 -> 21 von 31, sigllm-Golden 70 -> 70 von 80 — kaum Wirkung, niemand schrieb `shall`. BQ-02 bleibt eine lexikalische Heuristik, die an 80-90 % aller REQs feuert; BQ ist Task-Regel (anforderungsqualitaet), nicht im Kern-Fokus. Prior: // MAJOR (CR-SM-351, ITEM-2026-458): zwei Urteile aendern sich. CL-01 liest die UCs eines ACTOR ueber den typisierten Pfad ACTOR-io-FLOW-io-FUNC (beide Richtungen), FUNC in der FCHAIN des UC — vorher `ACTOR io UC`, seit CR-SM-266 verboten; die Regel feuerte damit an jedem Actor, sobald ein UC operatingMode trug (opus5-14: 7 Abnahmen, ITEM-2026-379). CR-R03 zaehlt `CR -relation-> MS` nicht mehr als Kollision — das ist die Meilenstein-Zuordnung, die MS-03 verlangt (opus5-14: 7 Funde an jedem Meilenstein, Plan-Task festgefahren). Prior: // MINOR (CR-SM-350, ITEM-2026-452): +RULE_TASKS, TASK_OWNED_RULES, TASK_ENTRY, taskOf() — die Eigentuemer-Spalte der Regeln (Kern vs. Task: conops, trade, irr, fmea, plan, anforderungsqualitaet, realisierung). Keine Regel aendert ihr Verhalten; wer danach filtert, ist der Konsument (graphcode, CR-GC-600/601). Prior: // MINOR (CR-SM-349, ITEM-2026-443): +AcceptedFindingSchema, acceptedRuleIds(), ACCEPTED_FINDINGS_ATTRIBUTE — die benannte Abweichung als Attribut acceptedFindings [{ruleId, reason}]. Keine Regel aendert ihr Verhalten; wer filtert, ist der Konsument (graphcode-Fokus, CR-GC-593). Prior: // 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 = '6.0.0'; // MAJOR (CR-SM-366): satisfy-where je Erfueller genau ein kind — FUNC {functional}, MOD/SYS {non-functional}, und NEU `FCHAIN -satisfy-> REQ where target.kinds in {non-functional}` (bisher ohne where). Eine functional-REQ an der Kette hatte keine FUNC und damit keinen realRef; jetzt R-18 `kinds-mismatch` mit candidate_sources [FUNC]. Bestand (5 Familien-Snapshots, 2026-09-27): 27 functional-REQ an FCHAIN, 53 satisfy-Kanten auf pre/postcondition-REQ, 4 auf risk/mitigation-REQ; ungueltige satisfy-Kanten 7 -> 91. Prior: // MINOR (CR-SM-341): `CR -relation-> SCHEMA` ergaenzt -- reine Erweiterung, keine Bestandskante wird ungueltig. Die Kante war der einzige Punkt, an dem zwei Regeln DESSELBEN Katalogs einander widersprachen: CR-R01 verlangt einen Umfang und nennt SCHEMA ausdruecklich (SCOPE_TYPES = FUNC/MOD/SCHEMA/REQ/UC, cr-quality-rules.ts), R-18 wies genau diese Kante ab, weil die Matrix fuer CR nur MS/UC/REQ/FUNC/MOD kannte. Gemessen an zwei Audit-Logs: 6 Ablehnungen in graphcode, 3 in sigllm -- jedes Mal ein CR, dessen Umfang ein Vertrag ist. Erweitert statt den fixHint zu kuerzen: ein CR, der eine Schnittstelle schneidet, hat genau dort seinen Umfang. Gleicher Zug, ohne Oberflaechenwirkung: R-18 haengt seinen Ablehnungsgrund als `context.trace_rejection` an den Befund (neuer Export `admittingAlternatives`). Prior: // 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';
@@ -390,6 +390,7 @@ export const ELEMENT_ATTRIBUTES = {
390
390
  { key: 'status', type: 'enum', enumValues: ['open', 'done'], description: 'CR completion status' },
391
391
  { key: 'rationale', type: 'string', description: 'Reason for the change' },
392
392
  { key: 'spike', type: 'boolean', description: 'CR is a spike/exploration' },
393
+ { key: 'architectureOnly', type: 'boolean', description: 'Done CR without code change (model/architecture only): exempt from the CR-R02 commitRef requirement (CR-SM-386)' },
393
394
  ],
394
395
  // CR-SM-364: genau ein Realisierungsattribut. `protocol`/`qos` entfielen — freie Strings, in
395
396
  // 0 von 186 Familien-FLOWs gesetzt, ohne Leser. Abweichende Kopplung je Verbraucher ⇒ FLOW teilen.
@@ -0,0 +1 @@
1
+ export declare function hasPlaceholder(text: string): boolean;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * placeholder.ts — the one definition of "this text still carries a placeholder" (CR-SM-383).
3
+ *
4
+ * UC-04 and BQ-07 each had their own pattern, both case-insensitive: `todo` matched the domain
5
+ * word "Todo" ("Benutzer fuegt einen neuen Todo-Eintrag hinzu"), and the finding could not be
6
+ * resolved by any rewording that kept the subject. Markers are written in capitals by
7
+ * convention; only the spelled-out phrases are matched regardless of case.
8
+ */
9
+ const MARKER_RE = /\b(TBD|TBR|TODO|FIXME|XXX)\b/;
10
+ const PHRASE_RE = /\b(placeholder|to be determined)\b|needs\s.*review/i;
11
+ export function hasPlaceholder(text) {
12
+ return MARKER_RE.test(text) || PHRASE_RE.test(text);
13
+ }
@@ -1,4 +1,5 @@
1
1
  import { normalizeReqKinds } from './ontology.js';
2
+ import { hasPlaceholder } from './placeholder.js';
2
3
  // ---------------------------------------------------------------------------
3
4
  // Weasel words list (BQ-01)
4
5
  // ---------------------------------------------------------------------------
@@ -205,7 +206,6 @@ export function bq06Conforming(graph) {
205
206
  // ---------------------------------------------------------------------------
206
207
  // BQ-07 Complete — REQ must have meaningful description and no placeholders
207
208
  // ---------------------------------------------------------------------------
208
- const placeholderPattern = /\b(TBD|TBR|TODO|placeholder|to be determined)\b|needs\s.*review/i;
209
209
  const BQ07_MIN_DESC_LENGTH = 20;
210
210
  export function bq07Complete(graph) {
211
211
  // CR-SM-266 REQ-X06: `kinds` ist Pflicht am REQ — hier als DRITTER Grund derselben Regel,
@@ -220,7 +220,7 @@ export function bq07Complete(graph) {
220
220
  // Angabe ist ein Vollstaendigkeitssignal und deshalb hier/warning — dieselbe Trennung, die
221
221
  // CR-SM-262 fuer RC-06 begruendet hat.
222
222
  const isIncomplete = (req) => descriptionOf(req).trim().length < BQ07_MIN_DESC_LENGTH ||
223
- placeholderPattern.test(descriptionOf(req)) ||
223
+ hasPlaceholder(descriptionOf(req)) ||
224
224
  normalizeReqKinds(req.kinds).length === 0;
225
225
  return reqElements(graph)
226
226
  .filter(isIncomplete)
@@ -230,7 +230,7 @@ export function bq07Complete(graph) {
230
230
  if (desc.trim().length < BQ07_MIN_DESC_LENGTH) {
231
231
  reasons.push(`description too short (${desc.trim().length}/${BQ07_MIN_DESC_LENGTH} chars)`);
232
232
  }
233
- if (placeholderPattern.test(desc)) {
233
+ if (hasPlaceholder(desc)) {
234
234
  reasons.push('contains placeholder');
235
235
  }
236
236
  if (normalizeReqKinds(req.kinds).length === 0) {
@@ -31,12 +31,18 @@
31
31
  * umformulierter Satz darf keinen Major ausloesen, deshalb steht im Golden-File nur die
32
32
  * Schluesselmenge, nicht die Prosa.
33
33
  *
34
- * Sprache: Englisch, wie die Regelbeschreibungen selbst. 18,6 KB reine Strings — der
35
- * `./browser`-Eintrag bleibt transitiv `node:`-frei (CR-SM-230), Strings ziehen nichts nach.
34
+ * Sprache: Englisch, wie die Regelbeschreibungen selbst — ausser `vorschlag`, der Satz, den ein Nutzer
35
+ * seinem Agenten schickt (CR-SM-384): Deutsch, weil er in der Sprache des Nutzers steht, nicht in der
36
+ * des Katalogs. Bis CR-SM-384 lag er als `VORSCHLAG_REGEL` in graphcode, ein paralleler Pfad zu
37
+ * `plain` und `fix_hint` mit derselben Driftgefahr wie oben. Reine Strings — der `./browser`-Eintrag
38
+ * bleibt transitiv `node:`-frei (CR-SM-230), Strings ziehen nichts nach.
36
39
  *
37
40
  * @author andreas@siglochconsulting
38
41
  */
39
- /** Ein verfasster Hilfeeintrag: die zwei Klartext-Schichten, dazu ein Kopier-Prompt, wo einer passt. */
42
+ /**
43
+ * Ein verfasster Hilfeeintrag: die zwei Klartext-Schichten, dazu ein Kopier-Prompt, wo einer passt,
44
+ * und — fuer die Regeln des Kern-Fokus — der Vorschlag an den Nutzer.
45
+ */
40
46
  export interface RuleHelpEntry {
41
47
  /** Schicht 0 — ohne SE-Jargon; endet mit der EINEN einfachen Handlung. */
42
48
  readonly plain: string;
@@ -44,6 +50,14 @@ export interface RuleHelpEntry {
44
50
  readonly se: string;
45
51
  /** Schicht 2 — ein kopierbarer Prompt (`se:*`-Skill oder MCP-Aufruf), wo einer passt. */
46
52
  readonly prompt?: string;
53
+ /**
54
+ * Schicht 3 (CR-SM-384) — der naechste Schritt als Bitte des NUTZERS an seinen Agenten: ein deutscher
55
+ * Satz, Imperativ, `{n}` = die Namen der Fund-Elemente. Keine Werkzeugnamen, keine Regel-IDs, keine
56
+ * Fix-Vorlage — der Nutzer entscheidet, der Agent fuehrt aus (graphcode CR-GC-729/730). Genau an den
57
+ * Regeln, die den Kern-Fokus stellen koennen: Task `kern`, Schwere nicht `info`, kein Eintrittspunkt
58
+ * einer Analyse (die haben ihren eigenen Satz beim Konsumenten). Beidseitig erzwungen im Test.
59
+ */
60
+ readonly vorschlag?: string;
47
61
  }
48
62
  /**
49
63
  * Je Regel-ID der Hilfeeintrag — Katalogregeln (`ALL_RULE_DEFS`) UND Conformance-Regeln
@@ -31,8 +31,11 @@
31
31
  * umformulierter Satz darf keinen Major ausloesen, deshalb steht im Golden-File nur die
32
32
  * Schluesselmenge, nicht die Prosa.
33
33
  *
34
- * Sprache: Englisch, wie die Regelbeschreibungen selbst. 18,6 KB reine Strings — der
35
- * `./browser`-Eintrag bleibt transitiv `node:`-frei (CR-SM-230), Strings ziehen nichts nach.
34
+ * Sprache: Englisch, wie die Regelbeschreibungen selbst — ausser `vorschlag`, der Satz, den ein Nutzer
35
+ * seinem Agenten schickt (CR-SM-384): Deutsch, weil er in der Sprache des Nutzers steht, nicht in der
36
+ * des Katalogs. Bis CR-SM-384 lag er als `VORSCHLAG_REGEL` in graphcode, ein paralleler Pfad zu
37
+ * `plain` und `fix_hint` mit derselben Driftgefahr wie oben. Reine Strings — der `./browser`-Eintrag
38
+ * bleibt transitiv `node:`-frei (CR-SM-230), Strings ziehen nichts nach.
36
39
  *
37
40
  * @author andreas@siglochconsulting
38
41
  */
@@ -93,6 +96,7 @@ export const RULE_HELP = {
93
96
  'BW-02': {
94
97
  plain: "This block hands out many different kinds of data at its edge → whoever uses it has to understand all of them, so either bundle them or split the block.",
95
98
  se: "Whitebox boundary width: distinct `SCHEMA` contracts on `FUNC` `io` `FLOW` `io` `FUNC` paths with one endpoint inside the `compose` subtree and one outside, judged against `metricPolicy.boundaryWidth.warning`. Parnas, information hiding — what a boundary HIDES is what makes it worth having. Rolled up over the subtree because a decomposed `FUNC` carries no `io` edges of its own (CR-SM-283).",
99
+ vorschlag: 'Bündle die Datenformate an der Grenze von {n} oder teile den Block.',
96
100
  },
97
101
  'CL-01': {
98
102
  plain: "Someone only ever uses the system in one way, which usually means their other situations are missing → describe the scenarios you left out.",
@@ -102,6 +106,7 @@ export const RULE_HELP = {
102
106
  'CR-01': {
103
107
  plain: "Two parts exchange an unusually large amount of data, which usually means the boundary is in the wrong place → reconsider the cut.",
104
108
  se: "High crossing `io` FLOW count between two `MOD`s — a coupling metric, advisory.",
109
+ vorschlag: 'Prüfe den Schnitt zwischen {n}, dort fließen ungewöhnlich viele Daten.',
105
110
  },
106
111
  'CR-R01': {
107
112
  plain: "A change is recorded but says nothing about what it changes → link it to what it touches.",
@@ -124,18 +129,22 @@ export const RULE_HELP = {
124
129
  plain: "A scenario that is not broken into sub-scenarios has no described sequence of steps → add one, even if the steps are done by hand.",
125
130
  se: "Leaf `UC` (no `UC -compose-> UC`) with no `FCHAIN`. A chain may consist of EXISTING FUNCs an actor strings together — it costs a node plus compose edges, not code.",
126
131
  prompt: "se:author-uc",
132
+ vorschlag: 'Beschreibe die Abläufe {n} als Kette von Funktionen.',
127
133
  },
128
134
  'FC-03': {
129
135
  plain: "A step inside a sequence contains further steps, so the sequence has hidden depth → lift them to the same level.",
130
136
  se: "`FUNC` inside an `FCHAIN` that itself `compose`s other `FUNC`s. Chains are flat by construction.",
137
+ vorschlag: 'Hebe die inneren Schritte der Funktionen {n} auf die Ebene ihrer Kette.',
131
138
  },
132
139
  'FC-04': {
133
140
  plain: "A sequence either has nobody starting it or nothing coming back out → wire both ends to whoever uses it.",
134
141
  se: "`FCHAIN` lacking an entry (`ACTOR -io-> FLOW -io-> FUNC∈chain`) or an exit (`FUNC∈chain -io-> FLOW -io-> ACTOR`). Stricter than FC-01: both directions, at FUNC/FLOW level, no UC-level bypass.",
142
+ vorschlag: 'Verbinde Anfang und Ende der Funktionsketten {n} mit dem Nutzer.',
135
143
  },
136
144
  'FC-05': {
137
145
  plain: "The steps of this sequence do not hand their results to each other — it falls apart into pieces, so nobody can say how long it is or where it gets stuck → add the missing step, connect the pieces with a data flow, or split it into separate sequences.",
138
146
  se: "`FCHAIN` whose member `FUNC`s do not form one (weakly) connected component along producer → consumer (`FUNC -io-> FLOW -io-> FUNC`, both ends members). Per side component the cause: `missing-link` (main and side part are joined through a `FUNC` outside the chain — compose it) or `bag` (no FUNC joins them: a missing FLOW between members, or parallel services at a shared source — split). One finding per chain, components in `context.side_components` (CR-SM-242). Silent while any member lacks an io input or output (R-31 first); entry/exit belong to FC-04 (CR-SM-363).",
147
+ vorschlag: 'Verbinde die Schritte der Funktionsketten {n} über Datenflüsse.',
139
148
  },
140
149
  'FM-01': {
141
150
  plain: "A requirement is marked as a risk but carries no risk ratings → rate how bad, how likely and how detectable it is (1-10 each).",
@@ -155,10 +164,12 @@ export const RULE_HELP = {
155
164
  'IO-01': {
156
165
  plain: "Two steps in the same sequence have no described data passing between them → add the data one hands to the other.",
157
166
  se: "A `FUNC` pair inside one `FCHAIN` with no `io` path (`FUNC -io-> FLOW -io-> FUNC`) connecting them.",
167
+ vorschlag: 'Ergänze den Datenfluss zwischen den Schritten {n}.',
158
168
  },
159
169
  'IO-02': {
160
170
  plain: "Two different places write into the same flow, so a reader cannot tell which version arrives \u2192 give each source its own flow; they may keep sharing one contract.",
161
171
  se: "`FLOW` with more than one producer (`FUNC`/`ACTOR -io-> FLOW`). R-10 checks the LOWER bound of the same axis (at least one producer, at least one consumer); the two can never fire on the same FLOW, so there is no double count. Only the producer side is bounded: several consumers are the normal shape of a shared contract \u2014 one source, many readers, which is exactly how a central configuration looks. One finding per FLOW, `value` = number of producers, `threshold` 1. Severity `error` (hygiene) and deliberately NOT in STEER_RULES — a decision, not a measured conflict: a split flow keeps its `SCHEMA`, so BW-02 (distinct contracts per boundary) does not rise. Whether a gate blocks a new finding is the consumer's gating choice, not part of the rule.",
172
+ vorschlag: 'Gib jeder Quelle der Datenflüsse {n} einen eigenen Datenfluss.',
162
173
  },
163
174
  'MS-01': {
164
175
  plain: "A milestone has no work assigned to it → assign the work items that belong to it.",
@@ -176,10 +187,12 @@ export const RULE_HELP = {
176
187
  'MT-01': {
177
188
  plain: "This module draws more from others than others draw from it → it will keep changing whenever they do. Measured, not judged: no threshold is set by default.",
178
189
  se: "Instability I = fan_out / (fan_in + fan_out), counted in DISTINCT CONTRACTS crossing the module boundary — the same `moduleCrossings` definition CR-01, R-04 and BW-02 use (CR-SM-274/276). CR-SM-293 fixed both halves: it used to count every trace touching the module, of which only 34 % was coupling (`allocate` is module SIZE, `satisfy` is specification); and the direction was inverted — the CONSUMER depends, so supplying outward is fan_in (Martin's Ca) and drawing inward is fan_out (Ce). `metricPolicy.instability` defaults to `null`: the distribution is bimodal, so no threshold can be read from it. The number stays in every `graph_metrics` module row, next to `uphillDependencies` (CR-SM-301).",
190
+ vorschlag: 'Prüfe die Abhängigkeiten des Moduls {n}.',
179
191
  },
180
192
  'MT-02': {
181
193
  plain: "The parts inside this module never talk to each other → it is really several modules in one.",
182
194
  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).",
195
+ vorschlag: 'Teile das Modul {n}, seine Teile arbeiten nicht zusammen.',
183
196
  },
184
197
  'MT-04': {
185
198
  plain: "This block's parts never talk to each other → it is really two blocks that happen to sit under one name.",
@@ -188,32 +201,39 @@ export const RULE_HELP = {
188
201
  'ND-01': {
189
202
  plain: "Two functions look like the same function written twice → merge them, or make clear what each one does differently.",
190
203
  se: "Near-duplicate `FUNC` above 0.85 similarity (name, description and legal trace partners). Since CR-SM-286 the similarity is computed IN the rule, cached per graph — before that it had to be injected, and without injection the rule returned green, indistinguishable from \"no duplicates\".",
204
+ vorschlag: 'Führe die doppelten Funktionen {n} zusammen oder grenze sie ab.',
191
205
  },
192
206
  'ND-02': {
193
207
  plain: "Two data contracts describe the same thing twice → merge them, or say what distinguishes them.",
194
208
  se: "Near-duplicate `SCHEMA` above 0.85 similarity (name, description, fields and legal trace partners). Partners are filtered through `isValidTrace`: a rule must not reach its verdict via an edge R-18 rejects (CR-SM-286).",
209
+ vorschlag: 'Führe die doppelten Datenformate {n} zusammen oder grenze sie ab.',
195
210
  },
196
211
  'NFR-01': {
197
212
  plain: "Something measured exceeds the limit that was set for it → fix it or change the limit deliberately.",
198
213
  se: "Measured value above its declared budget on a `MOD` (physical) or `FUNC`/`FCHAIN` (behavioural).",
214
+ vorschlag: 'Bringe {n} unter sein Budget oder ändere das Budget bewusst.',
199
215
  },
200
216
  'R-01': {
201
217
  plain: "A feature you've promised has no test proving it's met, so you can't show it works → add or author a test for it.",
202
218
  se: "`REQ` with no incoming `verify` trace (test→requirement coverage, INCOSE V&V).",
203
219
  prompt: "se:close-violations",
220
+ vorschlag: 'Ergänze Tests für die Anforderungen {n}.',
204
221
  },
205
222
  'R-02': {
206
223
  plain: "A function isn't linked to any feature it's meant to build, so it may be dead code → link it to the feature it serves, or delete it.",
207
224
  se: "`FUNC` with no `satisfy` trace to a `REQ` (design→requirement traceability).",
225
+ vorschlag: 'Ordne die Funktionen {n} den Anforderungen zu, die sie erfüllen.',
208
226
  },
209
227
  'R-04': {
210
228
  plain: "Too many different data formats cross this module's boundary → merge formats, or move the functions that carry them.",
211
229
  se: "`MOD` whose boundary is crossed by `metricPolicy.boundaryWidth.warning` or more DISTINCT contracts (`SCHEMA` on the io path `FUNC` ─io→ `FLOW` ─io→ `FUNC` across the boundary, union over all neighbours — CR-SM-276). Same question and same threshold as BW-02 on the FUNC tree (CR-SM-312); module SIZE is RD-04's question since CR-SM-311.",
212
230
  prompt: "se-view:arch",
231
+ vorschlag: 'Verringere die Datenformate an der Grenze des Moduls {n}.',
213
232
  },
214
233
  'R-05': {
215
234
  plain: "A test doesn't check any feature you promised → link it to the feature it tests, or remove it.",
216
235
  se: "`TEST` with no `verify` trace at all — neither to a `REQ` (test→requirement coverage) nor to a `SCHEMA` (contract test, CR-SM-317).",
236
+ vorschlag: 'Ordne die Tests {n} den Anforderungen zu, die sie prüfen.',
217
237
  },
218
238
  'R-32': {
219
239
  plain: "A data contract exists in code but no test exercises it as a contract → add a test that tries every variant, including the ones it must reject, and link it to the schema.",
@@ -222,30 +242,37 @@ export const RULE_HELP = {
222
242
  'R-08': {
223
243
  plain: "A link points at something that no longer exists → repair or remove the broken link.",
224
244
  se: "Trace whose source or target element is missing (dangling reference).",
245
+ vorschlag: 'Repariere oder entferne die Verbindungen an {n}, deren Ziel fehlt.',
225
246
  },
226
247
  'R-10': {
227
248
  plain: "A piece of data goes nowhere — nothing produces or consumes it → connect it to a function or a person/outside system.",
228
249
  se: "`FLOW` with no `io` trace to a `FUNC` or `ACTOR`.",
250
+ vorschlag: 'Vervollständige die Datenflüsse {n}: woher sie kommen und wohin sie gehen.',
229
251
  },
230
252
  'R-12': {
231
253
  plain: "Two items depend on each other in a loop, so neither can stand alone → remove or redirect one of the two links.",
232
254
  se: "Direct cycle: A→B and B→A via the same trace type, checked on `compose` / `allocate` / `relation` only. Data (`io`) is exempt — a function that reads and writes the same `FLOW` is normal reuse, not a dependency cycle.",
255
+ vorschlag: 'Löse die zyklische Abhängigkeit um {n} auf.',
233
256
  },
234
257
  'R-15': {
235
- plain: "A sequence of steps for a use case is empty → add the functions that make it up.",
236
- se: "`FCHAIN` with no `compose` to any `FUNC`.",
258
+ plain: "A sequence of steps is empty, or serves no use case → add the functions that make it up, and attach it to the use case it belongs to.",
259
+ se: "`FCHAIN` with no `compose` to any `FUNC`, or with no incoming `compose` from a `UC` — one rule_id, up to two findings per chain (pattern of R-10).",
260
+ vorschlag: 'Gib den Funktionsketten {n} ihre Funktionen und ordne sie ihrem Ablauf zu.',
237
261
  },
238
262
  'R-16': {
239
263
  plain: "A person or outside system isn't connected to anything → connect it to the data it sends or receives.",
240
264
  se: "`ACTOR` with no `io` trace to a `FLOW`.",
265
+ vorschlag: 'Verbinde den Nutzer {n} über Datenflüsse mit den Funktionen.',
241
266
  },
242
267
  'R-17': {
243
268
  plain: "The top level of your project is empty — nothing is inside it → add the main use cases, features, or modules.",
244
269
  se: "`SYS` with no `compose` to `UC` / `REQ` / `MOD`.",
270
+ vorschlag: 'Lege die Abläufe des Systems {n} an.',
245
271
  },
246
272
  'R-18': {
247
273
  plain: "You connected two items in a combination that isn't allowed → use an allowed link, or fix what's at each end.",
248
274
  se: "Trace whose (source-type, target-type) pair isn't an allowed combination in the metamodel of legal links.",
275
+ vorschlag: 'Korrigiere die unzulässigen Verbindungen an {n}.',
249
276
  },
250
277
  'R-19': {
251
278
  plain: "A test that's meant to run doesn't point to a test file → add the link to the test file, or, if it isn't written yet, mark it not-yet-written.",
@@ -258,14 +285,17 @@ export const RULE_HELP = {
258
285
  'R-21': {
259
286
  plain: "Two functions hand data over, but they are not in one chain together — or they are, and the chain is not covered: some function in it has no requirement, and no end-to-end test checks the chain → give each function of the chain a requirement it fulfils, or give the chain an end-to-end quality requirement and a test for it.",
260
287
  se: "FUNC↔FUNC handover (`FUNC` ─io→ `FLOW` ─io→ `FUNC`), two findings. (a) endpoints share NO `FCHAIN` → finding at the PRODUCER: no integration scope is declared. (b) endpoints share one, but no shared chain is covered → finding at the chain, naming the member FUNCs without a REQ. A chain is covered either (1) through its members — EVERY member FUNC (`FCHAIN` ─compose→ `FUNC`) satisfies at least one `REQ`; verifying those REQs is R-01's job (CR-SM-376) — or (2) end-to-end — a verified integration test (`TEST` ─verify→ `REQ` ←satisfy─ `FCHAIN`); an `FCHAIN` satisfies only non-functional REQs (CR-SM-366). SILENT when either endpoint is in no chain at all (that is R-30's statement) and when either is infrastructure (`chains >= policy.criticality.infrastructure`): a handover into a function that runs through every chain is a bus, not a missing test.",
288
+ vorschlag: 'Bringe die Übergaben an {n} in eine Funktionskette, deren Funktionen Anforderungen erfüllen oder die ein Integrationstest prüft.',
261
289
  },
262
290
  'R-22': {
263
291
  plain: "A function isn't assigned to any building block, so it has no home in the structure → put it on a module.",
264
292
  se: "`FUNC` with no `allocate` trace to a `MOD` (deployment assignment); every function lives on exactly one module.",
293
+ vorschlag: 'Ordne die Funktionen {n} Modulen zu.',
265
294
  },
266
295
  'R-23': {
267
296
  plain: "A building block is empty — no function is assigned to it or to any block inside it → put a function on it, or remove the empty block.",
268
297
  se: "`MOD` with no `FUNC` ─allocate→ trace anywhere in its `compose` subtree (`subtreeFuncs`, the same count R-04 uses, CR-SM-321); the module-side complement of R-22 (empty-container signal like R-14/R-16/R-17). A container `MOD` whose sub-modules hold functions is not empty.",
298
+ vorschlag: 'Gib den Modulen {n} Funktionen oder entferne sie.',
269
299
  },
270
300
  'R-26': {
271
301
  plain: "A data format in the model isn't linked to the schema code that defines it → link it to the schema (or mark it concept/outside).",
@@ -278,10 +308,12 @@ export const RULE_HELP = {
278
308
  'R-30': {
279
309
  plain: "This function sits in no chain of effects, so nobody can say which use case it serves — and the checks that would prove its wiring never look at it → add it to the chain of the use case it belongs to.",
280
310
  se: "FUNC belongs to a function chain (CR-GC-366): a `FUNC` needs an incoming `FCHAIN -compose-> FUNC`, directly or inherited from a parent `FUNC` it decomposes from. R-15 demanded the opposite direction — that a chain has functions — and nothing demanded that a function has a chain. That gap is load-bearing: IO-01 (FLOW paths between chain members) and R-21 (integration test per chain) both scope themselves to a chain, so a function outside every chain falls through both nets silently. Severity `warning`, not error: on a real model this fires on the majority of functions, and an error would block every further mutation through the delta gate.",
311
+ vorschlag: 'Ordne die Funktionen {n} der Kette ihres Ablaufs zu.',
281
312
  },
282
313
  'R-31': {
283
314
  plain: "This function has no input or no output, so it is a dead block in the picture — only an actor is allowed to be an end point → connect it to a flow on the missing side.",
284
315
  se: "FUNC is wired (CR-GC-366): a `FUNC` needs at least one incoming `FLOW -io-> FUNC` and one outgoing `FUNC -io-> FLOW`. Only an `ACTOR` may terminate a chain. R-10 asks the same question from the FLOW side ('does this flow have a producer and a consumer?') and therefore never sees a function with no io edge at all — it does not appear in the FLOW loop. IO-01 presupposes chain membership and misses it too. One finding per FUNC naming the missing side(s), not one per side: otherwise the counter exceeds its own denominator contribution, the mis-measurement documented in CR-SM-242.",
316
+ vorschlag: 'Verbinde die Funktionen {n} auf der fehlenden Seite mit einem Datenfluss.',
285
317
  },
286
318
  'RC-01': {
287
319
  plain: "A function points to code that isn't there anymore (file moved or name changed) → repoint it to the current code.",
@@ -327,41 +359,50 @@ export const RULE_HELP = {
327
359
  plain: "A smallest-piece feature has nothing built to fulfil it → add what implements it.",
328
360
  se: "Leaf `REQ` (no `compose`→`REQ` children) with no `satisfy` from a `FUNC`/`FCHAIN`/`MOD`/`SYS`.",
329
361
  prompt: "se:close-violations",
362
+ vorschlag: 'Lege an, was die Anforderungen {n} erfüllt.',
330
363
  },
331
364
  'RD-02': {
332
365
  plain: "You split a feature into smaller features but you're also building the big one directly → build only the small pieces, not both.",
333
366
  se: "Parent `REQ` (has `compose`→`REQ` children) carrying a direct `FUNC` `satisfy`; the satisfy belongs on the children.",
367
+ vorschlag: 'Lass die Anforderungen {n} nur über ihre Teilanforderungen erfüllen.',
334
368
  },
335
369
  'RD-04': {
336
370
  plain: "One thing has more than 9 parts directly under it → group them, so each level stays readable.",
337
371
  se: "Decomposition breadth above `metricPolicy.decompositionBreadth.warning` children on one level — `FUNC` `compose` `FUNC`, `SYS` `compose` `MOD`, a `MOD`'s sub-`MOD`s plus its own leaf `FUNC` (`allocate`), and the root `FUNC` forest anchored at `SYS` (CR-SM-282). Default 9, the upper end of 7±2 (CR-SM-296). CR-SM-311: a module level counts its own functions again — breadth, not size; module coupling is R-04. The lower bound is RD-05.",
372
+ vorschlag: 'Gruppiere die Teile unter {n}, es sind zu viele auf einer Ebene.',
338
373
  },
339
374
  'RD-05': {
340
375
  plain: "A level has only one or two parts under it → it groups nothing; fold it into the level above, or gather the parts that belong together under it.",
341
- se: "Decomposition breadth below `metricPolicy.decompositionBreadth.min` (default 3, exclusive) on one level, counted exactly like RD-04 (CR-SM-311, Z3). A level abstracts nothing below it. The finding carries no `value`/`threshold` and does not enter the steering score: it is readability, not boundary width. A level with no children at all is not this rule (empty MOD: R-23; FUNC without children is a leaf).",
376
+ se: "Decomposition breadth below `metricPolicy.decompositionBreadth.min` (default 3, exclusive) on one level, counted exactly like RD-04 (CR-SM-311, Z3). A level abstracts nothing below it. The finding carries no `value`/`threshold` and does not enter the steering score: it is readability, not boundary width. A level with no children at all is not this rule (empty MOD: R-23; FUNC without children is a leaf). The root level directly under `SYS` (`SYS` `compose` `MOD`, root `FUNC` forest) is exempt — there is no level above it, and one module or one root function is an architecture decision (CR-SM-388); RD-04 still applies there.",
377
+ vorschlag: 'Löse die Ebene {n} in die Ebene darüber auf oder sammle unter ihr, was zusammengehört.',
342
378
  },
343
379
  'SC-02': {
344
380
  plain: "A data format is defined but nothing uses it → connect it to the data it describes, or drop it.",
345
381
  se: "`SCHEMA` not referenced by any `FLOW` via a `relation` trace.",
382
+ vorschlag: 'Verbinde das Datenformat {n} mit seinem Datenfluss oder entferne es.',
346
383
  },
347
384
  'UC-01': {
348
385
  plain: "A scenario says what someone wants to do but never says what the system must provide → write down the requirements it needs.",
349
386
  se: "`UC` with no `compose` trace to any `REQ`. The use case carries no requirement content.",
350
387
  prompt: "se:author-req",
388
+ vorschlag: 'Lege für die Abläufe {n} Anforderungen mit Test an: fachliche je Funktion, Ende-zu-Ende-Vorgaben an der Kette.',
351
389
  },
352
390
  'UC-02': {
353
391
  plain: "A scenario has nobody who triggers it → name who or what starts it.",
354
392
  se: "`UC` that no `ACTOR` reaches: the one legal path is `ACTOR -io-> FLOW -io-> FUNC` (or back, `FUNC -io-> FLOW -io-> ACTOR`), the FUNC a member of one of the UC's chains (`UC -compose-> FCHAIN -compose-> FUNC`). A direct `ACTOR -io-> UC` is rejected by R-18. No skill prompt (CR-SM-370): `se:author-uc` shows creating UCs and `se:author-actor` says to leave actors unwired — neither is this work.",
393
+ vorschlag: 'Lege für die Abläufe {n} die Funktionen an, die der Nutzer über einen Datenfluss auslöst.',
355
394
  },
356
395
  'UC-03': {
357
396
  plain: "A scenario says what should be possible but not how it runs → describe the steps as a chain of functions.",
358
397
  se: "`UC` with no `compose` trace to an `FCHAIN`. No behavioural scenario is declared.",
359
398
  prompt: "se:author-uc",
399
+ vorschlag: 'Beschreibe die Abläufe {n} als Kette von Funktionen.',
360
400
  },
361
401
  'UC-04': {
362
402
  plain: "A scenario has no real description, or still carries a placeholder like TBD → write what the user actually wants to achieve.",
363
403
  se: "`UC` description shorter than 10 characters or containing TBD/TODO/FIXME/placeholder/XXX. Description IS the goal (CR-150).",
364
404
  prompt: "se:author-uc",
405
+ vorschlag: 'Beschreibe das Ziel der Abläufe {n}.',
365
406
  },
366
407
  'VR-01': {
367
408
  plain: "A test exists but no result was ever recorded, so nobody knows if it passed → record the outcome.",
package/dist/se/rules.js CHANGED
@@ -746,6 +746,12 @@ function decompositionBreadth(graph, policy) {
746
746
  // deshalb keine Zahl im Kontext und steuert nicht — sie ist Lesbarkeit, nicht Randbreite.
747
747
  // Eine Ebene ohne jedes Kind ist nicht ihr Fall: ein leeres MOD meldet R-23, eine FUNC ohne Kinder
748
748
  // ist ein Blatt.
749
+ //
750
+ // CR-SM-388 (ITEM-2026-731): die WURZEL ist ausgenommen — die Ebene direkt unter SYS, also beide
751
+ // Zaehlungen mit SYS als Eltern (`SYS compose MOD` und der FUNC-Wurzelwald). Der Rat „in die Ebene
752
+ // darueber aufloesen" ist dort nicht ausfuehrbar, ueber SYS liegt keine; und ein System aus einem
753
+ // Modul oder einer Wurzelfunktion ist eine Architekturentscheidung, keine leere Abstraktion.
754
+ // RD-04 (Obergrenze) gilt an der Wurzel weiter.
749
755
  // ---------------------------------------------------------------------------
750
756
  function decompositionNarrow(graph, policy) {
751
757
  const band = policy.decompositionBreadth;
@@ -753,7 +759,7 @@ function decompositionNarrow(graph, policy) {
753
759
  return [];
754
760
  const byId = indexOf(graph).byId;
755
761
  return breadthCounts(graph)
756
- .filter(c => c.n < band.min)
762
+ .filter(c => c.n < band.min && byId.get(c.parentId)?.type !== 'SYS')
757
763
  .map(c => {
758
764
  const parent = byId.get(c.parentId);
759
765
  return {
@@ -1687,7 +1693,7 @@ export const V3_RULES = [
1687
1693
  { id: 'RD-01', name: 'Unresolved requirement', severity: 'warning', evaluate: unresolvedRequirement, domain: ['REQ'] },
1688
1694
  { id: 'RD-02', name: 'Decomposition consistency', severity: 'warning', evaluate: decompositionConsistency, domain: ['REQ'] },
1689
1695
  { id: 'RD-04', name: 'Decomposition breadth', severity: 'warning', evaluate: decompositionBreadth, domain: ['FUNC', 'MOD', 'SYS'] },
1690
- { id: 'RD-05', name: 'Decomposition too narrow', severity: 'warning', evaluate: decompositionNarrow, domain: ['FUNC', 'MOD', 'SYS'] },
1696
+ { id: 'RD-05', name: 'Decomposition too narrow', severity: 'warning', evaluate: decompositionNarrow, domain: ['FUNC', 'MOD'] },
1691
1697
  { id: 'MS-01', name: 'Milestone empty scope', severity: 'warning', evaluate: msEmptyScope, domain: ['MS'] },
1692
1698
  { id: 'MS-02', name: 'Milestone dangling dependency', severity: 'warning', evaluate: msDanglingDependency, domain: ['MS'] },
1693
1699
  { id: 'R-30', name: 'FUNC leaf must belong to a function chain', severity: 'warning', evaluate: funcMustBeInEffectChain, domain: ['FUNC'] },
@@ -1,4 +1,5 @@
1
1
  import { indexOf } from './graph-index.js';
2
+ import { hasPlaceholder } from './placeholder.js';
2
3
  // ---------------------------------------------------------------------------
3
4
  // UC-01: UC must have compose→REQ trace (CR-165: satisfy UC→REQ is invalid per meta-model)
4
5
  // ---------------------------------------------------------------------------
@@ -94,13 +95,12 @@ export function uc03HasScenario(graph) {
94
95
  // UC-04: UC must have meaningful description (CR-150: description = goal)
95
96
  // ---------------------------------------------------------------------------
96
97
  const UC_DESC_MIN_LEN = 10;
97
- const PLACEHOLDER_RE = /\b(tbd|todo|fixme|placeholder|xxx)\b/i;
98
98
  export function uc04GoalDefined(graph) {
99
99
  return graph.elements
100
100
  .filter(e => e.type === 'UC')
101
101
  .filter(uc => {
102
102
  const d = uc.description?.trim() ?? '';
103
- return d.length < UC_DESC_MIN_LEN || PLACEHOLDER_RE.test(d);
103
+ return d.length < UC_DESC_MIN_LEN || hasPlaceholder(d);
104
104
  })
105
105
  .map(uc => ({
106
106
  rule_id: 'UC-04',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sigloch/contracts",
3
- "version": "10.13.0",
3
+ "version": "10.14.0",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",