kibi-core 0.22.1 → 0.22.3
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/checks.pl +92 -6
- package/src/logic_ir.pl +10 -1
- package/src/rule_registry.pl +4 -0
package/package.json
CHANGED
package/src/checks.pl
CHANGED
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
check_subject_key_shape/1,
|
|
73
73
|
check_ontology_quality/1
|
|
74
74
|
]).
|
|
75
|
-
:- use_module('logic_ir.pl', [logic_rule_safety/2, logic_rule_from_props/2, logic_rule_render/2, logic_rule_conflict/3, logic_rule_conflict_witness/
|
|
75
|
+
:- use_module('logic_ir.pl', [logic_rule_safety/2, logic_rule_from_props/2, logic_rule_render/2, logic_rule_conflict/3, logic_rule_conflict_witness/4, logic_rules_stratified/1, logic_rule_missing_keys/4, functional_predicate_declarations/1, logic_rule_property_form/2]).
|
|
76
76
|
% GENERATED registry facts; single source is schema/rule-registry.json.
|
|
77
77
|
:- use_module('rule_registry.pl', [known_rule/1]).
|
|
78
78
|
|
|
@@ -214,12 +214,97 @@ check_changed_symbol_coverage(ExtraSymbols, Violations) :-
|
|
|
214
214
|
symbol_coverage_violation(SymbolId, violation(
|
|
215
215
|
'symbol-coverage',
|
|
216
216
|
SymbolId,
|
|
217
|
-
|
|
218
|
-
|
|
217
|
+
Description,
|
|
218
|
+
Suggestion,
|
|
219
219
|
Source
|
|
220
220
|
)) :-
|
|
221
|
+
symbol_coverage_message(SymbolId, Description, Suggestion),
|
|
221
222
|
violation_source(SymbolId, symbol, Source).
|
|
222
223
|
|
|
224
|
+
%% symbol_coverage_message(+SymbolId, -Description, -Suggestion)
|
|
225
|
+
% A symbol with no covered_by gets the generic advice. A symbol that already
|
|
226
|
+
% has covered_by is missing a different link: none of its tests verifies a
|
|
227
|
+
% requirement, so the message names the tests, the requirement the symbol
|
|
228
|
+
% implements and the relationship that would connect them.
|
|
229
|
+
% implements REQ-kibi-symbol-link-guidance
|
|
230
|
+
symbol_coverage_message(SymbolId, Description, Suggestion) :-
|
|
231
|
+
findall(Test, kb_relationship(covered_by, SymbolId, Test), Tests0),
|
|
232
|
+
sort(Tests0, Tests),
|
|
233
|
+
Tests = [Test|_],
|
|
234
|
+
!,
|
|
235
|
+
findall(Req,
|
|
236
|
+
( kb_relationship(implements, SymbolId, Req),
|
|
237
|
+
kb_entity(Req, req, _)
|
|
238
|
+
),
|
|
239
|
+
Reqs0),
|
|
240
|
+
sort(Reqs0, Reqs),
|
|
241
|
+
coverage_id_list(Tests, TestList),
|
|
242
|
+
coverage_id_text(SymbolId, Symbol),
|
|
243
|
+
( Reqs = [Req|_]
|
|
244
|
+
-> coverage_id_list(Reqs, ReqList),
|
|
245
|
+
( Tests = [_]
|
|
246
|
+
-> format(string(Description),
|
|
247
|
+
"Production symbol lacks qualifying requirement coverage: ~w is covered_by ~w, but ~w does not verify ~w, which ~w implements.",
|
|
248
|
+
[Symbol, TestList, TestList, ReqList, Symbol])
|
|
249
|
+
; format(string(Description),
|
|
250
|
+
"Production symbol lacks qualifying requirement coverage: ~w is covered_by ~w, but none of them verifies ~w, which ~w implements.",
|
|
251
|
+
[Symbol, TestList, ReqList, Symbol])
|
|
252
|
+
),
|
|
253
|
+
covered_symbol_link_suggestion(Req, Test, Suggestion)
|
|
254
|
+
; format(string(Description),
|
|
255
|
+
"Production symbol lacks qualifying requirement coverage: ~w is covered_by ~w, but ~w implements no requirement and none of its tests verifies one.",
|
|
256
|
+
[Symbol, TestList, Symbol]),
|
|
257
|
+
coverage_id_text(Test, TestText),
|
|
258
|
+
format(string(Suggestion),
|
|
259
|
+
"Add implements(~w, REQ-...) for the requirement this symbol owns, then connect ~w to it: verified_by(SCEN-..., ~w) through the requirement's scenario, or verified_by(REQ-..., ~w) when the requirement has no scenario.",
|
|
260
|
+
[Symbol, TestText, TestText, TestText])
|
|
261
|
+
).
|
|
262
|
+
symbol_coverage_message(_SymbolId,
|
|
263
|
+
"Production symbol lacks qualifying requirement coverage.",
|
|
264
|
+
"Add 'covered_by: TEST-<area>-<behavior>' for production coverage. If the requirement has specified_by a scenario, use verified_by(scenario,test) or validates(test,scenario). Direct verified_by(req,test) does not count when a scenario exists.").
|
|
265
|
+
|
|
266
|
+
%% covered_symbol_link_suggestion(+Req, +Test, -Suggestion)
|
|
267
|
+
% The one relationship that would let Test cover Req, following the same
|
|
268
|
+
% paths test_covers_requirement/2 accepts.
|
|
269
|
+
covered_symbol_link_suggestion(Req, Test, Suggestion) :-
|
|
270
|
+
coverage_id_text(Req, ReqText),
|
|
271
|
+
coverage_id_text(Test, TestText),
|
|
272
|
+
findall(Scenario, kb_relationship(specified_by, Req, Scenario), Scenarios0),
|
|
273
|
+
sort(Scenarios0, Scenarios),
|
|
274
|
+
( ( Scenarios == [],
|
|
275
|
+
( kb_relationship(verified_by, Req, Test)
|
|
276
|
+
; kb_relationship(validates, Test, Req)
|
|
277
|
+
)
|
|
278
|
+
; member(Scenario, Scenarios),
|
|
279
|
+
scenario_verified_by_test(Scenario, Test)
|
|
280
|
+
)
|
|
281
|
+
-> format(string(Suggestion),
|
|
282
|
+
"~w is linked to ~w, but its verification_scope or verification_perspective does not match what ~w requires; set those test fields to the required values.",
|
|
283
|
+
[TestText, ReqText, ReqText])
|
|
284
|
+
; Scenarios = [Scenario|_]
|
|
285
|
+
-> coverage_id_text(Scenario, ScenarioText),
|
|
286
|
+
coverage_id_list(Scenarios, ScenarioList),
|
|
287
|
+
( ( kb_relationship(verified_by, Req, Test)
|
|
288
|
+
; kb_relationship(validates, Test, Req)
|
|
289
|
+
)
|
|
290
|
+
-> Direct = " Direct verified_by(req,test) does not count while the requirement has a scenario."
|
|
291
|
+
; Direct = ""
|
|
292
|
+
),
|
|
293
|
+
format(string(Suggestion),
|
|
294
|
+
"Add verified_by(~w, ~w) (~w is specified_by ~w), or validates(~w, ~w).~w",
|
|
295
|
+
[ScenarioText, TestText, ReqText, ScenarioList, TestText, ScenarioText, Direct])
|
|
296
|
+
; format(string(Suggestion),
|
|
297
|
+
"Add verified_by(~w, ~w) (~w has no scenario, so the direct link counts), or specify it with a scenario that ~w verifies.",
|
|
298
|
+
[ReqText, TestText, ReqText, TestText])
|
|
299
|
+
).
|
|
300
|
+
|
|
301
|
+
coverage_id_text(Id, Text) :-
|
|
302
|
+
normalize_term_atom(Id, Text).
|
|
303
|
+
|
|
304
|
+
coverage_id_list(Ids, Text) :-
|
|
305
|
+
maplist(coverage_id_text, Ids, Texts),
|
|
306
|
+
atomic_list_concat(Texts, ', ', Text).
|
|
307
|
+
|
|
223
308
|
%% check_symbol_traceability(+RequireAdr, -Violations)
|
|
224
309
|
% Finds all symbols lacking direct requirement ownership:
|
|
225
310
|
% - Every symbol must have at least one direct implements ownership path
|
|
@@ -2433,7 +2518,8 @@ check_domain_contradictions_and_witnesses(Violations, Witnesses) :-
|
|
|
2433
2518
|
% disproven remains status=unresolved rather than becoming consistency.
|
|
2434
2519
|
check_domain_contradiction_witnesses(Witnesses) :-
|
|
2435
2520
|
findall(Witness, req_conflict_witness(_ReqA, _ReqB, Witness), GroundWitnesses),
|
|
2436
|
-
|
|
2521
|
+
functional_predicate_declarations(Functional),
|
|
2522
|
+
findall(Witness, rule_contradiction_witness(Functional, Witness), RuleWitnesses),
|
|
2437
2523
|
append(GroundWitnesses, RuleWitnesses, Witnesses0),
|
|
2438
2524
|
sort(Witnesses0, Witnesses).
|
|
2439
2525
|
|
|
@@ -2622,9 +2708,9 @@ opposing_rule_requirement_facts(ReqA, ReqB, FactA, FactB, RuleA, RuleB) :-
|
|
|
2622
2708
|
logic_rule_from_props(PropsA, RuleA),
|
|
2623
2709
|
logic_rule_from_props(PropsB, RuleB).
|
|
2624
2710
|
|
|
2625
|
-
rule_contradiction_witness(Witness) :-
|
|
2711
|
+
rule_contradiction_witness(Functional, Witness) :-
|
|
2626
2712
|
opposing_rule_requirement_facts(ReqA, ReqB, FactA, FactB, RuleA, RuleB),
|
|
2627
|
-
logic_rule_conflict_witness(RuleA, RuleB, InternalWitness),
|
|
2713
|
+
logic_rule_conflict_witness(RuleA, RuleB, Functional, InternalWitness),
|
|
2628
2714
|
InternalWitness.status \= disjoint,
|
|
2629
2715
|
rule_conflict_side(ReqA, FactA, Left),
|
|
2630
2716
|
rule_conflict_side(ReqB, FactB, Right),
|
package/src/logic_ir.pl
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
logic_rule_conflict/3,
|
|
13
13
|
logic_rule_conflict/4,
|
|
14
14
|
logic_rule_conflict_witness/3,
|
|
15
|
+
logic_rule_conflict_witness/4,
|
|
15
16
|
logic_rule_missing_keys/4,
|
|
16
17
|
functional_predicate_declarations/1,
|
|
17
18
|
logic_atom_signature/2,
|
|
@@ -1215,7 +1216,15 @@ dependency_path(Current, Target, Edges, Seen) :-
|
|
|
1215
1216
|
% Consumers can attach requirement IDs and source spans without ever treating
|
|
1216
1217
|
% the rendered Prolog preview as executable source.
|
|
1217
1218
|
logic_rule_conflict_witness(RuleA, RuleB, Witness) :-
|
|
1218
|
-
|
|
1219
|
+
functional_predicate_declarations(Functional),
|
|
1220
|
+
logic_rule_conflict_witness(RuleA, RuleB, Functional, Witness).
|
|
1221
|
+
|
|
1222
|
+
%% logic_rule_conflict_witness(+RuleA, +RuleB, +Functional, -Witness)
|
|
1223
|
+
% As logic_rule_conflict_witness/3 with the functional declarations already
|
|
1224
|
+
% read. Reading them scans every predicate_schema fact, so a caller walking
|
|
1225
|
+
% every opposing rule pair reads them once instead of once per pair.
|
|
1226
|
+
logic_rule_conflict_witness(RuleA, RuleB, Functional, Witness) :-
|
|
1227
|
+
logic_rule_conflict(RuleA, RuleB, Functional, Status),
|
|
1219
1228
|
RuleA = rule(_KindA, ModalityA, HeadA, BodyA, ExceptionsA, ScopeA, FromA, ToA, SchemaA, _VariablesA),
|
|
1220
1229
|
RuleB = rule(_KindB, ModalityB, HeadB, BodyB, ExceptionsB, ScopeB, FromB, ToB, SchemaB, _VariablesB),
|
|
1221
1230
|
Witness = witness{
|
package/src/rule_registry.pl
CHANGED
|
@@ -62,6 +62,7 @@ known_rule('entity-context-missing').
|
|
|
62
62
|
known_rule('entity-context-acknowledged').
|
|
63
63
|
known_rule('decision-firm-unreferenced').
|
|
64
64
|
known_rule('decision-firm-superseded').
|
|
65
|
+
known_rule('decision-firm-edited').
|
|
65
66
|
known_rule('supersedes-reason-missing').
|
|
66
67
|
known_rule('supersedes-reason-acknowledged').
|
|
67
68
|
known_rule('policy-ownership').
|
|
@@ -115,6 +116,7 @@ rule_enforcement_class('entity-context-missing', canonical).
|
|
|
115
116
|
rule_enforcement_class('entity-context-acknowledged', advisory).
|
|
116
117
|
rule_enforcement_class('decision-firm-unreferenced', canonical).
|
|
117
118
|
rule_enforcement_class('decision-firm-superseded', canonical).
|
|
119
|
+
rule_enforcement_class('decision-firm-edited', canonical).
|
|
118
120
|
rule_enforcement_class('supersedes-reason-missing', canonical).
|
|
119
121
|
rule_enforcement_class('supersedes-reason-acknowledged', advisory).
|
|
120
122
|
rule_enforcement_class('policy-ownership', canonical).
|
|
@@ -168,6 +170,7 @@ rule_implementation('entity-context-missing', typescript).
|
|
|
168
170
|
rule_implementation('entity-context-acknowledged', typescript).
|
|
169
171
|
rule_implementation('decision-firm-unreferenced', typescript).
|
|
170
172
|
rule_implementation('decision-firm-superseded', typescript).
|
|
173
|
+
rule_implementation('decision-firm-edited', typescript).
|
|
171
174
|
rule_implementation('supersedes-reason-missing', typescript).
|
|
172
175
|
rule_implementation('supersedes-reason-acknowledged', typescript).
|
|
173
176
|
rule_implementation('policy-ownership', typescript).
|
|
@@ -291,6 +294,7 @@ rule_description('entity-context-missing', 'A current requirement, scenario, tes
|
|
|
291
294
|
rule_description('entity-context-acknowledged', 'Informational: how many current entities carry the review:context-missing tag, so the legacy context backlog stays visible; one summary finding per check').
|
|
292
295
|
rule_description('decision-firm-unreferenced', 'A requirement or ADR marked firmness: firm must name the human words it rests on: origin.ref (or approval_ref on a requirement); Kibi checks that the reference is present, not that it is genuine').
|
|
293
296
|
rule_description('decision-firm-superseded', 'A requirement or ADR that supersedes a firm decision, retired or not, must itself be firm (and so carry a ref to the human who changed the rule); a default or exploratory replacement cannot overrule a firm decision').
|
|
297
|
+
rule_description('decision-firm-edited', 'A current requirement or ADR that was firm at HEAD must not have its claim (title, stated meaning, the statement its body derives, an ADR\'s body, or its firmness) changed in place unless the edit cites new human words (a changed origin.ref, or approval_ref on a requirement); supersede it with a firm decision instead').
|
|
294
298
|
rule_description('supersedes-reason-missing', 'A current requirement or ADR that supersedes another must explain why under a \'Why this changed\' heading (at least 12 words of prose naming the evidence and answering the old decision\'s reasoning); supersede edges recorded before schema 9 are acknowledged in the manifest and exempt').
|
|
295
299
|
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').
|
|
296
300
|
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').
|