@sigloch/contracts 3.3.0 → 4.1.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/harness/index.d.ts +2 -2
- package/dist/se/action-priority.d.ts +101 -0
- package/dist/se/action-priority.js +124 -0
- package/dist/se/analysis-freshness-rules.d.ts +5 -0
- package/dist/se/analysis-freshness-rules.js +5 -5
- package/dist/se/ao-rules.d.ts +5 -39
- package/dist/se/ao-rules.js +20 -13
- package/dist/se/conformance-rules.d.ts +2 -2
- package/dist/se/conformance-rules.js +36 -30
- package/dist/se/cr-quality-rules.d.ts +2 -1
- package/dist/se/cr-quality-rules.js +9 -7
- package/dist/se/evaluate-all.d.ts +18 -3
- package/dist/se/evaluate-all.js +29 -26
- package/dist/se/fchain-quality-rules.d.ts +2 -1
- package/dist/se/fchain-quality-rules.js +8 -6
- package/dist/se/fmea-rules.d.ts +3 -11
- package/dist/se/fmea-rules.js +53 -16
- package/dist/se/format-e-parser.d.ts +14 -1
- package/dist/se/format-e-parser.js +8 -3
- package/dist/se/index.d.ts +4 -2
- package/dist/se/index.js +4 -2
- package/dist/se/metric-rules.d.ts +13 -5
- package/dist/se/metric-rules.js +23 -13
- package/dist/se/near-duplicate-rules.d.ts +2 -0
- package/dist/se/near-duplicate-rules.js +2 -2
- package/dist/se/ontology.d.ts +55 -4
- package/dist/se/ontology.js +46 -6
- package/dist/se/policy.d.ts +65 -0
- package/dist/se/policy.js +100 -0
- package/dist/se/quality-rules.d.ts +2 -1
- package/dist/se/quality-rules.js +9 -7
- package/dist/se/readiness.d.ts +9 -1
- package/dist/se/readiness.js +18 -2
- package/dist/se/rules.d.ts +37 -5
- package/dist/se/rules.js +112 -47
- package/dist/se/schema-quality-rules.d.ts +2 -1
- package/dist/se/schema-quality-rules.js +6 -4
- package/dist/se/uc-quality-rules.d.ts +2 -1
- package/dist/se/uc-quality-rules.js +10 -8
- package/dist/se/view-rules.d.ts +2 -6
- package/dist/se/view-rules.js +40 -13
- package/package.json +2 -1
package/dist/se/rules.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* @sigloch/contracts/se — single source of truth for SE validation rules.
|
|
5
5
|
*/
|
|
6
6
|
import { z } from 'zod/v4';
|
|
7
|
-
import { ElementType, TraceType,
|
|
7
|
+
import { ElementType, TraceType, TestRefsSchema, RealRefSchema } from './ontology.js';
|
|
8
8
|
import { isValidTrace } from './meta-model.js';
|
|
9
9
|
export const RuleSeverity = z.enum(['error', 'warning', 'info']);
|
|
10
10
|
/** Candidate target for resolving a violation (e.g. a REQ to satisfy, a TEST to link). */
|
|
@@ -168,16 +168,26 @@ function asilIsolation(graph) {
|
|
|
168
168
|
return violations;
|
|
169
169
|
}
|
|
170
170
|
// ---------------------------------------------------------------------------
|
|
171
|
-
// R-04:
|
|
171
|
+
// R-04: Modulgroesse GEGEN Kreuzungen (nicht „max module size")
|
|
172
|
+
//
|
|
173
|
+
// CR-SM-236: die Regel waegt Groesse gegen kreuzende io-Flows ab — ein grosses Modul ohne
|
|
174
|
+
// Kreuzungen ist info („kohaesiv, nur gross"), dasselbe Modul mit Kreuzungen ist warning.
|
|
175
|
+
// Der alte Name gab das nicht her, und die drei Schwellen (8/12/2) standen inline: eine
|
|
176
|
+
// Urteilsschwelle, die sich weder per grep noch aus dem Regelnamen ablesen laesst, ist nicht
|
|
177
|
+
// ueberpruefbar. Jetzt sind es `policy.moduleSize.{coupled,large,crossings}`.
|
|
178
|
+
// `null` → messen, nicht urteilen: die Regel schweigt.
|
|
172
179
|
// ---------------------------------------------------------------------------
|
|
173
|
-
function maxModuleSize(graph) {
|
|
174
|
-
const modules = graph.elements.filter(e => e.type === 'MOD');
|
|
180
|
+
function maxModuleSize(graph, policy) {
|
|
175
181
|
const violations = [];
|
|
182
|
+
const steps = policy.moduleSize;
|
|
183
|
+
if (steps === null)
|
|
184
|
+
return violations;
|
|
185
|
+
const modules = graph.elements.filter(e => e.type === 'MOD');
|
|
176
186
|
for (const mod of modules) {
|
|
177
187
|
const allocatedIds = graph.traces.filter(t => t.target === mod.id && t.type === 'allocate').map(t => t.source);
|
|
178
188
|
const allocated = allocatedIds.map(id => graph.elements.find(e => e.id === id)).filter((e) => !!e);
|
|
179
189
|
const funcCount = allocated.length;
|
|
180
|
-
if (funcCount <=
|
|
190
|
+
if (funcCount <= steps.coupled)
|
|
181
191
|
continue;
|
|
182
192
|
// Count crossing flows: io paths from FUNCs in this module to FUNCs in other modules
|
|
183
193
|
const funcIds = new Set(allocatedIds);
|
|
@@ -188,27 +198,27 @@ function maxModuleSize(graph) {
|
|
|
188
198
|
const tgtIn = funcIds.has(t.target);
|
|
189
199
|
return (srcIn && !tgtIn) || (!srcIn && tgtIn);
|
|
190
200
|
}).length;
|
|
191
|
-
if (funcCount >
|
|
201
|
+
if (funcCount > steps.large && crossings === 0) {
|
|
192
202
|
violations.push({
|
|
193
203
|
rule_id: 'R-04',
|
|
194
204
|
severity: 'info',
|
|
195
205
|
element_id: mod.id,
|
|
196
206
|
message: `${mod.id} has ${funcCount} functions but 0 crossings (cohesive, just large)`,
|
|
197
|
-
fix_hint:
|
|
207
|
+
fix_hint: `Size alone is not the finding: > ${steps.large} functions without crossing flows is cohesive. Split only if crossings appear`,
|
|
198
208
|
context: { element_type: mod.type, element_name: mod.name, candidate_targets: toCandidates(allocated) },
|
|
199
209
|
});
|
|
200
210
|
}
|
|
201
|
-
else if (funcCount >
|
|
211
|
+
else if (funcCount > steps.large && crossings > 0) {
|
|
202
212
|
violations.push({
|
|
203
213
|
rule_id: 'R-04',
|
|
204
214
|
severity: 'warning',
|
|
205
215
|
element_id: mod.id,
|
|
206
216
|
message: `${mod.id} has ${funcCount} functions and ${crossings} crossing flows (split recommended)`,
|
|
207
|
-
fix_hint:
|
|
217
|
+
fix_hint: `Split module into smaller units to reduce coupling (> ${steps.large} functions AND crossing flows)`,
|
|
208
218
|
context: { element_type: mod.type, element_name: mod.name, candidate_targets: toCandidates(allocated) },
|
|
209
219
|
});
|
|
210
220
|
}
|
|
211
|
-
else if (funcCount >
|
|
221
|
+
else if (funcCount > steps.coupled && crossings > steps.crossings) {
|
|
212
222
|
violations.push({
|
|
213
223
|
rule_id: 'R-04',
|
|
214
224
|
severity: 'warning',
|
|
@@ -439,7 +449,7 @@ function decompositionBreadth(graph) {
|
|
|
439
449
|
const byId = new Map(graph.elements.map(e => [e.id, e]));
|
|
440
450
|
const counts = new Map();
|
|
441
451
|
const bump = (parentId, kind) => {
|
|
442
|
-
const key = `${parentId}
|
|
452
|
+
const key = `${parentId}\u0000${kind}`;
|
|
443
453
|
const entry = counts.get(key);
|
|
444
454
|
if (entry)
|
|
445
455
|
entry.n++;
|
|
@@ -621,11 +631,11 @@ function validTracePattern(graph) {
|
|
|
621
631
|
}
|
|
622
632
|
// ---------------------------------------------------------------------------
|
|
623
633
|
// R-19: Runnable TEST binding (CR-GC-205 Item 4) — a TEST that is not explicitly
|
|
624
|
-
// concept-only (attributes.concept === true) should carry a valid
|
|
634
|
+
// concept-only (attributes.concept === true) should carry a valid testRefs runnable
|
|
625
635
|
// binding so the deduced selective test set is trustworthy. WARNING (a completeness
|
|
626
636
|
// signal like R-05, not a hard gate): a freshly-spec'd TEST is legitimately concept-
|
|
627
637
|
// level until implemented, and the no-phantom-file guarantee is enforced separately
|
|
628
|
-
// by the consumer's export materialization (every
|
|
638
|
+
// by the consumer's export materialization (every testRefs entry file is scaffolded). File
|
|
629
639
|
// EXISTENCE is out of scope here (rules are pure, no I/O); this guards presence/shape
|
|
630
640
|
// only and surfaces an unbound runnable TEST in rules_evaluate / readiness.
|
|
631
641
|
// ---------------------------------------------------------------------------
|
|
@@ -633,17 +643,71 @@ function testMustHaveRunnableBinding(graph) {
|
|
|
633
643
|
return graph.elements
|
|
634
644
|
.filter(e => e.type === 'TEST')
|
|
635
645
|
.filter(e => e.attributes?.concept !== true) // concept-only TESTs are exempt
|
|
636
|
-
|
|
646
|
+
// CR-SM-231: `testRefs` (Array, min 1) statt `testRef` (Objekt). Semantik unveraendert —
|
|
647
|
+
// Praesenz und Form, keine Datei-Existenz; die pruefst RC-02.
|
|
648
|
+
.filter(e => !TestRefsSchema.safeParse(e.attributes?.testRefs).success)
|
|
637
649
|
.map(test => ({
|
|
638
650
|
rule_id: 'R-19',
|
|
639
651
|
severity: 'warning',
|
|
640
652
|
element_id: test.id,
|
|
641
|
-
message: `${test.id} is a runnable TEST without a valid
|
|
642
|
-
fix_hint: 'Add attributes.
|
|
653
|
+
message: `${test.id} is a runnable TEST without a valid testRefs binding`,
|
|
654
|
+
fix_hint: 'Add attributes.testRefs [{file, case?, tool, level?}, …] with at least one entry, or set attributes.concept:true if it has no run artifact yet',
|
|
643
655
|
context: { element_type: test.type, element_name: test.name },
|
|
644
656
|
}));
|
|
645
657
|
}
|
|
646
658
|
// ---------------------------------------------------------------------------
|
|
659
|
+
// R-29: Testdatei-Exklusivitaet (CR-SM-231)
|
|
660
|
+
//
|
|
661
|
+
// Jede Testdatei erscheint in hoechstens einem `testRefs`. Das ist die Haelfte, die 1:n
|
|
662
|
+
// **erzwingt** statt es nur zu erlauben — ohne sie driftet das Attribut zurueck nach n:m,
|
|
663
|
+
// und genau das war der belegte Ist-Zustand: dieselbe Spec-Datei stand im `testRef` zweier
|
|
664
|
+
// TEST-Knoten. Folge: ein roter Lauf ist keiner Abnahme mehr eindeutig zuordenbar, und der
|
|
665
|
+
// TRR-Gate zaehlt dieselbe Evidenz doppelt.
|
|
666
|
+
//
|
|
667
|
+
// `error`, nicht `warning` (Entscheid 2026-08-13): eine doppelt beanspruchte Datei macht
|
|
668
|
+
// Gate-Zahlen nachweislich falsch. Das ist eine Fehlmessung, kein Vollstaendigkeits-Signal —
|
|
669
|
+
// und damit eine andere Klasse als R-19/R-20, die beide `warning` sind.
|
|
670
|
+
//
|
|
671
|
+
// Rein graph-strukturell, kein I/O: die Datei muss nicht existieren, um doppelt beansprucht
|
|
672
|
+
// zu sein.
|
|
673
|
+
// ---------------------------------------------------------------------------
|
|
674
|
+
function testFileExclusivity(graph) {
|
|
675
|
+
const claimedBy = new Map();
|
|
676
|
+
for (const el of graph.elements) {
|
|
677
|
+
if (el.type !== 'TEST')
|
|
678
|
+
continue;
|
|
679
|
+
const parsed = TestRefsSchema.safeParse(el.attributes?.testRefs);
|
|
680
|
+
if (!parsed.success)
|
|
681
|
+
continue; // keine/ungueltige Bindung → R-19-Gebiet
|
|
682
|
+
// Innerhalb EINES Knotens ist dieselbe Datei zweimal kein Konflikt, sondern eine
|
|
683
|
+
// Redundanz — sie wuerde sonst als Selbstkollision gemeldet.
|
|
684
|
+
for (const file of new Set(parsed.data.map(r => r.file))) {
|
|
685
|
+
const owners = claimedBy.get(file) ?? [];
|
|
686
|
+
owners.push(el.id);
|
|
687
|
+
claimedBy.set(file, owners);
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
const violations = [];
|
|
691
|
+
for (const [file, owners] of claimedBy) {
|
|
692
|
+
if (owners.length < 2)
|
|
693
|
+
continue;
|
|
694
|
+
const sorted = [...owners].sort();
|
|
695
|
+
// Einmal je beanspruchendem Knoten: jeder von ihnen muss handeln, und eine Meldung an
|
|
696
|
+
// nur einem waere fuer die anderen unsichtbar.
|
|
697
|
+
for (const owner of sorted) {
|
|
698
|
+
violations.push({
|
|
699
|
+
rule_id: 'R-29',
|
|
700
|
+
severity: 'error',
|
|
701
|
+
element_id: owner,
|
|
702
|
+
message: `${owner} claims test file '${file}', which is also claimed by ${sorted.filter(o => o !== owner).join(', ')}`,
|
|
703
|
+
fix_hint: 'A test file belongs to at most one TEST. Split the file, or drop the entry from all but the one acceptance it really evidences',
|
|
704
|
+
context: { element_type: 'TEST', element_name: graph.elements.find(e => e.id === owner)?.name },
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
return violations;
|
|
709
|
+
}
|
|
710
|
+
// ---------------------------------------------------------------------------
|
|
647
711
|
// R-20: FUNC realRef binding (CR-GC-205 Item 5, extended by CR-210, unified CR-228)
|
|
648
712
|
// — a FUNC that is not explicitly concept-only (attributes.concept === true) or
|
|
649
713
|
// externally realized (attributes.external === true) counts as BOUND when it EITHER
|
|
@@ -876,7 +940,7 @@ function modMustHaveAllocatedFunc(graph) {
|
|
|
876
940
|
// so its Zod definition is machine-resolvable (RC-03 then checks it resolves, RC-04
|
|
877
941
|
// that it is parsed at the interface). WARNING, not error: currently-unbound SCHEMAs
|
|
878
942
|
// on the reference model must not turn readiness red before any binding exists — the
|
|
879
|
-
// presence signal mirrors R-20 (FUNC realRef) / R-19 (
|
|
943
|
+
// presence signal mirrors R-20 (FUNC realRef) / R-19 (testRefs). Symbol RESOLUTION is
|
|
880
944
|
// out of scope here (pure, no I/O — that is RC-03's job).
|
|
881
945
|
// ---------------------------------------------------------------------------
|
|
882
946
|
function schemaMustHaveSchemaRef(graph) {
|
|
@@ -953,35 +1017,36 @@ function ebenenPraesenz(graph) {
|
|
|
953
1017
|
}];
|
|
954
1018
|
}
|
|
955
1019
|
export const V3_RULES = [
|
|
956
|
-
{ id: 'R-01', name: 'REQ must have verification', severity: 'error', evaluate: reqMustHaveVerification },
|
|
957
|
-
{ id: 'R-02', name: 'FUNC must satisfy REQ', severity: 'warning', evaluate: funcMustSatisfyReq },
|
|
958
|
-
{ id: 'R-03', name: 'ASIL isolation', severity: 'error', evaluate: asilIsolation },
|
|
959
|
-
{ id: 'R-04', name: '
|
|
960
|
-
{ id: 'R-05', name: 'TEST must verify REQ', severity: 'warning', evaluate: testMustVerifyReq },
|
|
961
|
-
{ id: 'R-14', name: 'UC must have compose', severity: 'warning', evaluate: ucMustHaveCompose },
|
|
962
|
-
{ id: 'R-15', name: 'FCHAIN must have compose', severity: 'warning', evaluate: fchainMustHaveCompose },
|
|
963
|
-
{ id: 'R-16', name: 'ACTOR must have io', severity: 'warning', evaluate: actorMustHaveTrace },
|
|
964
|
-
{ id: 'R-17', name: 'SYS must have compose', severity: 'warning', evaluate: sysMustHaveCompose },
|
|
965
|
-
{ id: 'R-08', name: 'Trace consistency', severity: 'error', evaluate: traceConsistency },
|
|
966
|
-
{ id: 'R-10', name: 'FLOW completeness', severity: 'warning', evaluate: flowCompleteness },
|
|
967
|
-
{ id: 'R-12', name: 'No circular dependencies', severity: 'warning', evaluate: noDirectCircular },
|
|
968
|
-
{ id: 'R-18', name: 'Valid trace pattern', severity: 'error', evaluate: validTracePattern },
|
|
969
|
-
{ id: 'R-19', name: 'Runnable TEST binding', severity: 'warning', evaluate: testMustHaveRunnableBinding },
|
|
970
|
-
{ id: 'R-
|
|
971
|
-
{ id: 'R-
|
|
972
|
-
{ id: 'R-
|
|
973
|
-
{ id: 'R-
|
|
974
|
-
{ id: 'R-
|
|
975
|
-
{ id: 'R-
|
|
976
|
-
{ id: '
|
|
977
|
-
{ id: 'RD-
|
|
978
|
-
{ id: 'RD-
|
|
979
|
-
{ id: 'RD-
|
|
980
|
-
{ id: '
|
|
981
|
-
{ id: 'MS-
|
|
982
|
-
{ id: '
|
|
1020
|
+
{ id: 'R-01', name: 'REQ must have verification', severity: 'error', evaluate: reqMustHaveVerification, domain: ['REQ'] },
|
|
1021
|
+
{ id: 'R-02', name: 'FUNC must satisfy REQ', severity: 'warning', evaluate: funcMustSatisfyReq, domain: ['FUNC'] },
|
|
1022
|
+
{ id: 'R-03', name: 'ASIL isolation', severity: 'error', evaluate: asilIsolation, domain: ['FUNC'] },
|
|
1023
|
+
{ id: 'R-04', name: 'Module size relative to crossing flows', severity: 'warning', evaluate: maxModuleSize, domain: ['MOD'] },
|
|
1024
|
+
{ id: 'R-05', name: 'TEST must verify REQ', severity: 'warning', evaluate: testMustVerifyReq, domain: ['TEST'] },
|
|
1025
|
+
{ id: 'R-14', name: 'UC must have compose', severity: 'warning', evaluate: ucMustHaveCompose, domain: ['UC'] },
|
|
1026
|
+
{ id: 'R-15', name: 'FCHAIN must have compose', severity: 'warning', evaluate: fchainMustHaveCompose, domain: ['FCHAIN'] },
|
|
1027
|
+
{ id: 'R-16', name: 'ACTOR must have io', severity: 'warning', evaluate: actorMustHaveTrace, domain: ['ACTOR'] },
|
|
1028
|
+
{ id: 'R-17', name: 'SYS must have compose', severity: 'warning', evaluate: sysMustHaveCompose, domain: ['SYS'] },
|
|
1029
|
+
{ id: 'R-08', name: 'Trace consistency', severity: 'error', evaluate: traceConsistency, domain: ['all'] },
|
|
1030
|
+
{ id: 'R-10', name: 'FLOW completeness', severity: 'warning', evaluate: flowCompleteness, domain: ['FUNC'] },
|
|
1031
|
+
{ id: 'R-12', name: 'No circular dependencies', severity: 'warning', evaluate: noDirectCircular, domain: ['FUNC'] },
|
|
1032
|
+
{ id: 'R-18', name: 'Valid trace pattern', severity: 'error', evaluate: validTracePattern, domain: ['all'] },
|
|
1033
|
+
{ id: 'R-19', name: 'Runnable TEST binding', severity: 'warning', evaluate: testMustHaveRunnableBinding, domain: ['TEST'] },
|
|
1034
|
+
{ id: 'R-29', name: 'Test file exclusivity', severity: 'error', evaluate: testFileExclusivity, domain: ['TEST'] },
|
|
1035
|
+
{ id: 'R-20', name: 'FUNC realRef binding', severity: 'warning', evaluate: funcMustHaveCodeBinding, domain: ['FUNC'] },
|
|
1036
|
+
{ id: 'R-21', name: 'FUNC↔FUNC connection needs integration test', severity: 'warning', evaluate: fchainMustHaveIntegrationTest, domain: ['FCHAIN'] },
|
|
1037
|
+
{ id: 'R-22', name: 'FUNC must be allocated to MOD', severity: 'warning', evaluate: funcMustBeAllocated, domain: ['FUNC'] },
|
|
1038
|
+
{ id: 'R-23', name: 'MOD must have allocated FUNC', severity: 'warning', evaluate: modMustHaveAllocatedFunc, domain: ['MOD'] },
|
|
1039
|
+
{ id: 'R-26', name: 'SCHEMA must have realRef', severity: 'warning', evaluate: schemaMustHaveSchemaRef, domain: ['SCHEMA'] },
|
|
1040
|
+
{ id: 'R-27', name: 'Physical MOD must have realRef', severity: 'warning', evaluate: physicalModMustHaveRealRef, domain: ['MOD'] },
|
|
1041
|
+
{ id: 'RD-01', name: 'Unresolved requirement', severity: 'warning', evaluate: unresolvedRequirement, domain: ['REQ'] },
|
|
1042
|
+
{ id: 'RD-02', name: 'Decomposition consistency', severity: 'warning', evaluate: decompositionConsistency, domain: ['REQ'] },
|
|
1043
|
+
{ id: 'RD-03', name: 'No premature decomposition', severity: 'info', evaluate: noPrematureDecomposition, domain: ['REQ'] },
|
|
1044
|
+
{ id: 'RD-04', name: 'Decomposition breadth', severity: 'warning', evaluate: decompositionBreadth, domain: ['FUNC', 'MOD', 'SYS'] },
|
|
1045
|
+
{ id: 'MS-01', name: 'Milestone empty scope', severity: 'warning', evaluate: msEmptyScope, domain: ['MS'] },
|
|
1046
|
+
{ id: 'MS-02', name: 'Milestone dangling dependency', severity: 'error', evaluate: msDanglingDependency, domain: ['MS'] },
|
|
1047
|
+
{ id: 'R-28', name: 'Ebenen-Präsenz (FLOW+SCHEMA when funcCount>1)', severity: 'warning', evaluate: ebenenPraesenz, domain: ['graph'] },
|
|
983
1048
|
];
|
|
984
|
-
/** Run all rules against a graph */
|
|
985
|
-
export function evaluateRules(graph) {
|
|
986
|
-
return V3_RULES.flatMap(rule => rule.evaluate(graph));
|
|
1049
|
+
/** Run all rules against a graph. CR-SM-236: `policy` ist Pflicht — wie bei `evaluateAllRules`. */
|
|
1050
|
+
export function evaluateRules(graph, policy) {
|
|
1051
|
+
return V3_RULES.flatMap(rule => rule.evaluate(graph, policy));
|
|
987
1052
|
}
|
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
*/
|
|
15
15
|
import type { OntologyGraph } from './ontology.js';
|
|
16
16
|
import type { RuleDefinition, RuleViolation } from './rules.js';
|
|
17
|
+
import type { MetricPolicy } from './policy.js';
|
|
17
18
|
export declare function sc02IsReferenced(graph: OntologyGraph): RuleViolation[];
|
|
18
19
|
export declare function sc04FlowHasSchema(graph: OntologyGraph): RuleViolation[];
|
|
19
20
|
export declare const SC_RULES: RuleDefinition[];
|
|
20
|
-
export declare function evaluateSCRules(graph: OntologyGraph): RuleViolation[];
|
|
21
|
+
export declare function evaluateSCRules(graph: OntologyGraph, policy: MetricPolicy): RuleViolation[];
|
|
@@ -49,9 +49,11 @@ export function sc04FlowHasSchema(graph) {
|
|
|
49
49
|
// Aggregated array & convenience runner
|
|
50
50
|
// ---------------------------------------------------------------------------
|
|
51
51
|
export const SC_RULES = [
|
|
52
|
-
{ id: 'SC-02', name: 'Schema referenced by FLOW', severity: 'warning', evaluate: sc02IsReferenced },
|
|
53
|
-
{ id: 'SC-04', name: 'FLOW has SCHEMA binding', severity: 'warning', evaluate: sc04FlowHasSchema },
|
|
52
|
+
{ id: 'SC-02', name: 'Schema referenced by FLOW', severity: 'warning', evaluate: sc02IsReferenced, domain: ['SCHEMA'] },
|
|
53
|
+
{ id: 'SC-04', name: 'FLOW has SCHEMA binding', severity: 'warning', evaluate: sc04FlowHasSchema, domain: ['SCHEMA'] },
|
|
54
54
|
];
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
// CR-SM-236: `policy` wird durchgereicht, auch wo diese Familie heute keine Schwelle hat —
|
|
56
|
+
// ein Sonderweg je Familie waere genau der zweite Pfad, den der Regelsatz verbietet.
|
|
57
|
+
export function evaluateSCRules(graph, policy) {
|
|
58
|
+
return SC_RULES.flatMap(rule => rule.evaluate(graph, policy));
|
|
57
59
|
}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import type { OntologyGraph } from './ontology.js';
|
|
5
5
|
import type { RuleDefinition, RuleViolation } from './rules.js';
|
|
6
|
+
import type { MetricPolicy } from './policy.js';
|
|
6
7
|
export declare function uc01HasRequirements(graph: OntologyGraph): RuleViolation[];
|
|
7
8
|
export declare function uc02HasActor(graph: OntologyGraph): RuleViolation[];
|
|
8
9
|
export declare function uc03HasScenario(graph: OntologyGraph): RuleViolation[];
|
|
@@ -10,4 +11,4 @@ export declare function uc04GoalDefined(graph: OntologyGraph): RuleViolation[];
|
|
|
10
11
|
export declare function uc05HasPostcondition(graph: OntologyGraph): RuleViolation[];
|
|
11
12
|
export declare function uc06HasPrecondition(graph: OntologyGraph): RuleViolation[];
|
|
12
13
|
export declare const UC_RULES: RuleDefinition[];
|
|
13
|
-
export declare function evaluateUCRules(graph: OntologyGraph): RuleViolation[];
|
|
14
|
+
export declare function evaluateUCRules(graph: OntologyGraph, policy: MetricPolicy): RuleViolation[];
|
|
@@ -111,13 +111,15 @@ export function uc06HasPrecondition(graph) {
|
|
|
111
111
|
// Aggregated
|
|
112
112
|
// ---------------------------------------------------------------------------
|
|
113
113
|
export const UC_RULES = [
|
|
114
|
-
{ id: 'UC-01', name: 'UC has requirements', severity: 'error', evaluate: uc01HasRequirements },
|
|
115
|
-
{ id: 'UC-02', name: 'UC has actor', severity: 'error', evaluate: uc02HasActor },
|
|
116
|
-
{ id: 'UC-03', name: 'UC has scenario', severity: 'warning', evaluate: uc03HasScenario },
|
|
117
|
-
{ id: 'UC-04', name: 'UC has goal', severity: 'warning', evaluate: uc04GoalDefined },
|
|
118
|
-
{ id: 'UC-05', name: 'UC has postcondition', severity: 'info', evaluate: uc05HasPostcondition },
|
|
119
|
-
{ id: 'UC-06', name: 'UC has precondition', severity: 'info', evaluate: uc06HasPrecondition },
|
|
114
|
+
{ id: 'UC-01', name: 'UC has requirements', severity: 'error', evaluate: uc01HasRequirements, domain: ['UC'] },
|
|
115
|
+
{ id: 'UC-02', name: 'UC has actor', severity: 'error', evaluate: uc02HasActor, domain: ['UC'] },
|
|
116
|
+
{ id: 'UC-03', name: 'UC has scenario', severity: 'warning', evaluate: uc03HasScenario, domain: ['UC'] },
|
|
117
|
+
{ id: 'UC-04', name: 'UC has goal', severity: 'warning', evaluate: uc04GoalDefined, domain: ['UC'] },
|
|
118
|
+
{ id: 'UC-05', name: 'UC has postcondition', severity: 'info', evaluate: uc05HasPostcondition, domain: ['UC'] },
|
|
119
|
+
{ id: 'UC-06', name: 'UC has precondition', severity: 'info', evaluate: uc06HasPrecondition, domain: ['UC'] },
|
|
120
120
|
];
|
|
121
|
-
|
|
122
|
-
|
|
121
|
+
// CR-SM-236: `policy` wird durchgereicht, auch wo diese Familie heute keine Schwelle hat —
|
|
122
|
+
// ein Sonderweg je Familie waere genau der zweite Pfad, den der Regelsatz verbietet.
|
|
123
|
+
export function evaluateUCRules(graph, policy) {
|
|
124
|
+
return UC_RULES.flatMap(rule => rule.evaluate(graph, policy));
|
|
123
125
|
}
|
package/dist/se/view-rules.d.ts
CHANGED
|
@@ -1,11 +1,7 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CR-184: View-related quality rules.
|
|
3
|
-
* VR-01: TEST without testResult attribute (info).
|
|
4
|
-
* CL-01: ACTOR without UCs in at least 2 distinct operatingModes (warning).
|
|
5
|
-
*/
|
|
6
1
|
import type { OntologyGraph } from './ontology.js';
|
|
7
2
|
import type { RuleViolation, RuleDefinition } from './rules.js';
|
|
3
|
+
import type { MetricPolicy } from './policy.js';
|
|
8
4
|
export declare function vr01TestNoResult(graph: OntologyGraph): RuleViolation[];
|
|
9
5
|
export declare function cl01ConopsCompleteness(graph: OntologyGraph): RuleViolation[];
|
|
10
6
|
export declare const VIEW_RULES: RuleDefinition[];
|
|
11
|
-
export declare function evaluateViewRules(graph: OntologyGraph): RuleViolation[];
|
|
7
|
+
export declare function evaluateViewRules(graph: OntologyGraph, policy: MetricPolicy): RuleViolation[];
|
package/dist/se/view-rules.js
CHANGED
|
@@ -1,15 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CR-184: View-related quality rules.
|
|
3
|
+
* VR-01: TEST with a testRefs entry that has no result (info).
|
|
4
|
+
* CL-01: ACTOR without UCs in at least 2 distinct operatingModes (warning).
|
|
5
|
+
*/
|
|
6
|
+
import { TestRefsSchema } from './ontology.js';
|
|
1
7
|
// ---------------------------------------------------------------------------
|
|
2
|
-
// VR-01:
|
|
8
|
+
// VR-01: ein testRefs-Eintrag ohne Ergebnis
|
|
9
|
+
//
|
|
10
|
+
// CR-SM-231b: liest das Ergebnis PRO EINTRAG statt am Knoten. Vorher hing `testResult` am
|
|
11
|
+
// TEST-Knoten — bei n Eintraegen mehrdeutig: laeuft eine Abnahme als vitest UND playwright,
|
|
12
|
+
// kann ein einzelnes Ergebnis nicht sagen, welcher Lauf gemeint ist. Die Meldung nennt jetzt
|
|
13
|
+
// die Dateien ohne Ergebnis, nicht nur den Knoten.
|
|
14
|
+
//
|
|
15
|
+
// Ein TEST ohne gueltige Bindung faellt R-19 zur Last, nicht hier: sonst meldeten beide
|
|
16
|
+
// Regeln denselben Sachverhalt (Presence/Resolution-Trennung).
|
|
3
17
|
// ---------------------------------------------------------------------------
|
|
4
18
|
export function vr01TestNoResult(graph) {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
.
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
19
|
+
const violations = [];
|
|
20
|
+
for (const el of graph.elements) {
|
|
21
|
+
if (el.type !== 'TEST')
|
|
22
|
+
continue;
|
|
23
|
+
const parsed = TestRefsSchema.safeParse(el.attributes?.testRefs);
|
|
24
|
+
if (!parsed.success)
|
|
25
|
+
continue; // keine/ungueltige Bindung → R-19-Gebiet
|
|
26
|
+
const pending = parsed.data.filter(ref => ref.result === undefined);
|
|
27
|
+
if (pending.length === 0)
|
|
28
|
+
continue;
|
|
29
|
+
violations.push({
|
|
30
|
+
rule_id: 'VR-01',
|
|
31
|
+
severity: 'info',
|
|
32
|
+
element_id: el.id,
|
|
33
|
+
message: `${el.id} has ${pending.length} of ${parsed.data.length} testRefs entries without a result — assumed pending: ${pending.map(r => r.file).join(', ')}`,
|
|
34
|
+
fix_hint: 'Record the run outcome on the entry (result, ranAt, evidence) — a binding without a result is not evidence',
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
return violations;
|
|
13
38
|
}
|
|
14
39
|
// ---------------------------------------------------------------------------
|
|
15
40
|
// CL-01: ACTOR without UCs in >= 2 distinct operatingModes
|
|
@@ -48,9 +73,11 @@ export function cl01ConopsCompleteness(graph) {
|
|
|
48
73
|
// Aggregated array & convenience runner
|
|
49
74
|
// ---------------------------------------------------------------------------
|
|
50
75
|
export const VIEW_RULES = [
|
|
51
|
-
{ id: 'VR-01', name: 'TestNoResult', severity: 'info', evaluate: vr01TestNoResult },
|
|
52
|
-
{ id: 'CL-01', name: 'ConopsCompleteness', severity: 'warning', evaluate: cl01ConopsCompleteness },
|
|
76
|
+
{ id: 'VR-01', name: 'TestNoResult', severity: 'info', evaluate: vr01TestNoResult, domain: ['TEST'] },
|
|
77
|
+
{ id: 'CL-01', name: 'ConopsCompleteness', severity: 'warning', evaluate: cl01ConopsCompleteness, domain: ['ACTOR'] },
|
|
53
78
|
];
|
|
54
|
-
|
|
55
|
-
|
|
79
|
+
// CR-SM-236: `policy` wird durchgereicht, auch wo diese Familie heute keine Schwelle hat —
|
|
80
|
+
// ein Sonderweg je Familie waere genau der zweite Pfad, den der Regelsatz verbietet.
|
|
81
|
+
export function evaluateViewRules(graph, policy) {
|
|
82
|
+
return VIEW_RULES.flatMap(rule => rule.evaluate(graph, policy));
|
|
56
83
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sigloch/contracts",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
"prepublishOnly": "npm run build && npm run test"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
+
"@sigloch/contracts": "^4.1.0",
|
|
31
32
|
"zod": "^4.3.6"
|
|
32
33
|
},
|
|
33
34
|
"license": "MIT",
|