kibi-core 0.13.0 → 0.14.1

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kibi-core",
3
- "version": "0.13.0",
3
+ "version": "0.14.1",
4
4
  "private": false,
5
5
  "description": "Core Prolog modules and RDF graph logic for Kibi",
6
6
  "type": "module",
@@ -65,6 +65,9 @@ entity_property(fact, predicate_arity, integer).
65
65
  entity_property(fact, argument_names, list).
66
66
  entity_property(fact, argument_types, list).
67
67
  entity_property(fact, argument_descriptions, list).
68
+ % Predicate-schema vocabularies are JSON objects serialized as strings.
69
+ entity_property(fact, argument_constants, string).
70
+ entity_property(fact, argument_aliases, string).
68
71
  entity_property(fact, aliases, list).
69
72
  entity_property(fact, examples, list).
70
73
  entity_property(fact, predicate_args, list).
@@ -17,6 +17,7 @@ relationship_type(publishes).
17
17
  relationship_type(consumes).
18
18
  relationship_type(relates_to).
19
19
  relationship_type(supersedes).
20
+ relationship_type(restates).
20
21
  relationship_type(constrains).
21
22
  relationship_type(requires_property).
22
23
  relationship_type(requires_predicate).
@@ -49,6 +50,13 @@ valid_relationship(requires_rule, req, fact).
49
50
  %% OldAdrId's status should be archived or deprecated as a consequence.
50
51
  valid_relationship(supersedes, adr, adr).
51
52
  valid_relationship(supersedes, req, req).
53
+
54
+ %% restates(+ReqId, +OtherReqId)
55
+ %% ReqId intentionally restates OtherReqId (for example a product requirement
56
+ %% echoed in a platform requirement). Both stay current; the pair is exempt
57
+ %% from domain-redundancy. Use supersedes when one replaces the other.
58
+ % implements REQ-kibi-restates-relationship
59
+ valid_relationship(restates, req, req).
52
60
  % escape hatch - allow any to any
53
61
  valid_relationship(relates_to, _, _).
54
62
 
@@ -105,6 +105,8 @@ is_fact_only_field(predicate_arity).
105
105
  is_fact_only_field(argument_names).
106
106
  is_fact_only_field(argument_types).
107
107
  is_fact_only_field(argument_descriptions).
108
+ is_fact_only_field(argument_constants).
109
+ is_fact_only_field(argument_aliases).
108
110
  is_fact_only_field(aliases).
109
111
  is_fact_only_field(examples).
110
112
  is_fact_only_field(predicate_args).
package/src/checks.pl CHANGED
@@ -27,15 +27,33 @@
27
27
  check_rule_verifiability/1,
28
28
  check_semantic_completeness/1,
29
29
  check_req_status_vocabulary/1,
30
+ check_entity_id_style/1,
31
+ check_domain_redundancy/1,
32
+ check_domain_implication/1,
33
+ check_subject_key_identity/1,
34
+ check_subject_key_shape/1,
35
+ check_ontology_quality/1,
30
36
  run_checks_json/0, % Entry point for JSON output
31
37
  violation_id_text/2 % Extract text from entity ID term (exported for testing)
32
38
  ]).
33
39
 
40
+ % Source contains non-ASCII text; do not depend on the host locale.
41
+ :- encoding(utf8).
42
+
34
43
  :- use_module(library(http/json)).
35
44
  :- use_module(library(http/json_convert)).
36
45
  :- use_module('kb.pl').
37
46
  :- use_module('../schema/entities.pl', [entity_type/1, required_property/2]).
38
47
  :- use_module('../schema/relationships.pl', [relationship_type/1]).
48
+ :- use_module('semantic_quality.pl', [
49
+ logical_ground_signature/2,
50
+ check_entity_id_style/1,
51
+ check_domain_redundancy/1,
52
+ check_domain_implication/1,
53
+ check_subject_key_identity/1,
54
+ check_subject_key_shape/1,
55
+ check_ontology_quality/1
56
+ ]).
39
57
  :- 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]).
40
58
  % GENERATED registry facts; single source is schema/rule-registry.json.
41
59
  :- use_module('rule_registry.pl', [known_rule/1]).
@@ -46,7 +64,7 @@ required_fields([id, title, status, created_at, updated_at, source]).
46
64
  % Relationship types to check for dangling references
47
65
  all_relationship_types([
48
66
  depends_on, verified_by, validates, specified_by,
49
- constrains, requires_property, requires_predicate, requires_rule, supersedes, relates_to
67
+ constrains, requires_property, requires_predicate, requires_rule, supersedes, restates, relates_to
50
68
  ]).
