kibi-core 0.7.1 → 0.9.1
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/entities.pl +28 -0
- package/schema/relationships.pl +2 -0
- package/schema/validation.pl +41 -32
- package/src/checks.pl +410 -19
- package/src/discovery.pl +88 -18
- package/src/kb.pl +256 -19
- package/src/logic_ir.pl +629 -0
- package/src/requirement_proof.pl +837 -0
|
@@ -0,0 +1,837 @@
|
|
|
1
|
+
% Module: requirement_proof
|
|
2
|
+
% Conservative, inspectable proof outcomes for requirement coverage reports.
|
|
3
|
+
|
|
4
|
+
:- module(requirement_proof, [
|
|
5
|
+
requirement_proof_context/1,
|
|
6
|
+
requirement_proof_context/4,
|
|
7
|
+
requirement_proof/4
|
|
8
|
+
]).
|
|
9
|
+
|
|
10
|
+
:- use_module(library(http/json)).
|
|
11
|
+
:- use_module(library(date)).
|
|
12
|
+
:- use_module(library(pcre)).
|
|
13
|
+
:- use_module('kb.pl').
|
|
14
|
+
:- use_module('checks.pl', [
|
|
15
|
+
check_domain_contradictions/1,
|
|
16
|
+
check_domain_contradiction_witnesses/1,
|
|
17
|
+
check_rule_safety/1,
|
|
18
|
+
check_rule_verifiability/1
|
|
19
|
+
]).
|
|
20
|
+
:- use_module('logic_ir.pl', [logic_rule_safety/2]).
|
|
21
|
+
|
|
22
|
+
proof_version('kibi.requirement-proof.v2').
|
|
23
|
+
|
|
24
|
+
requirement_proof_context(Context) :-
|
|
25
|
+
requirement_proof_context(unknown, '1970-01-01T00:00:00Z', 604800, Context).
|
|
26
|
+
|
|
27
|
+
requirement_proof_context(VerificationSnapshot, CheckedAt, MaxAgeSeconds, Context) :-
|
|
28
|
+
check_domain_contradictions(Contradictions),
|
|
29
|
+
check_domain_contradiction_witnesses(ContradictionWitnesses),
|
|
30
|
+
check_rule_safety(UnsafeRules),
|
|
31
|
+
check_rule_verifiability(UnverifiableRules),
|
|
32
|
+
normalize_atom(VerificationSnapshot, Snapshot),
|
|
33
|
+
normalize_atom(CheckedAt, CheckedAtAtom),
|
|
34
|
+
(catch(parse_time(CheckedAtAtom, CheckedAtStamp), _, fail) -> true ; CheckedAtStamp = -1),
|
|
35
|
+
normalize_integer(MaxAgeSeconds, MaxAge),
|
|
36
|
+
Context = _{
|
|
37
|
+
contradictions: Contradictions,
|
|
38
|
+
contradictionWitnesses: ContradictionWitnesses,
|
|
39
|
+
unsafeRules: UnsafeRules,
|
|
40
|
+
unverifiableRules: UnverifiableRules,
|
|
41
|
+
verificationSnapshot: Snapshot,
|
|
42
|
+
verificationCheckedAt: CheckedAtAtom,
|
|
43
|
+
verificationCheckedAtStamp: CheckedAtStamp,
|
|
44
|
+
verificationMaxAgeSeconds: MaxAge
|
|
45
|
+
}.
|
|
46
|
+
|
|
47
|
+
requirement_proof(ReqId, _ReqProps, _Context, Proof) :-
|
|
48
|
+
\+ kb:current_req(ReqId),
|
|
49
|
+
!,
|
|
50
|
+
proof_version(Version),
|
|
51
|
+
Proof = _{
|
|
52
|
+
proofVersion: Version,
|
|
53
|
+
proofStatus: not_applicable,
|
|
54
|
+
proofGaps: [],
|
|
55
|
+
proofRepairs: [],
|
|
56
|
+
proofStages: _{applicability: _{status: not_applicable}}
|
|
57
|
+
}.
|
|
58
|
+
requirement_proof(ReqId, ReqProps, Context, Proof) :-
|
|
59
|
+
semantic_inventory_stage(ReqProps, SemanticStage, Inventory),
|
|
60
|
+
logic_grounding_stage(ReqId, ReqProps, Inventory, Context, LogicStage),
|
|
61
|
+
contradiction_stage(ReqId, LogicStage.status, Context, ContradictionStage),
|
|
62
|
+
scenario_stage(ReqId, ScenarioStage, ScenarioIds),
|
|
63
|
+
scenario_test_stage(ScenarioIds, ScenarioTestStage, ScenarioTests),
|
|
64
|
+
passing_e2e_stage(ScenarioTests, Context, PassingE2eStage, PassingE2eTests),
|
|
65
|
+
executable_symbol_stage(PassingE2eTests, ExecutableStage, ExecutableSymbols),
|
|
66
|
+
production_symbol_stage(ReqId, PassingE2eTests, ProductionStage, ProductionSymbols),
|
|
67
|
+
source_coordinate_stage(ReqProps, ExecutableSymbols, ProductionSymbols, CoordinateStage),
|
|
68
|
+
Stages = _{
|
|
69
|
+
semanticInventory: SemanticStage,
|
|
70
|
+
logicGrounding: LogicStage,
|
|
71
|
+
contradictions: ContradictionStage,
|
|
72
|
+
scenarios: ScenarioStage,
|
|
73
|
+
scenarioTests: ScenarioTestStage,
|
|
74
|
+
passingE2e: PassingE2eStage,
|
|
75
|
+
executableSymbols: ExecutableStage,
|
|
76
|
+
productionSymbols: ProductionStage,
|
|
77
|
+
sourceCoordinates: CoordinateStage
|
|
78
|
+
},
|
|
79
|
+
proof_gaps(Stages, Gaps),
|
|
80
|
+
proof_repairs(Gaps, Repairs),
|
|
81
|
+
proof_status(Stages, Status),
|
|
82
|
+
proof_version(Version),
|
|
83
|
+
Proof = _{
|
|
84
|
+
proofVersion: Version,
|
|
85
|
+
proofStatus: Status,
|
|
86
|
+
proofGaps: Gaps,
|
|
87
|
+
proofRepairs: Repairs,
|
|
88
|
+
proofStages: Stages
|
|
89
|
+
}.
|
|
90
|
+
|
|
91
|
+
semantic_inventory_stage(Props, Stage, Entries) :-
|
|
92
|
+
( memberchk(semantic_inventory=RawInventory, Props),
|
|
93
|
+
inventory_entries(RawInventory, Entries0),
|
|
94
|
+
Entries0 \= []
|
|
95
|
+
-> Entries = Entries0,
|
|
96
|
+
inventory_counts(Entries, Modeled, Unresolved, Missing, Nonlogical, Malformed),
|
|
97
|
+
semantic_inventory_status(Unresolved, Missing, Malformed, Status),
|
|
98
|
+
length(Entries, Count),
|
|
99
|
+
Stage = _{
|
|
100
|
+
status: Status,
|
|
101
|
+
propositionCount: Count,
|
|
102
|
+
modeledCount: Modeled,
|
|
103
|
+
unresolvedCount: Unresolved,
|
|
104
|
+
missingCount: Missing,
|
|
105
|
+
nonlogicalCount: Nonlogical,
|
|
106
|
+
malformedCount: Malformed
|
|
107
|
+
}
|
|
108
|
+
; Entries = [],
|
|
109
|
+
Stage = _{
|
|
110
|
+
status: missing,
|
|
111
|
+
propositionCount: 0,
|
|
112
|
+
modeledCount: 0,
|
|
113
|
+
unresolvedCount: 0,
|
|
114
|
+
missingCount: 0,
|
|
115
|
+
nonlogicalCount: 0,
|
|
116
|
+
malformedCount: 0
|
|
117
|
+
}
|
|
118
|
+
).
|
|
119
|
+
|
|
120
|
+
inventory_counts(Entries, Modeled, Unresolved, Missing, Nonlogical, Malformed) :-
|
|
121
|
+
include(inventory_entry_status_is(modeled), Entries, ModeledEntries),
|
|
122
|
+
include(inventory_entry_unresolved, Entries, UnresolvedEntries),
|
|
123
|
+
include(inventory_entry_status_is(missing), Entries, MissingEntries),
|
|
124
|
+
include(inventory_entry_status_is(nonlogical), Entries, NonlogicalEntries),
|
|
125
|
+
exclude(valid_inventory_entry, Entries, MalformedEntries),
|
|
126
|
+
length(ModeledEntries, Modeled),
|
|
127
|
+
length(UnresolvedEntries, Unresolved),
|
|
128
|
+
length(MissingEntries, Missing),
|
|
129
|
+
length(NonlogicalEntries, Nonlogical),
|
|
130
|
+
length(MalformedEntries, Malformed).
|
|
131
|
+
|
|
132
|
+
semantic_inventory_status(_Unresolved, Missing, _Malformed, missing) :-
|
|
133
|
+
Missing > 0,
|
|
134
|
+
!.
|
|
135
|
+
semantic_inventory_status(_Unresolved, _Missing, Malformed, missing) :-
|
|
136
|
+
Malformed > 0,
|
|
137
|
+
!.
|
|
138
|
+
semantic_inventory_status(Unresolved, _Missing, _Malformed, unresolved) :-
|
|
139
|
+
Unresolved > 0,
|
|
140
|
+
!.
|
|
141
|
+
semantic_inventory_status(_, _, _, passed).
|
|
142
|
+
|
|
143
|
+
valid_inventory_entry(Entry) :-
|
|
144
|
+
inventory_entry_field(Entry, claim_key, ClaimKey),
|
|
145
|
+
normalize_atom(ClaimKey, ClaimKeyAtom),
|
|
146
|
+
ClaimKeyAtom \= '',
|
|
147
|
+
inventory_entry_field(Entry, claim_text, ClaimText),
|
|
148
|
+
normalize_atom(ClaimText, ClaimTextAtom),
|
|
149
|
+
ClaimTextAtom \= '',
|
|
150
|
+
inventory_entry_field(Entry, role, _),
|
|
151
|
+
inventory_entry_status(Entry, Status),
|
|
152
|
+
memberchk(Status, [modeled, ambiguous, ontology_gap, nonlogical, missing]),
|
|
153
|
+
inventory_entry_field(Entry, span, Span),
|
|
154
|
+
valid_inventory_span(Span).
|
|
155
|
+
|
|
156
|
+
valid_inventory_span(Span) :-
|
|
157
|
+
inventory_entry_field(Span, start, StartRaw),
|
|
158
|
+
inventory_entry_field(Span, end, EndRaw),
|
|
159
|
+
normalize_integer(StartRaw, Start),
|
|
160
|
+
normalize_integer(EndRaw, End),
|
|
161
|
+
Start >= 0,
|
|
162
|
+
End > Start.
|
|
163
|
+
|
|
164
|
+
inventory_entry_status_is(Expected, Entry) :-
|
|
165
|
+
inventory_entry_status(Entry, Expected).
|
|
166
|
+
|
|
167
|
+
inventory_entry_unresolved(Entry) :-
|
|
168
|
+
inventory_entry_status(Entry, Status),
|
|
169
|
+
memberchk(Status, [ambiguous, ontology_gap]).
|
|
170
|
+
|
|
171
|
+
inventory_entry_status(Entry, Status) :-
|
|
172
|
+
inventory_entry_field(Entry, status, RawStatus),
|
|
173
|
+
normalize_atom(RawStatus, Status).
|
|
174
|
+
|
|
175
|
+
inventory_entry_field(Dict, Key, Value) :-
|
|
176
|
+
is_dict(Dict),
|
|
177
|
+
get_dict(Key, Dict, Value).
|
|
178
|
+
inventory_entry_field(List, Key, Value) :-
|
|
179
|
+
is_list(List),
|
|
180
|
+
memberchk(Key=Value, List).
|
|
181
|
+
|
|
182
|
+
inventory_entries(Raw, Entries) :-
|
|
183
|
+
( is_list(Raw)
|
|
184
|
+
-> Entries = Raw
|
|
185
|
+
; Raw = ^^(Value, _)
|
|
186
|
+
-> inventory_entries(Value, Entries)
|
|
187
|
+
; (atom(Raw) ; string(Raw)),
|
|
188
|
+
catch(atom_json_dict(Raw, JsonEntries, [value_string_as(string)]), _, fail),
|
|
189
|
+
is_list(JsonEntries)
|
|
190
|
+
-> Entries = JsonEntries
|
|
191
|
+
; (atom(Raw) ; string(Raw)),
|
|
192
|
+
catch(term_string(PrologEntries, Raw), _, fail),
|
|
193
|
+
is_list(PrologEntries)
|
|
194
|
+
-> Entries = PrologEntries
|
|
195
|
+
; Entries = []
|
|
196
|
+
).
|
|
197
|
+
|
|
198
|
+
logic_grounding_stage(ReqId, Props, Inventory, Context, Stage) :-
|
|
199
|
+
requirement_claim_keys(Props, ManifestPresent, ManifestKeys),
|
|
200
|
+
inventory_assertive_keys(Inventory, InventoryKeys),
|
|
201
|
+
inventory_modeled_keys(Inventory, ModeledKeys),
|
|
202
|
+
findall(Evidence, valid_ground_evidence(ReqId, Evidence), Evidence0),
|
|
203
|
+
sort(Evidence0, Evidence),
|
|
204
|
+
grounded_claim_keys(Evidence, GroundedKeys),
|
|
205
|
+
missing_keys(ModeledKeys, GroundedKeys, MissingGroundKeys),
|
|
206
|
+
duplicate_ground_keys(Evidence, DuplicateGroundKeys),
|
|
207
|
+
inventory_ground_text_mismatches(Inventory, Evidence, ClaimTextMismatchKeys),
|
|
208
|
+
invalid_linked_claim_keys(ReqId, InvalidGroundKeys),
|
|
209
|
+
missing_keys(InventoryKeys, ManifestKeys, MissingManifestKeys),
|
|
210
|
+
missing_keys(ManifestKeys, InventoryKeys, ExtraManifestKeys),
|
|
211
|
+
linked_ground_keys(ReqId, LinkedGroundKeys),
|
|
212
|
+
missing_keys(LinkedGroundKeys, ManifestKeys, UndeclaredGroundKeys),
|
|
213
|
+
linked_rule_fact_ids(ReqId, RuleFactIds),
|
|
214
|
+
invalid_rule_fact_ids(RuleFactIds, Context, InvalidRuleFactIds),
|
|
215
|
+
logic_grounding_status(
|
|
216
|
+
ManifestPresent,
|
|
217
|
+
MissingGroundKeys,
|
|
218
|
+
MissingManifestKeys,
|
|
219
|
+
ExtraManifestKeys,
|
|
220
|
+
DuplicateGroundKeys,
|
|
221
|
+
ClaimTextMismatchKeys,
|
|
222
|
+
InvalidGroundKeys,
|
|
223
|
+
UndeclaredGroundKeys,
|
|
224
|
+
InvalidRuleFactIds,
|
|
225
|
+
Status
|
|
226
|
+
),
|
|
227
|
+
Stage = _{
|
|
228
|
+
status: Status,
|
|
229
|
+
manifestClaims: ManifestKeys,
|
|
230
|
+
inventoryClaims: InventoryKeys,
|
|
231
|
+
modeledClaims: ModeledKeys,
|
|
232
|
+
groundedClaims: GroundedKeys,
|
|
233
|
+
evidence: Evidence,
|
|
234
|
+
missingGroundClaims: MissingGroundKeys,
|
|
235
|
+
duplicateGroundClaims: DuplicateGroundKeys,
|
|
236
|
+
claimTextMismatchClaims: ClaimTextMismatchKeys,
|
|
237
|
+
invalidGroundClaims: InvalidGroundKeys,
|
|
238
|
+
missingManifestClaims: MissingManifestKeys,
|
|
239
|
+
extraManifestClaims: ExtraManifestKeys,
|
|
240
|
+
undeclaredGroundClaims: UndeclaredGroundKeys,
|
|
241
|
+
invalidRuleFacts: InvalidRuleFactIds
|
|
242
|
+
}.
|
|
243
|
+
|
|
244
|
+
requirement_claim_keys(Props, true, Keys) :-
|
|
245
|
+
memberchk(logic_claims=RawKeys, Props),
|
|
246
|
+
kb:normalize_term_atom_list(RawKeys, Keys0),
|
|
247
|
+
sort(Keys0, Keys),
|
|
248
|
+
Keys \= [],
|
|
249
|
+
!.
|
|
250
|
+
requirement_claim_keys(_, false, []).
|
|
251
|
+
|
|
252
|
+
inventory_assertive_keys(Entries, Keys) :-
|
|
253
|
+
findall(Key,
|
|
254
|
+
(member(Entry, Entries),
|
|
255
|
+
inventory_entry_status(Entry, Status),
|
|
256
|
+
Status \= nonlogical,
|
|
257
|
+
inventory_entry_field(Entry, claim_key, RawKey),
|
|
258
|
+
normalize_atom(RawKey, Key)),
|
|
259
|
+
Keys0),
|
|
260
|
+
sort(Keys0, Keys).
|
|
261
|
+
|
|
262
|
+
inventory_modeled_keys(Entries, Keys) :-
|
|
263
|
+
findall(Key,
|
|
264
|
+
(member(Entry, Entries),
|
|
265
|
+
inventory_entry_status(Entry, modeled),
|
|
266
|
+
inventory_entry_field(Entry, claim_key, RawKey),
|
|
267
|
+
normalize_atom(RawKey, Key)),
|
|
268
|
+
Keys0),
|
|
269
|
+
sort(Keys0, Keys).
|
|
270
|
+
|
|
271
|
+
valid_ground_evidence(ReqId, _{claimKey: ClaimKey, claimText: ClaimText, factId: FactId, lane: property}) :-
|
|
272
|
+
kb_relationship(requires_property, ReqId, FactId),
|
|
273
|
+
ground_claim_provenance(FactId, property_value, ClaimKey, ClaimText),
|
|
274
|
+
kb:fact_property_tuple(FactId, SubjectKey, _, _, _, _, _, _, _),
|
|
275
|
+
kb_relationship(constrains, ReqId, SubjectFactId),
|
|
276
|
+
kb:fact_subject_key(SubjectFactId, SubjectKey).
|
|
277
|
+
valid_ground_evidence(ReqId, _{claimKey: ClaimKey, claimText: ClaimText, factId: FactId, lane: predicate}) :-
|
|
278
|
+
kb_relationship(requires_predicate, ReqId, FactId),
|
|
279
|
+
ground_claim_provenance(FactId, predicate, ClaimKey, ClaimText),
|
|
280
|
+
kb:predicate_fact(FactId, _, _, _, _).
|
|
281
|
+
valid_ground_evidence(ReqId, _{claimKey: ClaimKey, claimText: ClaimText, factId: FactId, lane: rule}) :-
|
|
282
|
+
kb_relationship(requires_rule, ReqId, FactId),
|
|
283
|
+
ground_claim_provenance(FactId, rule, ClaimKey, ClaimText),
|
|
284
|
+
valid_rule_fact(FactId).
|
|
285
|
+
|
|
286
|
+
ground_claim_provenance(FactId, ExpectedKind, ClaimKey, ClaimText) :-
|
|
287
|
+
kb_entity(FactId, fact, Props),
|
|
288
|
+
memberchk(fact_kind=RawKind, Props),
|
|
289
|
+
normalize_atom(RawKind, ExpectedKind),
|
|
290
|
+
memberchk(claim_key=RawClaimKey, Props),
|
|
291
|
+
normalize_atom(RawClaimKey, ClaimKey),
|
|
292
|
+
memberchk(claim_text=RawClaimText, Props),
|
|
293
|
+
normalize_atom(RawClaimText, ClaimText),
|
|
294
|
+
ClaimText \= ''.
|
|
295
|
+
|
|
296
|
+
valid_rule_fact(FactId) :-
|
|
297
|
+
kb_entity(FactId, fact, Props),
|
|
298
|
+
logic_rule_safety(Props, []),
|
|
299
|
+
memberchk(rule_schema_id=RawSchemaId, Props),
|
|
300
|
+
normalize_atom(RawSchemaId, SchemaId),
|
|
301
|
+
kb_entity(SchemaId, fact, SchemaProps),
|
|
302
|
+
valid_rule_schema_props(SchemaProps).
|
|
303
|
+
|
|
304
|
+
valid_rule_schema_props(Props) :-
|
|
305
|
+
memberchk(fact_kind=RawKind, Props),
|
|
306
|
+
normalize_atom(RawKind, rule_schema),
|
|
307
|
+
memberchk(rule_name=RawName, Props),
|
|
308
|
+
normalize_atom(RawName, Name),
|
|
309
|
+
Name \= '',
|
|
310
|
+
memberchk(argument_names=RawNames, Props),
|
|
311
|
+
memberchk(argument_types=RawTypes, Props),
|
|
312
|
+
normalize_atom_list(RawNames, Names),
|
|
313
|
+
normalize_atom_list(RawTypes, Types),
|
|
314
|
+
same_length(Names, Types).
|
|
315
|
+
|
|
316
|
+
grounded_claim_keys(Evidence, Keys) :-
|
|
317
|
+
findall(Key, (member(Item, Evidence), Key = Item.claimKey), Keys0),
|
|
318
|
+
sort(Keys0, Keys).
|
|
319
|
+
|
|
320
|
+
duplicate_ground_keys(Evidence, Keys) :-
|
|
321
|
+
grounded_claim_keys(Evidence, UniqueKeys),
|
|
322
|
+
include(has_duplicate_ground(Evidence), UniqueKeys, Keys).
|
|
323
|
+
|
|
324
|
+
has_duplicate_ground(Evidence, Key) :-
|
|
325
|
+
include(evidence_for_key(Key), Evidence, Matches),
|
|
326
|
+
length(Matches, Count),
|
|
327
|
+
Count > 1.
|
|
328
|
+
|
|
329
|
+
evidence_for_key(Key, Evidence) :-
|
|
330
|
+
Evidence.claimKey == Key.
|
|
331
|
+
|
|
332
|
+
inventory_ground_text_mismatches(Inventory, Evidence, Keys) :-
|
|
333
|
+
findall(Key,
|
|
334
|
+
(member(Item, Evidence),
|
|
335
|
+
Key = Item.claimKey,
|
|
336
|
+
member(Entry, Inventory),
|
|
337
|
+
inventory_entry_field(Entry, claim_key, RawKey),
|
|
338
|
+
normalize_atom(RawKey, Key),
|
|
339
|
+
inventory_entry_field(Entry, claim_text, RawInventoryText),
|
|
340
|
+
normalize_atom(RawInventoryText, InventoryText),
|
|
341
|
+
InventoryText \== Item.claimText),
|
|
342
|
+
Keys0),
|
|
343
|
+
sort(Keys0, Keys).
|
|
344
|
+
|
|
345
|
+
linked_ground_keys(ReqId, Keys) :-
|
|
346
|
+
findall(Key, linked_ground_claim_key(ReqId, Key), Keys0),
|
|
347
|
+
sort(Keys0, Keys).
|
|
348
|
+
|
|
349
|
+
linked_ground_claim_key(ReqId, ClaimKey) :-
|
|
350
|
+
member(Relationship-Kind, [requires_property-property_value, requires_predicate-predicate, requires_rule-rule]),
|
|
351
|
+
kb_relationship(Relationship, ReqId, FactId),
|
|
352
|
+
ground_claim_provenance(FactId, Kind, ClaimKey, _).
|
|
353
|
+
|
|
354
|
+
invalid_linked_claim_keys(ReqId, Keys) :-
|
|
355
|
+
findall(ClaimKey,
|
|
356
|
+
(linked_ground_claim_key(ReqId, ClaimKey),
|
|
357
|
+
\+ valid_ground_evidence_for_key(ReqId, ClaimKey)),
|
|
358
|
+
Keys0),
|
|
359
|
+
sort(Keys0, Keys).
|
|
360
|
+
|
|
361
|
+
valid_ground_evidence_for_key(ReqId, ClaimKey) :-
|
|
362
|
+
valid_ground_evidence(ReqId, Evidence),
|
|
363
|
+
Evidence.claimKey == ClaimKey.
|
|
364
|
+
|
|
365
|
+
linked_rule_fact_ids(ReqId, FactIds) :-
|
|
366
|
+
findall(FactId, kb_relationship(requires_rule, ReqId, FactId), FactIds0),
|
|
367
|
+
sort(FactIds0, FactIds).
|
|
368
|
+
|
|
369
|
+
invalid_rule_fact_ids(RuleFactIds, Context, InvalidFactIds) :-
|
|
370
|
+
findall(FactId,
|
|
371
|
+
(member(FactId, RuleFactIds),
|
|
372
|
+
(violation_for_entity(Context.unsafeRules, FactId)
|
|
373
|
+
; violation_for_entity(Context.unverifiableRules, FactId))),
|
|
374
|
+
Invalid0),
|
|
375
|
+
sort(Invalid0, InvalidFactIds).
|
|
376
|
+
|
|
377
|
+
logic_grounding_status(false, _, _, _, _, _, _, _, _, missing) :- !.
|
|
378
|
+
logic_grounding_status(true, MissingGround, MissingManifest, _, _, _, _, _, _, missing) :-
|
|
379
|
+
(MissingGround \= [] ; MissingManifest \= []),
|
|
380
|
+
!.
|
|
381
|
+
logic_grounding_status(true, _, _, ExtraManifest, Duplicate, ClaimTextMismatch, Invalid, Undeclared, InvalidRules, unresolved) :-
|
|
382
|
+
(ExtraManifest \= [] ; Duplicate \= [] ; ClaimTextMismatch \= [] ; Invalid \= [] ; Undeclared \= [] ; InvalidRules \= []),
|
|
383
|
+
!.
|
|
384
|
+
logic_grounding_status(true, _, _, _, _, _, _, _, _, passed).
|
|
385
|
+
|
|
386
|
+
missing_keys(Expected, Actual, Missing) :-
|
|
387
|
+
findall(Key, (member(Key, Expected), \+ memberchk(Key, Actual)), Missing).
|
|
388
|
+
|
|
389
|
+
contradiction_stage(ReqId, _LogicStatus, Context, Stage) :-
|
|
390
|
+
requirement_contradiction_witnesses(ReqId, Context.contradictionWitnesses, Conflicts),
|
|
391
|
+
member(Witness, Conflicts),
|
|
392
|
+
Witness.status == contradiction,
|
|
393
|
+
Conflicts \= [],
|
|
394
|
+
!,
|
|
395
|
+
Stage = _{status: blocked, outcome: conflict_found, conflicts: Conflicts}.
|
|
396
|
+
contradiction_stage(ReqId, _LogicStatus, Context, Stage) :-
|
|
397
|
+
requirement_contradiction_witnesses(ReqId, Context.contradictionWitnesses, Conflicts),
|
|
398
|
+
Conflicts \= [],
|
|
399
|
+
!,
|
|
400
|
+
Stage = _{status: unresolved, outcome: analysis_incomplete, conflicts: Conflicts}.
|
|
401
|
+
contradiction_stage(ReqId, _LogicStatus, Context, Stage) :-
|
|
402
|
+
requirement_contradictions(ReqId, Context.contradictions, Conflicts),
|
|
403
|
+
Conflicts \= [],
|
|
404
|
+
!,
|
|
405
|
+
Stage = _{status: blocked, outcome: conflict_found, conflicts: Conflicts}.
|
|
406
|
+
contradiction_stage(_ReqId, LogicStatus, _Context, Stage) :-
|
|
407
|
+
LogicStatus \= passed,
|
|
408
|
+
!,
|
|
409
|
+
Stage = _{status: unresolved, outcome: incomplete_grounding, conflicts: []}.
|
|
410
|
+
contradiction_stage(_ReqId, passed, _Context, _{status: passed, outcome: no_conflict_found, conflicts: []}).
|
|
411
|
+
|
|
412
|
+
requirement_contradictions(ReqId, Violations, Conflicts) :-
|
|
413
|
+
include(violation_mentions_requirement(ReqId), Violations, Conflicts0),
|
|
414
|
+
maplist(violation_description, Conflicts0, Conflicts).
|
|
415
|
+
|
|
416
|
+
requirement_contradiction_witnesses(ReqId, Witnesses, Conflicts) :-
|
|
417
|
+
include(witness_mentions_requirement(ReqId), Witnesses, Conflicts).
|
|
418
|
+
|
|
419
|
+
witness_mentions_requirement(ReqId, Witness) :-
|
|
420
|
+
memberchk(ReqId, Witness.requirements).
|
|
421
|
+
|
|
422
|
+
violation_mentions_requirement(ReqId, violation(_, EntityId, _, _, _)) :-
|
|
423
|
+
normalize_atom(EntityId, Pair),
|
|
424
|
+
atomic_list_concat(Ids, '/', Pair),
|
|
425
|
+
memberchk(ReqId, Ids).
|
|
426
|
+
|
|
427
|
+
violation_description(violation(_, EntityId, Description, _, _), _{requirements: EntityId, reason: Description}).
|
|
428
|
+
|
|
429
|
+
violation_for_entity(Violations, EntityId) :-
|
|
430
|
+
member(violation(_, RawId, _, _, _), Violations),
|
|
431
|
+
normalize_atom(RawId, EntityId).
|
|
432
|
+
|
|
433
|
+
scenario_stage(ReqId, Stage, ScenarioIds) :-
|
|
434
|
+
findall(ScenarioId,
|
|
435
|
+
(kb_relationship(specified_by, ReqId, ScenarioId), kb_entity(ScenarioId, scenario, _)),
|
|
436
|
+
ScenarioIds0),
|
|
437
|
+
sort(ScenarioIds0, ScenarioIds),
|
|
438
|
+
(ScenarioIds = [] -> Status = missing ; Status = passed),
|
|
439
|
+
Stage = _{status: Status, scenarios: ScenarioIds}.
|
|
440
|
+
|
|
441
|
+
scenario_test_stage(ScenarioIds, Stage, ScenarioTests) :-
|
|
442
|
+
findall(TestId,
|
|
443
|
+
(member(ScenarioId, ScenarioIds), scenario_test(ScenarioId, TestId)),
|
|
444
|
+
ScenarioTests0),
|
|
445
|
+
sort(ScenarioTests0, ScenarioTests),
|
|
446
|
+
(ScenarioTests = [] -> Status = missing ; Status = passed),
|
|
447
|
+
Stage = _{status: Status, tests: ScenarioTests}.
|
|
448
|
+
|
|
449
|
+
scenario_test(ScenarioId, TestId) :-
|
|
450
|
+
kb_relationship(verified_by, ScenarioId, TestId),
|
|
451
|
+
kb_entity(TestId, test, _).
|
|
452
|
+
scenario_test(ScenarioId, TestId) :-
|
|
453
|
+
kb_relationship(validates, TestId, ScenarioId),
|
|
454
|
+
kb_entity(TestId, test, _).
|
|
455
|
+
|
|
456
|
+
passing_e2e_stage(ScenarioTests, Context, Stage, PassingE2eTests) :-
|
|
457
|
+
maplist(test_receipt_evidence(Context), ScenarioTests, Evidence),
|
|
458
|
+
evidence_tests_with_state(Evidence, passed, PassingE2eTests),
|
|
459
|
+
evidence_tests_with_state(Evidence, missing, MissingReceiptTests),
|
|
460
|
+
evidence_tests_with_state(Evidence, stale, StaleReceiptTests),
|
|
461
|
+
evidence_tests_with_state(Evidence, failed, FailedReceiptTests),
|
|
462
|
+
evidence_tests_with_state(Evidence, invalid, InvalidReceiptTests),
|
|
463
|
+
evidence_tests_with_state(Evidence, snapshot_unavailable, SnapshotUnavailableTests),
|
|
464
|
+
evidence_tests_with_state(Evidence, not_end_to_end, NonEndToEndTests),
|
|
465
|
+
passing_e2e_status(PassingE2eTests, InvalidReceiptTests, SnapshotUnavailableTests, Status),
|
|
466
|
+
Stage = _{
|
|
467
|
+
status: Status,
|
|
468
|
+
tests: PassingE2eTests,
|
|
469
|
+
receiptEvidence: Evidence,
|
|
470
|
+
missingReceiptTests: MissingReceiptTests,
|
|
471
|
+
staleReceiptTests: StaleReceiptTests,
|
|
472
|
+
failedReceiptTests: FailedReceiptTests,
|
|
473
|
+
invalidReceiptTests: InvalidReceiptTests,
|
|
474
|
+
snapshotUnavailableTests: SnapshotUnavailableTests,
|
|
475
|
+
nonEndToEndTests: NonEndToEndTests,
|
|
476
|
+
currentCodeSnapshot: Context.verificationSnapshot,
|
|
477
|
+
checkedAt: Context.verificationCheckedAt,
|
|
478
|
+
maxAgeSeconds: Context.verificationMaxAgeSeconds
|
|
479
|
+
}.
|
|
480
|
+
|
|
481
|
+
passing_e2e_status(Passing, _, _, passed) :- Passing \= [], !.
|
|
482
|
+
passing_e2e_status([], Invalid, _, unresolved) :- Invalid \= [], !.
|
|
483
|
+
passing_e2e_status([], _, SnapshotUnavailable, unresolved) :- SnapshotUnavailable \= [], !.
|
|
484
|
+
passing_e2e_status([], _, _, missing).
|
|
485
|
+
|
|
486
|
+
evidence_tests_with_state(Evidence, State, TestIds) :-
|
|
487
|
+
findall(TestId,
|
|
488
|
+
(member(Item, Evidence), Item.state == State, TestId = Item.testId),
|
|
489
|
+
TestIds0),
|
|
490
|
+
sort(TestIds0, TestIds).
|
|
491
|
+
|
|
492
|
+
test_receipt_evidence(Context, TestId, Evidence) :-
|
|
493
|
+
kb_entity(TestId, test, Props),
|
|
494
|
+
test_scope(Props, Scope),
|
|
495
|
+
( Scope \= end_to_end
|
|
496
|
+
-> Evidence = _{testId: TestId, state: not_end_to_end, scope: Scope}
|
|
497
|
+
; Context.verificationSnapshot == unknown
|
|
498
|
+
-> Evidence = _{testId: TestId, state: snapshot_unavailable, scope: Scope}
|
|
499
|
+
; verification_receipt_entries(Props, Receipts, ReceiptPropertyPresent),
|
|
500
|
+
receipt_evidence_state(TestId, Scope, Receipts, ReceiptPropertyPresent, Context, Evidence)
|
|
501
|
+
).
|
|
502
|
+
|
|
503
|
+
receipt_evidence_state(TestId, Scope, [], false, _Context,
|
|
504
|
+
_{testId: TestId, state: missing, scope: Scope, receiptCount: 0}) :- !.
|
|
505
|
+
receipt_evidence_state(TestId, Scope, Receipts, _Present, _Context,
|
|
506
|
+
_{testId: TestId, state: invalid, scope: Scope, receiptCount: Count}) :-
|
|
507
|
+
length(Receipts, Count),
|
|
508
|
+
(Receipts == []
|
|
509
|
+
; member(Receipt, Receipts), \+ valid_receipt_shape(TestId, Scope, Receipt)
|
|
510
|
+
; \+ chronological_receipt_history(Receipts)),
|
|
511
|
+
!.
|
|
512
|
+
receipt_evidence_state(TestId, Scope, Receipts, _Present, Context, Evidence) :-
|
|
513
|
+
include(receipt_for_snapshot(Context.verificationSnapshot), Receipts, CurrentReceipts),
|
|
514
|
+
( CurrentReceipts == []
|
|
515
|
+
-> length(Receipts, Count),
|
|
516
|
+
Evidence = _{testId: TestId, state: stale, scope: Scope, receiptCount: Count}
|
|
517
|
+
; latest_receipt(CurrentReceipts, Latest, FinishedStamp),
|
|
518
|
+
receipt_runtime_state(Latest, FinishedStamp, Context, State, AgeSeconds),
|
|
519
|
+
receipt_evidence_dict(TestId, Scope, Latest, State, AgeSeconds, Evidence)
|
|
520
|
+
).
|
|
521
|
+
|
|
522
|
+
verification_receipt_entries(Props, Receipts, Present) :-
|
|
523
|
+
( memberchk(verification_receipts=Raw, Props)
|
|
524
|
+
-> Present = true,
|
|
525
|
+
inventory_entries(Raw, Receipts)
|
|
526
|
+
; Present = false,
|
|
527
|
+
Receipts = []
|
|
528
|
+
).
|
|
529
|
+
|
|
530
|
+
valid_receipt_shape(TestId, Scope, Receipt) :-
|
|
531
|
+
inventory_entry_field(Receipt, version, RawVersion),
|
|
532
|
+
normalize_receipt_atom(RawVersion, 'kibi.verification-receipt.v1'),
|
|
533
|
+
inventory_entry_field(Receipt, receipt_id, RawReceiptId),
|
|
534
|
+
normalize_receipt_atom(RawReceiptId, ReceiptId), valid_receipt_id(ReceiptId),
|
|
535
|
+
inventory_entry_field(Receipt, test_id, RawTestId),
|
|
536
|
+
normalize_receipt_atom(RawTestId, TestId),
|
|
537
|
+
inventory_entry_field(Receipt, runner, RawRunner),
|
|
538
|
+
normalize_receipt_atom(RawRunner, Runner), Runner \= '',
|
|
539
|
+
inventory_entry_field(Receipt, command, RawCommand),
|
|
540
|
+
normalize_receipt_atom(RawCommand, Command), Command \= '',
|
|
541
|
+
inventory_entry_field(Receipt, scope, RawScope),
|
|
542
|
+
normalize_receipt_atom(RawScope, Scope),
|
|
543
|
+
inventory_entry_field(Receipt, outcome, RawOutcome),
|
|
544
|
+
normalize_receipt_atom(RawOutcome, Outcome),
|
|
545
|
+
memberchk(Outcome, [passed, failed, errored, cancelled, skipped]),
|
|
546
|
+
inventory_entry_field(Receipt, code_snapshot, RawSnapshot),
|
|
547
|
+
normalize_receipt_atom(RawSnapshot, Snapshot), valid_sha256(Snapshot),
|
|
548
|
+
inventory_entry_field(Receipt, environment_hash, RawEnvironmentHash),
|
|
549
|
+
normalize_receipt_atom(RawEnvironmentHash, EnvironmentHash), valid_sha256(EnvironmentHash),
|
|
550
|
+
inventory_entry_field(Receipt, artifact_digest, RawArtifactDigest),
|
|
551
|
+
normalize_receipt_atom(RawArtifactDigest, ArtifactDigest), valid_sha256(ArtifactDigest),
|
|
552
|
+
receipt_timestamp(Receipt, started_at, StartedStamp),
|
|
553
|
+
receipt_timestamp(Receipt, finished_at, FinishedStamp),
|
|
554
|
+
FinishedStamp >= StartedStamp.
|
|
555
|
+
|
|
556
|
+
valid_sha256(Value) :-
|
|
557
|
+
atom_length(Value, 64),
|
|
558
|
+
atom_chars(Value, Chars),
|
|
559
|
+
forall(member(Char, Chars), memberchk(Char, ['0','1','2','3','4','5','6','7','8','9',a,b,c,d,e,f])).
|
|
560
|
+
|
|
561
|
+
valid_receipt_id(Value) :-
|
|
562
|
+
re_match('^VR-[A-Za-z0-9][A-Za-z0-9._:-]{7,127}$', Value).
|
|
563
|
+
|
|
564
|
+
receipt_timestamp(Receipt, Field, Stamp) :-
|
|
565
|
+
inventory_entry_field(Receipt, Field, RawValue),
|
|
566
|
+
normalize_receipt_atom(RawValue, Value),
|
|
567
|
+
re_match('^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(?:[.][0-9]+)?(?:Z|[+-][0-9]{2}:[0-9]{2})$', Value),
|
|
568
|
+
catch(parse_time(Value, Stamp), _, fail).
|
|
569
|
+
|
|
570
|
+
chronological_receipt_history(Receipts) :-
|
|
571
|
+
maplist(receipt_finished_stamp, Receipts, FinishedStamps),
|
|
572
|
+
strictly_increasing(FinishedStamps).
|
|
573
|
+
|
|
574
|
+
receipt_finished_stamp(Receipt, Stamp) :-
|
|
575
|
+
receipt_timestamp(Receipt, finished_at, Stamp).
|
|
576
|
+
|
|
577
|
+
strictly_increasing([]).
|
|
578
|
+
strictly_increasing([_]).
|
|
579
|
+
strictly_increasing([Left, Right|Rest]) :-
|
|
580
|
+
Right > Left,
|
|
581
|
+
strictly_increasing([Right|Rest]).
|
|
582
|
+
|
|
583
|
+
receipt_for_snapshot(Snapshot, Receipt) :-
|
|
584
|
+
inventory_entry_field(Receipt, code_snapshot, RawSnapshot),
|
|
585
|
+
normalize_receipt_atom(RawSnapshot, Snapshot).
|
|
586
|
+
|
|
587
|
+
latest_receipt(Receipts, Latest, FinishedStamp) :-
|
|
588
|
+
findall(Stamp-Receipt,
|
|
589
|
+
(member(Receipt, Receipts), receipt_timestamp(Receipt, finished_at, Stamp)),
|
|
590
|
+
Pairs),
|
|
591
|
+
keysort(Pairs, Sorted),
|
|
592
|
+
last(Sorted, FinishedStamp-Latest).
|
|
593
|
+
|
|
594
|
+
receipt_runtime_state(_Receipt, FinishedStamp, Context, invalid, AgeSeconds) :-
|
|
595
|
+
AgeSeconds is Context.verificationCheckedAtStamp - FinishedStamp,
|
|
596
|
+
AgeSeconds < -300,
|
|
597
|
+
!.
|
|
598
|
+
receipt_runtime_state(_Receipt, FinishedStamp, Context, stale, AgeSeconds) :-
|
|
599
|
+
AgeSeconds is Context.verificationCheckedAtStamp - FinishedStamp,
|
|
600
|
+
AgeSeconds > Context.verificationMaxAgeSeconds,
|
|
601
|
+
!.
|
|
602
|
+
receipt_runtime_state(Receipt, FinishedStamp, Context, State, AgeSeconds) :-
|
|
603
|
+
AgeSeconds is Context.verificationCheckedAtStamp - FinishedStamp,
|
|
604
|
+
inventory_entry_field(Receipt, outcome, RawOutcome),
|
|
605
|
+
normalize_receipt_atom(RawOutcome, Outcome),
|
|
606
|
+
(Outcome == passed -> State = passed ; State = failed).
|
|
607
|
+
|
|
608
|
+
receipt_evidence_dict(TestId, Scope, Receipt, State, AgeSeconds, Evidence) :-
|
|
609
|
+
receipt_string_field(Receipt, receipt_id, ReceiptId),
|
|
610
|
+
receipt_string_field(Receipt, runner, Runner),
|
|
611
|
+
receipt_string_field(Receipt, command, Command),
|
|
612
|
+
receipt_string_field(Receipt, outcome, Outcome),
|
|
613
|
+
receipt_string_field(Receipt, code_snapshot, CodeSnapshot),
|
|
614
|
+
receipt_string_field(Receipt, environment_hash, EnvironmentHash),
|
|
615
|
+
receipt_string_field(Receipt, started_at, StartedAt),
|
|
616
|
+
receipt_string_field(Receipt, finished_at, FinishedAt),
|
|
617
|
+
receipt_string_field(Receipt, artifact_digest, ArtifactDigest),
|
|
618
|
+
Evidence = _{
|
|
619
|
+
testId: TestId,
|
|
620
|
+
state: State,
|
|
621
|
+
scope: Scope,
|
|
622
|
+
receiptId: ReceiptId,
|
|
623
|
+
runner: Runner,
|
|
624
|
+
command: Command,
|
|
625
|
+
outcome: Outcome,
|
|
626
|
+
codeSnapshot: CodeSnapshot,
|
|
627
|
+
environmentHash: EnvironmentHash,
|
|
628
|
+
startedAt: StartedAt,
|
|
629
|
+
finishedAt: FinishedAt,
|
|
630
|
+
artifactDigest: ArtifactDigest,
|
|
631
|
+
ageSeconds: AgeSeconds
|
|
632
|
+
}.
|
|
633
|
+
|
|
634
|
+
receipt_string_field(Receipt, Field, Value) :-
|
|
635
|
+
inventory_entry_field(Receipt, Field, RawValue),
|
|
636
|
+
normalize_receipt_atom(RawValue, Value).
|
|
637
|
+
|
|
638
|
+
normalize_receipt_atom(^^(Value, _Type), Atom) :-
|
|
639
|
+
!,
|
|
640
|
+
normalize_receipt_atom(Value, Atom).
|
|
641
|
+
normalize_receipt_atom(literal(type(_, Value)), Atom) :-
|
|
642
|
+
!,
|
|
643
|
+
normalize_receipt_atom(Value, Atom).
|
|
644
|
+
normalize_receipt_atom(Value, Atom) :-
|
|
645
|
+
string(Value),
|
|
646
|
+
!,
|
|
647
|
+
atom_string(Atom, Value).
|
|
648
|
+
normalize_receipt_atom(Value, Value) :-
|
|
649
|
+
atom(Value),
|
|
650
|
+
!.
|
|
651
|
+
normalize_receipt_atom(Value, Atom) :-
|
|
652
|
+
term_string(Value, String),
|
|
653
|
+
atom_string(Atom, String).
|
|
654
|
+
|
|
655
|
+
test_scope(Props, Scope) :-
|
|
656
|
+
memberchk(verification_scope=RawScope, Props),
|
|
657
|
+
!,
|
|
658
|
+
normalize_atom(RawScope, Scope).
|
|
659
|
+
test_scope(Props, end_to_end) :-
|
|
660
|
+
(memberchk(tags=Tags, Props), member(RawTag, Tags), normalize_atom(RawTag, Tag), downcase_atom(Tag, e2e)
|
|
661
|
+
; memberchk(source=RawSource, Props), normalize_atom(RawSource, Source), downcase_atom(Source, Lower), sub_atom(Lower, _, _, _, e2e)),
|
|
662
|
+
!.
|
|
663
|
+
test_scope(_, unknown).
|
|
664
|
+
|
|
665
|
+
executable_symbol_stage([], _{status: blocked, symbols: [], missingTests: []}, []).
|
|
666
|
+
executable_symbol_stage(PassingE2eTests, Stage, Symbols) :-
|
|
667
|
+
PassingE2eTests \= [],
|
|
668
|
+
findall(SymbolId,
|
|
669
|
+
(member(TestId, PassingE2eTests),
|
|
670
|
+
kb_relationship(executable_for, SymbolId, TestId),
|
|
671
|
+
kb_entity(SymbolId, symbol, _)),
|
|
672
|
+
Symbols0),
|
|
673
|
+
sort(Symbols0, Symbols),
|
|
674
|
+
include(test_missing_executable_symbol, PassingE2eTests, MissingTests),
|
|
675
|
+
(MissingTests = [] -> Status = passed ; Status = missing),
|
|
676
|
+
Stage = _{status: Status, symbols: Symbols, missingTests: MissingTests}.
|
|
677
|
+
|
|
678
|
+
test_missing_executable_symbol(TestId) :-
|
|
679
|
+
\+ (kb_relationship(executable_for, SymbolId, TestId), kb_entity(SymbolId, symbol, _)).
|
|
680
|
+
|
|
681
|
+
production_symbol_stage(ReqId, PassingE2eTests, Stage, ProductionSymbols) :-
|
|
682
|
+
findall(SymbolId,
|
|
683
|
+
(kb_relationship(implements, SymbolId, ReqId),
|
|
684
|
+
kb_entity(SymbolId, symbol, _),
|
|
685
|
+
\+ kb:executable_test_symbol(SymbolId)),
|
|
686
|
+
Production0),
|
|
687
|
+
sort(Production0, ProductionSymbols),
|
|
688
|
+
include(symbol_not_covered_by_tests(PassingE2eTests), ProductionSymbols, UncoveredSymbols),
|
|
689
|
+
production_stage_status(ProductionSymbols, PassingE2eTests, UncoveredSymbols, Status),
|
|
690
|
+
Stage = _{status: Status, symbols: ProductionSymbols, uncoveredSymbols: UncoveredSymbols}.
|
|
691
|
+
|
|
692
|
+
production_stage_status([], _, _, missing) :- !.
|
|
693
|
+
production_stage_status(_, [], _, blocked) :- !.
|
|
694
|
+
production_stage_status(_, _, [], passed) :- !.
|
|
695
|
+
production_stage_status(_, _, _, missing).
|
|
696
|
+
|
|
697
|
+
symbol_not_covered_by_tests(Tests, SymbolId) :-
|
|
698
|
+
\+ (member(TestId, Tests), kb_relationship(covered_by, SymbolId, TestId)).
|
|
699
|
+
|
|
700
|
+
source_coordinate_stage(ReqProps, ExecutableSymbols, ProductionSymbols, Stage) :-
|
|
701
|
+
append(ExecutableSymbols, ProductionSymbols, Symbols0),
|
|
702
|
+
sort(Symbols0, Symbols),
|
|
703
|
+
include(symbol_missing_coordinates, Symbols, MissingSymbols),
|
|
704
|
+
( \+ nonempty_source(ReqProps)
|
|
705
|
+
-> Status = missing,
|
|
706
|
+
RequirementSource = missing
|
|
707
|
+
; RequirementSource = present,
|
|
708
|
+
coordinate_stage_status(Symbols, MissingSymbols, Status)
|
|
709
|
+
),
|
|
710
|
+
Stage = _{
|
|
711
|
+
status: Status,
|
|
712
|
+
requirementSource: RequirementSource,
|
|
713
|
+
symbols: Symbols,
|
|
714
|
+
missingSymbols: MissingSymbols
|
|
715
|
+
}.
|
|
716
|
+
|
|
717
|
+
coordinate_stage_status([], _, blocked) :- !.
|
|
718
|
+
coordinate_stage_status(_, [], passed) :- !.
|
|
719
|
+
coordinate_stage_status(_, _, missing).
|
|
720
|
+
|
|
721
|
+
symbol_missing_coordinates(SymbolId) :-
|
|
722
|
+
kb_entity(SymbolId, symbol, Props),
|
|
723
|
+
\+ valid_symbol_coordinates(Props).
|
|
724
|
+
|
|
725
|
+
valid_symbol_coordinates(Props) :-
|
|
726
|
+
memberchk(sourceFile=RawSourceFile, Props),
|
|
727
|
+
normalize_atom(RawSourceFile, SourceFile),
|
|
728
|
+
SourceFile \= '',
|
|
729
|
+
memberchk(sourceLine=RawLine, Props),
|
|
730
|
+
memberchk(sourceColumn=RawColumn, Props),
|
|
731
|
+
memberchk(sourceEndLine=RawEndLine, Props),
|
|
732
|
+
memberchk(sourceEndColumn=RawEndColumn, Props),
|
|
733
|
+
normalize_integer(RawLine, Line),
|
|
734
|
+
normalize_integer(RawColumn, Column),
|
|
735
|
+
normalize_integer(RawEndLine, EndLine),
|
|
736
|
+
normalize_integer(RawEndColumn, EndColumn),
|
|
737
|
+
Line >= 1,
|
|
738
|
+
Column >= 0,
|
|
739
|
+
EndLine >= Line,
|
|
740
|
+
EndColumn >= 0.
|
|
741
|
+
|
|
742
|
+
nonempty_source(Props) :-
|
|
743
|
+
memberchk(source=RawSource, Props),
|
|
744
|
+
normalize_atom(RawSource, Source),
|
|
745
|
+
Source \= ''.
|
|
746
|
+
|
|
747
|
+
proof_status(Stages, unresolved) :-
|
|
748
|
+
Stages.contradictions.status == blocked,
|
|
749
|
+
!.
|
|
750
|
+
proof_status(Stages, missing) :-
|
|
751
|
+
stage_dict(Stages, Stage),
|
|
752
|
+
Stage.status == missing,
|
|
753
|
+
!.
|
|
754
|
+
proof_status(Stages, unresolved) :-
|
|
755
|
+
stage_dict(Stages, Stage),
|
|
756
|
+
memberchk(Stage.status, [unresolved, blocked]),
|
|
757
|
+
!.
|
|
758
|
+
proof_status(_, proven).
|
|
759
|
+
|
|
760
|
+
stage_dict(Stages, Stage) :-
|
|
761
|
+
dict_pairs(Stages, _, Pairs),
|
|
762
|
+
member(_-Stage, Pairs).
|
|
763
|
+
|
|
764
|
+
proof_gaps(Stages, Gaps) :-
|
|
765
|
+
findall(Gap, (gap_definition(Gap, _, _, _), proof_gap_present(Gap, Stages)), Gaps).
|
|
766
|
+
|
|
767
|
+
proof_gap_present(missing_semantic_inventory, Stages) :- Stages.semanticInventory.propositionCount =:= 0.
|
|
768
|
+
proof_gap_present(incomplete_semantic_inventory, Stages) :- Stages.semanticInventory.missingCount > 0.
|
|
769
|
+
proof_gap_present(malformed_semantic_inventory, Stages) :- Stages.semanticInventory.malformedCount > 0.
|
|
770
|
+
proof_gap_present(unresolved_semantic_proposition, Stages) :- Stages.semanticInventory.unresolvedCount > 0.
|
|
771
|
+
proof_gap_present(missing_logic_claims, Stages) :- Stages.logicGrounding.manifestClaims == [].
|
|
772
|
+
proof_gap_present(logic_manifest_mismatch, Stages) :-
|
|
773
|
+
(Stages.logicGrounding.missingManifestClaims \= [] ; Stages.logicGrounding.extraManifestClaims \= [] ; Stages.logicGrounding.undeclaredGroundClaims \= []).
|
|
774
|
+
proof_gap_present(missing_logic_grounding, Stages) :- Stages.logicGrounding.missingGroundClaims \= [].
|
|
775
|
+
proof_gap_present(ambiguous_logic_grounding, Stages) :-
|
|
776
|
+
(Stages.logicGrounding.duplicateGroundClaims \= [] ; Stages.logicGrounding.claimTextMismatchClaims \= [] ; Stages.logicGrounding.invalidGroundClaims \= [] ; Stages.logicGrounding.invalidRuleFacts \= []).
|
|
777
|
+
proof_gap_present(blocking_contradiction, Stages) :- Stages.contradictions.status == blocked.
|
|
778
|
+
proof_gap_present(contradiction_check_incomplete, Stages) :- Stages.contradictions.status == unresolved.
|
|
779
|
+
proof_gap_present(missing_scenario, Stages) :- Stages.scenarios.status == missing.
|
|
780
|
+
proof_gap_present(missing_scenario_test, Stages) :- Stages.scenarios.status == passed, Stages.scenarioTests.status == missing.
|
|
781
|
+
proof_gap_present(missing_passing_e2e, Stages) :- Stages.passingE2e.status == missing.
|
|
782
|
+
proof_gap_present(missing_verification_receipt, Stages) :- Stages.passingE2e.missingReceiptTests \= [].
|
|
783
|
+
proof_gap_present(stale_verification_receipt, Stages) :- Stages.passingE2e.staleReceiptTests \= [].
|
|
784
|
+
proof_gap_present(failed_verification_receipt, Stages) :- Stages.passingE2e.failedReceiptTests \= [].
|
|
785
|
+
proof_gap_present(invalid_verification_receipt, Stages) :- Stages.passingE2e.invalidReceiptTests \= [].
|
|
786
|
+
proof_gap_present(verification_snapshot_unavailable, Stages) :- Stages.passingE2e.snapshotUnavailableTests \= [].
|
|
787
|
+
proof_gap_present(missing_executable_test_symbol, Stages) :- Stages.executableSymbols.status == missing.
|
|
788
|
+
proof_gap_present(missing_production_symbol, Stages) :- Stages.productionSymbols.symbols == [].
|
|
789
|
+
proof_gap_present(missing_production_symbol_coverage, Stages) :- Stages.productionSymbols.uncoveredSymbols \= [].
|
|
790
|
+
proof_gap_present(missing_symbol_coordinates, Stages) :- Stages.sourceCoordinates.missingSymbols \= [].
|
|
791
|
+
proof_gap_present(missing_requirement_source, Stages) :- Stages.sourceCoordinates.requirementSource == missing.
|
|
792
|
+
|
|
793
|
+
gap_definition(missing_semantic_inventory, 10, semantic_inventory, "Run kb_semantic_advisor and persist its complete proposition ledger.").
|
|
794
|
+
gap_definition(incomplete_semantic_inventory, 11, semantic_inventory, "Classify every missing assertive span as modeled or explicitly unresolved.").
|
|
795
|
+
gap_definition(malformed_semantic_inventory, 12, semantic_inventory, "Restore claim provenance and valid UTF-8 spans for every proposition.").
|
|
796
|
+
gap_definition(unresolved_semantic_proposition, 20, semantic_inventory, "Resolve each ambiguity or ontology gap before claiming proof.").
|
|
797
|
+
gap_definition(missing_logic_claims, 30, logic_grounding, "Persist the stable claim-key manifest returned by semantic analysis.").
|
|
798
|
+
gap_definition(logic_manifest_mismatch, 31, logic_grounding, "Align the proposition ledger, logic_claims, and linked ground facts one-to-one.").
|
|
799
|
+
gap_definition(missing_logic_grounding, 32, logic_grounding, "Ground every modeled claim with one strict property, predicate, or safe rule fact.").
|
|
800
|
+
gap_definition(ambiguous_logic_grounding, 33, logic_grounding, "Remove duplicate or invalid ground representations and validate linked rules.").
|
|
801
|
+
gap_definition(blocking_contradiction, 40, contradictions, "Supersede or reconcile the conflicting normative requirement.").
|
|
802
|
+
gap_definition(contradiction_check_incomplete, 41, contradictions, "Complete logical grounding before interpreting absence of a conflict as evidence.").
|
|
803
|
+
gap_definition(missing_scenario, 50, scenarios, "Add a specified_by scenario for the requirement.").
|
|
804
|
+
gap_definition(missing_scenario_test, 51, scenario_tests, "Link the scenario to a test with verified_by or validates.").
|
|
805
|
+
gap_definition(missing_passing_e2e, 52, passing_e2e, "Record fresh passing end-to-end evidence on a scenario-backed test.").
|
|
806
|
+
gap_definition(missing_verification_receipt, 53, passing_e2e, "Record a kibi.verification-receipt.v1 result for the scenario-backed E2E test.").
|
|
807
|
+
gap_definition(stale_verification_receipt, 54, passing_e2e, "Re-run the E2E test against the current code snapshot and append its receipt.").
|
|
808
|
+
gap_definition(failed_verification_receipt, 55, passing_e2e, "Repair the failing E2E behavior and append a newer passing receipt for the same snapshot.").
|
|
809
|
+
gap_definition(invalid_verification_receipt, 56, passing_e2e, "Repair malformed, future-dated, mismatched, or otherwise uncheckable receipt evidence.").
|
|
810
|
+
gap_definition(verification_snapshot_unavailable, 57, passing_e2e, "Run coverage through a CLI or MCP runtime that exposes the deterministic workspace snapshot.").
|
|
811
|
+
gap_definition(missing_executable_test_symbol, 60, executable_symbols, "Link executable test code to every qualifying E2E test with executable_for.").
|
|
812
|
+
gap_definition(missing_production_symbol, 70, production_symbols, "Link at least one production symbol to the requirement with implements.").
|
|
813
|
+
gap_definition(missing_production_symbol_coverage, 71, production_symbols, "Link every implementing production symbol to a qualifying E2E test with covered_by.").
|
|
814
|
+
gap_definition(missing_symbol_coordinates, 80, source_coordinates, "Refresh and persist exact coordinates for every proof-bearing symbol.").
|
|
815
|
+
gap_definition(missing_requirement_source, 81, source_coordinates, "Bind the requirement to its current source document.").
|
|
816
|
+
|
|
817
|
+
proof_repairs(Gaps, Repairs) :-
|
|
818
|
+
findall(_{gap: Gap, priority: Priority, stage: Stage, action: Action},
|
|
819
|
+
(member(Gap, Gaps), gap_definition(Gap, Priority, Stage, Action)),
|
|
820
|
+
Repairs).
|
|
821
|
+
|
|
822
|
+
normalize_atom(Value, Atom) :-
|
|
823
|
+
kb:normalize_term_atom(Value, Atom).
|
|
824
|
+
|
|
825
|
+
normalize_atom_list(Value, Atoms) :-
|
|
826
|
+
kb:normalize_term_atom_list(Value, Atoms).
|
|
827
|
+
|
|
828
|
+
normalize_integer(Value, Integer) :-
|
|
829
|
+
( Value = ^^(Inner, _)
|
|
830
|
+
-> normalize_integer(Inner, Integer)
|
|
831
|
+
; integer(Value)
|
|
832
|
+
-> Integer = Value
|
|
833
|
+
; atom(Value)
|
|
834
|
+
-> atom_number(Value, Integer)
|
|
835
|
+
; string(Value)
|
|
836
|
+
-> number_string(Integer, Value)
|
|
837
|
+
).
|