kibi-core 0.14.1 → 0.15.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kibi-core",
3
- "version": "0.14.1",
3
+ "version": "0.15.0",
4
4
  "private": false,
5
5
  "description": "Core Prolog modules and RDF graph logic for Kibi",
6
6
  "type": "module",
@@ -8,7 +8,10 @@
8
8
  "node": ">=18"
9
9
  },
10
10
  "main": "./src/kb.pl",
11
- "files": ["src/**/*.pl", "schema/**/*.pl"],
11
+ "files": [
12
+ "src/**/*.pl",
13
+ "schema/**/*.pl"
14
+ ],
12
15
  "license": "AGPL-3.0-or-later",
13
16
  "author": "Piotr Franczyk",
14
17
  "repository": {
@@ -30,6 +30,9 @@ entity_property(_, severity, atom).
30
30
  entity_property(_, links, list).
31
31
  entity_property(_, text_ref, uri).
32
32
  entity_property(_, sourceFile, uri).
33
+ % Provenance of any authored entity: a JSON object (kind, ref, approved_by,
34
+ % recorded_at) naming who authored it and on what authority.
35
+ entity_property(_, origin, list_or_json).
33
36
  entity_property(req, semantic_text, string).
34
37
  entity_property(req, logic_claims, list).
35
38
  entity_property(req, semantic_clauses, list).
@@ -39,6 +42,17 @@ entity_property(req, semantic_source_hash, string).
39
42
  entity_property(req, semantic_inventory, list_or_json).
40
43
  entity_property(req, proof_exempt, boolean).
41
44
  entity_property(req, proof_exempt_reason, string).
45
+ % Human approval of an exception requirement (one that `exempts` another).
46
+ % Only an approved exception makes a success scenario feasible.
47
+ entity_property(req, approved_by, string).
48
+ entity_property(req, approval_ref, string).
49
+ % Why the requirement exists, in the words of whoever stated the intent.
50
+ % Explanation only: it is never part of the checked meaning.
51
+ entity_property(req, rationale, string).
52
+ % Claim keys of the exempted requirement an exception waives (all when absent).
53
+ entity_property(req, exempts_claims, list).
54
+ % Intended outcome of a scenario: success, rejection or error.
55
+ entity_property(scenario, expects, atom).
42
56
 
43
57
  % Typed fact fields - only valid for fact entities
44
58
  entity_property(fact, fact_kind, atom).
@@ -65,6 +79,9 @@ entity_property(fact, predicate_arity, integer).
65
79
  entity_property(fact, argument_names, list).
66
80
  entity_property(fact, argument_types, list).
67
81
  entity_property(fact, argument_descriptions, list).
82
+ % Predicate-schema functional dependency: the named arguments determine the
83
+ % remaining ones, so two atoms with equal keys describe the same fact.
84
+ entity_property(fact, key_arguments, list).
68
85
  % Predicate-schema vocabularies are JSON objects serialized as strings.
69
86
  entity_property(fact, argument_constants, string).
70
87
  entity_property(fact, argument_aliases, string).
@@ -112,6 +129,7 @@ optional_property(Type, priority) :- entity_type(Type).
112
129
  optional_property(Type, severity) :- entity_type(Type).
113
130
  optional_property(Type, links) :- entity_type(Type).
114
131
  optional_property(Type, text_ref) :- entity_type(Type).
132
+ optional_property(Type, origin) :- entity_type(Type).
115
133
  optional_property(req, semantic_text).
116
134
  optional_property(req, logic_claims).
117
135
  optional_property(req, semantic_clauses).
@@ -121,6 +139,10 @@ optional_property(req, semantic_source_hash).
121
139
  optional_property(req, semantic_inventory).
122
140
  optional_property(req, proof_exempt).
123
141
  optional_property(req, proof_exempt_reason).
142
+ optional_property(req, approved_by).
143
+ optional_property(req, approval_ref).
144
+ optional_property(req, rationale).
145
+ optional_property(req, exempts_claims).
124
146
  optional_property(test, verification_scope).
125
147
  optional_property(test, verification_perspective).
126
148
  optional_property(test, proof_contract).
@@ -22,6 +22,8 @@ relationship_type(constrains).
22
22
  relationship_type(requires_property).
23
23
  relationship_type(requires_predicate).
24
24
  relationship_type(requires_rule).
25
+ relationship_type(assumes).
26
+ relationship_type(exempts).
25
27
 
26
28
  % valid_relationship(RelType, FromType, ToType).
27
29
  valid_relationship(depends_on, req, req).
@@ -45,6 +47,18 @@ valid_relationship(requires_property, req, fact).
45
47
  valid_relationship(requires_predicate, req, fact).
46
48
  valid_relationship(requires_rule, req, fact).
47
49
 
50
+ %% assumes(+ScenarioId, +FactId)
51
+ %% The scenario's outcome depends on the property_value fact holding (for
52
+ %% example "the order total is 0"). Checked against current requirements by
53
+ %% the scenario-feasibility rule.
54
+ % implements REQ-kibi-scenario-feasibility-v2
55
+ valid_relationship(assumes, scenario, fact).
56
+
57
+ %% exempts(+ExceptionReqId, +BaseReqId)
58
+ %% An approved exception requirement exempts the scenarios it specifies from
59
+ %% BaseReqId's property constraints. BaseReqId stays current and unchanged.
60
+ valid_relationship(exempts, req, req).
61
+
48
62
  %% supersedes(+NewAdrId, +OldAdrId)