51
69
 
52
70
  %% check_all(-ViolationsDict)
@@ -72,6 +90,12 @@ check_all(ViolationsDict) :-
72
90
  check_semantic_completeness(SemanticCompleteness),
73
91
  check_req_status_vocabulary(ReqStatusVocabulary),
74
92
  check_proof_contract_symbols(ProofContractSymbols),
93
+ check_entity_id_style(EntityIdStyle),
94
+ check_domain_redundancy(DomainRedundancy),
95
+ check_domain_implication(DomainImplication),
96
+ check_subject_key_identity(SubjectKeyIdentity),
97
+ check_subject_key_shape(SubjectKeyShape),
98
+ check_ontology_quality(OntologyQuality),
75
99
  ViolationsDict = _{
76
100
  must_priority_coverage: MustPriority,
77
101
  symbol_coverage: SymbolCoverage,
@@ -90,7 +114,13 @@ check_all(ViolationsDict) :-
90
114
  rule_verifiability: RuleVerifiability,
91
115
  semantic_completeness: SemanticCompleteness,
92
116
  req_status_vocabulary: ReqStatusVocabulary,
93
- proof_contract_symbols: ProofContractSymbols
117
+ proof_contract_symbols: ProofContractSymbols,
118
+ entity_id_style: EntityIdStyle,
119
+ domain_redundancy: DomainRedundancy,
120
+ domain_implication: DomainImplication,
121
+ subject_key_identity: SubjectKeyIdentity,
122
+ subject_key_shape: SubjectKeyShape,
123
+ ontology_quality: OntologyQuality
94
124
  }.
95
125
 
96
126
  %% check_must_priority_coverage(-Violations)
@@ -134,7 +164,7 @@ symbol_coverage_violation(SymbolId, violation(
134
164
  'symbol-coverage',
135
165
  SymbolId,
136
166
  "Production symbol lacks qualifying requirement coverage.",
137
- "Add 'covered_by: TEST-xxx' for production coverage. If the requirement has specified_by a scenario, use verified_by(scenario,test) or validates(test,scenario). Direct verified_by(req,test) does not count when a scenario exists.",
167
+ "Add 'covered_by: TEST-<area>-<behavior>' for production coverage. If the requirement has specified_by a scenario, use verified_by(scenario,test) or validates(test,scenario). Direct verified_by(req,test) does not count when a scenario exists.",
138
168
  Source
139
169
  )) :-
140
170
  violation_source(SymbolId, symbol, Source).
