kibi-core 0.11.0 → 0.12.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 +1 -1
- package/schema/entities.pl +10 -4
- package/schema/validation.pl +3 -2
- package/src/checks.pl +69 -5
- package/src/discovery.pl +106 -4
- package/src/kb.pl +68 -5
- package/src/requirement_proof.pl +413 -143
- package/src/rule_registry.pl +131 -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,36 +44,41 @@ 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
|
-
scenario_test_stage(ScenarioIds, ScenarioTestStage,
|
|
64
|
-
passing_e2e_stage(
|
|
80
|
+
scenario_test_stage(ScenarioIds, ScenarioTestStage, _ScenarioTests),
|
|
81
|
+
passing_e2e_stage(ScenarioTestStage, Context, PassingE2eStage, PassingE2eTests),
|
|
65
82
|
executable_symbol_stage(PassingE2eTests, ExecutableStage, ExecutableSymbols),
|
|
66
83
|
production_symbol_stage(ReqId, PassingE2eTests, ProductionStage, ProductionSymbols),
|
|
67
84
|
source_coordinate_stage(ReqProps, ExecutableSymbols, ProductionSymbols, CoordinateStage),
|
|
@@ -89,6 +106,45 @@ requirement_proof(ReqId, ReqProps, Context, Proof) :-
|
|
|
89
106
|
proofStages: Stages
|
|
90
107
|
}.
|
|
91
108
|
|
|
109
|
+
exempt_requirement_proof(Reason, Proof) :-
|
|
110
|
+
proof_version(Version),
|
|
111
|
+
Proof = _{
|
|
112
|
+
proofVersion: Version,
|
|
113
|
+
proofStatus: not_applicable,
|
|
114
|
+
proofGaps: [],
|
|
115
|
+
proofAdvisories: [],
|
|
116
|
+
proofRepairs: [],
|
|
117
|
+
proofStages: _{applicability: _{status: not_applicable, reason: Reason}}
|
|
118
|
+
}.
|
|
119
|
+
|
|
120
|
+
%% proof_exempt(+Props, -Reason)
|
|
121
|
+
% A current requirement is exempt from E2E-proof scope when its author set
|
|
122
|
+
% proof_exempt to a truthy value AND supplied a non-empty proof_exempt_reason.
|
|
123
|
+
% Without the reason the exemption does not apply, so an author cannot park a
|
|
124
|
+
% requirement silently; upsert validation enforces the same pairing.
|
|
125
|
+
proof_exempt(Props, Reason) :-
|
|
126
|
+
memberchk(proof_exempt=RawFlag, Props),
|
|
127
|
+
normalize_atom(RawFlag, Flag),
|
|
128
|
+
memberchk(Flag, [true, yes, on, '1']),
|
|
129
|
+
memberchk(proof_exempt_reason=RawReason, Props),
|
|
130
|
+
normalize_atom(RawReason, Reason),
|
|
131
|
+
Reason \= ''.
|
|
132
|
+
|
|
133
|
+
%% applicability_exclusion_reason(+ReqId, -Reason)
|
|
134
|
+
% Typed reason a requirement fails current_req/1: superseded lifecycle, or a
|
|
135
|
+
% status outside the canonical+legacy requirement vocabulary (e.g. the ADR
|
|
136
|
+
% vocabulary 'accepted').
|
|
137
|
+
applicability_exclusion_reason(ReqId, "requirement is superseded by a newer requirement") :-
|
|
138
|
+
kb_relationship(supersedes, _, ReqId),
|
|
139
|
+
!.
|
|
140
|
+
applicability_exclusion_reason(ReqId, Reason) :-
|
|
141
|
+
kb_entity(ReqId, req, Props),
|
|
142
|
+
memberchk(status=Status, Props),
|
|
143
|
+
normalize_term_atom(Status, StatusAtom),
|
|
144
|
+
format(atom(Reason), "status '~w' is not a current requirement status", [StatusAtom]),
|
|
145
|
+
!.
|
|
146
|
+
applicability_exclusion_reason(_ReqId, "requirement is not current").
|
|
147
|
+
|
|
92
148
|
semantic_inventory_stage(Props, Stage, Entries) :-
|
|
93
149
|
( memberchk(semantic_inventory=RawInventory, Props),
|
|
94
150
|
inventory_entries(RawInventory, Entries0),
|
|
@@ -435,31 +491,94 @@ violation_for_entity(Violations, EntityId) :-
|
|
|
435
491
|
|
|
436
492
|
scenario_stage(ReqId, Stage, ScenarioIds) :-
|
|
437
493
|
findall(ScenarioId,
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
sort(
|
|
441
|
-
(
|
|
494
|
+
kb_relationship(specified_by, ReqId, ScenarioId),
|
|
495
|
+
ScenarioTargets0),
|
|
496
|
+
sort(ScenarioTargets0, ScenarioTargets),
|
|
497
|
+
include(existing_scenario, ScenarioTargets, ScenarioIds),
|
|
498
|
+
findall(ScenarioId,
|
|
499
|
+
(member(ScenarioId, ScenarioTargets), \+ existing_scenario(ScenarioId)),
|
|
500
|
+
InvalidScenarioTargets),
|
|
501
|
+
((ScenarioIds = [] ; InvalidScenarioTargets \= []) -> Status = missing ; Status = passed),
|
|
442
502
|
maplist(entity_source_ref, ScenarioIds, Sources),
|
|
443
|
-
Stage = _{
|
|
503
|
+
Stage = _{
|
|
504
|
+
status: Status,
|
|
505
|
+
scenarios: ScenarioIds,
|
|
506
|
+
scenarioTargets: ScenarioTargets,
|
|
507
|
+
invalidScenarioTargets: InvalidScenarioTargets,
|
|
508
|
+
sources: Sources
|
|
509
|
+
}.
|
|
510
|
+
|
|
511
|
+
existing_scenario(ScenarioId) :-
|
|
512
|
+
kb_entity(ScenarioId, scenario, _).
|
|
444
513
|
|
|
445
514
|
scenario_test_stage(ScenarioIds, Stage, ScenarioTests) :-
|
|
515
|
+
maplist(scenario_test_obligation, ScenarioIds, Obligations),
|
|
446
516
|
findall(TestId,
|
|
447
|
-
(member(
|
|
517
|
+
(member(Obligation, Obligations), member(TestId, Obligation.tests)),
|
|
448
518
|
ScenarioTests0),
|
|
449
519
|
sort(ScenarioTests0, ScenarioTests),
|
|
450
|
-
(
|
|
520
|
+
findall(TestId,
|
|
521
|
+
(member(Obligation, Obligations), member(TestId, Obligation.scenarioTestTargets)),
|
|
522
|
+
ScenarioTestTargets0),
|
|
523
|
+
sort(ScenarioTestTargets0, ScenarioTestTargets),
|
|
524
|
+
findall(TestId,
|
|
525
|
+
(member(Obligation, Obligations), member(TestId, Obligation.invalidScenarioTestTargets)),
|
|
526
|
+
InvalidScenarioTestTargets0),
|
|
527
|
+
sort(InvalidScenarioTestTargets0, InvalidScenarioTestTargets),
|
|
528
|
+
( ScenarioIds == []
|
|
529
|
+
-> Status = missing
|
|
530
|
+
; member(Obligation, Obligations), Obligation.status == missing
|
|
531
|
+
-> Status = missing
|
|
532
|
+
; Status = passed
|
|
533
|
+
),
|
|
451
534
|
maplist(entity_source_ref, ScenarioTests, Sources),
|
|
452
|
-
Stage = _{
|
|
535
|
+
Stage = _{
|
|
536
|
+
status: Status,
|
|
537
|
+
scenarios: ScenarioIds,
|
|
538
|
+
tests: ScenarioTests,
|
|
539
|
+
scenarioTestTargets: ScenarioTestTargets,
|
|
540
|
+
invalidScenarioTestTargets: InvalidScenarioTestTargets,
|
|
541
|
+
sources: Sources,
|
|
542
|
+
obligations: Obligations
|
|
543
|
+
}.
|
|
453
544
|
|
|
454
|
-
|
|
455
|
-
|
|
545
|
+
scenario_test_obligation(ScenarioId, Obligation) :-
|
|
546
|
+
findall(TestId, declared_scenario_test_target(ScenarioId, TestId), TestTargets0),
|
|
547
|
+
sort(TestTargets0, TestTargets),
|
|
548
|
+
include(existing_test, TestTargets, TestIds),
|
|
549
|
+
findall(TestId,
|
|
550
|
+
(member(TestId, TestTargets), \+ existing_test(TestId)),
|
|
551
|
+
InvalidScenarioTestTargets),
|
|
552
|
+
maplist(entity_source_ref, TestIds, Sources),
|
|
553
|
+
( (TestIds == [] ; InvalidScenarioTestTargets \= [])
|
|
554
|
+
-> Status = missing
|
|
555
|
+
; Status = passed
|
|
556
|
+
),
|
|
557
|
+
Obligation = _{
|
|
558
|
+
scenarioId: ScenarioId,
|
|
559
|
+
status: Status,
|
|
560
|
+
scenarioTestTargets: TestTargets,
|
|
561
|
+
invalidScenarioTestTargets: InvalidScenarioTestTargets,
|
|
562
|
+
tests: TestIds,
|
|
563
|
+
sources: Sources
|
|
564
|
+
}.
|
|
565
|
+
|
|
566
|
+
declared_scenario_test_target(ScenarioId, TestId) :-
|
|
567
|
+
kb_relationship(verified_by, ScenarioId, TestId).
|
|
568
|
+
declared_scenario_test_target(ScenarioId, TestId) :-
|
|
569
|
+
kb_relationship(validates, TestId, ScenarioId).
|
|
570
|
+
|
|
571
|
+
existing_test(TestId) :-
|
|
456
572
|
kb_entity(TestId, test, _).
|
|
573
|
+
|
|
457
574
|
scenario_test(ScenarioId, TestId) :-
|
|
458
|
-
|
|
459
|
-
|
|
575
|
+
declared_scenario_test_target(ScenarioId, TestId),
|
|
576
|
+
existing_test(TestId).
|
|
460
577
|
|
|
461
|
-
passing_e2e_stage(
|
|
462
|
-
maplist(
|
|
578
|
+
passing_e2e_stage(ScenarioTestStage, Context, Stage, PassingE2eTests) :-
|
|
579
|
+
maplist(scenario_passing_e2e_obligation(Context), ScenarioTestStage.obligations, ScenarioObligations),
|
|
580
|
+
findall(Item, (member(Obligation, ScenarioObligations), member(Item, Obligation.evidence)), Evidence0),
|
|
581
|
+
unique_evidence_by_test(Evidence0, Evidence),
|
|
463
582
|
evidence_tests_with_state(Evidence, passed, PassingE2eTests),
|
|
464
583
|
evidence_tests_with_state(Evidence, missing, MissingReceiptTests),
|
|
465
584
|
evidence_tests_with_state(Evidence, stale, StaleReceiptTests),
|
|
@@ -468,10 +587,11 @@ passing_e2e_stage(ScenarioTests, Context, Stage, PassingE2eTests) :-
|
|
|
468
587
|
evidence_tests_with_state(Evidence, contract_mismatch, ContractMismatchReceiptTests),
|
|
469
588
|
evidence_tests_with_state(Evidence, snapshot_unavailable, SnapshotUnavailableTests),
|
|
470
589
|
evidence_tests_with_state(Evidence, not_end_to_end, NonEndToEndTests),
|
|
471
|
-
passing_e2e_status(
|
|
590
|
+
passing_e2e_status(ScenarioObligations, Status),
|
|
472
591
|
Stage = _{
|
|
473
592
|
status: Status,
|
|
474
593
|
tests: PassingE2eTests,
|
|
594
|
+
scenarioObligations: ScenarioObligations,
|
|
475
595
|
receiptEvidence: Evidence,
|
|
476
596
|
missingReceiptTests: MissingReceiptTests,
|
|
477
597
|
staleReceiptTests: StaleReceiptTests,
|
|
@@ -480,16 +600,104 @@ passing_e2e_stage(ScenarioTests, Context, Stage, PassingE2eTests) :-
|
|
|
480
600
|
contractMismatchReceiptTests: ContractMismatchReceiptTests,
|
|
481
601
|
snapshotUnavailableTests: SnapshotUnavailableTests,
|
|
482
602
|
nonEndToEndTests: NonEndToEndTests,
|
|
483
|
-
currentCodeSnapshot: Context.
|
|
484
|
-
checkedAt: Context.
|
|
485
|
-
maxAgeSeconds: Context.
|
|
603
|
+
currentCodeSnapshot: Context.proofSnapshot,
|
|
604
|
+
checkedAt: Context.proofCheckedAt,
|
|
605
|
+
maxAgeSeconds: Context.proofMaxAgeSeconds
|
|
606
|
+
}.
|
|
607
|
+
|
|
608
|
+
unique_evidence_by_test(Evidence0, Evidence) :-
|
|
609
|
+
findall(TestId,
|
|
610
|
+
(member(Item, Evidence0), TestId = Item.testId),
|
|
611
|
+
TestIds0),
|
|
612
|
+
sort(TestIds0, TestIds),
|
|
613
|
+
maplist(first_evidence_for_test(Evidence0), TestIds, Evidence).
|
|
614
|
+
|
|
615
|
+
first_evidence_for_test(Evidence, TestId, Item) :-
|
|
616
|
+
member(Item, Evidence),
|
|
617
|
+
Item.testId == TestId,
|
|
618
|
+
!.
|
|
619
|
+
|
|
620
|
+
scenario_passing_e2e_obligation(Context, TestObligation, Obligation) :-
|
|
621
|
+
maplist(test_receipt_evidence(Context), TestObligation.tests, Evidence),
|
|
622
|
+
include(e2e_evidence, Evidence, E2eEvidence),
|
|
623
|
+
include(non_e2e_evidence, Evidence, NonEndToEndEvidence),
|
|
624
|
+
evidence_tests_with_state(E2eEvidence, passed, PassingE2eTests),
|
|
625
|
+
evidence_tests_with_state(E2eEvidence, missing, MissingReceiptTests),
|
|
626
|
+
evidence_tests_with_state(E2eEvidence, stale, StaleReceiptTests),
|
|
627
|
+
evidence_tests_with_state(E2eEvidence, failed, FailedReceiptTests),
|
|
628
|
+
evidence_tests_with_state(E2eEvidence, invalid, InvalidReceiptTests),
|
|
629
|
+
evidence_tests_with_state(E2eEvidence, contract_mismatch, ContractMismatchReceiptTests),
|
|
630
|
+
evidence_tests_with_state(E2eEvidence, snapshot_unavailable, SnapshotUnavailableTests),
|
|
631
|
+
evidence_tests_with_state(NonEndToEndEvidence, not_end_to_end, NonEndToEndTests),
|
|
632
|
+
scenario_obligation_gaps(TestObligation, E2eEvidence, Gaps),
|
|
633
|
+
scenario_obligation_status(TestObligation, E2eEvidence, Status),
|
|
634
|
+
findall(TestId,
|
|
635
|
+
(member(Item, E2eEvidence), TestId = Item.testId),
|
|
636
|
+
E2eTests0),
|
|
637
|
+
sort(E2eTests0, E2eTests),
|
|
638
|
+
Obligation = _{
|
|
639
|
+
scenarioId: TestObligation.scenarioId,
|
|
640
|
+
status: Status,
|
|
641
|
+
scenarioTestTargets: TestObligation.scenarioTestTargets,
|
|
642
|
+
invalidScenarioTestTargets: TestObligation.invalidScenarioTestTargets,
|
|
643
|
+
tests: TestObligation.tests,
|
|
644
|
+
e2eTests: E2eTests,
|
|
645
|
+
nonEndToEndTests: NonEndToEndTests,
|
|
646
|
+
evidence: Evidence,
|
|
647
|
+
gaps: Gaps,
|
|
648
|
+
passingE2eTests: PassingE2eTests,
|
|
649
|
+
missingReceiptTests: MissingReceiptTests,
|
|
650
|
+
staleReceiptTests: StaleReceiptTests,
|
|
651
|
+
failedReceiptTests: FailedReceiptTests,
|
|
652
|
+
invalidReceiptTests: InvalidReceiptTests,
|
|
653
|
+
contractMismatchReceiptTests: ContractMismatchReceiptTests,
|
|
654
|
+
snapshotUnavailableTests: SnapshotUnavailableTests
|
|
486
655
|
}.
|
|
487
656
|
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
657
|
+
e2e_evidence(Item) :- Item.state \= not_end_to_end.
|
|
658
|
+
non_e2e_evidence(Item) :- Item.state == not_end_to_end.
|
|
659
|
+
|
|
660
|
+
scenario_obligation_gaps(TestObligation, E2eEvidence, Gaps) :-
|
|
661
|
+
findall(Gap, scenario_obligation_gap(TestObligation, E2eEvidence, Gap), Gaps0),
|
|
662
|
+
sort(Gaps0, Gaps).
|
|
663
|
+
|
|
664
|
+
scenario_obligation_gap(TestObligation, _E2eEvidence, missing_scenario_test) :-
|
|
665
|
+
TestObligation.status == missing.
|
|
666
|
+
scenario_obligation_gap(TestObligation, E2eEvidence, missing_passing_e2e) :-
|
|
667
|
+
TestObligation.status == passed,
|
|
668
|
+
E2eEvidence == [].
|
|
669
|
+
scenario_obligation_gap(_TestObligation, Evidence, missing_proof_receipt) :-
|
|
670
|
+
member(Item, Evidence), Item.state == missing.
|
|
671
|
+
scenario_obligation_gap(_TestObligation, Evidence, stale_proof_receipt) :-
|
|
672
|
+
member(Item, Evidence), Item.state == stale.
|
|
673
|
+
scenario_obligation_gap(_TestObligation, Evidence, failed_proof_receipt) :-
|
|
674
|
+
member(Item, Evidence), Item.state == failed.
|
|
675
|
+
scenario_obligation_gap(_TestObligation, Evidence, invalid_proof_receipt) :-
|
|
676
|
+
member(Item, Evidence), Item.state == invalid.
|
|
677
|
+
scenario_obligation_gap(_TestObligation, Evidence, proof_contract_mismatch) :-
|
|
678
|
+
member(Item, Evidence), Item.state == contract_mismatch.
|
|
679
|
+
scenario_obligation_gap(_TestObligation, Evidence, proof_snapshot_unavailable) :-
|
|
680
|
+
member(Item, Evidence), Item.state == snapshot_unavailable.
|
|
681
|
+
|
|
682
|
+
scenario_obligation_status(TestObligation, _E2eEvidence, missing) :-
|
|
683
|
+
TestObligation.status == missing,
|
|
684
|
+
!.
|
|
685
|
+
scenario_obligation_status(_TestObligation, [], missing) :-
|
|
686
|
+
!.
|
|
687
|
+
scenario_obligation_status(_TestObligation, Evidence, passed) :-
|
|
688
|
+
Evidence \= [],
|
|
689
|
+
forall(member(Item, Evidence), Item.state == passed),
|
|
690
|
+
!.
|
|
691
|
+
scenario_obligation_status(_TestObligation, _Evidence, unresolved).
|
|
692
|
+
|
|
693
|
+
passing_e2e_status([], missing) :- !.
|
|
694
|
+
passing_e2e_status(Obligations, passed) :-
|
|
695
|
+
forall(member(Obligation, Obligations), Obligation.status == passed),
|
|
696
|
+
!.
|
|
697
|
+
passing_e2e_status(Obligations, missing) :-
|
|
698
|
+
member(Obligation, Obligations), Obligation.status == missing,
|
|
699
|
+
!.
|
|
700
|
+
passing_e2e_status(_Obligations, unresolved).
|
|
493
701
|
|
|
494
702
|
evidence_tests_with_state(Evidence, State, TestIds) :-
|
|
495
703
|
findall(TestId,
|
|
@@ -502,9 +710,9 @@ test_receipt_evidence(Context, TestId, Evidence) :-
|
|
|
502
710
|
test_scope(Props, Scope),
|
|
503
711
|
( Scope \= end_to_end
|
|
504
712
|
-> Evidence = _{testId: TestId, state: not_end_to_end, scope: Scope}
|
|
505
|
-
; Context.
|
|
713
|
+
; Context.proofSnapshot == unknown
|
|
506
714
|
-> Evidence = _{testId: TestId, state: snapshot_unavailable, scope: Scope}
|
|
507
|
-
;
|
|
715
|
+
; proof_receipt_entries(Props, Receipts, ReceiptPropertyPresent),
|
|
508
716
|
receipt_evidence_state(TestId, Scope, Props, Receipts, ReceiptPropertyPresent, Context, Evidence)
|
|
509
717
|
).
|
|
510
718
|
|
|
@@ -513,18 +721,18 @@ receipt_evidence_state(TestId, Scope, _Props, [], false, _Context,
|
|
|
513
721
|
receipt_evidence_state(TestId, Scope, Props, Receipts, _Present, _Context,
|
|
514
722
|
_{testId: TestId, state: invalid, scope: Scope, receiptCount: Count}) :-
|
|
515
723
|
length(Receipts, Count),
|
|
516
|
-
|
|
724
|
+
proof_contract_binding(Props, ContractBinding),
|
|
517
725
|
(ContractBinding == invalid
|
|
518
726
|
; Receipts == []
|
|
519
727
|
; member(Receipt, Receipts), \+ valid_receipt_shape(TestId, Receipt)
|
|
520
728
|
; \+ chronological_receipt_history(Receipts)),
|
|
521
729
|
!.
|
|
522
730
|
receipt_evidence_state(TestId, Scope, Props, Receipts, _Present, Context, Evidence) :-
|
|
523
|
-
|
|
731
|
+
receipt_for_current_mode(TestId, Context, Receipts, SnapshotReceipts),
|
|
524
732
|
( SnapshotReceipts == []
|
|
525
733
|
-> length(Receipts, Count),
|
|
526
734
|
Evidence = _{testId: TestId, state: stale, scope: Scope, receiptCount: Count}
|
|
527
|
-
;
|
|
735
|
+
; proof_contract_binding(Props, ContractBinding),
|
|
528
736
|
include(receipt_matches_current_binding(Scope, ContractBinding), SnapshotReceipts, CurrentReceipts),
|
|
529
737
|
( CurrentReceipts == []
|
|
530
738
|
-> receipt_contract_mismatch_evidence(TestId, Scope, SnapshotReceipts, ContractBinding, Evidence)
|
|
@@ -534,8 +742,8 @@ receipt_evidence_state(TestId, Scope, Props, Receipts, _Present, Context, Eviden
|
|
|
534
742
|
)
|
|
535
743
|
).
|
|
536
744
|
|
|
537
|
-
|
|
538
|
-
( memberchk(
|
|
745
|
+
proof_receipt_entries(Props, Receipts, Present) :-
|
|
746
|
+
( memberchk(proof_receipts=Raw, Props)
|
|
539
747
|
-> Present = true,
|
|
540
748
|
inventory_entries(Raw, Receipts)
|
|
541
749
|
; Present = false,
|
|
@@ -544,16 +752,11 @@ verification_receipt_entries(Props, Receipts, Present) :-
|
|
|
544
752
|
|
|
545
753
|
valid_receipt_shape(TestId, Receipt) :-
|
|
546
754
|
inventory_entry_field(Receipt, version, RawVersion),
|
|
547
|
-
normalize_receipt_atom(RawVersion,
|
|
548
|
-
memberchk(Version, ['kibi.verification-receipt.v1', 'kibi.verification-receipt.v2']),
|
|
755
|
+
normalize_receipt_atom(RawVersion, 'kibi.proof-receipt.v1'),
|
|
549
756
|
inventory_entry_field(Receipt, receipt_id, RawReceiptId),
|
|
550
757
|
normalize_receipt_atom(RawReceiptId, ReceiptId), valid_receipt_id(ReceiptId),
|
|
551
758
|
inventory_entry_field(Receipt, test_id, RawTestId),
|
|
552
759
|
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
760
|
inventory_entry_field(Receipt, scope, RawScope),
|
|
558
761
|
normalize_receipt_atom(RawScope, ReceiptScope),
|
|
559
762
|
memberchk(ReceiptScope, [unit, integration, end_to_end]),
|
|
@@ -566,55 +769,83 @@ valid_receipt_shape(TestId, Receipt) :-
|
|
|
566
769
|
normalize_receipt_atom(RawEnvironmentHash, EnvironmentHash), valid_sha256(EnvironmentHash),
|
|
567
770
|
inventory_entry_field(Receipt, artifact_digest, RawArtifactDigest),
|
|
568
771
|
normalize_receipt_atom(RawArtifactDigest, ArtifactDigest), valid_sha256(ArtifactDigest),
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
772
|
+
inventory_entry_field(Receipt, contract_hash, RawContractHash),
|
|
773
|
+
normalize_receipt_atom(RawContractHash, ContractHash), valid_sha256(ContractHash),
|
|
774
|
+
inventory_entry_field(Receipt, fingerprint, RawFingerprint),
|
|
775
|
+
normalize_receipt_atom(RawFingerprint, Fingerprint), valid_sha256(Fingerprint),
|
|
776
|
+
valid_fingerprint_components(Receipt),
|
|
777
|
+
inventory_entry_field(Receipt, integration_id, RawIntegrationId),
|
|
778
|
+
normalize_receipt_atom(RawIntegrationId, IntegrationId), IntegrationId \= '',
|
|
779
|
+
inventory_entry_field(Receipt, producer, RawProducer),
|
|
780
|
+
receipt_producer_name(RawProducer, ProducerName), ProducerName \= '',
|
|
576
781
|
inventory_entry_field(Receipt, command_argv, RawCommandArgv),
|
|
577
782
|
is_list(RawCommandArgv),
|
|
578
783
|
RawCommandArgv \= [],
|
|
579
784
|
maplist(nonempty_receipt_atom, RawCommandArgv),
|
|
580
|
-
inventory_entry_field(Receipt,
|
|
581
|
-
normalize_receipt_atom(
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
785
|
+
( inventory_entry_field(Receipt, binding_hash, RawBindingHash)
|
|
786
|
+
-> normalize_receipt_atom(RawBindingHash, BindingHash),
|
|
787
|
+
valid_sha256(BindingHash)
|
|
788
|
+
; true
|
|
789
|
+
),
|
|
790
|
+
inventory_entry_field(Receipt, run_outcome, RawRunOutcome),
|
|
791
|
+
normalize_receipt_atom(RawRunOutcome, RunOutcome),
|
|
792
|
+
memberchk(RunOutcome, [passed, failed, errored, cancelled, timed_out, interrupted, no_results]),
|
|
793
|
+
inventory_entry_field(Receipt, proof_results, RawResults),
|
|
794
|
+
is_list(RawResults),
|
|
795
|
+
maplist(valid_receipt_proof_result, RawResults),
|
|
796
|
+
receipt_timestamp(Receipt, started_at, StartedStamp),
|
|
797
|
+
receipt_timestamp(Receipt, finished_at, FinishedStamp),
|
|
798
|
+
FinishedStamp >= StartedStamp.
|
|
799
|
+
|
|
800
|
+
receipt_producer_name(Producer, Name) :-
|
|
801
|
+
inventory_entry_field(Producer, name, RawName),
|
|
802
|
+
normalize_receipt_atom(RawName, Name).
|
|
803
|
+
|
|
804
|
+
valid_fingerprint_components(Receipt) :-
|
|
805
|
+
inventory_entry_field(Receipt, fingerprint_components, Components),
|
|
806
|
+
forall(member(Key, [contract, integration, command, bindings, producer]),
|
|
807
|
+
(inventory_entry_field(Components, Key, Raw),
|
|
808
|
+
normalize_receipt_atom(Raw, Value),
|
|
809
|
+
valid_sha256(Value))),
|
|
810
|
+
!.
|
|
592
811
|
|
|
593
812
|
nonempty_receipt_atom(Value) :-
|
|
594
813
|
normalize_receipt_atom(Value, Atom),
|
|
595
814
|
Atom \= ''.
|
|
596
815
|
|
|
597
|
-
|
|
598
|
-
inventory_entry_field(
|
|
816
|
+
valid_receipt_proof_result(Result) :-
|
|
817
|
+
inventory_entry_field(Result, symbol_id, RawSymbolId),
|
|
599
818
|
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
|
-
|
|
819
|
+
inventory_entry_field(Result, target, RawTarget),
|
|
820
|
+
nonempty_receipt_atom(RawTarget),
|
|
821
|
+
inventory_entry_field(Result, outcome, RawOutcome),
|
|
822
|
+
normalize_receipt_atom(RawOutcome, ResultOutcome),
|
|
823
|
+
memberchk(ResultOutcome, [passed, failed, timed_out, skipped, interrupted, errored]),
|
|
824
|
+
inventory_entry_field(Result, binding, RawBinding),
|
|
825
|
+
normalize_receipt_atom(RawBinding, Binding),
|
|
826
|
+
memberchk(Binding, [native_case, aggregate_run]),
|
|
827
|
+
valid_receipt_attempts(Result).
|
|
828
|
+
|
|
829
|
+
valid_receipt_attempts(Result) :-
|
|
830
|
+
inventory_entry_field(Result, attempts, Attempts),
|
|
831
|
+
inventory_entry_field(Attempts, status, RawStatus),
|
|
832
|
+
normalize_receipt_atom(RawStatus, Status),
|
|
833
|
+
valid_attempt_status(Status, Attempts).
|
|
834
|
+
|
|
835
|
+
valid_attempt_status(unavailable, Attempts) :-
|
|
836
|
+
\+ inventory_entry_field(Attempts, entries, _Present), !.
|
|
837
|
+
valid_attempt_status(unavailable, Attempts) :-
|
|
838
|
+
inventory_entry_field(Attempts, entries, Entries),
|
|
839
|
+
Entries == [], !.
|
|
840
|
+
valid_attempt_status(complete, Attempts) :-
|
|
841
|
+
inventory_entry_field(Attempts, entries, Entries),
|
|
842
|
+
is_list(Entries),
|
|
843
|
+
Entries \= [],
|
|
844
|
+
forall(member(Entry, Entries),
|
|
845
|
+
(inventory_entry_field(Entry, outcome, RawEntryOutcome),
|
|
846
|
+
normalize_receipt_atom(RawEntryOutcome, EntryOutcome),
|
|
847
|
+
memberchk(EntryOutcome, [passed, failed, timed_out, skipped, interrupted, errored]))),
|
|
848
|
+
!.
|
|
618
849
|
|
|
619
850
|
valid_sha256(Value) :-
|
|
620
851
|
atom_length(Value, 64),
|
|
@@ -622,7 +853,7 @@ valid_sha256(Value) :-
|
|
|
622
853
|
forall(member(Char, Chars), memberchk(Char, ['0','1','2','3','4','5','6','7','8','9',a,b,c,d,e,f])).
|
|
623
854
|
|
|
624
855
|
valid_receipt_id(Value) :-
|
|
625
|
-
re_match('^
|
|
856
|
+
re_match('^PR-[A-Za-z0-9][A-Za-z0-9._:-]{7,127}$', Value).
|
|
626
857
|
|
|
627
858
|
receipt_timestamp(Receipt, Field, Stamp) :-
|
|
628
859
|
inventory_entry_field(Receipt, Field, RawValue),
|
|
@@ -647,6 +878,29 @@ receipt_for_snapshot(Snapshot, Receipt) :-
|
|
|
647
878
|
inventory_entry_field(Receipt, code_snapshot, RawSnapshot),
|
|
648
879
|
normalize_receipt_atom(RawSnapshot, Snapshot).
|
|
649
880
|
|
|
881
|
+
% Per-contract binding selector (W2). In per_contract mode a receipt carrying
|
|
882
|
+
% a binding_hash is current when it matches the test's current binding hash;
|
|
883
|
+
% receipts without one keep the strict snapshot semantics so older evidence
|
|
884
|
+
% stays valid until each contract is re-proven.
|
|
885
|
+
receipt_for_current_mode(TestId, Context, Receipts, SnapshotReceipts) :-
|
|
886
|
+
( Context.proofBindingMode == per_contract,
|
|
887
|
+
get_dict(TestId, Context.proofTestBindings, CurrentBinding)
|
|
888
|
+
-> include(receipt_with_binding_hash, Receipts, BoundReceipts),
|
|
889
|
+
include(receipt_for_binding(CurrentBinding), BoundReceipts, SnapshotReceipts),
|
|
890
|
+
( SnapshotReceipts == []
|
|
891
|
+
-> include(receipt_for_snapshot(Context.proofSnapshot), Receipts, SnapshotReceipts)
|
|
892
|
+
; true
|
|
893
|
+
)
|
|
894
|
+
; include(receipt_for_snapshot(Context.proofSnapshot), Receipts, SnapshotReceipts)
|
|
895
|
+
).
|
|
896
|
+
|
|
897
|
+
receipt_with_binding_hash(Receipt) :-
|
|
898
|
+
inventory_entry_field(Receipt, binding_hash, _).
|
|
899
|
+
|
|
900
|
+
receipt_for_binding(Binding, Receipt) :-
|
|
901
|
+
inventory_entry_field(Receipt, binding_hash, RawBinding),
|
|
902
|
+
normalize_receipt_atom(RawBinding, Binding).
|
|
903
|
+
|
|
650
904
|
receipt_matches_current_binding(Scope, ContractBinding, Receipt) :-
|
|
651
905
|
inventory_entry_field(Receipt, scope, RawScope),
|
|
652
906
|
normalize_receipt_atom(RawScope, Scope),
|
|
@@ -655,7 +909,7 @@ receipt_matches_current_binding(Scope, ContractBinding, Receipt) :-
|
|
|
655
909
|
receipt_matches_current_contract(none, _Receipt).
|
|
656
910
|
receipt_matches_current_contract(hash(ExpectedHash), Receipt) :-
|
|
657
911
|
inventory_entry_field(Receipt, version, RawVersion),
|
|
658
|
-
normalize_receipt_atom(RawVersion, 'kibi.
|
|
912
|
+
normalize_receipt_atom(RawVersion, 'kibi.proof-receipt.v1'),
|
|
659
913
|
inventory_entry_field(Receipt, contract_hash, RawContractHash),
|
|
660
914
|
normalize_receipt_atom(RawContractHash, ExpectedHash).
|
|
661
915
|
|
|
@@ -680,26 +934,26 @@ receipt_contract_mismatch_evidence(TestId, Scope, Receipts, ContractBinding, Evi
|
|
|
680
934
|
current_contract_hash_value(hash(Hash), Hash).
|
|
681
935
|
current_contract_hash_value(none, none).
|
|
682
936
|
|
|
683
|
-
|
|
684
|
-
( memberchk(
|
|
685
|
-
-> (
|
|
937
|
+
proof_contract_binding(Props, Binding) :-
|
|
938
|
+
( memberchk(proof_contract=RawContract, Props)
|
|
939
|
+
-> (proof_contract_hash(RawContract, Hash) -> Binding = hash(Hash) ; Binding = invalid)
|
|
686
940
|
; Binding = none
|
|
687
941
|
).
|
|
688
942
|
|
|
689
|
-
|
|
690
|
-
|
|
943
|
+
proof_contract_hash(RawContract, Hash) :-
|
|
944
|
+
proof_contract_dict(RawContract, Contract),
|
|
691
945
|
canonical_json_value(Contract, Canonical),
|
|
692
946
|
crypto_data_hash(Canonical, Hash, [algorithm(sha256), encoding(utf8)]).
|
|
693
947
|
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
948
|
+
proof_contract_dict(^^(Value, _), Contract) :- !,
|
|
949
|
+
proof_contract_dict(Value, Contract).
|
|
950
|
+
proof_contract_dict(literal(type(_, Value)), Contract) :- !,
|
|
951
|
+
proof_contract_dict(Value, Contract).
|
|
952
|
+
proof_contract_dict(literal(Value), Contract) :- !,
|
|
953
|
+
proof_contract_dict(Value, Contract).
|
|
954
|
+
proof_contract_dict(Contract, Contract) :-
|
|
701
955
|
is_dict(Contract), !.
|
|
702
|
-
|
|
956
|
+
proof_contract_dict(Raw, Contract) :-
|
|
703
957
|
(atom(Raw) ; string(Raw)),
|
|
704
958
|
catch(atom_json_dict(Raw, Contract, [value_string_as(string)]), _, fail),
|
|
705
959
|
is_dict(Contract).
|
|
@@ -731,23 +985,21 @@ latest_receipt(Receipts, Latest, FinishedStamp) :-
|
|
|
731
985
|
last(Sorted, FinishedStamp-Latest).
|
|
732
986
|
|
|
733
987
|
receipt_runtime_state(_Receipt, FinishedStamp, Context, invalid, AgeSeconds) :-
|
|
734
|
-
AgeSeconds is Context.
|
|
988
|
+
AgeSeconds is Context.proofCheckedAtStamp - FinishedStamp,
|
|
735
989
|
AgeSeconds < -300,
|
|
736
990
|
!.
|
|
737
991
|
receipt_runtime_state(_Receipt, FinishedStamp, Context, stale, AgeSeconds) :-
|
|
738
|
-
AgeSeconds is Context.
|
|
739
|
-
AgeSeconds > Context.
|
|
992
|
+
AgeSeconds is Context.proofCheckedAtStamp - FinishedStamp,
|
|
993
|
+
AgeSeconds > Context.proofMaxAgeSeconds,
|
|
740
994
|
!.
|
|
741
995
|
receipt_runtime_state(Receipt, FinishedStamp, Context, State, AgeSeconds) :-
|
|
742
|
-
AgeSeconds is Context.
|
|
996
|
+
AgeSeconds is Context.proofCheckedAtStamp - FinishedStamp,
|
|
743
997
|
inventory_entry_field(Receipt, outcome, RawOutcome),
|
|
744
998
|
normalize_receipt_atom(RawOutcome, Outcome),
|
|
745
999
|
(Outcome == passed -> State = passed ; State = failed).
|
|
746
1000
|
|
|
747
1001
|
receipt_evidence_dict(TestId, Scope, Receipt, State, AgeSeconds, Evidence) :-
|
|
748
1002
|
receipt_string_field(Receipt, receipt_id, ReceiptId),
|
|
749
|
-
receipt_string_field(Receipt, runner, Runner),
|
|
750
|
-
receipt_string_field(Receipt, command, Command),
|
|
751
1003
|
receipt_string_field(Receipt, outcome, Outcome),
|
|
752
1004
|
receipt_string_field(Receipt, code_snapshot, CodeSnapshot),
|
|
753
1005
|
receipt_string_field(Receipt, environment_hash, EnvironmentHash),
|
|
@@ -759,8 +1011,6 @@ receipt_evidence_dict(TestId, Scope, Receipt, State, AgeSeconds, Evidence) :-
|
|
|
759
1011
|
state: State,
|
|
760
1012
|
scope: Scope,
|
|
761
1013
|
receiptId: ReceiptId,
|
|
762
|
-
runner: Runner,
|
|
763
|
-
command: Command,
|
|
764
1014
|
outcome: Outcome,
|
|
765
1015
|
codeSnapshot: CodeSnapshot,
|
|
766
1016
|
environmentHash: EnvironmentHash,
|
|
@@ -828,15 +1078,19 @@ production_symbol_stage(ReqId, PassingE2eTests, Stage, ProductionSymbols) :-
|
|
|
828
1078
|
include(type_shape_symbol_with_structural_contract, ImplementingSymbols, StructuralSymbols),
|
|
829
1079
|
exclude(type_shape_symbol_with_structural_contract, ImplementingSymbols, ProductionSymbols),
|
|
830
1080
|
include(symbol_not_covered_by_tests(PassingE2eTests), ProductionSymbols, UncoveredSymbols),
|
|
831
|
-
production_stage_status(ProductionSymbols, StructuralSymbols, PassingE2eTests, UncoveredSymbols, Status),
|
|
1081
|
+
production_stage_status(ProductionSymbols, StructuralSymbols, PassingE2eTests, UncoveredSymbols, Status, StatusReason),
|
|
832
1082
|
maplist(symbol_coordinate_ref, ProductionSymbols, Coordinates),
|
|
833
|
-
|
|
1083
|
+
StageBase = _{
|
|
834
1084
|
status: Status,
|
|
835
1085
|
symbols: ProductionSymbols,
|
|
836
1086
|
structuralSymbols: StructuralSymbols,
|
|
837
1087
|
uncoveredSymbols: UncoveredSymbols,
|
|
838
1088
|
coordinates: Coordinates
|
|
839
|
-
}
|
|
1089
|
+
},
|
|
1090
|
+
( Status == passed
|
|
1091
|
+
-> Stage = StageBase
|
|
1092
|
+
; put_dict(reason, StageBase, StatusReason, Stage)
|
|
1093
|
+
).
|
|
840
1094
|
|
|
841
1095
|
type_shape_symbol_with_structural_contract(SymbolId) :-
|
|
842
1096
|
kb_entity(SymbolId, symbol, SymbolProps),
|
|
@@ -849,10 +1103,26 @@ type_shape_symbol_with_structural_contract(SymbolId) :-
|
|
|
849
1103
|
normalize_atom(RawStatus, Status),
|
|
850
1104
|
memberchk(Status, [active, passing]).
|
|
851
1105
|
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
1106
|
+
% Stage status plus a typed reason, from a single total clause so status and
|
|
1107
|
+
% reason can never disagree (the Align stale-receipts case reported `blocked`
|
|
1108
|
+
% with no indication that fresh receipts were the unblock). Clause order keeps
|
|
1109
|
+
% the historical precedence: no production symbols at all is `missing` even
|
|
1110
|
+
% when E2E evidence is also absent; only then does absent E2E evidence read
|
|
1111
|
+
% as `blocked`.
|
|
1112
|
+
production_stage_status(ProductionSymbols, StructuralSymbols, PassingE2eTests, UncoveredSymbols, Status, Reason) :-
|
|
1113
|
+
( ProductionSymbols == [],
|
|
1114
|
+
StructuralSymbols == []
|
|
1115
|
+
-> Status = missing,
|
|
1116
|
+
Reason = "no production symbols implement this requirement; link at least one with implements"
|
|
1117
|
+
; PassingE2eTests == []
|
|
1118
|
+
-> Status = blocked,
|
|
1119
|
+
Reason = "no passing E2E test evidence is available for the current snapshot; run kibi prove so this stage can evaluate production symbol coverage"
|
|
1120
|
+
; UncoveredSymbols == []
|
|
1121
|
+
-> Status = passed
|
|
1122
|
+
; Status = missing,
|
|
1123
|
+
length(UncoveredSymbols, Count),
|
|
1124
|
+
format(atom(Reason), "~w production symbol(s) lack covered_by links to the passing E2E tests", [Count])
|
|
1125
|
+
).
|
|
856
1126
|
|
|
857
1127
|
symbol_not_covered_by_tests(Tests, SymbolId) :-
|
|
858
1128
|
\+ (member(TestId, Tests), kb_relationship(covered_by, SymbolId, TestId)).
|
|
@@ -988,8 +1258,9 @@ proof_advisories(Stages, Advisories) :-
|
|
|
988
1258
|
proof_issues(Stages, _, Advisories).
|
|
989
1259
|
|
|
990
1260
|
% implements REQ-kibi-conservative-requirement-proof
|
|
991
|
-
%
|
|
992
|
-
%
|
|
1261
|
+
% Every linked scenario is a mandatory proof obligation. Receipt-completeness
|
|
1262
|
+
% failures therefore remain blocking even when another scenario has a passing
|
|
1263
|
+
% receipt; unit/integration-only evidence stays nonblocking in the E2E lane.
|
|
993
1264
|
% Invariant: proofStatus == proven implies proofGaps == [].
|
|
994
1265
|
proof_issues(Stages, Gaps, Advisories) :-
|
|
995
1266
|
findall(Gap,
|
|
@@ -1003,15 +1274,14 @@ proof_issues(Stages, Gaps, Advisories) :-
|
|
|
1003
1274
|
proof_issue_advisory(Gap, Stages)),
|
|
1004
1275
|
Advisories).
|
|
1005
1276
|
|
|
1006
|
-
receipt_completeness_issue(
|
|
1007
|
-
receipt_completeness_issue(
|
|
1008
|
-
receipt_completeness_issue(
|
|
1009
|
-
receipt_completeness_issue(
|
|
1010
|
-
receipt_completeness_issue(
|
|
1277
|
+
receipt_completeness_issue(missing_proof_receipt).
|
|
1278
|
+
receipt_completeness_issue(stale_proof_receipt).
|
|
1279
|
+
receipt_completeness_issue(failed_proof_receipt).
|
|
1280
|
+
receipt_completeness_issue(invalid_proof_receipt).
|
|
1281
|
+
receipt_completeness_issue(proof_contract_mismatch).
|
|
1011
1282
|
|
|
1012
|
-
proof_issue_advisory(
|
|
1013
|
-
|
|
1014
|
-
Stages.passingE2e.status == passed.
|
|
1283
|
+
proof_issue_advisory(_Gap, _Stages) :-
|
|
1284
|
+
fail.
|
|
1015
1285
|
|
|
1016
1286
|
proof_gap_present(missing_semantic_inventory, Stages) :- Stages.semanticInventory.propositionCount =:= 0.
|
|
1017
1287
|
proof_gap_present(incomplete_semantic_inventory, Stages) :- Stages.semanticInventory.missingCount > 0.
|
|
@@ -1028,12 +1298,12 @@ proof_gap_present(contradiction_check_incomplete, Stages) :- Stages.contradictio
|
|
|
1028
1298
|
proof_gap_present(missing_scenario, Stages) :- Stages.scenarios.status == missing.
|
|
1029
1299
|
proof_gap_present(missing_scenario_test, Stages) :- Stages.scenarios.status == passed, Stages.scenarioTests.status == missing.
|
|
1030
1300
|
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(
|
|
1301
|
+
proof_gap_present(missing_proof_receipt, Stages) :- Stages.passingE2e.missingReceiptTests \= [].
|
|
1302
|
+
proof_gap_present(stale_proof_receipt, Stages) :- Stages.passingE2e.staleReceiptTests \= [].
|
|
1303
|
+
proof_gap_present(failed_proof_receipt, Stages) :- Stages.passingE2e.failedReceiptTests \= [].
|
|
1304
|
+
proof_gap_present(invalid_proof_receipt, Stages) :- Stages.passingE2e.invalidReceiptTests \= [].
|
|
1305
|
+
proof_gap_present(proof_contract_mismatch, Stages) :- Stages.passingE2e.contractMismatchReceiptTests \= [].
|
|
1306
|
+
proof_gap_present(proof_snapshot_unavailable, Stages) :- Stages.passingE2e.snapshotUnavailableTests \= [].
|
|
1037
1307
|
proof_gap_present(missing_executable_test_symbol, Stages) :- Stages.executableSymbols.status == missing.
|
|
1038
1308
|
proof_gap_present(missing_production_symbol, Stages) :-
|
|
1039
1309
|
Stages.productionSymbols.symbols == [],
|
|
@@ -1055,12 +1325,12 @@ gap_definition(contradiction_check_incomplete, 41, contradictions, "Complete log
|
|
|
1055
1325
|
gap_definition(missing_scenario, 50, scenarios, "Add a specified_by scenario for the requirement.").
|
|
1056
1326
|
gap_definition(missing_scenario_test, 51, scenario_tests, "Link the scenario to a test with verified_by or validates.").
|
|
1057
1327
|
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(
|
|
1328
|
+
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.").
|
|
1329
|
+
gap_definition(stale_proof_receipt, 54, passing_e2e, "Re-run kibi prove against the current code snapshot and append fresh receipts.").
|
|
1330
|
+
gap_definition(failed_proof_receipt, 55, passing_e2e, "Repair the failing proof obligation and append a newer passing receipt for the same snapshot.").
|
|
1331
|
+
gap_definition(invalid_proof_receipt, 56, passing_e2e, "Repair malformed, future-dated, mismatched, or otherwise uncheckable receipt evidence.").
|
|
1332
|
+
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.").
|
|
1333
|
+
gap_definition(proof_snapshot_unavailable, 58, passing_e2e, "Run coverage through a CLI or MCP runtime that exposes the deterministic workspace snapshot.").
|
|
1064
1334
|
gap_definition(missing_executable_test_symbol, 60, executable_symbols, "Link executable test code to every qualifying E2E test with executable_for.").
|
|
1065
1335
|
gap_definition(missing_production_symbol, 70, production_symbols, "Link at least one production symbol to the requirement with implements.").
|
|
1066
1336
|
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.").
|