kibi-core 0.5.2 → 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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kibi-core",
3
- "version": "0.5.2",
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": {
@@ -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).
@@ -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.
@@ -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
- valid_polarity_in_props(Props).
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
- valid_polarity_in_props(Props).
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
@@ -16,6 +16,7 @@
16
16
  check_domain_contradictions/1, % Returns list of contradiction violations
17
17
  check_strict_fact_shape/1, % Returns list of malformed strict fact violations
18
18
  check_strict_req_fact_pairing/1,% Returns list of malformed strict req/fact pairing violations
19
+ check_strict_readiness/1, % Returns list of strict readiness audit violations
19
20
  run_checks_json/0, % Entry point for JSON output
20
21
  violation_id_text/2 % Extract text from entity ID term (exported for testing)
21
22
  ]).
@@ -32,7 +33,7 @@ required_fields([id, title, status, created_at, updated_at, source]).
32
33
  % Relationship types to check for dangling references
33
34
  all_relationship_types([
34
35
  depends_on, verified_by, validates, specified_by,
35
- constrains, requires_property, supersedes, relates_to
36
+ constrains, requires_property, requires_predicate, supersedes, relates_to
36
37
  ]).
37
38
 
38
39
  %% check_all(-ViolationsDict)
@@ -50,6 +51,7 @@ check_all(ViolationsDict) :-
50
51
  check_domain_contradictions(Contradictions),
51
52
  check_strict_fact_shape(StrictFactShape),
52
53
  check_strict_req_fact_pairing(StrictReqFactPairing),
54
+ check_strict_readiness(StrictReadiness),
53
55
  ViolationsDict = _{
54
56
  must_priority_coverage: MustPriority,
55
57
  symbol_coverage: SymbolCoverage,
@@ -60,7 +62,8 @@ check_all(ViolationsDict) :-
60
62
  deprecated_adr_no_successor: DeprecatedADRs,
61
63
  domain_contradictions: Contradictions,
62
64
  strict_fact_shape: StrictFactShape,
63
- strict_req_fact_pairing: StrictReqFactPairing
65
+ strict_req_fact_pairing: StrictReqFactPairing,
66
+ strict_readiness: StrictReadiness
64
67
  }.
65
68
 
66
69
  %% check_must_priority_coverage(-Violations)
@@ -384,9 +387,25 @@ strict_fact_shape_violation(violation(
384
387
  -> fail % Observation facts have no required fields beyond fact_kind
385
388
  ; Kind = meta
386
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
+ )
387
406
  ; % Unknown fact_kind - report as malformed
388
407
  format(string(Description), "Unknown fact_kind: ~w", [Kind]),
389
- Suggestion = "Use one of: subject, property_value, observation, meta"
408
+ Suggestion = "Use one of: subject, property_value, observation, meta, predicate_schema, predicate"
390
409
  ),
391
410
 
392
411
  ( memberchk(source=Source0, Props)
@@ -432,6 +451,68 @@ value_type_matches_field(int, Props) :- memberchk(value_int=_, Props), !.
432
451
  value_type_matches_field(number, Props) :- memberchk(value_number=_, Props), !.
433
452
  value_type_matches_field(bool, Props) :- memberchk(value_bool=_, Props), !.
434
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
+
435
516
  %% check_domain_contradictions(-Violations)
436
517
  % Finds all pairs of requirements with contradicting required properties.
437
518
  check_domain_contradictions(Violations) :-
@@ -445,7 +526,7 @@ check_domain_contradictions(Violations) :-
445
526
  ),
446
527
  ( contradicting_reqs(ReqA, ReqB, Reason),
447
528
  format(string(EntityId), "~w/~w", [ReqA, ReqB]),
448
- Description = Reason
529
+ format(string(Description), "~w [strict-readiness: contradiction-ready]", [Reason])
449
530
  ),
450
531
  Violations
451
532
  ).
@@ -582,6 +663,118 @@ strict_req_fact_pairing_kind_label(legacy, "a legacy fact without fact_kind").
582
663
  strict_req_fact_pairing_kind_label(Kind, Label) :-
583
664
  format(string(Label), "a fact_kind=~w fact", [Kind]).
584
665
 
