@sigloch/contracts 10.1.0 → 10.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/se/policy.js CHANGED
@@ -45,8 +45,20 @@ export const MetricPolicySchema = z.object({
45
45
  * graphcode-Selbstmodell 20 zusaetzliche Befunde erzeugt, und `readiness` zaehlt `info`
46
46
  * ungefiltert in den Nenner (`score = 1 - Verstoesse / applicable`) — genau der Grund, aus dem
47
47
  * MT-03 zur Messung statt zur Regel wurde. Wer doktrin-streng fahren will, setzt 5.
48
+ *
49
+ * CR-SM-311: die Breite ist ein BAND. `min` ist die Untergrenze (Z3, ITEM-2026-053): eine Ebene
50
+ * mit weniger Elementen abstrahiert nichts. Beide Grenzen sind Pflicht, beide exklusiv — `min`
51
+ * und `warning` selbst liegen im Band.
48
52
  */
49
- decompositionBreadth: z.object({ warning: z.number().int().min(2) }).nullable(),
53
+ decompositionBreadth: z
54
+ .object({
55
+ /** Unter `min` Kindern ist eine Ebene entartet. */
56
+ min: z.number().int().min(1),
57
+ /** Ueber `warning` Kindern ist eine Ebene zu breit. */
58
+ warning: z.number().int().min(2),
59
+ })
60
+ .refine((b) => b.min <= b.warning, { message: 'decompositionBreadth.min must not exceed decompositionBreadth.warning' })
61
+ .nullable(),
50
62
  /**
51
63
  * CR-SM-283: Randbreite je Blackbox (BW-02) — verschiedene Vertraege, die den Rand queren.
52
64
  * Bewusst OHNE `info`-Stufe: die Verteilung gibt sie nicht her. bok und graph-view-edit enden
@@ -55,6 +67,26 @@ export const MetricPolicySchema = z.object({
55
67
  * aus dem MT-03 zur Messung statt zur Regel wurde. `null` -> messen, nicht urteilen.
56
68
  */
57
69
  boundaryWidth: z.object({ warning: z.number().int().min(1) }).nullable(),
70
+ /**
71
+ * CR-SM-314: ab wie vielen Wirkketten gilt eine FUNC als INFRASTRUKTUR.
72
+ *
73
+ * Die Zahl selbst (`functionCriticality`) urteilt nie — sie steht in jeder FUNC-Zeile. Diese
74
+ * Schwelle sagt nur, wo eine Regel sie als Ausnahme lesen darf; erster Abnehmer ist R-21
75
+ * (CR-SM-313): eine Uebergabe an eine Funktion, die quer durch alle Ketten laeuft, ist kein
76
+ * fehlender Integrationstest, sondern ein Bus.
77
+ *
78
+ * ABGELESEN, nicht gesetzt — aber nicht an der Verteilungsform. Die ist ein duenner Schwanz
79
+ * (17 Familiengraphen: 224 FUNCs in 1 Kette, 37 in 2, 6 in 3, 8 in 4, je 1 in 5/6/11) und gibt
80
+ * wie bei `instability` keine Schwelle her (CR-SM-293). Ablesbar ist die WIRKUNG: auf
81
+ * graphcodes 31 kettenlose Uebergaben stellen die Schwellen 3 und 4 identisch 19 frei, 5 und 6
82
+ * identisch 6. Ein Plateau von 3 bis 4 — innerhalb dessen die Wahl wirkungsfrei ist. Der
83
+ * Startwert ist seine Untergrenze.
84
+ *
85
+ * `min(2)`, nicht `min(1)`: bei 1 waere JEDE FUNC in einer Kette Infrastruktur, und die
86
+ * Ausnahme fraesse ihre eigene Regel. `null` -> keine FUNC gilt als Infrastruktur; die Zahl
87
+ * bleibt trotzdem gemessen.
88
+ */
89
+ criticality: z.object({ infrastructure: z.number().int().min(2) }).nullable(),
58
90
  /**
59
91
  * FM-03: RPN (severity · occurrence · detection), ab dem ein Risiko-REQ eine bestandene
60
92
  * Verifikation braucht (exklusiv). null → FM-03 feuert nie.
@@ -75,23 +107,6 @@ export const MetricPolicySchema = z.object({
75
107
  * exportierten Graphen steht sie nie** (Zellwerte sind normative Wertung, keine Fakten).
76
108
  */
77
109
  apTable: ApTableSchema.nullable(),
78
- /**
79
- * R-04: Modulgröße **gegen** Kreuzungen — die Regel wägt beides ab, ihr alter Name
80
- * („Max module size") gab das nicht her. null → R-04 feuert nie.
81
- *
82
- * Drei Werte, weil die Regel drei Urteile fällt. Ein einzelner Schwellwert hätte zwei davon
83
- * im Code gelassen — genau der Zustand, den dieser CR beseitigt.
84
- */
85
- moduleSize: z
86
- .object({
87
- /** > `large` FUNCs: mit Kreuzungen warning, ohne Kreuzungen info („kohäsiv, nur groß"). */
88
- large: z.number().int().min(1),
89
- /** > `coupled` FUNCs ist die Untergrenze, ab der die Regel überhaupt hinsieht. */
90
- coupled: z.number().int().min(1),
91
- /** Kreuzungen, ab denen ein nur `coupled`-großes Modul warnt (exklusiv). */
92
- crossings: z.number().int().min(0),
93
- })
94
- .nullable(),
95
110
  });
