@sigloch/contracts 6.3.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.
- package/dist/se/ao-rules.d.ts +12 -12
- package/dist/se/ao-rules.js +88 -225
- package/dist/se/conformance-rules.d.ts +28 -0
- package/dist/se/conformance-rules.js +40 -17
- package/dist/se/cr-quality-rules.js +75 -58
- package/dist/se/evaluate-all.d.ts +4 -2
- package/dist/se/evaluate-all.js +40 -24
- package/dist/se/fchain-quality-rules.d.ts +0 -1
- package/dist/se/fchain-quality-rules.js +0 -44
- package/dist/se/flat-graph.d.ts +9 -1
- package/dist/se/flat-graph.js +15 -6
- package/dist/se/format-e-parser.d.ts +14 -2
- package/dist/se/format-e-parser.js +33 -9
- package/dist/se/grammar-snapshot.d.ts +10 -7
- package/dist/se/grammar-snapshot.js +172 -27
- package/dist/se/index.d.ts +4 -3
- package/dist/se/index.js +4 -3
- package/dist/se/meta-model.d.ts +99 -7
- package/dist/se/meta-model.js +155 -14
- package/dist/se/metric-rules.d.ts +20 -1
- package/dist/se/metric-rules.js +97 -58
- package/dist/se/module-crossings.d.ts +102 -0
- package/dist/se/module-crossings.js +196 -0
- package/dist/se/near-duplicate-rules.d.ts +16 -24
- package/dist/se/near-duplicate-rules.js +21 -92
- package/dist/se/ontology.d.ts +32 -47
- package/dist/se/ontology.js +56 -13
- package/dist/se/policy.d.ts +6 -0
- package/dist/se/policy.js +53 -2
- package/dist/se/quality-rules.d.ts +18 -0
- package/dist/se/quality-rules.js +68 -12
- package/dist/se/readiness.d.ts +7 -5
- package/dist/se/readiness.js +40 -22
- package/dist/se/rules.d.ts +6 -7
- package/dist/se/rules.js +358 -128
- package/dist/se/schema-quality-rules.d.ts +0 -1
- package/dist/se/schema-quality-rules.js +7 -28
- package/dist/se/similarity.d.ts +61 -0
- package/dist/se/similarity.js +116 -0
- package/dist/se/uc-quality-rules.js +31 -12
- package/package.json +3 -2
|
@@ -16,6 +16,5 @@ import type { OntologyGraph } from './ontology.js';
|
|
|
16
16
|
import type { RuleDefinition, RuleViolation } from './rules.js';
|
|
17
17
|
import type { MetricPolicy } from './policy.js';
|
|
18
18
|
export declare function sc02IsReferenced(graph: OntologyGraph): RuleViolation[];
|
|
19
|
-
export declare function sc04FlowHasSchema(graph: OntologyGraph): RuleViolation[];
|
|
20
19
|
export declare const SC_RULES: RuleDefinition[];
|
|
21
20
|
export declare function evaluateSCRules(graph: OntologyGraph, policy: MetricPolicy): RuleViolation[];
|
|
@@ -20,40 +20,19 @@ export function sc02IsReferenced(graph) {
|
|
|
20
20
|
}));
|
|
21
21
|
}
|
|
22
22
|
// ---------------------------------------------------------------------------
|
|
23
|
-
// SC-04
|
|
24
|
-
//
|
|
25
|
-
//
|
|
26
|
-
//
|
|
27
|
-
// rule.
|
|
23
|
+
// SC-04 (FLOW must reference a SCHEMA, CR-SM-226) ist mit CR-SM-271 Teil 2 ersatzlos
|
|
24
|
+
// entfallen: die Untergrenze ist Grammatik geworden (`FLOW -relation-> SCHEMA [1..1]`,
|
|
25
|
+
// meta-model.ts REQUIRED_PATTERNS) und meldet als error aus dem R-18-Kardinalitaets-Bein
|
|
26
|
+
// (rules.ts cardinalityViolations) — Grammatik ersetzt Regel, kein Regel-Loch.
|
|
28
27
|
// ---------------------------------------------------------------------------
|
|
29
|
-
export function sc04FlowHasSchema(graph) {
|
|
30
|
-
const schemas = graph.elements.filter(e => e.type === 'SCHEMA');
|
|
31
|
-
return graph.elements
|
|
32
|
-
.filter(e => e.type === 'FLOW')
|
|
33
|
-
.filter(f => !graph.traces.some(t => t.source === f.id && t.type === 'relation' &&
|
|
34
|
-
graph.elements.some(e => e.id === t.target && e.type === 'SCHEMA')))
|
|
35
|
-
.map(f => ({
|
|
36
|
-
rule_id: 'SC-04',
|
|
37
|
-
severity: 'warning',
|
|
38
|
-
element_id: f.id,
|
|
39
|
-
message: `${f.id} has no SCHEMA binding`,
|
|
40
|
-
fix_hint: 'Link a SCHEMA via relation trace to define this FLOW\'s data contract',
|
|
41
|
-
context: {
|
|
42
|
-
element_type: f.type,
|
|
43
|
-
element_name: f.name,
|
|
44
|
-
candidate_targets: schemas.map(s => ({ id: s.id, type: s.type, name: s.name })),
|
|
45
|
-
},
|
|
46
|
-
}));
|
|
47
|
-
}
|
|
48
28
|
// ---------------------------------------------------------------------------
|
|
49
29
|
// Aggregated array & convenience runner
|
|
50
30
|
// ---------------------------------------------------------------------------
|
|
51
31
|
export const SC_RULES = [
|
|
52
|
-
// Die Asymmetrie ist gewollt (CR-SM-242): SC-02 iteriert ueber SCHEMA
|
|
53
|
-
// benutzt")
|
|
54
|
-
//
|
|
32
|
+
// Die Asymmetrie zur FLOW-Seite ist gewollt (CR-SM-242): SC-02 iteriert ueber SCHEMA
|
|
33
|
+
// („wird dieses SCHEMA benutzt"); die Gegenrichtung je FLOW („hat dieser FLOW einen
|
|
34
|
+
// Datenvertrag") traegt seit CR-SM-271 das R-18-Untergrenzen-Bein.
|
|
55
35
|
{ id: 'SC-02', name: 'Schema referenced by FLOW', severity: 'warning', evaluate: sc02IsReferenced, domain: ['SCHEMA'] },
|
|
56
|
-
{ id: 'SC-04', name: 'FLOW has SCHEMA binding', severity: 'warning', evaluate: sc04FlowHasSchema, domain: ['FLOW'] },
|
|
57
36
|
];
|
|
58
37
|
// CR-SM-236: `policy` wird durchgereicht, auch wo diese Familie heute keine Schwelle hat —
|
|
59
38
|
// ein Sonderweg je Familie waere genau der zweite Pfad, den der Regelsatz verbietet.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CR-SM-286 — die Ähnlichkeitsrechnung von ND-01/ND-02/BQ-04, dort wo die Regeln stehen.
|
|
3
|
+
*
|
|
4
|
+
* ## Warum sie hierher gehört
|
|
5
|
+
*
|
|
6
|
+
* Bis hierher lebte die Formel als **Prosa** in `near-duplicate-rules.ts` und als **Code** in
|
|
7
|
+
* `graphcode/src/kernel/measure/nd-similarity.ts`, verbunden durch
|
|
8
|
+
* `setND01SimilarityMatrix()` / `setND02SimilarityMatrix()`. Zwei Pakete mussten sich über
|
|
9
|
+
* etwas einig sein, das nur eines von beiden ausführt.
|
|
10
|
+
*
|
|
11
|
+
* Die Naht kostete vier Dinge, alle gemessen:
|
|
12
|
+
*
|
|
13
|
+
* 1. **Fail-open.** Ohne Injektion gaben ND-01/ND-02 `[]` zurück — bei severity `error` und
|
|
14
|
+
* ununterscheidbar von „keine Duplikate". Gemessen: moneyflow trägt 16 ND-01-Befunde, die
|
|
15
|
+
* für JEDEN Konsumenten ausserhalb graphcodes unsichtbar waren (`report:silence`, die
|
|
16
|
+
* Spike-Skripte, jedes künftige Werkzeug). Die Familie hat für „kann nicht urteilen" eine
|
|
17
|
+
* Konvention — `policy.X = null`, `moduleMetrics.instability = null`, beide sagen es
|
|
18
|
+
* ausdrücklich; ND fiel statt dessen nach grün.
|
|
19
|
+
* 2. **Prozessweiter Modulzustand.** `let _nd01Matrix` ist global. graphcode schützte sich mit
|
|
20
|
+
* der Klammer `withNDMatrices(og, run)` und einem `finally`-Reset; jeder andere Aufrufer
|
|
21
|
+
* nicht. Zwei Graphen nacheinander bewertet — und Graph A urteilt über Graph B.
|
|
22
|
+
* 3. **AO-D01 verstärkte es.** Die Regel liegt IM Gate-Katalog und übersprang ihre
|
|
23
|
+
* Overlap-Prüfung, wenn keine Matrix da war („no matrix → assume pass"): eine Gate-Regel,
|
|
24
|
+
* die sich stillschweigend abschwächt.
|
|
25
|
+
* 4. **BQ-04 ist am selben Muster komplett tot** — `setBQ04SimilarityMatrix()` ruft im ganzen
|
|
26
|
+
* Familienbaum niemand (CR-SM-278, heute erneut geprüft).
|
|
27
|
+
*
|
|
28
|
+
* Präzedenz aus demselben Repo: CR-SM-276 hat `moduleCrossings` genau deshalb NACH contracts
|
|
29
|
+
* gelegt — damit zwei Regelköpfe (CR-01, R-04) EINE Definition von „Kreuzung" teilen. Hier ist
|
|
30
|
+
* es dieselbe Bewegung für drei Regelköpfe und eine Definition von „ähnlich".
|
|
31
|
+
*
|
|
32
|
+
* ## Was sich NICHT ändert
|
|
33
|
+
*
|
|
34
|
+
* Formeln, Gewichte und die Schwelle 0,85 sind zeichengleich aus graphcodes Implementierung
|
|
35
|
+
* übernommen. Das ist kein neues Urteil, es ist derselbe Code an der richtigen Stelle.
|
|
36
|
+
*
|
|
37
|
+
* Rein und deterministisch: kein Store, kein Transport, kein Filesystem, kein Zufall, keine
|
|
38
|
+
* Abhängigkeit ausserhalb der Ontologie. Ergebnisse hängen je Graph-Objekt im WeakMap-Cache,
|
|
39
|
+
* wie `moduleCrossings` — die Matrix ist O(n²) und wird je Voll-Lauf mehrfach gebraucht.
|
|
40
|
+
*/
|
|
41
|
+
import type { OntologyGraph } from './ontology.js';
|
|
42
|
+
/** Wort-Token >= 3 Zeichen, lowercase, Unicode-Buchstaben/Ziffern. */
|
|
43
|
+
export declare function tokens(s: unknown): Set<string>;
|
|
44
|
+
/** Jaccard-Aehnlichkeit; leer/leer = 1 (identisch leer). */
|
|
45
|
+
export declare function jaccard(a: ReadonlySet<string>, b: ReadonlySet<string>): number;
|
|
46
|
+
export interface SimilarityMatrix {
|
|
47
|
+
/** Element-uids in kanonischer Ordnung — der Index in `matrix`. */
|
|
48
|
+
readonly ids: readonly string[];
|
|
49
|
+
/** Symmetrisch, Diagonale 1. */
|
|
50
|
+
readonly matrix: readonly (readonly number[])[];
|
|
51
|
+
}
|
|
52
|
+
/** FUNC-Aehnlichkeit je Graph (ND-01). */
|
|
53
|
+
export declare const funcSimilarity: (graph: OntologyGraph) => SimilarityMatrix;
|
|
54
|
+
/** SCHEMA-Aehnlichkeit je Graph (ND-02, AO-D01). */
|
|
55
|
+
export declare const schemaSimilarity: (graph: OntologyGraph) => SimilarityMatrix;
|
|
56
|
+
/** Paare ueber der Schwelle, kanonisch geordnet (i < j) — die eine Schleife fuer alle drei Regeln. */
|
|
57
|
+
export declare function pairsAbove(sim: SimilarityMatrix, threshold: number): Array<{
|
|
58
|
+
a: string;
|
|
59
|
+
b: string;
|
|
60
|
+
similarity: number;
|
|
61
|
+
}>;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { isValidTrace } from './meta-model.js';
|
|
2
|
+
/** Wort-Token >= 3 Zeichen, lowercase, Unicode-Buchstaben/Ziffern. */
|
|
3
|
+
export function tokens(s) {
|
|
4
|
+
const text = typeof s === 'string' ? s : '';
|
|
5
|
+
return new Set(text
|
|
6
|
+
.toLowerCase()
|
|
7
|
+
.split(/[^\p{L}\p{N}]+/u)
|
|
8
|
+
.filter(w => w.length >= 3));
|
|
9
|
+
}
|
|
10
|
+
/** Jaccard-Aehnlichkeit; leer/leer = 1 (identisch leer). */
|
|
11
|
+
export function jaccard(a, b) {
|
|
12
|
+
if (a.size === 0 && b.size === 0)
|
|
13
|
+
return 1;
|
|
14
|
+
let inter = 0;
|
|
15
|
+
for (const x of a)
|
|
16
|
+
if (b.has(x))
|
|
17
|
+
inter += 1;
|
|
18
|
+
const union = a.size + b.size - inter;
|
|
19
|
+
return union === 0 ? 1 : inter / union;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Partner-uids eines Elements ueber LEGALE Traces der genannten Typen.
|
|
23
|
+
*
|
|
24
|
+
* CR-SM-286: der Legalitaetsfilter ist nicht Kosmetik. Ohne ihn aenderte eine grammatikwidrige
|
|
25
|
+
* Kante das Urteil: `se-rule-pair-legality.test.ts` (Pruefung A, CR-SM-278) meldete **35**
|
|
26
|
+
* Faelle, in denen ND-02 erst durch eine injizierte illegale Kante auf SCHEMA anschlug —
|
|
27
|
+
* `ACTOR -compose-> SCHEMA`, `CR -io-> SCHEMA`, `FLOW -compose-> SCHEMA` und so fort. Eine
|
|
28
|
+
* Regel, deren Verdikt an einer Kante haengt, die R-18 als `error` ablehnt, urteilt ueber einen
|
|
29
|
+
* Graphen, den es nicht geben darf.
|
|
30
|
+
*/
|
|
31
|
+
function partners(graph, id, traceTypes) {
|
|
32
|
+
const byType = new Map(graph.elements.map(e => [e.id, e.type]));
|
|
33
|
+
const out = new Set();
|
|
34
|
+
for (const t of graph.traces) {
|
|
35
|
+
if (!traceTypes.includes(t.type))
|
|
36
|
+
continue;
|
|
37
|
+
if (t.source !== id && t.target !== id)
|
|
38
|
+
continue;
|
|
39
|
+
const src = byType.get(t.source);
|
|
40
|
+
const tgt = byType.get(t.target);
|
|
41
|
+
if (!src || !tgt)
|
|
42
|
+
continue; // haengende Kante -> R-08, nicht hier
|
|
43
|
+
if (!isValidTrace({ source: src, target: tgt, type: t.type }))
|
|
44
|
+
continue;
|
|
45
|
+
out.add(t.source === id ? t.target : t.source);
|
|
46
|
+
}
|
|
47
|
+
return out;
|
|
48
|
+
}
|
|
49
|
+
/** Erstes Wort des Namens als Verb-Naeherung (FUNC-Namen: Verb-Objekt). */
|
|
50
|
+
function firstWord(name) {
|
|
51
|
+
const text = typeof name === 'string' ? name : '';
|
|
52
|
+
return text.toLowerCase().split(/[^\p{L}\p{N}]+/u).find(w => w.length > 0) ?? '';
|
|
53
|
+
}
|
|
54
|
+
const byId = (a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0);
|
|
55
|
+
/** Feld-Menge eines SCHEMA: `attributes.fields` (Array), sonst Beschreibungs-Token. */
|
|
56
|
+
function schemaFields(el) {
|
|
57
|
+
const fields = el.attributes?.fields;
|
|
58
|
+
if (Array.isArray(fields) && fields.length > 0) {
|
|
59
|
+
return new Set(fields.map(f => String(typeof f === 'object' && f !== null ? (f.name ?? JSON.stringify(f)) : f).toLowerCase()));
|
|
60
|
+
}
|
|
61
|
+
return tokens(el.description);
|
|
62
|
+
}
|
|
63
|
+
const CACHE = new WeakMap();
|
|
64
|
+
function build(graph) {
|
|
65
|
+
// ND-01: 0.35*descr_jaccard + 0.25*verb_match + 0.25*io_topology + 0.15*req_overlap
|
|
66
|
+
const funcs = graph.elements.filter(e => e.type === 'FUNC').sort(byId);
|
|
67
|
+
const fDescr = funcs.map(f => tokens(f.description));
|
|
68
|
+
const fVerb = funcs.map(f => firstWord(f.name));
|
|
69
|
+
const fIo = funcs.map(f => partners(graph, f.id, ['io']));
|
|
70
|
+
const fReq = funcs.map(f => new Set(graph.traces.filter(t => t.type === 'satisfy' && t.source === f.id).map(t => t.target)));
|
|
71
|
+
const func = {
|
|
72
|
+
ids: funcs.map(f => f.id),
|
|
73
|
+
matrix: funcs.map((_, i) => funcs.map((_, j) => i === j ? 1
|
|
74
|
+
: 0.35 * jaccard(fDescr[i], fDescr[j])
|
|
75
|
+
+ 0.25 * (fVerb[i] !== '' && fVerb[i] === fVerb[j] ? 1 : 0)
|
|
76
|
+
+ 0.25 * jaccard(fIo[i], fIo[j])
|
|
77
|
+
+ 0.15 * jaccard(fReq[i], fReq[j]))),
|
|
78
|
+
};
|
|
79
|
+
// ND-02: 0.50*field_jaccard + 0.30*descr_jaccard + 0.20*usage_overlap
|
|
80
|
+
const schemas = graph.elements.filter(e => e.type === 'SCHEMA').sort(byId);
|
|
81
|
+
const sFields = schemas.map(schemaFields);
|
|
82
|
+
const sDescr = schemas.map(s => tokens(s.description));
|
|
83
|
+
const sUse = schemas.map(s => partners(graph, s.id, ['relation', 'io', 'compose']));
|
|
84
|
+
const schema = {
|
|
85
|
+
ids: schemas.map(s => s.id),
|
|
86
|
+
matrix: schemas.map((_, i) => schemas.map((_, j) => i === j ? 1
|
|
87
|
+
: 0.5 * jaccard(sFields[i], sFields[j])
|
|
88
|
+
+ 0.3 * jaccard(sDescr[i], sDescr[j])
|
|
89
|
+
+ 0.2 * jaccard(sUse[i], sUse[j]))),
|
|
90
|
+
};
|
|
91
|
+
return { func, schema };
|
|
92
|
+
}
|
|
93
|
+
function similarities(graph) {
|
|
94
|
+
let s = CACHE.get(graph);
|
|
95
|
+
if (s === undefined) {
|
|
96
|
+
s = build(graph);
|
|
97
|
+
CACHE.set(graph, s);
|
|
98
|
+
}
|
|
99
|
+
return s;
|
|
100
|
+
}
|
|
101
|
+
/** FUNC-Aehnlichkeit je Graph (ND-01). */
|
|
102
|
+
export const funcSimilarity = (graph) => similarities(graph).func;
|
|
103
|
+
/** SCHEMA-Aehnlichkeit je Graph (ND-02, AO-D01). */
|
|
104
|
+
export const schemaSimilarity = (graph) => similarities(graph).schema;
|
|
105
|
+
/** Paare ueber der Schwelle, kanonisch geordnet (i < j) — die eine Schleife fuer alle drei Regeln. */
|
|
106
|
+
export function pairsAbove(sim, threshold) {
|
|
107
|
+
const out = [];
|
|
108
|
+
for (let i = 0; i < sim.ids.length; i++) {
|
|
109
|
+
for (let j = i + 1; j < sim.ids.length; j++) {
|
|
110
|
+
const s = sim.matrix[i][j];
|
|
111
|
+
if (s >= threshold)
|
|
112
|
+
out.push({ a: sim.ids[i], b: sim.ids[j], similarity: s });
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return out;
|
|
116
|
+
}
|
|
@@ -32,26 +32,45 @@ export function uc02HasActor(graph) {
|
|
|
32
32
|
// ueber Elemente und Traces), dann je UC nur noch Mitgliedschaft pruefen. Dieselbe Aussage,
|
|
33
33
|
// dieselben Befunde in derselben Reihenfolge — die Iteration ueber `graph.elements` bleibt
|
|
34
34
|
// die aeussere Schleife.
|
|
35
|
+
// CR-SM-266 D1/D4: die Regel prueft ERREICHBARKEIT statt Adjazenz.
|
|
36
|
+
//
|
|
37
|
+
// Bis hierher suchte sie einen io-Ziel-Treffer direkt am UC oder an dessen compose-Kind —
|
|
38
|
+
// also genau die beiden Kanten `ACTOR -io-> UC` und (ueber die FCHAIN) den Direktweg. Beide
|
|
39
|
+
// Patterns sind mit D1/D4 entfallen; die Regel waere damit UNERFUELLBAR geworden: sie haette
|
|
40
|
+
// einen Zustand verlangt, den R-18 im selben Lauf als ungueltige Kante ablehnt — der
|
|
41
|
+
// gegenlaeufige Fall aus Gate 4 des Grammatik-Reviews, und fuer den Nutzer eine Sackgasse
|
|
42
|
+
// statt eines Fehlers.
|
|
43
|
+
//
|
|
44
|
+
// Der tragende Pfad ist jetzt der einzige: `ACTOR -io-> FLOW -io-> FUNC`, FUNC in einer
|
|
45
|
+
// FCHAIN des UC. Zwei Sprungweiten (Actor→FLOW, FLOW→FUNC), danach Mengen-Mitgliedschaft —
|
|
46
|
+
// die CR-SM-261-Optimierung bleibt damit erhalten (kein Scan je UC, alles ueber den Index
|
|
47
|
+
// aus CR-SM-264), und die Iteration ueber `elementsOfType('UC')` bleibt die aeussere
|
|
48
|
+
// Schleife, also ist die Befundreihenfolge weiter die Graph-Reihenfolge (Gate 5).
|
|
35
49
|
const idx = indexOf(graph);
|
|
36
50
|
const actorIds = idx.idsOfType('ACTOR');
|
|
37
|
-
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
51
|
+
// Schritt 1: die FLOWs, die ein ACTOR speist.
|
|
52
|
+
const actorFlows = new Set(idx.tracesOfType('io').filter(t => actorIds.has(t.source)).map(t => t.target));
|
|
53
|
+
// Schritt 2: die FUNCs, die an einem solchen FLOW haengen — in BEIDE Richtungen. Ein UC ist
|
|
54
|
+
// auch dann an einen Actor angebunden, wenn die Kette IHM etwas liefert (FUNC -io-> FLOW
|
|
55
|
+
// -io-> ACTOR), nicht nur wenn sie von ihm getriggert wird. Die alte Fassung sah nur die
|
|
56
|
+
// Trigger-Richtung, weil `ACTOR -io-> UC` nur so herum existierte.
|
|
57
|
+
const actorFacingFuncs = new Set();
|
|
58
|
+
for (const t of idx.tracesOfType('io')) {
|
|
59
|
+
if (actorFlows.has(t.source))
|
|
60
|
+
actorFacingFuncs.add(t.target);
|
|
61
|
+
if (actorIds.has(t.target) && idx.typeOf(t.source) === 'FLOW') {
|
|
62
|
+
for (const producer of idx.in(t.source, 'io'))
|
|
63
|
+
actorFacingFuncs.add(producer.source);
|
|
64
|
+
}
|
|
45
65
|
}
|
|
46
66
|
return idx.elementsOfType('UC')
|
|
47
|
-
.filter(uc => !
|
|
48
|
-
!(composeTargets.get(uc.id) ?? []).some(target => actorIoTargets.has(target)))
|
|
67
|
+
.filter(uc => !idx.out(uc.id, 'compose').some(chain => idx.out(chain.target, 'compose').some(member => actorFacingFuncs.has(member.target))))
|
|
49
68
|
.map(uc => ({
|
|
50
69
|
rule_id: 'UC-02',
|
|
51
70
|
severity: 'error',
|
|
52
71
|
element_id: uc.id,
|
|
53
|
-
message: `${uc.id}
|
|
54
|
-
fix_hint: '
|
|
72
|
+
message: `${uc.id} is not reachable from any ACTOR`,
|
|
73
|
+
fix_hint: 'Wire an ACTOR to a FLOW that feeds (or is fed by) a FUNC in one of this UC\'s function chains',
|
|
55
74
|
context: { element_type: uc.type, element_name: uc.name },
|
|
56
75
|
}));
|
|
57
76
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sigloch/contracts",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "10.0.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -29,7 +29,8 @@
|
|
|
29
29
|
"check:grammar": "node scripts/check-grammar-version.mjs",
|
|
30
30
|
"grammar:snapshot": "SE_WRITE_GRAMMAR_SNAPSHOT=1 vitest run tests/unit/se-grammar-invariant.test.ts",
|
|
31
31
|
"prepublishOnly": "npm run build && npm run test",
|
|
32
|
-
"grammar:measure": "SE_MEASURE=1 vitest run tests/unit/se-grammar-measure.test.ts"
|
|
32
|
+
"grammar:measure": "SE_MEASURE=1 vitest run tests/unit/se-grammar-measure.test.ts",
|
|
33
|
+
"report:silence": "node scripts/rules-silence-report.mjs"
|
|
33
34
|
},
|
|
34
35
|
"dependencies": {
|
|
35
36
|
"zod": "^4.3.6"
|