666
+ %% check_strict_readiness(-Violations)
667
+ % Reports current strict-readiness levels for requirements that are still not
668
+ % contradiction-ready. Legacy and prose-only requirements remain audit-only and
669
+ % are intentionally not treated as contradictions.
670
+ check_strict_readiness(Violations) :-
671
+ findall(
672
+ Violation,
673
+ strict_readiness_violation(Violation),
674
+ Violations0
675
+ ),
676
+ sort(Violations0, Violations).
677
+
678
+ strict_readiness_violation(violation(
679
+ 'strict-readiness',
680
+ ReqId,
681
+ Description,
682
+ Suggestion,
683
+ Source
684
+ )) :-
685
+ strict_readiness_issue(ReqId, Description, Suggestion),
686
+ violation_source(ReqId, req, Source).
687
+
688
+ strict_readiness_issue(
689
+ ReqId,
690
+ "Strict readiness: not-ready (prose-only). Requirement has no fact links, so contradiction checks skip it.",
691
+ "Add a subject fact via constrains and a property_value fact via requires_property to model contradiction-safe semantics"
692
+ ) :-
693
+ kb_entity(ReqId, req, _),
694
+ strict_readiness_level(ReqId, prose_only).
695
+
696
+ strict_readiness_issue(
697
+ ReqId,
698
+ "Strict readiness: not-ready (traceable). Requirement is linked to facts only through legacy or non-strict links, so contradiction checks skip it.",
699
+ "Replace prose or legacy fact links with a subject fact via constrains and a property_value fact via requires_property"
700
+ ) :-
701
+ kb_entity(ReqId, req, _),
702
+ strict_readiness_level(ReqId, traceable).
703
+
704
+ strict_readiness_issue(
705
+ ReqId,
706
+ Description,
707
+ "Add a matching property_value fact via requires_property for the same subject_key"
708
+ ) :-
709
+ kb_entity(ReqId, req, _),
710
+ strict_readiness_level(ReqId, has_subject),
711
+ strict_readiness_primary_subject(ReqId, SubjectFactId, SubjectKey),
712
+ format(
713
+ string(Description),
714
+ "Strict readiness: not-ready (has-subject). Requirement constrains ~w (~w) but has no matching strict requires_property fact, so contradiction checks skip it.",
715
+ [SubjectFactId, SubjectKey]
716
+ ).
717
+
718
+ strict_readiness_issue(
719
+ ReqId,
720
+ "Strict readiness: not-ready (strict-ready). Requirement has strict subject and property facts but no contradiction-ready matched pair, so contradiction checks still skip it.",
721
+ "Align constrains and requires_property on the same subject_key, and keep the requirement current if it should participate in contradiction checks"
722
+ ) :-
723
+ kb_entity(ReqId, req, _),
724
+ strict_readiness_level(ReqId, strict_ready).
725
+
726
+ strict_readiness_level(ReqId, contradiction_ready) :-
727
+ kb:current_req(ReqId),
728
+ kb:effective_req_property_fact(
729
+ ReqId,
730
+ _SubjectKey,
731
+ _FactId,
732
+ _PropertyKey,
733
+ _Operator,
734
+ _ValueType,
735
+ _Value,
736
+ _Unit,
737
+ _Scope,
738
+ _Polarity,
739
+ _ValidFrom,
740
+ _ValidTo
741
+ ),
742
+ !.
743
+ strict_readiness_level(ReqId, strict_ready) :-
744
+ strict_readiness_has_strict_subject(ReqId),
745
+ strict_readiness_has_strict_property(ReqId),
746
+ !.
747
+ strict_readiness_level(ReqId, has_subject) :-
748
+ strict_readiness_has_strict_subject(ReqId),
749
+ !.
750
+ strict_readiness_level(ReqId, traceable) :-
751
+ strict_readiness_has_fact_link(ReqId),
752
+ !.
753
+ strict_readiness_level(_ReqId, prose_only).
754
+
755
+ strict_readiness_has_fact_link(ReqId) :-
756
+ kb_relationship(constrains, ReqId, FactId),
757
+ kb_entity(FactId, fact, _),
758
+ !.
759
+ strict_readiness_has_fact_link(ReqId) :-
760
+ kb_relationship(requires_property, ReqId, FactId),
761
+ kb_entity(FactId, fact, _),
762
+ !.
763
+
764
+ strict_readiness_has_strict_subject(ReqId) :-
765
+ strict_readiness_primary_subject(ReqId, _FactId, _SubjectKey),
766
+ !.
767
+
768
+ strict_readiness_primary_subject(ReqId, FactId, SubjectKey) :-
769
+ kb_relationship(constrains, ReqId, FactId),
770
+ strict_req_fact_pairing_fact_kind(FactId, subject),
771
+ kb:fact_subject_key(FactId, SubjectKey).
772
+
773
+ strict_readiness_has_strict_property(ReqId) :-
774
+ kb_relationship(requires_property, ReqId, FactId),
775
+ strict_req_fact_pairing_fact_kind(FactId, property_value),
776
+ !.
777
+
585
778
  %% run_checks_json
586
779
  % Entry point for JSON output. Prints all violations as JSON to stdout.
587
780
  run_checks_json :-
@@ -630,6 +823,7 @@ check_all_with_options(ViolationsDict, RequireAdr) :-
630
823
  check_domain_contradictions(Contradictions),
631
824
  check_strict_fact_shape(StrictFactShape),
632
825
  check_strict_req_fact_pairing(StrictReqFactPairing),
826
+ check_strict_readiness(StrictReadiness),
633
827
  ViolationsDict = _{
634
828
  must_priority_coverage: MustPriority,
635
829
  symbol_coverage: SymbolCoverage,
@@ -640,7 +834,8 @@ check_all_with_options(ViolationsDict, RequireAdr) :-
640
834
  deprecated_adr_no_successor: DeprecatedADRs,
641
835
  domain_contradictions: Contradictions,
642
836
  strict_fact_shape: StrictFactShape,
643
- strict_req_fact_pairing: StrictReqFactPairing
837
+ strict_req_fact_pairing: StrictReqFactPairing,
838
+ strict_readiness: StrictReadiness
644
839
  }.
645
840
 
646
841
  %% violations_dict_to_json(+ViolationsDict, -JsonDict)
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) :-