kibi-core 0.9.1 → 0.11.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 +6 -4
- package/schema/entities.pl +3 -0
- package/schema/relationships.pl +1 -0
- package/schema/validation.pl +1 -0
- package/src/checks.pl +123 -5
- package/src/discovery.pl +2 -1
- package/src/kb.pl +1087 -24
- package/src/logic_ir.pl +1 -1
- package/src/requirement_proof.pl +306 -49
- package/src/status.pl +161 -34
package/src/logic_ir.pl
CHANGED
|
@@ -530,7 +530,7 @@ rule_dependency_edge(
|
|
|
530
530
|
logic_predicate_signature(atom(Namespace, Name, Args, _Polarity, _ClosedWorld), predicate(Namespace, Name, Arity)) :-
|
|
531
531
|
length(Args, Arity).
|
|
532
532
|
|
|
533
|
-
expression_dependency(
|
|
533
|
+
expression_dependency(Atom, Atom, positive) :-
|
|
534
534
|
Atom = atom(_, _, _, _, _).
|
|
535
535
|
expression_dependency(not(Atom), Atom, negative).
|
|
536
536
|
expression_dependency(all(Items), Atom, Sign) :- member(Item, Items), expression_dependency(Item, Atom, Sign).
|
package/src/requirement_proof.pl
CHANGED
|
@@ -8,12 +8,12 @@
|
|
|
8
8
|
]).
|
|
9
9
|
|
|
10
10
|
:- use_module(library(http/json)).
|
|
11
|
+
:- use_module(library(crypto)).
|
|
11
12
|
:- use_module(library(date)).
|
|
12
13
|
:- use_module(library(pcre)).
|
|
13
14
|
:- use_module('kb.pl').
|
|
14
15
|
:- use_module('checks.pl', [
|
|
15
|
-
|
|
16
|
-
check_domain_contradiction_witnesses/1,
|
|
16
|
+
check_domain_contradictions_and_witnesses/2,
|
|
17
17
|
check_rule_safety/1,
|
|
18
18
|
check_rule_verifiability/1
|
|
19
19
|
]).
|
|
@@ -25,8 +25,7 @@ requirement_proof_context(Context) :-
|
|
|
25
25
|
requirement_proof_context(unknown, '1970-01-01T00:00:00Z', 604800, Context).
|
|
26
26
|
|
|
27
27
|
requirement_proof_context(VerificationSnapshot, CheckedAt, MaxAgeSeconds, Context) :-
|
|
28
|
-
|
|
29
|
-
check_domain_contradiction_witnesses(ContradictionWitnesses),
|
|
28
|
+
check_domain_contradictions_and_witnesses(Contradictions, ContradictionWitnesses),
|
|
30
29
|
check_rule_safety(UnsafeRules),
|
|
31
30
|
check_rule_verifiability(UnverifiableRules),
|
|
32
31
|
normalize_atom(VerificationSnapshot, Snapshot),
|
|
@@ -52,6 +51,7 @@ requirement_proof(ReqId, _ReqProps, _Context, Proof) :-
|
|
|
52
51
|
proofVersion: Version,
|
|
53
52
|
proofStatus: not_applicable,
|
|
54
53
|
proofGaps: [],
|
|
54
|
+
proofAdvisories: [],
|
|
55
55
|
proofRepairs: [],
|
|
56
56
|
proofStages: _{applicability: _{status: not_applicable}}
|
|
57
57
|
}.
|
|
@@ -76,7 +76,7 @@ requirement_proof(ReqId, ReqProps, Context, Proof) :-
|
|
|
76
76
|
productionSymbols: ProductionStage,
|
|
77
77
|
sourceCoordinates: CoordinateStage
|
|
78
78
|
},
|
|
79
|
-
|
|
79
|
+
proof_issues(Stages, Gaps, Advisories),
|
|
80
80
|
proof_repairs(Gaps, Repairs),
|
|
81
81
|
proof_status(Stages, Status),
|
|
82
82
|
proof_version(Version),
|
|
@@ -84,6 +84,7 @@ requirement_proof(ReqId, ReqProps, Context, Proof) :-
|
|
|
84
84
|
proofVersion: Version,
|
|
85
85
|
proofStatus: Status,
|
|
86
86
|
proofGaps: Gaps,
|
|
87
|
+
proofAdvisories: Advisories,
|
|
87
88
|
proofRepairs: Repairs,
|
|
88
89
|
proofStages: Stages
|
|
89
90
|
}.
|
|
@@ -224,6 +225,7 @@ logic_grounding_stage(ReqId, Props, Inventory, Context, Stage) :-
|
|
|
224
225
|
InvalidRuleFactIds,
|
|
225
226
|
Status
|
|
226
227
|
),
|
|
228
|
+
ground_source_refs(Evidence, GroundSources),
|
|
227
229
|
Stage = _{
|
|
228
230
|
status: Status,
|
|
229
231
|
manifestClaims: ManifestKeys,
|
|
@@ -238,7 +240,8 @@ logic_grounding_stage(ReqId, Props, Inventory, Context, Stage) :-
|
|
|
238
240
|
missingManifestClaims: MissingManifestKeys,
|
|
239
241
|
extraManifestClaims: ExtraManifestKeys,
|
|
240
242
|
undeclaredGroundClaims: UndeclaredGroundKeys,
|
|
241
|
-
invalidRuleFacts: InvalidRuleFactIds
|
|
243
|
+
invalidRuleFacts: InvalidRuleFactIds,
|
|
244
|
+
sources: GroundSources
|
|
242
245
|
}.
|
|
243
246
|
|
|
244
247
|
requirement_claim_keys(Props, true, Keys) :-
|
|
@@ -436,7 +439,8 @@ scenario_stage(ReqId, Stage, ScenarioIds) :-
|
|
|
436
439
|
ScenarioIds0),
|
|
437
440
|
sort(ScenarioIds0, ScenarioIds),
|
|
438
441
|
(ScenarioIds = [] -> Status = missing ; Status = passed),
|
|
439
|
-
|
|
442
|
+
maplist(entity_source_ref, ScenarioIds, Sources),
|
|
443
|
+
Stage = _{status: Status, scenarios: ScenarioIds, sources: Sources}.
|
|
440
444
|
|
|
441
445
|
scenario_test_stage(ScenarioIds, Stage, ScenarioTests) :-
|
|
442
446
|
findall(TestId,
|
|
@@ -444,7 +448,8 @@ scenario_test_stage(ScenarioIds, Stage, ScenarioTests) :-
|
|
|
444
448
|
ScenarioTests0),
|
|
445
449
|
sort(ScenarioTests0, ScenarioTests),
|
|
446
450
|
(ScenarioTests = [] -> Status = missing ; Status = passed),
|
|
447
|
-
|
|
451
|
+
maplist(entity_source_ref, ScenarioTests, Sources),
|
|
452
|
+
Stage = _{status: Status, tests: ScenarioTests, sources: Sources}.
|
|
448
453
|
|
|
449
454
|
scenario_test(ScenarioId, TestId) :-
|
|
450
455
|
kb_relationship(verified_by, ScenarioId, TestId),
|
|
@@ -460,9 +465,10 @@ passing_e2e_stage(ScenarioTests, Context, Stage, PassingE2eTests) :-
|
|
|
460
465
|
evidence_tests_with_state(Evidence, stale, StaleReceiptTests),
|
|
461
466
|
evidence_tests_with_state(Evidence, failed, FailedReceiptTests),
|
|
462
467
|
evidence_tests_with_state(Evidence, invalid, InvalidReceiptTests),
|
|
468
|
+
evidence_tests_with_state(Evidence, contract_mismatch, ContractMismatchReceiptTests),
|
|
463
469
|
evidence_tests_with_state(Evidence, snapshot_unavailable, SnapshotUnavailableTests),
|
|
464
470
|
evidence_tests_with_state(Evidence, not_end_to_end, NonEndToEndTests),
|
|
465
|
-
passing_e2e_status(PassingE2eTests, InvalidReceiptTests, SnapshotUnavailableTests, Status),
|
|
471
|
+
passing_e2e_status(PassingE2eTests, InvalidReceiptTests, ContractMismatchReceiptTests, SnapshotUnavailableTests, Status),
|
|
466
472
|
Stage = _{
|
|
467
473
|
status: Status,
|
|
468
474
|
tests: PassingE2eTests,
|
|
@@ -471,6 +477,7 @@ passing_e2e_stage(ScenarioTests, Context, Stage, PassingE2eTests) :-
|
|
|
471
477
|
staleReceiptTests: StaleReceiptTests,
|
|
472
478
|
failedReceiptTests: FailedReceiptTests,
|
|
473
479
|
invalidReceiptTests: InvalidReceiptTests,
|
|
480
|
+
contractMismatchReceiptTests: ContractMismatchReceiptTests,
|
|
474
481
|
snapshotUnavailableTests: SnapshotUnavailableTests,
|
|
475
482
|
nonEndToEndTests: NonEndToEndTests,
|
|
476
483
|
currentCodeSnapshot: Context.verificationSnapshot,
|
|
@@ -478,10 +485,11 @@ passing_e2e_stage(ScenarioTests, Context, Stage, PassingE2eTests) :-
|
|
|
478
485
|
maxAgeSeconds: Context.verificationMaxAgeSeconds
|
|
479
486
|
}.
|
|
480
487
|
|
|
481
|
-
passing_e2e_status(Passing, _, _, passed) :- Passing \= [], !.
|
|
482
|
-
passing_e2e_status([], Invalid, _, unresolved) :- Invalid \= [], !.
|
|
483
|
-
passing_e2e_status([], _,
|
|
484
|
-
passing_e2e_status([], _, _,
|
|
488
|
+
passing_e2e_status(Passing, _, _, _, passed) :- Passing \= [], !.
|
|
489
|
+
passing_e2e_status([], Invalid, _, _, unresolved) :- Invalid \= [], !.
|
|
490
|
+
passing_e2e_status([], _, ContractMismatch, _, unresolved) :- ContractMismatch \= [], !.
|
|
491
|
+
passing_e2e_status([], _, _, SnapshotUnavailable, unresolved) :- SnapshotUnavailable \= [], !.
|
|
492
|
+
passing_e2e_status([], _, _, _, missing).
|
|
485
493
|
|
|
486
494
|
evidence_tests_with_state(Evidence, State, TestIds) :-
|
|
487
495
|
findall(TestId,
|
|
@@ -497,26 +505,33 @@ test_receipt_evidence(Context, TestId, Evidence) :-
|
|
|
497
505
|
; Context.verificationSnapshot == unknown
|
|
498
506
|
-> Evidence = _{testId: TestId, state: snapshot_unavailable, scope: Scope}
|
|
499
507
|
; verification_receipt_entries(Props, Receipts, ReceiptPropertyPresent),
|
|
500
|
-
receipt_evidence_state(TestId, Scope, Receipts, ReceiptPropertyPresent, Context, Evidence)
|
|
508
|
+
receipt_evidence_state(TestId, Scope, Props, Receipts, ReceiptPropertyPresent, Context, Evidence)
|
|
501
509
|
).
|
|
502
510
|
|
|
503
|
-
receipt_evidence_state(TestId, Scope, [], false, _Context,
|
|
511
|
+
receipt_evidence_state(TestId, Scope, _Props, [], false, _Context,
|
|
504
512
|
_{testId: TestId, state: missing, scope: Scope, receiptCount: 0}) :- !.
|
|
505
|
-
receipt_evidence_state(TestId, Scope, Receipts, _Present, _Context,
|
|
513
|
+
receipt_evidence_state(TestId, Scope, Props, Receipts, _Present, _Context,
|
|
506
514
|
_{testId: TestId, state: invalid, scope: Scope, receiptCount: Count}) :-
|
|
507
515
|
length(Receipts, Count),
|
|
508
|
-
(
|
|
509
|
-
|
|
516
|
+
verification_contract_binding(Props, ContractBinding),
|
|
517
|
+
(ContractBinding == invalid
|
|
518
|
+
; Receipts == []
|
|
519
|
+
; member(Receipt, Receipts), \+ valid_receipt_shape(TestId, Receipt)
|
|
510
520
|
; \+ chronological_receipt_history(Receipts)),
|
|
511
521
|
!.
|
|
512
|
-
receipt_evidence_state(TestId, Scope, Receipts, _Present, Context, Evidence) :-
|
|
513
|
-
include(receipt_for_snapshot(Context.verificationSnapshot), Receipts,
|
|
514
|
-
(
|
|
522
|
+
receipt_evidence_state(TestId, Scope, Props, Receipts, _Present, Context, Evidence) :-
|
|
523
|
+
include(receipt_for_snapshot(Context.verificationSnapshot), Receipts, SnapshotReceipts),
|
|
524
|
+
( SnapshotReceipts == []
|
|
515
525
|
-> length(Receipts, Count),
|
|
516
526
|
Evidence = _{testId: TestId, state: stale, scope: Scope, receiptCount: Count}
|
|
517
|
-
;
|
|
518
|
-
|
|
519
|
-
|
|
527
|
+
; verification_contract_binding(Props, ContractBinding),
|
|
528
|
+
include(receipt_matches_current_binding(Scope, ContractBinding), SnapshotReceipts, CurrentReceipts),
|
|
529
|
+
( CurrentReceipts == []
|
|
530
|
+
-> receipt_contract_mismatch_evidence(TestId, Scope, SnapshotReceipts, ContractBinding, Evidence)
|
|
531
|
+
; latest_receipt(CurrentReceipts, Latest, FinishedStamp),
|
|
532
|
+
receipt_runtime_state(Latest, FinishedStamp, Context, State, AgeSeconds),
|
|
533
|
+
receipt_evidence_dict(TestId, Scope, Latest, State, AgeSeconds, Evidence)
|
|
534
|
+
)
|
|
520
535
|
).
|
|
521
536
|
|
|
522
537
|
verification_receipt_entries(Props, Receipts, Present) :-
|
|
@@ -527,9 +542,10 @@ verification_receipt_entries(Props, Receipts, Present) :-
|
|
|
527
542
|
Receipts = []
|
|
528
543
|
).
|
|
529
544
|
|
|
530
|
-
valid_receipt_shape(TestId,
|
|
545
|
+
valid_receipt_shape(TestId, Receipt) :-
|
|
531
546
|
inventory_entry_field(Receipt, version, RawVersion),
|
|
532
|
-
normalize_receipt_atom(RawVersion,
|
|
547
|
+
normalize_receipt_atom(RawVersion, Version),
|
|
548
|
+
memberchk(Version, ['kibi.verification-receipt.v1', 'kibi.verification-receipt.v2']),
|
|
533
549
|
inventory_entry_field(Receipt, receipt_id, RawReceiptId),
|
|
534
550
|
normalize_receipt_atom(RawReceiptId, ReceiptId), valid_receipt_id(ReceiptId),
|
|
535
551
|
inventory_entry_field(Receipt, test_id, RawTestId),
|
|
@@ -539,10 +555,11 @@ valid_receipt_shape(TestId, Scope, Receipt) :-
|
|
|
539
555
|
inventory_entry_field(Receipt, command, RawCommand),
|
|
540
556
|
normalize_receipt_atom(RawCommand, Command), Command \= '',
|
|
541
557
|
inventory_entry_field(Receipt, scope, RawScope),
|
|
542
|
-
normalize_receipt_atom(RawScope,
|
|
558
|
+
normalize_receipt_atom(RawScope, ReceiptScope),
|
|
559
|
+
memberchk(ReceiptScope, [unit, integration, end_to_end]),
|
|
543
560
|
inventory_entry_field(Receipt, outcome, RawOutcome),
|
|
544
561
|
normalize_receipt_atom(RawOutcome, Outcome),
|
|
545
|
-
memberchk(Outcome, [passed, failed, errored, cancelled, skipped]),
|
|
562
|
+
memberchk(Outcome, [passed, failed, errored, cancelled, skipped, timed_out, interrupted]),
|
|
546
563
|
inventory_entry_field(Receipt, code_snapshot, RawSnapshot),
|
|
547
564
|
normalize_receipt_atom(RawSnapshot, Snapshot), valid_sha256(Snapshot),
|
|
548
565
|
inventory_entry_field(Receipt, environment_hash, RawEnvironmentHash),
|
|
@@ -551,7 +568,53 @@ valid_receipt_shape(TestId, Scope, Receipt) :-
|
|
|
551
568
|
normalize_receipt_atom(RawArtifactDigest, ArtifactDigest), valid_sha256(ArtifactDigest),
|
|
552
569
|
receipt_timestamp(Receipt, started_at, StartedStamp),
|
|
553
570
|
receipt_timestamp(Receipt, finished_at, FinishedStamp),
|
|
554
|
-
FinishedStamp >= StartedStamp
|
|
571
|
+
FinishedStamp >= StartedStamp,
|
|
572
|
+
valid_receipt_version_fields(Version, Receipt).
|
|
573
|
+
|
|
574
|
+
valid_receipt_version_fields('kibi.verification-receipt.v1', _Receipt).
|
|
575
|
+
valid_receipt_version_fields('kibi.verification-receipt.v2', Receipt) :-
|
|
576
|
+
inventory_entry_field(Receipt, command_argv, RawCommandArgv),
|
|
577
|
+
is_list(RawCommandArgv),
|
|
578
|
+
RawCommandArgv \= [],
|
|
579
|
+
maplist(nonempty_receipt_atom, RawCommandArgv),
|
|
580
|
+
inventory_entry_field(Receipt, contract_hash, RawContractHash),
|
|
581
|
+
normalize_receipt_atom(RawContractHash, ContractHash),
|
|
582
|
+
valid_sha256(ContractHash),
|
|
583
|
+
inventory_entry_field(Receipt, case_results, RawCases),
|
|
584
|
+
is_list(RawCases),
|
|
585
|
+
RawCases \= [],
|
|
586
|
+
maplist(valid_receipt_case, RawCases),
|
|
587
|
+
findall(Key,
|
|
588
|
+
(member(Case, RawCases), receipt_case_key(Case, Key)), Keys),
|
|
589
|
+
sort(Keys, UniqueKeys),
|
|
590
|
+
length(Keys, KeyCount),
|
|
591
|
+
length(UniqueKeys, KeyCount).
|
|
592
|
+
|
|
593
|
+
nonempty_receipt_atom(Value) :-
|
|
594
|
+
normalize_receipt_atom(Value, Atom),
|
|
595
|
+
Atom \= ''.
|
|
596
|
+
|
|
597
|
+
valid_receipt_case(Case) :-
|
|
598
|
+
inventory_entry_field(Case, symbol_id, RawSymbolId),
|
|
599
|
+
nonempty_receipt_atom(RawSymbolId),
|
|
600
|
+
inventory_entry_field(Case, project, RawProject),
|
|
601
|
+
nonempty_receipt_atom(RawProject),
|
|
602
|
+
inventory_entry_field(Case, outcome, RawOutcome),
|
|
603
|
+
normalize_receipt_atom(RawOutcome, Outcome),
|
|
604
|
+
memberchk(Outcome, [passed, failed, timed_out, skipped, interrupted]),
|
|
605
|
+
inventory_entry_field(Case, retries, Retries),
|
|
606
|
+
normalize_integer(Retries, RetryCount),
|
|
607
|
+
RetryCount >= 0,
|
|
608
|
+
inventory_entry_field(Case, duration_ms, Duration),
|
|
609
|
+
normalize_integer(Duration, DurationMs),
|
|
610
|
+
DurationMs >= 0.
|
|
611
|
+
|
|
612
|
+
receipt_case_key(Case, Key) :-
|
|
613
|
+
inventory_entry_field(Case, project, Project),
|
|
614
|
+
inventory_entry_field(Case, symbol_id, SymbolId),
|
|
615
|
+
normalize_receipt_atom(Project, ProjectAtom),
|
|
616
|
+
normalize_receipt_atom(SymbolId, SymbolAtom),
|
|
617
|
+
atomic_list_concat([ProjectAtom, SymbolAtom], '/', Key).
|
|
555
618
|
|
|
556
619
|
valid_sha256(Value) :-
|
|
557
620
|
atom_length(Value, 64),
|
|
@@ -584,6 +647,82 @@ receipt_for_snapshot(Snapshot, Receipt) :-
|
|
|
584
647
|
inventory_entry_field(Receipt, code_snapshot, RawSnapshot),
|
|
585
648
|
normalize_receipt_atom(RawSnapshot, Snapshot).
|
|
586
649
|
|
|
650
|
+
receipt_matches_current_binding(Scope, ContractBinding, Receipt) :-
|
|
651
|
+
inventory_entry_field(Receipt, scope, RawScope),
|
|
652
|
+
normalize_receipt_atom(RawScope, Scope),
|
|
653
|
+
receipt_matches_current_contract(ContractBinding, Receipt).
|
|
654
|
+
|
|
655
|
+
receipt_matches_current_contract(none, _Receipt).
|
|
656
|
+
receipt_matches_current_contract(hash(ExpectedHash), Receipt) :-
|
|
657
|
+
inventory_entry_field(Receipt, version, RawVersion),
|
|
658
|
+
normalize_receipt_atom(RawVersion, 'kibi.verification-receipt.v2'),
|
|
659
|
+
inventory_entry_field(Receipt, contract_hash, RawContractHash),
|
|
660
|
+
normalize_receipt_atom(RawContractHash, ExpectedHash).
|
|
661
|
+
|
|
662
|
+
receipt_contract_mismatch_evidence(TestId, Scope, Receipts, ContractBinding, Evidence) :-
|
|
663
|
+
length(Receipts, Count),
|
|
664
|
+
findall(Hash,
|
|
665
|
+
(member(Receipt, Receipts),
|
|
666
|
+
inventory_entry_field(Receipt, contract_hash, RawHash),
|
|
667
|
+
normalize_receipt_atom(RawHash, Hash)),
|
|
668
|
+
Hashes0),
|
|
669
|
+
sort(Hashes0, Hashes),
|
|
670
|
+
current_contract_hash_value(ContractBinding, CurrentContractHash),
|
|
671
|
+
Evidence = _{
|
|
672
|
+
testId: TestId,
|
|
673
|
+
state: contract_mismatch,
|
|
674
|
+
scope: Scope,
|
|
675
|
+
receiptCount: Count,
|
|
676
|
+
currentContractHash: CurrentContractHash,
|
|
677
|
+
receiptContractHashes: Hashes
|
|
678
|
+
}.
|
|
679
|
+
|
|
680
|
+
current_contract_hash_value(hash(Hash), Hash).
|
|
681
|
+
current_contract_hash_value(none, none).
|
|
682
|
+
|
|
683
|
+
verification_contract_binding(Props, Binding) :-
|
|
684
|
+
( memberchk(verification_contract=RawContract, Props)
|
|
685
|
+
-> (verification_contract_hash(RawContract, Hash) -> Binding = hash(Hash) ; Binding = invalid)
|
|
686
|
+
; Binding = none
|
|
687
|
+
).
|
|
688
|
+
|
|
689
|
+
verification_contract_hash(RawContract, Hash) :-
|
|
690
|
+
verification_contract_dict(RawContract, Contract),
|
|
691
|
+
canonical_json_value(Contract, Canonical),
|
|
692
|
+
crypto_data_hash(Canonical, Hash, [algorithm(sha256), encoding(utf8)]).
|
|
693
|
+
|
|
694
|
+
verification_contract_dict(^^(Value, _), Contract) :- !,
|
|
695
|
+
verification_contract_dict(Value, Contract).
|
|
696
|
+
verification_contract_dict(literal(type(_, Value)), Contract) :- !,
|
|
697
|
+
verification_contract_dict(Value, Contract).
|
|
698
|
+
verification_contract_dict(literal(Value), Contract) :- !,
|
|
699
|
+
verification_contract_dict(Value, Contract).
|
|
700
|
+
verification_contract_dict(Contract, Contract) :-
|
|
701
|
+
is_dict(Contract), !.
|
|
702
|
+
verification_contract_dict(Raw, Contract) :-
|
|
703
|
+
(atom(Raw) ; string(Raw)),
|
|
704
|
+
catch(atom_json_dict(Raw, Contract, [value_string_as(string)]), _, fail),
|
|
705
|
+
is_dict(Contract).
|
|
706
|
+
|
|
707
|
+
canonical_json_value(Value, Json) :-
|
|
708
|
+
is_dict(Value), !,
|
|
709
|
+
dict_pairs(Value, _, Pairs),
|
|
710
|
+
maplist(canonical_json_pair, Pairs, Parts),
|
|
711
|
+
atomics_to_string(Parts, ",", Body),
|
|
712
|
+
format(string(Json), "{~s}", [Body]).
|
|
713
|
+
canonical_json_value(Value, Json) :-
|
|
714
|
+
is_list(Value), !,
|
|
715
|
+
maplist(canonical_json_value, Value, Parts),
|
|
716
|
+
atomics_to_string(Parts, ",", Body),
|
|
717
|
+
format(string(Json), "[~s]", [Body]).
|
|
718
|
+
canonical_json_value(Value, Json) :-
|
|
719
|
+
with_output_to(string(Json), json_write(current_output, Value, [width(0)])).
|
|
720
|
+
|
|
721
|
+
canonical_json_pair(Key-Value, Json) :-
|
|
722
|
+
with_output_to(string(KeyJson), json_write(current_output, Key, [width(0)])),
|
|
723
|
+
canonical_json_value(Value, ValueJson),
|
|
724
|
+
format(string(Json), "~s:~s", [KeyJson, ValueJson]).
|
|
725
|
+
|
|
587
726
|
latest_receipt(Receipts, Latest, FinishedStamp) :-
|
|
588
727
|
findall(Stamp-Receipt,
|
|
589
728
|
(member(Receipt, Receipts), receipt_timestamp(Receipt, finished_at, Stamp)),
|
|
@@ -662,7 +801,7 @@ test_scope(Props, end_to_end) :-
|
|
|
662
801
|
!.
|
|
663
802
|
test_scope(_, unknown).
|
|
664
803
|
|
|
665
|
-
executable_symbol_stage([], _{status: blocked, symbols: [], missingTests: []}, []).
|
|
804
|
+
executable_symbol_stage([], _{status: blocked, symbols: [], missingTests: [], coordinates: []}, []).
|
|
666
805
|
executable_symbol_stage(PassingE2eTests, Stage, Symbols) :-
|
|
667
806
|
PassingE2eTests \= [],
|
|
668
807
|
findall(SymbolId,
|
|
@@ -673,7 +812,8 @@ executable_symbol_stage(PassingE2eTests, Stage, Symbols) :-
|
|
|
673
812
|
sort(Symbols0, Symbols),
|
|
674
813
|
include(test_missing_executable_symbol, PassingE2eTests, MissingTests),
|
|
675
814
|
(MissingTests = [] -> Status = passed ; Status = missing),
|
|
676
|
-
|
|
815
|
+
maplist(symbol_coordinate_ref, Symbols, Coordinates),
|
|
816
|
+
Stage = _{status: Status, symbols: Symbols, missingTests: MissingTests, coordinates: Coordinates}.
|
|
677
817
|
|
|
678
818
|
test_missing_executable_symbol(TestId) :-
|
|
679
819
|
\+ (kb_relationship(executable_for, SymbolId, TestId), kb_entity(SymbolId, symbol, _)).
|
|
@@ -683,16 +823,36 @@ production_symbol_stage(ReqId, PassingE2eTests, Stage, ProductionSymbols) :-
|
|
|
683
823
|
(kb_relationship(implements, SymbolId, ReqId),
|
|
684
824
|
kb_entity(SymbolId, symbol, _),
|
|
685
825
|
\+ kb:executable_test_symbol(SymbolId)),
|
|
686
|
-
|
|
687
|
-
sort(
|
|
826
|
+
Implementing0),
|
|
827
|
+
sort(Implementing0, ImplementingSymbols),
|
|
828
|
+
include(type_shape_symbol_with_structural_contract, ImplementingSymbols, StructuralSymbols),
|
|
829
|
+
exclude(type_shape_symbol_with_structural_contract, ImplementingSymbols, ProductionSymbols),
|
|
688
830
|
include(symbol_not_covered_by_tests(PassingE2eTests), ProductionSymbols, UncoveredSymbols),
|
|
689
|
-
production_stage_status(ProductionSymbols, PassingE2eTests, UncoveredSymbols, Status),
|
|
690
|
-
|
|
831
|
+
production_stage_status(ProductionSymbols, StructuralSymbols, PassingE2eTests, UncoveredSymbols, Status),
|
|
832
|
+
maplist(symbol_coordinate_ref, ProductionSymbols, Coordinates),
|
|
833
|
+
Stage = _{
|
|
834
|
+
status: Status,
|
|
835
|
+
symbols: ProductionSymbols,
|
|
836
|
+
structuralSymbols: StructuralSymbols,
|
|
837
|
+
uncoveredSymbols: UncoveredSymbols,
|
|
838
|
+
coordinates: Coordinates
|
|
839
|
+
}.
|
|
691
840
|
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
841
|
+
type_shape_symbol_with_structural_contract(SymbolId) :-
|
|
842
|
+
kb_entity(SymbolId, symbol, SymbolProps),
|
|
843
|
+
memberchk(symbol_role=RawRole, SymbolProps),
|
|
844
|
+
normalize_atom(RawRole, 'type-shape'),
|
|
845
|
+
kb_relationship(covered_by, SymbolId, TestId),
|
|
846
|
+
kb_entity(TestId, test, TestProps),
|
|
847
|
+
test_scope(TestProps, unit),
|
|
848
|
+
memberchk(status=RawStatus, TestProps),
|
|
849
|
+
normalize_atom(RawStatus, Status),
|
|
850
|
+
memberchk(Status, [active, passing]).
|
|
851
|
+
|
|
852
|
+
production_stage_status([], [], _, _, missing) :- !.
|
|
853
|
+
production_stage_status(_, _, [], _, blocked) :- !.
|
|
854
|
+
production_stage_status(_, _, _, [], passed) :- !.
|
|
855
|
+
production_stage_status(_, _, _, _, missing).
|
|
696
856
|
|
|
697
857
|
symbol_not_covered_by_tests(Tests, SymbolId) :-
|
|
698
858
|
\+ (member(TestId, Tests), kb_relationship(covered_by, SymbolId, TestId)).
|
|
@@ -707,11 +867,15 @@ source_coordinate_stage(ReqProps, ExecutableSymbols, ProductionSymbols, Stage) :
|
|
|
707
867
|
; RequirementSource = present,
|
|
708
868
|
coordinate_stage_status(Symbols, MissingSymbols, Status)
|
|
709
869
|
),
|
|
870
|
+
requirement_source_path(ReqProps, RequirementPath),
|
|
871
|
+
maplist(symbol_coordinate_ref, Symbols, Coordinates),
|
|
710
872
|
Stage = _{
|
|
711
873
|
status: Status,
|
|
712
874
|
requirementSource: RequirementSource,
|
|
875
|
+
requirementPath: RequirementPath,
|
|
713
876
|
symbols: Symbols,
|
|
714
|
-
missingSymbols: MissingSymbols
|
|
877
|
+
missingSymbols: MissingSymbols,
|
|
878
|
+
coordinates: Coordinates
|
|
715
879
|
}.
|
|
716
880
|
|
|
717
881
|
coordinate_stage_status([], _, blocked) :- !.
|
|
@@ -724,7 +888,7 @@ symbol_missing_coordinates(SymbolId) :-
|
|
|
724
888
|
|
|
725
889
|
valid_symbol_coordinates(Props) :-
|
|
726
890
|
memberchk(sourceFile=RawSourceFile, Props),
|
|
727
|
-
|
|
891
|
+
source_path_atom(RawSourceFile, SourceFile),
|
|
728
892
|
SourceFile \= '',
|
|
729
893
|
memberchk(sourceLine=RawLine, Props),
|
|
730
894
|
memberchk(sourceColumn=RawColumn, Props),
|
|
@@ -739,10 +903,66 @@ valid_symbol_coordinates(Props) :-
|
|
|
739
903
|
EndLine >= Line,
|
|
740
904
|
EndColumn >= 0.
|
|
741
905
|
|
|
742
|
-
|
|
906
|
+
% Prefer sourceFile, then source, without URI-basename stripping. Paths must
|
|
907
|
+
% remain inspectable coordinates for report linking.
|
|
908
|
+
proof_source_property(Props, RawSource) :-
|
|
909
|
+
memberchk(sourceFile=RawSource, Props),
|
|
910
|
+
source_path_atom(RawSource, _),
|
|
911
|
+
!.
|
|
912
|
+
proof_source_property(Props, RawSource) :-
|
|
743
913
|
memberchk(source=RawSource, Props),
|
|
744
|
-
|
|
745
|
-
|
|
914
|
+
source_path_atom(RawSource, _).
|
|
915
|
+
|
|
916
|
+
nonempty_source(Props) :-
|
|
917
|
+
proof_source_property(Props, _).
|
|
918
|
+
|
|
919
|
+
requirement_source_path(Props, Path) :-
|
|
920
|
+
proof_source_property(Props, RawSource),
|
|
921
|
+
source_path_atom(RawSource, Path),
|
|
922
|
+
!.
|
|
923
|
+
requirement_source_path(_, '').
|
|
924
|
+
|
|
925
|
+
entity_source_path(Id, Path) :-
|
|
926
|
+
kb_entity(Id, _, Props),
|
|
927
|
+
requirement_source_path(Props, Path).
|
|
928
|
+
|
|
929
|
+
entity_source_ref(Id, _{id: Id, path: Path}) :-
|
|
930
|
+
entity_source_path(Id, Path).
|
|
931
|
+
|
|
932
|
+
ground_source_refs(Evidence, Refs) :-
|
|
933
|
+
findall(Ref,
|
|
934
|
+
(member(Item, Evidence),
|
|
935
|
+
entity_source_ref(Item.factId, Ref),
|
|
936
|
+
Ref.path \= ''),
|
|
937
|
+
Refs0),
|
|
938
|
+
sort(Refs0, Refs).
|
|
939
|
+
|
|
940
|
+
symbol_coordinate_ref(SymbolId, Ref) :-
|
|
941
|
+
kb_entity(SymbolId, symbol, Props),
|
|
942
|
+
memberchk(sourceFile=RawFile, Props),
|
|
943
|
+
source_path_atom(RawFile, File),
|
|
944
|
+
File \= '',
|
|
945
|
+
!,
|
|
946
|
+
( valid_symbol_coordinates(Props)
|
|
947
|
+
-> memberchk(sourceLine=RawLine, Props),
|
|
948
|
+
memberchk(sourceColumn=RawColumn, Props),
|
|
949
|
+
memberchk(sourceEndLine=RawEndLine, Props),
|
|
950
|
+
memberchk(sourceEndColumn=RawEndColumn, Props),
|
|
951
|
+
normalize_integer(RawLine, Line),
|
|
952
|
+
normalize_integer(RawColumn, Column),
|
|
953
|
+
normalize_integer(RawEndLine, EndLine),
|
|
954
|
+
normalize_integer(RawEndColumn, EndColumn),
|
|
955
|
+
Ref = _{
|
|
956
|
+
id: SymbolId,
|
|
957
|
+
path: File,
|
|
958
|
+
line: Line,
|
|
959
|
+
column: Column,
|
|
960
|
+
endLine: EndLine,
|
|
961
|
+
endColumn: EndColumn
|
|
962
|
+
}
|
|
963
|
+
; Ref = _{id: SymbolId, path: File}
|
|
964
|
+
).
|
|
965
|
+
symbol_coordinate_ref(SymbolId, _{id: SymbolId, path: ''}).
|
|
746
966
|
|
|
747
967
|
proof_status(Stages, unresolved) :-
|
|
748
968
|
Stages.contradictions.status == blocked,
|
|
@@ -762,7 +982,36 @@ stage_dict(Stages, Stage) :-
|
|
|
762
982
|
member(_-Stage, Pairs).
|
|
763
983
|
|
|
764
984
|
proof_gaps(Stages, Gaps) :-
|
|
765
|
-
|
|
985
|
+
proof_issues(Stages, Gaps, _).
|
|
986
|
+
|
|
987
|
+
proof_advisories(Stages, Advisories) :-
|
|
988
|
+
proof_issues(Stages, _, Advisories).
|
|
989
|
+
|
|
990
|
+
% implements REQ-kibi-conservative-requirement-proof
|
|
991
|
+
% proofGaps are blocking only. Receipt-completeness codes become
|
|
992
|
+
% proofAdvisories when passingE2e already supplies strict proof.
|
|
993
|
+
% Invariant: proofStatus == proven implies proofGaps == [].
|
|
994
|
+
proof_issues(Stages, Gaps, Advisories) :-
|
|
995
|
+
findall(Gap,
|
|
996
|
+
(gap_definition(Gap, _, _, _),
|
|
997
|
+
proof_gap_present(Gap, Stages),
|
|
998
|
+
\+ proof_issue_advisory(Gap, Stages)),
|
|
999
|
+
Gaps),
|
|
1000
|
+
findall(Gap,
|
|
1001
|
+
(gap_definition(Gap, _, _, _),
|
|
1002
|
+
proof_gap_present(Gap, Stages),
|
|
1003
|
+
proof_issue_advisory(Gap, Stages)),
|
|
1004
|
+
Advisories).
|
|
1005
|
+
|
|
1006
|
+
receipt_completeness_issue(missing_verification_receipt).
|
|
1007
|
+
receipt_completeness_issue(stale_verification_receipt).
|
|
1008
|
+
receipt_completeness_issue(failed_verification_receipt).
|
|
1009
|
+
receipt_completeness_issue(invalid_verification_receipt).
|
|
1010
|
+
receipt_completeness_issue(verification_contract_mismatch).
|
|
1011
|
+
|
|
1012
|
+
proof_issue_advisory(Gap, Stages) :-
|
|
1013
|
+
receipt_completeness_issue(Gap),
|
|
1014
|
+
Stages.passingE2e.status == passed.
|
|
766
1015
|
|
|
767
1016
|
proof_gap_present(missing_semantic_inventory, Stages) :- Stages.semanticInventory.propositionCount =:= 0.
|
|
768
1017
|
proof_gap_present(incomplete_semantic_inventory, Stages) :- Stages.semanticInventory.missingCount > 0.
|
|
@@ -783,9 +1032,12 @@ proof_gap_present(missing_verification_receipt, Stages) :- Stages.passingE2e.mis
|
|
|
783
1032
|
proof_gap_present(stale_verification_receipt, Stages) :- Stages.passingE2e.staleReceiptTests \= [].
|
|
784
1033
|
proof_gap_present(failed_verification_receipt, Stages) :- Stages.passingE2e.failedReceiptTests \= [].
|
|
785
1034
|
proof_gap_present(invalid_verification_receipt, Stages) :- Stages.passingE2e.invalidReceiptTests \= [].
|
|
1035
|
+
proof_gap_present(verification_contract_mismatch, Stages) :- Stages.passingE2e.contractMismatchReceiptTests \= [].
|
|
786
1036
|
proof_gap_present(verification_snapshot_unavailable, Stages) :- Stages.passingE2e.snapshotUnavailableTests \= [].
|
|
787
1037
|
proof_gap_present(missing_executable_test_symbol, Stages) :- Stages.executableSymbols.status == missing.
|
|
788
|
-
proof_gap_present(missing_production_symbol, Stages) :-
|
|
1038
|
+
proof_gap_present(missing_production_symbol, Stages) :-
|
|
1039
|
+
Stages.productionSymbols.symbols == [],
|
|
1040
|
+
Stages.productionSymbols.structuralSymbols == [].
|
|
789
1041
|
proof_gap_present(missing_production_symbol_coverage, Stages) :- Stages.productionSymbols.uncoveredSymbols \= [].
|
|
790
1042
|
proof_gap_present(missing_symbol_coordinates, Stages) :- Stages.sourceCoordinates.missingSymbols \= [].
|
|
791
1043
|
proof_gap_present(missing_requirement_source, Stages) :- Stages.sourceCoordinates.requirementSource == missing.
|
|
@@ -803,14 +1055,15 @@ gap_definition(contradiction_check_incomplete, 41, contradictions, "Complete log
|
|
|
803
1055
|
gap_definition(missing_scenario, 50, scenarios, "Add a specified_by scenario for the requirement.").
|
|
804
1056
|
gap_definition(missing_scenario_test, 51, scenario_tests, "Link the scenario to a test with verified_by or validates.").
|
|
805
1057
|
gap_definition(missing_passing_e2e, 52, passing_e2e, "Record fresh passing end-to-end evidence on a scenario-backed test.").
|
|
806
|
-
gap_definition(missing_verification_receipt, 53, passing_e2e, "
|
|
1058
|
+
gap_definition(missing_verification_receipt, 53, passing_e2e, "Run the exact current verification contract through kibi verify and append its kibi.verification-receipt.v2 result for the scenario-backed E2E test.").
|
|
807
1059
|
gap_definition(stale_verification_receipt, 54, passing_e2e, "Re-run the E2E test against the current code snapshot and append its receipt.").
|
|
808
1060
|
gap_definition(failed_verification_receipt, 55, passing_e2e, "Repair the failing E2E behavior and append a newer passing receipt for the same snapshot.").
|
|
809
1061
|
gap_definition(invalid_verification_receipt, 56, passing_e2e, "Repair malformed, future-dated, mismatched, or otherwise uncheckable receipt evidence.").
|
|
810
|
-
gap_definition(
|
|
1062
|
+
gap_definition(verification_contract_mismatch, 57, passing_e2e, "Run the current verification contract and append its receipt without rewriting historical evidence.").
|
|
1063
|
+
gap_definition(verification_snapshot_unavailable, 58, passing_e2e, "Run coverage through a CLI or MCP runtime that exposes the deterministic workspace snapshot.").
|
|
811
1064
|
gap_definition(missing_executable_test_symbol, 60, executable_symbols, "Link executable test code to every qualifying E2E test with executable_for.").
|
|
812
1065
|
gap_definition(missing_production_symbol, 70, production_symbols, "Link at least one production symbol to the requirement with implements.").
|
|
813
|
-
gap_definition(missing_production_symbol_coverage, 71, production_symbols, "Link every implementing production symbol to a qualifying E2E test with covered_by.").
|
|
1066
|
+
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.").
|
|
814
1067
|
gap_definition(missing_symbol_coordinates, 80, source_coordinates, "Refresh and persist exact coordinates for every proof-bearing symbol.").
|
|
815
1068
|
gap_definition(missing_requirement_source, 81, source_coordinates, "Bind the requirement to its current source document.").
|
|
816
1069
|
|
|
@@ -822,6 +1075,10 @@ proof_repairs(Gaps, Repairs) :-
|
|
|
822
1075
|
normalize_atom(Value, Atom) :-
|
|
823
1076
|
kb:normalize_term_atom(Value, Atom).
|
|
824
1077
|
|
|
1078
|
+
source_path_atom(Value, Atom) :-
|
|
1079
|
+
kb:source_value_atom(Value, Atom),
|
|
1080
|
+
Atom \= ''.
|
|
1081
|
+
|
|
825
1082
|
normalize_atom_list(Value, Atoms) :-
|
|
826
1083
|
kb:normalize_term_atom_list(Value, Atoms).
|
|
827
1084
|
|