kibi-core 0.24.0 → 0.27.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 +1 -1
- package/schema/validation.pl +3 -1
- package/src/certificate_check.pl +38 -4
- package/src/certificates.pl +10 -2
- package/src/checks.pl +178 -17
- package/src/claim_obligations.pl +371 -28
- package/src/code_certificates.pl +243 -4
- package/src/discovery.pl +65 -23
- package/src/intervals.pl +35 -14
- package/src/kb.pl +337 -38
- package/src/kb_consistency.pl +15 -0
- package/src/logic_ir.pl +130 -161
- package/src/model_check.pl +776 -0
- package/src/requirement_proof.pl +101 -13
- package/src/rule_registry.pl +1 -1
- package/src/status.pl +16 -0
- package/src/units.pl +1 -3
package/src/kb.pl
CHANGED
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
kb_indexed_sources/1,
|
|
28
28
|
kb_query_entities/8,
|
|
29
29
|
kb_search_entities/6,
|
|
30
|
+
kb_relationship_edges/3,
|
|
30
31
|
kb_list_search_candidates/5,
|
|
31
32
|
kb_list_search_candidates/6,
|
|
32
33
|
kb_entity_ids/1,
|
|
@@ -34,6 +35,7 @@
|
|
|
34
35
|
kb_entity/3,
|
|
35
36
|
kb_entities_by_source/2,
|
|
36
37
|
kb_assert_relationship/4,
|
|
38
|
+
kb_assert_relationships_reporting/2,
|
|
37
39
|
kb_query_proof_contracts/4,
|
|
38
40
|
kb_assert_relationship_no_audit/4,
|
|
39
41
|
kb_log_relationship_upsert/4,
|
|
@@ -72,6 +74,7 @@
|
|
|
72
74
|
check_req_contradiction/1,
|
|
73
75
|
normalize_term_atom/2,
|
|
74
76
|
normalize_term_atom_list/2,
|
|
77
|
+
normalize_literal_atom_list/2,
|
|
75
78
|
changeset/4, % Export for testing
|
|
76
79
|
kb_uri/1
|
|
77
80
|
]).
|
|
@@ -85,8 +88,10 @@
|
|
|
85
88
|
:- use_module(library(sha), [sha_hash/3, hash_atom/2]).
|
|
86
89
|
:- use_module(library(http/json)).
|
|
87
90
|
:- use_module(library(aggregate), [aggregate_all/3]).
|
|
88
|
-
:- use_module(library(lists), [sum_list/2]).
|
|
91
|
+
:- use_module(library(lists), [sum_list/2, subtract/3, intersection/3]).
|
|
89
92
|
:- use_module(library(ordsets)).
|
|
93
|
+
:- use_module(library(assoc), [list_to_assoc/2, get_assoc/3]).
|
|
94
|
+
:- use_module(library(pairs), [group_pairs_by_key/2, pairs_values/2]).
|
|
90
95
|
:- use_module(library(solution_sequences), [distinct/1, limit/2, offset/2, order_by/2]).
|
|
91
96
|
:- use_module('../schema/entities.pl', [entity_type/1, entity_property/3, required_property/2]).
|
|
92
97
|
:- use_module('../schema/relationships.pl', [relationship_type/1, valid_relationship/3]).
|
|
@@ -139,6 +144,17 @@ kb_uri('urn-kibi:').
|
|
|
139
144
|
:- dynamic kb_entity_props_memo/2.
|
|
140
145
|
:- dynamic kb_entity_props_memo_key/1.
|
|
141
146
|
:- dynamic kb_decision_signature_memo/2.
|
|
147
|
+
:- dynamic kb_req_predicate_memo/2.
|
|
148
|
+
:- dynamic kb_req_subject_memo/2.
|
|
149
|
+
:- dynamic kb_contradiction_witness_memo/2.
|
|
150
|
+
:- dynamic kb_consistency_memo/3.
|
|
151
|
+
:- dynamic kb_predicate_schema_memo/2.
|
|
152
|
+
:- dynamic kb_requirement_proof_memo/3.
|
|
153
|
+
:- dynamic kb_requirement_ladder_memo/3.
|
|
154
|
+
:- dynamic kb_status_source_index_memo/2.
|
|
155
|
+
:- dynamic kb_scenario_feasibility_memo/3.
|
|
156
|
+
:- dynamic kb_search_ids_memo/4.
|
|
157
|
+
:- dynamic kb_query_ids_memo/3.
|
|
142
158
|
:- dynamic kb_index_verified_key/1.
|
|
143
159
|
:- dynamic entity/4. % Support legacy .pl file format (Type, Id, Title, Props)
|
|
144
160
|
|
|
@@ -366,6 +382,17 @@ kb_detach :-
|
|
|
366
382
|
retractall(kb_entity_props_memo(_, _)),
|
|
367
383
|
retractall(kb_entity_props_memo_key(_)),
|
|
368
384
|
retractall(kb_decision_signature_memo(_, _)),
|
|
385
|
+
retractall(kb_req_predicate_memo(_, _)),
|
|
386
|
+
retractall(kb_req_subject_memo(_, _)),
|
|
387
|
+
retractall(kb_contradiction_witness_memo(_, _)),
|
|
388
|
+
retractall(kb_consistency_memo(_, _, _)),
|
|
389
|
+
retractall(kb_predicate_schema_memo(_, _)),
|
|
390
|
+
retractall(kb_requirement_proof_memo(_, _, _)),
|
|
391
|
+
retractall(kb_requirement_ladder_memo(_, _, _)),
|
|
392
|
+
retractall(kb_status_source_index_memo(_, _)),
|
|
393
|
+
retractall(kb_scenario_feasibility_memo(_, _, _)),
|
|
394
|
+
retractall(kb_search_ids_memo(_, _, _, _)),
|
|
395
|
+
retractall(kb_query_ids_memo(_, _, _)),
|
|
369
396
|
retractall(kb_index_entity(_, _)),
|
|
370
397
|
retractall(kb_index_type(_, _)),
|
|
371
398
|
retractall(kb_index_tag(_, _)),
|
|
@@ -1697,10 +1724,7 @@ kb_query_entities(TypeFilter, IdFilter, Tags, SourceFilter, Limit, Offset, Rows,
|
|
|
1697
1724
|
Offset >= 0,
|
|
1698
1725
|
is_list(Tags),
|
|
1699
1726
|
kb_ensure_indexes,
|
|
1700
|
-
|
|
1701
|
-
indexed_entity_match(TypeFilter, IdFilter, Tags, SourceFilter, Id),
|
|
1702
|
-
RawIds),
|
|
1703
|
-
sort(RawIds, Ids),
|
|
1727
|
+
query_index_ids(TypeFilter, IdFilter, Tags, SourceFilter, Ids),
|
|
1704
1728
|
length(Ids, Count),
|
|
1705
1729
|
drop_index_ids(Offset, Ids, Remaining),
|
|
1706
1730
|
take_index_ids(Limit, Remaining, PageIds),
|
|
@@ -1709,6 +1733,25 @@ kb_query_entities(TypeFilter, IdFilter, Tags, SourceFilter, Limit, Offset, Rows,
|
|
|
1709
1733
|
kb_entity(Id, Type, Props) ),
|
|
1710
1734
|
Rows).
|
|
1711
1735
|
|
|
1736
|
+
% Clients read a large result in bounded pages, each page a separate query,
|
|
1737
|
+
% so the ids matched for the last filter are kept for its next pages instead
|
|
1738
|
+
% of matching and sorting the whole index again per page. Keyed like the
|
|
1739
|
+
% search memo, so any write starts a fresh match.
|
|
1740
|
+
% The caller ensured the indexes, so the entity count is known.
|
|
1741
|
+
query_index_ids(TypeFilter, IdFilter, Tags, SourceFilter, Ids) :-
|
|
1742
|
+
kb_index_entity_count(IndexCount),
|
|
1743
|
+
index_verification_key(IndexCount, Key),
|
|
1744
|
+
Filters = filters(TypeFilter, IdFilter, Tags, SourceFilter),
|
|
1745
|
+
( kb_query_ids_memo(Key, Filters, Cached)
|
|
1746
|
+
-> Ids = Cached
|
|
1747
|
+
; findall(Id,
|
|
1748
|
+
indexed_entity_match(TypeFilter, IdFilter, Tags, SourceFilter, Id),
|
|
1749
|
+
RawIds),
|
|
1750
|
+
sort(RawIds, Ids),
|
|
1751
|
+
retractall(kb_query_ids_memo(_, _, _)),
|
|
1752
|
+
assertz(kb_query_ids_memo(Key, Filters, Ids))
|
|
1753
|
+
).
|
|
1754
|
+
|
|
1712
1755
|
%% kb_query_proof_contracts(+Id, +Limit, +Offset, -Rows)
|
|
1713
1756
|
% Page proof-campaign candidates while reading only contract and binding RDF
|
|
1714
1757
|
% properties. In particular, do not call kb_entity/3 here: that materializes
|
|
@@ -1847,19 +1890,37 @@ kb_search_entities(TypeFilter, Query, Limit, Offset, Rows, Count) :-
|
|
|
1847
1890
|
kb_ensure_indexes,
|
|
1848
1891
|
index_text_tokens(Query, QueryTokens),
|
|
1849
1892
|
QueryTokens \= [],
|
|
1850
|
-
|
|
1851
|
-
( kb_index_entity(Id, _),
|
|
1852
|
-
(TypeFilter == none -> true ; kb_index_type(TypeFilter, Id)),
|
|
1853
|
-
( indexed_markdown_source(Id)
|
|
1854
|
-
; forall(member(QueryToken, QueryTokens),
|
|
1855
|
-
indexed_token_match(QueryToken, Id)) ) ),
|
|
1856
|
-
RawIds),
|
|
1857
|
-
sort(RawIds, Ids),
|
|
1893
|
+
search_index_ids(TypeFilter, QueryTokens, Ids),
|
|
1858
1894
|
length(Ids, Count),
|
|
1859
1895
|
drop_index_ids(Offset, Ids, Remaining),
|
|
1860
1896
|
take_index_ids(Limit, Remaining, PageIds),
|
|
1861
1897
|
search_candidate_rows(PageIds, Rows).
|
|
1862
1898
|
|
|
1899
|
+
% Callers read a large candidate set page by page, each page a separate
|
|
1900
|
+
% query, so the ids matched for the last query are kept for its next pages
|
|
1901
|
+
% instead of scanning the token index again. The memo is keyed like the index
|
|
1902
|
+
% verification (graph, RDF generation and index size), so any write starts a
|
|
1903
|
+
% fresh scan.
|
|
1904
|
+
search_index_ids(TypeFilter, QueryTokens, Ids) :-
|
|
1905
|
+
( kb_index_entity_count(IndexCount)
|
|
1906
|
+
-> true
|
|
1907
|
+
; IndexCount = none
|
|
1908
|
+
),
|
|
1909
|
+
index_verification_key(IndexCount, Key),
|
|
1910
|
+
( kb_search_ids_memo(Key, TypeFilter, QueryTokens, Cached)
|
|
1911
|
+
-> Ids = Cached
|
|
1912
|
+
; findall(Id,
|
|
1913
|
+
( kb_index_entity(Id, _),
|
|
1914
|
+
(TypeFilter == none -> true ; kb_index_type(TypeFilter, Id)),
|
|
1915
|
+
( indexed_markdown_source(Id)
|
|
1916
|
+
; forall(member(QueryToken, QueryTokens),
|
|
1917
|
+
indexed_token_match(QueryToken, Id)) ) ),
|
|
1918
|
+
RawIds),
|
|
1919
|
+
sort(RawIds, Ids),
|
|
1920
|
+
retractall(kb_search_ids_memo(_, _, _, _)),
|
|
1921
|
+
assertz(kb_search_ids_memo(Key, TypeFilter, QueryTokens, Ids))
|
|
1922
|
+
).
|
|
1923
|
+
|
|
1863
1924
|
%% kb_list_search_candidates(+Type, +Limit, +Offset, -Rows, -Count)
|
|
1864
1925
|
% Page every indexed entity (optionally of one type) as projected search
|
|
1865
1926
|
% candidate rows, ordered by id. Semantic discovery uses it when no lexical
|
|
@@ -1977,6 +2038,25 @@ kb_assert_relationship(RelType, FromId, ToId, Metadata) :-
|
|
|
1977
2038
|
kb_log_relationship_upsert(RelType, FromId, ToId, Metadata)
|
|
1978
2039
|
).
|
|
1979
2040
|
|
|
2041
|
+
%% kb_assert_relationships_reporting(+Relationships, -FailedPositions)
|
|
2042
|
+
% Assert each rel(Type, From, To) of Relationships with kb_assert_relationship/4
|
|
2043
|
+
% (empty metadata), in order, keeping every one that succeeds. FailedPositions
|
|
2044
|
+
% lists the 1-based positions of those that failed or raised; their writes are
|
|
2045
|
+
% rolled back with their own transaction. Sync uses it after a batch
|
|
2046
|
+
% transaction fails, so one dangling link no longer sends the whole batch
|
|
2047
|
+
% through one round trip per relationship.
|
|
2048
|
+
kb_assert_relationships_reporting(Relationships, FailedPositions) :-
|
|
2049
|
+
foldl(assert_relationship_reporting, Relationships, 1-[], _-Failed0),
|
|
2050
|
+
reverse(Failed0, FailedPositions).
|
|
2051
|
+
|
|
2052
|
+
assert_relationship_reporting(rel(RelType, FromId, ToId), Position-Failed0, Next-Failed) :-
|
|
2053
|
+
Next is Position + 1,
|
|
2054
|
+
% mutalog-disable-next-line literal: an unbound metadata entry is ignored like an empty list
|
|
2055
|
+
( catch(kb_assert_relationship(RelType, FromId, ToId, []), _, fail)
|
|
2056
|
+
-> Failed = Failed0
|
|
2057
|
+
; Failed = [Position|Failed0]
|
|
2058
|
+
).
|
|
2059
|
+
|
|
1980
2060
|
%% kb_assert_relationship_no_audit(+Type, +From, +To, +Metadata)
|
|
1981
2061
|
% Assert a relationship RDF payload without recording audit side effects.
|
|
1982
2062
|
% Used by write-gated MCP transactions so failed contradiction checks do not
|
|
@@ -2180,6 +2260,37 @@ kb_relationship_with_reluri(RelURI, Graph, FromId, ToId) :-
|
|
|
2180
2260
|
entity_uri_to_id(FromURI, FromId),
|
|
2181
2261
|
entity_uri_to_id(ToURI, ToId).
|
|
2182
2262
|
|
|
2263
|
+
%% kb_relationship_edges(+Types, +Ids, -Edges)
|
|
2264
|
+
% implements REQ-mcp-search-discovery
|
|
2265
|
+
% Every [Type, From, To] relationship of one of Types that starts or ends at
|
|
2266
|
+
% one of Ids (a set), in the order and with the multiplicity of
|
|
2267
|
+
% findall([T,F,O], (member(T, Types), kb_relationship(T, F, O),
|
|
2268
|
+
% (member(F, Ids) ; member(O, Ids))), Edges)
|
|
2269
|
+
% Search reads the graph around its results this way. Ends are compared as
|
|
2270
|
+
% URIs, so only the matching edges are converted back to ids rather than
|
|
2271
|
+
% every edge of every type.
|
|
2272
|
+
kb_relationship_edges(Types, Ids, Edges) :-
|
|
2273
|
+
kb_graph(Graph),
|
|
2274
|
+
kb_uri(BaseURI),
|
|
2275
|
+
atom_concat(BaseURI, 'entity/', Prefix),
|
|
2276
|
+
findall(URI,
|
|
2277
|
+
( member(Id, Ids),
|
|
2278
|
+
( atom_concat(Prefix, Id, URI)
|
|
2279
|
+
; atom_concat('kb:entity/', Id, URI)
|
|
2280
|
+
) ),
|
|
2281
|
+
URIs0),
|
|
2282
|
+
sort(URIs0, URIs),
|
|
2283
|
+
findall([Type, From, To],
|
|
2284
|
+
( member(Type, Types),
|
|
2285
|
+
atom_concat(BaseURI, Type, RelURI),
|
|
2286
|
+
rdf(FromURI, RelURI, ToURI, Graph),
|
|
2287
|
+
( ord_memberchk(FromURI, URIs)
|
|
2288
|
+
; ord_memberchk(ToURI, URIs)
|
|
2289
|
+
),
|
|
2290
|
+
entity_uri_to_id(FromURI, From),
|
|
2291
|
+
entity_uri_to_id(ToURI, To) ),
|
|
2292
|
+
Edges).
|
|
2293
|
+
|
|
2183
2294
|
% Helper predicates
|
|
2184
2295
|
|
|
2185
2296
|
%% entity_id_to_uri(+Id, -URI)
|
|
@@ -2638,13 +2749,7 @@ contradicting_reqs(ReqA, ReqB, Reason) :-
|
|
|
2638
2749
|
% contradiction. This remains deliberately syntactic: differently shaped
|
|
2639
2750
|
% terms are never treated as equivalent merely because their prose is similar.
|
|
2640
2751
|
req_conflict_witness(ReqA, ReqB, Witness) :-
|
|
2641
|
-
|
|
2642
|
-
current_req(ReqB),
|
|
2643
|
-
ReqA @< ReqB,
|
|
2644
|
-
kb_relationship(constrains, ReqA, SubjectFactA),
|
|
2645
|
-
kb_relationship(constrains, ReqB, SubjectFactB),
|
|
2646
|
-
fact_subject_key(SubjectFactA, SubjectKey),
|
|
2647
|
-
fact_subject_key(SubjectFactB, SubjectKey),
|
|
2752
|
+
subject_conflict_pair(ReqA, ReqB, SubjectKey),
|
|
2648
2753
|
effective_req_property_fact(ReqA, SubjectKey, FactA, PropertyKey, OpA, ValTypeA, ValA, UnitA, ScopeA, PolarityA, ValidFromA, ValidToA),
|
|
2649
2754
|
effective_req_property_fact(ReqB, SubjectKey, FactB, PropertyKey, OpB, ValTypeB, ValB, UnitB, ScopeB, PolarityB, ValidFromB, ValidToB),
|
|
2650
2755
|
FactA \= FactB,
|
|
@@ -2672,11 +2777,7 @@ req_conflict_witness(ReqA, ReqB, Witness) :-
|
|
|
2672
2777
|
right: Right
|
|
2673
2778
|
}.
|
|
2674
2779
|
req_conflict_witness(ReqA, ReqB, Witness) :-
|
|
2675
|
-
|
|
2676
|
-
current_req(ReqB),
|
|
2677
|
-
ReqA @< ReqB,
|
|
2678
|
-
effective_req_predicate(ReqA, FactA, Namespace, Name, Args, PolarityA),
|
|
2679
|
-
effective_req_predicate(ReqB, FactB, Namespace, Name, Args, PolarityB),
|
|
2780
|
+
predicate_conflict_pair(ReqA, ReqB, FactA, FactB, Namespace, Name, Args, PolarityA, PolarityB),
|
|
2680
2781
|
FactA \= FactB,
|
|
2681
2782
|
opposite_predicate_polarity(PolarityA, PolarityB),
|
|
2682
2783
|
atomic_list_concat(Args, ',', ArgsText),
|
|
@@ -2756,6 +2857,143 @@ req_conflict_witness(ReqA, ReqB, Witness) :-
|
|
|
2756
2857
|
right: Right
|
|
2757
2858
|
}.
|
|
2758
2859
|
|
|
2860
|
+
% Cost: the search answer layer and the upsert contradiction check ask for
|
|
2861
|
+
% the witnesses of one requirement with the other side unbound, and kb_check
|
|
2862
|
+
% asks for every pair. Enumerating every current requirement as the partner
|
|
2863
|
+
% and only then reading either side's facts cost a full requirement scan per
|
|
2864
|
+
% requirement (quadratic for kb_check). The pairings below return the same
|
|
2865
|
+
% solutions in the same order as that enumeration: partners come from an
|
|
2866
|
+
% index of the current requirements constraining each subject or stating
|
|
2867
|
+
% each predicate, visited in the order current_req/1 enumerates them, so a
|
|
2868
|
+
% requirement without such facts has no partners at all.
|
|
2869
|
+
subject_conflict_pair(ReqA, ReqB, SubjectKey) :-
|
|
2870
|
+
% The mode tests only pick the cheapest route: every route gives the
|
|
2871
|
+
% same solutions in the same order for the arguments it is given.
|
|
2872
|
+
% mutalog-disable-next-line negate,goal_true,goal_fail: routing only; the later branches give the same pairs
|
|
2873
|
+
( nonvar(ReqA), nonvar(ReqB)
|
|
2874
|
+
-> current_req(ReqA),
|
|
2875
|
+
current_req(ReqB),
|
|
2876
|
+
ReqA @< ReqB
|
|
2877
|
+
% mutalog-disable-next-line negate,goal_true,goal_fail: routing only; the last branch gives the same pairs
|
|
2878
|
+
; nonvar(ReqB)
|
|
2879
|
+
-> current_req(ReqB),
|
|
2880
|
+
req_subject_partners(ReqB, before, Partners),
|
|
2881
|
+
member(ReqA, Partners)
|
|
2882
|
+
; current_req(ReqA),
|
|
2883
|
+
req_subject_partners(ReqA, after, Partners),
|
|
2884
|
+
member(ReqB, Partners)
|
|
2885
|
+
),
|
|
2886
|
+
kb_relationship(constrains, ReqA, SubjectFactA),
|
|
2887
|
+
kb_relationship(constrains, ReqB, SubjectFactB),
|
|
2888
|
+
fact_subject_key(SubjectFactA, SubjectKey),
|
|
2889
|
+
fact_subject_key(SubjectFactB, SubjectKey).
|
|
2890
|
+
|
|
2891
|
+
%% req_subject_partners(+ReqId, +Side, -Partners)
|
|
2892
|
+
% Current requirements constraining a subject that ReqId constrains, in
|
|
2893
|
+
% current_req/1 order: those after ReqId in standard order (Side = after) or
|
|
2894
|
+
% before it (Side = before).
|
|
2895
|
+
req_subject_partners(ReqId, Side, Partners) :-
|
|
2896
|
+
req_subject_index(Index),
|
|
2897
|
+
findall(Position-Partner,
|
|
2898
|
+
( req_subject_fact_key(ReqId, SubjectKey),
|
|
2899
|
+
get_assoc(SubjectKey, Index, Entries),
|
|
2900
|
+
member(Position-Partner, Entries),
|
|
2901
|
+
subject_partner_side(Side, ReqId, Partner) ),
|
|
2902
|
+
Partners0),
|
|
2903
|
+
sort(Partners0, Partners1),
|
|
2904
|
+
pairs_values(Partners1, Partners).
|
|
2905
|
+
|
|
2906
|
+
% mutalog-disable-next-line cut_insert: the comparison has at most one solution
|
|
2907
|
+
subject_partner_side(after, ReqId, Partner) :- ReqId @< Partner.
|
|
2908
|
+
subject_partner_side(before, ReqId, Partner) :- Partner @< ReqId.
|
|
2909
|
+
|
|
2910
|
+
req_subject_fact_key(ReqId, SubjectKey) :-
|
|
2911
|
+
% mutalog-disable-next-line goal_true: extra partners are dropped by the pair's own constrains check
|
|
2912
|
+
kb_relationship(constrains, ReqId, SubjectFactId),
|
|
2913
|
+
fact_subject_key(SubjectFactId, SubjectKey).
|
|
2914
|
+
|
|
2915
|
+
% Memoized per KB generation, like the predicate index below.
|
|
2916
|
+
req_subject_index(Index) :-
|
|
2917
|
+
index_verification_key(req_subjects, Key),
|
|
2918
|
+
( kb_req_subject_memo(Key, Cached)
|
|
2919
|
+
-> Index = Cached
|
|
2920
|
+
; findall(ReqId, current_req(ReqId), ReqIds),
|
|
2921
|
+
findall(SubjectKey-(Position-ReqId),
|
|
2922
|
+
( nth1(Position, ReqIds, ReqId),
|
|
2923
|
+
req_subject_fact_key(ReqId, SubjectKey) ),
|
|
2924
|
+
Pairs0),
|
|
2925
|
+
sort(Pairs0, Pairs),
|
|
2926
|
+
group_pairs_by_key(Pairs, Grouped),
|
|
2927
|
+
list_to_assoc(Grouped, Computed),
|
|
2928
|
+
retractall(kb_req_subject_memo(_, _)),
|
|
2929
|
+
assertz(kb_req_subject_memo(Key, Computed)),
|
|
2930
|
+
Index = Computed
|
|
2931
|
+
).
|
|
2932
|
+
|
|
2933
|
+
predicate_conflict_pair(ReqA, ReqB, FactA, FactB, Namespace, Name, Args, PolarityA, PolarityB) :-
|
|
2934
|
+
( nonvar(ReqA), var(ReqB)
|
|
2935
|
+
-> current_req(ReqA),
|
|
2936
|
+
findall(Position-Partner,
|
|
2937
|
+
( effective_req_predicate(ReqA, _, PartnerNs, PartnerName, PartnerArgs, _),
|
|
2938
|
+
current_req_predicate(PartnerNs-PartnerName-PartnerArgs, Position, Partner),
|
|
2939
|
+
ReqA @< Partner ),
|
|
2940
|
+
Partners0),
|
|
2941
|
+
sort(Partners0, Partners),
|
|
2942
|
+
member(_-ReqB, Partners)
|
|
2943
|
+
; nonvar(ReqB), var(ReqA)
|
|
2944
|
+
-> current_req(ReqB),
|
|
2945
|
+
findall(Position-Partner,
|
|
2946
|
+
( effective_req_predicate(ReqB, _, PartnerNs, PartnerName, PartnerArgs, _),
|
|
2947
|
+
current_req_predicate(PartnerNs-PartnerName-PartnerArgs, Position, Partner),
|
|
2948
|
+
Partner @< ReqB ),
|
|
2949
|
+
Partners0),
|
|
2950
|
+
sort(Partners0, Partners),
|
|
2951
|
+
member(_-ReqA, Partners)
|
|
2952
|
+
% mutalog-disable-next-line negate,goal_true,goal_fail: routing only; the last branch gives the same pairs
|
|
2953
|
+
; nonvar(ReqA), nonvar(ReqB)
|
|
2954
|
+
-> current_req(ReqA),
|
|
2955
|
+
current_req(ReqB),
|
|
2956
|
+
ReqA @< ReqB
|
|
2957
|
+
; current_req(ReqA),
|
|
2958
|
+
findall(Position-Partner,
|
|
2959
|
+
( effective_req_predicate(ReqA, _, PartnerNs, PartnerName, PartnerArgs, _),
|
|
2960
|
+
current_req_predicate(PartnerNs-PartnerName-PartnerArgs, Position, Partner),
|
|
2961
|
+
ReqA @< Partner ),
|
|
2962
|
+
Partners0),
|
|
2963
|
+
sort(Partners0, Partners),
|
|
2964
|
+
member(_-ReqB, Partners)
|
|
2965
|
+
),
|
|
2966
|
+
effective_req_predicate(ReqA, FactA, Namespace, Name, Args, PolarityA),
|
|
2967
|
+
effective_req_predicate(ReqB, FactB, Namespace, Name, Args, PolarityB).
|
|
2968
|
+
|
|
2969
|
+
%% current_req_predicate(+Signature, -Position, -ReqId)
|
|
2970
|
+
% A current requirement stating a predicate fact with Signature
|
|
2971
|
+
% (Namespace-Name-Args), and its position in current_req/1 order.
|
|
2972
|
+
current_req_predicate(Signature, Position, ReqId) :-
|
|
2973
|
+
req_predicate_index(Index),
|
|
2974
|
+
get_assoc(Signature, Index, Entries),
|
|
2975
|
+
member(Position-ReqId, Entries).
|
|
2976
|
+
|
|
2977
|
+
% Memoized per KB generation, like the decision signatures: reading a
|
|
2978
|
+
% predicate fact decodes its entity, and every lookup would otherwise read
|
|
2979
|
+
% the predicate facts of every requirement.
|
|
2980
|
+
req_predicate_index(Index) :-
|
|
2981
|
+
index_verification_key(req_predicates, Key),
|
|
2982
|
+
( kb_req_predicate_memo(Key, Cached)
|
|
2983
|
+
-> Index = Cached
|
|
2984
|
+
; findall(ReqId, current_req(ReqId), ReqIds),
|
|
2985
|
+
findall((Namespace-Name-Args)-(Position-ReqId),
|
|
2986
|
+
( nth1(Position, ReqIds, ReqId),
|
|
2987
|
+
effective_req_predicate(ReqId, _, Namespace, Name, Args, _) ),
|
|
2988
|
+
Pairs0),
|
|
2989
|
+
sort(Pairs0, Pairs),
|
|
2990
|
+
group_pairs_by_key(Pairs, Grouped),
|
|
2991
|
+
list_to_assoc(Grouped, Computed),
|
|
2992
|
+
retractall(kb_req_predicate_memo(_, _)),
|
|
2993
|
+
assertz(kb_req_predicate_memo(Key, Computed)),
|
|
2994
|
+
Index = Computed
|
|
2995
|
+
).
|
|
2996
|
+
|
|
2759
2997
|
%% kb_domain_contradiction_pairs_examined(-Count)
|
|
2760
2998
|
% implements REQ-search-answer-hygiene
|
|
2761
2999
|
% How many distinct pairs of requirement facts the domain-contradictions
|
|
@@ -2771,22 +3009,12 @@ kb_domain_contradiction_pairs_examined(Count) :-
|
|
|
2771
3009
|
length(Pairs, Count).
|
|
2772
3010
|
|
|
2773
3011
|
domain_contradiction_candidate_pair(FactA, FactB) :-
|
|
2774
|
-
|
|
2775
|
-
current_req(ReqB),
|
|
2776
|
-
ReqA @< ReqB,
|
|
2777
|
-
kb_relationship(constrains, ReqA, SubjectFactA),
|
|
2778
|
-
kb_relationship(constrains, ReqB, SubjectFactB),
|
|
2779
|
-
fact_subject_key(SubjectFactA, SubjectKey),
|
|
2780
|
-
fact_subject_key(SubjectFactB, SubjectKey),
|
|
3012
|
+
subject_conflict_pair(ReqA, ReqB, SubjectKey),
|
|
2781
3013
|
effective_req_property_fact(ReqA, SubjectKey, FactA, PropertyKey, _, _, _, _, _, _, _, _),
|
|
2782
3014
|
effective_req_property_fact(ReqB, SubjectKey, FactB, PropertyKey, _, _, _, _, _, _, _, _),
|
|
2783
3015
|
FactA \= FactB.
|
|
2784
3016
|
domain_contradiction_candidate_pair(FactA, FactB) :-
|
|
2785
|
-
|
|
2786
|
-
current_req(ReqB),
|
|
2787
|
-
ReqA @< ReqB,
|
|
2788
|
-
effective_req_predicate(ReqA, FactA, Namespace, Name, Args, _),
|
|
2789
|
-
effective_req_predicate(ReqB, FactB, Namespace, Name, Args, _),
|
|
3017
|
+
predicate_conflict_pair(_ReqA, _ReqB, FactA, FactB, _Namespace, _Name, _Args, _, _),
|
|
2790
3018
|
FactA \= FactB.
|
|
2791
3019
|
domain_contradiction_candidate_pair(FactA, FactB) :-
|
|
2792
3020
|
decision_signatures(Signatures),
|
|
@@ -3022,7 +3250,32 @@ comparison_quantity(ValType, Value, Unit, Type, CanonValue, CanonUnit) :-
|
|
|
3022
3250
|
|
|
3023
3251
|
%% predicate_schema(+FactId, -Namespace, -Name, -Arity, -ArgumentNames, -ArgumentTypes)
|
|
3024
3252
|
% Read one project-local ontology predicate schema fact.
|
|
3253
|
+
% Finding the schemas of a predicate scans every fact, and contradiction
|
|
3254
|
+
% witnesses, decision signatures and key lookups ask for them per candidate,
|
|
3255
|
+
% so the schema rows are memoized per KB generation (outside RDF
|
|
3256
|
+
% transactions), in the order the scan finds them.
|
|
3257
|
+
predicate_schema(FactId, Namespace, Name, Arity, ArgumentNames, ArgumentTypes) :-
|
|
3258
|
+
var(FactId),
|
|
3259
|
+
\+ rdf_active_transaction(_),
|
|
3260
|
+
!,
|
|
3261
|
+
predicate_schema_rows(Rows),
|
|
3262
|
+
member(schema(FactId, Namespace, Name, Arity, ArgumentNames, ArgumentTypes), Rows).
|
|
3025
3263
|
predicate_schema(FactId, Namespace, Name, Arity, ArgumentNames, ArgumentTypes) :-
|
|
3264
|
+
predicate_schema_scan(FactId, Namespace, Name, Arity, ArgumentNames, ArgumentTypes).
|
|
3265
|
+
|
|
3266
|
+
predicate_schema_rows(Rows) :-
|
|
3267
|
+
index_verification_key(predicate_schemas, Key),
|
|
3268
|
+
( kb_predicate_schema_memo(Key, Cached)
|
|
3269
|
+
-> Rows = Cached
|
|
3270
|
+
; findall(schema(FactId, Namespace, Name, Arity, ArgumentNames, ArgumentTypes),
|
|
3271
|
+
predicate_schema_scan(FactId, Namespace, Name, Arity, ArgumentNames, ArgumentTypes),
|
|
3272
|
+
Computed),
|
|
3273
|
+
retractall(kb_predicate_schema_memo(_, _)),
|
|
3274
|
+
assertz(kb_predicate_schema_memo(Key, Computed)),
|
|
3275
|
+
Rows = Computed
|
|
3276
|
+
).
|
|
3277
|
+
|
|
3278
|
+
predicate_schema_scan(FactId, Namespace, Name, Arity, ArgumentNames, ArgumentTypes) :-
|
|
3026
3279
|
kb_entity(FactId, fact, Props),
|
|
3027
3280
|
memberchk(fact_kind=KindRaw, Props),
|
|
3028
3281
|
normalize_term_atom(KindRaw, predicate_schema),
|
|
@@ -3080,6 +3333,23 @@ normalize_term_atom_list(Raw, Atoms) :-
|
|
|
3080
3333
|
!,
|
|
3081
3334
|
maplist(normalize_term_atom, Value, Atoms).
|
|
3082
3335
|
|
|
3336
|
+
%% normalize_literal_atom_list(+Raw, -Atoms)
|
|
3337
|
+
% The members of a stored list as atoms, verbatim. Unlike
|
|
3338
|
+
% normalize_term_atom_list it keeps a member's '/' segments, so set members
|
|
3339
|
+
% such as rxjs/operators, GET /api/users or finance/de stay whole.
|
|
3340
|
+
normalize_literal_atom_list(Raw, Atoms) :-
|
|
3341
|
+
unwrap_rdf_value(Raw, Value),
|
|
3342
|
+
is_list(Value),
|
|
3343
|
+
maplist(normalize_literal_atom, Value, Atoms).
|
|
3344
|
+
|
|
3345
|
+
normalize_literal_atom(Raw, Atom) :-
|
|
3346
|
+
unwrap_rdf_value(Raw, Value),
|
|
3347
|
+
( atom(Value) -> Atom = Value
|
|
3348
|
+
; string(Value) -> atom_string(Atom, Value)
|
|
3349
|
+
; number(Value) -> atom_number(Atom, Value)
|
|
3350
|
+
; term_string(Value, String), atom_string(Atom, String)
|
|
3351
|
+
).
|
|
3352
|
+
|
|
3083
3353
|
normalize_term_integer(Raw, Integer) :-
|
|
3084
3354
|
unwrap_rdf_value(Raw, Value),
|
|
3085
3355
|
( integer(Value)
|
|
@@ -3125,7 +3395,7 @@ value_from_props(Props, bool, Value) :- memberchk(value_bool=ValueRaw, Props), !
|
|
|
3125
3395
|
% in any order compare equal.
|
|
3126
3396
|
value_from_props(Props, set, Value) :-
|
|
3127
3397
|
memberchk(value_set=ValueRaw, Props),
|
|
3128
|
-
|
|
3398
|
+
normalize_literal_atom_list(ValueRaw, Atoms),
|
|
3129
3399
|
!,
|
|
3130
3400
|
sort(Atoms, Value).
|
|
3131
3401
|
value_from_props(_, _, '').
|
|
@@ -3226,6 +3496,35 @@ same_value(TypeA, ValA, TypeB, ValB) :-
|
|
|
3226
3496
|
values_conflict(eq, ValA, eq, ValB, Type) :-
|
|
3227
3497
|
\+ same_value(Type, ValA, Type, ValB).
|
|
3228
3498
|
|
|
3499
|
+
% A set fact with includes says every member of its set has the property;
|
|
3500
|
+
% eq on a set says exactly those members do. Two includes facts always agree
|
|
3501
|
+
% (the members having the property include both sets); includes conflicts
|
|
3502
|
+
% with eq only when it names a member the eq set leaves out.
|
|
3503
|
+
% implements REQ-kibi-non-numeric-proof-kinds
|
|
3504
|
+
values_conflict(includes, Included, eq, Exact, set) :-
|
|
3505
|
+
\+ subtract(Included, Exact, []).
|
|
3506
|
+
values_conflict(eq, Exact, includes, Included, set) :-
|
|
3507
|
+
\+ subtract(Included, Exact, []).
|
|
3508
|
+
|
|
3509
|
+
% subset_of says every member with the property is in its set, excludes that
|
|
3510
|
+
% no member of its set has it. subset_of conflicts with a set that must have
|
|
3511
|
+
% the property (eq or includes) when that set names a member outside it;
|
|
3512
|
+
% excludes conflicts when the two sets share a member. subset_of and excludes
|
|
3513
|
+
% never conflict with each other or themselves: the empty set satisfies them.
|
|
3514
|
+
% implements REQ-kibi-source-proof-kinds
|
|
3515
|
+
values_conflict(subset_of, Allowed, Op, Required, set) :-
|
|
3516
|
+
memberchk(Op, [eq, includes]),
|
|
3517
|
+
\+ subtract(Required, Allowed, []).
|
|
3518
|
+
values_conflict(Op, Required, subset_of, Allowed, set) :-
|
|
3519
|
+
memberchk(Op, [eq, includes]),
|
|
3520
|
+
\+ subtract(Required, Allowed, []).
|
|
3521
|
+
values_conflict(excludes, Excluded, Op, Required, set) :-
|
|
3522
|
+
memberchk(Op, [eq, includes]),
|
|
3523
|
+
intersection(Excluded, Required, [_|_]).
|
|
3524
|
+
values_conflict(Op, Required, excludes, Excluded, set) :-
|
|
3525
|
+
memberchk(Op, [eq, includes]),
|
|
3526
|
+
intersection(Excluded, Required, [_|_]).
|
|
3527
|
+
|
|
3229
3528
|
% Eq/neq conflict on the same scalar value.
|
|
3230
3529
|
values_conflict(eq, ValA, neq, ValB, Type) :-
|
|
3231
3530
|
same_value(Type, ValA, Type, ValB).
|
package/src/kb_consistency.pl
CHANGED
|
@@ -58,7 +58,22 @@ check_kb_consistency_unresolved(Violations) :-
|
|
|
58
58
|
Unsorted),
|
|
59
59
|
sort(Unsorted, Violations).
|
|
60
60
|
|
|
61
|
+
% Both rules read the same verification, so it is produced once per KB
|
|
62
|
+
% generation (recomputed inside an RDF transaction).
|
|
61
63
|
consistency_verification(Bundle, Report) :-
|
|
64
|
+
( \+ rdf_db:rdf_active_transaction(_),
|
|
65
|
+
kb:index_verification_key(kb_consistency, Key)
|
|
66
|
+
-> ( kb:kb_consistency_memo(Key, Bundle0, Report0)
|
|
67
|
+
-> Bundle = Bundle0,
|
|
68
|
+
Report = Report0
|
|
69
|
+
; compute_consistency_verification(Bundle, Report),
|
|
70
|
+
retractall(kb:kb_consistency_memo(_, _, _)),
|
|
71
|
+
assertz(kb:kb_consistency_memo(Key, Bundle, Report))
|
|
72
|
+
)
|
|
73
|
+
; compute_consistency_verification(Bundle, Report)
|
|
74
|
+
).
|
|
75
|
+
|
|
76
|
+
compute_consistency_verification(Bundle, Report) :-
|
|
62
77
|
kb_certificates(Produced),
|
|
63
78
|
with_output_to(string(Json), json_write_dict(current_output, Produced, [width(0)])),
|
|
64
79
|
open_string(Json, Stream),
|