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.
Files changed (3) hide show
  1. package/package.json +1 -1
  2. package/src/checks.pl +87 -1
  3. package/src/kb.pl +13 -8
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kibi-core",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "private": false,
5
5
  "description": "Core Prolog modules and RDF graph logic for Kibi",
6
6
  "type": "module",
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 = "Archived/deprecated ADR has no successor — add a supersedes link from the replacement ADR",
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/archived/rejected ADRs that still constrain symbols.
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, archived, rejected]),
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 archived/deprecated but has no supersedes relationship pointing to it.
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, [archived, deprecated]),
603
+ memberchk(StatusAtom, [superseded, deprecated]),
602
604
  \+ kb_relationship(supersedes, _, Id).
603
605
 
604
606
  %% current_req(+Id)
605
- % Requirement is current when active and not superseded by another requirement.
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, active),
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)