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.
@@ -27,6 +27,7 @@
27
27
  :- use_module('logic_ir.pl', [logic_rule_safety/2]).
28
28
  :- use_module('code_certificates.pl', [code_certificate_check/2, code_certificate_digest/2]).
29
29
  :- use_module('claim_obligations.pl', [claim_obligation/3, obligation_equal/2]).
30
+ :- use_module('model_check.pl', [model_check_obligation/4]).
30
31
 
31
32
  proof_version('kibi.requirement-proof.v5').
32
33
 
@@ -53,7 +54,13 @@ requirement_proof_context(VerificationSnapshot, CheckedAt, MaxAgeSeconds,
53
54
  (catch(parse_time(CheckedAtAtom, CheckedAtStamp), _, fail) -> true ; CheckedAtStamp = -1),
54
55
  normalize_integer(MaxAgeSeconds, MaxAge),
55
56
  normalize_atom(BindingMode, BindingModeAtom),
57
+ % Everything else in the context follows from the KB, so the proof memo
58
+ % key needs only the caller's parameters.
59
+ variant_sha1(params(Snapshot, CheckedAtAtom, MaxAge, BindingModeAtom, TestBindings), ParamsKey),
60
+ variant_sha1(findings(Contradictions, ContradictionWitnesses, UnsafeRules, UnverifiableRules), FindingsKey),
56
61
  Context = _{
62
+ proofMemoKey: ParamsKey,
63
+ ladderMemoKey: FindingsKey,
57
64
  contradictions: Contradictions,
58
65
  contradictionWitnesses: ContradictionWitnesses,
59
66
  unsafeRules: UnsafeRules,
@@ -67,28 +74,86 @@ requirement_proof_context(VerificationSnapshot, CheckedAt, MaxAgeSeconds,
67
74
  }.
68
75
 
69
76
  % implements REQ-kibi-conservative-requirement-proof
77
+ % A requirement's proof is kept per KB generation and proof parameters, so
78
+ % the evidence-floor rule, the coverage evidence and a coverage report in one
79
+ % check read each ladder once. Inside a transaction (what-if staging) the
80
+ % KB is not the generation's, so the proof is computed afresh.
81
+ requirement_proof(ReqId, ReqProps, Context, Proof) :-
82
+ ( \+ rdf_db:rdf_active_transaction(_),
83
+ get_dict(proofMemoKey, Context, ParamsKey),
84
+ kb:index_verification_key(requirement_proof, IndexKey)
85
+ -> Key = key(IndexKey, ParamsKey),
86
+ ( kb:kb_requirement_proof_memo(ReqId, Key, Proof0)
87
+ -> Proof = Proof0
88
+ ; compute_requirement_proof(ReqId, ReqProps, Context, Proof),
89
+ ( once(kb:kb_requirement_proof_memo(_, Stored, _)),
90
+ Stored \== Key
91
+ -> retractall(kb:kb_requirement_proof_memo(_, _, _))
92
+ ; true
93
+ ),
94
+ assertz(kb:kb_requirement_proof_memo(ReqId, Key, Proof))
95
+ )
96
+ ; compute_requirement_proof(ReqId, ReqProps, Context, Proof)
97
+ ).
98
+
99
+ % The stages before passing_e2e read the KB and the context's KB-derived
100
+ % findings (contradictions, unsafe and unverifiable rules) only, never the
101
+ % caller's snapshot, receipt bindings or checkedAt. A check passes a new
102
+ % checkedAt every run, so these stages are kept per KB generation and those
103
+ % findings apart from the full proof; inside a transaction they are computed
104
+ % afresh.
105
+ receipt_independent_stages(ReqId, ReqProps, Context, Ladder) :-
106
+ ( \+ rdf_db:rdf_active_transaction(_),
107
+ get_dict(ladderMemoKey, Context, FindingsKey),
108
+ kb:index_verification_key(requirement_ladder, IndexKey)
109
+ -> variant_sha1(ReqProps, PropsKey),
110
+ Key = key(IndexKey, FindingsKey, PropsKey),
111
+ ( kb:kb_requirement_ladder_memo(ReqId, Key, Ladder0)
112
+ -> Ladder = Ladder0
113
+ ; compute_receipt_independent_stages(ReqId, ReqProps, Context, Ladder),
114
+ ( once(kb:kb_requirement_ladder_memo(_, key(Stored, _, _), _)),
115
+ Stored \== IndexKey
116
+ -> retractall(kb:kb_requirement_ladder_memo(_, _, _))
117
+ ; true
118
+ ),
119
+ retractall(kb:kb_requirement_ladder_memo(ReqId, _, _)),
120
+ assertz(kb:kb_requirement_ladder_memo(ReqId, Key, Ladder))
121
+ )
122
+ ; compute_receipt_independent_stages(ReqId, ReqProps, Context, Ladder)
123
+ ).
124
+
125
+ compute_receipt_independent_stages(ReqId, ReqProps, Context,
126
+ ladder(SemanticStage, Inventory, LogicStage, ContradictionStage,
127
+ ScenarioStage, ScenarioTestStage, ScenarioTests, SymbolSets)) :-
128
+ semantic_inventory_stage(ReqProps, SemanticStage, Inventory),
129
+ logic_grounding_stage(ReqId, ReqProps, Inventory, Context, LogicStage),
130
+ contradiction_stage(ReqId, LogicStage.status, SemanticStage.status, Context, ContradictionStage),
131
+ scenario_stage(ReqId, ScenarioStage, ScenarioIds),
132
+ scenario_test_stage(ScenarioIds, ScenarioTestStage, ScenarioTests),
133
+ production_symbol_sets(ReqId, SymbolSets).
134
+
70
135
  % Exemption first: a current requirement explicitly marked proof_exempt with a
71
136
  % reason is intentionally outside E2E-proof scope and must never fall through
72
137
  % to the ladder (which would report misleading receipt gaps).
73
- requirement_proof(ReqId, ReqProps, _Context, Proof) :-
138
+ compute_requirement_proof(ReqId, ReqProps, _Context, Proof) :-
74
139
  kb:current_req(ReqId),
75
140
  proof_exempt(ReqProps, Reason),
76
141
  !,
77
142
  exempt_requirement_proof(Reason, ReqProps, Proof).
78
- requirement_proof(ReqId, ReqProps, _Context, Proof) :-
143
+ compute_requirement_proof(ReqId, ReqProps, _Context, Proof) :-
79
144
  \+ kb:current_req(ReqId),
80
145
  !,
81
146
  applicability_exclusion_reason(ReqId, Reason),
82
147
  exempt_requirement_proof(Reason, ReqProps, Proof).
83
- requirement_proof(ReqId, ReqProps, Context, Proof) :-
84
- semantic_inventory_stage(ReqProps, SemanticStage, Inventory),
85
- logic_grounding_stage(ReqId, ReqProps, Inventory, Context, LogicStage),
86
- contradiction_stage(ReqId, LogicStage.status, SemanticStage.status, Context, ContradictionStage),
87
- scenario_stage(ReqId, ScenarioStage, ScenarioIds),
88
- scenario_test_stage(ScenarioIds, ScenarioTestStage, ScenarioTests),
148
+ compute_requirement_proof(ReqId, ReqProps, Context, Proof) :-
149
+ receipt_independent_stages(ReqId, ReqProps, Context,
150
+ ladder(SemanticStage, Inventory, LogicStage, ContradictionStage,
151
+ ScenarioStage, ScenarioTestStage, ScenarioTests, SymbolSets)),
89
152
  passing_e2e_stage(ScenarioTestStage, Context, PassingE2eStage, PassingE2eTests),
90
153
  executable_symbol_stage(PassingE2eTests, ExecutableStage, ExecutableSymbols),
91
- production_symbol_stage(ReqId, PassingE2eTests, ScenarioTests, PassingE2eStage.receiptEvidence, Context, ProductionStage, ProductionSymbols),
154
+ production_symbol_stage_from_sets(ReqId, SymbolSets, PassingE2eTests, ScenarioTests,
155
+ PassingE2eStage.receiptEvidence, Context,
156
+ ProductionStage, ProductionSymbols),
92
157
  source_coordinate_stage(ReqProps, ExecutableSymbols, ProductionSymbols, CoordinateStage),
93
158
  Stages = _{
94
159
  semanticInventory: SemanticStage,
@@ -1009,7 +1074,7 @@ valid_receipt_version_fields('kibi.proof-receipt.v1', _Receipt).
1009
1074
  valid_receipt_version_fields('kibi.proof-receipt.v2', Receipt) :-
1010
1075
  inventory_entry_field(Receipt, kind, RawKind),
1011
1076
  normalize_receipt_atom(RawKind, Kind),
1012
- memberchk(Kind, [e2e, example, property, exhaustive, typecheck, arithmetic, smt]),
1077
+ memberchk(Kind, [e2e, example, property, exhaustive, typecheck, arithmetic, smt, rule, state_machine, regex, source]),
1013
1078
  inventory_entry_field(Receipt, evidence_level, RawLevel),
1014
1079
  normalize_receipt_atom(RawLevel, Level),
1015
1080
  memberchk(Level, [proved, tested]),
@@ -1019,7 +1084,7 @@ valid_receipt_version_fields('kibi.proof-receipt.v2', Receipt) :-
1019
1084
  ; true
1020
1085
  ),
1021
1086
  ( Level == proved
1022
- -> memberchk(Kind, [exhaustive, typecheck, arithmetic, smt]),
1087
+ -> memberchk(Kind, [exhaustive, typecheck, arithmetic, smt, rule, state_machine, regex, source]),
1023
1088
  inventory_entry_field(Receipt, outcome, RawOutcome),
1024
1089
  normalize_receipt_atom(RawOutcome, passed),
1025
1090
  inventory_entry_field(Receipt, certificate, _),
@@ -1210,6 +1275,11 @@ canonical_json_value(Value, Json) :-
1210
1275
  maplist(canonical_json_value, Value, Parts),
1211
1276
  atomics_to_string(Parts, ",", Body),
1212
1277
  format(string(Json), "[~s]", [Body]).
1278
+ % JSON true, false and null read back as atoms; json_write would quote them,
1279
+ % so the hash would differ from the one the CLI recorded at ingest.
1280
+ canonical_json_value(Value, Json) :-
1281
+ memberchk(Value, [true, false, null]), !,
1282
+ atom_string(Value, Json).
1213
1283
  canonical_json_value(Value, Json) :-
1214
1284
  with_output_to(string(Json), json_write(current_output, Value, [width(0)])).
1215
1285
 
@@ -1374,6 +1444,14 @@ production_symbol_stage(ReqId, PassingE2eTests, Stage, ProductionSymbols) :-
1374
1444
  production_symbol_stage(ReqId, PassingE2eTests, ScenarioTests, [], Context, Stage, ProductionSymbols).
1375
1445
 
1376
1446
  production_symbol_stage(ReqId, PassingE2eTests, ScenarioTests, ReceiptEvidence, Context, Stage, ProductionSymbols) :-
1447
+ production_symbol_sets(ReqId, Sets),
1448
+ production_symbol_stage_from_sets(ReqId, Sets, PassingE2eTests, ScenarioTests,
1449
+ ReceiptEvidence, Context, Stage, ProductionSymbols).
1450
+
1451
+ % The implementing symbols of a requirement, split into structural
1452
+ % (type-shape with a unit-test contract) and production ones, with the
1453
+ % production symbols' coordinates: all read from the KB alone.
1454
+ production_symbol_sets(ReqId, symbol_sets(StructuralSymbols, ProductionSymbols, Coordinates)) :-
1377
1455
  findall(SymbolId,
1378
1456
  (kb_relationship(implements, SymbolId, ReqId),
1379
1457
  kb_entity(SymbolId, symbol, _),
@@ -1382,13 +1460,17 @@ production_symbol_stage(ReqId, PassingE2eTests, ScenarioTests, ReceiptEvidence,
1382
1460
  sort(Implementing0, ImplementingSymbols),
1383
1461
  include(type_shape_symbol_with_structural_contract, ImplementingSymbols, StructuralSymbols),
1384
1462
  exclude(type_shape_symbol_with_structural_contract, ImplementingSymbols, ProductionSymbols),
1463
+ maplist(symbol_coordinate_ref, ProductionSymbols, Coordinates).
1464
+
1465
+ production_symbol_stage_from_sets(ReqId, symbol_sets(StructuralSymbols, ProductionSymbols, Coordinates),
1466
+ PassingE2eTests, ScenarioTests, ReceiptEvidence, Context,
1467
+ Stage, ProductionSymbols) :-
1385
1468
  include(symbol_not_covered_by_tests(PassingE2eTests), ProductionSymbols, UncoveredProduction),
1386
1469
  structural_unit_receipts(StructuralSymbols, Context, UnitReceipts),
1387
1470
  exclude(structural_symbol_covered(PassingE2eTests, UnitReceipts), StructuralSymbols, UncoveredStructural),
1388
1471
  append(UncoveredProduction, UncoveredStructural, UncoveredSymbols),
1389
1472
  production_stage_status(ProductionSymbols, StructuralSymbols, PassingE2eTests,
1390
1473
  UncoveredProduction, UncoveredStructural, Status, StatusReason),
1391
- maplist(symbol_coordinate_ref, ProductionSymbols, Coordinates),
1392
1474
  production_coverage_explanations(
1393
1475
  ReqId, ProductionSymbols, StructuralSymbols, UncoveredSymbols,
1394
1476
  PassingE2eTests, ScenarioTests, ReceiptEvidence, UnitReceipts-Context, Explanations),
@@ -1996,7 +2078,7 @@ claim_entry(ReqId, ProofStatus, IntentLevel, BoundTests, InventoryEntry, Entry)
1996
2078
  sort(Bindings0, Bindings),
1997
2079
  include(binding_is(rejected), Bindings, RejectedBindings),
1998
2080
  maplist(binding_rejection(Key), RejectedBindings, Rejections),
1999
- ( member(B, Bindings), get_dict(binding, B, derived), get_dict(scope, B, code)
2081
+ ( member(B, Bindings), get_dict(binding, B, derived), \+ get_dict(scope, B, intent)
2000
2082
  -> Level = proved
2001
2083
  ; Level = tested
2002
2084
  ),
@@ -2097,6 +2179,12 @@ certificate_obligation_problem(arithmetic, Evidence, Obligation, Problem) :-
2097
2179
  ; Problem = 'the certificate\'s claim is not the fact\'s constraint'
2098
2180
  ).
2099
2181
 
2182
+ certificate_obligation_problem(Kind, Evidence, Obligation, Problem) :-
2183
+ memberchk(Kind, [rule, state_machine, source]),
2184
+ get_dict(certificate, Evidence, Certificate),
2185
+ model_check_obligation(Kind, Obligation, Certificate, problem(Problem0)),
2186
+ atom_string(Problem, Problem0).
2187
+
2100
2188
  %% claim_summary(+ClaimEvidence, -Summary, -UnprovedClaims)
2101
2189
  claim_summary(ClaimEvidence, Summary, Unproved) :-
2102
2190
  include(applicable_claim, ClaimEvidence, Applicable),
@@ -318,4 +318,4 @@ rule_description('policy-markers', 'When a plugin activated for kibi.check-polic
318
318
  rule_description('kb-consistency', 'The facts current requirements name must hold together, jointly and not only pairwise: every property context needs a value that satisfies all its facts, every atom and decision key one truth value, and stored rules a stratification. Each finding is a contradiction certificate (an irreducible core, two disagreeing facts or a negation cycle) re-verified by the independent checker, or a certificate that checker rejects').
319
319
  rule_description('kb-consistency-unresolved', 'Advisory: a property context, atom or decision key whose facts the consistency checker cannot compare (mixed value types or units, an unsupported operator or polarity, or a contradiction whose validity windows never overlap); it never counts as consistent').
320
320
  rule_description('evidence-floor', 'Every current requirement\'s computed evidence level (proved, tested, asserted, unverified) must reach its floor: required_evidence when set, otherwise tested for a firm requirement and no floor for any other. Receipts that are only older than the code count as pending re-proof and pass, unless kibi check runs with --fresh-evidence, which also needs a clean proof snapshot').
321
- rule_description('proof-contract-claims', 'From schema 12, every certificate-kind proof contract (exhaustive, typecheck, arithmetic, smt) must name the claims it proves in proves, and each named claim must be a modeled claim of a current requirement the test reaches through its scenarios, grounded by a fact that proof kind can express or attest').
321
+ rule_description('proof-contract-claims', 'From schema 12, every certificate-kind proof contract (exhaustive, typecheck, arithmetic, smt, rule, state_machine, regex) must name the claims it proves in proves, and each named claim must be a modeled claim of a current requirement the test reaches through its scenarios, grounded by a fact that proof kind can express or attest').
package/src/status.pl CHANGED
@@ -203,7 +203,23 @@ kb_snapshot_time(SnapshotTime) :-
203
203
  % made kb_status_json quadratic (paths x entities) and could wedge large KBs
204
204
  % for minutes whenever mtime churn (e.g. a git checkout) marked whole lanes
205
205
  % newer than the snapshot.
206
+ % The source -> entity id index reads every entity, so it is kept per KB
207
+ % generation (whose key names the attached store's graph): status runs it on
208
+ % each freshness probe while the KB is unchanged. Inside a transaction it is
209
+ % built afresh.
206
210
  entity_source_id_index(Index) :-
211
+ ( \+ rdf_db:rdf_active_transaction(_),
212
+ kb:index_verification_key(status_source_index, Key)
213
+ -> ( kb:kb_status_source_index_memo(Key, Index0)
214
+ -> Index = Index0
215
+ ; compute_entity_source_id_index(Index),
216
+ retractall(kb:kb_status_source_index_memo(_, _)),
217
+ assertz(kb:kb_status_source_index_memo(Key, Index))
218
+ )
219
+ ; compute_entity_source_id_index(Index)
220
+ ).
221
+
222
+ compute_entity_source_id_index(Index) :-
207
223
  findall(RelativeSource-Id,
208
224
  ( kb_entity(Id, _Type, Props),
209
225
  entity_source_property(Props, RawSource),
package/src/units.pl CHANGED
@@ -120,13 +120,11 @@ numeric_value_type(number).
120
120
 
121
121
  known_unit(Unit, BaseUnit, Factor) :-
122
122
  atom(Unit),
123
- Unit \== '',
124
123
  ( unit_symbol(Unit, BaseUnit, Factor)
125
124
  -> true
126
125
  ; downcase_atom(Unit, Lower),
127
126
  unit_word(Lower, BaseUnit, Factor)
128
- ),
129
- !.
127
+ ).
130
128
 
131
129
  % Integers (including big integers) stay exact. Floats have binary noise
132
130
  % rounded away (0.1 h * 3600 = 360.00000000000006) before deciding whether the