kibi-core 0.11.0 → 0.13.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 +10 -4
- package/schema/validation.pl +3 -2
- package/src/checks.pl +199 -5
- package/src/discovery.pl +161 -4
- package/src/kb.pl +68 -5
- package/src/requirement_proof.pl +695 -145
- package/src/rule_registry.pl +137 -0
- package/src/status.pl +38 -19
- package/src/symbol_classification.pl +47 -0
package/src/requirement_proof.pl
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
:- module(requirement_proof, [
|
|
5
5
|
requirement_proof_context/1,
|
|
6
6
|
requirement_proof_context/4,
|
|
7
|
+
requirement_proof_context/6,
|
|
7
8
|
requirement_proof/4
|
|
8
9
|
]).
|
|
9
10
|
|
|
@@ -19,12 +20,23 @@
|
|
|
19
20
|
]).
|
|
20
21
|
:- use_module('logic_ir.pl', [logic_rule_safety/2]).
|
|
21
22
|
|
|
22
|
-
proof_version('kibi.requirement-proof.
|
|
23
|
+
proof_version('kibi.requirement-proof.v3').
|
|
23
24
|
|
|
24
25
|
requirement_proof_context(Context) :-
|
|
25
26
|
requirement_proof_context(unknown, '1970-01-01T00:00:00Z', 604800, Context).
|
|
26
27
|
|
|
27
28
|
requirement_proof_context(VerificationSnapshot, CheckedAt, MaxAgeSeconds, Context) :-
|
|
29
|
+
requirement_proof_context(VerificationSnapshot, CheckedAt, MaxAgeSeconds,
|
|
30
|
+
strict_snapshot, _{}, Context).
|
|
31
|
+
|
|
32
|
+
% Per-contract receipt binding (W2): BindingMode is strict_snapshot (the
|
|
33
|
+
% default: a receipt is current only against the whole-workspace snapshot it
|
|
34
|
+
% was proven on) or per_contract (a receipt carrying a binding_hash is current
|
|
35
|
+
% while its contract + receipt-stripped test document are unchanged, via the
|
|
36
|
+
% TestBindings dict TestId -> BindingHash; receipts without a binding_hash
|
|
37
|
+
% still fall back to the snapshot match).
|
|
38
|
+
requirement_proof_context(VerificationSnapshot, CheckedAt, MaxAgeSeconds,
|
|
39
|
+
BindingMode, TestBindings, Context) :-
|
|
28
40
|
check_domain_contradictions_and_witnesses(Contradictions, ContradictionWitnesses),
|
|
29
41
|
check_rule_safety(UnsafeRules),
|
|
30
42
|
check_rule_verifiability(UnverifiableRules),
|
|
@@ -32,38 +44,43 @@ requirement_proof_context(VerificationSnapshot, CheckedAt, MaxAgeSeconds, Contex
|
|
|
32
44
|
normalize_atom(CheckedAt, CheckedAtAtom),
|
|
33
45
|
(catch(parse_time(CheckedAtAtom, CheckedAtStamp), _, fail) -> true ; CheckedAtStamp = -1),
|
|
34
46
|
normalize_integer(MaxAgeSeconds, MaxAge),
|
|
47
|
+
normalize_atom(BindingMode, BindingModeAtom),
|
|
35
48
|
Context = _{
|
|
36
49
|
contradictions: Contradictions,
|
|
37
50
|
contradictionWitnesses: ContradictionWitnesses,
|
|
38
51
|
unsafeRules: UnsafeRules,
|
|
39
52
|
unverifiableRules: UnverifiableRules,
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
53
|
+
proofSnapshot: Snapshot,
|
|
54
|
+
proofCheckedAt: CheckedAtAtom,
|
|
55
|
+
proofCheckedAtStamp: CheckedAtStamp,
|
|
56
|
+
proofMaxAgeSeconds: MaxAge,
|
|
57
|
+
proofBindingMode: BindingModeAtom,
|
|
58
|
+
proofTestBindings: TestBindings
|
|
44
59
|
}.
|
|
45
60
|
|
|
61
|
+
% implements REQ-kibi-proof-applicability-reason
|
|
62
|
+
% Exemption first: a current requirement explicitly marked proof_exempt with a
|
|
63
|
+
% reason is intentionally outside E2E-proof scope and must never fall through
|
|
64
|
+
% to the ladder (which would report misleading receipt gaps).
|
|
65
|
+
requirement_proof(ReqId, ReqProps, _Context, Proof) :-
|
|
66
|
+
kb:current_req(ReqId),
|
|
67
|
+
proof_exempt(ReqProps, Reason),
|
|
68
|
+
!,
|
|
69
|
+
exempt_requirement_proof(Reason, Proof).
|
|
46
70
|
requirement_proof(ReqId, _ReqProps, _Context, Proof) :-
|
|
47
71
|
\+ kb:current_req(ReqId),
|
|
48
72
|
!,
|
|
49
|
-
|
|
50
|
-
Proof
|
|
51
|
-
proofVersion: Version,
|
|
52
|
-
proofStatus: not_applicable,
|
|
53
|
-
proofGaps: [],
|
|
54
|
-
proofAdvisories: [],
|
|
55
|
-
proofRepairs: [],
|
|
56
|
-
proofStages: _{applicability: _{status: not_applicable}}
|
|
57
|
-
}.
|
|
73
|
+
applicability_exclusion_reason(ReqId, Reason),
|
|
74
|
+
exempt_requirement_proof(Reason, Proof).
|
|
58
75
|
requirement_proof(ReqId, ReqProps, Context, Proof) :-
|
|
59
76
|
semantic_inventory_stage(ReqProps, SemanticStage, Inventory),
|
|
60
77
|
logic_grounding_stage(ReqId, ReqProps, Inventory, Context, LogicStage),
|
|
61
78
|
contradiction_stage(ReqId, LogicStage.status, Context, ContradictionStage),
|
|
62
79
|
scenario_stage(ReqId, ScenarioStage, ScenarioIds),
|
|
63
80
|
scenario_test_stage(ScenarioIds, ScenarioTestStage, ScenarioTests),
|
|
64
|
-
passing_e2e_stage(
|
|
81
|
+
passing_e2e_stage(ScenarioTestStage, Context, PassingE2eStage, PassingE2eTests),
|
|
65
82
|
executable_symbol_stage(PassingE2eTests, ExecutableStage, ExecutableSymbols),
|
|
66
|
-
production_symbol_stage(ReqId, PassingE2eTests, ProductionStage, ProductionSymbols),
|
|
83
|
+
production_symbol_stage(ReqId, PassingE2eTests, ScenarioTests, PassingE2eStage.receiptEvidence, Context, ProductionStage, ProductionSymbols),
|
|
67
84
|
source_coordinate_stage(ReqProps, ExecutableSymbols, ProductionSymbols, CoordinateStage),
|
|
68
85
|
Stages = _{
|
|
69
86
|
semanticInventory: SemanticStage,
|
|
@@ -86,9 +103,49 @@ requirement_proof(ReqId, ReqProps, Context, Proof) :-
|
|
|
86
103
|
proofGaps: Gaps,
|
|
87
104
|
proofAdvisories: Advisories,
|
|
88
105
|
proofRepairs: Repairs,
|
|
89
|
-
proofStages: Stages
|
|
106
|
+
proofStages: Stages,
|
|
107
|
+
testResolutions: ScenarioTestStage.resolutions
|
|
90
108
|
}.
|
|
91
109
|
|
|
110
|
+
exempt_requirement_proof(Reason, Proof) :-
|
|
111
|
+
proof_version(Version),
|
|
112
|
+
Proof = _{
|
|
113
|
+
proofVersion: Version,
|
|
114
|
+
proofStatus: not_applicable,
|
|
115
|
+
proofGaps: [],
|
|
116
|
+
proofAdvisories: [],
|
|
117
|
+
proofRepairs: [],
|
|
118
|
+
proofStages: _{applicability: _{status: not_applicable, reason: Reason}}
|
|
119
|
+
}.
|
|
120
|
+
|
|
121
|
+
%% proof_exempt(+Props, -Reason)
|
|
122
|
+
% A current requirement is exempt from E2E-proof scope when its author set
|
|
123
|
+
% proof_exempt to a truthy value AND supplied a non-empty proof_exempt_reason.
|
|
124
|
+
% Without the reason the exemption does not apply, so an author cannot park a
|
|
125
|
+
% requirement silently; upsert validation enforces the same pairing.
|
|
126
|
+
proof_exempt(Props, Reason) :-
|
|
127
|
+
memberchk(proof_exempt=RawFlag, Props),
|
|
128
|
+
normalize_atom(RawFlag, Flag),
|
|
129
|
+
memberchk(Flag, [true, yes, on, '1']),
|
|
130
|
+
memberchk(proof_exempt_reason=RawReason, Props),
|
|
131
|
+
normalize_atom(RawReason, Reason),
|
|
132
|
+
Reason \= ''.
|
|
133
|
+
|
|
134
|
+
%% applicability_exclusion_reason(+ReqId, -Reason)
|
|
135
|
+
% Typed reason a requirement fails current_req/1: superseded lifecycle, or a
|
|
136
|
+
% status outside the canonical+legacy requirement vocabulary (e.g. the ADR
|
|
137
|
+
% vocabulary 'accepted').
|
|
138
|
+
applicability_exclusion_reason(ReqId, "requirement is superseded by a newer requirement") :-
|
|
139
|
+
kb_relationship(supersedes, _, ReqId),
|
|
140
|
+
!.
|
|
141
|
+
applicability_exclusion_reason(ReqId, Reason) :-
|
|
142
|
+
kb_entity(ReqId, req, Props),
|
|
143
|
+
memberchk(status=Status, Props),
|
|
144
|
+
normalize_term_atom(Status, StatusAtom),
|
|
145
|
+
format(atom(Reason), "status '~w' is not a current requirement status", [StatusAtom]),
|
|
146
|
+
!.
|
|
147
|
+
applicability_exclusion_reason(_ReqId, "requirement is not current").
|
|
148
|
+
|
|
92
149
|
semantic_inventory_stage(Props, Stage, Entries) :-
|
|
93
150
|
( memberchk(semantic_inventory=RawInventory, Props),
|
|
94
151
|
inventory_entries(RawInventory, Entries0),
|
|
@@ -435,31 +492,96 @@ violation_for_entity(Violations, EntityId) :-
|
|
|
435
492
|
|
|
436
493
|
scenario_stage(ReqId, Stage, ScenarioIds) :-
|
|
437
494
|
findall(ScenarioId,
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
sort(
|
|
441
|
-
(
|
|
495
|
+
kb_relationship(specified_by, ReqId, ScenarioId),
|
|
496
|
+
ScenarioTargets0),
|
|
497
|
+
sort(ScenarioTargets0, ScenarioTargets),
|
|
498
|
+
include(existing_scenario, ScenarioTargets, ScenarioIds),
|
|
499
|
+
findall(ScenarioId,
|
|
500
|
+
(member(ScenarioId, ScenarioTargets), \+ existing_scenario(ScenarioId)),
|
|
501
|
+
InvalidScenarioTargets),
|
|
502
|
+
((ScenarioIds = [] ; InvalidScenarioTargets \= []) -> Status = missing ; Status = passed),
|
|
442
503
|
maplist(entity_source_ref, ScenarioIds, Sources),
|
|
443
|
-
Stage = _{
|
|
504
|
+
Stage = _{
|
|
505
|
+
status: Status,
|
|
506
|
+
scenarios: ScenarioIds,
|
|
507
|
+
scenarioTargets: ScenarioTargets,
|
|
508
|
+
invalidScenarioTargets: InvalidScenarioTargets,
|
|
509
|
+
sources: Sources
|
|
510
|
+
}.
|
|
511
|
+
|
|
512
|
+
existing_scenario(ScenarioId) :-
|
|
513
|
+
kb_entity(ScenarioId, scenario, _).
|
|
444
514
|
|
|
445
515
|
scenario_test_stage(ScenarioIds, Stage, ScenarioTests) :-
|
|
516
|
+
maplist(scenario_test_obligation, ScenarioIds, Obligations),
|
|
446
517
|
findall(TestId,
|
|
447
|
-
(member(
|
|
518
|
+
(member(Obligation, Obligations), member(TestId, Obligation.tests)),
|
|
448
519
|
ScenarioTests0),
|
|
449
520
|
sort(ScenarioTests0, ScenarioTests),
|
|
450
|
-
(
|
|
521
|
+
findall(TestId,
|
|
522
|
+
(member(Obligation, Obligations), member(TestId, Obligation.scenarioTestTargets)),
|
|
523
|
+
ScenarioTestTargets0),
|
|
524
|
+
sort(ScenarioTestTargets0, ScenarioTestTargets),
|
|
525
|
+
findall(TestId,
|
|
526
|
+
(member(Obligation, Obligations), member(TestId, Obligation.invalidScenarioTestTargets)),
|
|
527
|
+
InvalidScenarioTestTargets0),
|
|
528
|
+
sort(InvalidScenarioTestTargets0, InvalidScenarioTestTargets),
|
|
529
|
+
( ScenarioIds == []
|
|
530
|
+
-> Status = missing
|
|
531
|
+
; member(Obligation, Obligations), Obligation.status == missing
|
|
532
|
+
-> Status = missing
|
|
533
|
+
; Status = passed
|
|
534
|
+
),
|
|
451
535
|
maplist(entity_source_ref, ScenarioTests, Sources),
|
|
452
|
-
|
|
536
|
+
maplist(test_resolution_record, ScenarioTests, Resolutions),
|
|
537
|
+
Stage = _{
|
|
538
|
+
status: Status,
|
|
539
|
+
scenarios: ScenarioIds,
|
|
540
|
+
tests: ScenarioTests,
|
|
541
|
+
scenarioTestTargets: ScenarioTestTargets,
|
|
542
|
+
invalidScenarioTestTargets: InvalidScenarioTestTargets,
|
|
543
|
+
sources: Sources,
|
|
544
|
+
obligations: Obligations,
|
|
545
|
+
resolutions: Resolutions
|
|
546
|
+
}.
|
|
453
547
|
|
|
454
|
-
|
|
455
|
-
|
|
548
|
+
scenario_test_obligation(ScenarioId, Obligation) :-
|
|
549
|
+
findall(TestId, declared_scenario_test_target(ScenarioId, TestId), TestTargets0),
|
|
550
|
+
sort(TestTargets0, TestTargets),
|
|
551
|
+
include(existing_test, TestTargets, TestIds),
|
|
552
|
+
findall(TestId,
|
|
553
|
+
(member(TestId, TestTargets), \+ existing_test(TestId)),
|
|
554
|
+
InvalidScenarioTestTargets),
|
|
555
|
+
maplist(entity_source_ref, TestIds, Sources),
|
|
556
|
+
( (TestIds == [] ; InvalidScenarioTestTargets \= [])
|
|
557
|
+
-> Status = missing
|
|
558
|
+
; Status = passed
|
|
559
|
+
),
|
|
560
|
+
Obligation = _{
|
|
561
|
+
scenarioId: ScenarioId,
|
|
562
|
+
status: Status,
|
|
563
|
+
scenarioTestTargets: TestTargets,
|
|
564
|
+
invalidScenarioTestTargets: InvalidScenarioTestTargets,
|
|
565
|
+
tests: TestIds,
|
|
566
|
+
sources: Sources
|
|
567
|
+
}.
|
|
568
|
+
|
|
569
|
+
declared_scenario_test_target(ScenarioId, TestId) :-
|
|
570
|
+
kb_relationship(verified_by, ScenarioId, TestId).
|
|
571
|
+
declared_scenario_test_target(ScenarioId, TestId) :-
|
|
572
|
+
kb_relationship(validates, TestId, ScenarioId).
|
|
573
|
+
|
|
574
|
+
existing_test(TestId) :-
|
|
456
575
|
kb_entity(TestId, test, _).
|
|
576
|
+
|
|
457
577
|
scenario_test(ScenarioId, TestId) :-
|
|
458
|
-
|
|
459
|
-
|
|
578
|
+
declared_scenario_test_target(ScenarioId, TestId),
|
|
579
|
+
existing_test(TestId).
|
|
460
580
|
|
|
461
|
-
passing_e2e_stage(
|
|
462
|
-
maplist(
|
|
581
|
+
passing_e2e_stage(ScenarioTestStage, Context, Stage, PassingE2eTests) :-
|
|
582
|
+
maplist(scenario_passing_e2e_obligation(Context), ScenarioTestStage.obligations, ScenarioObligations),
|
|
583
|
+
findall(Item, (member(Obligation, ScenarioObligations), member(Item, Obligation.evidence)), Evidence0),
|
|
584
|
+
unique_evidence_by_test(Evidence0, Evidence),
|
|
463
585
|
evidence_tests_with_state(Evidence, passed, PassingE2eTests),
|
|
464
586
|
evidence_tests_with_state(Evidence, missing, MissingReceiptTests),
|
|
465
587
|
evidence_tests_with_state(Evidence, stale, StaleReceiptTests),
|
|
@@ -468,10 +590,11 @@ passing_e2e_stage(ScenarioTests, Context, Stage, PassingE2eTests) :-
|
|
|
468
590
|
evidence_tests_with_state(Evidence, contract_mismatch, ContractMismatchReceiptTests),
|
|
469
591
|
evidence_tests_with_state(Evidence, snapshot_unavailable, SnapshotUnavailableTests),
|
|
470
592
|
evidence_tests_with_state(Evidence, not_end_to_end, NonEndToEndTests),
|
|
471
|
-
passing_e2e_status(
|
|
593
|
+
passing_e2e_status(ScenarioObligations, Status),
|
|
472
594
|
Stage = _{
|
|
473
595
|
status: Status,
|
|
474
596
|
tests: PassingE2eTests,
|
|
597
|
+
scenarioObligations: ScenarioObligations,
|
|
475
598
|
receiptEvidence: Evidence,
|
|
476
599
|
missingReceiptTests: MissingReceiptTests,
|
|
477
600
|
staleReceiptTests: StaleReceiptTests,
|
|
@@ -480,16 +603,104 @@ passing_e2e_stage(ScenarioTests, Context, Stage, PassingE2eTests) :-
|
|
|
480
603
|
contractMismatchReceiptTests: ContractMismatchReceiptTests,
|
|
481
604
|
snapshotUnavailableTests: SnapshotUnavailableTests,
|
|
482
605
|
nonEndToEndTests: NonEndToEndTests,
|
|
483
|
-
currentCodeSnapshot: Context.
|
|
484
|
-
checkedAt: Context.
|
|
485
|
-
maxAgeSeconds: Context.
|
|
606
|
+
currentCodeSnapshot: Context.proofSnapshot,
|
|
607
|
+
checkedAt: Context.proofCheckedAt,
|
|
608
|
+
maxAgeSeconds: Context.proofMaxAgeSeconds
|
|
609
|
+
}.
|
|
610
|
+
|
|
611
|
+
unique_evidence_by_test(Evidence0, Evidence) :-
|
|
612
|
+
findall(TestId,
|
|
613
|
+
(member(Item, Evidence0), TestId = Item.testId),
|
|
614
|
+
TestIds0),
|
|
615
|
+
sort(TestIds0, TestIds),
|
|
616
|
+
maplist(first_evidence_for_test(Evidence0), TestIds, Evidence).
|
|
617
|
+
|
|
618
|
+
first_evidence_for_test(Evidence, TestId, Item) :-
|
|
619
|
+
member(Item, Evidence),
|
|
620
|
+
Item.testId == TestId,
|
|
621
|
+
!.
|
|
622
|
+
|
|
623
|
+
scenario_passing_e2e_obligation(Context, TestObligation, Obligation) :-
|
|
624
|
+
maplist(test_receipt_evidence(Context), TestObligation.tests, Evidence),
|
|
625
|
+
include(e2e_evidence, Evidence, E2eEvidence),
|
|
626
|
+
include(non_e2e_evidence, Evidence, NonEndToEndEvidence),
|
|
627
|
+
evidence_tests_with_state(E2eEvidence, passed, PassingE2eTests),
|
|
628
|
+
evidence_tests_with_state(E2eEvidence, missing, MissingReceiptTests),
|
|
629
|
+
evidence_tests_with_state(E2eEvidence, stale, StaleReceiptTests),
|
|
630
|
+
evidence_tests_with_state(E2eEvidence, failed, FailedReceiptTests),
|
|
631
|
+
evidence_tests_with_state(E2eEvidence, invalid, InvalidReceiptTests),
|
|
632
|
+
evidence_tests_with_state(E2eEvidence, contract_mismatch, ContractMismatchReceiptTests),
|
|
633
|
+
evidence_tests_with_state(E2eEvidence, snapshot_unavailable, SnapshotUnavailableTests),
|
|
634
|
+
evidence_tests_with_state(NonEndToEndEvidence, not_end_to_end, NonEndToEndTests),
|
|
635
|
+
scenario_obligation_gaps(TestObligation, E2eEvidence, Gaps),
|
|
636
|
+
scenario_obligation_status(TestObligation, E2eEvidence, Status),
|
|
637
|
+
findall(TestId,
|
|
638
|
+
(member(Item, E2eEvidence), TestId = Item.testId),
|
|
639
|
+
E2eTests0),
|
|
640
|
+
sort(E2eTests0, E2eTests),
|
|
641
|
+
Obligation = _{
|
|
642
|
+
scenarioId: TestObligation.scenarioId,
|
|
643
|
+
status: Status,
|
|
644
|
+
scenarioTestTargets: TestObligation.scenarioTestTargets,
|
|
645
|
+
invalidScenarioTestTargets: TestObligation.invalidScenarioTestTargets,
|
|
646
|
+
tests: TestObligation.tests,
|
|
647
|
+
e2eTests: E2eTests,
|
|
648
|
+
nonEndToEndTests: NonEndToEndTests,
|
|
649
|
+
evidence: Evidence,
|
|
650
|
+
gaps: Gaps,
|
|
651
|
+
passingE2eTests: PassingE2eTests,
|
|
652
|
+
missingReceiptTests: MissingReceiptTests,
|
|
653
|
+
staleReceiptTests: StaleReceiptTests,
|
|
654
|
+
failedReceiptTests: FailedReceiptTests,
|
|
655
|
+
invalidReceiptTests: InvalidReceiptTests,
|
|
656
|
+
contractMismatchReceiptTests: ContractMismatchReceiptTests,
|
|
657
|
+
snapshotUnavailableTests: SnapshotUnavailableTests
|
|
486
658
|
}.
|
|
487
659
|
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
660
|
+
e2e_evidence(Item) :- Item.state \= not_end_to_end.
|
|
661
|
+
non_e2e_evidence(Item) :- Item.state == not_end_to_end.
|
|
662
|
+
|
|
663
|
+
scenario_obligation_gaps(TestObligation, E2eEvidence, Gaps) :-
|
|
664
|
+
findall(Gap, scenario_obligation_gap(TestObligation, E2eEvidence, Gap), Gaps0),
|
|
665
|
+
sort(Gaps0, Gaps).
|
|
666
|
+
|
|
667
|
+
scenario_obligation_gap(TestObligation, _E2eEvidence, missing_scenario_test) :-
|
|
668
|
+
TestObligation.status == missing.
|
|
669
|
+
scenario_obligation_gap(TestObligation, E2eEvidence, missing_passing_e2e) :-
|
|
670
|
+
TestObligation.status == passed,
|
|
671
|
+
E2eEvidence == [].
|
|
672
|
+
scenario_obligation_gap(_TestObligation, Evidence, missing_proof_receipt) :-
|
|
673
|
+
member(Item, Evidence), Item.state == missing.
|
|
674
|
+
scenario_obligation_gap(_TestObligation, Evidence, stale_proof_receipt) :-
|
|
675
|
+
member(Item, Evidence), Item.state == stale.
|
|
676
|
+
scenario_obligation_gap(_TestObligation, Evidence, failed_proof_receipt) :-
|
|
677
|
+
member(Item, Evidence), Item.state == failed.
|
|
678
|
+
scenario_obligation_gap(_TestObligation, Evidence, invalid_proof_receipt) :-
|
|
679
|
+
member(Item, Evidence), Item.state == invalid.
|
|
680
|
+
scenario_obligation_gap(_TestObligation, Evidence, proof_contract_mismatch) :-
|
|
681
|
+
member(Item, Evidence), Item.state == contract_mismatch.
|
|
682
|
+
scenario_obligation_gap(_TestObligation, Evidence, proof_snapshot_unavailable) :-
|
|
683
|
+
member(Item, Evidence), Item.state == snapshot_unavailable.
|
|
684
|
+
|
|
685
|
+
scenario_obligation_status(TestObligation, _E2eEvidence, missing) :-
|
|
686
|
+
TestObligation.status == missing,
|
|
687
|
+
!.
|
|
688
|
+
scenario_obligation_status(_TestObligation, [], missing) :-
|
|
689
|
+
!.
|
|
690
|
+
scenario_obligation_status(_TestObligation, Evidence, passed) :-
|
|
691
|
+
Evidence \= [],
|
|
692
|
+
forall(member(Item, Evidence), Item.state == passed),
|
|
693
|
+
!.
|
|
694
|
+
scenario_obligation_status(_TestObligation, _Evidence, unresolved).
|
|
695
|
+
|
|
696
|
+
passing_e2e_status([], missing) :- !.
|
|
697
|
+
passing_e2e_status(Obligations, passed) :-
|
|
698
|
+
forall(member(Obligation, Obligations), Obligation.status == passed),
|
|
699
|
+
!.
|
|
700
|
+
passing_e2e_status(Obligations, missing) :-
|
|
701
|
+
member(Obligation, Obligations), Obligation.status == missing,
|
|
702
|
+
!.
|
|
703
|
+
passing_e2e_status(_Obligations, unresolved).
|
|
493
704
|
|
|
494
705
|
evidence_tests_with_state(Evidence, State, TestIds) :-
|
|
495
706
|
findall(TestId,
|
|
@@ -502,9 +713,9 @@ test_receipt_evidence(Context, TestId, Evidence) :-
|
|
|
502
713
|
test_scope(Props, Scope),
|
|
503
714
|
( Scope \= end_to_end
|
|
504
715
|
-> Evidence = _{testId: TestId, state: not_end_to_end, scope: Scope}
|
|
505
|
-
; Context.
|
|
716
|
+
; Context.proofSnapshot == unknown
|
|
506
717
|
-> Evidence = _{testId: TestId, state: snapshot_unavailable, scope: Scope}
|
|
507
|
-
;
|
|
718
|
+
; proof_receipt_entries(Props, Receipts, ReceiptPropertyPresent),
|
|
508
719
|
receipt_evidence_state(TestId, Scope, Props, Receipts, ReceiptPropertyPresent, Context, Evidence)
|
|
509
720
|
).
|
|
510
721
|
|
|
@@ -513,18 +724,18 @@ receipt_evidence_state(TestId, Scope, _Props, [], false, _Context,
|
|
|
513
724
|
receipt_evidence_state(TestId, Scope, Props, Receipts, _Present, _Context,
|
|
514
725
|
_{testId: TestId, state: invalid, scope: Scope, receiptCount: Count}) :-
|
|
515
726
|
length(Receipts, Count),
|
|
516
|
-
|
|
727
|
+
proof_contract_binding(Props, ContractBinding),
|
|
517
728
|
(ContractBinding == invalid
|
|
518
729
|
; Receipts == []
|
|
519
730
|
; member(Receipt, Receipts), \+ valid_receipt_shape(TestId, Receipt)
|
|
520
731
|
; \+ chronological_receipt_history(Receipts)),
|
|
521
732
|
!.
|
|
522
733
|
receipt_evidence_state(TestId, Scope, Props, Receipts, _Present, Context, Evidence) :-
|
|
523
|
-
|
|
734
|
+
receipt_for_current_mode(TestId, Context, Receipts, SnapshotReceipts),
|
|
524
735
|
( SnapshotReceipts == []
|
|
525
736
|
-> length(Receipts, Count),
|
|
526
737
|
Evidence = _{testId: TestId, state: stale, scope: Scope, receiptCount: Count}
|
|
527
|
-
;
|
|
738
|
+
; proof_contract_binding(Props, ContractBinding),
|
|
528
739
|
include(receipt_matches_current_binding(Scope, ContractBinding), SnapshotReceipts, CurrentReceipts),
|
|
529
740
|
( CurrentReceipts == []
|
|
530
741
|
-> receipt_contract_mismatch_evidence(TestId, Scope, SnapshotReceipts, ContractBinding, Evidence)
|
|
@@ -534,8 +745,8 @@ receipt_evidence_state(TestId, Scope, Props, Receipts, _Present, Context, Eviden
|
|
|
534
745
|
)
|
|
535
746
|
).
|
|
536
747
|
|
|
537
|
-
|
|
538
|
-
( memberchk(
|
|
748
|
+
proof_receipt_entries(Props, Receipts, Present) :-
|
|
749
|
+
( memberchk(proof_receipts=Raw, Props)
|
|
539
750
|
-> Present = true,
|
|
540
751
|
inventory_entries(Raw, Receipts)
|
|
541
752
|
; Present = false,
|
|
@@ -544,16 +755,11 @@ verification_receipt_entries(Props, Receipts, Present) :-
|
|
|
544
755
|
|
|
545
756
|
valid_receipt_shape(TestId, Receipt) :-
|
|
546
757
|
inventory_entry_field(Receipt, version, RawVersion),
|
|
547
|
-
normalize_receipt_atom(RawVersion,
|
|
548
|
-
memberchk(Version, ['kibi.verification-receipt.v1', 'kibi.verification-receipt.v2']),
|
|
758
|
+
normalize_receipt_atom(RawVersion, 'kibi.proof-receipt.v1'),
|
|
549
759
|
inventory_entry_field(Receipt, receipt_id, RawReceiptId),
|
|
550
760
|
normalize_receipt_atom(RawReceiptId, ReceiptId), valid_receipt_id(ReceiptId),
|
|
551
761
|
inventory_entry_field(Receipt, test_id, RawTestId),
|
|
552
762
|
normalize_receipt_atom(RawTestId, TestId),
|
|
553
|
-
inventory_entry_field(Receipt, runner, RawRunner),
|
|
554
|
-
normalize_receipt_atom(RawRunner, Runner), Runner \= '',
|
|
555
|
-
inventory_entry_field(Receipt, command, RawCommand),
|
|
556
|
-
normalize_receipt_atom(RawCommand, Command), Command \= '',
|
|
557
763
|
inventory_entry_field(Receipt, scope, RawScope),
|
|
558
764
|
normalize_receipt_atom(RawScope, ReceiptScope),
|
|
559
765
|
memberchk(ReceiptScope, [unit, integration, end_to_end]),
|
|
@@ -566,55 +772,83 @@ valid_receipt_shape(TestId, Receipt) :-
|
|
|
566
772
|
normalize_receipt_atom(RawEnvironmentHash, EnvironmentHash), valid_sha256(EnvironmentHash),
|
|
567
773
|
inventory_entry_field(Receipt, artifact_digest, RawArtifactDigest),
|
|
568
774
|
normalize_receipt_atom(RawArtifactDigest, ArtifactDigest), valid_sha256(ArtifactDigest),
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
775
|
+
inventory_entry_field(Receipt, contract_hash, RawContractHash),
|
|
776
|
+
normalize_receipt_atom(RawContractHash, ContractHash), valid_sha256(ContractHash),
|
|
777
|
+
inventory_entry_field(Receipt, fingerprint, RawFingerprint),
|
|
778
|
+
normalize_receipt_atom(RawFingerprint, Fingerprint), valid_sha256(Fingerprint),
|
|
779
|
+
valid_fingerprint_components(Receipt),
|
|
780
|
+
inventory_entry_field(Receipt, integration_id, RawIntegrationId),
|
|
781
|
+
normalize_receipt_atom(RawIntegrationId, IntegrationId), IntegrationId \= '',
|
|
782
|
+
inventory_entry_field(Receipt, producer, RawProducer),
|
|
783
|
+
receipt_producer_name(RawProducer, ProducerName), ProducerName \= '',
|
|
576
784
|
inventory_entry_field(Receipt, command_argv, RawCommandArgv),
|
|
577
785
|
is_list(RawCommandArgv),
|
|
578
786
|
RawCommandArgv \= [],
|
|
579
787
|
maplist(nonempty_receipt_atom, RawCommandArgv),
|
|
580
|
-
inventory_entry_field(Receipt,
|
|
581
|
-
normalize_receipt_atom(
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
788
|
+
( inventory_entry_field(Receipt, binding_hash, RawBindingHash)
|
|
789
|
+
-> normalize_receipt_atom(RawBindingHash, BindingHash),
|
|
790
|
+
valid_sha256(BindingHash)
|
|
791
|
+
; true
|
|
792
|
+
),
|
|
793
|
+
inventory_entry_field(Receipt, run_outcome, RawRunOutcome),
|
|
794
|
+
normalize_receipt_atom(RawRunOutcome, RunOutcome),
|
|
795
|
+
memberchk(RunOutcome, [passed, failed, errored, cancelled, timed_out, interrupted, no_results]),
|
|
796
|
+
inventory_entry_field(Receipt, proof_results, RawResults),
|
|
797
|
+
is_list(RawResults),
|
|
798
|
+
maplist(valid_receipt_proof_result, RawResults),
|
|
799
|
+
receipt_timestamp(Receipt, started_at, StartedStamp),
|
|
800
|
+
receipt_timestamp(Receipt, finished_at, FinishedStamp),
|
|
801
|
+
FinishedStamp >= StartedStamp.
|
|
802
|
+
|
|
803
|
+
receipt_producer_name(Producer, Name) :-
|
|
804
|
+
inventory_entry_field(Producer, name, RawName),
|
|
805
|
+
normalize_receipt_atom(RawName, Name).
|
|
806
|
+
|
|
807
|
+
valid_fingerprint_components(Receipt) :-
|
|
808
|
+
inventory_entry_field(Receipt, fingerprint_components, Components),
|
|
809
|
+
forall(member(Key, [contract, integration, command, bindings, producer]),
|
|
810
|
+
(inventory_entry_field(Components, Key, Raw),
|
|
811
|
+
normalize_receipt_atom(Raw, Value),
|
|
812
|
+
valid_sha256(Value))),
|
|
813
|
+
!.
|
|
592
814
|
|
|
593
815
|
nonempty_receipt_atom(Value) :-
|
|
594
816
|
normalize_receipt_atom(Value, Atom),
|
|
595
817
|
Atom \= ''.
|
|
596
818
|
|
|
597
|
-
|
|
598
|
-
inventory_entry_field(
|
|
819
|
+
valid_receipt_proof_result(Result) :-
|
|
820
|
+
inventory_entry_field(Result, symbol_id, RawSymbolId),
|
|
599
821
|
nonempty_receipt_atom(RawSymbolId),
|
|
600
|
-
inventory_entry_field(
|
|
601
|
-
nonempty_receipt_atom(
|
|
602
|
-
inventory_entry_field(
|
|
603
|
-
normalize_receipt_atom(RawOutcome,
|
|
604
|
-
memberchk(
|
|
605
|
-
inventory_entry_field(
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
822
|
+
inventory_entry_field(Result, target, RawTarget),
|
|
823
|
+
nonempty_receipt_atom(RawTarget),
|
|
824
|
+
inventory_entry_field(Result, outcome, RawOutcome),
|
|
825
|
+
normalize_receipt_atom(RawOutcome, ResultOutcome),
|
|
826
|
+
memberchk(ResultOutcome, [passed, failed, timed_out, skipped, interrupted, errored]),
|
|
827
|
+
inventory_entry_field(Result, binding, RawBinding),
|
|
828
|
+
normalize_receipt_atom(RawBinding, Binding),
|
|
829
|
+
memberchk(Binding, [native_case, aggregate_run]),
|
|
830
|
+
valid_receipt_attempts(Result).
|
|
831
|
+
|
|
832
|
+
valid_receipt_attempts(Result) :-
|
|
833
|
+
inventory_entry_field(Result, attempts, Attempts),
|
|
834
|
+
inventory_entry_field(Attempts, status, RawStatus),
|
|
835
|
+
normalize_receipt_atom(RawStatus, Status),
|
|
836
|
+
valid_attempt_status(Status, Attempts).
|
|
837
|
+
|
|
838
|
+
valid_attempt_status(unavailable, Attempts) :-
|
|
839
|
+
\+ inventory_entry_field(Attempts, entries, _Present), !.
|
|
840
|
+
valid_attempt_status(unavailable, Attempts) :-
|
|
841
|
+
inventory_entry_field(Attempts, entries, Entries),
|
|
842
|
+
Entries == [], !.
|
|
843
|
+
valid_attempt_status(complete, Attempts) :-
|
|
844
|
+
inventory_entry_field(Attempts, entries, Entries),
|
|
845
|
+
is_list(Entries),
|
|
846
|
+
Entries \= [],
|
|
847
|
+
forall(member(Entry, Entries),
|
|
848
|
+
(inventory_entry_field(Entry, outcome, RawEntryOutcome),
|
|
849
|
+
normalize_receipt_atom(RawEntryOutcome, EntryOutcome),
|
|
850
|
+
memberchk(EntryOutcome, [passed, failed, timed_out, skipped, interrupted, errored]))),
|
|
851
|
+
!.
|
|
618
852
|
|
|
619
853
|
valid_sha256(Value) :-
|
|
620
854
|
atom_length(Value, 64),
|
|
@@ -622,7 +856,7 @@ valid_sha256(Value) :-
|
|
|
622
856
|
forall(member(Char, Chars), memberchk(Char, ['0','1','2','3','4','5','6','7','8','9',a,b,c,d,e,f])).
|
|
623
857
|
|
|
624
858
|
valid_receipt_id(Value) :-
|
|
625
|
-
re_match('^
|
|
859
|
+
re_match('^PR-[A-Za-z0-9][A-Za-z0-9._:-]{7,127}$', Value).
|
|
626
860
|
|
|
627
861
|
receipt_timestamp(Receipt, Field, Stamp) :-
|
|
628
862
|
inventory_entry_field(Receipt, Field, RawValue),
|
|
@@ -647,6 +881,29 @@ receipt_for_snapshot(Snapshot, Receipt) :-
|
|
|
647
881
|
inventory_entry_field(Receipt, code_snapshot, RawSnapshot),
|
|
648
882
|
normalize_receipt_atom(RawSnapshot, Snapshot).
|
|
649
883
|
|
|
884
|
+
% Per-contract binding selector (W2). In per_contract mode a receipt carrying
|
|
885
|
+
% a binding_hash is current when it matches the test's current binding hash;
|
|
886
|
+
% receipts without one keep the strict snapshot semantics so older evidence
|
|
887
|
+
% stays valid until each contract is re-proven.
|
|
888
|
+
receipt_for_current_mode(TestId, Context, Receipts, SnapshotReceipts) :-
|
|
889
|
+
( Context.proofBindingMode == per_contract,
|
|
890
|
+
get_dict(TestId, Context.proofTestBindings, CurrentBinding)
|
|
891
|
+
-> include(receipt_with_binding_hash, Receipts, BoundReceipts),
|
|
892
|
+
include(receipt_for_binding(CurrentBinding), BoundReceipts, SnapshotReceipts),
|
|
893
|
+
( SnapshotReceipts == []
|
|
894
|
+
-> include(receipt_for_snapshot(Context.proofSnapshot), Receipts, SnapshotReceipts)
|
|
895
|
+
; true
|
|
896
|
+
)
|
|
897
|
+
; include(receipt_for_snapshot(Context.proofSnapshot), Receipts, SnapshotReceipts)
|
|
898
|
+
).
|
|
899
|
+
|
|
900
|
+
receipt_with_binding_hash(Receipt) :-
|
|
901
|
+
inventory_entry_field(Receipt, binding_hash, _).
|
|
902
|
+
|
|
903
|
+
receipt_for_binding(Binding, Receipt) :-
|
|
904
|
+
inventory_entry_field(Receipt, binding_hash, RawBinding),
|
|
905
|
+
normalize_receipt_atom(RawBinding, Binding).
|
|
906
|
+
|
|
650
907
|
receipt_matches_current_binding(Scope, ContractBinding, Receipt) :-
|
|
651
908
|
inventory_entry_field(Receipt, scope, RawScope),
|
|
652
909
|
normalize_receipt_atom(RawScope, Scope),
|
|
@@ -655,7 +912,7 @@ receipt_matches_current_binding(Scope, ContractBinding, Receipt) :-
|
|
|
655
912
|
receipt_matches_current_contract(none, _Receipt).
|
|
656
913
|
receipt_matches_current_contract(hash(ExpectedHash), Receipt) :-
|
|
657
914
|
inventory_entry_field(Receipt, version, RawVersion),
|
|
658
|
-
normalize_receipt_atom(RawVersion, 'kibi.
|
|
915
|
+
normalize_receipt_atom(RawVersion, 'kibi.proof-receipt.v1'),
|
|
659
916
|
inventory_entry_field(Receipt, contract_hash, RawContractHash),
|
|
660
917
|
normalize_receipt_atom(RawContractHash, ExpectedHash).
|
|
661
918
|
|
|
@@ -680,26 +937,26 @@ receipt_contract_mismatch_evidence(TestId, Scope, Receipts, ContractBinding, Evi
|
|
|
680
937
|
current_contract_hash_value(hash(Hash), Hash).
|
|
681
938
|
current_contract_hash_value(none, none).
|
|
682
939
|
|
|
683
|
-
|
|
684
|
-
( memberchk(
|
|
685
|
-
-> (
|
|
940
|
+
proof_contract_binding(Props, Binding) :-
|
|
941
|
+
( memberchk(proof_contract=RawContract, Props)
|
|
942
|
+
-> (proof_contract_hash(RawContract, Hash) -> Binding = hash(Hash) ; Binding = invalid)
|
|
686
943
|
; Binding = none
|
|
687
944
|
).
|
|
688
945
|
|
|
689
|
-
|
|
690
|
-
|
|
946
|
+
proof_contract_hash(RawContract, Hash) :-
|
|
947
|
+
proof_contract_dict(RawContract, Contract),
|
|
691
948
|
canonical_json_value(Contract, Canonical),
|
|
692
949
|
crypto_data_hash(Canonical, Hash, [algorithm(sha256), encoding(utf8)]).
|
|
693
950
|
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
951
|
+
proof_contract_dict(^^(Value, _), Contract) :- !,
|
|
952
|
+
proof_contract_dict(Value, Contract).
|
|
953
|
+
proof_contract_dict(literal(type(_, Value)), Contract) :- !,
|
|
954
|
+
proof_contract_dict(Value, Contract).
|
|
955
|
+
proof_contract_dict(literal(Value), Contract) :- !,
|
|
956
|
+
proof_contract_dict(Value, Contract).
|
|
957
|
+
proof_contract_dict(Contract, Contract) :-
|
|
701
958
|
is_dict(Contract), !.
|
|
702
|
-
|
|
959
|
+
proof_contract_dict(Raw, Contract) :-
|
|
703
960
|
(atom(Raw) ; string(Raw)),
|
|
704
961
|
catch(atom_json_dict(Raw, Contract, [value_string_as(string)]), _, fail),
|
|
705
962
|
is_dict(Contract).
|
|
@@ -731,23 +988,21 @@ latest_receipt(Receipts, Latest, FinishedStamp) :-
|
|
|
731
988
|
last(Sorted, FinishedStamp-Latest).
|
|
732
989
|
|
|
733
990
|
receipt_runtime_state(_Receipt, FinishedStamp, Context, invalid, AgeSeconds) :-
|
|
734
|
-
AgeSeconds is Context.
|
|
991
|
+
AgeSeconds is Context.proofCheckedAtStamp - FinishedStamp,
|
|
735
992
|
AgeSeconds < -300,
|
|
736
993
|
!.
|
|
737
994
|
receipt_runtime_state(_Receipt, FinishedStamp, Context, stale, AgeSeconds) :-
|
|
738
|
-
AgeSeconds is Context.
|
|
739
|
-
AgeSeconds > Context.
|
|
995
|
+
AgeSeconds is Context.proofCheckedAtStamp - FinishedStamp,
|
|
996
|
+
AgeSeconds > Context.proofMaxAgeSeconds,
|
|
740
997
|
!.
|
|
741
998
|
receipt_runtime_state(Receipt, FinishedStamp, Context, State, AgeSeconds) :-
|
|
742
|
-
AgeSeconds is Context.
|
|
999
|
+
AgeSeconds is Context.proofCheckedAtStamp - FinishedStamp,
|
|
743
1000
|
inventory_entry_field(Receipt, outcome, RawOutcome),
|
|
744
1001
|
normalize_receipt_atom(RawOutcome, Outcome),
|
|
745
1002
|
(Outcome == passed -> State = passed ; State = failed).
|
|
746
1003
|
|
|
747
1004
|
receipt_evidence_dict(TestId, Scope, Receipt, State, AgeSeconds, Evidence) :-
|
|
748
1005
|
receipt_string_field(Receipt, receipt_id, ReceiptId),
|
|
749
|
-
receipt_string_field(Receipt, runner, Runner),
|
|
750
|
-
receipt_string_field(Receipt, command, Command),
|
|
751
1006
|
receipt_string_field(Receipt, outcome, Outcome),
|
|
752
1007
|
receipt_string_field(Receipt, code_snapshot, CodeSnapshot),
|
|
753
1008
|
receipt_string_field(Receipt, environment_hash, EnvironmentHash),
|
|
@@ -759,8 +1014,6 @@ receipt_evidence_dict(TestId, Scope, Receipt, State, AgeSeconds, Evidence) :-
|
|
|
759
1014
|
state: State,
|
|
760
1015
|
scope: Scope,
|
|
761
1016
|
receiptId: ReceiptId,
|
|
762
|
-
runner: Runner,
|
|
763
|
-
command: Command,
|
|
764
1017
|
outcome: Outcome,
|
|
765
1018
|
codeSnapshot: CodeSnapshot,
|
|
766
1019
|
environmentHash: EnvironmentHash,
|
|
@@ -819,6 +1072,14 @@ test_missing_executable_symbol(TestId) :-
|
|
|
819
1072
|
\+ (kb_relationship(executable_for, SymbolId, TestId), kb_entity(SymbolId, symbol, _)).
|
|
820
1073
|
|
|
821
1074
|
production_symbol_stage(ReqId, PassingE2eTests, Stage, ProductionSymbols) :-
|
|
1075
|
+
findall(TestId,
|
|
1076
|
+
(kb_relationship(specified_by, ReqId, ScenarioId),
|
|
1077
|
+
scenario_test(ScenarioId, TestId)),
|
|
1078
|
+
ScenarioTests0),
|
|
1079
|
+
sort(ScenarioTests0, ScenarioTests),
|
|
1080
|
+
production_symbol_stage(ReqId, PassingE2eTests, ScenarioTests, [], _{}, Stage, ProductionSymbols).
|
|
1081
|
+
|
|
1082
|
+
production_symbol_stage(ReqId, PassingE2eTests, ScenarioTests, ReceiptEvidence, Context, Stage, ProductionSymbols) :-
|
|
822
1083
|
findall(SymbolId,
|
|
823
1084
|
(kb_relationship(implements, SymbolId, ReqId),
|
|
824
1085
|
kb_entity(SymbolId, symbol, _),
|
|
@@ -828,15 +1089,23 @@ production_symbol_stage(ReqId, PassingE2eTests, Stage, ProductionSymbols) :-
|
|
|
828
1089
|
include(type_shape_symbol_with_structural_contract, ImplementingSymbols, StructuralSymbols),
|
|
829
1090
|
exclude(type_shape_symbol_with_structural_contract, ImplementingSymbols, ProductionSymbols),
|
|
830
1091
|
include(symbol_not_covered_by_tests(PassingE2eTests), ProductionSymbols, UncoveredSymbols),
|
|
831
|
-
production_stage_status(ProductionSymbols, StructuralSymbols, PassingE2eTests, UncoveredSymbols, Status),
|
|
1092
|
+
production_stage_status(ProductionSymbols, StructuralSymbols, PassingE2eTests, UncoveredSymbols, Status, StatusReason),
|
|
832
1093
|
maplist(symbol_coordinate_ref, ProductionSymbols, Coordinates),
|
|
833
|
-
|
|
1094
|
+
production_coverage_explanations(
|
|
1095
|
+
ReqId, ProductionSymbols, StructuralSymbols, UncoveredSymbols,
|
|
1096
|
+
PassingE2eTests, ScenarioTests, ReceiptEvidence, Context, Explanations),
|
|
1097
|
+
StageBase = _{
|
|
834
1098
|
status: Status,
|
|
835
1099
|
symbols: ProductionSymbols,
|
|
836
1100
|
structuralSymbols: StructuralSymbols,
|
|
837
1101
|
uncoveredSymbols: UncoveredSymbols,
|
|
838
|
-
coordinates: Coordinates
|
|
839
|
-
|
|
1102
|
+
coordinates: Coordinates,
|
|
1103
|
+
explanations: Explanations
|
|
1104
|
+
},
|
|
1105
|
+
( Status == passed
|
|
1106
|
+
-> Stage = StageBase
|
|
1107
|
+
; put_dict(reason, StageBase, StatusReason, Stage)
|
|
1108
|
+
).
|
|
840
1109
|
|
|
841
1110
|
type_shape_symbol_with_structural_contract(SymbolId) :-
|
|
842
1111
|
kb_entity(SymbolId, symbol, SymbolProps),
|
|
@@ -849,14 +1118,295 @@ type_shape_symbol_with_structural_contract(SymbolId) :-
|
|
|
849
1118
|
normalize_atom(RawStatus, Status),
|
|
850
1119
|
memberchk(Status, [active, passing]).
|
|
851
1120
|
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
1121
|
+
% Stage status plus a typed reason, from a single total clause so status and
|
|
1122
|
+
% reason can never disagree (the Align stale-receipts case reported `blocked`
|
|
1123
|
+
% with no indication that fresh receipts were the unblock). Clause order keeps
|
|
1124
|
+
% the historical precedence: no production symbols at all is `missing` even
|
|
1125
|
+
% when E2E evidence is also absent; only then does absent E2E evidence read
|
|
1126
|
+
% as `blocked`.
|
|
1127
|
+
production_stage_status(ProductionSymbols, StructuralSymbols, PassingE2eTests, UncoveredSymbols, Status, Reason) :-
|
|
1128
|
+
( ProductionSymbols == [],
|
|
1129
|
+
StructuralSymbols == []
|
|
1130
|
+
-> Status = missing,
|
|
1131
|
+
Reason = "no production symbols implement this requirement; link at least one with implements"
|
|
1132
|
+
; PassingE2eTests == []
|
|
1133
|
+
-> Status = blocked,
|
|
1134
|
+
Reason = "no passing E2E test evidence is available for the current snapshot; run kibi prove so this stage can evaluate production symbol coverage"
|
|
1135
|
+
; UncoveredSymbols == []
|
|
1136
|
+
-> Status = passed
|
|
1137
|
+
; Status = missing,
|
|
1138
|
+
length(UncoveredSymbols, Count),
|
|
1139
|
+
format(atom(Reason), "~w production symbol(s) lack covered_by links to the passing E2E tests", [Count])
|
|
1140
|
+
).
|
|
856
1141
|
|
|
857
1142
|
symbol_not_covered_by_tests(Tests, SymbolId) :-
|
|
858
1143
|
\+ (member(TestId, Tests), kb_relationship(covered_by, SymbolId, TestId)).
|
|
859
1144
|
|
|
1145
|
+
production_coverage_explanations(ReqId, ProductionSymbols, StructuralSymbols, UncoveredSymbols,
|
|
1146
|
+
PassingE2eTests, ScenarioTests, ReceiptEvidence, Context, Explanations) :-
|
|
1147
|
+
maplist(production_symbol_explanation(ReqId, UncoveredSymbols, PassingE2eTests, ScenarioTests, ReceiptEvidence, Context),
|
|
1148
|
+
ProductionSymbols, ProductionExplanations),
|
|
1149
|
+
maplist(structural_symbol_explanation(ReqId, PassingE2eTests, ScenarioTests, ReceiptEvidence, Context),
|
|
1150
|
+
StructuralSymbols, StructuralExplanations),
|
|
1151
|
+
append(ProductionExplanations, StructuralExplanations, Explanations).
|
|
1152
|
+
|
|
1153
|
+
structural_symbol_explanation(ReqId, PassingE2eTests, ScenarioTests, ReceiptEvidence, Context, SymbolId, Explanation) :-
|
|
1154
|
+
symbol_classification(SymbolId, Classification),
|
|
1155
|
+
symbol_coverage_candidates(structural, PassingE2eTests, ScenarioTests, ReceiptEvidence, Context, SymbolId, Candidates),
|
|
1156
|
+
explanation_reason_text(structural_unit_contract, ReasonText),
|
|
1157
|
+
Explanation = _{
|
|
1158
|
+
symbolId: SymbolId,
|
|
1159
|
+
requirementId: ReqId,
|
|
1160
|
+
classification: Classification,
|
|
1161
|
+
status: covered,
|
|
1162
|
+
reason: structural_unit_contract,
|
|
1163
|
+
reasonText: ReasonText,
|
|
1164
|
+
coverageCandidates: Candidates
|
|
1165
|
+
}.
|
|
1166
|
+
|
|
1167
|
+
production_symbol_explanation(ReqId, UncoveredSymbols, PassingE2eTests, ScenarioTests, ReceiptEvidence, Context, SymbolId, Explanation) :-
|
|
1168
|
+
symbol_classification(SymbolId, Classification),
|
|
1169
|
+
symbol_coverage_candidates(behavioral, PassingE2eTests, ScenarioTests, ReceiptEvidence, Context, SymbolId, Candidates),
|
|
1170
|
+
production_symbol_rollup(UncoveredSymbols, PassingE2eTests, SymbolId, Candidates, Status, Reason),
|
|
1171
|
+
explanation_reason_text(Reason, ReasonText),
|
|
1172
|
+
Explanation = _{
|
|
1173
|
+
symbolId: SymbolId,
|
|
1174
|
+
requirementId: ReqId,
|
|
1175
|
+
classification: Classification,
|
|
1176
|
+
status: Status,
|
|
1177
|
+
reason: Reason,
|
|
1178
|
+
reasonText: ReasonText,
|
|
1179
|
+
coverageCandidates: Candidates
|
|
1180
|
+
}.
|
|
1181
|
+
|
|
1182
|
+
production_symbol_rollup(_Uncovered, [], _SymbolId, _Candidates, uncovered, stage_blocked_no_passing_e2e) :-
|
|
1183
|
+
!.
|
|
1184
|
+
production_symbol_rollup(UncoveredSymbols, _Passing, SymbolId, Candidates, uncovered, Reason) :-
|
|
1185
|
+
memberchk(SymbolId, UncoveredSymbols),
|
|
1186
|
+
!,
|
|
1187
|
+
(Candidates == [] -> Reason = covered_by_missing ; Reason = no_qualifying_e2e_coverage).
|
|
1188
|
+
production_symbol_rollup(_Uncovered, _Passing, _SymbolId, _Candidates, covered, covered).
|
|
1189
|
+
|
|
1190
|
+
symbol_classification(SymbolId, Classification) :-
|
|
1191
|
+
kb_entity(SymbolId, symbol, Props),
|
|
1192
|
+
memberchk(symbol_role=RawRole, Props),
|
|
1193
|
+
normalize_atom(RawRole, Role),
|
|
1194
|
+
Role \= '',
|
|
1195
|
+
!,
|
|
1196
|
+
Classification = Role.
|
|
1197
|
+
symbol_classification(_SymbolId, unknown).
|
|
1198
|
+
|
|
1199
|
+
symbol_coverage_candidates(Mode, PassingE2eTests, ScenarioTests, ReceiptEvidence, Context, SymbolId, Candidates) :-
|
|
1200
|
+
findall(TestId, kb_relationship(covered_by, SymbolId, TestId), TestIds0),
|
|
1201
|
+
sort(TestIds0, TestIds),
|
|
1202
|
+
maplist(coverage_candidate_dict(Mode, PassingE2eTests, ScenarioTests, ReceiptEvidence, Context), TestIds, Candidates).
|
|
1203
|
+
|
|
1204
|
+
coverage_candidate_dict(Mode, PassingE2eTests, ScenarioTests, ReceiptEvidence, Context, TestId, Candidate) :-
|
|
1205
|
+
coverage_candidate_qualification(Mode, PassingE2eTests, ScenarioTests, ReceiptEvidence, Context, TestId, Qualifies, Primary, Secondaries),
|
|
1206
|
+
explanation_reason_text(Primary, ReasonText),
|
|
1207
|
+
cheap_candidate_scope(TestId, Scope),
|
|
1208
|
+
reused_receipt_state(ReceiptEvidence, TestId, ReceiptState),
|
|
1209
|
+
CandidateBase = _{
|
|
1210
|
+
testId: TestId,
|
|
1211
|
+
relationship: covered_by,
|
|
1212
|
+
qualifies: Qualifies,
|
|
1213
|
+
reason: Primary,
|
|
1214
|
+
reasonText: ReasonText,
|
|
1215
|
+
secondaryReasons: Secondaries,
|
|
1216
|
+
scope: Scope
|
|
1217
|
+
},
|
|
1218
|
+
( ReceiptState == none
|
|
1219
|
+
-> Candidate = CandidateBase
|
|
1220
|
+
; put_dict(receiptState, CandidateBase, ReceiptState, Candidate)
|
|
1221
|
+
).
|
|
1222
|
+
|
|
1223
|
+
% Shared candidate evaluator. Mode selects the coverage contract:
|
|
1224
|
+
% behavioral — production symbols require passing scenario-backed E2E
|
|
1225
|
+
% structural — type-shape symbols may also qualify via a unit contract
|
|
1226
|
+
coverage_candidate_qualification(_Mode, PassingE2eTests, _ScenarioTests, _ReceiptEvidence, _Context, TestId, true, covered, []) :-
|
|
1227
|
+
memberchk(TestId, PassingE2eTests),
|
|
1228
|
+
!.
|
|
1229
|
+
coverage_candidate_qualification(_Mode, _Passing, _Scenario, _Evidence, _Context, TestId, false, missing_test_entity, []) :-
|
|
1230
|
+
\+ kb_entity(TestId, test, _),
|
|
1231
|
+
!.
|
|
1232
|
+
coverage_candidate_qualification(structural, _Passing, _Scenario, _Evidence, _Context, TestId, true, structural_unit_contract, []) :-
|
|
1233
|
+
structural_unit_contract_test(TestId),
|
|
1234
|
+
!.
|
|
1235
|
+
coverage_candidate_qualification(_Mode, _Passing, ScenarioTests, ReceiptEvidence, Context, TestId, false, Primary, Secondaries) :-
|
|
1236
|
+
cheap_scope_reject_reason(TestId, ScopeReason),
|
|
1237
|
+
( \+ memberchk(TestId, ScenarioTests)
|
|
1238
|
+
-> Primary = test_not_in_requirement_scenario_chain,
|
|
1239
|
+
maybe_singleton(ScopeReason, ScopeSecondaries),
|
|
1240
|
+
reused_receipt_reject_reasons(ReceiptEvidence, TestId, ReceiptSecondaries),
|
|
1241
|
+
append(ScopeSecondaries, ReceiptSecondaries, Secondaries)
|
|
1242
|
+
; ScopeReason \= none
|
|
1243
|
+
-> Primary = ScopeReason,
|
|
1244
|
+
reused_receipt_reject_reasons(ReceiptEvidence, TestId, Secondaries)
|
|
1245
|
+
; receipt_reject_for_in_chain(ReceiptEvidence, Context, TestId, Primary, Secondaries)
|
|
1246
|
+
).
|
|
1247
|
+
|
|
1248
|
+
structural_unit_contract_test(TestId) :-
|
|
1249
|
+
kb_entity(TestId, test, TestProps),
|
|
1250
|
+
test_scope(TestProps, unit),
|
|
1251
|
+
memberchk(status=RawStatus, TestProps),
|
|
1252
|
+
normalize_atom(RawStatus, Status),
|
|
1253
|
+
memberchk(Status, [active, passing]).
|
|
1254
|
+
|
|
1255
|
+
cheap_candidate_scope(TestId, Scope) :-
|
|
1256
|
+
kb_entity(TestId, test, Props),
|
|
1257
|
+
!,
|
|
1258
|
+
test_scope(Props, Scope).
|
|
1259
|
+
cheap_candidate_scope(_TestId, unknown).
|
|
1260
|
+
|
|
1261
|
+
cheap_scope_reject_reason(TestId, Reason) :-
|
|
1262
|
+
cheap_candidate_scope(TestId, Scope),
|
|
1263
|
+
scope_reject_reason(Scope, Reason).
|
|
1264
|
+
|
|
1265
|
+
scope_reject_reason(unit, test_scope_is_unit) :- !.
|
|
1266
|
+
scope_reject_reason(end_to_end, none) :- !.
|
|
1267
|
+
scope_reject_reason(_Scope, test_scope_is_non_e2e).
|
|
1268
|
+
|
|
1269
|
+
maybe_singleton(none, []) :- !.
|
|
1270
|
+
maybe_singleton(Reason, [Reason]).
|
|
1271
|
+
|
|
1272
|
+
reused_receipt_state(Evidence, TestId, State) :-
|
|
1273
|
+
member(Item, Evidence),
|
|
1274
|
+
Item.testId == TestId,
|
|
1275
|
+
!,
|
|
1276
|
+
State = Item.state.
|
|
1277
|
+
reused_receipt_state(_Evidence, _TestId, none).
|
|
1278
|
+
|
|
1279
|
+
reused_receipt_item(Evidence, TestId, Item) :-
|
|
1280
|
+
member(Item, Evidence),
|
|
1281
|
+
Item.testId == TestId,
|
|
1282
|
+
!.
|
|
1283
|
+
|
|
1284
|
+
reused_receipt_reject_reasons(Evidence, TestId, Reasons) :-
|
|
1285
|
+
reused_receipt_item(Evidence, TestId, Item),
|
|
1286
|
+
!,
|
|
1287
|
+
receipt_item_reject_reason(Item, Reason),
|
|
1288
|
+
maybe_singleton(Reason, Reasons).
|
|
1289
|
+
reused_receipt_reject_reasons(_Evidence, _TestId, []).
|
|
1290
|
+
|
|
1291
|
+
receipt_reject_for_in_chain(Evidence, _Context, TestId, Primary, []) :-
|
|
1292
|
+
reused_receipt_item(Evidence, TestId, Item),
|
|
1293
|
+
!,
|
|
1294
|
+
receipt_item_reject_reason(Item, Reason),
|
|
1295
|
+
(Reason == none -> Primary = no_qualifying_e2e_coverage ; Primary = Reason).
|
|
1296
|
+
receipt_reject_for_in_chain(_Evidence, Context, TestId, Primary, []) :-
|
|
1297
|
+
is_dict(Context),
|
|
1298
|
+
Context \= _{},
|
|
1299
|
+
test_receipt_evidence(Context, TestId, Evidence),
|
|
1300
|
+
!,
|
|
1301
|
+
receipt_item_reject_reason(Evidence, Reason),
|
|
1302
|
+
(Reason == none -> Primary = no_qualifying_e2e_coverage ; Primary = Reason).
|
|
1303
|
+
receipt_reject_for_in_chain(_Evidence, _Context, _TestId, no_qualifying_e2e_coverage, []).
|
|
1304
|
+
|
|
1305
|
+
receipt_item_reject_reason(Item, stale_proof_receipt) :-
|
|
1306
|
+
Item.state == stale,
|
|
1307
|
+
get_dict(codeSnapshot, Item, _),
|
|
1308
|
+
!.
|
|
1309
|
+
receipt_item_reject_reason(Item, receipt_snapshot_mismatch) :-
|
|
1310
|
+
Item.state == stale,
|
|
1311
|
+
!.
|
|
1312
|
+
receipt_item_reject_reason(Item, Reason) :-
|
|
1313
|
+
receipt_state_reject_reason(Item.state, Reason).
|
|
1314
|
+
|
|
1315
|
+
receipt_state_reject_reason(missing, missing_proof_receipt) :- !.
|
|
1316
|
+
receipt_state_reject_reason(snapshot_unavailable, proof_snapshot_unavailable) :- !.
|
|
1317
|
+
receipt_state_reject_reason(contract_mismatch, receipt_contract_mismatch) :- !.
|
|
1318
|
+
receipt_state_reject_reason(invalid, invalid_proof_receipt) :- !.
|
|
1319
|
+
receipt_state_reject_reason(failed, failed_proof_receipt) :- !.
|
|
1320
|
+
receipt_state_reject_reason(not_end_to_end, none) :- !.
|
|
1321
|
+
receipt_state_reject_reason(stale, receipt_snapshot_mismatch) :- !.
|
|
1322
|
+
receipt_state_reject_reason(passed, none) :- !.
|
|
1323
|
+
receipt_state_reject_reason(none, none) :- !.
|
|
1324
|
+
receipt_state_reject_reason(_, none).
|
|
1325
|
+
|
|
1326
|
+
explanation_reason_text(Reason, Text) :-
|
|
1327
|
+
explanation_reason_text_def(Reason, Text),
|
|
1328
|
+
!.
|
|
1329
|
+
explanation_reason_text(Reason, Reason) :-
|
|
1330
|
+
atom(Reason).
|
|
1331
|
+
|
|
1332
|
+
explanation_reason_text_def(covered, "Qualifying covered_by to a passing scenario-backed E2E test").
|
|
1333
|
+
explanation_reason_text_def(covered_by_missing, "No covered_by relationship to any test").
|
|
1334
|
+
explanation_reason_text_def(no_qualifying_e2e_coverage, "covered_by relationships exist but none qualify as passing scenario-backed E2E coverage").
|
|
1335
|
+
explanation_reason_text_def(structural_unit_contract, "type-shape symbol is satisfied by a unit structural contract").
|
|
1336
|
+
explanation_reason_text_def(executable_test, "Symbol is executable test code linked with executable_for").
|
|
1337
|
+
explanation_reason_text_def(stage_blocked_no_passing_e2e, "Production coverage cannot be evaluated because no passing E2E evidence is available for the current snapshot").
|
|
1338
|
+
explanation_reason_text_def(missing_test_entity, "covered_by target is not a test entity").
|
|
1339
|
+
explanation_reason_text_def(test_not_in_requirement_scenario_chain, "Test is not in this requirement's scenario chain").
|
|
1340
|
+
explanation_reason_text_def(test_scope_is_unit, "Test verification_scope is unit, which cannot cover behavioral production symbols").
|
|
1341
|
+
explanation_reason_text_def(test_scope_is_non_e2e, "Test verification_scope is not end_to_end").
|
|
1342
|
+
explanation_reason_text_def(proof_snapshot_unavailable, "Workspace snapshot is unavailable so receipt currency cannot be proven").
|
|
1343
|
+
explanation_reason_text_def(missing_proof_receipt, "Scenario-backed E2E test has no kibi.proof-receipt.v1").
|
|
1344
|
+
explanation_reason_text_def(receipt_snapshot_mismatch, "Receipts exist but none match the current snapshot or binding").
|
|
1345
|
+
explanation_reason_text_def(stale_proof_receipt, "Current-snapshot receipt is older than the allowed proof age").
|
|
1346
|
+
explanation_reason_text_def(receipt_contract_mismatch, "Receipt does not match the current proof contract hash or fingerprint").
|
|
1347
|
+
explanation_reason_text_def(invalid_proof_receipt, "Receipt evidence is malformed or otherwise uncheckable").
|
|
1348
|
+
explanation_reason_text_def(failed_proof_receipt, "Latest current receipt outcome is not passed").
|
|
1349
|
+
|
|
1350
|
+
test_resolution_record(TestId, _{
|
|
1351
|
+
testId: TestId,
|
|
1352
|
+
requiredProofs: RequiredProofs,
|
|
1353
|
+
executableFor: ExecutableFor
|
|
1354
|
+
}) :-
|
|
1355
|
+
test_required_proof_refs(TestId, RequiredProofs),
|
|
1356
|
+
test_executable_for_refs(TestId, ExecutableFor).
|
|
1357
|
+
|
|
1358
|
+
test_required_proof_refs(TestId, Refs) :-
|
|
1359
|
+
kb_entity(TestId, test, Props),
|
|
1360
|
+
memberchk(proof_contract=RawContract, Props),
|
|
1361
|
+
proof_contract_dict(RawContract, Contract),
|
|
1362
|
+
contract_required_symbol_ids(Contract, SymbolIds),
|
|
1363
|
+
!,
|
|
1364
|
+
maplist(symbol_resolution_ref, SymbolIds, Refs).
|
|
1365
|
+
test_required_proof_refs(_TestId, []).
|
|
1366
|
+
|
|
1367
|
+
contract_required_symbol_ids(Contract, SymbolIds) :-
|
|
1368
|
+
( get_dict(required_proofs, Contract, Raw)
|
|
1369
|
+
; get_dict('required_proofs', Contract, Raw)
|
|
1370
|
+
),
|
|
1371
|
+
!,
|
|
1372
|
+
inventory_entries(Raw, Entries),
|
|
1373
|
+
findall(SymbolId,
|
|
1374
|
+
(member(Entry, Entries), required_proof_entry_symbol(Entry, SymbolId)),
|
|
1375
|
+
SymbolIds0),
|
|
1376
|
+
sort(SymbolIds0, SymbolIds).
|
|
1377
|
+
contract_required_symbol_ids(_, []).
|
|
1378
|
+
|
|
1379
|
+
required_proof_entry_symbol(Entry, SymbolId) :-
|
|
1380
|
+
( inventory_entry_field(Entry, symbol_id, Raw)
|
|
1381
|
+
; inventory_entry_field(Entry, symbolId, Raw)
|
|
1382
|
+
),
|
|
1383
|
+
normalize_atom(Raw, SymbolId),
|
|
1384
|
+
SymbolId \= ''.
|
|
1385
|
+
|
|
1386
|
+
test_executable_for_refs(TestId, Refs) :-
|
|
1387
|
+
findall(SymbolId,
|
|
1388
|
+
(kb_relationship(executable_for, SymbolId, TestId), kb_entity(SymbolId, symbol, _)),
|
|
1389
|
+
SymbolIds0),
|
|
1390
|
+
sort(SymbolIds0, SymbolIds),
|
|
1391
|
+
maplist(symbol_resolution_ref, SymbolIds, Refs).
|
|
1392
|
+
|
|
1393
|
+
symbol_resolution_ref(SymbolId, Ref) :-
|
|
1394
|
+
kb_entity(SymbolId, symbol, _),
|
|
1395
|
+
!,
|
|
1396
|
+
symbol_coordinate_ref(SymbolId, Coord),
|
|
1397
|
+
symbol_classification(SymbolId, Role),
|
|
1398
|
+
put_dict(_{role: Role, exists: true}, Coord, Ref).
|
|
1399
|
+
symbol_resolution_ref(SymbolId, _{
|
|
1400
|
+
id: SymbolId,
|
|
1401
|
+
sourceFile: '',
|
|
1402
|
+
sourceLine: 0,
|
|
1403
|
+
sourceColumn: 0,
|
|
1404
|
+
sourceEndLine: 0,
|
|
1405
|
+
sourceEndColumn: 0,
|
|
1406
|
+
role: unresolved,
|
|
1407
|
+
exists: false
|
|
1408
|
+
}).
|
|
1409
|
+
|
|
860
1410
|
source_coordinate_stage(ReqProps, ExecutableSymbols, ProductionSymbols, Stage) :-
|
|
861
1411
|
append(ExecutableSymbols, ProductionSymbols, Symbols0),
|
|
862
1412
|
sort(Symbols0, Symbols),
|
|
@@ -988,8 +1538,9 @@ proof_advisories(Stages, Advisories) :-
|
|
|
988
1538
|
proof_issues(Stages, _, Advisories).
|
|
989
1539
|
|
|
990
1540
|
% implements REQ-kibi-conservative-requirement-proof
|
|
991
|
-
%
|
|
992
|
-
%
|
|
1541
|
+
% Every linked scenario is a mandatory proof obligation. Receipt-completeness
|
|
1542
|
+
% failures therefore remain blocking even when another scenario has a passing
|
|
1543
|
+
% receipt; unit/integration-only evidence stays nonblocking in the E2E lane.
|
|
993
1544
|
% Invariant: proofStatus == proven implies proofGaps == [].
|
|
994
1545
|
proof_issues(Stages, Gaps, Advisories) :-
|
|
995
1546
|
findall(Gap,
|
|
@@ -1003,15 +1554,14 @@ proof_issues(Stages, Gaps, Advisories) :-
|
|
|
1003
1554
|
proof_issue_advisory(Gap, Stages)),
|
|
1004
1555
|
Advisories).
|
|
1005
1556
|
|
|
1006
|
-
receipt_completeness_issue(
|
|
1007
|
-
receipt_completeness_issue(
|
|
1008
|
-
receipt_completeness_issue(
|
|
1009
|
-
receipt_completeness_issue(
|
|
1010
|
-
receipt_completeness_issue(
|
|
1557
|
+
receipt_completeness_issue(missing_proof_receipt).
|
|
1558
|
+
receipt_completeness_issue(stale_proof_receipt).
|
|
1559
|
+
receipt_completeness_issue(failed_proof_receipt).
|
|
1560
|
+
receipt_completeness_issue(invalid_proof_receipt).
|
|
1561
|
+
receipt_completeness_issue(proof_contract_mismatch).
|
|
1011
1562
|
|
|
1012
|
-
proof_issue_advisory(
|
|
1013
|
-
|
|
1014
|
-
Stages.passingE2e.status == passed.
|
|
1563
|
+
proof_issue_advisory(_Gap, _Stages) :-
|
|
1564
|
+
fail.
|
|
1015
1565
|
|
|
1016
1566
|
proof_gap_present(missing_semantic_inventory, Stages) :- Stages.semanticInventory.propositionCount =:= 0.
|
|
1017
1567
|
proof_gap_present(incomplete_semantic_inventory, Stages) :- Stages.semanticInventory.missingCount > 0.
|
|
@@ -1028,12 +1578,12 @@ proof_gap_present(contradiction_check_incomplete, Stages) :- Stages.contradictio
|
|
|
1028
1578
|
proof_gap_present(missing_scenario, Stages) :- Stages.scenarios.status == missing.
|
|
1029
1579
|
proof_gap_present(missing_scenario_test, Stages) :- Stages.scenarios.status == passed, Stages.scenarioTests.status == missing.
|
|
1030
1580
|
proof_gap_present(missing_passing_e2e, Stages) :- Stages.passingE2e.status == missing.
|
|
1031
|
-
proof_gap_present(
|
|
1032
|
-
proof_gap_present(
|
|
1033
|
-
proof_gap_present(
|
|
1034
|
-
proof_gap_present(
|
|
1035
|
-
proof_gap_present(
|
|
1036
|
-
proof_gap_present(
|
|
1581
|
+
proof_gap_present(missing_proof_receipt, Stages) :- Stages.passingE2e.missingReceiptTests \= [].
|
|
1582
|
+
proof_gap_present(stale_proof_receipt, Stages) :- Stages.passingE2e.staleReceiptTests \= [].
|
|
1583
|
+
proof_gap_present(failed_proof_receipt, Stages) :- Stages.passingE2e.failedReceiptTests \= [].
|
|
1584
|
+
proof_gap_present(invalid_proof_receipt, Stages) :- Stages.passingE2e.invalidReceiptTests \= [].
|
|
1585
|
+
proof_gap_present(proof_contract_mismatch, Stages) :- Stages.passingE2e.contractMismatchReceiptTests \= [].
|
|
1586
|
+
proof_gap_present(proof_snapshot_unavailable, Stages) :- Stages.passingE2e.snapshotUnavailableTests \= [].
|
|
1037
1587
|
proof_gap_present(missing_executable_test_symbol, Stages) :- Stages.executableSymbols.status == missing.
|
|
1038
1588
|
proof_gap_present(missing_production_symbol, Stages) :-
|
|
1039
1589
|
Stages.productionSymbols.symbols == [],
|
|
@@ -1055,12 +1605,12 @@ gap_definition(contradiction_check_incomplete, 41, contradictions, "Complete log
|
|
|
1055
1605
|
gap_definition(missing_scenario, 50, scenarios, "Add a specified_by scenario for the requirement.").
|
|
1056
1606
|
gap_definition(missing_scenario_test, 51, scenario_tests, "Link the scenario to a test with verified_by or validates.").
|
|
1057
1607
|
gap_definition(missing_passing_e2e, 52, passing_e2e, "Record fresh passing end-to-end evidence on a scenario-backed test.").
|
|
1058
|
-
gap_definition(
|
|
1059
|
-
gap_definition(
|
|
1060
|
-
gap_definition(
|
|
1061
|
-
gap_definition(
|
|
1062
|
-
gap_definition(
|
|
1063
|
-
gap_definition(
|
|
1608
|
+
gap_definition(missing_proof_receipt, 53, passing_e2e, "Run the configured proof integration through kibi prove and append its kibi.proof-receipt.v1 result for the scenario-backed proof-bearing test.").
|
|
1609
|
+
gap_definition(stale_proof_receipt, 54, passing_e2e, "Re-run kibi prove against the current code snapshot and append fresh receipts.").
|
|
1610
|
+
gap_definition(failed_proof_receipt, 55, passing_e2e, "Repair the failing proof obligation and append a newer passing receipt for the same snapshot.").
|
|
1611
|
+
gap_definition(invalid_proof_receipt, 56, passing_e2e, "Repair malformed, future-dated, mismatched, or otherwise uncheckable receipt evidence.").
|
|
1612
|
+
gap_definition(proof_contract_mismatch, 57, passing_e2e, "Run kibi prove so the receipt carries the current proof contract hash and execution fingerprint without rewriting historical evidence.").
|
|
1613
|
+
gap_definition(proof_snapshot_unavailable, 58, passing_e2e, "Run coverage through a CLI or MCP runtime that exposes the deterministic workspace snapshot.").
|
|
1064
1614
|
gap_definition(missing_executable_test_symbol, 60, executable_symbols, "Link executable test code to every qualifying E2E test with executable_for.").
|
|
1065
1615
|
gap_definition(missing_production_symbol, 70, production_symbols, "Link at least one production symbol to the requirement with implements.").
|
|
1066
1616
|
gap_definition(missing_production_symbol_coverage, 71, production_symbols, "Link every runtime implementing production symbol to a qualifying E2E test with covered_by; type-shape symbols retain structural unit contracts instead.").
|