@sigloch/contracts 10.12.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.
- package/dist/se/analysis-freshness-rules.js +41 -2
- package/dist/se/conformance-rules.js +40 -3
- package/dist/se/cr-quality-rules.js +72 -0
- package/dist/se/evaluate-all.d.ts +8 -1
- package/dist/se/evaluate-all.js +32 -29
- package/dist/se/fchain-quality-rules.d.ts +2 -1
- package/dist/se/fchain-quality-rules.js +132 -0
- package/dist/se/fmea-rules.js +19 -9
- package/dist/se/grammar-snapshot.d.ts +9 -9
- package/dist/se/grammar-snapshot.js +25 -10
- package/dist/se/index.d.ts +3 -3
- package/dist/se/index.js +3 -3
- package/dist/se/meta-model.js +27 -16
- package/dist/se/ontology.d.ts +42 -21
- package/dist/se/ontology.js +53 -16
- package/dist/se/placeholder.d.ts +1 -0
- package/dist/se/placeholder.js +13 -0
- package/dist/se/quality-rules.js +3 -3
- package/dist/se/readiness.d.ts +14 -0
- package/dist/se/readiness.js +30 -3
- package/dist/se/rule-help.d.ts +17 -3
- package/dist/se/rule-help.js +65 -12
- package/dist/se/rules.d.ts +16 -0
- package/dist/se/rules.js +63 -28
- package/dist/se/uc-quality-rules.js +2 -2
- package/package.json +1 -1
package/dist/se/rule-help.js
CHANGED
|
@@ -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
|
|
35
|
-
*
|
|
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.",
|
|
@@ -115,41 +120,56 @@ export const RULE_HELP = {
|
|
|
115
120
|
plain: "Several open changes touch the same thing, so they will collide → sequence them or merge them.",
|
|
116
121
|
se: "One element tracked by more than one `CR` with `status` open/in-progress.",
|
|
117
122
|
},
|
|
123
|
+
'CR-R05': {
|
|
124
|
+
plain: "This smallest-piece requirement has no work order — no change request points at it or at the function that fulfils it, and that function is not built yet, so no plan will ever build it → attach it to the change request that builds it.",
|
|
125
|
+
se: "Leaf `REQ` (no `compose`→`REQ` children) with no build order: no `CR -relation->` on the REQ itself nor on a `FUNC` that `satisfy`s it (CR-SM-343; the REQ side of CR-R01). A `FUNC` that is already built — valid `realRef` or `external`, not `concept` — covers the REQ without a CR (CR-SM-375). A CR on a `MOD`/`SYS` carrier does not count — a module is a container; such REQs take a direct `CR -relation-> REQ`. Silent, and not scored, on a graph without any `CR` node (`RULE_PRECONDITION`).",
|
|
126
|
+
prompt: "se-plan",
|
|
127
|
+
},
|
|
118
128
|
'FC-02': {
|
|
119
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.",
|
|
120
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.",
|
|
121
131
|
prompt: "se:author-uc",
|
|
132
|
+
vorschlag: 'Beschreibe die Abläufe {n} als Kette von Funktionen.',
|
|
122
133
|
},
|
|
123
134
|
'FC-03': {
|
|
124
135
|
plain: "A step inside a sequence contains further steps, so the sequence has hidden depth → lift them to the same level.",
|
|
125
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.',
|
|
126
138
|
},
|
|
127
139
|
'FC-04': {
|
|
128
140
|
plain: "A sequence either has nobody starting it or nothing coming back out → wire both ends to whoever uses it.",
|
|
129
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.',
|
|
143
|
+
},
|
|
144
|
+
'FC-05': {
|
|
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.",
|
|
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.',
|
|
130
148
|
},
|
|
131
149
|
'FM-01': {
|
|
132
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).",
|
|
133
|
-
se: "
|
|
151
|
+
se: "`REQ` with `role:\"risk\"` missing `severity` / `occurrence` / `detection` attributes (AIAG-VDA). The role is the requirement's motivation, not its kind — satisfy-neutral (CR-SM-365).",
|
|
134
152
|
prompt: "se-fmea",
|
|
135
153
|
},
|
|
136
154
|
'FM-02': {
|
|
137
155
|
plain: "A known risk has nothing planned against it → write the countermeasure as its own requirement.",
|
|
138
|
-
se: "
|
|
156
|
+
se: "`REQ` with `role:\"risk\"` and no `compose`d `REQ` with `role:\"mitigation\"`. The mitigation keeps its own kind (functional or non-functional) and is satisfied like any REQ of that kind (CR-SM-365).",
|
|
139
157
|
prompt: "se-fmea",
|
|
140
158
|
},
|
|
141
159
|
'FM-03': {
|
|
142
160
|
plain: "A high risk has no test that actually passed → add a test proving the countermeasure works.",
|
|
143
|
-
se: "
|
|
161
|
+
se: "`REQ` with `role:\"risk\"`, Action Priority High (AIAG-VDA, RPN band as interim) and no verifying `TEST` whose every `testRefs` entry has `result:\"passed\"`.",
|
|
144
162
|
prompt: "se-fmea",
|
|
145
163
|
},
|
|
146
164
|
'IO-01': {
|
|
147
165
|
plain: "Two steps in the same sequence have no described data passing between them → add the data one hands to the other.",
|
|
148
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}.',
|
|
149
168
|
},
|
|
150
169
|
'IO-02': {
|
|
151
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.",
|
|
152
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.',
|
|
153
173
|
},
|
|
154
174
|
'MS-01': {
|
|
155
175
|
plain: "A milestone has no work assigned to it → assign the work items that belong to it.",
|
|
@@ -167,10 +187,12 @@ export const RULE_HELP = {
|
|
|
167
187
|
'MT-01': {
|
|
168
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.",
|
|
169
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}.',
|
|
170
191
|
},
|
|
171
192
|
'MT-02': {
|
|
172
193
|
plain: "The parts inside this module never talk to each other → it is really several modules in one.",
|
|
173
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.',
|
|
174
196
|
},
|
|
175
197
|
'MT-04': {
|
|
176
198
|
plain: "This block's parts never talk to each other → it is really two blocks that happen to sit under one name.",
|
|
@@ -179,32 +201,39 @@ export const RULE_HELP = {
|
|
|
179
201
|
'ND-01': {
|
|
180
202
|
plain: "Two functions look like the same function written twice → merge them, or make clear what each one does differently.",
|
|
181
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.',
|
|
182
205
|
},
|
|
183
206
|
'ND-02': {
|
|
184
207
|
plain: "Two data contracts describe the same thing twice → merge them, or say what distinguishes them.",
|
|
185
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.',
|
|
186
210
|
},
|
|
187
211
|
'NFR-01': {
|
|
188
212
|
plain: "Something measured exceeds the limit that was set for it → fix it or change the limit deliberately.",
|
|
189
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.',
|
|
190
215
|
},
|
|
191
216
|
'R-01': {
|
|
192
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.",
|
|
193
218
|
se: "`REQ` with no incoming `verify` trace (test→requirement coverage, INCOSE V&V).",
|
|
194
219
|
prompt: "se:close-violations",
|
|
220
|
+
vorschlag: 'Ergänze Tests für die Anforderungen {n}.',
|
|
195
221
|
},
|
|
196
222
|
'R-02': {
|
|
197
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.",
|
|
198
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.',
|
|
199
226
|
},
|
|
200
227
|
'R-04': {
|
|
201
228
|
plain: "Too many different data formats cross this module's boundary → merge formats, or move the functions that carry them.",
|
|
202
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.",
|
|
203
230
|
prompt: "se-view:arch",
|
|
231
|
+
vorschlag: 'Verringere die Datenformate an der Grenze des Moduls {n}.',
|
|
204
232
|
},
|
|
205
233
|
'R-05': {
|
|
206
234
|
plain: "A test doesn't check any feature you promised → link it to the feature it tests, or remove it.",
|
|
207
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.',
|
|
208
237
|
},
|
|
209
238
|
'R-32': {
|
|
210
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.",
|
|
@@ -213,30 +242,37 @@ export const RULE_HELP = {
|
|
|
213
242
|
'R-08': {
|
|
214
243
|
plain: "A link points at something that no longer exists → repair or remove the broken link.",
|
|
215
244
|
se: "Trace whose source or target element is missing (dangling reference).",
|
|
245
|
+
vorschlag: 'Repariere oder entferne die Verbindungen an {n}, deren Ziel fehlt.',
|
|
216
246
|
},
|
|
217
247
|
'R-10': {
|
|
218
248
|
plain: "A piece of data goes nowhere — nothing produces or consumes it → connect it to a function or a person/outside system.",
|
|
219
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.',
|
|
220
251
|
},
|
|
221
252
|
'R-12': {
|
|
222
253
|
plain: "Two items depend on each other in a loop, so neither can stand alone → remove or redirect one of the two links.",
|
|
223
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.',
|
|
224
256
|
},
|
|
225
257
|
'R-15': {
|
|
226
|
-
plain: "A sequence of steps
|
|
227
|
-
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.',
|
|
228
261
|
},
|
|
229
262
|
'R-16': {
|
|
230
263
|
plain: "A person or outside system isn't connected to anything → connect it to the data it sends or receives.",
|
|
231
264
|
se: "`ACTOR` with no `io` trace to a `FLOW`.",
|
|
265
|
+
vorschlag: 'Verbinde den Nutzer {n} über Datenflüsse mit den Funktionen.',
|
|
232
266
|
},
|
|
233
267
|
'R-17': {
|
|
234
268
|
plain: "The top level of your project is empty — nothing is inside it → add the main use cases, features, or modules.",
|
|
235
269
|
se: "`SYS` with no `compose` to `UC` / `REQ` / `MOD`.",
|
|
270
|
+
vorschlag: 'Lege die Abläufe des Systems {n} an.',
|
|
236
271
|
},
|
|
237
272
|
'R-18': {
|
|
238
273
|
plain: "You connected two items in a combination that isn't allowed → use an allowed link, or fix what's at each end.",
|
|
239
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}.',
|
|
240
276
|
},
|
|
241
277
|
'R-19': {
|
|
242
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.",
|
|
@@ -247,16 +283,19 @@ export const RULE_HELP = {
|
|
|
247
283
|
se: "Realized `FUNC` with no valid `realRef` `{file, symbol}` (graph↔code binding); else `concept:true` / `external:true`.",
|
|
248
284
|
},
|
|
249
285
|
'R-21': {
|
|
250
|
-
plain: "Two functions hand data over, but they are not in one chain together — or they are, and
|
|
251
|
-
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
|
|
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.",
|
|
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.',
|
|
252
289
|
},
|
|
253
290
|
'R-22': {
|
|
254
291
|
plain: "A function isn't assigned to any building block, so it has no home in the structure → put it on a module.",
|
|
255
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.',
|
|
256
294
|
},
|
|
257
295
|
'R-23': {
|
|
258
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.",
|
|
259
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.',
|
|
260
299
|
},
|
|
261
300
|
'R-26': {
|
|
262
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).",
|
|
@@ -269,10 +308,12 @@ export const RULE_HELP = {
|
|
|
269
308
|
'R-30': {
|
|
270
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.",
|
|
271
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.',
|
|
272
312
|
},
|
|
273
313
|
'R-31': {
|
|
274
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.",
|
|
275
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.',
|
|
276
317
|
},
|
|
277
318
|
'RC-01': {
|
|
278
319
|
plain: "A function points to code that isn't there anymore (file moved or name changed) → repoint it to the current code.",
|
|
@@ -310,46 +351,58 @@ export const RULE_HELP = {
|
|
|
310
351
|
plain: "Code somewhere reads a data contract on its own, but the model does not know that place → go through the one translator the model names, or add the new translator to the model.",
|
|
311
352
|
se: "Zod-bound `SCHEMA` parsed (import + `.parse`/`.safeParse`) in a file that is neither its definition nor the `realRef` of a `FUNC` io-connected to a `FLOW` of this SCHEMA. The first code → model check: a second translator of one contract is a parallel path (spike CR-GC-637 — such paths share a contract, they are not similar). One finding per SCHEMA; test files exempt; runs only with `fileScope: 'all'` (CR-SM-358).",
|
|
312
353
|
},
|
|
354
|
+
'RC-10': {
|
|
355
|
+
plain: "No file in the code can be assigned to this building block, so nobody can check whether its imports cross boundaries the model does not know → give the block its source folder, or point one of its functions at its code.",
|
|
356
|
+
se: "`MOD` with neither a `path` attribute nor an allocated `FUNC` carrying a bound `realRef` — the only two ways a file resolves to a module. Without either, RC-05 and the boundary measurement are blind for this module by construction (sigllm: 0 RC-05 findings at 19 % import coverage, 8 at 81 %). The message names the cost from `importCoverage`. `concept`/`external` modules are exempt, as for R-20 (CR-SM-344).",
|
|
357
|
+
},
|
|
313
358
|
'RD-01': {
|
|
314
359
|
plain: "A smallest-piece feature has nothing built to fulfil it → add what implements it.",
|
|
315
360
|
se: "Leaf `REQ` (no `compose`→`REQ` children) with no `satisfy` from a `FUNC`/`FCHAIN`/`MOD`/`SYS`.",
|
|
316
361
|
prompt: "se:close-violations",
|
|
362
|
+
vorschlag: 'Lege an, was die Anforderungen {n} erfüllt.',
|
|
317
363
|
},
|
|
318
364
|
'RD-02': {
|
|
319
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.",
|
|
320
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.',
|
|
321
368
|
},
|
|
322
369
|
'RD-04': {
|
|
323
370
|
plain: "One thing has more than 9 parts directly under it → group them, so each level stays readable.",
|
|
324
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.',
|
|
325
373
|
},
|
|
326
374
|
'RD-05': {
|
|
327
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.",
|
|
328
|
-
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.',
|
|
329
378
|
},
|
|
330
379
|
'SC-02': {
|
|
331
380
|
plain: "A data format is defined but nothing uses it → connect it to the data it describes, or drop it.",
|
|
332
381
|
se: "`SCHEMA` not referenced by any `FLOW` via a `relation` trace.",
|
|
382
|
+
vorschlag: 'Verbinde das Datenformat {n} mit seinem Datenfluss oder entferne es.',
|
|
333
383
|
},
|
|
334
384
|
'UC-01': {
|
|
335
385
|
plain: "A scenario says what someone wants to do but never says what the system must provide → write down the requirements it needs.",
|
|
336
386
|
se: "`UC` with no `compose` trace to any `REQ`. The use case carries no requirement content.",
|
|
337
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.',
|
|
338
389
|
},
|
|
339
390
|
'UC-02': {
|
|
340
391
|
plain: "A scenario has nobody who triggers it → name who or what starts it.",
|
|
341
|
-
se: "`UC`
|
|
342
|
-
|
|
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.',
|
|
343
394
|
},
|
|
344
395
|
'UC-03': {
|
|
345
396
|
plain: "A scenario says what should be possible but not how it runs → describe the steps as a chain of functions.",
|
|
346
397
|
se: "`UC` with no `compose` trace to an `FCHAIN`. No behavioural scenario is declared.",
|
|
347
398
|
prompt: "se:author-uc",
|
|
399
|
+
vorschlag: 'Beschreibe die Abläufe {n} als Kette von Funktionen.',
|
|
348
400
|
},
|
|
349
401
|
'UC-04': {
|
|
350
402
|
plain: "A scenario has no real description, or still carries a placeholder like TBD → write what the user actually wants to achieve.",
|
|
351
403
|
se: "`UC` description shorter than 10 characters or containing TBD/TODO/FIXME/placeholder/XXX. Description IS the goal (CR-150).",
|
|
352
404
|
prompt: "se:author-uc",
|
|
405
|
+
vorschlag: 'Beschreibe das Ziel der Abläufe {n}.',
|
|
353
406
|
},
|
|
354
407
|
'VR-01': {
|
|
355
408
|
plain: "A test exists but no result was ever recorded, so nobody knows if it passed → record the outcome.",
|
package/dist/se/rules.d.ts
CHANGED
|
@@ -81,6 +81,14 @@ export declare const ViolationContext: z.ZodObject<{
|
|
|
81
81
|
parent_module: z.ZodOptional<z.ZodString>;
|
|
82
82
|
current_description: z.ZodOptional<z.ZodString>;
|
|
83
83
|
also_affects: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
84
|
+
side_components: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
85
|
+
members: z.ZodArray<z.ZodString>;
|
|
86
|
+
cause: z.ZodEnum<{
|
|
87
|
+
"missing-link": "missing-link";
|
|
88
|
+
bag: "bag";
|
|
89
|
+
}>;
|
|
90
|
+
via: z.ZodArray<z.ZodString>;
|
|
91
|
+
}, z.core.$strip>>>;
|
|
84
92
|
trace_rejection: z.ZodOptional<z.ZodObject<{
|
|
85
93
|
reason: z.ZodEnum<{
|
|
86
94
|
"no-pattern": "no-pattern";
|
|
@@ -212,6 +220,14 @@ export declare const RuleViolation: z.ZodObject<{
|
|
|
212
220
|
parent_module: z.ZodOptional<z.ZodString>;
|
|
213
221
|
current_description: z.ZodOptional<z.ZodString>;
|
|
214
222
|
also_affects: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
223
|
+
side_components: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
224
|
+
members: z.ZodArray<z.ZodString>;
|
|
225
|
+
cause: z.ZodEnum<{
|
|
226
|
+
"missing-link": "missing-link";
|
|
227
|
+
bag: "bag";
|
|
228
|
+
}>;
|
|
229
|
+
via: z.ZodArray<z.ZodString>;
|
|
230
|
+
}, z.core.$strip>>>;
|
|
215
231
|
trace_rejection: z.ZodOptional<z.ZodObject<{
|
|
216
232
|
reason: z.ZodEnum<{
|
|
217
233
|
"no-pattern": "no-pattern";
|
package/dist/se/rules.js
CHANGED
|
@@ -35,6 +35,17 @@ export const ViolationContext = z.object({
|
|
|
35
35
|
* unterschlagen.
|
|
36
36
|
*/
|
|
37
37
|
also_affects: z.array(z.string()).optional(),
|
|
38
|
+
/**
|
|
39
|
+
* CR-SM-363 (FC-05): die NEBENKOMPONENTEN einer zerfallenen FCHAIN, je mit Ursache —
|
|
40
|
+
* `missing-link` (Haupt- und Nebenteil sind ueber eine FUNC ausserhalb der Kette verbunden;
|
|
41
|
+
* `via` nennt sie) oder `bag` (nur eine gemeinsame Quelle, parallele Dienste). EIN Befund je
|
|
42
|
+
* Kette (CR-SM-242), die Komponenten hier statt als n Befunde.
|
|
43
|
+
*/
|
|
44
|
+
side_components: z.array(z.object({
|
|
45
|
+
members: z.array(z.string()),
|
|
46
|
+
cause: z.enum(['missing-link', 'bag']),
|
|
47
|
+
via: z.array(z.string()),
|
|
48
|
+
})).optional(),
|
|
38
49
|
/**
|
|
39
50
|
* CR-SM-341: der Ablehnungsgrund von R-18, maschinenlesbar statt nur als Satz.
|
|
40
51
|
*
|
|
@@ -242,24 +253,15 @@ function funcMustSatisfyReq(graph) {
|
|
|
242
253
|
.map(st => idx.byId.get(st.target))
|
|
243
254
|
.filter((e) => !!e)
|
|
244
255
|
: [];
|
|
245
|
-
// Also include constraints whose description mentions this func's name
|
|
246
|
-
const namePattern = fn.name.toLowerCase();
|
|
247
|
-
const matchingConstraints = allReqs.filter(r => r.kinds?.includes('non-functional') && r.description.toLowerCase().includes(namePattern));
|
|
248
256
|
// CR-SM-346 (ITEM-2026-299): der Kandidatenkreis wird am META-MODELL gefiltert, nicht
|
|
249
|
-
// an einer zweiten, hier abgeschriebenen kinds-Liste
|
|
250
|
-
//
|
|
251
|
-
//
|
|
252
|
-
// gab korrekt `null` zurueck. 86 Befunde, 0 Empfehlungen: der Befund war unbedienbar.
|
|
257
|
+
// an einer zweiten, hier abgeschriebenen kinds-Liste — `isValidTrace` statt Eigenbau: eine
|
|
258
|
+
// Regel, die Kandidaten anbietet, die ihr eigenes Meta-Modell abweist, erzeugt Waste statt
|
|
259
|
+
// Fuehrung. `FUNC -satisfy-> REQ` laesst nur `functional` zu (CR-SM-366).
|
|
253
260
|
//
|
|
254
|
-
//
|
|
255
|
-
//
|
|
256
|
-
//
|
|
257
|
-
|
|
258
|
-
//
|
|
259
|
-
// `isValidTrace` statt Eigenbau: eine Regel, die Kandidaten anbietet, die ihr eigenes
|
|
260
|
-
// Meta-Modell abweist, erzeugt Waste statt Fuehrung — und zwei Wahrheiten ueber dieselbe
|
|
261
|
-
// Grammatik sind genau der Defekt, den CR-SM-286/-302 beseitigt haben.
|
|
262
|
-
const candidates = [...new Map([...siblingReqs, ...matchingConstraints, ...allReqs].map(e => [e.id, e])).values()]
|
|
261
|
+
// CR-SM-366: der fruehere `matchingConstraints`-Zweig (non-functional-REQ, deren Text den
|
|
262
|
+
// FUNC-Namen nennt) ist entfallen. Er zog genau die Sorte herein, die das Muster abweist;
|
|
263
|
+
// seit ein REQ genau EIN kind traegt, konnte kein Treffer den Filter je passieren.
|
|
264
|
+
const candidates = [...new Map([...siblingReqs, ...allReqs].map(e => [e.id, e])).values()]
|
|
263
265
|
.filter(r => isValidTrace({ source: 'FUNC', target: 'REQ', type: 'satisfy', targetKinds: r.kinds }));
|
|
264
266
|
return {
|
|
265
267
|
rule_id: 'R-02',
|
|
@@ -744,6 +746,12 @@ function decompositionBreadth(graph, policy) {
|
|
|
744
746
|
// deshalb keine Zahl im Kontext und steuert nicht — sie ist Lesbarkeit, nicht Randbreite.
|
|
745
747
|
// Eine Ebene ohne jedes Kind ist nicht ihr Fall: ein leeres MOD meldet R-23, eine FUNC ohne Kinder
|
|
746
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.
|
|
747
755
|
// ---------------------------------------------------------------------------
|
|
748
756
|
function decompositionNarrow(graph, policy) {
|
|
749
757
|
const band = policy.decompositionBreadth;
|
|
@@ -751,7 +759,7 @@ function decompositionNarrow(graph, policy) {
|
|
|
751
759
|
return [];
|
|
752
760
|
const byId = indexOf(graph).byId;
|
|
753
761
|
return breadthCounts(graph)
|
|
754
|
-
.filter(c => c.n < band.min)
|
|
762
|
+
.filter(c => c.n < band.min && byId.get(c.parentId)?.type !== 'SYS')
|
|
755
763
|
.map(c => {
|
|
756
764
|
const parent = byId.get(c.parentId);
|
|
757
765
|
return {
|
|
@@ -827,7 +835,9 @@ function actorMustHaveTrace(graph) {
|
|
|
827
835
|
severity: 'warning',
|
|
828
836
|
element_id: a.id,
|
|
829
837
|
message: `${a.id} has no io traces (disconnected actor)`,
|
|
830
|
-
|
|
838
|
+
// CR-SM-371: ein ACTOR hat genau zwei legale Kanten (ACTOR -io-> FLOW, FLOW -io-> ACTOR);
|
|
839
|
+
// der UC wird ueber den FLOW an einer FUNC seiner Kette erreicht, nie direkt (R-18).
|
|
840
|
+
fix_hint: `Wire the actor through a FLOW via graph_mutate Format-E: \`${a.id} -io-> FLOW-x\` (it sends) or \`FLOW-x -io-> ${a.id}\` (it receives), plus \`FLOW-x -io-> FUNC-y\` or \`FUNC-y -io-> FLOW-x\` for a FUNC in the use case's chain — never an io edge to the UC itself`,
|
|
831
841
|
context: {
|
|
832
842
|
element_type: a.type,
|
|
833
843
|
element_name: a.name,
|
|
@@ -927,7 +937,6 @@ function validTracePattern(graph) {
|
|
|
927
937
|
});
|
|
928
938
|
if (!why)
|
|
929
939
|
return [];
|
|
930
|
-
const { detail, hint } = explainRejection(why, t, src, tgt);
|
|
931
940
|
// CR-SM-341: derselbe Grund, den `detail` in Prosa giesst — hier zusaetzlich als Struktur.
|
|
932
941
|
// Keine zweite Rechnung: `why` liegt vor, die Alternativen befragen `isValidTrace`.
|
|
933
942
|
const shape = {
|
|
@@ -936,6 +945,7 @@ function validTracePattern(graph) {
|
|
|
936
945
|
targetKinds: idx.byId.get(t.target)?.kinds,
|
|
937
946
|
};
|
|
938
947
|
const alt = admittingAlternatives(shape);
|
|
948
|
+
const { detail, hint } = explainRejection(why, t, src, tgt, alt.sources);
|
|
939
949
|
const where = why.reason === 'no-pattern' ? undefined : why.pattern.where;
|
|
940
950
|
return [{
|
|
941
951
|
rule_id: 'R-18',
|
|
@@ -970,7 +980,7 @@ function validTracePattern(graph) {
|
|
|
970
980
|
* `kinds` steht da und passt nicht (Kante heben oder Kind korrigieren). Wer das nicht
|
|
971
981
|
* unterscheiden kann, sucht am falschen Ende.
|
|
972
982
|
*/
|
|
973
|
-
function explainRejection(why, t, src, tgt) {
|
|
983
|
+
function explainRejection(why, t, src, tgt, admittingSources) {
|
|
974
984
|
if (why.reason === 'no-pattern') {
|
|
975
985
|
return {
|
|
976
986
|
detail: `${src} → ${tgt} is not a valid ${t.type} pattern`,
|
|
@@ -991,7 +1001,11 @@ function explainRejection(why, t, src, tgt) {
|
|
|
991
1001
|
}
|
|
992
1002
|
: {
|
|
993
1003
|
detail: `${admits}, but ${end} declares {${[...why.declared].sort().join(', ')}}`,
|
|
994
|
-
|
|
1004
|
+
// CR-SM-366: den Erfueller NENNEN statt "FCHAIN carries every kind" — das galt nie fuer
|
|
1005
|
+
// die Code-Bindung und gilt seit dem FCHAIN-where gar nicht mehr.
|
|
1006
|
+
hint: admittingSources.length > 0
|
|
1007
|
+
? `Correct '${where.field}' on ${end}, or ${t.type} it from ${admittingSources.join(' | ')} — the pattern admitting {${[...why.declared].sort().join(', ')}}`
|
|
1008
|
+
: `Correct '${where.field}' on ${end} to a subset of {${allowed}} — no source type admits {${[...why.declared].sort().join(', ')}} (retired kind? split a mixed REQ)`,
|
|
995
1009
|
};
|
|
996
1010
|
}
|
|
997
1011
|
/**
|
|
@@ -1270,7 +1284,7 @@ function funcMustHaveCodeBinding(graph) {
|
|
|
1270
1284
|
severity: 'warning',
|
|
1271
1285
|
element_id: fn.id,
|
|
1272
1286
|
message: `${fn.id} is not fully realized — unbound compose ${leafList.length === 1 ? 'child' : 'children'}: ${leafList.join(', ')}`,
|
|
1273
|
-
fix_hint: `
|
|
1287
|
+
fix_hint: `Bind the leaves via graph_mutate Format-E (${leafList.map((id) => `\`~ ${id} @realRef {"file":…,"symbol":…}\``).join(', ')}) — the parent inherits their binding`,
|
|
1274
1288
|
context: { element_type: fn.type, element_name: fn.name },
|
|
1275
1289
|
});
|
|
1276
1290
|
}
|
|
@@ -1300,6 +1314,10 @@ function funcMustHaveCodeBinding(graph) {
|
|
|
1300
1314
|
// Co-adjacency at a shared FLOW is not an asserted interface — treating it
|
|
1301
1315
|
// as one produced P·C findings per hub FLOW and made reuse the expensive
|
|
1302
1316
|
// choice. The FCHAIN is the modelled claim; the test is owed on the claim.
|
|
1317
|
+
// CR-SM-376: second way to cover a chain. Since CR-SM-366 an FCHAIN carries
|
|
1318
|
+
// only NFRs; its effect is covered through its members when EVERY member
|
|
1319
|
+
// FUNC (FCHAIN ─compose→ FUNC) satisfies at least one REQ. Verifying those
|
|
1320
|
+
// member REQs is R-01's job, not R-21's.
|
|
1303
1321
|
// ---------------------------------------------------------------------------
|
|
1304
1322
|
function fchainMustHaveIntegrationTest(graph, policy) {
|
|
1305
1323
|
const idx = indexOf(graph);
|
|
@@ -1333,6 +1351,22 @@ function fchainMustHaveIntegrationTest(graph, policy) {
|
|
|
1333
1351
|
testedChains.add(t.source);
|
|
1334
1352
|
}
|
|
1335
1353
|
}
|
|
1354
|
+
// CR-SM-376: der zweite Weg — die Kette ist ueber ihre Glieder gedeckt, wenn JEDE Glied-FUNC
|
|
1355
|
+
// (direkte FCHAIN -compose-> FUNC, dieselbe Definition wie `chainsByFunc`) eine REQ erfuellt.
|
|
1356
|
+
// `unmetMembers` liefert die Glieder ohne REQ; leer = gedeckt. Eine Kette ohne Glied kann hier
|
|
1357
|
+
// nicht auftauchen: ZWEIG 2 hat mindestens die beiden Enden der Uebergabe als Glieder.
|
|
1358
|
+
const funcsWithReq = new Set(idx.tracesOfType('satisfy').filter(t => isFunc(t.source) && typeOf.get(t.target) === 'REQ').map(t => t.source));
|
|
1359
|
+
const membersOf = new Map();
|
|
1360
|
+
for (const [fn, chains] of chainsOf) {
|
|
1361
|
+
for (const ch of chains) {
|
|
1362
|
+
const list = membersOf.get(ch);
|
|
1363
|
+
if (list)
|
|
1364
|
+
list.push(fn);
|
|
1365
|
+
else
|
|
1366
|
+
membersOf.set(ch, [fn]);
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
const unmetMembers = (ch) => (membersOf.get(ch) ?? []).filter(fn => !funcsWithReq.has(fn)).sort();
|
|
1336
1370
|
// CR-SM-313: die Infrastruktur-Ausnahme. Die ZAHL kommt aus der Kennzahl (CR-SM-314), nicht
|
|
1337
1371
|
// aus einer zweiten Rechnung — eine Uebergabe an eine Funktion, die quer durch alle Ketten
|
|
1338
1372
|
// laeuft, ist kein fehlender Integrationstest, sondern ein Bus. `null` = keine Ausnahme.
|
|
@@ -1372,8 +1406,9 @@ function fchainMustHaveIntegrationTest(graph, policy) {
|
|
|
1372
1406
|
});
|
|
1373
1407
|
continue;
|
|
1374
1408
|
}
|
|
1375
|
-
// ZWEIG 2 — gemeinsame Kette, aber keine davon
|
|
1376
|
-
|
|
1409
|
+
// ZWEIG 2 — gemeinsame Kette, aber keine davon gedeckt: Befund an der Kette. Gedeckt heisst
|
|
1410
|
+
// (a) verifizierte Ketten-REQ oder (b, CR-SM-376) jede Glied-FUNC erfuellt eine REQ.
|
|
1411
|
+
if (shared.some(ch => testedChains.has(ch) || unmetMembers(ch).length === 0))
|
|
1377
1412
|
continue;
|
|
1378
1413
|
const anchor = shared[0];
|
|
1379
1414
|
const key = `${anchor}|${p}>${c}`;
|
|
@@ -1385,8 +1420,8 @@ function fchainMustHaveIntegrationTest(graph, policy) {
|
|
|
1385
1420
|
rule_id: 'R-21',
|
|
1386
1421
|
severity: 'warning',
|
|
1387
1422
|
element_id: anchor,
|
|
1388
|
-
message: `${anchor} has no integration test covering connection ${p} → ${c}`,
|
|
1389
|
-
fix_hint: '
|
|
1423
|
+
message: `${anchor} has no integration test covering connection ${p} → ${c}; member FUNCs without a REQ: ${unmetMembers(anchor).join(', ')}`,
|
|
1424
|
+
fix_hint: 'Give each member FUNC of the chain a REQ it satisfies (FUNC -satisfy-> REQ), or attach an end-to-end non-functional REQ to the chain (FCHAIN -satisfy-> REQ) and verify it with an integration TEST',
|
|
1390
1425
|
context: { element_type: anchorEl?.type, element_name: anchorEl?.name },
|
|
1391
1426
|
});
|
|
1392
1427
|
}
|
|
@@ -1650,7 +1685,7 @@ export const V3_RULES = [
|
|
|
1650
1685
|
{ id: 'R-19', name: 'Runnable TEST binding', severity: 'warning', evaluate: testMustHaveRunnableBinding, domain: ['TEST'] },
|
|
1651
1686
|
{ id: 'R-29', name: 'Test file exclusivity', severity: 'error', evaluate: testFileExclusivity, domain: ['TEST'] },
|
|
1652
1687
|
{ id: 'R-20', name: 'FUNC realRef binding', severity: 'warning', evaluate: funcMustHaveCodeBinding, domain: ['FUNC'] },
|
|
1653
|
-
{ id: 'R-21', name: 'FUNC↔FUNC handover needs a shared chain
|
|
1688
|
+
{ id: 'R-21', name: 'FUNC↔FUNC handover needs a shared chain covered by member REQs or an integration test', severity: 'warning', evaluate: fchainMustHaveIntegrationTest, domain: ['FCHAIN', 'FUNC'] },
|
|
1654
1689
|
{ id: 'R-22', name: 'FUNC must be allocated to MOD', severity: 'warning', evaluate: funcMustBeAllocated, domain: ['FUNC'] },
|
|
1655
1690
|
{ id: 'R-23', name: 'MOD must have allocated FUNC', severity: 'warning', evaluate: modMustHaveAllocatedFunc, domain: ['MOD'] },
|
|
1656
1691
|
{ id: 'R-26', name: 'SCHEMA must have realRef', severity: 'warning', evaluate: schemaMustHaveSchemaRef, domain: ['SCHEMA'] },
|
|
@@ -1658,7 +1693,7 @@ export const V3_RULES = [
|
|
|
1658
1693
|
{ id: 'RD-01', name: 'Unresolved requirement', severity: 'warning', evaluate: unresolvedRequirement, domain: ['REQ'] },
|
|
1659
1694
|
{ id: 'RD-02', name: 'Decomposition consistency', severity: 'warning', evaluate: decompositionConsistency, domain: ['REQ'] },
|
|
1660
1695
|
{ id: 'RD-04', name: 'Decomposition breadth', severity: 'warning', evaluate: decompositionBreadth, domain: ['FUNC', 'MOD', 'SYS'] },
|
|
1661
|
-
{ id: 'RD-05', name: 'Decomposition too narrow', severity: 'warning', evaluate: decompositionNarrow, domain: ['FUNC', 'MOD'
|
|
1696
|
+
{ id: 'RD-05', name: 'Decomposition too narrow', severity: 'warning', evaluate: decompositionNarrow, domain: ['FUNC', 'MOD'] },
|
|
1662
1697
|
{ id: 'MS-01', name: 'Milestone empty scope', severity: 'warning', evaluate: msEmptyScope, domain: ['MS'] },
|
|
1663
1698
|
{ id: 'MS-02', name: 'Milestone dangling dependency', severity: 'warning', evaluate: msDanglingDependency, domain: ['MS'] },
|
|
1664
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 ||
|
|
103
|
+
return d.length < UC_DESC_MIN_LEN || hasPlaceholder(d);
|
|
104
104
|
})
|
|
105
105
|
.map(uc => ({
|
|
106
106
|
rule_id: 'UC-04',
|