kibi-core 0.12.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -5
- package/src/checks.pl +134 -4
- package/src/discovery.pl +55 -0
- package/src/requirement_proof.pl +285 -5
- package/src/rule_registry.pl +6 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kibi-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Core Prolog modules and RDF graph logic for Kibi",
|
|
6
6
|
"type": "module",
|
|
@@ -8,10 +8,7 @@
|
|
|
8
8
|
"node": ">=18"
|
|
9
9
|
},
|
|
10
10
|
"main": "./src/kb.pl",
|
|
11
|
-
"files": [
|
|
12
|
-
"src/**/*.pl",
|
|
13
|
-
"schema/**/*.pl"
|
|
14
|
-
],
|
|
11
|
+
"files": ["src/**/*.pl", "schema/**/*.pl"],
|
|
15
12
|
"license": "AGPL-3.0-or-later",
|
|
16
13
|
"author": "Piotr Franczyk",
|
|
17
14
|
"repository": {
|
package/src/checks.pl
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
check_must_priority_coverage/1, % Returns list of must-priority violations
|
|
11
11
|
check_symbol_coverage/1, % Returns list of uncovered symbols
|
|
12
12
|
check_symbol_traceability/2, % Returns list of symbols lacking requirement traceability (ReqAdr option)
|
|
13
|
+
check_proof_contract_symbols/1, % Returns unresolved or unexpected required_proofs symbol issues
|
|
13
14
|
check_no_dangling_refs/1, % Returns list of dangling ref violations
|
|
14
15
|
check_no_cycles/1, % Returns list of cycle violations
|
|
15
16
|
check_required_fields/1, % Returns list of missing required field violations
|
|
@@ -70,6 +71,7 @@ check_all(ViolationsDict) :-
|
|
|
70
71
|
check_rule_verifiability(RuleVerifiability),
|
|
71
72
|
check_semantic_completeness(SemanticCompleteness),
|
|
72
73
|
check_req_status_vocabulary(ReqStatusVocabulary),
|
|
74
|
+
check_proof_contract_symbols(ProofContractSymbols),
|
|
73
75
|
ViolationsDict = _{
|
|
74
76
|
must_priority_coverage: MustPriority,
|
|
75
77
|
symbol_coverage: SymbolCoverage,
|
|
@@ -87,7 +89,8 @@ check_all(ViolationsDict) :-
|
|
|
87
89
|
rule_safety: RuleSafety,
|
|
88
90
|
rule_verifiability: RuleVerifiability,
|
|
89
91
|
semantic_completeness: SemanticCompleteness,
|
|
90
|
-
req_status_vocabulary: ReqStatusVocabulary
|
|
92
|
+
req_status_vocabulary: ReqStatusVocabulary,
|
|
93
|
+
proof_contract_symbols: ProofContractSymbols
|
|
91
94
|
}.
|
|
92
95
|
|
|
93
96
|
%% check_must_priority_coverage(-Violations)
|
|
@@ -148,6 +151,17 @@ check_symbol_traceability(RequireAdr, Violations) :-
|
|
|
148
151
|
),
|
|
149
152
|
sort(Violations0, Violations).
|
|
150
153
|
|
|
154
|
+
symbol_traceability_violation(_RequireAdr, violation(
|
|
155
|
+
'symbol-traceability',
|
|
156
|
+
SymbolId,
|
|
157
|
+
"Symbol mixes executable_for test identity with production implements or covered_by.",
|
|
158
|
+
"Split the symbol: keep executable_for on the test-code symbol and use a separate production symbol for implements/covered_by.",
|
|
159
|
+
Source
|
|
160
|
+
)) :-
|
|
161
|
+
kb_entity(SymbolId, symbol, _),
|
|
162
|
+
mixed_role_symbol(SymbolId),
|
|
163
|
+
violation_source(SymbolId, symbol, Source).
|
|
164
|
+
|
|
151
165
|
symbol_traceability_violation(RequireAdr, violation(
|
|
152
166
|
'symbol-traceability',
|
|
153
167
|
SymbolId,
|
|
@@ -186,6 +200,118 @@ symbol_traceability_violation(RequireAdr, violation(
|
|
|
186
200
|
),
|
|
187
201
|
violation_source(SymbolId, symbol, Source).
|
|
188
202
|
|
|
203
|
+
%% check_proof_contract_symbols(-Violations)
|
|
204
|
+
% Advisory integrity for proof_contract.required_proofs and optional
|
|
205
|
+
% proof_bindings.source_file. Kibi does not infer TEST names from filenames.
|
|
206
|
+
check_proof_contract_symbols(Violations) :-
|
|
207
|
+
findall(Violation, proof_contract_symbol_violation(Violation), Violations0),
|
|
208
|
+
sort(Violations0, Violations).
|
|
209
|
+
|
|
210
|
+
proof_contract_symbol_violation(violation(
|
|
211
|
+
'proof-contract-symbols',
|
|
212
|
+
TestId,
|
|
213
|
+
Description,
|
|
214
|
+
Suggestion,
|
|
215
|
+
Source
|
|
216
|
+
)) :-
|
|
217
|
+
kb_entity(TestId, test, Props),
|
|
218
|
+
memberchk(proof_contract=RawContract, Props),
|
|
219
|
+
parse_json_object(RawContract, Contract),
|
|
220
|
+
required_proof_symbol_id(Contract, SymbolId),
|
|
221
|
+
\+ kb_entity(SymbolId, symbol, _),
|
|
222
|
+
format(string(Description), "proof_contract.required_proofs names unresolved symbol ~w", [SymbolId]),
|
|
223
|
+
Suggestion = "Point required_proofs.symbol_id at an existing symbol entity; Kibi does not infer TEST names from source filenames.",
|
|
224
|
+
violation_source(TestId, test, Source).
|
|
225
|
+
|
|
226
|
+
proof_contract_symbol_violation(violation(
|
|
227
|
+
'proof-contract-symbols',
|
|
228
|
+
TestId,
|
|
229
|
+
Description,
|
|
230
|
+
Suggestion,
|
|
231
|
+
Source
|
|
232
|
+
)) :-
|
|
233
|
+
kb_entity(TestId, test, Props),
|
|
234
|
+
memberchk(proof_contract=RawContract, Props),
|
|
235
|
+
parse_json_object(RawContract, Contract),
|
|
236
|
+
required_proof_symbol_id(Contract, SymbolId),
|
|
237
|
+
kb_entity(SymbolId, symbol, SymbolProps),
|
|
238
|
+
memberchk(symbol_role=RawRole, SymbolProps),
|
|
239
|
+
normalize_term_atom(RawRole, 'type-shape'),
|
|
240
|
+
format(string(Description), "proof_contract.required_proofs names type-shape symbol ~w; required proofs must not target structural contracts", [SymbolId]),
|
|
241
|
+
Suggestion = "Required proofs identify executable proof symbols. Use a production or executable-test symbol, not a type-shape contract.",
|
|
242
|
+
violation_source(TestId, test, Source).
|
|
243
|
+
|
|
244
|
+
proof_contract_symbol_violation(violation(
|
|
245
|
+
'proof-contract-symbols',
|
|
246
|
+
TestId,
|
|
247
|
+
Description,
|
|
248
|
+
Suggestion,
|
|
249
|
+
Source
|
|
250
|
+
)) :-
|
|
251
|
+
kb_entity(TestId, test, Props),
|
|
252
|
+
memberchk(proof_bindings=RawBindings, Props),
|
|
253
|
+
parse_json_list(RawBindings, Bindings),
|
|
254
|
+
member(Binding, Bindings),
|
|
255
|
+
is_dict(Binding),
|
|
256
|
+
(get_dict(source_file, Binding, RawBindingSource) ; get_dict(sourceFile, Binding, RawBindingSource)),
|
|
257
|
+
normalize_term_atom(RawBindingSource, BindingSource),
|
|
258
|
+
BindingSource \= '',
|
|
259
|
+
(get_dict(symbol_id, Binding, RawSymbolId) ; get_dict(symbolId, Binding, RawSymbolId)),
|
|
260
|
+
normalize_term_atom(RawSymbolId, SymbolId),
|
|
261
|
+
SymbolId \= '',
|
|
262
|
+
kb_entity(SymbolId, symbol, SymbolProps),
|
|
263
|
+
( memberchk(sourceFile=RawSymbolSource, SymbolProps)
|
|
264
|
+
-> normalize_term_atom(RawSymbolSource, SymbolSource)
|
|
265
|
+
; SymbolSource = ''
|
|
266
|
+
),
|
|
267
|
+
SymbolSource \= BindingSource,
|
|
268
|
+
format(string(Description), "proof_bindings.source_file for ~w is ~w but the symbol sourceFile is ~w", [SymbolId, BindingSource, SymbolSource]),
|
|
269
|
+
Suggestion = "Align proof_bindings.source_file with the symbol's sourceFile, or omit source_file. Kibi does not infer TEST names from filenames.",
|
|
270
|
+
violation_source(TestId, test, Source).
|
|
271
|
+
|
|
272
|
+
required_proof_symbol_id(Contract, SymbolId) :-
|
|
273
|
+
( get_dict(required_proofs, Contract, Raw)
|
|
274
|
+
; get_dict('required_proofs', Contract, Raw)
|
|
275
|
+
),
|
|
276
|
+
parse_json_list(Raw, Entries),
|
|
277
|
+
member(Entry, Entries),
|
|
278
|
+
( is_dict(Entry),
|
|
279
|
+
(get_dict(symbol_id, Entry, RawId) ; get_dict(symbolId, Entry, RawId))
|
|
280
|
+
; is_list(Entry),
|
|
281
|
+
memberchk(symbol_id=RawId, Entry)
|
|
282
|
+
),
|
|
283
|
+
normalize_term_atom(RawId, SymbolId),
|
|
284
|
+
SymbolId \= ''.
|
|
285
|
+
|
|
286
|
+
parse_json_object(^^(Value, _), Dict) :-
|
|
287
|
+
!,
|
|
288
|
+
parse_json_object(Value, Dict).
|
|
289
|
+
parse_json_object(literal(type(_, Value)), Dict) :-
|
|
290
|
+
!,
|
|
291
|
+
parse_json_object(Value, Dict).
|
|
292
|
+
parse_json_object(literal(Value), Dict) :-
|
|
293
|
+
!,
|
|
294
|
+
parse_json_object(Value, Dict).
|
|
295
|
+
parse_json_object(Dict, Dict) :-
|
|
296
|
+
is_dict(Dict),
|
|
297
|
+
!.
|
|
298
|
+
parse_json_object(Raw, Dict) :-
|
|
299
|
+
(atom(Raw) ; string(Raw)),
|
|
300
|
+
catch(atom_json_dict(Raw, Dict, [value_string_as(string)]), _, fail),
|
|
301
|
+
is_dict(Dict).
|
|
302
|
+
|
|
303
|
+
parse_json_list(Raw, Entries) :-
|
|
304
|
+
is_list(Raw),
|
|
305
|
+
!,
|
|
306
|
+
Entries = Raw.
|
|
307
|
+
parse_json_list(^^(Value, _), Entries) :-
|
|
308
|
+
!,
|
|
309
|
+
parse_json_list(Value, Entries).
|
|
310
|
+
parse_json_list(Raw, Entries) :-
|
|
311
|
+
(atom(Raw) ; string(Raw)),
|
|
312
|
+
catch(atom_json_dict(Raw, Entries, [value_string_as(string)]), _, fail),
|
|
313
|
+
is_list(Entries).
|
|
314
|
+
|
|
189
315
|
%% check_no_dangling_refs(-Violations)
|
|
190
316
|
% Finds all relationships referencing non-existent entities.
|
|
191
317
|
check_no_dangling_refs(Violations) :-
|
|
@@ -1478,7 +1604,8 @@ check_selected_dispatch(Rules, _{
|
|
|
1478
1604
|
rule_safety: RuleSafety,
|
|
1479
1605
|
rule_verifiability: RuleVerifiability,
|
|
1480
1606
|
semantic_completeness: SemanticCompleteness,
|
|
1481
|
-
req_status_vocabulary: ReqStatusVocabulary
|
|
1607
|
+
req_status_vocabulary: ReqStatusVocabulary,
|
|
1608
|
+
proof_contract_symbols: ProofContractSymbols
|
|
1482
1609
|
}) :-
|
|
1483
1610
|
selected_rule(Rules, 'must-priority-coverage', check_must_priority_coverage, MustPriority),
|
|
1484
1611
|
selected_rule(Rules, 'symbol-coverage', check_symbol_coverage, SymbolCoverage),
|
|
@@ -1496,7 +1623,8 @@ check_selected_dispatch(Rules, _{
|
|
|
1496
1623
|
selected_rule(Rules, 'rule-safety', check_rule_safety, RuleSafety),
|
|
1497
1624
|
selected_rule(Rules, 'rule-verifiability', check_rule_verifiability, RuleVerifiability),
|
|
1498
1625
|
selected_rule(Rules, 'semantic-completeness', check_semantic_completeness, SemanticCompleteness),
|
|
1499
|
-
selected_rule(Rules, 'req-status-vocabulary', check_req_status_vocabulary, ReqStatusVocabulary)
|
|
1626
|
+
selected_rule(Rules, 'req-status-vocabulary', check_req_status_vocabulary, ReqStatusVocabulary),
|
|
1627
|
+
selected_rule(Rules, 'proof-contract-symbols', check_proof_contract_symbols, ProofContractSymbols).
|
|
1500
1628
|
|
|
1501
1629
|
selected_rule(Rules, Name, Goal, Violations) :-
|
|
1502
1630
|
( memberchk(Name, Rules)
|
|
@@ -1552,6 +1680,7 @@ check_all_with_options(ViolationsDict, RequireAdr) :-
|
|
|
1552
1680
|
check_rule_verifiability(RuleVerifiability),
|
|
1553
1681
|
check_semantic_completeness(SemanticCompleteness),
|
|
1554
1682
|
check_req_status_vocabulary(ReqStatusVocabulary),
|
|
1683
|
+
check_proof_contract_symbols(ProofContractSymbols),
|
|
1555
1684
|
ViolationsDict = _{
|
|
1556
1685
|
must_priority_coverage: MustPriority,
|
|
1557
1686
|
symbol_coverage: SymbolCoverage,
|
|
@@ -1569,7 +1698,8 @@ check_all_with_options(ViolationsDict, RequireAdr) :-
|
|
|
1569
1698
|
rule_safety: RuleSafety,
|
|
1570
1699
|
rule_verifiability: RuleVerifiability,
|
|
1571
1700
|
semantic_completeness: SemanticCompleteness,
|
|
1572
|
-
req_status_vocabulary: ReqStatusVocabulary
|
|
1701
|
+
req_status_vocabulary: ReqStatusVocabulary,
|
|
1702
|
+
proof_contract_symbols: ProofContractSymbols
|
|
1573
1703
|
}.
|
|
1574
1704
|
|
|
1575
1705
|
%% violations_dict_to_json(+ViolationsDict, -JsonDict)
|
package/src/discovery.pl
CHANGED
|
@@ -8,6 +8,9 @@
|
|
|
8
8
|
coverage_report_json/11,
|
|
9
9
|
coverage_report_json/12,
|
|
10
10
|
coverage_evidence_json/5,
|
|
11
|
+
requirement_proof_json/5,
|
|
12
|
+
requirement_proof_json/7,
|
|
13
|
+
symbol_proof_json/7,
|
|
11
14
|
graph_expand_json/8
|
|
12
15
|
]).
|
|
13
16
|
|
|
@@ -40,6 +43,58 @@ coverage_report_json(By, Tags, IncludePassing, IncludeTransitive, Limit, Offset,
|
|
|
40
43
|
coverage_report_json(By, Tags, IncludePassing, IncludeTransitive, Limit, Offset, VerificationSnapshot, CheckedAt, MaxAgeSeconds, JsonString) :-
|
|
41
44
|
coverage_report_json(By, Tags, IncludePassing, strict_snapshot, _{}, IncludeTransitive, Limit, Offset, VerificationSnapshot, CheckedAt, MaxAgeSeconds, JsonString).
|
|
42
45
|
|
|
46
|
+
requirement_proof_json(ReqId, VerificationSnapshot, CheckedAt, MaxAgeSeconds, JsonString) :-
|
|
47
|
+
requirement_proof_json(ReqId, strict_snapshot, _{}, VerificationSnapshot, CheckedAt, MaxAgeSeconds, JsonString).
|
|
48
|
+
|
|
49
|
+
requirement_proof_json(ReqId, BindingMode, TestBindings, VerificationSnapshot, CheckedAt, MaxAgeSeconds, JsonString) :-
|
|
50
|
+
requirement_proof_context(VerificationSnapshot, CheckedAt, MaxAgeSeconds, BindingMode, TestBindings, Context),
|
|
51
|
+
kb_entity(ReqId, req, Props),
|
|
52
|
+
requirement_proof(ReqId, Props, Context, Proof),
|
|
53
|
+
put_dict(id, Proof, ReqId, ProofWithId),
|
|
54
|
+
dict_json_string(ProofWithId, JsonString).
|
|
55
|
+
|
|
56
|
+
symbol_proof_json(SymbolId, BindingMode, TestBindings, VerificationSnapshot, CheckedAt, MaxAgeSeconds, JsonString) :-
|
|
57
|
+
requirement_proof_context(VerificationSnapshot, CheckedAt, MaxAgeSeconds, BindingMode, TestBindings, Context),
|
|
58
|
+
kb_entity(SymbolId, symbol, _),
|
|
59
|
+
findall(ReqId,
|
|
60
|
+
(kb_relationship(implements, SymbolId, ReqId), kb_entity(ReqId, req, _)),
|
|
61
|
+
ReqIds0),
|
|
62
|
+
sort(ReqIds0, ReqIds),
|
|
63
|
+
maplist(requirement_proof_with_id(Context), ReqIds, Proofs),
|
|
64
|
+
symbol_explain_role(SymbolId, Role),
|
|
65
|
+
(kb:executable_test_symbol(SymbolId) -> ExecutableTest = true ; ExecutableTest = false),
|
|
66
|
+
findall(TestId, kb_relationship(executable_for, SymbolId, TestId), ExecutableFor0),
|
|
67
|
+
sort(ExecutableFor0, ExecutableFor),
|
|
68
|
+
findall(TestId, kb_relationship(covered_by, SymbolId, TestId), CoveredBy0),
|
|
69
|
+
sort(CoveredBy0, CoveredBy),
|
|
70
|
+
Response = _{
|
|
71
|
+
id: SymbolId,
|
|
72
|
+
symbolId: SymbolId,
|
|
73
|
+
role: Role,
|
|
74
|
+
executableTest: ExecutableTest,
|
|
75
|
+
implementingRequirements: ReqIds,
|
|
76
|
+
executableFor: ExecutableFor,
|
|
77
|
+
coveredBy: CoveredBy,
|
|
78
|
+
proofs: Proofs
|
|
79
|
+
},
|
|
80
|
+
dict_json_string(Response, JsonString).
|
|
81
|
+
|
|
82
|
+
requirement_proof_with_id(Context, ReqId, ProofWithId) :-
|
|
83
|
+
kb_entity(ReqId, req, Props),
|
|
84
|
+
requirement_proof(ReqId, Props, Context, Proof),
|
|
85
|
+
put_dict(id, Proof, ReqId, ProofWithId).
|
|
86
|
+
|
|
87
|
+
symbol_explain_role(SymbolId, executable_test) :-
|
|
88
|
+
kb:executable_test_symbol(SymbolId),
|
|
89
|
+
!.
|
|
90
|
+
symbol_explain_role(SymbolId, Role) :-
|
|
91
|
+
kb_entity(SymbolId, symbol, Props),
|
|
92
|
+
memberchk(symbol_role=RawRole, Props),
|
|
93
|
+
kb:normalize_term_atom(RawRole, Role),
|
|
94
|
+
Role \= '',
|
|
95
|
+
!.
|
|
96
|
+
symbol_explain_role(_SymbolId, unknown).
|
|
97
|
+
|
|
43
98
|
% Slim per-requirement proof evidence for quality diagnostics (W1 push-down).
|
|
44
99
|
% Projects only the fields the quality diagnostics consume - proof status,
|
|
45
100
|
% the passing-e2e stage summary, and receipt-related gap codes - instead of
|
package/src/requirement_proof.pl
CHANGED
|
@@ -77,10 +77,10 @@ requirement_proof(ReqId, ReqProps, Context, Proof) :-
|
|
|
77
77
|
logic_grounding_stage(ReqId, ReqProps, Inventory, Context, LogicStage),
|
|
78
78
|
contradiction_stage(ReqId, LogicStage.status, Context, ContradictionStage),
|
|
79
79
|
scenario_stage(ReqId, ScenarioStage, ScenarioIds),
|
|
80
|
-
scenario_test_stage(ScenarioIds, ScenarioTestStage,
|
|
80
|
+
scenario_test_stage(ScenarioIds, ScenarioTestStage, ScenarioTests),
|
|
81
81
|
passing_e2e_stage(ScenarioTestStage, Context, PassingE2eStage, PassingE2eTests),
|
|
82
82
|
executable_symbol_stage(PassingE2eTests, ExecutableStage, ExecutableSymbols),
|
|
83
|
-
production_symbol_stage(ReqId, PassingE2eTests, ProductionStage, ProductionSymbols),
|
|
83
|
+
production_symbol_stage(ReqId, PassingE2eTests, ScenarioTests, PassingE2eStage.receiptEvidence, Context, ProductionStage, ProductionSymbols),
|
|
84
84
|
source_coordinate_stage(ReqProps, ExecutableSymbols, ProductionSymbols, CoordinateStage),
|
|
85
85
|
Stages = _{
|
|
86
86
|
semanticInventory: SemanticStage,
|
|
@@ -103,7 +103,8 @@ requirement_proof(ReqId, ReqProps, Context, Proof) :-
|
|
|
103
103
|
proofGaps: Gaps,
|
|
104
104
|
proofAdvisories: Advisories,
|
|
105
105
|
proofRepairs: Repairs,
|
|
106
|
-
proofStages: Stages
|
|
106
|
+
proofStages: Stages,
|
|
107
|
+
testResolutions: ScenarioTestStage.resolutions
|
|
107
108
|
}.
|
|
108
109
|
|
|
109
110
|
exempt_requirement_proof(Reason, Proof) :-
|
|
@@ -532,6 +533,7 @@ scenario_test_stage(ScenarioIds, Stage, ScenarioTests) :-
|
|
|
532
533
|
; Status = passed
|
|
533
534
|
),
|
|
534
535
|
maplist(entity_source_ref, ScenarioTests, Sources),
|
|
536
|
+
maplist(test_resolution_record, ScenarioTests, Resolutions),
|
|
535
537
|
Stage = _{
|
|
536
538
|
status: Status,
|
|
537
539
|
scenarios: ScenarioIds,
|
|
@@ -539,7 +541,8 @@ scenario_test_stage(ScenarioIds, Stage, ScenarioTests) :-
|
|
|
539
541
|
scenarioTestTargets: ScenarioTestTargets,
|
|
540
542
|
invalidScenarioTestTargets: InvalidScenarioTestTargets,
|
|
541
543
|
sources: Sources,
|
|
542
|
-
obligations: Obligations
|
|
544
|
+
obligations: Obligations,
|
|
545
|
+
resolutions: Resolutions
|
|
543
546
|
}.
|
|
544
547
|
|
|
545
548
|
scenario_test_obligation(ScenarioId, Obligation) :-
|
|
@@ -1069,6 +1072,14 @@ test_missing_executable_symbol(TestId) :-
|
|
|
1069
1072
|
\+ (kb_relationship(executable_for, SymbolId, TestId), kb_entity(SymbolId, symbol, _)).
|
|
1070
1073
|
|
|
1071
1074
|
production_symbol_stage(ReqId, PassingE2eTests, Stage, ProductionSymbols) :-
|
|
1075
|
+
findall(TestId,
|
|
1076
|
+
(kb_relationship(specified_by, ReqId, ScenarioId),
|
|
1077
|
+
scenario_test(ScenarioId, TestId)),
|
|
1078
|
+
ScenarioTests0),
|
|
1079
|
+
sort(ScenarioTests0, ScenarioTests),
|
|
1080
|
+
production_symbol_stage(ReqId, PassingE2eTests, ScenarioTests, [], _{}, Stage, ProductionSymbols).
|
|
1081
|
+
|
|
1082
|
+
production_symbol_stage(ReqId, PassingE2eTests, ScenarioTests, ReceiptEvidence, Context, Stage, ProductionSymbols) :-
|
|
1072
1083
|
findall(SymbolId,
|
|
1073
1084
|
(kb_relationship(implements, SymbolId, ReqId),
|
|
1074
1085
|
kb_entity(SymbolId, symbol, _),
|
|
@@ -1080,12 +1091,16 @@ production_symbol_stage(ReqId, PassingE2eTests, Stage, ProductionSymbols) :-
|
|
|
1080
1091
|
include(symbol_not_covered_by_tests(PassingE2eTests), ProductionSymbols, UncoveredSymbols),
|
|
1081
1092
|
production_stage_status(ProductionSymbols, StructuralSymbols, PassingE2eTests, UncoveredSymbols, Status, StatusReason),
|
|
1082
1093
|
maplist(symbol_coordinate_ref, ProductionSymbols, Coordinates),
|
|
1094
|
+
production_coverage_explanations(
|
|
1095
|
+
ReqId, ProductionSymbols, StructuralSymbols, UncoveredSymbols,
|
|
1096
|
+
PassingE2eTests, ScenarioTests, ReceiptEvidence, Context, Explanations),
|
|
1083
1097
|
StageBase = _{
|
|
1084
1098
|
status: Status,
|
|
1085
1099
|
symbols: ProductionSymbols,
|
|
1086
1100
|
structuralSymbols: StructuralSymbols,
|
|
1087
1101
|
uncoveredSymbols: UncoveredSymbols,
|
|
1088
|
-
coordinates: Coordinates
|
|
1102
|
+
coordinates: Coordinates,
|
|
1103
|
+
explanations: Explanations
|
|
1089
1104
|
},
|
|
1090
1105
|
( Status == passed
|
|
1091
1106
|
-> Stage = StageBase
|
|
@@ -1127,6 +1142,271 @@ production_stage_status(ProductionSymbols, StructuralSymbols, PassingE2eTests, U
|
|
|
1127
1142
|
symbol_not_covered_by_tests(Tests, SymbolId) :-
|
|
1128
1143
|
\+ (member(TestId, Tests), kb_relationship(covered_by, SymbolId, TestId)).
|
|
1129
1144
|
|
|
1145
|
+
production_coverage_explanations(ReqId, ProductionSymbols, StructuralSymbols, UncoveredSymbols,
|
|
1146
|
+
PassingE2eTests, ScenarioTests, ReceiptEvidence, Context, Explanations) :-
|
|
1147
|
+
maplist(production_symbol_explanation(ReqId, UncoveredSymbols, PassingE2eTests, ScenarioTests, ReceiptEvidence, Context),
|
|
1148
|
+
ProductionSymbols, ProductionExplanations),
|
|
1149
|
+
maplist(structural_symbol_explanation(ReqId, PassingE2eTests, ScenarioTests, ReceiptEvidence, Context),
|
|
1150
|
+
StructuralSymbols, StructuralExplanations),
|
|
1151
|
+
append(ProductionExplanations, StructuralExplanations, Explanations).
|
|
1152
|
+
|
|
1153
|
+
structural_symbol_explanation(ReqId, PassingE2eTests, ScenarioTests, ReceiptEvidence, Context, SymbolId, Explanation) :-
|
|
1154
|
+
symbol_classification(SymbolId, Classification),
|
|
1155
|
+
symbol_coverage_candidates(structural, PassingE2eTests, ScenarioTests, ReceiptEvidence, Context, SymbolId, Candidates),
|
|
1156
|
+
explanation_reason_text(structural_unit_contract, ReasonText),
|
|
1157
|
+
Explanation = _{
|
|
1158
|
+
symbolId: SymbolId,
|
|
1159
|
+
requirementId: ReqId,
|
|
1160
|
+
classification: Classification,
|
|
1161
|
+
status: covered,
|
|
1162
|
+
reason: structural_unit_contract,
|
|
1163
|
+
reasonText: ReasonText,
|
|
1164
|
+
coverageCandidates: Candidates
|
|
1165
|
+
}.
|
|
1166
|
+
|
|
1167
|
+
production_symbol_explanation(ReqId, UncoveredSymbols, PassingE2eTests, ScenarioTests, ReceiptEvidence, Context, SymbolId, Explanation) :-
|
|
1168
|
+
symbol_classification(SymbolId, Classification),
|
|
1169
|
+
symbol_coverage_candidates(behavioral, PassingE2eTests, ScenarioTests, ReceiptEvidence, Context, SymbolId, Candidates),
|
|
1170
|
+
production_symbol_rollup(UncoveredSymbols, PassingE2eTests, SymbolId, Candidates, Status, Reason),
|
|
1171
|
+
explanation_reason_text(Reason, ReasonText),
|
|
1172
|
+
Explanation = _{
|
|
1173
|
+
symbolId: SymbolId,
|
|
1174
|
+
requirementId: ReqId,
|
|
1175
|
+
classification: Classification,
|
|
1176
|
+
status: Status,
|
|
1177
|
+
reason: Reason,
|
|
1178
|
+
reasonText: ReasonText,
|
|
1179
|
+
coverageCandidates: Candidates
|
|
1180
|
+
}.
|
|
1181
|
+
|
|
1182
|
+
production_symbol_rollup(_Uncovered, [], _SymbolId, _Candidates, uncovered, stage_blocked_no_passing_e2e) :-
|
|
1183
|
+
!.
|
|
1184
|
+
production_symbol_rollup(UncoveredSymbols, _Passing, SymbolId, Candidates, uncovered, Reason) :-
|
|
1185
|
+
memberchk(SymbolId, UncoveredSymbols),
|
|
1186
|
+
!,
|
|
1187
|
+
(Candidates == [] -> Reason = covered_by_missing ; Reason = no_qualifying_e2e_coverage).
|
|
1188
|
+
production_symbol_rollup(_Uncovered, _Passing, _SymbolId, _Candidates, covered, covered).
|
|
1189
|
+
|
|
1190
|
+
symbol_classification(SymbolId, Classification) :-
|
|
1191
|
+
kb_entity(SymbolId, symbol, Props),
|
|
1192
|
+
memberchk(symbol_role=RawRole, Props),
|
|
1193
|
+
normalize_atom(RawRole, Role),
|
|
1194
|
+
Role \= '',
|
|
1195
|
+
!,
|
|
1196
|
+
Classification = Role.
|
|
1197
|
+
symbol_classification(_SymbolId, unknown).
|
|
1198
|
+
|
|
1199
|
+
symbol_coverage_candidates(Mode, PassingE2eTests, ScenarioTests, ReceiptEvidence, Context, SymbolId, Candidates) :-
|
|
1200
|
+
findall(TestId, kb_relationship(covered_by, SymbolId, TestId), TestIds0),
|
|
1201
|
+
sort(TestIds0, TestIds),
|
|
1202
|
+
maplist(coverage_candidate_dict(Mode, PassingE2eTests, ScenarioTests, ReceiptEvidence, Context), TestIds, Candidates).
|
|
1203
|
+
|
|
1204
|
+
coverage_candidate_dict(Mode, PassingE2eTests, ScenarioTests, ReceiptEvidence, Context, TestId, Candidate) :-
|
|
1205
|
+
coverage_candidate_qualification(Mode, PassingE2eTests, ScenarioTests, ReceiptEvidence, Context, TestId, Qualifies, Primary, Secondaries),
|
|
1206
|
+
explanation_reason_text(Primary, ReasonText),
|
|
1207
|
+
cheap_candidate_scope(TestId, Scope),
|
|
1208
|
+
reused_receipt_state(ReceiptEvidence, TestId, ReceiptState),
|
|
1209
|
+
CandidateBase = _{
|
|
1210
|
+
testId: TestId,
|
|
1211
|
+
relationship: covered_by,
|
|
1212
|
+
qualifies: Qualifies,
|
|
1213
|
+
reason: Primary,
|
|
1214
|
+
reasonText: ReasonText,
|
|
1215
|
+
secondaryReasons: Secondaries,
|
|
1216
|
+
scope: Scope
|
|
1217
|
+
},
|
|
1218
|
+
( ReceiptState == none
|
|
1219
|
+
-> Candidate = CandidateBase
|
|
1220
|
+
; put_dict(receiptState, CandidateBase, ReceiptState, Candidate)
|
|
1221
|
+
).
|
|
1222
|
+
|
|
1223
|
+
% Shared candidate evaluator. Mode selects the coverage contract:
|
|
1224
|
+
% behavioral — production symbols require passing scenario-backed E2E
|
|
1225
|
+
% structural — type-shape symbols may also qualify via a unit contract
|
|
1226
|
+
coverage_candidate_qualification(_Mode, PassingE2eTests, _ScenarioTests, _ReceiptEvidence, _Context, TestId, true, covered, []) :-
|
|
1227
|
+
memberchk(TestId, PassingE2eTests),
|
|
1228
|
+
!.
|
|
1229
|
+
coverage_candidate_qualification(_Mode, _Passing, _Scenario, _Evidence, _Context, TestId, false, missing_test_entity, []) :-
|
|
1230
|
+
\+ kb_entity(TestId, test, _),
|
|
1231
|
+
!.
|
|
1232
|
+
coverage_candidate_qualification(structural, _Passing, _Scenario, _Evidence, _Context, TestId, true, structural_unit_contract, []) :-
|
|
1233
|
+
structural_unit_contract_test(TestId),
|
|
1234
|
+
!.
|
|
1235
|
+
coverage_candidate_qualification(_Mode, _Passing, ScenarioTests, ReceiptEvidence, Context, TestId, false, Primary, Secondaries) :-
|
|
1236
|
+
cheap_scope_reject_reason(TestId, ScopeReason),
|
|
1237
|
+
( \+ memberchk(TestId, ScenarioTests)
|
|
1238
|
+
-> Primary = test_not_in_requirement_scenario_chain,
|
|
1239
|
+
maybe_singleton(ScopeReason, ScopeSecondaries),
|
|
1240
|
+
reused_receipt_reject_reasons(ReceiptEvidence, TestId, ReceiptSecondaries),
|
|
1241
|
+
append(ScopeSecondaries, ReceiptSecondaries, Secondaries)
|
|
1242
|
+
; ScopeReason \= none
|
|
1243
|
+
-> Primary = ScopeReason,
|
|
1244
|
+
reused_receipt_reject_reasons(ReceiptEvidence, TestId, Secondaries)
|
|
1245
|
+
; receipt_reject_for_in_chain(ReceiptEvidence, Context, TestId, Primary, Secondaries)
|
|
1246
|
+
).
|
|
1247
|
+
|
|
1248
|
+
structural_unit_contract_test(TestId) :-
|
|
1249
|
+
kb_entity(TestId, test, TestProps),
|
|
1250
|
+
test_scope(TestProps, unit),
|
|
1251
|
+
memberchk(status=RawStatus, TestProps),
|
|
1252
|
+
normalize_atom(RawStatus, Status),
|
|
1253
|
+
memberchk(Status, [active, passing]).
|
|
1254
|
+
|
|
1255
|
+
cheap_candidate_scope(TestId, Scope) :-
|
|
1256
|
+
kb_entity(TestId, test, Props),
|
|
1257
|
+
!,
|
|
1258
|
+
test_scope(Props, Scope).
|
|
1259
|
+
cheap_candidate_scope(_TestId, unknown).
|
|
1260
|
+
|
|
1261
|
+
cheap_scope_reject_reason(TestId, Reason) :-
|
|
1262
|
+
cheap_candidate_scope(TestId, Scope),
|
|
1263
|
+
scope_reject_reason(Scope, Reason).
|
|
1264
|
+
|
|
1265
|
+
scope_reject_reason(unit, test_scope_is_unit) :- !.
|
|
1266
|
+
scope_reject_reason(end_to_end, none) :- !.
|
|
1267
|
+
scope_reject_reason(_Scope, test_scope_is_non_e2e).
|
|
1268
|
+
|
|
1269
|
+
maybe_singleton(none, []) :- !.
|
|
1270
|
+
maybe_singleton(Reason, [Reason]).
|
|
1271
|
+
|
|
1272
|
+
reused_receipt_state(Evidence, TestId, State) :-
|
|
1273
|
+
member(Item, Evidence),
|
|
1274
|
+
Item.testId == TestId,
|
|
1275
|
+
!,
|
|
1276
|
+
State = Item.state.
|
|
1277
|
+
reused_receipt_state(_Evidence, _TestId, none).
|
|
1278
|
+
|
|
1279
|
+
reused_receipt_item(Evidence, TestId, Item) :-
|
|
1280
|
+
member(Item, Evidence),
|
|
1281
|
+
Item.testId == TestId,
|
|
1282
|
+
!.
|
|
1283
|
+
|
|
1284
|
+
reused_receipt_reject_reasons(Evidence, TestId, Reasons) :-
|
|
1285
|
+
reused_receipt_item(Evidence, TestId, Item),
|
|
1286
|
+
!,
|
|
1287
|
+
receipt_item_reject_reason(Item, Reason),
|
|
1288
|
+
maybe_singleton(Reason, Reasons).
|
|
1289
|
+
reused_receipt_reject_reasons(_Evidence, _TestId, []).
|
|
1290
|
+
|
|
1291
|
+
receipt_reject_for_in_chain(Evidence, _Context, TestId, Primary, []) :-
|
|
1292
|
+
reused_receipt_item(Evidence, TestId, Item),
|
|
1293
|
+
!,
|
|
1294
|
+
receipt_item_reject_reason(Item, Reason),
|
|
1295
|
+
(Reason == none -> Primary = no_qualifying_e2e_coverage ; Primary = Reason).
|
|
1296
|
+
receipt_reject_for_in_chain(_Evidence, Context, TestId, Primary, []) :-
|
|
1297
|
+
is_dict(Context),
|
|
1298
|
+
Context \= _{},
|
|
1299
|
+
test_receipt_evidence(Context, TestId, Evidence),
|
|
1300
|
+
!,
|
|
1301
|
+
receipt_item_reject_reason(Evidence, Reason),
|
|
1302
|
+
(Reason == none -> Primary = no_qualifying_e2e_coverage ; Primary = Reason).
|
|
1303
|
+
receipt_reject_for_in_chain(_Evidence, _Context, _TestId, no_qualifying_e2e_coverage, []).
|
|
1304
|
+
|
|
1305
|
+
receipt_item_reject_reason(Item, stale_proof_receipt) :-
|
|
1306
|
+
Item.state == stale,
|
|
1307
|
+
get_dict(codeSnapshot, Item, _),
|
|
1308
|
+
!.
|
|
1309
|
+
receipt_item_reject_reason(Item, receipt_snapshot_mismatch) :-
|
|
1310
|
+
Item.state == stale,
|
|
1311
|
+
!.
|
|
1312
|
+
receipt_item_reject_reason(Item, Reason) :-
|
|
1313
|
+
receipt_state_reject_reason(Item.state, Reason).
|
|
1314
|
+
|
|
1315
|
+
receipt_state_reject_reason(missing, missing_proof_receipt) :- !.
|
|
1316
|
+
receipt_state_reject_reason(snapshot_unavailable, proof_snapshot_unavailable) :- !.
|
|
1317
|
+
receipt_state_reject_reason(contract_mismatch, receipt_contract_mismatch) :- !.
|
|
1318
|
+
receipt_state_reject_reason(invalid, invalid_proof_receipt) :- !.
|
|
1319
|
+
receipt_state_reject_reason(failed, failed_proof_receipt) :- !.
|
|
1320
|
+
receipt_state_reject_reason(not_end_to_end, none) :- !.
|
|
1321
|
+
receipt_state_reject_reason(stale, receipt_snapshot_mismatch) :- !.
|
|
1322
|
+
receipt_state_reject_reason(passed, none) :- !.
|
|
1323
|
+
receipt_state_reject_reason(none, none) :- !.
|
|
1324
|
+
receipt_state_reject_reason(_, none).
|
|
1325
|
+
|
|
1326
|
+
explanation_reason_text(Reason, Text) :-
|
|
1327
|
+
explanation_reason_text_def(Reason, Text),
|
|
1328
|
+
!.
|
|
1329
|
+
explanation_reason_text(Reason, Reason) :-
|
|
1330
|
+
atom(Reason).
|
|
1331
|
+
|
|
1332
|
+
explanation_reason_text_def(covered, "Qualifying covered_by to a passing scenario-backed E2E test").
|
|
1333
|
+
explanation_reason_text_def(covered_by_missing, "No covered_by relationship to any test").
|
|
1334
|
+
explanation_reason_text_def(no_qualifying_e2e_coverage, "covered_by relationships exist but none qualify as passing scenario-backed E2E coverage").
|
|
1335
|
+
explanation_reason_text_def(structural_unit_contract, "type-shape symbol is satisfied by a unit structural contract").
|
|
1336
|
+
explanation_reason_text_def(executable_test, "Symbol is executable test code linked with executable_for").
|
|
1337
|
+
explanation_reason_text_def(stage_blocked_no_passing_e2e, "Production coverage cannot be evaluated because no passing E2E evidence is available for the current snapshot").
|
|
1338
|
+
explanation_reason_text_def(missing_test_entity, "covered_by target is not a test entity").
|
|
1339
|
+
explanation_reason_text_def(test_not_in_requirement_scenario_chain, "Test is not in this requirement's scenario chain").
|
|
1340
|
+
explanation_reason_text_def(test_scope_is_unit, "Test verification_scope is unit, which cannot cover behavioral production symbols").
|
|
1341
|
+
explanation_reason_text_def(test_scope_is_non_e2e, "Test verification_scope is not end_to_end").
|
|
1342
|
+
explanation_reason_text_def(proof_snapshot_unavailable, "Workspace snapshot is unavailable so receipt currency cannot be proven").
|
|
1343
|
+
explanation_reason_text_def(missing_proof_receipt, "Scenario-backed E2E test has no kibi.proof-receipt.v1").
|
|
1344
|
+
explanation_reason_text_def(receipt_snapshot_mismatch, "Receipts exist but none match the current snapshot or binding").
|
|
1345
|
+
explanation_reason_text_def(stale_proof_receipt, "Current-snapshot receipt is older than the allowed proof age").
|
|
1346
|
+
explanation_reason_text_def(receipt_contract_mismatch, "Receipt does not match the current proof contract hash or fingerprint").
|
|
1347
|
+
explanation_reason_text_def(invalid_proof_receipt, "Receipt evidence is malformed or otherwise uncheckable").
|
|
1348
|
+
explanation_reason_text_def(failed_proof_receipt, "Latest current receipt outcome is not passed").
|
|
1349
|
+
|
|
1350
|
+
test_resolution_record(TestId, _{
|
|
1351
|
+
testId: TestId,
|
|
1352
|
+
requiredProofs: RequiredProofs,
|
|
1353
|
+
executableFor: ExecutableFor
|
|
1354
|
+
}) :-
|
|
1355
|
+
test_required_proof_refs(TestId, RequiredProofs),
|
|
1356
|
+
test_executable_for_refs(TestId, ExecutableFor).
|
|
1357
|
+
|
|
1358
|
+
test_required_proof_refs(TestId, Refs) :-
|
|
1359
|
+
kb_entity(TestId, test, Props),
|
|
1360
|
+
memberchk(proof_contract=RawContract, Props),
|
|
1361
|
+
proof_contract_dict(RawContract, Contract),
|
|
1362
|
+
contract_required_symbol_ids(Contract, SymbolIds),
|
|
1363
|
+
!,
|
|
1364
|
+
maplist(symbol_resolution_ref, SymbolIds, Refs).
|
|
1365
|
+
test_required_proof_refs(_TestId, []).
|
|
1366
|
+
|
|
1367
|
+
contract_required_symbol_ids(Contract, SymbolIds) :-
|
|
1368
|
+
( get_dict(required_proofs, Contract, Raw)
|
|
1369
|
+
; get_dict('required_proofs', Contract, Raw)
|
|
1370
|
+
),
|
|
1371
|
+
!,
|
|
1372
|
+
inventory_entries(Raw, Entries),
|
|
1373
|
+
findall(SymbolId,
|
|
1374
|
+
(member(Entry, Entries), required_proof_entry_symbol(Entry, SymbolId)),
|
|
1375
|
+
SymbolIds0),
|
|
1376
|
+
sort(SymbolIds0, SymbolIds).
|
|
1377
|
+
contract_required_symbol_ids(_, []).
|
|
1378
|
+
|
|
1379
|
+
required_proof_entry_symbol(Entry, SymbolId) :-
|
|
1380
|
+
( inventory_entry_field(Entry, symbol_id, Raw)
|
|
1381
|
+
; inventory_entry_field(Entry, symbolId, Raw)
|
|
1382
|
+
),
|
|
1383
|
+
normalize_atom(Raw, SymbolId),
|
|
1384
|
+
SymbolId \= ''.
|
|
1385
|
+
|
|
1386
|
+
test_executable_for_refs(TestId, Refs) :-
|
|
1387
|
+
findall(SymbolId,
|
|
1388
|
+
(kb_relationship(executable_for, SymbolId, TestId), kb_entity(SymbolId, symbol, _)),
|
|
1389
|
+
SymbolIds0),
|
|
1390
|
+
sort(SymbolIds0, SymbolIds),
|
|
1391
|
+
maplist(symbol_resolution_ref, SymbolIds, Refs).
|
|
1392
|
+
|
|
1393
|
+
symbol_resolution_ref(SymbolId, Ref) :-
|
|
1394
|
+
kb_entity(SymbolId, symbol, _),
|
|
1395
|
+
!,
|
|
1396
|
+
symbol_coordinate_ref(SymbolId, Coord),
|
|
1397
|
+
symbol_classification(SymbolId, Role),
|
|
1398
|
+
put_dict(_{role: Role, exists: true}, Coord, Ref).
|
|
1399
|
+
symbol_resolution_ref(SymbolId, _{
|
|
1400
|
+
id: SymbolId,
|
|
1401
|
+
sourceFile: '',
|
|
1402
|
+
sourceLine: 0,
|
|
1403
|
+
sourceColumn: 0,
|
|
1404
|
+
sourceEndLine: 0,
|
|
1405
|
+
sourceEndColumn: 0,
|
|
1406
|
+
role: unresolved,
|
|
1407
|
+
exists: false
|
|
1408
|
+
}).
|
|
1409
|
+
|
|
1130
1410
|
source_coordinate_stage(ReqProps, ExecutableSymbols, ProductionSymbols, Stage) :-
|
|
1131
1411
|
append(ExecutableSymbols, ProductionSymbols, Symbols0),
|
|
1132
1412
|
sort(Symbols0, Symbols),
|
package/src/rule_registry.pl
CHANGED
|
@@ -33,6 +33,7 @@ known_rule('strict-req-fact-pairing').
|
|
|
33
33
|
known_rule('predicate-verifiability').
|
|
34
34
|
known_rule('strict-readiness').
|
|
35
35
|
known_rule('semantic-completeness').
|
|
36
|
+
known_rule('proof-contract-symbols').
|
|
36
37
|
|
|
37
38
|
rule_enforcement_class('must-priority-coverage', canonical).
|
|
38
39
|
rule_enforcement_class('symbol-coverage', canonical).
|
|
@@ -53,6 +54,7 @@ rule_enforcement_class('strict-req-fact-pairing', advisory).
|
|
|
53
54
|
rule_enforcement_class('predicate-verifiability', advisory).
|
|
54
55
|
rule_enforcement_class('strict-readiness', migration).
|
|
55
56
|
rule_enforcement_class('semantic-completeness', migration).
|
|
57
|
+
rule_enforcement_class('proof-contract-symbols', advisory).
|
|
56
58
|
|
|
57
59
|
rule_implementation('must-priority-coverage', prolog).
|
|
58
60
|
rule_implementation('symbol-coverage', prolog).
|
|
@@ -73,6 +75,7 @@ rule_implementation('strict-req-fact-pairing', prolog).
|
|
|
73
75
|
rule_implementation('predicate-verifiability', prolog).
|
|
74
76
|
rule_implementation('strict-readiness', prolog).
|
|
75
77
|
rule_implementation('semantic-completeness', prolog).
|
|
78
|
+
rule_implementation('proof-contract-symbols', prolog).
|
|
76
79
|
|
|
77
80
|
rule_predicate('must-priority-coverage', check_must_priority_coverage).
|
|
78
81
|
rule_predicate('symbol-coverage', check_symbol_coverage).
|
|
@@ -91,6 +94,7 @@ rule_predicate('strict-req-fact-pairing', check_strict_req_fact_pairing).
|
|
|
91
94
|
rule_predicate('predicate-verifiability', check_predicate_verifiability).
|
|
92
95
|
rule_predicate('strict-readiness', check_strict_readiness).
|
|
93
96
|
rule_predicate('semantic-completeness', check_semantic_completeness).
|
|
97
|
+
rule_predicate('proof-contract-symbols', check_proof_contract_symbols).
|
|
94
98
|
|
|
95
99
|
rule_predicate_arity('must-priority-coverage', 1).
|
|
96
100
|
rule_predicate_arity('symbol-coverage', 1).
|
|
@@ -109,6 +113,7 @@ rule_predicate_arity('strict-req-fact-pairing', 1).
|
|
|
109
113
|
rule_predicate_arity('predicate-verifiability', 1).
|
|
110
114
|
rule_predicate_arity('strict-readiness', 1).
|
|
111
115
|
rule_predicate_arity('semantic-completeness', 1).
|
|
116
|
+
rule_predicate_arity('proof-contract-symbols', 1).
|
|
112
117
|
|
|
113
118
|
rule_description('must-priority-coverage', 'Every must-priority requirement must have a scenario and a test').
|
|
114
119
|
rule_description('symbol-coverage', 'Production symbols need qualifying coverage via covered_by plus a canonical requirement/scenario test path').
|
|
@@ -129,3 +134,4 @@ rule_description('strict-req-fact-pairing', 'Detect requirements with incomplete
|
|
|
129
134
|
rule_description('predicate-verifiability', 'Detect requires_predicate links that do not target ground fact_kind: predicate facts').
|
|
130
135
|
rule_description('strict-readiness', 'Report strict contradiction-readiness levels for requirements that are still prose-only or otherwise not contradiction-ready').
|
|
131
136
|
rule_description('semantic-completeness', 'Every inventoried assertive proposition is modeled or explicitly classified').
|
|
137
|
+
rule_description('proof-contract-symbols', 'Detect unresolved required_proofs.symbol_id values, type-shape required proofs, and proof_bindings.source_file disagreement with the named symbol sourceFile').
|