@sigloch/contracts 9.1.0 → 10.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -23,14 +23,37 @@ const measurablePattern = /(\d+[\s]*(ms|s|sec|min|%|percent|byte|MB|GB|times|x|s
23
23
  function reqElements(graph) {
24
24
  return graph.elements.filter(e => e.type === 'REQ');
25
25
  }
26
+ /**
27
+ * CR-SM-275: `description` ist im Zod-Schema Pflicht, aber die Regeln bekommen Graphen aus
28
+ * Store/Import ohne Re-Parse — dort fehlt das Feld. `req.description.trim()` warf dann
29
+ * `TypeError` und riss `evaluateAllRules` ab (gemessen am kadjar-Graphen, Spike CR-GC-438 §7).
30
+ * EIN Normalisierungspunkt statt eines Guards je Fundstelle: eine fehlende Beschreibung ist
31
+ * die leere Beschreibung — ein BEFUND (BQ-06/BQ-07 melden sie), kein Absturz.
32
+ */
33
+ /**
34
+ * CR-SM-285: `?? ''` faengt nur `null`/`undefined`. Ein Graph aus einer fremden Quelle (Import,
35
+ * fremdes Tool, handgeschriebene Fixture) kann `description` als Zahl, Objekt oder Array tragen —
36
+ * dann warf `.trim()` und `evaluateAllRules` STARB, statt zu urteilen. Am Gate heisst das:
37
+ * Stacktrace statt `block`-Verdict, der Aufrufer sieht kein Urteil.
38
+ *
39
+ * Gemessen vor dem Fix: `null` ok, `42`/`{}`/`[]`/`true` -> "descriptionOf(...).trim is not a
40
+ * function". Vier von fuenf Typen.
41
+ *
42
+ * Nicht-Strings werden zu `''` — also behandelt wie "keine Beschreibung". Das ist die richtige
43
+ * Deutung: ein Objekt IST keine Beschreibung, und die Regel soll das melden statt zu raten oder
44
+ * zu sterben.
45
+ */
46
+ function descriptionOf(el) {
47
+ return typeof el.description === 'string' ? el.description : '';
48
+ }
26
49
  // ---------------------------------------------------------------------------
27
50
  // BQ-01 Unambiguous — detect weasel words in REQ descriptions
28
51
  // ---------------------------------------------------------------------------
29
52
  export function bq01Unambiguous(graph) {
30
53
  return reqElements(graph)
31
- .filter(req => weaselPattern.test(req.description))
54
+ .filter(req => weaselPattern.test(descriptionOf(req)))
32
55
  .map(req => {
33
- const match = req.description.match(weaselPattern);
56
+ const match = descriptionOf(req).match(weaselPattern);
34
57
  return {
35
58
  rule_id: 'BQ-01',
36
59
  severity: 'warning',
@@ -50,7 +73,7 @@ export function bq01Unambiguous(graph) {
50
73
  // ---------------------------------------------------------------------------
51
74
  export function bq02Verifiable(graph) {
52
75
  return reqElements(graph)
53
- .filter(req => !measurablePattern.test(req.description))
76
+ .filter(req => !measurablePattern.test(descriptionOf(req)))
54
77
  .map(req => ({
55
78
  rule_id: 'BQ-02',
56
79
  severity: 'warning',
@@ -85,6 +108,24 @@ export function setBQ04SimilarityMatrix(data) {
85
108
  /**
86
109
  * BQ-04 checks for duplicate / near-duplicate requirements using
87
110
  * pre-computed embedding similarity. Returns [] when no matrix is set.
111
+ *
112
+ * CR-SM-286: **bewusst NICHT angeschlossen — anders als ND-01/ND-02.**
113
+ *
114
+ * Die Naht ist dieselbe und sie ist genauso tot: `setBQ04SimilarityMatrix()` ruft im gesamten
115
+ * Familienbaum niemand (CR-SM-278, erneut geprueft). Der Unterschied liegt in der Eingabe. ND-01
116
+ * und ND-02 nennen deterministische Formeln ueber Graphinhalt (Jaccard ueber Beschreibung,
117
+ * Verb, io-Topologie, Felder) — die konnten nach `similarity.ts` wandern und tun dort dasselbe.
118
+ * BQ-04 verlangt laut eigener Zeile "pre-computed EMBEDDING similarity", und Embeddings kann ein
119
+ * reines Vertragspaket nicht berechnen: kein Modell, kein Netz, kein Zufall.
120
+ *
121
+ * Ein Ersatzmass haette ich erfinden muessen. Ein Versuch mit 0.7*Beschreibung + 0.3*Name lief:
122
+ * **0 Befunde an allen neun Familiengraphen** und 4950 an einer templatierten Fixture — also
123
+ * genau die zwei Gate-7-Ausreisser zugleich ("0 Befunde am Selbstmodell" und "quadratisch mit
124
+ * der Graphgroesse"). Erfundene Gewichte ohne Messung sind keine Reparatur.
125
+ *
126
+ * Damit ist BQ-04 der naechste AO-D03-Fall: entweder eine Eingabe, die der Host liefern MUSS
127
+ * (dann gehoert die Regel nicht in ein reines Paket), oder streichen. Das ist eine
128
+ * Grammatik-Entscheidung — `se-grammar-review`, eigener CR, nicht hier nebenbei.
88
129
  */
89
130
  export function bq04Necessary(graph) {
90
131
  if (!_bq04Matrix)
@@ -141,7 +182,7 @@ const conformingPatternDE = /\b(soll|muss|darf nicht)\s+\w+/i;
141
182
  export function bq06Conforming(graph) {
142
183
  return reqElements(graph)
143
184
  .filter(req => {
144
- const desc = req.description.trim();
185
+ const desc = descriptionOf(req).trim();
145
186
  return !conformingPattern.test(desc) && !conformingPatternDE.test(desc);
146
187
  })
147
188
  .map(req => ({
@@ -174,17 +215,18 @@ export function bq07Complete(graph) {
174
215
  // zulaessig ist. Das ist die Fehlmessung und deshalb R-18/error; das blosse Fehlen der
175
216
  // Angabe ist ein Vollstaendigkeitssignal und deshalb hier/warning — dieselbe Trennung, die
176
217
  // CR-SM-262 fuer RC-06 begruendet hat.
177
- const isIncomplete = (req) => req.description.trim().length < BQ07_MIN_DESC_LENGTH ||
178
- placeholderPattern.test(req.description) ||
218
+ const isIncomplete = (req) => descriptionOf(req).trim().length < BQ07_MIN_DESC_LENGTH ||
219
+ placeholderPattern.test(descriptionOf(req)) ||
179
220
  normalizeReqKinds(req.kinds).length === 0;
180
221
  return reqElements(graph)
181
222
  .filter(isIncomplete)
182
223
  .map(req => {
224
+ const desc = descriptionOf(req);
183
225
  const reasons = [];
184
- if (req.description.trim().length < BQ07_MIN_DESC_LENGTH) {
185
- reasons.push(`description too short (${req.description.trim().length}/${BQ07_MIN_DESC_LENGTH} chars)`);
226
+ if (desc.trim().length < BQ07_MIN_DESC_LENGTH) {
227
+ reasons.push(`description too short (${desc.trim().length}/${BQ07_MIN_DESC_LENGTH} chars)`);
186
228
  }
187
- if (placeholderPattern.test(req.description)) {
229
+ if (placeholderPattern.test(desc)) {
188
230
  reasons.push('contains placeholder');
189
231
  }
190
232
  if (normalizeReqKinds(req.kinds).length === 0) {
@@ -9,24 +9,24 @@
9
9
  */
10
10
  import { z } from 'zod/v4';
11
11
  export declare const ReadinessDimension: z.ZodEnum<{
12
+ schema: "schema";
12
13
  req: "req";
13
14
  uc: "uc";
14
15
  arch: "arch";
15
16
  alloc: "alloc";
16
17
  ver: "ver";
17
- schema: "schema";
18
18
  cr: "cr";
19
19
  ms: "ms";
20
20
  }>;
21
21
  export type ReadinessDimensionType = z.infer<typeof ReadinessDimension>;
22
22
  export declare const ReadinessScore: z.ZodObject<{
23
23
  dimension: z.ZodEnum<{
24
+ schema: "schema";
24
25
  req: "req";
25
26
  uc: "uc";
26
27
  arch: "arch";
27
28
  alloc: "alloc";
28
29
  ver: "ver";
29
- schema: "schema";
30
30
  cr: "cr";
31
31
  ms: "ms";
32
32
  }>;
@@ -49,12 +49,12 @@ export type ReadinessScoreType = z.infer<typeof ReadinessScore>;
49
49
  export declare const ReadinessReport: z.ZodObject<{
50
50
  scores: z.ZodArray<z.ZodObject<{
51
51
  dimension: z.ZodEnum<{
52
+ schema: "schema";
52
53
  req: "req";
53
54
  uc: "uc";
54
55
  arch: "arch";
55
56
  alloc: "alloc";
56
57
  ver: "ver";
57
- schema: "schema";
58
58
  cr: "cr";
59
59
  ms: "ms";
60
60
  }>;
@@ -14,7 +14,7 @@ export const ReadinessDimension = z.enum([
14
14
  'arch', // Functional architecture (R-02, R-03, R-10, R-12)
15
15
  'alloc', // Module allocation (R-04)
16
16
  'ver', // Test coverage (R-01, R-05)
17
- 'schema', // Interface completeness (R-26 binding, SC-02/SC-04 usage)
17
+ 'schema', // Interface completeness (R-26 binding, SC-02 usage)
18
18
  'cr', // CR traceability (CR-R01..R03)
19
19
  'ms', // Milestone planning (MS-01..02)
20
20
  ]);
@@ -79,8 +79,7 @@ export const RULE_TO_DIMENSION = {
79
79
  // CR-SM-231: R-29 (Testdatei-Exklusivitaet) gehoert zu 'ver' wie R-19 — beide bewerten die
80
80
  // Evidenz-Bindung einer Abnahme, R-19 ihre Praesenz, R-29 ihre Eindeutigkeit.
81
81
  'R-29': 'ver',
82
- 'R-22': 'alloc', 'R-23': 'alloc', 'R-26': 'schema', 'R-27': 'arch',
83
- // CR-GC-366: R-30 (Wirkketten-Bindung) und R-31 (io-Verdrahtung) bewerten beide, ob ein
82
+ 'R-22': 'alloc', 'R-23': 'alloc', 'R-26': 'schema', // CR-GC-366: R-30 (Wirkketten-Bindung) und R-31 (io-Verdrahtung) bewerten beide, ob ein
84
83
  // Funktionsblock ueberhaupt im Bauplan haengt — dieselbe Dimension wie R-20 (realRef).
85
84
  // NICHT 'uc': R-14..R-17 fragen, ob ein Behaelter gefuellt ist;
86
85
  // diese beiden fragen von der FUNC aus, ob sie angeschlossen ist.
@@ -88,19 +87,19 @@ export const RULE_TO_DIMENSION = {
88
87
  // uc
89
88
  'UC-01': 'uc', 'UC-02': 'uc', 'UC-03': 'uc', 'UC-04': 'uc',
90
89
  'UC-05': 'uc', 'UC-06': 'uc',
91
- 'R-14': 'uc', 'R-15': 'uc', 'R-16': 'uc', 'R-17': 'uc',
90
+ 'R-15': 'uc', 'R-16': 'uc', 'R-17': 'uc',
92
91
  // FC-04 (CR-SM-226): FCHAIN actor-bounded (trigger+consumer) — same dimension as FC-01..03.
93
- 'FC-01': 'uc', 'FC-02': 'uc', 'FC-03': 'uc', 'FC-04': 'uc',
92
+ 'FC-02': 'uc', 'FC-03': 'uc', 'FC-04': 'uc',
94
93
  // arch
95
- 'R-02': 'arch', 'R-03': 'arch', 'R-10': 'arch', 'R-12': 'arch',
94
+ 'R-02': 'arch', 'R-10': 'arch', 'R-12': 'arch',
96
95
  // alloc
97
96
  'R-04': 'alloc',
98
97
  // ver
99
98
  'R-01': 'ver', 'R-05': 'ver',
100
99
  // schema
101
100
  'SC-02': 'schema', // SC-01/SC-03 deleted (BOK-CR-026) — R-26 is the binding rule
102
- // CR-SM-226: SC-04 sharp per-FLOW SCHEMA-binding check same dimension as SC-02.
103
- 'SC-04': 'schema',
101
+ // SC-04 entfiel mit CR-SM-271: FLOW ohne SCHEMA ist jetzt Grammatik (R-18-Bein, error)
102
+ // und zaehlt damit unter R-18/'arch' statt als eigene 'schema'-Zeile.
104
103
  // structural rules without primary dimension → assigned by closest concern
105
104
  'R-08': 'arch',
106
105
  // near-duplicate detection
@@ -110,12 +109,12 @@ export const RULE_TO_DIMENSION = {
110
109
  // rule (CR-SM-223) — a per-module advisory would depress this score permanently.
111
110
  'MT-01': 'alloc', 'MT-02': 'alloc',
112
111
  // CR traceability
113
- 'CR-R01': 'cr', 'CR-R02': 'cr', 'CR-R03': 'cr', 'CR-R04': 'cr',
114
- // architecture optimization
115
- 'AO-D01': 'arch', 'AO-D03': 'arch',
112
+ 'CR-R01': 'cr', 'CR-R02': 'cr', 'CR-R03': 'cr', // architecture optimization
113
+ // CR-SM-283: BW-02 (Whitebox-Randbreite) ersetzt AO-D03 an dieser Stelle — dieselbe
114
+ // Dimension, denn beide fragen nach der Struktur des Funktionsschnitts, nicht nach Evidenz.
115
+ 'BW-02': 'arch',
116
116
  // allocation rules (CR-191)
117
- 'CR-01': 'arch', 'RT-01': 'arch', 'PH-01': 'arch', 'CA-01': 'arch',
118
- // milestone planning
117
+ 'CR-01': 'arch', // milestone planning
119
118
  'MS-01': 'ms', 'MS-02': 'ms', 'MS-03': 'ms',
120
119
  // FMEA / risk
121
120
  'FM-01': 'req', 'FM-02': 'req', 'FM-03': 'ver',
@@ -161,7 +160,7 @@ export const RULE_TO_PHASE = {
161
160
  'BQ-01': 'SRR', 'BQ-02': 'SRR', 'BQ-04': 'SRR', 'BQ-06': 'SRR', 'BQ-07': 'SRR',
162
161
  'RD-01': 'SRR', 'RD-02': 'SRR', 'RD-03': 'SRR',
163
162
  'UC-01': 'SRR', 'UC-02': 'SRR', 'UC-03': 'SRR', 'UC-04': 'SRR', 'UC-05': 'SRR', 'UC-06': 'SRR',
164
- 'R-14': 'SRR', 'R-16': 'SRR', 'R-17': 'SRR',
163
+ 'R-16': 'SRR', 'R-17': 'SRR',
165
164
  'FC-02': 'SRR',
166
165
  'CL-01': 'SRR',
167
166
  'FM-01': 'SRR', 'FM-02': 'SRR',
@@ -169,21 +168,18 @@ export const RULE_TO_PHASE = {
169
168
  'MS-01': 'SRR', 'MS-02': 'SRR', 'MS-03': 'SRR',
170
169
  // PDR — architecture/functional completeness.
171
170
  'RD-04': 'PDR',
172
- 'R-02': 'PDR', 'R-03': 'PDR', 'R-08': 'PDR', 'R-10': 'PDR', 'R-12': 'PDR', 'R-18': 'PDR',
171
+ 'R-02': 'PDR', 'R-08': 'PDR', 'R-10': 'PDR', 'R-12': 'PDR', 'R-18': 'PDR',
173
172
  'R-04': 'PDR', 'R-22': 'PDR', 'R-23': 'PDR',
174
173
  'R-15': 'PDR',
175
- 'FC-01': 'PDR', 'FC-03': 'PDR', 'FC-04': 'PDR',
174
+ 'FC-03': 'PDR', 'FC-04': 'PDR',
176
175
  // CR-GC-366: Anschluss des Funktionsbaus — gehoert an dasselbe Gate wie R-15/IO-01,
177
176
  // die auf derselben Kette aufsetzen.
178
177
  'R-30': 'PDR', 'R-31': 'PDR',
179
178
  'MT-01': 'PDR', 'MT-02': 'PDR',
180
179
  'ND-01': 'PDR',
181
- 'AO-D01': 'PDR', 'AO-D03': 'PDR', 'CR-01': 'PDR', 'RT-01': 'PDR', 'PH-01': 'PDR', 'CA-01': 'PDR',
182
- 'IO-01': 'PDR', // CR-SM-226: extended to all FCHAIN FUNC-pairs, added to the phase axis.
183
- 'CR-R04': 'PDR',
180
+ 'BW-02': 'PDR', 'CR-01': 'PDR', 'IO-01': 'PDR', // CR-SM-226: extended to all FCHAIN FUNC-pairs, added to the phase axis.
184
181
  // CDR — critical design/schema completeness.
185
- 'R-26': 'CDR', 'R-27': 'CDR',
186
- 'SC-02': 'CDR', 'SC-04': 'CDR', // SC-04: FLOW→SCHEMA sharp rule (CR-SM-226)
182
+ 'R-26': 'CDR', 'SC-02': 'CDR', // SC-04 entfiel mit CR-SM-271 — der FLOW-ohne-SCHEMA-Fall haengt an R-18/PDR
187
183
  'ND-02': 'CDR',
188
184
  'NFR-01': 'CDR',
189
185
  // TRR — test readiness.
@@ -80,6 +80,9 @@ export declare const ViolationContext: z.ZodObject<{
80
80
  }, z.core.$strip>>>;
81
81
  parent_module: z.ZodOptional<z.ZodString>;
82
82
  current_description: z.ZodOptional<z.ZodString>;
83
+ also_affects: z.ZodOptional<z.ZodArray<z.ZodString>>;
84
+ value: z.ZodOptional<z.ZodNumber>;
85
+ threshold: z.ZodOptional<z.ZodNumber>;
83
86
  }, z.core.$strip>;
84
87
  export type ViolationContext = z.infer<typeof ViolationContext>;
85
88
  export declare const RuleViolation: z.ZodObject<{
@@ -139,6 +142,9 @@ export declare const RuleViolation: z.ZodObject<{
139
142
  }, z.core.$strip>>>;
140
143
  parent_module: z.ZodOptional<z.ZodString>;
141
144
  current_description: z.ZodOptional<z.ZodString>;
145
+ also_affects: z.ZodOptional<z.ZodArray<z.ZodString>>;
146
+ value: z.ZodOptional<z.ZodNumber>;
147
+ threshold: z.ZodOptional<z.ZodNumber>;
142
148
  }, z.core.$strip>>;
143
149
  }, z.core.$strip>;
144
150
  export type RuleViolation = z.infer<typeof RuleViolation>;