kibi-core 0.5.3 → 0.6.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 +9 -0
- package/schema/relationships.pl +2 -0
- package/schema/validation.pl +43 -3
- package/src/checks.pl +80 -2
- package/src/kb.pl +53 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kibi-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Core Prolog modules and RDF graph logic for Kibi",
|
|
6
6
|
"type": "module",
|
|
@@ -9,10 +9,7 @@
|
|
|
9
9
|
"bun": ">=1.0"
|
|
10
10
|
},
|
|
11
11
|
"main": "./src/kb.pl",
|
|
12
|
-
"files": [
|
|
13
|
-
"src/**/*.pl",
|
|
14
|
-
"schema/**/*.pl"
|
|
15
|
-
],
|
|
12
|
+
"files": ["src/**/*.pl", "schema/**/*.pl"],
|
|
16
13
|
"license": "AGPL-3.0-or-later",
|
|
17
14
|
"author": "Piotr Franczyk",
|
|
18
15
|
"repository": {
|
package/schema/entities.pl
CHANGED
|
@@ -47,6 +47,15 @@ entity_property(fact, closed_world, boolean).
|
|
|
47
47
|
entity_property(fact, valid_from, datetime).
|
|
48
48
|
entity_property(fact, valid_to, datetime).
|
|
49
49
|
entity_property(fact, canonical_key, string).
|
|
50
|
+
entity_property(fact, predicate_name, string).
|
|
51
|
+
entity_property(fact, predicate_namespace, string).
|
|
52
|
+
entity_property(fact, predicate_arity, integer).
|
|
53
|
+
entity_property(fact, argument_names, list).
|
|
54
|
+
entity_property(fact, argument_types, list).
|
|
55
|
+
entity_property(fact, argument_descriptions, list).
|
|
56
|
+
entity_property(fact, aliases, list).
|
|
57
|
+
entity_property(fact, examples, list).
|
|
58
|
+
entity_property(fact, predicate_args, list).
|
|
50
59
|
|
|
51
60
|
% Typed test verification fields - only valid for test entities
|
|
52
61
|
entity_property(test, verification_scope, atom).
|
package/schema/relationships.pl
CHANGED
|
@@ -18,6 +18,7 @@ relationship_type(relates_to).
|
|
|
18
18
|
relationship_type(supersedes).
|
|
19
19
|
relationship_type(constrains).
|
|
20
20
|
relationship_type(requires_property).
|
|
21
|
+
relationship_type(requires_predicate).
|
|
21
22
|
|
|
22
23
|
% valid_relationship(RelType, FromType, ToType).
|
|
23
24
|
valid_relationship(depends_on, req, req).
|
|
@@ -38,6 +39,7 @@ valid_relationship(publishes, symbol, event).
|
|
|
38
39
|
valid_relationship(consumes, symbol, event).
|
|
39
40
|
valid_relationship(constrains, req, fact).
|
|
40
41
|
valid_relationship(requires_property, req, fact).
|
|
42
|
+
valid_relationship(requires_predicate, req, fact).
|
|
41
43
|
|
|
42
44
|
%% supersedes(+NewAdrId, +OldAdrId)
|
|
43
45
|
%% NewAdrId is the decision that replaces OldAdrId.
|
package/schema/validation.pl
CHANGED
|
@@ -88,6 +88,15 @@ is_fact_only_field(closed_world).
|
|
|
88
88
|
is_fact_only_field(valid_from).
|
|
89
89
|
is_fact_only_field(valid_to).
|
|
90
90
|
is_fact_only_field(canonical_key).
|
|
91
|
+
is_fact_only_field(predicate_name).
|
|
92
|
+
is_fact_only_field(predicate_namespace).
|
|
93
|
+
is_fact_only_field(predicate_arity).
|
|
94
|
+
is_fact_only_field(argument_names).
|
|
95
|
+
is_fact_only_field(argument_types).
|
|
96
|
+
is_fact_only_field(argument_descriptions).
|
|
97
|
+
is_fact_only_field(aliases).
|
|
98
|
+
is_fact_only_field(examples).
|
|
99
|
+
is_fact_only_field(predicate_args).
|
|
91
100
|
|
|
92
101
|
% is_test_only_field(+Key) - true if Key is a test-specific field
|
|
93
102
|
is_test_only_field(verification_scope).
|
|
@@ -97,7 +106,7 @@ is_test_only_field(verification_perspective).
|
|
|
97
106
|
validate_fact_shape(subject, Props) :-
|
|
98
107
|
memberchk(subject_key=_Val, Props),
|
|
99
108
|
valid_optional_fact_enums(Props),
|
|
100
|
-
|
|
109
|
+
valid_strict_polarity_in_props(Props).
|
|
101
110
|
validate_fact_shape(property_value, Props) :-
|
|
102
111
|
memberchk(subject_key=_Subject, Props),
|
|
103
112
|
memberchk(property_key=_Property, Props),
|
|
@@ -108,7 +117,7 @@ validate_fact_shape(property_value, Props) :-
|
|
|
108
117
|
exactly_one_value_field(Props),
|
|
109
118
|
value_type_matches_field(VT, Props),
|
|
110
119
|
valid_optional_fact_enums(Props),
|
|
111
|
-
|
|
120
|
+
valid_strict_polarity_in_props(Props).
|
|
112
121
|
validate_fact_shape(observation, Props) :-
|
|
113
122
|
valid_optional_fact_enums(Props),
|
|
114
123
|
valid_polarity_in_props(Props).
|
|
@@ -116,9 +125,26 @@ validate_fact_shape(meta, Props) :-
|
|
|
116
125
|
% Meta facts are allowed but don't require full strict property tuple yet
|
|
117
126
|
valid_optional_fact_enums(Props),
|
|
118
127
|
valid_polarity_in_props(Props).
|
|
128
|
+
validate_fact_shape(predicate_schema, Props) :-
|
|
129
|
+
memberchk(predicate_name=_Name, Props),
|
|
130
|
+
memberchk(predicate_arity=Arity, Props),
|
|
131
|
+
Arity >= 1,
|
|
132
|
+
memberchk(argument_names=ArgumentNames, Props),
|
|
133
|
+
memberchk(argument_types=ArgumentTypes, Props),
|
|
134
|
+
same_length(ArgumentNames, ArgumentTypes),
|
|
135
|
+
length(ArgumentNames, Arity),
|
|
136
|
+
valid_optional_fact_enums(Props),
|
|
137
|
+
valid_polarity_in_props(Props).
|
|
138
|
+
validate_fact_shape(predicate, Props) :-
|
|
139
|
+
memberchk(predicate_name=_Name, Props),
|
|
140
|
+
memberchk(predicate_args=Args, Props),
|
|
141
|
+
Args \= [],
|
|
142
|
+
memberchk(canonical_key=_CanonicalKey, Props),
|
|
143
|
+
valid_optional_fact_enums(Props),
|
|
144
|
+
valid_predicate_polarity_in_props(Props).
|
|
119
145
|
validate_fact_shape(Kind, _Props) :-
|
|
120
146
|
% Unknown fact_kind values fail validation
|
|
121
|
-
\+ memberchk(Kind, [subject, property_value, observation, meta]),
|
|
147
|
+
\+ memberchk(Kind, [subject, property_value, observation, meta, predicate_schema, predicate]),
|
|
122
148
|
fail.
|
|
123
149
|
|
|
124
150
|
% valid_operator(+Op)
|
|
@@ -140,6 +166,12 @@ valid_value_type(bool).
|
|
|
140
166
|
valid_polarity_in_props(Props) :-
|
|
141
167
|
( memberchk(polarity=P, Props) -> valid_polarity(P) ; true ).
|
|
142
168
|
|
|
169
|
+
valid_strict_polarity_in_props(Props) :-
|
|
170
|
+
( memberchk(polarity=P, Props) -> valid_strict_polarity(P) ; true ).
|
|
171
|
+
|
|
172
|
+
valid_predicate_polarity_in_props(Props) :-
|
|
173
|
+
( memberchk(polarity=P, Props) -> valid_predicate_polarity(P) ; true ).
|
|
174
|
+
|
|
143
175
|
% valid_optional_fact_enums(+Props)
|
|
144
176
|
% Validates enum-typed fact fields whenever they are present
|
|
145
177
|
valid_optional_fact_enums(Props) :-
|
|
@@ -155,6 +187,14 @@ valid_optional_test_enums(Props) :-
|
|
|
155
187
|
% valid_polarity(+P)
|
|
156
188
|
valid_polarity(require).
|
|
157
189
|
valid_polarity(forbid).
|
|
190
|
+
valid_polarity(assert).
|
|
191
|
+
valid_polarity(deny).
|
|
192
|
+
|
|
193
|
+
valid_strict_polarity(require).
|
|
194
|
+
valid_strict_polarity(forbid).
|
|
195
|
+
|
|
196
|
+
valid_predicate_polarity(assert).
|
|
197
|
+
valid_predicate_polarity(deny).
|
|
158
198
|
|
|
159
199
|
% valid_verification_scope(+Scope)
|
|
160
200
|
valid_verification_scope(unit).
|
package/src/checks.pl
CHANGED
|
@@ -33,7 +33,7 @@ required_fields([id, title, status, created_at, updated_at, source]).
|
|
|
33
33
|
% Relationship types to check for dangling references
|
|
34
34
|
all_relationship_types([
|
|
35
35
|
depends_on, verified_by, validates, specified_by,
|
|
36
|
-
constrains, requires_property, supersedes, relates_to
|
|
36
|
+
constrains, requires_property, requires_predicate, supersedes, relates_to
|
|
37
37
|
]).
|
|
38
38
|
|
|
39
39
|
%% check_all(-ViolationsDict)
|
|
@@ -387,9 +387,25 @@ strict_fact_shape_violation(violation(
|
|
|
387
387
|
-> fail % Observation facts have no required fields beyond fact_kind
|
|
388
388
|
; Kind = meta
|
|
389
389
|
-> fail % Meta facts have no required fields beyond fact_kind
|
|
390
|
+
; Kind = predicate_schema
|
|
391
|
+
-> findall(Msg, predicate_schema_shape_error(Props, Msg), Errors),
|
|
392
|
+
( Errors = []
|
|
393
|
+
-> fail
|
|
394
|
+
; Errors = [First|_],
|
|
395
|
+
Description = First,
|
|
396
|
+
Suggestion = "Ensure predicate_schema facts have predicate_name, predicate_arity, argument_names, and argument_types with matching arity"
|
|
397
|
+
)
|
|
398
|
+
; Kind = predicate
|
|
399
|
+
-> findall(Msg, predicate_shape_error(Props, Msg), Errors),
|
|
400
|
+
( Errors = []
|
|
401
|
+
-> fail
|
|
402
|
+
; Errors = [First|_],
|
|
403
|
+
Description = First,
|
|
404
|
+
Suggestion = "Ensure predicate facts have predicate_name, non-empty predicate_args, canonical_key, and assert/deny polarity when present"
|
|
405
|
+
)
|
|
390
406
|
; % Unknown fact_kind - report as malformed
|
|
391
407
|
format(string(Description), "Unknown fact_kind: ~w", [Kind]),
|
|
392
|
-
Suggestion = "Use one of: subject, property_value, observation, meta"
|
|
408
|
+
Suggestion = "Use one of: subject, property_value, observation, meta, predicate_schema, predicate"
|
|
393
409
|
),
|
|
394
410
|
|
|
395
411
|
( memberchk(source=Source0, Props)
|
|
@@ -435,6 +451,68 @@ value_type_matches_field(int, Props) :- memberchk(value_int=_, Props), !.
|
|
|
435
451
|
value_type_matches_field(number, Props) :- memberchk(value_number=_, Props), !.
|
|
436
452
|
value_type_matches_field(bool, Props) :- memberchk(value_bool=_, Props), !.
|
|
437
453
|
|
|
454
|
+
predicate_schema_shape_error(Props, "Predicate schema fact missing required field: predicate_name") :-
|
|
455
|
+
\+ memberchk(predicate_name=_, Props).
|
|
456
|
+
predicate_schema_shape_error(Props, "Predicate schema fact missing required field: predicate_arity") :-
|
|
457
|
+
memberchk(predicate_name=_, Props),
|
|
458
|
+
\+ memberchk(predicate_arity=_, Props).
|
|
459
|
+
predicate_schema_shape_error(Props, "Predicate schema fact missing required field: argument_names") :-
|
|
460
|
+
memberchk(predicate_arity=_, Props),
|
|
461
|
+
\+ memberchk(argument_names=_, Props).
|
|
462
|
+
predicate_schema_shape_error(Props, "Predicate schema fact missing required field: argument_types") :-
|
|
463
|
+
memberchk(argument_names=_, Props),
|
|
464
|
+
\+ memberchk(argument_types=_, Props).
|
|
465
|
+
predicate_schema_shape_error(Props, "Predicate schema fact argument_names length must match predicate_arity") :-
|
|
466
|
+
memberchk(predicate_arity=RawArity, Props),
|
|
467
|
+
checks_normalize_integer(RawArity, Arity),
|
|
468
|
+
memberchk(argument_names=RawNames, Props),
|
|
469
|
+
checks_normalize_atom_list(RawNames, Names),
|
|
470
|
+
length(Names, Count),
|
|
471
|
+
Count =\= Arity.
|
|
472
|
+
predicate_schema_shape_error(Props, "Predicate schema fact argument_types length must match predicate_arity") :-
|
|
473
|
+
memberchk(predicate_arity=RawArity, Props),
|
|
474
|
+
checks_normalize_integer(RawArity, Arity),
|
|
475
|
+
memberchk(argument_types=RawTypes, Props),
|
|
476
|
+
checks_normalize_atom_list(RawTypes, Types),
|
|
477
|
+
length(Types, Count),
|
|
478
|
+
Count =\= Arity.
|
|
479
|
+
|
|
480
|
+
predicate_shape_error(Props, "Predicate fact missing required field: predicate_name") :-
|
|
481
|
+
\+ memberchk(predicate_name=_, Props).
|
|
482
|
+
predicate_shape_error(Props, "Predicate fact missing required field: predicate_args") :-
|
|
483
|
+
memberchk(predicate_name=_, Props),
|
|
484
|
+
\+ memberchk(predicate_args=_, Props).
|
|
485
|
+
predicate_shape_error(Props, "Predicate fact predicate_args must be non-empty") :-
|
|
486
|
+
memberchk(predicate_args=RawArgs, Props),
|
|
487
|
+
checks_normalize_atom_list(RawArgs, Args),
|
|
488
|
+
Args = [].
|
|
489
|
+
predicate_shape_error(Props, "Predicate fact missing required field: canonical_key") :-
|
|
490
|
+
memberchk(predicate_args=_, Props),
|
|
491
|
+
\+ memberchk(canonical_key=_, Props).
|
|
492
|
+
predicate_shape_error(Props, "Predicate fact polarity must be assert or deny") :-
|
|
493
|
+
memberchk(polarity=RawPolarity, Props),
|
|
494
|
+
normalize_term_atom(RawPolarity, Polarity),
|
|
495
|
+
\+ memberchk(Polarity, [assert, deny]).
|
|
496
|
+
|
|
497
|
+
checks_normalize_integer(Raw, Integer) :-
|
|
498
|
+
( Raw = ^^(Value, _Type)
|
|
499
|
+
-> checks_normalize_integer(Value, Integer)
|
|
500
|
+
; integer(Raw)
|
|
501
|
+
-> Integer = Raw
|
|
502
|
+
; atom(Raw)
|
|
503
|
+
-> atom_number(Raw, Integer)
|
|
504
|
+
; string(Raw)
|
|
505
|
+
-> number_string(Integer, Raw)
|
|
506
|
+
).
|
|
507
|
+
|
|
508
|
+
checks_normalize_atom_list(Raw, Atoms) :-
|
|
509
|
+
( Raw = ^^(Value, _Type)
|
|
510
|
+
-> checks_normalize_atom_list(Value, Atoms)
|
|
511
|
+
; is_list(Raw)
|
|
512
|
+
-> maplist(normalize_term_atom, Raw, Atoms)
|
|
513
|
+
; Atoms = []
|
|
514
|
+
).
|
|
515
|
+
|
|
438
516
|
%% check_domain_contradictions(-Violations)
|
|
439
517
|
% Finds all pairs of requirements with contradicting required properties.
|
|
440
518
|
check_domain_contradictions(Violations) :-
|
package/src/kb.pl
CHANGED
|
@@ -39,6 +39,8 @@
|
|
|
39
39
|
adr_chain/2,
|
|
40
40
|
deprecated_no_successor/1,
|
|
41
41
|
symbol_no_req_coverage/2,
|
|
42
|
+
predicate_schema/6,
|
|
43
|
+
predicate_fact/5,
|
|
42
44
|
contradicting_reqs/3,
|
|
43
45
|
check_req_contradiction/1,
|
|
44
46
|
normalize_term_atom/2,
|
|
@@ -962,6 +964,57 @@ fact_property_tuple(FactId, Subject, Property, Op, ValType, Value, Unit, Scope,
|
|
|
962
964
|
( memberchk(scope=ScopeRaw, Props) -> normalize_term_atom(ScopeRaw, Scope) ; Scope = '' ),
|
|
963
965
|
( memberchk(polarity=PolarityRaw, Props) -> normalize_term_atom(PolarityRaw, Polarity) ; Polarity = require ).
|
|
964
966
|
|
|
967
|
+
%% predicate_schema(+FactId, -Namespace, -Name, -Arity, -ArgumentNames, -ArgumentTypes)
|
|
968
|
+
% Read one project-local ontology predicate schema fact.
|
|
969
|
+
predicate_schema(FactId, Namespace, Name, Arity, ArgumentNames, ArgumentTypes) :-
|
|
970
|
+
kb_entity(FactId, fact, Props),
|
|
971
|
+
memberchk(fact_kind=KindRaw, Props),
|
|
972
|
+
normalize_term_atom(KindRaw, predicate_schema),
|
|
973
|
+
memberchk(predicate_name=NameRaw, Props),
|
|
974
|
+
normalize_term_atom(NameRaw, Name),
|
|
975
|
+
( memberchk(predicate_namespace=NamespaceRaw, Props) -> normalize_term_atom(NamespaceRaw, Namespace) ; Namespace = default ),
|
|
976
|
+
memberchk(predicate_arity=ArityRaw, Props),
|
|
977
|
+
normalize_term_integer(ArityRaw, Arity),
|
|
978
|
+
memberchk(argument_names=ArgumentNamesRaw, Props),
|
|
979
|
+
normalize_term_atom_list(ArgumentNamesRaw, ArgumentNames),
|
|
980
|
+
memberchk(argument_types=ArgumentTypesRaw, Props),
|
|
981
|
+
normalize_term_atom_list(ArgumentTypesRaw, ArgumentTypes).
|
|
982
|
+
|
|
983
|
+
%% predicate_fact(+FactId, -Namespace, -Name, -Args, -Polarity)
|
|
984
|
+
% Read one ground ontology predicate fact.
|
|
985
|
+
predicate_fact(FactId, Namespace, Name, Args, Polarity) :-
|
|
986
|
+
kb_entity(FactId, fact, Props),
|
|
987
|
+
memberchk(fact_kind=KindRaw, Props),
|
|
988
|
+
normalize_term_atom(KindRaw, predicate),
|
|
989
|
+
memberchk(predicate_name=NameRaw, Props),
|
|
990
|
+
normalize_term_atom(NameRaw, Name),
|
|
991
|
+
( memberchk(predicate_namespace=NamespaceRaw, Props) -> normalize_term_atom(NamespaceRaw, Namespace) ; Namespace = default ),
|
|
992
|
+
memberchk(predicate_args=ArgsRaw, Props),
|
|
993
|
+
normalize_term_atom_list(ArgsRaw, Args),
|
|
994
|
+
( memberchk(polarity=PolarityRaw, Props) -> normalize_term_atom(PolarityRaw, Polarity) ; Polarity = assert ).
|
|
995
|
+
|
|
996
|
+
normalize_term_atom_list(List, Atoms) :-
|
|
997
|
+
is_list(List),
|
|
998
|
+
!,
|
|
999
|
+
maplist(normalize_term_atom, List, Atoms).
|
|
1000
|
+
normalize_term_atom_list(Raw, Atoms) :-
|
|
1001
|
+
unwrap_rdf_value(Raw, Value),
|
|
1002
|
+
is_list(Value),
|
|
1003
|
+
!,
|
|
1004
|
+
maplist(normalize_term_atom, Value, Atoms).
|
|
1005
|
+
|
|
1006
|
+
normalize_term_integer(Raw, Integer) :-
|
|
1007
|
+
unwrap_rdf_value(Raw, Value),
|
|
1008
|
+
( integer(Value)
|
|
1009
|
+
-> Integer = Value
|
|
1010
|
+
; atom(Value)
|
|
1011
|
+
-> atom_number(Value, Integer)
|
|
1012
|
+
; string(Value)
|
|
1013
|
+
-> number_string(Integer, Value)
|
|
1014
|
+
; Value = ^^(Nested, _Type)
|
|
1015
|
+
-> normalize_term_integer(Nested, Integer)
|
|
1016
|
+
).
|
|
1017
|
+
|
|
965
1018
|
%% fact_valid_interval(+FactId, -ValidFrom, -ValidTo)
|
|
966
1019
|
% Extract optional validity bounds for property_value facts.
|
|
967
1020
|
fact_valid_interval(FactId, ValidFrom, ValidTo) :-
|