kibi-core 0.1.8 → 0.1.10
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/package.json +1 -1
- package/src/checks.pl +87 -1
- package/src/kb.pl +13 -8
package/package.json
CHANGED
package/src/checks.pl
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
check_all_json/1, % Returns all violations as JSON string
|
|
9
9
|
check_must_priority_coverage/1, % Returns list of must-priority violations
|
|
10
10
|
check_symbol_coverage/1, % Returns list of uncovered symbols
|
|
11
|
+
check_symbol_traceability/2, % Returns list of symbols lacking direct traceability (ReqAdr option)
|
|
11
12
|
check_no_dangling_refs/1, % Returns list of dangling ref violations
|
|
12
13
|
check_no_cycles/1, % Returns list of cycle violations
|
|
13
14
|
check_required_fields/1, % Returns list of missing required field violations
|
|
@@ -34,9 +35,11 @@ all_relationship_types([
|
|
|
34
35
|
%% check_all(-ViolationsDict)
|
|
35
36
|
% Returns a dict with all violations grouped by rule type.
|
|
36
37
|
% Each value is a list of violation terms: violation(Rule, EntityId, Description, Suggestion, Source)
|
|
38
|
+
% For symbol-traceability, uses RequireAdr=false as default.
|
|
37
39
|
check_all(ViolationsDict) :-
|
|
38
40
|
check_must_priority_coverage(MustPriority),
|
|
39
41
|
check_symbol_coverage(SymbolCoverage),
|
|
42
|
+
check_symbol_traceability(false, SymbolTraceability),
|
|
40
43
|
check_no_dangling_refs(DanglingRefs),
|
|
41
44
|
check_no_cycles(Cycles),
|
|
42
45
|
check_required_fields(RequiredFields),
|
|
@@ -45,6 +48,7 @@ check_all(ViolationsDict) :-
|
|
|
45
48
|
ViolationsDict = _{
|
|
46
49
|
must_priority_coverage: MustPriority,
|
|
47
50
|
symbol_coverage: SymbolCoverage,
|
|
51
|
+
symbol_traceability: SymbolTraceability,
|
|
48
52
|
no_dangling_refs: DanglingRefs,
|
|
49
53
|
no_cycles: Cycles,
|
|
50
54
|
required_fields: RequiredFields,
|
|
@@ -98,6 +102,55 @@ symbol_coverage_violation(SymbolId, violation(
|
|
|
98
102
|
)) :-
|
|
99
103
|
violation_source(SymbolId, symbol, Source).
|
|
100
104
|
|
|
105
|
+
%% check_symbol_traceability(+RequireAdr, -Violations)
|
|
106
|
+
% Finds all symbols lacking direct traceability:
|
|
107
|
+
% - Every symbol must have at least one direct 'implements' relationship to a requirement
|
|
108
|
+
% - If RequireAdr=true, the symbol must also have at least one 'constrained_by' relationship to an ADR
|
|
109
|
+
check_symbol_traceability(RequireAdr, Violations) :-
|
|
110
|
+
findall(
|
|
111
|
+
Violation,
|
|
112
|
+
symbol_traceability_violation(RequireAdr, Violation),
|
|
113
|
+
Violations0
|
|
114
|
+
),
|
|
115
|
+
sort(Violations0, Violations).
|
|
116
|
+
|
|
117
|
+
symbol_traceability_violation(RequireAdr, violation(
|
|
118
|
+
'symbol-traceability',
|
|
119
|
+
SymbolId,
|
|
120
|
+
Description,
|
|
121
|
+
Suggestion,
|
|
122
|
+
Source
|
|
123
|
+
)) :-
|
|
124
|
+
kb_entity(SymbolId, symbol, _),
|
|
125
|
+
% Check if symbol has direct implements to a requirement
|
|
126
|
+
( kb_relationship(implements, SymbolId, ReqId),
|
|
127
|
+
kb_entity(ReqId, req, _)
|
|
128
|
+
-> HasReq = true
|
|
129
|
+
; HasReq = false
|
|
130
|
+
),
|
|
131
|
+
% Check if symbol has constrained_by to ADR (only if required)
|
|
132
|
+
( RequireAdr = true ->
|
|
133
|
+
( kb_relationship(constrained_by, SymbolId, AdrId),
|
|
134
|
+
kb_entity(AdrId, adr, _)
|
|
135
|
+
-> HasAdr = true
|
|
136
|
+
; HasAdr = false
|
|
137
|
+
)
|
|
138
|
+
; HasAdr = true % Not required, so pass this check
|
|
139
|
+
),
|
|
140
|
+
% Determine what is missing
|
|
141
|
+
( HasReq = false, HasAdr = false, RequireAdr = true ->
|
|
142
|
+
Description = "Symbol has no direct requirement link and no ADR constraint.",
|
|
143
|
+
Suggestion = "Add 'implements: REQ-xxx' and 'constrained_by: ADR-xxx' in symbols.yaml."
|
|
144
|
+
; HasReq = false ->
|
|
145
|
+
Description = "Symbol has no direct requirement link.",
|
|
146
|
+
Suggestion = "Add 'implements: REQ-xxx' in symbols.yaml."
|
|
147
|
+
; HasAdr = false ->
|
|
148
|
+
Description = "Symbol has no ADR constraint.",
|
|
149
|
+
Suggestion = "Add 'constrained_by: ADR-xxx' in symbols.yaml."
|
|
150
|
+
; fail % No violation
|
|
151
|
+
),
|
|
152
|
+
violation_source(SymbolId, symbol, Source).
|
|
153
|
+
|
|
101
154
|
%% check_no_dangling_refs(-Violations)
|
|
102
155
|
% Finds all relationships referencing non-existent entities.
|
|
103
156
|
check_no_dangling_refs(Violations) :-
|
|
@@ -271,7 +324,7 @@ deprecated_adr_violation(violation(
|
|
|
271
324
|
)) :-
|
|
272
325
|
deprecated_no_successor(AdrId),
|
|
273
326
|
|
|
274
|
-
Description = "
|
|
327
|
+
Description = "Superseded/deprecated ADR has no successor — add a supersedes link from the replacement ADR",
|
|
275
328
|
|
|
276
329
|
format(string(Suggestion), "Create a new ADR and add: links: [{type: supersedes, target: ~w}]", [AdrId]),
|
|
277
330
|
|
|
@@ -323,6 +376,39 @@ check_all_json(JsonString) :-
|
|
|
323
376
|
JsonString
|
|
324
377
|
).
|
|
325
378
|
|
|
379
|
+
%% check_all_json_with_options(-JsonString, +RequireAdr)
|
|
380
|
+
% Returns all violations as JSON string with options.
|
|
381
|
+
% RequireAdr: if true, symbol-traceability also requires ADR constraints.
|
|
382
|
+
check_all_json_with_options(JsonString, RequireAdr) :-
|
|
383
|
+
check_all_with_options(ViolationsDict, RequireAdr),
|
|
384
|
+
violations_dict_to_json(ViolationsDict, JsonDict),
|
|
385
|
+
with_output_to_string(
|
|
386
|
+
json_write_dict(current_output, JsonDict, [width(0)]),
|
|
387
|
+
JsonString
|
|
388
|
+
).
|
|
389
|
+
|
|
390
|
+
%% check_all_with_options(-ViolationsDict, +RequireAdr)
|
|
391
|
+
% Returns a dict with all violations, respecting options.
|
|
392
|
+
check_all_with_options(ViolationsDict, RequireAdr) :-
|
|
393
|
+
check_must_priority_coverage(MustPriority),
|
|
394
|
+
check_symbol_coverage(SymbolCoverage),
|
|
395
|
+
check_symbol_traceability(RequireAdr, SymbolTraceability),
|
|
396
|
+
check_no_dangling_refs(DanglingRefs),
|
|
397
|
+
check_no_cycles(Cycles),
|
|
398
|
+
check_required_fields(RequiredFields),
|
|
399
|
+
check_deprecated_adrs(DeprecatedADRs),
|
|
400
|
+
check_domain_contradictions(Contradictions),
|
|
401
|
+
ViolationsDict = _{
|
|
402
|
+
must_priority_coverage: MustPriority,
|
|
403
|
+
symbol_coverage: SymbolCoverage,
|
|
404
|
+
symbol_traceability: SymbolTraceability,
|
|
405
|
+
no_dangling_refs: DanglingRefs,
|
|
406
|
+
no_cycles: Cycles,
|
|
407
|
+
required_fields: RequiredFields,
|
|
408
|
+
deprecated_adr_no_successor: DeprecatedADRs,
|
|
409
|
+
domain_contradictions: Contradictions
|
|
410
|
+
}.
|
|
411
|
+
|
|
326
412
|
%% violations_dict_to_json(+ViolationsDict, -JsonDict)
|
|
327
413
|
% Converts a dict of violation/5 term lists to a dict of JSON-compatible dicts.
|
|
328
414
|
violations_dict_to_json(Dict, JsonDict) :-
|
package/src/kb.pl
CHANGED
|
@@ -554,12 +554,12 @@ conflicting(Adr1, Adr2) :-
|
|
|
554
554
|
Adr1 @< Adr2.
|
|
555
555
|
|
|
556
556
|
%% deprecated_still_used(+Adr, -Symbols)
|
|
557
|
-
% Deprecated/
|
|
557
|
+
% Deprecated/superseded ADRs that still constrain symbols.
|
|
558
558
|
deprecated_still_used(Adr, Symbols) :-
|
|
559
559
|
kb_entity(Adr, adr, Props),
|
|
560
560
|
memberchk(status=Status, Props),
|
|
561
561
|
normalize_term_atom(Status, StatusAtom),
|
|
562
|
-
memberchk(StatusAtom, [deprecated,
|
|
562
|
+
memberchk(StatusAtom, [deprecated, superseded]),
|
|
563
563
|
setof(Symbol, kb_relationship(constrained_by, Symbol, Adr), Symbols),
|
|
564
564
|
!.
|
|
565
565
|
deprecated_still_used(_, []).
|
|
@@ -569,9 +569,11 @@ deprecated_still_used(_, []).
|
|
|
569
569
|
%% ------------------------------------------------------------------
|
|
570
570
|
|
|
571
571
|
%% current_adr(+Id)
|
|
572
|
-
% True when Id is an ADR not superseded by any other ADR.
|
|
572
|
+
% True when Id is an accepted ADR not superseded by any other ADR.
|
|
573
573
|
current_adr(Id) :-
|
|
574
|
-
kb_entity(Id, adr,
|
|
574
|
+
kb_entity(Id, adr, Props),
|
|
575
|
+
memberchk(status=Status, Props),
|
|
576
|
+
normalize_term_atom(Status, accepted),
|
|
575
577
|
\+ kb_relationship(supersedes, _, Id).
|
|
576
578
|
|
|
577
579
|
%% superseded_by(+OldId, -NewId)
|
|
@@ -593,20 +595,23 @@ adr_chain_acc(Id, Visited, [Id|Rest]) :-
|
|
|
593
595
|
adr_chain_acc(Newer, [Id|Visited], Rest).
|
|
594
596
|
|
|
595
597
|
%% deprecated_no_successor(+OldId)
|
|
596
|
-
% Lint rule: ADR is
|
|
598
|
+
% Lint rule: ADR is superseded/deprecated but has no supersedes relationship pointing to it.
|
|
597
599
|
deprecated_no_successor(Id) :-
|
|
598
600
|
kb_entity(Id, adr, Props),
|
|
599
601
|
memberchk(status=Status, Props),
|
|
600
602
|
normalize_term_atom(Status, StatusAtom),
|
|
601
|
-
memberchk(StatusAtom, [
|
|
603
|
+
memberchk(StatusAtom, [superseded, deprecated]),
|
|
602
604
|
\+ kb_relationship(supersedes, _, Id).
|
|
603
605
|
|
|
604
606
|
%% current_req(+Id)
|
|
605
|
-
% Requirement is current when
|
|
607
|
+
% Requirement is current when not deprecated and not superseded by another requirement.
|
|
608
|
+
% Canonical statuses: open, in_progress, closed.
|
|
609
|
+
% Legacy statuses accepted for backwards compatibility: active, approved.
|
|
606
610
|
current_req(Id) :-
|
|
607
611
|
kb_entity(Id, req, Props),
|
|
608
612
|
memberchk(status=Status, Props),
|
|
609
|
-
normalize_term_atom(Status,
|
|
613
|
+
normalize_term_atom(Status, StatusAtom),
|
|
614
|
+
memberchk(StatusAtom, [open, in_progress, closed, active, approved]),
|
|
610
615
|
\+ kb_relationship(supersedes, _, Id).
|
|
611
616
|
|
|
612
617
|
%% contradicting_reqs(-ReqA, -ReqB, -Reason)
|