49
63
  %% NewAdrId is the decision that replaces OldAdrId.
50
64
  %% OldAdrId's status should be archived or deprecated as a consequence.
@@ -8,6 +8,7 @@
8
8
 
9
9
  :- use_module('entities.pl').
10
10
  :- use_module('relationships.pl').
11
+ :- use_module(library(http/json)).
11
12
 
12
13
  % validate_entity(+Type, +Props:list)
13
14
  % Props is a list of Property=Value pairs (e.g. id=ID, title=Title)
@@ -19,7 +20,9 @@ validate_entity(Type, Props) :-
19
20
  % all properties have correct types
20
21
  forall(member(Key=Val, Props), validate_property_type(Type, Key, Val)),
21
22
  % validate entity-specific shape constraints
22
- validate_entity_shape(Type, Props).
23
+ validate_entity_shape(Type, Props),
24
+ % provenance, when present, names a known origin kind
25
+ valid_origin_in_props(Props).
23
26
 
24
27
  % validate_relationship(+RelType, +From, +To)
25
28
  % From and To are pairs Type=Id or structures type(Type) - allow Type or Type=Id
@@ -105,6 +108,7 @@ is_fact_only_field(predicate_arity).
105
108
  is_fact_only_field(argument_names).
106
109
  is_fact_only_field(argument_types).
107
110
  is_fact_only_field(argument_descriptions).
111
+ is_fact_only_field(key_arguments).
108
112
  is_fact_only_field(argument_constants).
109
113
  is_fact_only_field(argument_aliases).
110
114
  is_fact_only_field(aliases).
@@ -156,6 +160,7 @@ validate_fact_shape(predicate_schema, Props) :-
156
160
  memberchk(argument_types=ArgumentTypes, Props),
157
161
  same_length(ArgumentNames, ArgumentTypes),
158
162
  length(ArgumentNames, Arity),
163
+ valid_key_arguments(Props, ArgumentNames),
159
164
  valid_optional_fact_enums(Props),
160
165
  valid_polarity_in_props(Props), !.
161
166
  validate_fact_shape(predicate, Props) :-
@@ -185,6 +190,24 @@ validate_fact_shape(Kind, _Props) :-
185
190
  \+ memberchk(Kind, [subject, property_value, observation, meta, predicate_schema, predicate, rule_schema, rule]),
186
191
  fail.
187
192
 
193
+ % valid_key_arguments(+Props, +ArgumentNames)
194
+ % Optional key_arguments must be a non-empty list of distinct declared
195
+ % argument names.
196
+ valid_key_arguments(Props, ArgumentNames) :-
197
+ ( memberchk(key_arguments=Keys, Props)
198
+ -> is_list(Keys),
199
+ Keys \= [],
200
+ maplist(text_atom, ArgumentNames, NameAtoms),
201
+ maplist(text_atom, Keys, KeyAtoms),
202
+ sort(KeyAtoms, UniqueKeys),
203
+ same_length(KeyAtoms, UniqueKeys),
204
+ forall(member(Key, KeyAtoms), memberchk(Key, NameAtoms))
205
+ ; true
206
+ ).
207
+
208
+ text_atom(Value, Atom) :- atom(Value), !, Atom = Value.
209
+ text_atom(Value, Atom) :- string(Value), atom_string(Atom, Value).
210
+
188
211
  % valid_operator(+Op)
189
212
  valid_operator(Op) :- memberchk(Op, [eq, neq, lt, lte, gt, gte]), !.
190
213
 
@@ -256,3 +279,24 @@ value_type_matches_field(string, Props) :- memberchk(value_string=_, Props), !.
256
279
  value_type_matches_field(int, Props) :- memberchk(value_int=_, Props), !.
257
280
  value_type_matches_field(number, Props) :- memberchk(value_number=_, Props), !.
258
281
  value_type_matches_field(bool, Props) :- memberchk(value_bool=_, Props), !.
282
+
283
+ % valid_origin_in_props(+Props)
284
+ % Entity provenance travels as a JSON object string (or a dict). When present
285
+ % its kind must be human, agent, migration or import.
286
+ valid_origin_in_props(Props) :-
287
+ ( memberchk(origin=Raw, Props)
288
+ -> origin_kind(Raw, Kind),
289
+ memberchk(Kind, [human, agent, migration, import])
290
+ ; true
291
+ ).
292
+
293
+ origin_kind(Raw, Kind) :-
294
+ ( is_dict(Raw)
295
+ -> Dict = Raw
296
+ ; (atom(Raw) ; string(Raw)),
297
+ catch(atom_json_dict(Raw, Dict, []), _, fail),
298
+ is_dict(Dict)
299
+ ),
300
+ get_dict(kind, Dict, RawKind),
301
+ (atom(RawKind) ; string(RawKind)),
302
+ atom_string(Kind, RawKind).