96
111
  /**
97
112
  * Der Startwert, sichtbar als solcher — **kein** Fallback in der Regel.
@@ -133,17 +148,13 @@ export const DEFAULT_METRIC_POLICY = {
133
148
  // weil `info` bei 5 den readiness-Nenner verwaessert haette; das Argument traegt fuer die
134
149
  // OBERgrenze nicht, denn RD-04 meldet als `warning`. Die Zahl steht jetzt an EINER Stelle und
135
150
  // gilt fuer beide Zerlegungsfragen -- Sub-FUNC und Sub-MOD.
136
- decompositionBreadth: { warning: 9 },
151
+ // CR-SM-311: Untergrenze 3 eine Ebene mit weniger Elementen ist entartet (Z3).
152
+ decompositionBreadth: { min: 3, warning: 9 },
137
153
  // CR-SM-283: aus der Verteilung abgelesen — bok/gve max 3, graphcode 19, moneyflow 17.
138
154
  boundaryWidth: { warning: 5 },
155
+ // CR-SM-314: Untergrenze des gemessenen Plateaus 3..4 (siehe Feldkommentar oben).
156
+ criticality: { infrastructure: 3 },
139
157
  riskRpn: 100,
140
- // `large`/`coupled`/`crossings` = die drei Literale aus `maxModuleSize`: vorher
141
- // `funcCount <= 8` → skip, `funcCount > 12` → groß, `crossings > 2` → gekoppelt.
142
- // CR-SM-296: an dieselbe Doktrin gebunden statt danebengesetzt. R-04 ist seit diesem CR ALLEIN
143
- // fuer die Modulgroesse zustaendig (RD-04 hat ihr Allokations-Bein abgegeben), also gilt hier
144
- // dieselbe 7+-2: `large` = 9 (Obergrenze), `coupled` = 7 (Untergrenze, ab der die Regel
145
- // hinsieht). Vorher 12/8 -- zwei Zahlen fuer dieselbe Frage, unverbunden mit den 11 darueber.
146
- moduleSize: { large: 9, coupled: 7, crossings: 2 },
147
158
  // CR-SM-229: kein Startwert möglich und keiner gewollt — die Tabelle steht nur im Handbook.
148
159
  // `null` heißt hier nicht „aus", sondern „markierter Übergang": bestätigte AP-Invarianten,
149
160
  // sonst RPN-Bänder. `apMethod()` macht das für den Leser sichtbar.
@@ -34,7 +34,6 @@ export declare const ReadinessScore: z.ZodObject<{
34
34
  violations: z.ZodNumber;
35
35
  applicable: z.ZodNumber;
36
36
  coreApplicable: z.ZodNumber;
37
- ready: z.ZodBoolean;
38
37
  }, z.core.$strip>;
39
38
  export type ReadinessScoreType = z.infer<typeof ReadinessScore>;
40
39
  /**
@@ -62,7 +61,6 @@ export declare const ReadinessReport: z.ZodObject<{
62
61
  violations: z.ZodNumber;
63
62
  applicable: z.ZodNumber;
64
63
  coreApplicable: z.ZodNumber;
65
- ready: z.ZodBoolean;
66
64
  }, z.core.$strip>>;
67
65
  timestamp: z.ZodISODateTime;
68
66
  }, z.core.$strip>;
@@ -44,10 +44,9 @@ export const ReadinessScore = z.object({
44
44
  * Unterscheidung, an der der dokumentierte Ausweg aus CR-SM-237 gescheitert ist.
45
45
  */
46
46
  coreApplicable: z.number().int(),