@@ -189,13 +219,13 @@ symbol_traceability_violation(RequireAdr, violation(
189
219
  % Determine what is missing
190
220
  ( HasReq = false, HasAdr = false, RequireAdr = true ->
191
221
  Description = "Symbol has no direct requirement ownership and no ADR constraint.",
192
- Suggestion = "Add a direct 'implements: REQ-xxx' link for ownership, use 'covered_by' only for production coverage, use 'executable_for' only for executable test code, and add 'constrained_by: ADR-xxx' in symbols.yaml."
222
+ Suggestion = "Add a direct 'implements: REQ-<area>-<behavior>' link for ownership, use 'covered_by' only for production coverage, use 'executable_for' only for executable test code, and add 'constrained_by: ADR-<decision>' in symbols.yaml."
193
223
  ; HasReq = false ->
194
224
  Description = "Symbol has no direct requirement ownership.",
195
- Suggestion = "Add a direct 'implements: REQ-xxx' link for ownership. Use 'covered_by' for production coverage and 'executable_for' for executable test code identity."
225
+ Suggestion = "Add a direct 'implements: REQ-<area>-<behavior>' link for ownership. Use 'covered_by' for production coverage and 'executable_for' for executable test code identity."
196
226
  ; HasAdr = false ->
197
227
  Description = "Symbol has no ADR constraint.",
198
- Suggestion = "Add 'constrained_by: ADR-xxx' in symbols.yaml."
228
+ Suggestion = "Add 'constrained_by: ADR-<decision>' in symbols.yaml."
199
229
  ; fail % No violation
200
230
  ),
201
231
  violation_source(SymbolId, symbol, Source).
@@ -1307,32 +1337,6 @@ grounded_requirement_claim_fact(ReqId, ClaimKey, FactId) :-
1307
1337
  kb_relationship(requires_rule, ReqId, FactId),
1308
1338
  ground_fact_claim_key(FactId, rule, ClaimKey).
1309
1339
 
1310
- logical_ground_signature(FactId, predicate(Namespace, Name, Args, Polarity)) :-
1311
- kb:predicate_fact(FactId, Namespace, Name, Args, Polarity).
1312
- logical_ground_signature(
1313
- FactId,
1314
- property(Subject, Property, Operator, ValueType, Value, Unit, Scope, Polarity)
1315
- ) :-
1316
- kb:fact_property_tuple(
1317
- FactId,
1318
- Subject,
1319
- Property,
1320
- Operator,
1321
- ValueType,
1322
- Value,
1323
- Unit,
1324
- Scope,
1325
- Polarity
1326
- ).
1327
- logical_ground_signature(FactId, rule(SemanticKey)) :-
1328
- kb_entity(FactId, fact, Props),
1329
- memberchk(fact_kind=RawKind, Props),
1330
- normalize_term_atom(RawKind, rule),
1331
- ( memberchk(semantic_key=RawKey, Props)
1332
- -> normalize_term_atom(RawKey, SemanticKey)
1333
- ; memberchk(rule_hash=RawKey, Props), normalize_term_atom(RawKey, SemanticKey)
1334
- ).
1335
-
1336
1340
  ground_fact_claim_key(FactId, ExpectedKind, ClaimKey) :-
1337
1341
  kb_entity(FactId, fact, Props),
1338
1342
  memberchk(fact_kind=RawKind, Props),
@@ -1605,7 +1609,13 @@ check_selected_dispatch(Rules, _{
1605
1609
  rule_verifiability: RuleVerifiability,
1606
1610
  semantic_completeness: SemanticCompleteness,
1607
1611
  req_status_vocabulary: ReqStatusVocabulary,
1608
- proof_contract_symbols: ProofContractSymbols
1612
+ proof_contract_symbols: ProofContractSymbols,
1613
+ entity_id_style: EntityIdStyle,
1614
+ domain_redundancy: DomainRedundancy,
1615
+ domain_implication: DomainImplication,
1616
+ subject_key_identity: SubjectKeyIdentity,
1617
+ subject_key_shape: SubjectKeyShape,
1618
+ ontology_quality: OntologyQuality
1609
1619
  }) :-
1610
1620
  selected_rule(Rules, 'must-priority-coverage', check_must_priority_coverage, MustPriority),
1611
1621
  selected_rule(Rules, 'symbol-coverage', check_symbol_coverage, SymbolCoverage),
@@ -1624,7 +1634,13 @@ check_selected_dispatch(Rules, _{
1624
1634
  selected_rule(Rules, 'rule-verifiability', check_rule_verifiability, RuleVerifiability),
1625
1635
  selected_rule(Rules, 'semantic-completeness', check_semantic_completeness, SemanticCompleteness),
1626
1636
  selected_rule(Rules, 'req-status-vocabulary', check_req_status_vocabulary, ReqStatusVocabulary),
1627
- selected_rule(Rules, 'proof-contract-symbols', check_proof_contract_symbols, ProofContractSymbols).
1637
+ selected_rule(Rules, 'proof-contract-symbols', check_proof_contract_symbols, ProofContractSymbols),
1638
+ selected_rule(Rules, 'entity-id-style', check_entity_id_style, EntityIdStyle),
1639
+ selected_rule(Rules, 'domain-redundancy', check_domain_redundancy, DomainRedundancy),
1640
+ selected_rule(Rules, 'domain-implication', check_domain_implication, DomainImplication),
1641
+ selected_rule(Rules, 'subject-key-identity', check_subject_key_identity, SubjectKeyIdentity),
1642
+ selected_rule(Rules, 'subject-key-shape', check_subject_key_shape, SubjectKeyShape),
1643
+ selected_rule(Rules, 'ontology-quality', check_ontology_quality, OntologyQuality).
1628
1644
 
1629
1645
  selected_rule(Rules, Name, Goal, Violations) :-
1630
1646
  ( memberchk(Name, Rules)
@@ -1681,6 +1697,12 @@ check_all_with_options(ViolationsDict, RequireAdr) :-
1681
1697
  check_semantic_completeness(SemanticCompleteness),
1682
1698
  check_req_status_vocabulary(ReqStatusVocabulary),
1683
1699
  check_proof_contract_symbols(ProofContractSymbols),
1700
+ check_entity_id_style(EntityIdStyle),
1701
+ check_domain_redundancy(DomainRedundancy),
1702
+ check_domain_implication(DomainImplication),
1703
+ check_subject_key_identity(SubjectKeyIdentity),
1704
+ check_subject_key_shape(SubjectKeyShape),
1705
+ check_ontology_quality(OntologyQuality),
1684
1706
  ViolationsDict = _{
1685
1707
  must_priority_coverage: MustPriority,
1686
1708
  symbol_coverage: SymbolCoverage,
@@ -1699,7 +1721,13 @@ check_all_with_options(ViolationsDict, RequireAdr) :-
1699
1721
  rule_verifiability: RuleVerifiability,
1700
1722
  semantic_completeness: SemanticCompleteness,
1701
1723
  req_status_vocabulary: ReqStatusVocabulary,
1702
- proof_contract_symbols: ProofContractSymbols
1724
+ proof_contract_symbols: ProofContractSymbols,
1725
+ entity_id_style: EntityIdStyle,
1726
+ domain_redundancy: DomainRedundancy,
1727
+ domain_implication: DomainImplication,
1728
+ subject_key_identity: SubjectKeyIdentity,
1729
+ subject_key_shape: SubjectKeyShape,
1730
+ ontology_quality: OntologyQuality
1703
1731
  }.
1704
1732
 
1705
1733
  %% violations_dict_to_json(+ViolationsDict, -JsonDict)
@@ -1718,7 +1746,12 @@ pairs_to_json_pairs([Key-Violations|Rest], [Key-JsonViolations|JsonRest]) :-
1718
1746
 
1719
1747
  %% violation_to_json(+Violation, -JsonDict)
1720
1748
  % Converts a violation(Rule, EntityId, Description, Suggestion, Source) term
1721
- % to a JSON-compatible dict.
1749
+ % to a JSON-compatible dict. violation/6 carries precomputed JSON-safe
1750
+ % evidence (witnesses) as its sixth argument.
1751
+ violation_to_json(violation(Rule, EntityId, Description, Suggestion, Source, Evidence), JsonDict) :-
1752
+ !,
1753
+ violation_term_to_dict(violation(Rule, EntityId, Description, Suggestion, Source), BaseDict),
1754
+ put_dict(evidence, BaseDict, Evidence, JsonDict).
1722
1755
  violation_to_json(Violation, JsonDict) :-
1723
1756
  violation_term_to_dict(Violation, BaseDict),
1724
1757
  ( contradiction_violation_witnesses(Violation, Witnesses),
package/src/discovery.pl CHANGED
@@ -14,12 +14,16 @@
14
14
  graph_expand_json/8
15
15
  ]).
16
16
 
17
+ % Source contains non-ASCII text; do not depend on the host locale.
18
+ :- encoding(utf8).
19
+
17
20
  :- use_module(library(http/json)).
18
21
  :- use_module(library(aggregate)).
19
22
  :- use_module(library(apply)).
20
23
  :- use_module(library(lists)).
21
24
  :- use_module(library(pairs)).
22
25
  :- use_module('kb.pl').
26
+ :- use_module(library(semweb/rdf_db), [rdf_generation/1]).
23
27
  :- use_module('requirement_proof.pl', [requirement_proof_context/1, requirement_proof_context/4, requirement_proof_context/6, requirement_proof/4]).
24
28
  :- use_module('status.pl', [status_meta_dict/1]).
25
29
  :- use_module('../schema/relationships.pl', [relationship_type/1]).
@@ -153,11 +157,11 @@ coverage_evidence_receipt_gap(Gap) :-
153
157
  coverage_report_json(By, Tags, IncludePassing, BindingMode, TestBindings, IncludeTransitive, Limit, Offset, VerificationSnapshot, CheckedAt, MaxAgeSeconds, JsonString) :-
154
158
  % Binding mode applies to requirement rows only; other By values keep
155
159
  % their snapshot-era arity so type/symbol reports are unchanged.
156
- ( By == req
157
- -> coverage_rows(req, Tags, IncludePassing, IncludeTransitive, VerificationSnapshot, CheckedAt, MaxAgeSeconds, BindingMode, TestBindings, Rows0, Summary)
158
- ; coverage_rows(By, Tags, IncludePassing, IncludeTransitive, VerificationSnapshot, CheckedAt, MaxAgeSeconds, Rows0, Summary)
159
- ),
160
- sort_dict_rows(Rows0, SortedRows),
160
+ memo_sorted_coverage(
161
+ binding(By, Tags, IncludePassing, BindingMode, TestBindings, IncludeTransitive, VerificationSnapshot, CheckedAt, MaxAgeSeconds),
162
+ binding_coverage_rows(By, Tags, IncludePassing, BindingMode, TestBindings, IncludeTransitive, VerificationSnapshot, CheckedAt, MaxAgeSeconds),
163
+ SortedRows,
164
+ Summary),
161
165
  paginate_rows(SortedRows, Offset, Limit, Rows),
162
166
  status_meta_dict(Meta),
163
167
  Response = _{summary: Summary, rows: Rows, meta: Meta},
@@ -171,13 +175,44 @@ coverage_report_json(By, Tags, IncludePassing, BindingMode, TestBindings, Includ
171
175
  % reason — without diffing full exports by hand. The summary is always
172
176
  % computed over all rows so filtered responses keep whole-KB counts.
173
177
  coverage_report_json(By, Tags, IncludePassing, StatusFilter, IncludeTransitive, Limit, Offset, VerificationSnapshot, CheckedAt, MaxAgeSeconds, JsonString) :-
174
- status_filtered_coverage_rows(By, Tags, IncludePassing, StatusFilter, IncludeTransitive, VerificationSnapshot, CheckedAt, MaxAgeSeconds, MatchedRows0, Summary),
175
- sort_dict_rows(MatchedRows0, SortedRows),
178
+ memo_sorted_coverage(
179
+ status(By, Tags, IncludePassing, StatusFilter, IncludeTransitive, VerificationSnapshot, CheckedAt, MaxAgeSeconds),
180
+ status_filtered_coverage_rows(By, Tags, IncludePassing, StatusFilter, IncludeTransitive, VerificationSnapshot, CheckedAt, MaxAgeSeconds),
181
+ SortedRows,
182
+ Summary),
176
183
  paginate_rows(SortedRows, Offset, Limit, Rows),
177
184
  status_meta_dict(Meta),
178
185
  Response = _{summary: Summary, rows: Rows, meta: Meta},
179
186
  dict_json_string(Response, JsonString).
180
187
 
188
+ binding_coverage_rows(req, Tags, IncludePassing, BindingMode, TestBindings, IncludeTransitive, VerificationSnapshot, CheckedAt, MaxAgeSeconds, Rows, Summary) :-
189
+ !,
190
+ coverage_rows(req, Tags, IncludePassing, IncludeTransitive, VerificationSnapshot, CheckedAt, MaxAgeSeconds, BindingMode, TestBindings, Rows, Summary).
191
+ binding_coverage_rows(By, Tags, IncludePassing, _BindingMode, _TestBindings, IncludeTransitive, VerificationSnapshot, CheckedAt, MaxAgeSeconds, Rows, Summary) :-
192
+ coverage_rows(By, Tags, IncludePassing, IncludeTransitive, VerificationSnapshot, CheckedAt, MaxAgeSeconds, Rows, Summary).
193
+
194
+ :- dynamic coverage_memo/3.
195
+
196
+ %% memo_sorted_coverage(+Args, :Compute, -SortedRows, -Summary)
197
+ % Large coverage reports are fetched in bounded pages so no single answer
198
+ % approaches the engine's output cap. Every page would otherwise recompute
199
+ % the whole report; keep the most recent sorted result for the exact
200
+ % arguments, graph, and RDF generation. Coverage reads only the store and its
201
+ % arguments, so the memo is invalidated by any store change.
202
+ memo_sorted_coverage(Args, Compute, SortedRows, Summary) :-
203
+ (kb:kb_graph(Graph) -> true ; Graph = none),
204
+ rdf_generation(Generation),
205
+ Key = key(Args, Graph, Generation),
206
+ ( coverage_memo(Stored, StoredRows, StoredSummary),
207
+ Stored =@= Key
208
+ -> SortedRows = StoredRows,
209
+ Summary = StoredSummary
210
+ ; call(Compute, Rows0, Summary),
211
+ sort_dict_rows(Rows0, SortedRows),
212
+ retractall(coverage_memo(_, _, _)),
213
+ assertz(coverage_memo(Key, SortedRows, Summary))
214
+ ).
215
+
181
216
  status_filtered_coverage_rows(By, Tags, IncludePassing, [], IncludeTransitive, VerificationSnapshot, CheckedAt, MaxAgeSeconds, Rows, Summary) :-
182
217
  !,
183
218
  coverage_rows(By, Tags, IncludePassing, IncludeTransitive, VerificationSnapshot, CheckedAt, MaxAgeSeconds, Rows, Summary).