kibi-core 0.12.0 → 0.14.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 -5
- package/schema/entities.pl +3 -0
- package/schema/relationships.pl +8 -0
- package/schema/validation.pl +2 -0
- package/src/checks.pl +199 -36
- package/src/discovery.pl +97 -7
- package/src/kb.pl +322 -13
- package/src/requirement_proof.pl +337 -7
- package/src/rule_registry.pl +46 -0
- package/src/semantic_quality.pl +750 -0
- package/src/status.pl +41 -15
- package/src/units.pl +146 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kibi-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Core Prolog modules and RDF graph logic for Kibi",
|
|
6
6
|
"type": "module",
|
|
@@ -8,10 +8,7 @@
|
|
|
8
8
|
"node": ">=18"
|
|
9
9
|
},
|
|
10
10
|
"main": "./src/kb.pl",
|
|
11
|
-
"files": [
|
|
12
|
-
"src/**/*.pl",
|
|
13
|
-
"schema/**/*.pl"
|
|
14
|
-
],
|
|
11
|
+
"files": ["src/**/*.pl", "schema/**/*.pl"],
|
|
15
12
|
"license": "AGPL-3.0-or-later",
|
|
16
13
|
"author": "Piotr Franczyk",
|
|
17
14
|
"repository": {
|
package/schema/entities.pl
CHANGED
|
@@ -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).
|
package/schema/relationships.pl
CHANGED
|
@@ -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
|
|
package/schema/validation.pl
CHANGED
|
@@ -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
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
check_must_priority_coverage/1, % Returns list of must-priority violations
|
|
11
11
|
check_symbol_coverage/1, % Returns list of uncovered symbols
|
|
12
12
|
check_symbol_traceability/2, % Returns list of symbols lacking requirement traceability (ReqAdr option)
|
|
13
|
+
check_proof_contract_symbols/1, % Returns unresolved or unexpected required_proofs symbol issues
|
|
13
14
|
check_no_dangling_refs/1, % Returns list of dangling ref violations
|
|
14
15
|
check_no_cycles/1, % Returns list of cycle violations
|
|
15
16
|
check_required_fields/1, % Returns list of missing required field violations
|
|
@@ -26,15 +27,33 @@
|
|
|
26
27
|
check_rule_verifiability/1,
|
|
27
28
|
check_semantic_completeness/1,
|
|
28
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,
|
|
29
36
|
run_checks_json/0, % Entry point for JSON output
|
|
30
37
|
violation_id_text/2 % Extract text from entity ID term (exported for testing)
|
|
31
38
|
]).
|
|
32
39
|
|
|
40
|
+
% Source contains non-ASCII text; do not depend on the host locale.
|
|
41
|
+
:- encoding(utf8).
|
|
42
|
+
|
|
33
43
|
:- use_module(library(http/json)).
|
|
34
44
|
:- use_module(library(http/json_convert)).
|
|
35
45
|
:- use_module('kb.pl').
|
|
36
46
|
:- use_module('../schema/entities.pl', [entity_type/1, required_property/2]).
|
|
37
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
|
+
]).
|
|
38
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]).
|
|
39
58
|
% GENERATED registry facts; single source is schema/rule-registry.json.
|
|
40
59
|
:- use_module('rule_registry.pl', [known_rule/1]).
|
|
@@ -45,7 +64,7 @@ required_fields([id, title, status, created_at, updated_at, source]).
|
|
|
45
64
|
% Relationship types to check for dangling references
|
|
46
65
|
all_relationship_types([
|
|
47
66
|
depends_on, verified_by, validates, specified_by,
|
|
48
|
-
constrains, requires_property, requires_predicate, requires_rule, supersedes, relates_to
|
|
67
|
+
constrains, requires_property, requires_predicate, requires_rule, supersedes, restates, relates_to
|
|
49
68
|
]).
|
|
50
69
|
|
|
51
70
|
%% check_all(-ViolationsDict)
|
|
@@ -70,6 +89,13 @@ check_all(ViolationsDict) :-
|
|
|
70
89
|
check_rule_verifiability(RuleVerifiability),
|
|
71
90
|
check_semantic_completeness(SemanticCompleteness),
|
|
72
91
|
check_req_status_vocabulary(ReqStatusVocabulary),
|
|
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),
|
|
73
99
|
ViolationsDict = _{
|
|
74
100
|
must_priority_coverage: MustPriority,
|
|
75
101
|
symbol_coverage: SymbolCoverage,
|
|
@@ -87,7 +113,14 @@ check_all(ViolationsDict) :-
|
|
|
87
113
|
rule_safety: RuleSafety,
|
|
88
114
|
rule_verifiability: RuleVerifiability,
|
|
89
115
|
semantic_completeness: SemanticCompleteness,
|
|
90
|
-
req_status_vocabulary: ReqStatusVocabulary
|
|
116
|
+
req_status_vocabulary: ReqStatusVocabulary,
|
|
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
|
|
91
124
|
}.
|
|
92
125
|
|
|
93
126
|
%% check_must_priority_coverage(-Violations)
|
|
@@ -131,7 +164,7 @@ symbol_coverage_violation(SymbolId, violation(
|
|
|
131
164
|
'symbol-coverage',
|
|
132
165
|
SymbolId,
|
|
133
166
|
"Production symbol lacks qualifying requirement coverage.",
|
|
134
|
-
"Add 'covered_by: TEST
|
|
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.",
|
|
135
168
|
Source
|
|
136
169
|
)) :-
|
|
137
170
|
violation_source(SymbolId, symbol, Source).
|
|
@@ -148,6 +181,17 @@ check_symbol_traceability(RequireAdr, Violations) :-
|
|
|
148
181
|
),
|
|
149
182
|
sort(Violations0, Violations).
|
|
150
183
|
|
|
184
|
+
symbol_traceability_violation(_RequireAdr, violation(
|
|
185
|
+
'symbol-traceability',
|
|
186
|
+
SymbolId,
|
|
187
|
+
"Symbol mixes executable_for test identity with production implements or covered_by.",
|
|
188
|
+
"Split the symbol: keep executable_for on the test-code symbol and use a separate production symbol for implements/covered_by.",
|
|
189
|
+
Source
|
|
190
|
+
)) :-
|
|
191
|
+
kb_entity(SymbolId, symbol, _),
|
|
192
|
+
mixed_role_symbol(SymbolId),
|
|
193
|
+
violation_source(SymbolId, symbol, Source).
|
|
194
|
+
|
|
151
195
|
symbol_traceability_violation(RequireAdr, violation(
|
|
152
196
|
'symbol-traceability',
|
|
153
197
|
SymbolId,
|
|
@@ -175,17 +219,129 @@ symbol_traceability_violation(RequireAdr, violation(
|
|
|
175
219
|
% Determine what is missing
|
|
176
220
|
( HasReq = false, HasAdr = false, RequireAdr = true ->
|
|
177
221
|
Description = "Symbol has no direct requirement ownership and no ADR constraint.",
|
|
178
|
-
Suggestion = "Add a direct 'implements: REQ
|
|
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."
|
|
179
223
|
; HasReq = false ->
|
|
180
224
|
Description = "Symbol has no direct requirement ownership.",
|
|
181
|
-
Suggestion = "Add a direct 'implements: REQ
|
|
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."
|
|
182
226
|
; HasAdr = false ->
|
|
183
227
|
Description = "Symbol has no ADR constraint.",
|
|
184
|
-
Suggestion = "Add 'constrained_by: ADR
|
|
228
|
+
Suggestion = "Add 'constrained_by: ADR-<decision>' in symbols.yaml."
|
|
185
229
|
; fail % No violation
|
|
186
230
|
),
|
|
187
231
|
violation_source(SymbolId, symbol, Source).
|
|
188
232
|
|
|
233
|
+
%% check_proof_contract_symbols(-Violations)
|
|
234
|
+
% Advisory integrity for proof_contract.required_proofs and optional
|
|
235
|
+
% proof_bindings.source_file. Kibi does not infer TEST names from filenames.
|
|
236
|
+
check_proof_contract_symbols(Violations) :-
|
|
237
|
+
findall(Violation, proof_contract_symbol_violation(Violation), Violations0),
|
|
238
|
+
sort(Violations0, Violations).
|
|
239
|
+
|
|
240
|
+
proof_contract_symbol_violation(violation(
|
|
241
|
+
'proof-contract-symbols',
|
|
242
|
+
TestId,
|
|
243
|
+
Description,
|
|
244
|
+
Suggestion,
|
|
245
|
+
Source
|
|
246
|
+
)) :-
|
|
247
|
+
kb_entity(TestId, test, Props),
|
|
248
|
+
memberchk(proof_contract=RawContract, Props),
|
|
249
|
+
parse_json_object(RawContract, Contract),
|
|
250
|
+
required_proof_symbol_id(Contract, SymbolId),
|
|
251
|
+
\+ kb_entity(SymbolId, symbol, _),
|
|
252
|
+
format(string(Description), "proof_contract.required_proofs names unresolved symbol ~w", [SymbolId]),
|
|
253
|
+
Suggestion = "Point required_proofs.symbol_id at an existing symbol entity; Kibi does not infer TEST names from source filenames.",
|
|
254
|
+
violation_source(TestId, test, Source).
|
|
255
|
+
|
|
256
|
+
proof_contract_symbol_violation(violation(
|
|
257
|
+
'proof-contract-symbols',
|
|
258
|
+
TestId,
|
|
259
|
+
Description,
|
|
260
|
+
Suggestion,
|
|
261
|
+
Source
|
|
262
|
+
)) :-
|
|
263
|
+
kb_entity(TestId, test, Props),
|
|
264
|
+
memberchk(proof_contract=RawContract, Props),
|
|
265
|
+
parse_json_object(RawContract, Contract),
|
|
266
|
+
required_proof_symbol_id(Contract, SymbolId),
|
|
267
|
+
kb_entity(SymbolId, symbol, SymbolProps),
|
|
268
|
+
memberchk(symbol_role=RawRole, SymbolProps),
|
|
269
|
+
normalize_term_atom(RawRole, 'type-shape'),
|
|
270
|
+
format(string(Description), "proof_contract.required_proofs names type-shape symbol ~w; required proofs must not target structural contracts", [SymbolId]),
|
|
271
|
+
Suggestion = "Required proofs identify executable proof symbols. Use a production or executable-test symbol, not a type-shape contract.",
|
|
272
|
+
violation_source(TestId, test, Source).
|
|
273
|
+
|
|
274
|
+
proof_contract_symbol_violation(violation(
|
|
275
|
+
'proof-contract-symbols',
|
|
276
|
+
TestId,
|
|
277
|
+
Description,
|
|
278
|
+
Suggestion,
|
|
279
|
+
Source
|
|
280
|
+
)) :-
|
|
281
|
+
kb_entity(TestId, test, Props),
|
|
282
|
+
memberchk(proof_bindings=RawBindings, Props),
|
|
283
|
+
parse_json_list(RawBindings, Bindings),
|
|
284
|
+
member(Binding, Bindings),
|
|
285
|
+
is_dict(Binding),
|
|
286
|
+
(get_dict(source_file, Binding, RawBindingSource) ; get_dict(sourceFile, Binding, RawBindingSource)),
|
|
287
|
+
normalize_term_atom(RawBindingSource, BindingSource),
|
|
288
|
+
BindingSource \= '',
|
|
289
|
+
(get_dict(symbol_id, Binding, RawSymbolId) ; get_dict(symbolId, Binding, RawSymbolId)),
|
|
290
|
+
normalize_term_atom(RawSymbolId, SymbolId),
|
|
291
|
+
SymbolId \= '',
|
|
292
|
+
kb_entity(SymbolId, symbol, SymbolProps),
|
|
293
|
+
( memberchk(sourceFile=RawSymbolSource, SymbolProps)
|
|
294
|
+
-> normalize_term_atom(RawSymbolSource, SymbolSource)
|
|
295
|
+
; SymbolSource = ''
|
|
296
|
+
),
|
|
297
|
+
SymbolSource \= BindingSource,
|
|
298
|
+
format(string(Description), "proof_bindings.source_file for ~w is ~w but the symbol sourceFile is ~w", [SymbolId, BindingSource, SymbolSource]),
|
|
299
|
+
Suggestion = "Align proof_bindings.source_file with the symbol's sourceFile, or omit source_file. Kibi does not infer TEST names from filenames.",
|
|
300
|
+
violation_source(TestId, test, Source).
|
|
301
|
+
|
|
302
|
+
required_proof_symbol_id(Contract, SymbolId) :-
|
|
303
|
+
( get_dict(required_proofs, Contract, Raw)
|
|
304
|
+
; get_dict('required_proofs', Contract, Raw)
|
|
305
|
+
),
|
|
306
|
+
parse_json_list(Raw, Entries),
|
|
307
|
+
member(Entry, Entries),
|
|
308
|
+
( is_dict(Entry),
|
|
309
|
+
(get_dict(symbol_id, Entry, RawId) ; get_dict(symbolId, Entry, RawId))
|
|
310
|
+
; is_list(Entry),
|
|
311
|
+
memberchk(symbol_id=RawId, Entry)
|
|
312
|
+
),
|
|
313
|
+
normalize_term_atom(RawId, SymbolId),
|
|
314
|
+
SymbolId \= ''.
|
|
315
|
+
|
|
316
|
+
parse_json_object(^^(Value, _), Dict) :-
|
|
317
|
+
!,
|
|
318
|
+
parse_json_object(Value, Dict).
|
|
319
|
+
parse_json_object(literal(type(_, Value)), Dict) :-
|
|
320
|
+
!,
|
|
321
|
+
parse_json_object(Value, Dict).
|
|
322
|
+
parse_json_object(literal(Value), Dict) :-
|
|
323
|
+
!,
|
|
324
|
+
parse_json_object(Value, Dict).
|
|
325
|
+
parse_json_object(Dict, Dict) :-
|
|
326
|
+
is_dict(Dict),
|
|
327
|
+
!.
|
|
328
|
+
parse_json_object(Raw, Dict) :-
|
|
329
|
+
(atom(Raw) ; string(Raw)),
|
|
330
|
+
catch(atom_json_dict(Raw, Dict, [value_string_as(string)]), _, fail),
|
|
331
|
+
is_dict(Dict).
|
|
332
|
+
|
|
333
|
+
parse_json_list(Raw, Entries) :-
|
|
334
|
+
is_list(Raw),
|
|
335
|
+
!,
|
|
336
|
+
Entries = Raw.
|
|
337
|
+
parse_json_list(^^(Value, _), Entries) :-
|
|
338
|
+
!,
|
|
339
|
+
parse_json_list(Value, Entries).
|
|
340
|
+
parse_json_list(Raw, Entries) :-
|
|
341
|
+
(atom(Raw) ; string(Raw)),
|
|
342
|
+
catch(atom_json_dict(Raw, Entries, [value_string_as(string)]), _, fail),
|
|
343
|
+
is_list(Entries).
|
|
344
|
+
|
|
189
345
|
%% check_no_dangling_refs(-Violations)
|
|
190
346
|
% Finds all relationships referencing non-existent entities.
|
|
191
347
|
check_no_dangling_refs(Violations) :-
|
|
@@ -1181,32 +1337,6 @@ grounded_requirement_claim_fact(ReqId, ClaimKey, FactId) :-
|
|
|
1181
1337
|
kb_relationship(requires_rule, ReqId, FactId),
|
|
1182
1338
|
ground_fact_claim_key(FactId, rule, ClaimKey).
|
|
1183
1339
|
|
|
1184
|
-
logical_ground_signature(FactId, predicate(Namespace, Name, Args, Polarity)) :-
|
|
1185
|
-
kb:predicate_fact(FactId, Namespace, Name, Args, Polarity).
|
|
1186
|
-
logical_ground_signature(
|
|
1187
|
-
FactId,
|
|
1188
|
-
property(Subject, Property, Operator, ValueType, Value, Unit, Scope, Polarity)
|
|
1189
|
-
) :-
|
|
1190
|
-
kb:fact_property_tuple(
|
|
1191
|
-
FactId,
|
|
1192
|
-
Subject,
|
|
1193
|
-
Property,
|
|
1194
|
-
Operator,
|
|
1195
|
-
ValueType,
|
|
1196
|
-
Value,
|
|
1197
|
-
Unit,
|
|
1198
|
-
Scope,
|
|
1199
|
-
Polarity
|
|
1200
|
-
).
|
|
1201
|
-
logical_ground_signature(FactId, rule(SemanticKey)) :-
|
|
1202
|
-
kb_entity(FactId, fact, Props),
|
|
1203
|
-
memberchk(fact_kind=RawKind, Props),
|
|
1204
|
-
normalize_term_atom(RawKind, rule),
|
|
1205
|
-
( memberchk(semantic_key=RawKey, Props)
|
|
1206
|
-
-> normalize_term_atom(RawKey, SemanticKey)
|
|
1207
|
-
; memberchk(rule_hash=RawKey, Props), normalize_term_atom(RawKey, SemanticKey)
|
|
1208
|
-
).
|
|
1209
|
-
|
|
1210
1340
|
ground_fact_claim_key(FactId, ExpectedKind, ClaimKey) :-
|
|
1211
1341
|
kb_entity(FactId, fact, Props),
|
|
1212
1342
|
memberchk(fact_kind=RawKind, Props),
|
|
@@ -1478,7 +1608,14 @@ check_selected_dispatch(Rules, _{
|
|
|
1478
1608
|
rule_safety: RuleSafety,
|
|
1479
1609
|
rule_verifiability: RuleVerifiability,
|
|
1480
1610
|
semantic_completeness: SemanticCompleteness,
|
|
1481
|
-
req_status_vocabulary: ReqStatusVocabulary
|
|
1611
|
+
req_status_vocabulary: ReqStatusVocabulary,
|
|
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
|
|
1482
1619
|
}) :-
|
|
1483
1620
|
selected_rule(Rules, 'must-priority-coverage', check_must_priority_coverage, MustPriority),
|
|
1484
1621
|
selected_rule(Rules, 'symbol-coverage', check_symbol_coverage, SymbolCoverage),
|
|
@@ -1496,7 +1633,14 @@ check_selected_dispatch(Rules, _{
|
|
|
1496
1633
|
selected_rule(Rules, 'rule-safety', check_rule_safety, RuleSafety),
|
|
1497
1634
|
selected_rule(Rules, 'rule-verifiability', check_rule_verifiability, RuleVerifiability),
|
|
1498
1635
|
selected_rule(Rules, 'semantic-completeness', check_semantic_completeness, SemanticCompleteness),
|
|
1499
|
-
selected_rule(Rules, 'req-status-vocabulary', check_req_status_vocabulary, ReqStatusVocabulary)
|
|
1636
|
+
selected_rule(Rules, 'req-status-vocabulary', check_req_status_vocabulary, ReqStatusVocabulary),
|
|
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).
|
|
1500
1644
|
|
|
1501
1645
|
selected_rule(Rules, Name, Goal, Violations) :-
|
|
1502
1646
|
( memberchk(Name, Rules)
|
|
@@ -1552,6 +1696,13 @@ check_all_with_options(ViolationsDict, RequireAdr) :-
|
|
|
1552
1696
|
check_rule_verifiability(RuleVerifiability),
|
|
1553
1697
|
check_semantic_completeness(SemanticCompleteness),
|
|
1554
1698
|
check_req_status_vocabulary(ReqStatusVocabulary),
|
|
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),
|
|
1555
1706
|
ViolationsDict = _{
|
|
1556
1707
|
must_priority_coverage: MustPriority,
|
|
1557
1708
|
symbol_coverage: SymbolCoverage,
|
|
@@ -1569,7 +1720,14 @@ check_all_with_options(ViolationsDict, RequireAdr) :-
|
|
|
1569
1720
|
rule_safety: RuleSafety,
|
|
1570
1721
|
rule_verifiability: RuleVerifiability,
|
|
1571
1722
|
semantic_completeness: SemanticCompleteness,
|
|
1572
|
-
req_status_vocabulary: ReqStatusVocabulary
|
|
1723
|
+
req_status_vocabulary: ReqStatusVocabulary,
|
|
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
|
|
1573
1731
|
}.
|
|
1574
1732
|
|
|
1575
1733
|
%% violations_dict_to_json(+ViolationsDict, -JsonDict)
|
|
@@ -1588,7 +1746,12 @@ pairs_to_json_pairs([Key-Violations|Rest], [Key-JsonViolations|JsonRest]) :-
|
|
|
1588
1746
|
|
|
1589
1747
|
%% violation_to_json(+Violation, -JsonDict)
|
|
1590
1748
|
% Converts a violation(Rule, EntityId, Description, Suggestion, Source) term
|
|
1591
|
-
% 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).
|
|
1592
1755
|
violation_to_json(Violation, JsonDict) :-
|
|
1593
1756
|
violation_term_to_dict(Violation, BaseDict),
|
|
1594
1757
|
( contradiction_violation_witnesses(Violation, Witnesses),
|
package/src/discovery.pl
CHANGED
|
@@ -8,15 +8,22 @@
|
|
|
8
8
|
coverage_report_json/11,
|
|
9
9
|
coverage_report_json/12,
|
|
10
10
|
coverage_evidence_json/5,
|
|
11
|
+
requirement_proof_json/5,
|
|
12
|
+
requirement_proof_json/7,
|
|
13
|
+
symbol_proof_json/7,
|
|
11
14
|
graph_expand_json/8
|
|
12
15
|
]).
|
|
13
16
|
|
|
17
|
+
% Source contains non-ASCII text; do not depend on the host locale.
|
|
18
|
+
:- encoding(utf8).
|
|
19
|
+
|
|
14
20
|
:- use_module(library(http/json)).
|
|
15
21
|
:- use_module(library(aggregate)).
|
|
16
22
|
:- use_module(library(apply)).
|
|
17
23
|
:- use_module(library(lists)).
|
|
18
24
|
:- use_module(library(pairs)).
|
|
19
25
|
:- use_module('kb.pl').
|
|
26
|
+
:- use_module(library(semweb/rdf_db), [rdf_generation/1]).
|
|
20
27
|
:- use_module('requirement_proof.pl', [requirement_proof_context/1, requirement_proof_context/4, requirement_proof_context/6, requirement_proof/4]).
|
|
21
28
|
:- use_module('status.pl', [status_meta_dict/1]).
|
|
22
29
|
:- use_module('../schema/relationships.pl', [relationship_type/1]).
|
|
@@ -40,6 +47,58 @@ coverage_report_json(By, Tags, IncludePassing, IncludeTransitive, Limit, Offset,
|
|
|
40
47
|
coverage_report_json(By, Tags, IncludePassing, IncludeTransitive, Limit, Offset, VerificationSnapshot, CheckedAt, MaxAgeSeconds, JsonString) :-
|
|
41
48
|
coverage_report_json(By, Tags, IncludePassing, strict_snapshot, _{}, IncludeTransitive, Limit, Offset, VerificationSnapshot, CheckedAt, MaxAgeSeconds, JsonString).
|
|
42
49
|
|
|
50
|
+
requirement_proof_json(ReqId, VerificationSnapshot, CheckedAt, MaxAgeSeconds, JsonString) :-
|
|
51
|
+
requirement_proof_json(ReqId, strict_snapshot, _{}, VerificationSnapshot, CheckedAt, MaxAgeSeconds, JsonString).
|
|
52
|
+
|
|
53
|
+
requirement_proof_json(ReqId, BindingMode, TestBindings, VerificationSnapshot, CheckedAt, MaxAgeSeconds, JsonString) :-
|
|
54
|
+
requirement_proof_context(VerificationSnapshot, CheckedAt, MaxAgeSeconds, BindingMode, TestBindings, Context),
|
|
55
|
+
kb_entity(ReqId, req, Props),
|
|
56
|
+
requirement_proof(ReqId, Props, Context, Proof),
|
|
57
|
+
put_dict(id, Proof, ReqId, ProofWithId),
|
|
58
|
+
dict_json_string(ProofWithId, JsonString).
|
|
59
|
+
|
|
60
|
+
symbol_proof_json(SymbolId, BindingMode, TestBindings, VerificationSnapshot, CheckedAt, MaxAgeSeconds, JsonString) :-
|
|
61
|
+
requirement_proof_context(VerificationSnapshot, CheckedAt, MaxAgeSeconds, BindingMode, TestBindings, Context),
|
|
62
|
+
kb_entity(SymbolId, symbol, _),
|
|
63
|
+
findall(ReqId,
|
|
64
|
+
(kb_relationship(implements, SymbolId, ReqId), kb_entity(ReqId, req, _)),
|
|
65
|
+
ReqIds0),
|
|
66
|
+
sort(ReqIds0, ReqIds),
|
|
67
|
+
maplist(requirement_proof_with_id(Context), ReqIds, Proofs),
|
|
68
|
+
symbol_explain_role(SymbolId, Role),
|
|
69
|
+
(kb:executable_test_symbol(SymbolId) -> ExecutableTest = true ; ExecutableTest = false),
|
|
70
|
+
findall(TestId, kb_relationship(executable_for, SymbolId, TestId), ExecutableFor0),
|
|
71
|
+
sort(ExecutableFor0, ExecutableFor),
|
|
72
|
+
findall(TestId, kb_relationship(covered_by, SymbolId, TestId), CoveredBy0),
|
|
73
|
+
sort(CoveredBy0, CoveredBy),
|
|
74
|
+
Response = _{
|
|
75
|
+
id: SymbolId,
|
|
76
|
+
symbolId: SymbolId,
|
|
77
|
+
role: Role,
|
|
78
|
+
executableTest: ExecutableTest,
|
|
79
|
+
implementingRequirements: ReqIds,
|
|
80
|
+
executableFor: ExecutableFor,
|
|
81
|
+
coveredBy: CoveredBy,
|
|
82
|
+
proofs: Proofs
|
|
83
|
+
},
|
|
84
|
+
dict_json_string(Response, JsonString).
|
|
85
|
+
|
|
86
|
+
requirement_proof_with_id(Context, ReqId, ProofWithId) :-
|
|
87
|
+
kb_entity(ReqId, req, Props),
|
|
88
|
+
requirement_proof(ReqId, Props, Context, Proof),
|
|
89
|
+
put_dict(id, Proof, ReqId, ProofWithId).
|
|
90
|
+
|
|
91
|
+
symbol_explain_role(SymbolId, executable_test) :-
|
|
92
|
+
kb:executable_test_symbol(SymbolId),
|
|
93
|
+
!.
|
|
94
|
+
symbol_explain_role(SymbolId, Role) :-
|
|
95
|
+
kb_entity(SymbolId, symbol, Props),
|
|
96
|
+
memberchk(symbol_role=RawRole, Props),
|
|
97
|
+
kb:normalize_term_atom(RawRole, Role),
|
|
98
|
+
Role \= '',
|
|
99
|
+
!.
|
|
100
|
+
symbol_explain_role(_SymbolId, unknown).
|
|
101
|
+
|
|
43
102
|
% Slim per-requirement proof evidence for quality diagnostics (W1 push-down).
|
|
44
103
|
% Projects only the fields the quality diagnostics consume - proof status,
|
|
45
104
|
% the passing-e2e stage summary, and receipt-related gap codes - instead of
|
|
@@ -98,11 +157,11 @@ coverage_evidence_receipt_gap(Gap) :-
|
|
|
98
157
|
coverage_report_json(By, Tags, IncludePassing, BindingMode, TestBindings, IncludeTransitive, Limit, Offset, VerificationSnapshot, CheckedAt, MaxAgeSeconds, JsonString) :-
|
|
99
158
|
% Binding mode applies to requirement rows only; other By values keep
|
|
100
159
|
% their snapshot-era arity so type/symbol reports are unchanged.
|
|
101
|
-
(
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
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),
|
|
106
165
|
paginate_rows(SortedRows, Offset, Limit, Rows),
|
|
107
166
|
status_meta_dict(Meta),
|
|
108
167
|
Response = _{summary: Summary, rows: Rows, meta: Meta},
|
|
@@ -116,13 +175,44 @@ coverage_report_json(By, Tags, IncludePassing, BindingMode, TestBindings, Includ
|
|
|
116
175
|
% reason — without diffing full exports by hand. The summary is always
|
|
117
176
|
% computed over all rows so filtered responses keep whole-KB counts.
|
|
118
177
|
coverage_report_json(By, Tags, IncludePassing, StatusFilter, IncludeTransitive, Limit, Offset, VerificationSnapshot, CheckedAt, MaxAgeSeconds, JsonString) :-
|
|
119
|
-
|
|
120
|
-
|
|
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),
|
|
121
183
|
paginate_rows(SortedRows, Offset, Limit, Rows),
|
|
122
184
|
status_meta_dict(Meta),
|
|
123
185
|
Response = _{summary: Summary, rows: Rows, meta: Meta},
|
|
124
186
|
dict_json_string(Response, JsonString).
|
|
125
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
|
+
|
|
126
216
|
status_filtered_coverage_rows(By, Tags, IncludePassing, [], IncludeTransitive, VerificationSnapshot, CheckedAt, MaxAgeSeconds, Rows, Summary) :-
|
|
127
217
|
!,
|
|
128
218
|
coverage_rows(By, Tags, IncludePassing, IncludeTransitive, VerificationSnapshot, CheckedAt, MaxAgeSeconds, Rows, Summary).
|