kibi-core 0.10.0 → 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 +5 -2
- 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 +3 -0
- package/src/logic_ir.pl +1 -1
- package/src/requirement_proof.pl +306 -49
- package/src/status.pl +125 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kibi-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Core Prolog modules and RDF graph logic for Kibi",
|
|
6
6
|
"type": "module",
|
|
@@ -8,7 +8,10 @@
|
|
|
8
8
|
"node": ">=18"
|
|
9
9
|
},
|
|
10
10
|
"main": "./src/kb.pl",
|
|
11
|
-
"files": [
|
|
11
|
+
"files": [
|
|
12
|
+
"src/**/*.pl",
|
|
13
|
+
"schema/**/*.pl"
|
|
14
|
+
],
|
|
12
15
|
"license": "AGPL-3.0-or-later",
|
|
13
16
|
"author": "Piotr Franczyk",
|
|
14
17
|
"repository": {
|
package/schema/entities.pl
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
:- module(kibi_entities, [entity_type/1, entity_property/3, required_property/2, optional_property/2]).
|
|
4
4
|
|
|
5
5
|
% Entity types
|
|
6
|
+
% implements REQ-004
|
|
6
7
|
entity_type(req).
|
|
7
8
|
entity_type(scenario).
|
|
8
9
|
entity_type(test).
|
|
@@ -87,6 +88,7 @@ entity_property(symbol, sourceEndColumn, integer).
|
|
|
87
88
|
% Typed test verification fields - only valid for test entities
|
|
88
89
|
entity_property(test, verification_scope, atom).
|
|
89
90
|
entity_property(test, verification_perspective, atom).
|
|
91
|
+
entity_property(test, verification_contract, list_or_json).
|
|
90
92
|
entity_property(test, verification_receipts, string).
|
|
91
93
|
|
|
92
94
|
% Required properties for all entity types
|
|
@@ -113,6 +115,7 @@ optional_property(req, semantic_source_hash).
|
|
|
113
115
|
optional_property(req, semantic_inventory).
|
|
114
116
|
optional_property(test, verification_scope).
|
|
115
117
|
optional_property(test, verification_perspective).
|
|
118
|
+
optional_property(test, verification_contract).
|
|
116
119
|
optional_property(test, verification_receipts).
|
|
117
120
|
|
|
118
121
|
% Documentation helpers
|
package/schema/relationships.pl
CHANGED
package/schema/validation.pl
CHANGED
|
@@ -119,6 +119,7 @@ is_fact_only_field(claim_span_end).
|
|
|
119
119
|
% is_test_only_field(+Key) - true if Key is a test-specific field
|
|
120
120
|
is_test_only_field(verification_scope).
|
|
121
121
|
is_test_only_field(verification_perspective).
|
|
122
|
+
is_test_only_field(verification_contract).
|
|
122
123
|
is_test_only_field(verification_receipts).
|
|
123
124
|
|
|
124
125
|
% validate_fact_shape(+Kind, +Props)
|
package/src/checks.pl
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
:- module(checks, [
|
|
7
7
|
check_all/1, % Returns all violations as a dict
|
|
8
8
|
check_all_json/1, % Returns all violations as JSON string
|
|
9
|
+
check_selected_json/2, % Returns only selected rule violations as JSON
|
|
9
10
|
check_must_priority_coverage/1, % Returns list of must-priority violations
|
|
10
11
|
check_symbol_coverage/1, % Returns list of uncovered symbols
|
|
11
12
|
check_symbol_traceability/2, % Returns list of symbols lacking requirement traceability (ReqAdr option)
|
|
@@ -14,6 +15,7 @@
|
|
|
14
15
|
check_required_fields/1, % Returns list of missing required field violations
|
|
15
16
|
check_deprecated_adrs/1, % Returns list of deprecated ADR violations
|
|
16
17
|
check_domain_contradictions/1, % Returns list of contradiction violations
|
|
18
|
+
check_domain_contradictions_and_witnesses/2,
|
|
17
19
|
check_domain_contradiction_witnesses/1,
|
|
18
20
|
check_strict_fact_shape/1, % Returns list of malformed strict fact violations
|
|
19
21
|
check_strict_req_fact_pairing/1,% Returns list of malformed strict req/fact pairing violations
|
|
@@ -591,6 +593,15 @@ check_domain_contradictions(Violations) :-
|
|
|
591
593
|
check_domain_contradiction_witnesses(Witnesses),
|
|
592
594
|
maplist(contradiction_witness_violation, Witnesses, Violations).
|
|
593
595
|
|
|
596
|
+
%% check_domain_contradictions_and_witnesses(-Violations, -Witnesses)
|
|
597
|
+
% Compute the contradiction witness graph once when a caller needs both the
|
|
598
|
+
% report violations and their exact evidence. The existing one-result
|
|
599
|
+
% predicates remain unchanged; this paired form avoids duplicate work in
|
|
600
|
+
% proof contexts without weakening any contradiction checks.
|
|
601
|
+
check_domain_contradictions_and_witnesses(Violations, Witnesses) :-
|
|
602
|
+
check_domain_contradiction_witnesses(Witnesses),
|
|
603
|
+
maplist(contradiction_witness_violation, Witnesses, Violations).
|
|
604
|
+
|
|
594
605
|
%% check_domain_contradiction_witnesses(-Witnesses)
|
|
595
606
|
% Exact, JSON-safe evidence for every strict-property, ground-predicate, and
|
|
596
607
|
% safe-rule contradiction result. Rule overlap that cannot be proven or
|
|
@@ -989,9 +1000,11 @@ predicate_verifiability_kind_label(Kind, Label) :-
|
|
|
989
1000
|
|
|
990
1001
|
%% check_logic_coverage(-Violations)
|
|
991
1002
|
% Validates explicitly declared atomic requirement claim manifests. Legacy
|
|
992
|
-
% requirements without logic_claims remain eligible for gradual backfill
|
|
993
|
-
%
|
|
994
|
-
%
|
|
1003
|
+
% requirements without logic_claims remain eligible for gradual backfill. A
|
|
1004
|
+
% modeled proposition must be grounded exactly once; ambiguous, ontology-gap,
|
|
1005
|
+
% and missing propositions remain explicit unresolved inventory states and are
|
|
1006
|
+
% not required to have a ground fact (missing is reported by semantic
|
|
1007
|
+
% completeness).
|
|
995
1008
|
check_logic_coverage(Violations) :-
|
|
996
1009
|
findall(Violation, logic_coverage_violation(Violation), Violations0),
|
|
997
1010
|
sort(Violations0, Violations).
|
|
@@ -1006,6 +1019,7 @@ logic_coverage_violation(violation(
|
|
|
1006
1019
|
kb:current_req(ReqId),
|
|
1007
1020
|
requirement_logic_claims(ReqId, ClaimKeys),
|
|
1008
1021
|
member(ClaimKey, ClaimKeys),
|
|
1022
|
+
claim_requires_grounding(ReqId, ClaimKey),
|
|
1009
1023
|
\+ grounded_requirement_claim(ReqId, ClaimKey),
|
|
1010
1024
|
format(
|
|
1011
1025
|
string(Description),
|
|
@@ -1014,6 +1028,24 @@ logic_coverage_violation(violation(
|
|
|
1014
1028
|
),
|
|
1015
1029
|
violation_source(ReqId, req, Source).
|
|
1016
1030
|
|
|
1031
|
+
logic_coverage_violation(violation(
|
|
1032
|
+
'logic-coverage',
|
|
1033
|
+
ReqId,
|
|
1034
|
+
Description,
|
|
1035
|
+
"Keep ontology gaps, ambiguities, and missing interpretations unresolved; only modeled propositions may have logical ground facts",
|
|
1036
|
+
Source
|
|
1037
|
+
)) :-
|
|
1038
|
+
kb:current_req(ReqId),
|
|
1039
|
+
requirement_inventory_status(ReqId, ClaimKey, Status),
|
|
1040
|
+
memberchk(Status, [ambiguous, ontology_gap, missing]),
|
|
1041
|
+
grounded_requirement_claim(ReqId, ClaimKey),
|
|
1042
|
+
format(
|
|
1043
|
+
string(Description),
|
|
1044
|
+
"Requirement grounds unresolved ~w proposition ~w",
|
|
1045
|
+
[Status, ClaimKey]
|
|
1046
|
+
),
|
|
1047
|
+
violation_source(ReqId, req, Source).
|
|
1048
|
+
|
|
1017
1049
|
logic_coverage_violation(violation(
|
|
1018
1050
|
'logic-coverage',
|
|
1019
1051
|
ReqId,
|
|
@@ -1076,6 +1108,28 @@ requirement_logic_claims(ReqId, ClaimKeys) :-
|
|
|
1076
1108
|
memberchk(logic_claims=RawClaimKeys, Props),
|
|
1077
1109
|
kb:normalize_term_atom_list(RawClaimKeys, ClaimKeys).
|
|
1078
1110
|
|
|
1111
|
+
claim_requires_grounding(ReqId, ClaimKey) :-
|
|
1112
|
+
requirement_inventory_status(ReqId, ClaimKey, modeled),
|
|
1113
|
+
!.
|
|
1114
|
+
claim_requires_grounding(ReqId, ClaimKey) :-
|
|
1115
|
+
\+ requirement_inventory_status(ReqId, ClaimKey, _).
|
|
1116
|
+
|
|
1117
|
+
requirement_inventory_status(ReqId, ClaimKey, Status) :-
|
|
1118
|
+
kb_entity(ReqId, req, Props),
|
|
1119
|
+
memberchk(semantic_inventory=RawInventory, Props),
|
|
1120
|
+
inventory_entries(RawInventory, Entries),
|
|
1121
|
+
member(Entry, Entries),
|
|
1122
|
+
checks_inventory_entry_field(Entry, claim_key, RawClaimKey),
|
|
1123
|
+
kb:normalize_term_atom(RawClaimKey, ClaimKey),
|
|
1124
|
+
inventory_entry_status(Entry, Status).
|
|
1125
|
+
|
|
1126
|
+
checks_inventory_entry_field(Entry, Key, Value) :-
|
|
1127
|
+
is_dict(Entry),
|
|
1128
|
+
get_dict(Key, Entry, Value).
|
|
1129
|
+
checks_inventory_entry_field(Entry, Key, Value) :-
|
|
1130
|
+
is_list(Entry),
|
|
1131
|
+
memberchk(Key=Value, Entry).
|
|
1132
|
+
|
|
1079
1133
|
grounded_requirement_claim(ReqId, ClaimKey) :-
|
|
1080
1134
|
grounded_requirement_claim_fact(ReqId, ClaimKey, _FactId).
|
|
1081
1135
|
|
|
@@ -1222,13 +1276,15 @@ rule_verifiability_description(Props, FactId, Description) :-
|
|
|
1222
1276
|
rule_verifiability_description(Props, FactId, Description) :-
|
|
1223
1277
|
memberchk(fact_kind=RawKind, Props),
|
|
1224
1278
|
normalize_term_atom(RawKind, rule),
|
|
1225
|
-
memberchk(rule_schema_id=
|
|
1279
|
+
memberchk(rule_schema_id=RawSchemaId, Props),
|
|
1280
|
+
normalize_term_atom(RawSchemaId, SchemaId),
|
|
1226
1281
|
\+ kb_entity(SchemaId, fact, _),
|
|
1227
1282
|
format(string(Description), "Rule fact ~w references missing rule schema ~w", [FactId, SchemaId]).
|
|
1228
1283
|
rule_verifiability_description(Props, FactId, Description) :-
|
|
1229
1284
|
memberchk(fact_kind=RawKind, Props),
|
|
1230
1285
|
normalize_term_atom(RawKind, rule),
|
|
1231
|
-
memberchk(rule_schema_id=
|
|
1286
|
+
memberchk(rule_schema_id=RawSchemaId, Props),
|
|
1287
|
+
normalize_term_atom(RawSchemaId, SchemaId),
|
|
1232
1288
|
kb_entity(SchemaId, fact, SchemaProps),
|
|
1233
1289
|
\+ valid_rule_schema_props(SchemaProps),
|
|
1234
1290
|
format(string(Description), "Rule fact ~w references ~w, which is not a rule_schema fact", [FactId, SchemaId]).
|
|
@@ -1286,6 +1342,10 @@ inventory_entries(Raw, Entries) :-
|
|
|
1286
1342
|
catch(atom_json_dict(Raw, JsonEntries, [value_string_as(string)]), _, fail),
|
|
1287
1343
|
is_list(JsonEntries)
|
|
1288
1344
|
-> Entries = JsonEntries
|
|
1345
|
+
; (atom(Raw) ; string(Raw)),
|
|
1346
|
+
catch(term_string(PrologEntries, Raw), _, fail),
|
|
1347
|
+
is_list(PrologEntries)
|
|
1348
|
+
-> Entries = PrologEntries
|
|
1289
1349
|
; Entries = []
|
|
1290
1350
|
).
|
|
1291
1351
|
|
|
@@ -1341,6 +1401,64 @@ check_all_json(JsonString) :-
|
|
|
1341
1401
|
JsonString
|
|
1342
1402
|
).
|
|
1343
1403
|
|
|
1404
|
+
%% check_selected_json(+Rules, -JsonString)
|
|
1405
|
+
% Run only the named aggregated checks. This is intentionally separate from
|
|
1406
|
+
% check_all/1 so focused diagnostics cannot evaluate unrelated rule queries.
|
|
1407
|
+
check_selected_json(Rules, JsonString) :-
|
|
1408
|
+
check_selected(Rules, ViolationsDict),
|
|
1409
|
+
violations_dict_to_json(ViolationsDict, JsonDict),
|
|
1410
|
+
with_output_to_string(
|
|
1411
|
+
json_write_dict(current_output, JsonDict, [width(0)]),
|
|
1412
|
+
JsonString
|
|
1413
|
+
).
|
|
1414
|
+
|
|
1415
|
+
check_selected(Rules, _{
|
|
1416
|
+
must_priority_coverage: MustPriority,
|
|
1417
|
+
symbol_coverage: SymbolCoverage,
|
|
1418
|
+
symbol_traceability: SymbolTraceability,
|
|
1419
|
+
no_dangling_refs: DanglingRefs,
|
|
1420
|
+
no_cycles: Cycles,
|
|
1421
|
+
required_fields: RequiredFields,
|
|
1422
|
+
deprecated_adr_no_successor: DeprecatedADRs,
|
|
1423
|
+
domain_contradictions: Contradictions,
|
|
1424
|
+
strict_fact_shape: StrictFactShape,
|
|
1425
|
+
strict_req_fact_pairing: StrictReqFactPairing,
|
|
1426
|
+
strict_readiness: StrictReadiness,
|
|
1427
|
+
predicate_verifiability: PredicateVerifiability,
|
|
1428
|
+
logic_coverage: LogicCoverage,
|
|
1429
|
+
rule_safety: RuleSafety,
|
|
1430
|
+
rule_verifiability: RuleVerifiability,
|
|
1431
|
+
semantic_completeness: SemanticCompleteness
|
|
1432
|
+
}) :-
|
|
1433
|
+
selected_rule(Rules, 'must-priority-coverage', check_must_priority_coverage, MustPriority),
|
|
1434
|
+
selected_rule(Rules, 'symbol-coverage', check_symbol_coverage, SymbolCoverage),
|
|
1435
|
+
selected_rule_with_options(Rules, 'symbol-traceability', check_symbol_traceability, SymbolTraceability),
|
|
1436
|
+
selected_rule(Rules, 'no-dangling-refs', check_no_dangling_refs, DanglingRefs),
|
|
1437
|
+
selected_rule(Rules, 'no-cycles', check_no_cycles, Cycles),
|
|
1438
|
+
selected_rule(Rules, 'required-fields', check_required_fields, RequiredFields),
|
|
1439
|
+
selected_rule(Rules, 'deprecated-adr-no-successor', check_deprecated_adrs, DeprecatedADRs),
|
|
1440
|
+
selected_rule(Rules, 'domain-contradictions', check_domain_contradictions, Contradictions),
|
|
1441
|
+
selected_rule(Rules, 'strict-fact-shape', check_strict_fact_shape, StrictFactShape),
|
|
1442
|
+
selected_rule(Rules, 'strict-req-fact-pairing', check_strict_req_fact_pairing, StrictReqFactPairing),
|
|
1443
|
+
selected_rule(Rules, 'strict-readiness', check_strict_readiness, StrictReadiness),
|
|
1444
|
+
selected_rule(Rules, 'predicate-verifiability', check_predicate_verifiability, PredicateVerifiability),
|
|
1445
|
+
selected_rule(Rules, 'logic-coverage', check_logic_coverage, LogicCoverage),
|
|
1446
|
+
selected_rule(Rules, 'rule-safety', check_rule_safety, RuleSafety),
|
|
1447
|
+
selected_rule(Rules, 'rule-verifiability', check_rule_verifiability, RuleVerifiability),
|
|
1448
|
+
selected_rule(Rules, 'semantic-completeness', check_semantic_completeness, SemanticCompleteness).
|
|
1449
|
+
|
|
1450
|
+
selected_rule(Rules, Name, Goal, Violations) :-
|
|
1451
|
+
( memberchk(Name, Rules)
|
|
1452
|
+
-> call(Goal, Violations)
|
|
1453
|
+
; Violations = []
|
|
1454
|
+
).
|
|
1455
|
+
|
|
1456
|
+
selected_rule_with_options(Rules, Name, Goal, Violations) :-
|
|
1457
|
+
( memberchk(Name, Rules)
|
|
1458
|
+
-> call(Goal, false, Violations)
|
|
1459
|
+
; Violations = []
|
|
1460
|
+
).
|
|
1461
|
+
|
|
1344
1462
|
%% check_all_json_with_options(-JsonString, +RequireAdr)
|
|
1345
1463
|
% Returns all violations as JSON string with options.
|
|
1346
1464
|
% RequireAdr: if true, symbol-traceability also requires ADR constraints.
|
package/src/discovery.pl
CHANGED
|
@@ -143,7 +143,7 @@ coverage_rows(type, _Tags, _IncludePassing, _IncludeTransitive, _VerificationSna
|
|
|
143
143
|
requirement_coverage_row(Tags, IncludeTransitive, ProofContext, Row) :-
|
|
144
144
|
kb_entity(Id, req, Props),
|
|
145
145
|
matches_tags(Tags, Props),
|
|
146
|
-
entity_title_status_source(Props, Title, Status,
|
|
146
|
+
entity_title_status_source(Props, Title, Status, Source),
|
|
147
147
|
entity_priority(Props, Priority),
|
|
148
148
|
count_distinct_targets(specified_by, Id, source, ScenarioCount),
|
|
149
149
|
requirement_test_count(Id, TestCount),
|
|
@@ -156,6 +156,7 @@ requirement_coverage_row(Tags, IncludeTransitive, ProofContext, Row) :-
|
|
|
156
156
|
type: req,
|
|
157
157
|
title: Title,
|
|
158
158
|
status: Status,
|
|
159
|
+
source: Source,
|
|
159
160
|
priority: Priority,
|
|
160
161
|
scenarioCount: ScenarioCount,
|
|
161
162
|
testCount: TestCount,
|
package/src/kb.pl
CHANGED
|
@@ -1839,6 +1839,9 @@ literal_to_value(semantic_inventory, Literal, Value) :-
|
|
|
1839
1839
|
literal_to_value(verification_receipts, Literal, Value) :-
|
|
1840
1840
|
!,
|
|
1841
1841
|
structured_literal_value(Literal, Value).
|
|
1842
|
+
literal_to_value(verification_contract, Literal, Value) :-
|
|
1843
|
+
!,
|
|
1844
|
+
structured_literal_value(Literal, Value).
|
|
1842
1845
|
literal_to_value(_Key, Literal, Value) :-
|
|
1843
1846
|
( % Handle ^^/2 functor (RDF typed literal shorthand)
|
|
1844
1847
|
Literal = ^^(StrVal, 'http://www.w3.org/2001/XMLSchema#string')
|
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
|
|
package/src/status.pl
CHANGED
|
@@ -19,12 +19,16 @@ status_meta_dict(StatusDict) :-
|
|
|
19
19
|
snapshot_id(SnapshotId),
|
|
20
20
|
synced_at(DataFile, SyncedAt),
|
|
21
21
|
freshness_state(DataFile, Dirty, SyncState),
|
|
22
|
+
stale_reasons(StaleReasons, StaleReasonCount, StaleReasonsTruncated),
|
|
22
23
|
StatusDict = _{
|
|
23
24
|
branch: Branch,
|
|
24
25
|
snapshotId: SnapshotId,
|
|
25
26
|
syncedAt: SyncedAt,
|
|
26
27
|
dirty: Dirty,
|
|
27
28
|
syncState: SyncState,
|
|
29
|
+
staleReasons: StaleReasons,
|
|
30
|
+
staleReasonCount: StaleReasonCount,
|
|
31
|
+
staleReasonsTruncated: StaleReasonsTruncated,
|
|
28
32
|
kbPath: KbPath,
|
|
29
33
|
lastSyncSource: persisted
|
|
30
34
|
}.
|
|
@@ -37,6 +41,9 @@ status_meta_dict(StatusDict) :-
|
|
|
37
41
|
syncedAt: null,
|
|
38
42
|
dirty: false,
|
|
39
43
|
syncState: unknown,
|
|
44
|
+
staleReasons: [],
|
|
45
|
+
staleReasonCount: 0,
|
|
46
|
+
staleReasonsTruncated: false,
|
|
40
47
|
kbPath: KbPath,
|
|
41
48
|
lastSyncSource: unknown
|
|
42
49
|
}.
|
|
@@ -104,9 +111,90 @@ freshness_state(DataFile, false, fresh) :-
|
|
|
104
111
|
!.
|
|
105
112
|
freshness_state(_, true, unknown).
|
|
106
113
|
|
|
114
|
+
stale_reasons(Reasons, Count, Truncated) :-
|
|
115
|
+
findall(Reason, stale_indexed_source_reason(Reason), IndexedReasons),
|
|
116
|
+
findall(Reason, stale_knowledge_lane_reason(Reason), KnowledgeReasons),
|
|
117
|
+
findall(Reason, stale_documentation_reason(Reason), DocumentationReasons),
|
|
118
|
+
append(IndexedReasons, KnowledgeReasons, IndexedAndKnowledge),
|
|
119
|
+
append(IndexedAndKnowledge, DocumentationReasons, Reasons0),
|
|
120
|
+
sort(Reasons0, Sorted),
|
|
121
|
+
length(Sorted, Count),
|
|
122
|
+
( Count > 200
|
|
123
|
+
-> length(Reasons, 200), append(Reasons, _, Sorted), Truncated = true
|
|
124
|
+
; Reasons = Sorted, Truncated = false
|
|
125
|
+
).
|
|
126
|
+
|
|
127
|
+
stale_indexed_source_reason(Reason) :-
|
|
128
|
+
attached_workspace_root(WorkspaceRoot),
|
|
129
|
+
kb:kb_indexed_sources(Sources),
|
|
130
|
+
member(SourceAtom, Sources),
|
|
131
|
+
repo_relative_source(SourceAtom, RelativeSource),
|
|
132
|
+
directory_file_path(WorkspaceRoot, RelativeSource, SourcePath),
|
|
133
|
+
( exists_file(SourcePath)
|
|
134
|
+
-> time_file(SourcePath, FileTime),
|
|
135
|
+
kb_snapshot_time(SnapshotTime),
|
|
136
|
+
FileTime > SnapshotTime,
|
|
137
|
+
Code = indexed_source_newer
|
|
138
|
+
; exists_directory(SourcePath)
|
|
139
|
+
-> fail
|
|
140
|
+
; Code = indexed_source_missing
|
|
141
|
+
),
|
|
142
|
+
entity_ids_for_source(RelativeSource, EntityIds),
|
|
143
|
+
Reason = _{code: Code, path: RelativeSource, entityIds: EntityIds}.
|
|
144
|
+
|
|
145
|
+
stale_knowledge_lane_reason(Reason) :-
|
|
146
|
+
attached_workspace_root(WorkspaceRoot),
|
|
147
|
+
knowledge_lane(Lane),
|
|
148
|
+
directory_file_path(WorkspaceRoot, '.kb', KbRoot),
|
|
149
|
+
directory_file_path(KbRoot, Lane, LaneRoot),
|
|
150
|
+
exists_directory(LaneRoot),
|
|
151
|
+
kb_snapshot_time(SnapshotTime),
|
|
152
|
+
directory_tree_newer_path(LaneRoot, SnapshotTime, Path),
|
|
153
|
+
workspace_relative_path(WorkspaceRoot, Path, RelativePath),
|
|
154
|
+
entity_ids_for_source(RelativePath, EntityIds),
|
|
155
|
+
Reason = _{code: knowledge_source_newer, path: RelativePath, entityIds: EntityIds}.
|
|
156
|
+
|
|
157
|
+
stale_documentation_reason(Reason) :-
|
|
158
|
+
attached_workspace_root(WorkspaceRoot),
|
|
159
|
+
directory_file_path(WorkspaceRoot, 'documentation', DocumentationRoot),
|
|
160
|
+
exists_directory(DocumentationRoot),
|
|
161
|
+
kb_snapshot_time(SnapshotTime),
|
|
162
|
+
directory_tree_newer_path(DocumentationRoot, SnapshotTime, Path),
|
|
163
|
+
workspace_relative_path(WorkspaceRoot, Path, RelativePath),
|
|
164
|
+
entity_ids_for_source(RelativePath, EntityIds),
|
|
165
|
+
Reason = _{code: documentation_source_newer, path: RelativePath, entityIds: EntityIds}.
|
|
166
|
+
|
|
167
|
+
kb_snapshot_time(SnapshotTime) :-
|
|
168
|
+
kb:kb_attached(KbPath),
|
|
169
|
+
( directory_file_path(KbPath, 'CURRENT', DataFile), exists_file(DataFile)
|
|
170
|
+
-> time_file(DataFile, SnapshotTime)
|
|
171
|
+
; directory_file_path(KbPath, 'kb.rdf', DataFile), exists_file(DataFile)
|
|
172
|
+
-> time_file(DataFile, SnapshotTime)
|
|
173
|
+
; SnapshotTime = 0
|
|
174
|
+
).
|
|
175
|
+
|
|
176
|
+
entity_ids_for_source(RelativeSource, EntityIds) :-
|
|
177
|
+
findall(Id,
|
|
178
|
+
(kb_entity(Id, _Type, Props),
|
|
179
|
+
entity_source_property(Props, RawSource),
|
|
180
|
+
source_value_atom(RawSource, SourceAtom),
|
|
181
|
+
repo_relative_source(SourceAtom, RelativeSource),
|
|
182
|
+
Id \= '') ,
|
|
183
|
+
Ids0),
|
|
184
|
+
sort(Ids0, EntityIds).
|
|
185
|
+
|
|
186
|
+
entity_source_property(Props, RawSource) :-
|
|
187
|
+
memberchk(sourceFile=RawSource, Props),
|
|
188
|
+
!.
|
|
189
|
+
entity_source_property(Props, RawSource) :-
|
|
190
|
+
memberchk(source=RawSource, Props).
|
|
191
|
+
|
|
107
192
|
workspace_state_changed(SnapshotTime) :-
|
|
108
193
|
workspace_source_changed(SnapshotTime),
|
|
109
194
|
!.
|
|
195
|
+
workspace_state_changed(SnapshotTime) :-
|
|
196
|
+
knowledge_lane_tree_changed(SnapshotTime),
|
|
197
|
+
!.
|
|
110
198
|
workspace_state_changed(SnapshotTime) :-
|
|
111
199
|
documentation_tree_changed(SnapshotTime),
|
|
112
200
|
!.
|
|
@@ -126,6 +214,15 @@ workspace_source_changed(SnapshotTime) :-
|
|
|
126
214
|
),
|
|
127
215
|
!.
|
|
128
216
|
|
|
217
|
+
knowledge_lane_tree_changed(SnapshotTime) :-
|
|
218
|
+
attached_workspace_root(WorkspaceRoot),
|
|
219
|
+
knowledge_lane(Lane),
|
|
220
|
+
directory_file_path(WorkspaceRoot, '.kb', KbRoot),
|
|
221
|
+
directory_file_path(KbRoot, Lane, LaneRoot),
|
|
222
|
+
exists_directory(LaneRoot),
|
|
223
|
+
directory_tree_newer(LaneRoot, SnapshotTime),
|
|
224
|
+
!.
|
|
225
|
+
|
|
129
226
|
documentation_tree_changed(SnapshotTime) :-
|
|
130
227
|
attached_workspace_root(WorkspaceRoot),
|
|
131
228
|
directory_file_path(WorkspaceRoot, 'documentation', DocumentationRoot),
|
|
@@ -140,6 +237,7 @@ directory_tree_newer(Path, SnapshotTime) :-
|
|
|
140
237
|
time_file(Path, EntryTime),
|
|
141
238
|
EntryTime > SnapshotTime,
|
|
142
239
|
!.
|
|
240
|
+
|
|
143
241
|
directory_tree_newer(Path, SnapshotTime) :-
|
|
144
242
|
exists_directory(Path),
|
|
145
243
|
directory_files(Path, Entries),
|
|
@@ -150,6 +248,21 @@ directory_tree_newer(Path, SnapshotTime) :-
|
|
|
150
248
|
directory_tree_newer(ChildPath, SnapshotTime),
|
|
151
249
|
!.
|
|
152
250
|
|
|
251
|
+
directory_tree_newer_path(Path, SnapshotTime, Path) :-
|
|
252
|
+
exists_file(Path),
|
|
253
|
+
entity_documentation_file(Path),
|
|
254
|
+
\+ ignored_documentation_file(Path),
|
|
255
|
+
time_file(Path, EntryTime),
|
|
256
|
+
EntryTime > SnapshotTime.
|
|
257
|
+
directory_tree_newer_path(Path, SnapshotTime, ChildPath) :-
|
|
258
|
+
exists_directory(Path),
|
|
259
|
+
directory_files(Path, Entries),
|
|
260
|
+
member(Entry, Entries),
|
|
261
|
+
Entry \= '.',
|
|
262
|
+
Entry \= '..',
|
|
263
|
+
directory_file_path(Path, Entry, Candidate),
|
|
264
|
+
directory_tree_newer_path(Candidate, SnapshotTime, ChildPath).
|
|
265
|
+
|
|
153
266
|
attached_workspace_root(WorkspaceRoot) :-
|
|
154
267
|
kb:kb_attached(KbPath),
|
|
155
268
|
branch_workspace_from_kb_path(KbPath, _Branch, WorkspaceRoot).
|
|
@@ -219,8 +332,20 @@ source_value_atom(Val, Atom) :-
|
|
|
219
332
|
term_string(Val, Str),
|
|
220
333
|
atom_string(Atom, Str).
|
|
221
334
|
|
|
335
|
+
knowledge_lane(requirements).
|
|
336
|
+
knowledge_lane(scenarios).
|
|
337
|
+
knowledge_lane(tests).
|
|
338
|
+
knowledge_lane(facts).
|
|
339
|
+
knowledge_lane(adr).
|
|
340
|
+
knowledge_lane(flags).
|
|
341
|
+
knowledge_lane(events).
|
|
342
|
+
|
|
222
343
|
ignored_documentation_file(Path) :-
|
|
223
344
|
file_base_name(Path, 'README.md').
|
|
345
|
+
ignored_documentation_file(Path) :-
|
|
346
|
+
sub_atom(Path, _, _, _, '/tests/e2e/').
|
|
347
|
+
ignored_documentation_file(Path) :-
|
|
348
|
+
sub_atom(Path, _, _, _, '/tests/benchmarks/').
|
|
224
349
|
|
|
225
350
|
entity_documentation_file(Path) :-
|
|
226
351
|
read_file_to_string(Path, Content, []),
|