47
- // CR-SM-235: `score >= readyThreshold`. Die Schwelle steht bewusst NICHT hier sie ist
48
- // Eingabe von `computeReadiness`, und eine Zahl im Kommentar wäre die dritte Meinung dazu.
49
- // CR-SM-270: bei `score === null` ist `ready` immer `false` nicht messbar ist nicht bereit.
50
- ready: z.boolean(),
47
+ // CR-SM-310: kein `ready` mehr. Ob eine Dimension zu schwach ist, urteilt der Konsument mit
48
+ // SEINER Schwelle (graphcode: generate.ts, focusThreshold aus der Config). Die Messung traegt
49
+ // kein Urteil vorher stand es zweimal: hier als `ready` und beim Konsumenten.
51
50
  });
52
51
  /**
53
52
  * CR-SM-237: `overallScore` ist weg. Es war das **ungewichtete** Mittel der acht
@@ -96,12 +95,14 @@ export const RULE_TO_DIMENSION = {
96
95
  'RD-01': 'req', 'RD-02': 'req', 'RD-03': 'req',
97
96
  // RD-04 is decomposition *breadth* — an architecture concern, not a requirement one
98
97
  'RD-04': 'arch',
98
+ // CR-SM-311: die Untergrenze derselben Breite — ebenfalls Architektur
99
+ 'RD-05': 'arch',
99
100
  // trace/realization/allocation completeness rules (CR-228 D: previously unmapped → advisory fall-through)
100
101
  'R-18': 'arch', 'R-19': 'ver', 'R-20': 'arch', 'R-21': 'ver',
101
102
  // CR-SM-231: R-29 (Testdatei-Exklusivitaet) gehoert zu 'ver' wie R-19 — beide bewerten die
102
103
  // Evidenz-Bindung einer Abnahme, R-19 ihre Praesenz, R-29 ihre Eindeutigkeit.
103
104
  'R-29': 'ver',
104
- '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
105
+ 'R-22': 'alloc', 'R-23': 'alloc', 'R-26': 'schema', 'R-32': 'ver', // CR-GC-366: R-30 (Wirkketten-Bindung) und R-31 (io-Verdrahtung) bewerten beide, ob ein
105
106
  // Funktionsblock ueberhaupt im Bauplan haengt — dieselbe Dimension wie R-20 (realRef).
106
107
  // NICHT 'uc': R-14..R-17 fragen, ob ein Behaelter gefuellt ist;
107
108
  // diese beiden fragen von der FUNC aus, ob sie angeschlossen ist.
@@ -149,6 +150,7 @@ export const RULE_TO_DIMENSION = {
149
150
  'NFR-01': 'arch',
150
151
  // cross-module IO (CR-192)
151
152
  'IO-01': 'arch',
153
+ 'IO-02': 'arch', // CR-SM-307: Flusstopologie, dieselbe Dimension wie R-10/IO-01.
152
154
  // view rules (CR-184)
153
155
  'VR-01': 'ver',
154
156
  'CL-01': 'uc',
@@ -189,7 +191,7 @@ export const RULE_TO_PHASE = {
189
191
  'CR-R01': 'SRR', 'CR-R03': 'SRR',
190
192
  'MS-01': 'SRR', 'MS-02': 'SRR', 'MS-03': 'SRR',
191
193
  // PDR — architecture/functional completeness.
192
- 'RD-04': 'PDR',
194
+ 'RD-04': 'PDR', 'RD-05': 'PDR',
193
195
  'R-02': 'PDR', 'R-08': 'PDR', 'R-10': 'PDR', 'R-12': 'PDR', 'R-18': 'PDR',
194
196
  'R-04': 'PDR', 'R-22': 'PDR', 'R-23': 'PDR',
195
197
  'R-15': 'PDR',
@@ -199,13 +201,13 @@ export const RULE_TO_PHASE = {
199
201
  'R-30': 'PDR', 'R-31': 'PDR',
200
202
  'MT-01': 'PDR', 'MT-02': 'PDR',
201
203
  'ND-01': 'PDR',
202
- 'BW-02': 'PDR', 'CR-01': 'PDR', 'IO-01': 'PDR', // CR-SM-226: extended to all FCHAIN FUNC-pairs, added to the phase axis.
204
+ 'BW-02': 'PDR', 'CR-01': 'PDR', 'IO-01': 'PDR', 'IO-02': 'PDR', // CR-SM-226: extended to all FCHAIN FUNC-pairs, added to the phase axis.
203
205
  // CDR — critical design/schema completeness.
204
206
  'R-26': 'CDR', 'SC-02': 'CDR', // SC-04 entfiel mit CR-SM-271 — der FLOW-ohne-SCHEMA-Fall haengt an R-18/PDR
205
207
  'ND-02': 'CDR',
206
208
  'NFR-01': 'CDR',
207
209
  // TRR — test readiness.
208
- 'R-01': 'TRR', 'R-05': 'TRR', 'R-19': 'TRR', 'R-20': 'TRR', 'R-21': 'TRR',
210
+ 'R-01': 'TRR', 'R-05': 'TRR', 'R-32': 'TRR', 'R-19': 'TRR', 'R-20': 'TRR', 'R-21': 'TRR',
209
211
  // CR-SM-231: eine doppelt beanspruchte Testdatei laesst den TRR-Gate dieselbe Evidenz
210
212
  // doppelt zaehlen — der Befund gehoert genau an dieses Gate.
211
213
  'R-29': 'TRR',
@@ -147,6 +147,10 @@ export const RULE_HELP = {
147
147
  plain: "Two steps in the same sequence have no described data passing between them → add the data one hands to the other.",
148
148
  se: "A `FUNC` pair inside one `FCHAIN` with no `io` path (`FUNC -io-> FLOW -io-> FUNC`) connecting them.",
149
149
  },
150
+ 'IO-02': {
151
+ 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.",
152
+ 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.",
153
+ },
150
154
  'MS-01': {
151
155
  plain: "A milestone has no work assigned to it → assign the work items that belong to it.",
152
156
  se: "`MS` with no `CR` `relation`.",
@@ -190,13 +194,17 @@ export const RULE_HELP = {
190
194
  se: "`FUNC` with no `satisfy` trace to a `REQ` (design→requirement traceability).",
191
195
  },
192
196
  'R-04': {
193
- plain: "A module does too much or is too tangledopen it and split it.",
194
- se: "`MOD` with >12 `FUNC`, or 8–12 `FUNC` with >2 flows crossing the module boundary (cohesion/coupling).",
197
+ plain: "Too many different data formats cross this module's boundarymerge formats, or move the functions that carry them.",
198
+ 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.",
195
199
  prompt: "se-view:arch",
196
200
  },
197
201
  'R-05': {
198
202
  plain: "A test doesn't check any feature you promised → link it to the feature it tests, or remove it.",
199
- se: "`TEST` with no `verify` trace (test→requirement coverage) to a `REQ`.",
203
+ se: "`TEST` with no `verify` trace at all — neither to a `REQ` (test→requirement coverage) nor to a `SCHEMA` (contract test, CR-SM-317).",
204
+ },
205
+ 'R-32': {
206
+ 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.",
207
+ se: "`SCHEMA` with a symbol `realRef` (a Zod contract) and no `TEST -verify-> SCHEMA`. RC-04 checks the parse at the boundary; this checks the contract test (CR-SM-319).",
200
208
  },
201
209
  'R-08': {
202
210
  plain: "A link points at something that no longer exists → repair or remove the broken link.",
@@ -235,8 +243,8 @@ export const RULE_HELP = {
235
243
  se: "Realized `FUNC` with no valid `realRef` `{file, symbol}` (graph↔code binding); else `concept:true` / `external:true`.",
236
244
  },
237
245
  'R-21': {
238
- plain: "You grouped functions into a chain that passes data along, but nothing tests that hand-off → add an integration test that checks the chain works.",
239
- se: "FUNC↔FUNC connection (`FUNC` ─io→ `FLOW` ─io→ `FUNC`) whose endpoints DO share an `FCHAIN`, but no shared chain carries a verified integration test (`TEST` ─verify→ `REQ` ←satisfy─ `FCHAIN`). Pairs sharing no `FCHAIN` are silent: co-adjacency at a reused `FLOW` is not an asserted interface.",
246
+ plain: "Two functions hand data over, but they are not in one chain together or they are, and nothing tests the hand-off → put both into the same chain, and give that chain an integration test.",
247
+ 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 carries a verified integration test (`TEST` ─verify→ `REQ` ←satisfy─ `FCHAIN`) → finding at the chain. 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.",
240
248
  },
241
249
  'R-22': {
242
250
  plain: "A function isn't assigned to any building block, so it has no home in the structure → put it on a module.",
@@ -301,7 +309,11 @@ export const RULE_HELP = {
301
309
  },
302
310
  'RD-04': {
303
311
  plain: "One thing has more than 9 parts directly under it → group them, so each level stays readable.",
304
- se: "Decomposition breadth above `metricPolicy.decompositionBreadth.warning` children on one level — `FUNC` `compose` `FUNC`, `SYS`/`MOD` `compose` `MOD`, and the root `FUNC` forest anchored at `SYS` (CR-SM-282). Default 9, the upper end of 7±2 (CR-SM-296). The `FUNC` `allocate` `MOD` leg moved to R-04 in that CR: counting allocated FUNCs is module SIZE, and one question deserves one rule.",
312
+ 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.",
313
+ },
314
+ 'RD-05': {
315
+ 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.",
316
+ 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).",
305
317
  },
306
318
  'SC-02': {
307
319
  plain: "A data format is defined but nothing uses it → connect it to the data it describes, or drop it.",