kibi-core 0.8.1 → 0.10.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/package.json +2 -3
- package/schema/entities.pl +17 -1
- package/schema/validation.pl +2 -0
- package/src/checks.pl +144 -34
- package/src/discovery.pl +88 -18
- package/src/kb.pl +1339 -42
- package/src/requirement_proof.pl +837 -0
- package/src/status.pl +42 -40
package/package.json
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kibi-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Core Prolog modules and RDF graph logic for Kibi",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"engines": {
|
|
8
|
-
"node": ">=18"
|
|
9
|
-
"bun": ">=1.0"
|
|
8
|
+
"node": ">=18"
|
|
10
9
|
},
|
|
11
10
|
"main": "./src/kb.pl",
|
|
12
11
|
"files": ["src/**/*.pl", "schema/**/*.pl"],
|
package/schema/entities.pl
CHANGED
|
@@ -29,8 +29,13 @@ entity_property(_, severity, atom).
|
|
|
29
29
|
entity_property(_, links, list).
|
|
30
30
|
entity_property(_, text_ref, uri).
|
|
31
31
|
entity_property(_, sourceFile, uri).
|
|
32
|
+
entity_property(req, semantic_text, string).
|
|
32
33
|
entity_property(req, logic_claims, list).
|
|
33
|
-
entity_property(req,
|
|
34
|
+
entity_property(req, semantic_clauses, list).
|
|
35
|
+
entity_property(req, semantic_inventory_version, string).
|
|
36
|
+
entity_property(req, semantic_source_field, string).
|
|
37
|
+
entity_property(req, semantic_source_hash, string).
|
|
38
|
+
entity_property(req, semantic_inventory, list_or_json).
|
|
34
39
|
|
|
35
40
|
% Typed fact fields - only valid for fact entities
|
|
36
41
|
entity_property(fact, fact_kind, atom).
|
|
@@ -74,10 +79,15 @@ entity_property(fact, claim_span_end, integer).
|
|
|
74
79
|
% Typed symbol metadata fields - only valid for symbol entities
|
|
75
80
|
entity_property(symbol, symbol_role, atom).
|
|
76
81
|
entity_property(symbol, granularity_reason, atom).
|
|
82
|
+
entity_property(symbol, sourceLine, integer).
|
|
83
|
+
entity_property(symbol, sourceColumn, integer).
|
|
84
|
+
entity_property(symbol, sourceEndLine, integer).
|
|
85
|
+
entity_property(symbol, sourceEndColumn, integer).
|
|
77
86
|
|
|
78
87
|
% Typed test verification fields - only valid for test entities
|
|
79
88
|
entity_property(test, verification_scope, atom).
|
|
80
89
|
entity_property(test, verification_perspective, atom).
|
|
90
|
+
entity_property(test, verification_receipts, string).
|
|
81
91
|
|
|
82
92
|
% Required properties for all entity types
|
|
83
93
|
required_property(Type, id) :- entity_type(Type).
|
|
@@ -94,10 +104,16 @@ optional_property(Type, priority) :- entity_type(Type).
|
|
|
94
104
|
optional_property(Type, severity) :- entity_type(Type).
|
|
95
105
|
optional_property(Type, links) :- entity_type(Type).
|
|
96
106
|
optional_property(Type, text_ref) :- entity_type(Type).
|
|
107
|
+
optional_property(req, semantic_text).
|
|
97
108
|
optional_property(req, logic_claims).
|
|
109
|
+
optional_property(req, semantic_clauses).
|
|
110
|
+
optional_property(req, semantic_inventory_version).
|
|
111
|
+
optional_property(req, semantic_source_field).
|
|
112
|
+
optional_property(req, semantic_source_hash).
|
|
98
113
|
optional_property(req, semantic_inventory).
|
|
99
114
|
optional_property(test, verification_scope).
|
|
100
115
|
optional_property(test, verification_perspective).
|
|
116
|
+
optional_property(test, verification_receipts).
|
|
101
117
|
|
|
102
118
|
% Documentation helpers
|
|
103
119
|
% list all entity types
|
package/schema/validation.pl
CHANGED
|
@@ -45,6 +45,7 @@ check_kind(string, V) :- string(V).
|
|
|
45
45
|
check_kind(atom_or_string, V) :- atom(V) ; string(V).
|
|
46
46
|
check_kind(datetime, V) :- string(V). % accept ISO strings for now
|
|
47
47
|
check_kind(list, V) :- is_list(V).
|
|
48
|
+
check_kind(list_or_json, V) :- is_list(V) ; string(V) ; atom(V).
|
|
48
49
|
check_kind(uri, V) :- string(V).
|
|
49
50
|
check_kind(integer, V) :- integer(V).
|
|
50
51
|
check_kind(number, V) :- number(V).
|
|
@@ -118,6 +119,7 @@ is_fact_only_field(claim_span_end).
|
|
|
118
119
|
% is_test_only_field(+Key) - true if Key is a test-specific field
|
|
119
120
|
is_test_only_field(verification_scope).
|
|
120
121
|
is_test_only_field(verification_perspective).
|
|
122
|
+
is_test_only_field(verification_receipts).
|
|
121
123
|
|
|
122
124
|
% validate_fact_shape(+Kind, +Props)
|
|
123
125
|
validate_fact_shape(subject, Props) :-
|
package/src/checks.pl
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
check_required_fields/1, % Returns list of missing required field violations
|
|
15
15
|
check_deprecated_adrs/1, % Returns list of deprecated ADR violations
|
|
16
16
|
check_domain_contradictions/1, % Returns list of contradiction violations
|
|
17
|
+
check_domain_contradiction_witnesses/1,
|
|
17
18
|
check_strict_fact_shape/1, % Returns list of malformed strict fact violations
|
|
18
19
|
check_strict_req_fact_pairing/1,% Returns list of malformed strict req/fact pairing violations
|
|
19
20
|
check_strict_readiness/1, % Returns list of strict readiness audit violations
|
|
@@ -31,7 +32,7 @@
|
|
|
31
32
|
:- use_module('kb.pl').
|
|
32
33
|
:- use_module('../schema/entities.pl', [entity_type/1, required_property/2]).
|
|
33
34
|
:- use_module('../schema/relationships.pl', [relationship_type/1]).
|
|
34
|
-
:- use_module('logic_ir.pl', [logic_rule_safety/2, logic_rule_from_props/2, logic_rule_conflict/3, logic_rules_stratified/1]).
|
|
35
|
+
:- use_module('logic_ir.pl', [logic_rule_safety/2, logic_rule_from_props/2, logic_rule_conflict/3, logic_rule_conflict_witness/3, logic_rules_stratified/1]).
|
|
35
36
|
|
|
36
37
|
% Required fields for all entities
|
|
37
38
|
required_fields([id, title, status, created_at, updated_at, source]).
|
|
@@ -587,39 +588,39 @@ checks_normalize_atom_list(Raw, Atoms) :-
|
|
|
587
588
|
%% check_domain_contradictions(-Violations)
|
|
588
589
|
% Finds all pairs of requirements with contradicting required properties.
|
|
589
590
|
check_domain_contradictions(Violations) :-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
RuleViolations
|
|
619
|
-
),
|
|
620
|
-
append(StrictViolations, RuleViolations, Violations).
|
|
591
|
+
check_domain_contradiction_witnesses(Witnesses),
|
|
592
|
+
maplist(contradiction_witness_violation, Witnesses, Violations).
|
|
593
|
+
|
|
594
|
+
%% check_domain_contradiction_witnesses(-Witnesses)
|
|
595
|
+
% Exact, JSON-safe evidence for every strict-property, ground-predicate, and
|
|
596
|
+
% safe-rule contradiction result. Rule overlap that cannot be proven or
|
|
597
|
+
% disproven remains status=unresolved rather than becoming consistency.
|
|
598
|
+
check_domain_contradiction_witnesses(Witnesses) :-
|
|
599
|
+
findall(Witness, req_conflict_witness(_ReqA, _ReqB, Witness), GroundWitnesses),
|
|
600
|
+
findall(Witness, rule_contradiction_witness(Witness), RuleWitnesses),
|
|
601
|
+
append(GroundWitnesses, RuleWitnesses, Witnesses0),
|
|
602
|
+
sort(Witnesses0, Witnesses).
|
|
603
|
+
|
|
604
|
+
contradiction_witness_violation(Witness, violation(
|
|
605
|
+
'domain-contradictions',
|
|
606
|
+
EntityId,
|
|
607
|
+
Description,
|
|
608
|
+
Suggestion,
|
|
609
|
+
""
|
|
610
|
+
)) :-
|
|
611
|
+
Witness.requirements = [ReqA, ReqB],
|
|
612
|
+
format(string(EntityId), "~w/~w", [ReqA, ReqB]),
|
|
613
|
+
( Witness.kind == rule
|
|
614
|
+
-> Description = Witness.reason,
|
|
615
|
+
Suggestion = "Align the opposing rule heads, separate their scopes, or add an explicit supersedes relationship"
|
|
616
|
+
; format(string(Description), "~w [strict-readiness: contradiction-ready]", [Witness.reason]),
|
|
617
|
+
Suggestion = "Supersede one requirement or align both to the same canonical logical term"
|
|
618
|
+
).
|
|
621
619
|
|
|
622
620
|
opposing_rule_requirements(ReqA, ReqB, RuleA, RuleB) :-
|
|
621
|
+
opposing_rule_requirement_facts(ReqA, ReqB, _FactA, _FactB, RuleA, RuleB).
|
|
622
|
+
|
|
623
|
+
opposing_rule_requirement_facts(ReqA, ReqB, FactA, FactB, RuleA, RuleB) :-
|
|
623
624
|
kb:current_req(ReqA),
|
|
624
625
|
kb:current_req(ReqB),
|
|
625
626
|
ReqA @< ReqB,
|
|
@@ -635,6 +636,94 @@ opposing_rule_requirements(ReqA, ReqB, RuleA, RuleB) :-
|
|
|
635
636
|
logic_rule_from_props(PropsA, RuleA),
|
|
636
637
|
logic_rule_from_props(PropsB, RuleB).
|
|
637
638
|
|
|
639
|
+
rule_contradiction_witness(Witness) :-
|
|
640
|
+
opposing_rule_requirement_facts(ReqA, ReqB, FactA, FactB, RuleA, RuleB),
|
|
641
|
+
logic_rule_conflict_witness(RuleA, RuleB, InternalWitness),
|
|
642
|
+
InternalWitness.status \= disjoint,
|
|
643
|
+
rule_conflict_side(ReqA, FactA, Left),
|
|
644
|
+
rule_conflict_side(ReqB, FactB, Right),
|
|
645
|
+
rule_comparison_evidence(InternalWitness, Comparison),
|
|
646
|
+
format(string(Reason), "Rule conflict (~w) between ~w and ~w", [InternalWitness.status, ReqA, ReqB]),
|
|
647
|
+
Witness = _{
|
|
648
|
+
kind: rule,
|
|
649
|
+
status: InternalWitness.status,
|
|
650
|
+
requirements: [ReqA, ReqB],
|
|
651
|
+
reason: Reason,
|
|
652
|
+
left: Left,
|
|
653
|
+
right: Right,
|
|
654
|
+
comparison: Comparison
|
|
655
|
+
}.
|
|
656
|
+
|
|
657
|
+
rule_conflict_side(ReqId, FactId, Side) :-
|
|
658
|
+
evidence_entity_source(ReqId, req, RequirementSource),
|
|
659
|
+
kb_entity(FactId, fact, Props),
|
|
660
|
+
evidence_property_text(Props, source, FactSource),
|
|
661
|
+
evidence_property_text(Props, claim_key, ClaimKey),
|
|
662
|
+
evidence_property_text(Props, claim_text, ClaimText),
|
|
663
|
+
evidence_property_text(Props, rule_hash, RuleHash),
|
|
664
|
+
evidence_property_text(Props, semantic_key, SemanticKey),
|
|
665
|
+
evidence_property_text(Props, rule_ir, RuleIr),
|
|
666
|
+
evidence_property_integer(Props, claim_span_start, ClaimSpanStart),
|
|
667
|
+
evidence_property_integer(Props, claim_span_end, ClaimSpanEnd),
|
|
668
|
+
Side = _{
|
|
669
|
+
requirementId: ReqId,
|
|
670
|
+
requirementSource: RequirementSource,
|
|
671
|
+
factId: FactId,
|
|
672
|
+
factSource: FactSource,
|
|
673
|
+
claimKey: ClaimKey,
|
|
674
|
+
claimText: ClaimText,
|
|
675
|
+
claimSpan: _{start: ClaimSpanStart, end: ClaimSpanEnd},
|
|
676
|
+
ruleHash: RuleHash,
|
|
677
|
+
semanticKey: SemanticKey,
|
|
678
|
+
ruleIr: RuleIr
|
|
679
|
+
}.
|
|
680
|
+
|
|
681
|
+
rule_comparison_evidence(Internal, Evidence) :-
|
|
682
|
+
term_string(Internal.head_a, HeadA, [quoted(true), max_depth(0)]),
|
|
683
|
+
term_string(Internal.head_b, HeadB, [quoted(true), max_depth(0)]),
|
|
684
|
+
term_string(Internal.body_a, BodyA, [quoted(true), max_depth(0)]),
|
|
685
|
+
term_string(Internal.body_b, BodyB, [quoted(true), max_depth(0)]),
|
|
686
|
+
term_string(Internal.scope_a, ScopeA, [quoted(true), max_depth(0)]),
|
|
687
|
+
term_string(Internal.scope_b, ScopeB, [quoted(true), max_depth(0)]),
|
|
688
|
+
Evidence = _{
|
|
689
|
+
modalityA: Internal.modality_a,
|
|
690
|
+
modalityB: Internal.modality_b,
|
|
691
|
+
headA: HeadA,
|
|
692
|
+
headB: HeadB,
|
|
693
|
+
bodyA: BodyA,
|
|
694
|
+
bodyB: BodyB,
|
|
695
|
+
scopeA: ScopeA,
|
|
696
|
+
scopeB: ScopeB,
|
|
697
|
+
validFromA: Internal.valid_from_a,
|
|
698
|
+
validToA: Internal.valid_to_a,
|
|
699
|
+
validFromB: Internal.valid_from_b,
|
|
700
|
+
validToB: Internal.valid_to_b,
|
|
701
|
+
ruleSchemaA: Internal.rule_schema_a,
|
|
702
|
+
ruleSchemaB: Internal.rule_schema_b
|
|
703
|
+
}.
|
|
704
|
+
|
|
705
|
+
evidence_entity_source(EntityId, Type, Source) :-
|
|
706
|
+
kb_entity(EntityId, Type, Props),
|
|
707
|
+
evidence_property_text(Props, source, Source).
|
|
708
|
+
|
|
709
|
+
evidence_property_text(Props, Key, Text) :-
|
|
710
|
+
(memberchk(Key=Raw, Props) -> evidence_term_text(Raw, Text) ; Text = '').
|
|
711
|
+
|
|
712
|
+
evidence_property_integer(Props, Key, Integer) :-
|
|
713
|
+
(memberchk(Key=Raw, Props), catch(checks_normalize_integer(Raw, Integer), _, fail) -> true ; Integer = -1).
|
|
714
|
+
|
|
715
|
+
evidence_term_text(Raw, Text) :-
|
|
716
|
+
( Raw = ^^(Value, _Type)
|
|
717
|
+
-> evidence_term_text(Value, Text)
|
|
718
|
+
; Raw = literal(type(_, Value))
|
|
719
|
+
-> evidence_term_text(Value, Text)
|
|
720
|
+
; atom(Raw)
|
|
721
|
+
-> Text = Raw
|
|
722
|
+
; string(Raw)
|
|
723
|
+
-> atom_string(Text, Raw)
|
|
724
|
+
; term_string(Raw, String), atom_string(Text, String)
|
|
725
|
+
).
|
|
726
|
+
|
|
638
727
|
%% check_strict_req_fact_pairing(-Violations)
|
|
639
728
|
% Finds current requirements attempting strict-lane modeling with incomplete
|
|
640
729
|
% subject/property pairing or wrong-lane fact targets.
|
|
@@ -1194,7 +1283,7 @@ inventory_entries(Raw, Entries) :-
|
|
|
1194
1283
|
( is_list(Raw) -> Entries = Raw
|
|
1195
1284
|
; Raw = ^^(Value, _) -> inventory_entries(Value, Entries)
|
|
1196
1285
|
; (atom(Raw) ; string(Raw)),
|
|
1197
|
-
catch(
|
|
1286
|
+
catch(atom_json_dict(Raw, JsonEntries, [value_string_as(string)]), _, fail),
|
|
1198
1287
|
is_list(JsonEntries)
|
|
1199
1288
|
-> Entries = JsonEntries
|
|
1200
1289
|
; Entries = []
|
|
@@ -1319,7 +1408,28 @@ pairs_to_json_pairs([Key-Violations|Rest], [Key-JsonViolations|JsonRest]) :-
|
|
|
1319
1408
|
% Converts a violation(Rule, EntityId, Description, Suggestion, Source) term
|
|
1320
1409
|
% to a JSON-compatible dict.
|
|
1321
1410
|
violation_to_json(Violation, JsonDict) :-
|
|
1322
|
-
violation_term_to_dict(Violation,
|
|
1411
|
+
violation_term_to_dict(Violation, BaseDict),
|
|
1412
|
+
( contradiction_violation_witnesses(Violation, Witnesses),
|
|
1413
|
+
Witnesses \= []
|
|
1414
|
+
-> put_dict(evidence, BaseDict, _{witnesses: Witnesses}, JsonDict)
|
|
1415
|
+
; JsonDict = BaseDict
|
|
1416
|
+
).
|
|
1417
|
+
|
|
1418
|
+
contradiction_violation_witnesses(
|
|
1419
|
+
violation('domain-contradictions', EntityId, Description, _Suggestion, _Source),
|
|
1420
|
+
Witnesses
|
|
1421
|
+
) :-
|
|
1422
|
+
violation_id_text(EntityId, EntityIdText),
|
|
1423
|
+
violation_text(Description, DescriptionText),
|
|
1424
|
+
check_domain_contradiction_witnesses(AllWitnesses),
|
|
1425
|
+
include(witness_matches_violation(EntityIdText, DescriptionText), AllWitnesses, Witnesses).
|
|
1426
|
+
|
|
1427
|
+
witness_matches_violation(EntityIdText, DescriptionText, Witness) :-
|
|
1428
|
+
Witness.requirements = [ReqA, ReqB],
|
|
1429
|
+
format(string(ExpectedEntityId), "~w/~w", [ReqA, ReqB]),
|
|
1430
|
+
ExpectedEntityId == EntityIdText,
|
|
1431
|
+
violation_text(Witness.reason, ReasonText),
|
|
1432
|
+
sub_string(DescriptionText, 0, _, _, ReasonText).
|
|
1323
1433
|
|
|
1324
1434
|
violation_term_to_dict(violation(Rule, EntityId, Description, Suggestion, Source), JsonDict) :-
|
|
1325
1435
|
violation_text(Rule, RuleText),
|
package/src/discovery.pl
CHANGED
|
@@ -4,12 +4,14 @@
|
|
|
4
4
|
:- module(discovery, [
|
|
5
5
|
find_gaps_json/8,
|
|
6
6
|
coverage_report_json/7,
|
|
7
|
+
coverage_report_json/10,
|
|
7
8
|
graph_expand_json/8
|
|
8
9
|
]).
|
|
9
10
|
|
|
10
11
|
:- use_module(library(http/json)).
|
|
11
12
|
:- use_module(library(aggregate)).
|
|
12
13
|
:- use_module('kb.pl').
|
|
14
|
+
:- use_module('requirement_proof.pl', [requirement_proof_context/1, requirement_proof_context/4, requirement_proof/4]).
|
|
13
15
|
:- use_module('status.pl', [status_meta_dict/1]).
|
|
14
16
|
:- use_module('../schema/relationships.pl', [relationship_type/1]).
|
|
15
17
|
|
|
@@ -27,7 +29,10 @@ find_gaps_json(TypeFilter, MissingRelationships, PresentRelationships, Tags, Sou
|
|
|
27
29
|
dict_json_string(Response, JsonString).
|
|
28
30
|
|
|
29
31
|
coverage_report_json(By, Tags, IncludePassing, IncludeTransitive, Limit, Offset, JsonString) :-
|
|
30
|
-
|
|
32
|
+
coverage_report_json(By, Tags, IncludePassing, IncludeTransitive, Limit, Offset, unknown, '1970-01-01T00:00:00Z', 604800, JsonString).
|
|
33
|
+
|
|
34
|
+
coverage_report_json(By, Tags, IncludePassing, IncludeTransitive, Limit, Offset, VerificationSnapshot, CheckedAt, MaxAgeSeconds, JsonString) :-
|
|
35
|
+
coverage_rows(By, Tags, IncludePassing, IncludeTransitive, VerificationSnapshot, CheckedAt, MaxAgeSeconds, Rows0, Summary),
|
|
31
36
|
sort_dict_rows(Rows0, SortedRows),
|
|
32
37
|
paginate_rows(SortedRows, Offset, Limit, Rows),
|
|
33
38
|
status_meta_dict(Meta),
|
|
@@ -95,14 +100,15 @@ relationship_count(Id, Relationship, Count) :-
|
|
|
95
100
|
(kb_relationship(Relationship, Id, _); kb_relationship(Relationship, _, Id)),
|
|
96
101
|
Count).
|
|
97
102
|
|
|
98
|
-
coverage_rows(req, Tags, IncludePassing, IncludeTransitive, Rows, Summary) :-
|
|
103
|
+
coverage_rows(req, Tags, IncludePassing, IncludeTransitive, VerificationSnapshot, CheckedAt, MaxAgeSeconds, Rows, Summary) :-
|
|
99
104
|
!,
|
|
105
|
+
requirement_proof_context(VerificationSnapshot, CheckedAt, MaxAgeSeconds, ProofContext),
|
|
100
106
|
findall(Row,
|
|
101
|
-
requirement_coverage_row(Tags, IncludeTransitive, Row),
|
|
107
|
+
requirement_coverage_row(Tags, IncludeTransitive, ProofContext, Row),
|
|
102
108
|
AllRows),
|
|
103
109
|
filter_req_coverage_rows(IncludePassing, AllRows, Rows),
|
|
104
110
|
coverage_summary(AllRows, Summary).
|
|
105
|
-
coverage_rows(symbol, Tags, IncludePassing, _IncludeTransitive, Rows, Summary) :-
|
|
111
|
+
coverage_rows(symbol, Tags, IncludePassing, _IncludeTransitive, _VerificationSnapshot, _CheckedAt, _MaxAgeSeconds, Rows, Summary) :-
|
|
106
112
|
!,
|
|
107
113
|
findall(Row,
|
|
108
114
|
symbol_coverage_row(Tags, Row),
|
|
@@ -110,10 +116,23 @@ coverage_rows(symbol, Tags, IncludePassing, _IncludeTransitive, Rows, Summary) :
|
|
|
110
116
|
filter_symbol_coverage_rows(IncludePassing, AllRows, Rows),
|
|
111
117
|
length(AllRows, Total),
|
|
112
118
|
include(symbol_row_fully_covered, AllRows, CoveredRows),
|
|
119
|
+
include(symbol_row_uncovered, AllRows, UncoveredRows),
|
|
120
|
+
include(symbol_row_not_applicable, AllRows, NotApplicableRows),
|
|
121
|
+
include(symbol_row_mixed_role, AllRows, MixedRoleRows),
|
|
113
122
|
length(CoveredRows, Covered),
|
|
114
|
-
Uncovered
|
|
115
|
-
|
|
116
|
-
|
|
123
|
+
length(UncoveredRows, Uncovered),
|
|
124
|
+
length(NotApplicableRows, NotApplicable),
|
|
125
|
+
length(MixedRoleRows, MixedRole),
|
|
126
|
+
Applicable is Total - NotApplicable,
|
|
127
|
+
Summary = _{
|
|
128
|
+
total: Total,
|
|
129
|
+
applicable: Applicable,
|
|
130
|
+
fullyCovered: Covered,
|
|
131
|
+
uncovered: Uncovered,
|
|
132
|
+
notApplicable: NotApplicable,
|
|
133
|
+
mixedRole: MixedRole
|
|
134
|
+
}.
|
|
135
|
+
coverage_rows(type, _Tags, _IncludePassing, _IncludeTransitive, _VerificationSnapshot, _CheckedAt, _MaxAgeSeconds, Rows, Summary) :-
|
|
117
136
|
findall(Type-Count,
|
|
118
137
|
type_entity_count(Type, Count),
|
|
119
138
|
Pairs),
|
|
@@ -121,7 +140,7 @@ coverage_rows(type, _Tags, _IncludePassing, _IncludeTransitive, Rows, Summary) :
|
|
|
121
140
|
length(Rows, Total),
|
|
122
141
|
Summary = _{total: Total}.
|
|
123
142
|
|
|
124
|
-
requirement_coverage_row(Tags, IncludeTransitive, Row) :-
|
|
143
|
+
requirement_coverage_row(Tags, IncludeTransitive, ProofContext, Row) :-
|
|
125
144
|
kb_entity(Id, req, Props),
|
|
126
145
|
matches_tags(Tags, Props),
|
|
127
146
|
entity_title_status_source(Props, Title, Status, _Source),
|
|
@@ -132,7 +151,7 @@ requirement_coverage_row(Tags, IncludeTransitive, Row) :-
|
|
|
132
151
|
count_transitive_symbols(Id, IncludeTransitive, TransitiveSymbolCount),
|
|
133
152
|
requirement_coverage_state(Id, Props, Gaps, Evaluated, CoverageStatus),
|
|
134
153
|
requirement_coverage_depth(Id, ScenarioCount, CoverageDepth, CoverageEvidence),
|
|
135
|
-
|
|
154
|
+
BaseRow = _{
|
|
136
155
|
id: Id,
|
|
137
156
|
type: req,
|
|
138
157
|
title: Title,
|
|
@@ -151,7 +170,9 @@ requirement_coverage_row(Tags, IncludeTransitive, Row) :-
|
|
|
151
170
|
scenarioTests: CoverageEvidence.scenarioTests,
|
|
152
171
|
testStatuses: CoverageEvidence.testStatuses,
|
|
153
172
|
verificationScopes: CoverageEvidence.verificationScopes
|
|
154
|
-
}
|
|
173
|
+
},
|
|
174
|
+
requirement_proof(Id, Props, ProofContext, Proof),
|
|
175
|
+
put_dict(Proof, BaseRow, Row).
|
|
155
176
|
|
|
156
177
|
symbol_coverage_row(Tags, Row) :-
|
|
157
178
|
kb_entity(Id, symbol, Props),
|
|
@@ -159,19 +180,32 @@ symbol_coverage_row(Tags, Row) :-
|
|
|
159
180
|
entity_title_status_source(Props, Title, Status, _Source),
|
|
160
181
|
count_direct_requirements(Id, DirectRequirementCount),
|
|
161
182
|
count_direct_tests(Id, TestCount),
|
|
162
|
-
(
|
|
163
|
-
|
|
183
|
+
count_executable_tests(Id, ExecutableTestCount),
|
|
184
|
+
( mixed_role_symbol(Id)
|
|
185
|
+
-> TraceabilityRole = mixed,
|
|
186
|
+
Gaps = [invalid_mixed_symbol_role],
|
|
164
187
|
CoverageStatus = uncovered
|
|
165
|
-
;
|
|
166
|
-
|
|
188
|
+
; executable_test_symbol(Id)
|
|
189
|
+
-> TraceabilityRole = executable_test,
|
|
190
|
+
Gaps = [],
|
|
191
|
+
CoverageStatus = not_applicable
|
|
192
|
+
; TraceabilityRole = production,
|
|
193
|
+
( symbol_no_req_coverage(Id, _)
|
|
194
|
+
-> Gaps = [missing_requirement],
|
|
195
|
+
CoverageStatus = uncovered
|
|
196
|
+
; Gaps = [],
|
|
197
|
+
CoverageStatus = fully_covered
|
|
198
|
+
)
|
|
167
199
|
),
|
|
168
200
|
Row = _{
|
|
169
201
|
id: Id,
|
|
170
202
|
type: symbol,
|
|
171
203
|
title: Title,
|
|
172
204
|
status: Status,
|
|
205
|
+
traceabilityRole: TraceabilityRole,
|
|
173
206
|
directRequirementCount: DirectRequirementCount,
|
|
174
207
|
testCount: TestCount,
|
|
208
|
+
executableTestCount: ExecutableTestCount,
|
|
175
209
|
gaps: Gaps,
|
|
176
210
|
coverageStatus: CoverageStatus
|
|
177
211
|
}.
|
|
@@ -185,6 +219,10 @@ coverage_summary(Rows, Summary) :-
|
|
|
185
219
|
include(req_row_missing_scenario, Rows, MissingScenarioRows),
|
|
186
220
|
include(req_row_missing_test, Rows, MissingTestRows),
|
|
187
221
|
include(req_row_missing_both, Rows, MissingBothRows),
|
|
222
|
+
include(req_row_proof_proven, Rows, ProofProvenRows),
|
|
223
|
+
include(req_row_proof_unresolved, Rows, ProofUnresolvedRows),
|
|
224
|
+
include(req_row_proof_missing, Rows, ProofMissingRows),
|
|
225
|
+
include(req_row_proof_not_applicable, Rows, ProofNotApplicableRows),
|
|
188
226
|
length(EvaluatedRows, Evaluated),
|
|
189
227
|
length(FullyCoveredRows, FullyCovered),
|
|
190
228
|
length(UncoveredRows, Uncovered),
|
|
@@ -192,6 +230,10 @@ coverage_summary(Rows, Summary) :-
|
|
|
192
230
|
length(MissingScenarioRows, MissingScenario),
|
|
193
231
|
length(MissingTestRows, MissingTest),
|
|
194
232
|
length(MissingBothRows, MissingScenarioAndTest),
|
|
233
|
+
length(ProofProvenRows, ProofProven),
|
|
234
|
+
length(ProofUnresolvedRows, ProofUnresolved),
|
|
235
|
+
length(ProofMissingRows, ProofMissing),
|
|
236
|
+
length(ProofNotApplicableRows, ProofNotApplicable),
|
|
195
237
|
Summary = _{
|
|
196
238
|
total: Total,
|
|
197
239
|
evaluated: Evaluated,
|
|
@@ -200,7 +242,11 @@ coverage_summary(Rows, Summary) :-
|
|
|
200
242
|
notApplicable: NotApplicable,
|
|
201
243
|
missingScenario: MissingScenario,
|
|
202
244
|
missingTest: MissingTest,
|
|
203
|
-
missingScenarioAndTest: MissingScenarioAndTest
|
|
245
|
+
missingScenarioAndTest: MissingScenarioAndTest,
|
|
246
|
+
proofProven: ProofProven,
|
|
247
|
+
proofUnresolved: ProofUnresolved,
|
|
248
|
+
proofMissing: ProofMissing,
|
|
249
|
+
proofNotApplicable: ProofNotApplicable
|
|
204
250
|
}.
|
|
205
251
|
|
|
206
252
|
req_row_fully_covered(Row) :-
|
|
@@ -221,10 +267,24 @@ req_row_missing_test(Row) :-
|
|
|
221
267
|
Row.gaps = [missing_test].
|
|
222
268
|
req_row_missing_both(Row) :-
|
|
223
269
|
Row.gaps = [missing_scenario_and_test].
|
|
270
|
+
req_row_proof_proven(Row) :-
|
|
271
|
+
Row.proofStatus == proven.
|
|
272
|
+
req_row_proof_unresolved(Row) :-
|
|
273
|
+
Row.proofStatus == unresolved.
|
|
274
|
+
req_row_proof_missing(Row) :-
|
|
275
|
+
Row.proofStatus == missing.
|
|
276
|
+
req_row_proof_not_applicable(Row) :-
|
|
277
|
+
Row.proofStatus == not_applicable.
|
|
224
278
|
|
|
225
279
|
symbol_row_fully_covered(Row) :-
|
|
226
280
|
CoverageStatus = Row.get(coverageStatus),
|
|
227
281
|
CoverageStatus = fully_covered.
|
|
282
|
+
symbol_row_uncovered(Row) :-
|
|
283
|
+
Row.coverageStatus == uncovered.
|
|
284
|
+
symbol_row_not_applicable(Row) :-
|
|
285
|
+
Row.coverageStatus == not_applicable.
|
|
286
|
+
symbol_row_mixed_role(Row) :-
|
|
287
|
+
Row.traceabilityRole == mixed.
|
|
228
288
|
|
|
229
289
|
count_distinct_targets(Relationship, Id, source, Count) :-
|
|
230
290
|
!,
|
|
@@ -235,7 +295,8 @@ count_distinct_targets(Relationship, Id, source, Count) :-
|
|
|
235
295
|
requirement_test_count(Id, Count) :-
|
|
236
296
|
findall(TestId, kb_relationship(verified_by, Id, TestId), Verified0),
|
|
237
297
|
findall(TestId, kb_relationship(validates, TestId, Id), Validates0),
|
|
238
|
-
|
|
298
|
+
findall(TestId, scenario_requirement_test(Id, TestId), ScenarioTests0),
|
|
299
|
+
append([Verified0, Validates0, ScenarioTests0], Combined0),
|
|
239
300
|
sort(Combined0, Combined),
|
|
240
301
|
length(Combined, Count).
|
|
241
302
|
|
|
@@ -381,6 +442,11 @@ count_direct_tests(Id, Count) :-
|
|
|
381
442
|
sort(TestIds0, TestIds),
|
|
382
443
|
length(TestIds, Count).
|
|
383
444
|
|
|
445
|
+
count_executable_tests(Id, Count) :-
|
|
446
|
+
findall(TestId, kb_relationship(executable_for, Id, TestId), TestIds0),
|
|
447
|
+
sort(TestIds0, TestIds),
|
|
448
|
+
length(TestIds, Count).
|
|
449
|
+
|
|
384
450
|
requirement_gap_list(Id, [Reason]) :-
|
|
385
451
|
coverage_gap(Id, Reason),
|
|
386
452
|
!.
|
|
@@ -399,11 +465,15 @@ requirement_coverage_state(_Id, _Props, [], false, not_applicable).
|
|
|
399
465
|
|
|
400
466
|
filter_req_coverage_rows(true, Rows, Rows).
|
|
401
467
|
filter_req_coverage_rows(false, Rows, Filtered) :-
|
|
402
|
-
exclude(
|
|
468
|
+
exclude(req_row_proof_passing, Rows, Filtered).
|
|
469
|
+
|
|
470
|
+
req_row_proof_passing(Row) :-
|
|
471
|
+
Status = Row.proofStatus,
|
|
472
|
+
memberchk(Status, [proven, not_applicable]).
|
|
403
473
|
|
|
404
474
|
filter_symbol_coverage_rows(true, Rows, Rows).
|
|
405
475
|
filter_symbol_coverage_rows(false, Rows, Filtered) :-
|
|
406
|
-
|
|
476
|
+
include(symbol_row_uncovered, Rows, Filtered).
|
|
407
477
|
|
|
408
478
|
entity_priority(Props, Priority) :-
|
|
409
479
|
( memberchk(priority=PriorityValue, Props)
|