kibi-core 0.22.4 → 0.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/certificate_check.pl +756 -0
- package/src/certificates.pl +237 -0
- package/src/checks.pl +19 -4
- package/src/discovery.pl +17 -8
- package/src/kb.pl +32 -19
- package/src/kb_consistency.pl +213 -0
- package/src/requirement_proof.pl +90 -7
- package/src/rule_registry.pl +12 -0
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
% Module: kb_consistency
|
|
2
|
+
% implements REQ-kibi-kb-certificates
|
|
3
|
+
% The kb-consistency check rules. Each run produces the certificate bundle
|
|
4
|
+
% (certificates.pl), round-trips it through JSON exactly as a saved bundle
|
|
5
|
+
% would be read, and re-checks it with the independent checker
|
|
6
|
+
% (certificate_check.pl). Only what the checker accepts becomes a finding:
|
|
7
|
+
%
|
|
8
|
+
% kb-consistency a re-verified contradiction (an irreducible
|
|
9
|
+
% core of property facts, two facts that
|
|
10
|
+
% disagree on an atom or a decision, or a
|
|
11
|
+
% negation cycle among stored rules), or a
|
|
12
|
+
% certificate the checker rejects
|
|
13
|
+
% kb-consistency-unresolved a context the checker cannot compare (mixed
|
|
14
|
+
% types or units, unsupported operators or
|
|
15
|
+
% polarities, windows that never overlap)
|
|
16
|
+
:- module(kb_consistency, [
|
|
17
|
+
check_kb_consistency/1,
|
|
18
|
+
check_kb_consistency_unresolved/1
|
|
19
|
+
]).
|
|
20
|
+
|
|
21
|
+
:- encoding(utf8).
|
|
22
|
+
|
|
23
|
+
:- use_module(library(apply)).
|
|
24
|
+
:- use_module(library(lists)).
|
|
25
|
+
:- use_module(library(http/json)).
|
|
26
|
+
:- use_module('kb.pl', [kb_entity/3, kb_relationship/3]).
|
|
27
|
+
:- use_module('certificates.pl', [kb_certificates/1]).
|
|
28
|
+
:- use_module('certificate_check.pl', [verify_certificates/2]).
|
|
29
|
+
|
|
30
|
+
%% check_kb_consistency(-Violations)
|
|
31
|
+
check_kb_consistency(Violations) :-
|
|
32
|
+
consistency_verification(Bundle, Report),
|
|
33
|
+
numbered_certificates(Bundle, Numbered),
|
|
34
|
+
findall(Index, (member(Failure, Report.rejected), get_dict(certificate, Failure, Index)), Rejected),
|
|
35
|
+
findall(Violation,
|
|
36
|
+
( member(Index-Certificate, Numbered),
|
|
37
|
+
\+ memberchk(Index, Rejected),
|
|
38
|
+
contradiction_violation(Certificate, Violation)
|
|
39
|
+
),
|
|
40
|
+
Contradictions),
|
|
41
|
+
findall(Violation,
|
|
42
|
+
( member(Failure, Report.rejected),
|
|
43
|
+
rejected_violation(Failure, Violation)
|
|
44
|
+
),
|
|
45
|
+
RejectedViolations),
|
|
46
|
+
append(Contradictions, RejectedViolations, Unsorted),
|
|
47
|
+
sort(Unsorted, Violations).
|
|
48
|
+
|
|
49
|
+
%% check_kb_consistency_unresolved(-Violations)
|
|
50
|
+
check_kb_consistency_unresolved(Violations) :-
|
|
51
|
+
consistency_verification(_Bundle, Report),
|
|
52
|
+
findall(Violation,
|
|
53
|
+
( member(Theorem, Report.theorems),
|
|
54
|
+
get_dict(unresolved, Theorem, Entries),
|
|
55
|
+
member(Entry, Entries),
|
|
56
|
+
unresolved_violation(Theorem.theorem, Entry, Violation)
|
|
57
|
+
),
|
|
58
|
+
Unsorted),
|
|
59
|
+
sort(Unsorted, Violations).
|
|
60
|
+
|
|
61
|
+
consistency_verification(Bundle, Report) :-
|
|
62
|
+
kb_certificates(Produced),
|
|
63
|
+
with_output_to(string(Json), json_write_dict(current_output, Produced, [width(0)])),
|
|
64
|
+
open_string(Json, Stream),
|
|
65
|
+
json_read_dict(Stream, Bundle, [value_string_as(string)]),
|
|
66
|
+
verify_certificates(Bundle, Report).
|
|
67
|
+
|
|
68
|
+
numbered_certificates(Bundle, Numbered) :-
|
|
69
|
+
findall(Index-Certificate, nth1(Index, Bundle.certificates, Certificate), Numbered).
|
|
70
|
+
|
|
71
|
+
% ------------------------------------------------------------------
|
|
72
|
+
% Re-verified contradictions
|
|
73
|
+
% ------------------------------------------------------------------
|
|
74
|
+
|
|
75
|
+
contradiction_violation(Certificate, violation('kb-consistency', EntityId, Description, Suggestion, "", _{certificate: Certificate})) :-
|
|
76
|
+
get_dict(kind, Certificate, KindText),
|
|
77
|
+
atom_string(Kind, KindText),
|
|
78
|
+
contradiction_text(Kind, Certificate, Facts, Description),
|
|
79
|
+
fact_requirements(Facts, Requirements),
|
|
80
|
+
entity_label(Requirements, Facts, EntityId),
|
|
81
|
+
suggestion_text(Kind, Requirements, Suggestion).
|
|
82
|
+
|
|
83
|
+
contradiction_text(property_contradiction, Certificate, Facts, Description) :-
|
|
84
|
+
Facts = Certificate.core,
|
|
85
|
+
context_text(Certificate, Context),
|
|
86
|
+
atomic_list_concat(Facts, ', ', FactText),
|
|
87
|
+
findall(Text,
|
|
88
|
+
( member(Drop, Certificate.dropModels),
|
|
89
|
+
format(string(Text), "~w: ~w", [Drop.factId, Drop.value])
|
|
90
|
+
),
|
|
91
|
+
DropTexts),
|
|
92
|
+
atomic_list_concat(DropTexts, ', ', DropText),
|
|
93
|
+
format(string(Description),
|
|
94
|
+
"Facts ~w cannot all hold for ~w: no value satisfies them together, and dropping any one leaves a value (~w)",
|
|
95
|
+
[FactText, Context, DropText]).
|
|
96
|
+
contradiction_text(predicate_contradiction, Certificate, Facts, Description) :-
|
|
97
|
+
Facts = Certificate.core,
|
|
98
|
+
Facts = [FactA, FactB],
|
|
99
|
+
atom_text(Certificate, Atom),
|
|
100
|
+
format(string(Description),
|
|
101
|
+
"Facts ~w and ~w give ~w opposite truth values", [FactA, FactB, Atom]).
|
|
102
|
+
contradiction_text(decision_contradiction, Certificate, Facts, Description) :-
|
|
103
|
+
Facts = Certificate.core,
|
|
104
|
+
Facts = [FactA, FactB],
|
|
105
|
+
decision_text(Certificate, Decision),
|
|
106
|
+
format(string(Description),
|
|
107
|
+
"Facts ~w and ~w decide ~w differently: one allows it and the other denies it", [FactA, FactB, Decision]).
|
|
108
|
+
contradiction_text(rule_negation_cycle, Certificate, [], Description) :-
|
|
109
|
+
findall(Text,
|
|
110
|
+
( member(Edge, Certificate.cycle),
|
|
111
|
+
signature_text(Edge.head, Head),
|
|
112
|
+
signature_text(Edge.body, Body),
|
|
113
|
+
( atom_string(negative, Edge.sign) -> Arrow = "depends on not" ; Arrow = "depends on" ),
|
|
114
|
+
format(string(Text), "~w ~w ~w", [Head, Arrow, Body])
|
|
115
|
+
),
|
|
116
|
+
Texts),
|
|
117
|
+
atomic_list_concat(Texts, "; ", CycleText),
|
|
118
|
+
format(string(Description),
|
|
119
|
+
"Stored rules are not stratified: a predicate depends on its own negation (~w)", [CycleText]).
|
|
120
|
+
|
|
121
|
+
suggestion_text(rule_negation_cycle, _Requirements,
|
|
122
|
+
"Break the cycle: change one rule so no predicate depends on its own negation, or supersede the requirement that stores it") :- !.
|
|
123
|
+
suggestion_text(_Kind, Requirements, Suggestion) :-
|
|
124
|
+
atomic_list_concat(Requirements, ', ', RequirementText),
|
|
125
|
+
format(string(Suggestion),
|
|
126
|
+
"Supersede or change one of ~w so the facts can hold together; kibi check --write-certificates <file> saves the certificate",
|
|
127
|
+
[RequirementText]).
|
|
128
|
+
|
|
129
|
+
% ------------------------------------------------------------------
|
|
130
|
+
% Rejected certificates and unresolved contexts
|
|
131
|
+
% ------------------------------------------------------------------
|
|
132
|
+
|
|
133
|
+
rejected_violation(Failure, violation('kb-consistency', "kb-certificates", Description,
|
|
134
|
+
"Kibi produced a certificate its own checker rejects. Report it with the bundle from kibi check --write-certificates <file>", "")) :-
|
|
135
|
+
( get_dict(group, Failure, Group)
|
|
136
|
+
-> group_text(Group, Where)
|
|
137
|
+
; format(string(Where), "certificate ~w (~w)", [Failure.certificate, Failure.kind])
|
|
138
|
+
),
|
|
139
|
+
format(string(Description),
|
|
140
|
+
"The independent checker rejected ~w: ~w", [Where, Failure.reason]).
|
|
141
|
+
|
|
142
|
+
unresolved_violation(_Theorem, Entry, violation('kb-consistency-unresolved', EntityId, Description, Suggestion, "")) :-
|
|
143
|
+
group_text(Entry, Where),
|
|
144
|
+
( get_dict(reason, Entry, Reason)
|
|
145
|
+
-> true
|
|
146
|
+
; Reason = "the contradicting facts' validity windows never overlap"
|
|
147
|
+
),
|
|
148
|
+
entry_facts(Entry, Facts),
|
|
149
|
+
fact_requirements(Facts, Requirements),
|
|
150
|
+
entity_label(Requirements, Facts, EntityId0),
|
|
151
|
+
( EntityId0 == "" -> EntityId = Where ; EntityId = EntityId0 ),
|
|
152
|
+
format(string(Description),
|
|
153
|
+
"Kibi cannot decide whether the facts for ~w hold together: ~w. It never counts as consistent", [Where, Reason]),
|
|
154
|
+
Suggestion = "Align the facts' value_type, unit, operator or polarity so they can be compared, or accept that this context stays unchecked".
|
|
155
|
+
|
|
156
|
+
entry_facts(Entry, Facts) :-
|
|
157
|
+
get_dict(subjectKey, Entry, Subject),
|
|
158
|
+
!,
|
|
159
|
+
get_dict(propertyKey, Entry, Property),
|
|
160
|
+
findall(Fact,
|
|
161
|
+
( kb:current_req(Req),
|
|
162
|
+
kb:effective_req_property_fact(Req, Subject, Fact, Property, _, _, _, _, _, _, _, _)
|
|
163
|
+
),
|
|
164
|
+
Facts0),
|
|
165
|
+
sort(Facts0, Facts).
|
|
166
|
+
entry_facts(_Entry, []).
|
|
167
|
+
|
|
168
|
+
% ------------------------------------------------------------------
|
|
169
|
+
% Text helpers
|
|
170
|
+
% ------------------------------------------------------------------
|
|
171
|
+
|
|
172
|
+
fact_requirements(Facts, Requirements) :-
|
|
173
|
+
findall(Req,
|
|
174
|
+
( member(FactText, Facts),
|
|
175
|
+
atom_string(Fact, FactText),
|
|
176
|
+
member(Type, [requires_property, requires_predicate, requires_rule]),
|
|
177
|
+
kb_relationship(Type, Req, Fact),
|
|
178
|
+
kb:current_req(Req)
|
|
179
|
+
),
|
|
180
|
+
Requirements0),
|
|
181
|
+
sort(Requirements0, Requirements).
|
|
182
|
+
|
|
183
|
+
entity_label([], [], "") :- !.
|
|
184
|
+
entity_label([], Facts, Label) :- !, atomic_list_concat(Facts, '/', Label0), atom_string(Label0, Label).
|
|
185
|
+
entity_label(Requirements, _Facts, Label) :- atomic_list_concat(Requirements, '/', Label0), atom_string(Label0, Label).
|
|
186
|
+
|
|
187
|
+
context_text(Label, Text) :-
|
|
188
|
+
( get_dict(scope, Label, Scope0) -> atom_string(Scope0, Scope) ; Scope = "" ),
|
|
189
|
+
( Scope == ""
|
|
190
|
+
-> format(string(Text), "~w.~w", [Label.subjectKey, Label.propertyKey])
|
|
191
|
+
; format(string(Text), "~w.~w (scope ~w)", [Label.subjectKey, Label.propertyKey, Scope])
|
|
192
|
+
).
|
|
193
|
+
|
|
194
|
+
group_text(Group, Text) :-
|
|
195
|
+
( get_dict(subjectKey, Group, _)
|
|
196
|
+
-> context_text(Group, Text)
|
|
197
|
+
; get_dict(args, Group, _)
|
|
198
|
+
-> atom_text(Group, Text)
|
|
199
|
+
; get_dict(key, Group, _)
|
|
200
|
+
-> decision_text(Group, Text)
|
|
201
|
+
; format(string(Text), "~w", [Group])
|
|
202
|
+
).
|
|
203
|
+
|
|
204
|
+
atom_text(Label, Text) :-
|
|
205
|
+
atomic_list_concat(Label.args, ', ', Args),
|
|
206
|
+
format(string(Text), "~w:~w(~w)", [Label.namespace, Label.name, Args]).
|
|
207
|
+
|
|
208
|
+
decision_text(Label, Text) :-
|
|
209
|
+
atomic_list_concat(Label.key, ', ', Key),
|
|
210
|
+
format(string(Text), "~w:~w/~w for (~w)", [Label.namespace, Label.name, Label.arity, Key]).
|
|
211
|
+
|
|
212
|
+
signature_text(Signature, Text) :-
|
|
213
|
+
format(string(Text), "~w:~w/~w", [Signature.namespace, Signature.name, Signature.arity]).
|
package/src/requirement_proof.pl
CHANGED
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
]).
|
|
26
26
|
:- use_module('logic_ir.pl', [logic_rule_safety/2]).
|
|
27
27
|
|
|
28
|
-
proof_version('kibi.requirement-proof.
|
|
28
|
+
proof_version('kibi.requirement-proof.v4').
|
|
29
29
|
|
|
30
30
|
requirement_proof_context(Context) :-
|
|
31
31
|
requirement_proof_context(unknown, '1970-01-01T00:00:00Z', 604800, Context).
|
|
@@ -71,12 +71,12 @@ requirement_proof(ReqId, ReqProps, _Context, Proof) :-
|
|
|
71
71
|
kb:current_req(ReqId),
|
|
72
72
|
proof_exempt(ReqProps, Reason),
|
|
73
73
|
!,
|
|
74
|
-
exempt_requirement_proof(Reason, Proof).
|
|
75
|
-
requirement_proof(ReqId,
|
|
74
|
+
exempt_requirement_proof(Reason, ReqProps, Proof).
|
|
75
|
+
requirement_proof(ReqId, ReqProps, _Context, Proof) :-
|
|
76
76
|
\+ kb:current_req(ReqId),
|
|
77
77
|
!,
|
|
78
78
|
applicability_exclusion_reason(ReqId, Reason),
|
|
79
|
-
exempt_requirement_proof(Reason, Proof).
|
|
79
|
+
exempt_requirement_proof(Reason, ReqProps, Proof).
|
|
80
80
|
requirement_proof(ReqId, ReqProps, Context, Proof) :-
|
|
81
81
|
semantic_inventory_stage(ReqProps, SemanticStage, Inventory),
|
|
82
82
|
logic_grounding_stage(ReqId, ReqProps, Inventory, Context, LogicStage),
|
|
@@ -101,10 +101,13 @@ requirement_proof(ReqId, ReqProps, Context, Proof) :-
|
|
|
101
101
|
proof_issues(Stages, Gaps, Advisories),
|
|
102
102
|
proof_repairs(Gaps, Repairs),
|
|
103
103
|
proof_status(Stages, Status),
|
|
104
|
+
requirement_evidence(ReqProps, Status, PassingE2eTests, EvidenceLevel, Evidence),
|
|
104
105
|
proof_version(Version),
|
|
105
106
|
Proof = _{
|
|
106
107
|
proofVersion: Version,
|
|
107
108
|
proofStatus: Status,
|
|
109
|
+
evidenceLevel: EvidenceLevel,
|
|
110
|
+
evidence: Evidence,
|
|
108
111
|
proofGaps: Gaps,
|
|
109
112
|
proofAdvisories: Advisories,
|
|
110
113
|
proofRepairs: Repairs,
|
|
@@ -112,11 +115,14 @@ requirement_proof(ReqId, ReqProps, Context, Proof) :-
|
|
|
112
115
|
testResolutions: ScenarioTestStage.resolutions
|
|
113
116
|
}.
|
|
114
117
|
|
|
115
|
-
exempt_requirement_proof(Reason, Proof) :-
|
|
118
|
+
exempt_requirement_proof(Reason, ReqProps, Proof) :-
|
|
119
|
+
requirement_evidence(ReqProps, not_applicable, [], EvidenceLevel, Evidence),
|
|
116
120
|
proof_version(Version),
|
|
117
121
|
Proof = _{
|
|
118
122
|
proofVersion: Version,
|
|
119
123
|
proofStatus: not_applicable,
|
|
124
|
+
evidenceLevel: EvidenceLevel,
|
|
125
|
+
evidence: Evidence,
|
|
120
126
|
proofGaps: [],
|
|
121
127
|
proofAdvisories: [],
|
|
122
128
|
proofRepairs: [],
|
|
@@ -1674,7 +1680,84 @@ proof_status(Stages, unresolved) :-
|
|
|
1674
1680
|
stage_dict(Stages, Stage),
|
|
1675
1681
|
memberchk(Stage.status, [unresolved, blocked]),
|
|
1676
1682
|
!.
|
|
1677
|
-
proof_status(_,
|
|
1683
|
+
proof_status(_, tested).
|
|
1684
|
+
|
|
1685
|
+
% implements REQ-kibi-evidence-levels
|
|
1686
|
+
%% requirement_evidence(+Props, +ProofStatus, +PassingE2eTests, -Level, -Evidence)
|
|
1687
|
+
% How strong the evidence behind a requirement is, never stronger than what
|
|
1688
|
+
% backs it. Levels, strongest first: proved (a re-checkable certificate
|
|
1689
|
+
% covers every case), tested (passing E2E receipts cover every linked
|
|
1690
|
+
% scenario: proofStatus tested), asserted (a person stands behind the
|
|
1691
|
+
% statement but nothing current checks it), unverified (an agent wrote it and
|
|
1692
|
+
% no person approved it). No proof kind issues certificates yet, so proved
|
|
1693
|
+
% is never reached here. Evidence lists what earned the level and anything
|
|
1694
|
+
% weaker that also holds, each entry naming its kind and scope.
|
|
1695
|
+
requirement_evidence(Props, ProofStatus, PassingE2eTests, Level, Evidence) :-
|
|
1696
|
+
findall(Entry, evidence_entry(Props, ProofStatus, PassingE2eTests, Entry), Evidence),
|
|
1697
|
+
evidence_level(Evidence, Level).
|
|
1698
|
+
|
|
1699
|
+
evidence_entry(_Props, tested, PassingE2eTests, _{kind: e2e, scope: code, level: tested, tests: PassingE2eTests}).
|
|
1700
|
+
evidence_entry(Props, _ProofStatus, _PassingE2eTests, Entry) :-
|
|
1701
|
+
human_intent(Props, Basis, Ref),
|
|
1702
|
+
( Ref == ''
|
|
1703
|
+
-> Entry = _{kind: human_intent, scope: intent, level: asserted, basis: Basis}
|
|
1704
|
+
; Entry = _{kind: human_intent, scope: intent, level: asserted, basis: Basis, ref: Ref}
|
|
1705
|
+
).
|
|
1706
|
+
|
|
1707
|
+
evidence_level(Evidence, Level) :-
|
|
1708
|
+
( member(Level0, [proved, tested, asserted]),
|
|
1709
|
+
member(Entry, Evidence),
|
|
1710
|
+
get_dict(level, Entry, Level0)
|
|
1711
|
+
-> Level = Level0
|
|
1712
|
+
; Level = unverified
|
|
1713
|
+
).
|
|
1714
|
+
|
|
1715
|
+
%% human_intent(+Props, -Basis, -Ref)
|
|
1716
|
+
% A person stands behind the requirement: someone approved it
|
|
1717
|
+
% (origin.approved_by, the field agent-requirement-unapproved reads), or it
|
|
1718
|
+
% reads as firm (firmness: firm, or no firmness and an origin other than
|
|
1719
|
+
% agent, as for decision firmness). The top-level approved_by approves an
|
|
1720
|
+
% exception; an agent can write it, so on its own it approves nothing here.
|
|
1721
|
+
human_intent(Props, approved, Ref) :-
|
|
1722
|
+
origin_field(Props, approved_by, _),
|
|
1723
|
+
!,
|
|
1724
|
+
intent_ref(Props, Ref).
|
|
1725
|
+
human_intent(Props, firm, Ref) :-
|
|
1726
|
+
effective_firmness(Props, firm),
|
|
1727
|
+
!,
|
|
1728
|
+
intent_ref(Props, Ref).
|
|
1729
|
+
|
|
1730
|
+
effective_firmness(Props, Firmness) :-
|
|
1731
|
+
( memberchk(firmness=Raw, Props),
|
|
1732
|
+
normalize_atom(Raw, Explicit),
|
|
1733
|
+
memberchk(Explicit, [firm, default, exploratory])
|
|
1734
|
+
-> Firmness = Explicit
|
|
1735
|
+
; origin_field(Props, kind, agent)
|
|
1736
|
+
-> Firmness = default
|
|
1737
|
+
; Firmness = firm
|
|
1738
|
+
).
|
|
1739
|
+
|
|
1740
|
+
intent_ref(Props, Ref) :-
|
|
1741
|
+
( origin_field(Props, ref, Ref0)
|
|
1742
|
+
-> Ref = Ref0
|
|
1743
|
+
; prop_text(Props, approval_ref, Ref0)
|
|
1744
|
+
-> Ref = Ref0
|
|
1745
|
+
; Ref = ''
|
|
1746
|
+
).
|
|
1747
|
+
|
|
1748
|
+
prop_text(Props, Key, Text) :-
|
|
1749
|
+
Prop =.. [=, Key, Raw],
|
|
1750
|
+
memberchk(Prop, Props),
|
|
1751
|
+
normalize_atom(Raw, Text),
|
|
1752
|
+
Text \== ''.
|
|
1753
|
+
|
|
1754
|
+
origin_field(Props, Key, Value) :-
|
|
1755
|
+
memberchk(origin=Raw, Props),
|
|
1756
|
+
normalize_atom(Raw, Json),
|
|
1757
|
+
catch(atom_json_dict(Json, Origin, [value_string_as(atom)]), _, fail),
|
|
1758
|
+
get_dict(Key, Origin, Value0),
|
|
1759
|
+
normalize_atom(Value0, Value),
|
|
1760
|
+
Value \== ''.
|
|
1678
1761
|
|
|
1679
1762
|
stage_dict(Stages, Stage) :-
|
|
1680
1763
|
dict_pairs(Stages, _, Pairs),
|
|
@@ -1690,7 +1773,7 @@ proof_advisories(Stages, Advisories) :-
|
|
|
1690
1773
|
% Every linked scenario is a mandatory proof obligation. Receipt-completeness
|
|
1691
1774
|
% failures therefore remain blocking even when another scenario has a passing
|
|
1692
1775
|
% receipt; unit/integration-only evidence stays nonblocking in the E2E lane.
|
|
1693
|
-
% Invariant: proofStatus ==
|
|
1776
|
+
% Invariant: proofStatus == tested implies proofGaps == [].
|
|
1694
1777
|
proof_issues(Stages, Gaps, Advisories) :-
|
|
1695
1778
|
findall(Gap,
|
|
1696
1779
|
(gap_definition(Gap, _, _, _),
|
package/src/rule_registry.pl
CHANGED
|
@@ -67,6 +67,8 @@ known_rule('supersedes-reason-missing').
|
|
|
67
67
|
known_rule('supersedes-reason-acknowledged').
|
|
68
68
|
known_rule('policy-ownership').
|
|
69
69
|
known_rule('policy-markers').
|
|
70
|
+
known_rule('kb-consistency').
|
|
71
|
+
known_rule('kb-consistency-unresolved').
|
|
70
72
|
|
|
71
73
|
rule_enforcement_class('must-priority-coverage', canonical).
|
|
72
74
|
rule_enforcement_class('symbol-coverage', canonical).
|
|
@@ -121,6 +123,8 @@ rule_enforcement_class('supersedes-reason-missing', canonical).
|
|
|
121
123
|
rule_enforcement_class('supersedes-reason-acknowledged', advisory).
|
|
122
124
|
rule_enforcement_class('policy-ownership', canonical).
|
|
123
125
|
rule_enforcement_class('policy-markers', canonical).
|
|
126
|
+
rule_enforcement_class('kb-consistency', canonical).
|
|
127
|
+
rule_enforcement_class('kb-consistency-unresolved', advisory).
|
|
124
128
|
|
|
125
129
|
rule_implementation('must-priority-coverage', prolog).
|
|
126
130
|
rule_implementation('symbol-coverage', prolog).
|
|
@@ -175,6 +179,8 @@ rule_implementation('supersedes-reason-missing', typescript).
|
|
|
175
179
|
rule_implementation('supersedes-reason-acknowledged', typescript).
|
|
176
180
|
rule_implementation('policy-ownership', typescript).
|
|
177
181
|
rule_implementation('policy-markers', typescript).
|
|
182
|
+
rule_implementation('kb-consistency', prolog).
|
|
183
|
+
rule_implementation('kb-consistency-unresolved', prolog).
|
|
178
184
|
|
|
179
185
|
rule_predicate('must-priority-coverage', check_must_priority_coverage).
|
|
180
186
|
rule_predicate('symbol-coverage', check_symbol_coverage).
|
|
@@ -210,6 +216,8 @@ rule_predicate('ontology-quality', check_ontology_quality).
|
|
|
210
216
|
rule_predicate('symbol-owner-superseded', check_symbol_owner_superseded).
|
|
211
217
|
rule_predicate('adr-unlinked', check_adr_unlinked).
|
|
212
218
|
rule_predicate('adr-proposed', check_adr_proposed).
|
|
219
|
+
rule_predicate('kb-consistency', check_kb_consistency).
|
|
220
|
+
rule_predicate('kb-consistency-unresolved', check_kb_consistency_unresolved).
|
|
213
221
|
|
|
214
222
|
rule_predicate_arity('must-priority-coverage', 1).
|
|
215
223
|
rule_predicate_arity('symbol-coverage', 1).
|
|
@@ -245,6 +253,8 @@ rule_predicate_arity('ontology-quality', 1).
|
|
|
245
253
|
rule_predicate_arity('symbol-owner-superseded', 1).
|
|
246
254
|
rule_predicate_arity('adr-unlinked', 1).
|
|
247
255
|
rule_predicate_arity('adr-proposed', 1).
|
|
256
|
+
rule_predicate_arity('kb-consistency', 1).
|
|
257
|
+
rule_predicate_arity('kb-consistency-unresolved', 1).
|
|
248
258
|
|
|
249
259
|
rule_description('must-priority-coverage', 'Every current must-priority requirement (not superseded, retired, or closed without the implemented tag) must have a scenario and a test').
|
|
250
260
|
rule_description('symbol-coverage', 'Production symbols need qualifying coverage via covered_by plus a canonical requirement/scenario test path').
|
|
@@ -299,3 +309,5 @@ rule_description('supersedes-reason-missing', 'A current requirement or ADR that
|
|
|
299
309
|
rule_description('supersedes-reason-acknowledged', 'Informational: how many current superseding requirements and ADRs have only supersede edges the schema 9 migration acknowledged without a \'Why this changed\' section; one summary finding per check').
|
|
300
310
|
rule_description('policy-ownership', 'When a plugin activated for kibi.check-policy.v1 declares ownership rules, every production symbol in a rule\'s file set must implement a current requirement grounded (requires_predicate) in one of the rule\'s predicates; projects without an activated check policy are unaffected').
|
|
301
311
|
rule_description('policy-markers', 'When a plugin activated for kibi.check-policy.v1 declares marker rules, every file implementing a requirement grounded in the rule\'s pattern predicate (with its declared sibling files) must contain each marker the KB declares for that pattern').
|
|
312
|
+
rule_description('kb-consistency', 'The facts current requirements name must hold together, jointly and not only pairwise: every property context needs a value that satisfies all its facts, every atom and decision key one truth value, and stored rules a stratification. Each finding is a contradiction certificate (an irreducible core, two disagreeing facts or a negation cycle) re-verified by the independent checker, or a certificate that checker rejects').
|
|
313
|
+
rule_description('kb-consistency-unresolved', 'Advisory: a property context, atom or decision key whose facts the consistency checker cannot compare (mixed value types or units, an unsupported operator or polarity, or a contradiction whose validity windows never overlap); it never counts as consistent').
|