kibi-core 0.6.4 → 0.7.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/schema/entities.pl +4 -0
- package/src/checks.pl +61 -11
- package/src/discovery.pl +127 -1
- package/src/status.pl +6 -0
package/package.json
CHANGED
package/schema/entities.pl
CHANGED
|
@@ -57,6 +57,10 @@ entity_property(fact, aliases, list).
|
|
|
57
57
|
entity_property(fact, examples, list).
|
|
58
58
|
entity_property(fact, predicate_args, list).
|
|
59
59
|
|
|
60
|
+
% Typed symbol metadata fields - only valid for symbol entities
|
|
61
|
+
entity_property(symbol, symbol_role, atom).
|
|
62
|
+
entity_property(symbol, granularity_reason, atom).
|
|
63
|
+
|
|
60
64
|
% Typed test verification fields - only valid for test entities
|
|
61
65
|
entity_property(test, verification_scope, atom).
|
|
62
66
|
entity_property(test, verification_perspective, atom).
|
package/src/checks.pl
CHANGED
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
check_strict_fact_shape/1, % Returns list of malformed strict fact violations
|
|
18
18
|
check_strict_req_fact_pairing/1,% Returns list of malformed strict req/fact pairing violations
|
|
19
19
|
check_strict_readiness/1, % Returns list of strict readiness audit violations
|
|
20
|
+
check_predicate_verifiability/1,% Returns list of unverifiable predicate-lane requirement links
|
|
20
21
|
run_checks_json/0, % Entry point for JSON output
|
|
21
22
|
violation_id_text/2 % Extract text from entity ID term (exported for testing)
|
|
22
23
|
]).
|
|
@@ -52,6 +53,7 @@ check_all(ViolationsDict) :-
|
|
|
52
53
|
check_strict_fact_shape(StrictFactShape),
|
|
53
54
|
check_strict_req_fact_pairing(StrictReqFactPairing),
|
|
54
55
|
check_strict_readiness(StrictReadiness),
|
|
56
|
+
check_predicate_verifiability(PredicateVerifiability),
|
|
55
57
|
ViolationsDict = _{
|
|
56
58
|
must_priority_coverage: MustPriority,
|
|
57
59
|
symbol_coverage: SymbolCoverage,
|
|
@@ -63,7 +65,8 @@ check_all(ViolationsDict) :-
|
|
|
63
65
|
domain_contradictions: Contradictions,
|
|
64
66
|
strict_fact_shape: StrictFactShape,
|
|
65
67
|
strict_req_fact_pairing: StrictReqFactPairing,
|
|
66
|
-
strict_readiness: StrictReadiness
|
|
68
|
+
strict_readiness: StrictReadiness,
|
|
69
|
+
predicate_verifiability: PredicateVerifiability
|
|
67
70
|
}.
|
|
68
71
|
|
|
69
72
|
%% check_must_priority_coverage(-Violations)
|
|
@@ -132,7 +135,6 @@ symbol_traceability_violation(RequireAdr, violation(
|
|
|
132
135
|
Source
|
|
133
136
|
)) :-
|
|
134
137
|
kb_entity(SymbolId, symbol, _),
|
|
135
|
-
\+ executable_test_symbol(SymbolId),
|
|
136
138
|
% Check if symbol has direct requirement ownership
|
|
137
139
|
( symbol_owns_requirement(SymbolId, ReqId),
|
|
138
140
|
kb_entity(ReqId, req, _)
|
|
@@ -148,6 +150,7 @@ symbol_traceability_violation(RequireAdr, violation(
|
|
|
148
150
|
)
|
|
149
151
|
; HasAdr = true % Not required, so pass this check
|
|
150
152
|
),
|
|
153
|
+
\+ executable_test_symbol(SymbolId),
|
|
151
154
|
% Determine what is missing
|
|
152
155
|
( HasReq = false, HasAdr = false, RequireAdr = true ->
|
|
153
156
|
Description = "Symbol has no direct requirement ownership and no ADR constraint.",
|
|
@@ -307,15 +310,14 @@ missing_required_field(Required, violation(
|
|
|
307
310
|
)) :-
|
|
308
311
|
kb_entity(EntityId, _Type, Props),
|
|
309
312
|
member(Field, Required),
|
|
310
|
-
\+ memberchk(Field=_, Props),
|
|
311
|
-
|
|
312
|
-
format(string(Description), "Missing required field: ~w", [Field]),
|
|
313
|
-
format(string(Suggestion), "Add ~w to entity definition", [Field]),
|
|
314
|
-
|
|
315
313
|
( memberchk(source=Source, Props)
|
|
316
314
|
-> true
|
|
317
315
|
; Source = ""
|
|
318
|
-
)
|
|
316
|
+
),
|
|
317
|
+
\+ memberchk(Field=_, Props),
|
|
318
|
+
|
|
319
|
+
format(string(Description), "Missing required field: ~w", [Field]),
|
|
320
|
+
format(string(Suggestion), "Add ~w to entity definition", [Field]).
|
|
319
321
|
|
|
320
322
|
%% check_deprecated_adrs(-Violations)
|
|
321
323
|
% Finds all deprecated ADRs without successors.
|
|
@@ -428,8 +430,7 @@ property_value_shape_error(Props, "Property value fact missing required field: v
|
|
|
428
430
|
\+ memberchk(value_type=_, Props).
|
|
429
431
|
property_value_shape_error(Props, "Property value fact missing value field (value_string, value_int, value_number, or value_bool)") :-
|
|
430
432
|
memberchk(value_type=_, Props),
|
|
431
|
-
\+ (
|
|
432
|
-
memberchk(value_number=_, Props); memberchk(value_bool=_, Props)).
|
|
433
|
+
\+ has_value_field(Props).
|
|
433
434
|
property_value_shape_error(Props, "Property value fact has multiple value fields (should have exactly one)") :-
|
|
434
435
|
findall(F, (member(F=_, Props), is_value_field(F)), Fields),
|
|
435
436
|
length(Fields, Count),
|
|
@@ -445,6 +446,11 @@ is_value_field(value_int).
|
|
|
445
446
|
is_value_field(value_number).
|
|
446
447
|
is_value_field(value_bool).
|
|
447
448
|
|
|
449
|
+
has_value_field(Props) :- memberchk(value_string=_, Props), !.
|
|
450
|
+
has_value_field(Props) :- memberchk(value_int=_, Props), !.
|
|
451
|
+
has_value_field(Props) :- memberchk(value_number=_, Props), !.
|
|
452
|
+
has_value_field(Props) :- memberchk(value_bool=_, Props), !.
|
|
453
|
+
|
|
448
454
|
% value_type_matches_field(+ValueType, +Props)
|
|
449
455
|
value_type_matches_field(string, Props) :- memberchk(value_string=_, Props), !.
|
|
450
456
|
value_type_matches_field(int, Props) :- memberchk(value_int=_, Props), !.
|
|
@@ -752,6 +758,48 @@ strict_readiness_level(ReqId, traceable) :-
|
|
|
752
758
|
!.
|
|
753
759
|
strict_readiness_level(_ReqId, prose_only).
|
|
754
760
|
|
|
761
|
+
%% check_predicate_verifiability(-Violations)
|
|
762
|
+
% Finds requirements whose requires_predicate links do not target ground
|
|
763
|
+
% predicate facts. This keeps ontology-lane prose from looking modeled when the
|
|
764
|
+
% target is still a legacy, observation, meta, schema, or strict-property fact.
|
|
765
|
+
check_predicate_verifiability(Violations) :-
|
|
766
|
+
findall(
|
|
767
|
+
Violation,
|
|
768
|
+
predicate_verifiability_violation(Violation),
|
|
769
|
+
Violations0
|
|
770
|
+
),
|
|
771
|
+
sort(Violations0, Violations).
|
|
772
|
+
|
|
773
|
+
predicate_verifiability_violation(violation(
|
|
774
|
+
'predicate-verifiability',
|
|
775
|
+
ReqId,
|
|
776
|
+
Description,
|
|
777
|
+
"Apply kb_suggest_predicates and link requires_predicate to a fact_kind: predicate fact, or record a review:ontology-gap observation when no predicate fits",
|
|
778
|
+
Source
|
|
779
|
+
)) :-
|
|
780
|
+
kb:current_req(ReqId),
|
|
781
|
+
kb_relationship(requires_predicate, ReqId, FactId),
|
|
782
|
+
kb_entity(FactId, fact, Props),
|
|
783
|
+
predicate_verifiability_fact_kind(Props, Kind),
|
|
784
|
+
Kind \= predicate,
|
|
785
|
+
predicate_verifiability_kind_label(Kind, KindLabel),
|
|
786
|
+
format(
|
|
787
|
+
string(Description),
|
|
788
|
+
"Requirement uses requires_predicate ~w but target is ~w, not fact_kind=predicate",
|
|
789
|
+
[FactId, KindLabel]
|
|
790
|
+
),
|
|
791
|
+
violation_source(ReqId, req, Source).
|
|
792
|
+
|
|
793
|
+
predicate_verifiability_fact_kind(Props, Kind) :-
|
|
794
|
+
( memberchk(fact_kind=RawKind, Props)
|
|
795
|
+
-> normalize_term_atom(RawKind, Kind)
|
|
796
|
+
; Kind = legacy
|
|
797
|
+
).
|
|
798
|
+
|
|
799
|
+
predicate_verifiability_kind_label(legacy, "a legacy fact without fact_kind").
|
|
800
|
+
predicate_verifiability_kind_label(Kind, Label) :-
|
|
801
|
+
format(string(Label), "fact_kind=~w", [Kind]).
|
|
802
|
+
|
|
755
803
|
strict_readiness_has_fact_link(ReqId) :-
|
|
756
804
|
kb_relationship(constrains, ReqId, FactId),
|
|
757
805
|
kb_entity(FactId, fact, _),
|
|
@@ -824,6 +872,7 @@ check_all_with_options(ViolationsDict, RequireAdr) :-
|
|
|
824
872
|
check_strict_fact_shape(StrictFactShape),
|
|
825
873
|
check_strict_req_fact_pairing(StrictReqFactPairing),
|
|
826
874
|
check_strict_readiness(StrictReadiness),
|
|
875
|
+
check_predicate_verifiability(PredicateVerifiability),
|
|
827
876
|
ViolationsDict = _{
|
|
828
877
|
must_priority_coverage: MustPriority,
|
|
829
878
|
symbol_coverage: SymbolCoverage,
|
|
@@ -835,7 +884,8 @@ check_all_with_options(ViolationsDict, RequireAdr) :-
|
|
|
835
884
|
domain_contradictions: Contradictions,
|
|
836
885
|
strict_fact_shape: StrictFactShape,
|
|
837
886
|
strict_req_fact_pairing: StrictReqFactPairing,
|
|
838
|
-
strict_readiness: StrictReadiness
|
|
887
|
+
strict_readiness: StrictReadiness,
|
|
888
|
+
predicate_verifiability: PredicateVerifiability
|
|
839
889
|
}.
|
|
840
890
|
|
|
841
891
|
%% violations_dict_to_json(+ViolationsDict, -JsonDict)
|
package/src/discovery.pl
CHANGED
|
@@ -131,6 +131,7 @@ requirement_coverage_row(Tags, IncludeTransitive, Row) :-
|
|
|
131
131
|
count_direct_symbols(Id, DirectSymbolCount),
|
|
132
132
|
count_transitive_symbols(Id, IncludeTransitive, TransitiveSymbolCount),
|
|
133
133
|
requirement_coverage_state(Id, Props, Gaps, Evaluated, CoverageStatus),
|
|
134
|
+
requirement_coverage_depth(Id, ScenarioCount, CoverageDepth, CoverageEvidence),
|
|
134
135
|
Row = _{
|
|
135
136
|
id: Id,
|
|
136
137
|
type: req,
|
|
@@ -143,7 +144,13 @@ requirement_coverage_row(Tags, IncludeTransitive, Row) :-
|
|
|
143
144
|
transitiveSymbolCount: TransitiveSymbolCount,
|
|
144
145
|
gaps: Gaps,
|
|
145
146
|
evaluated: Evaluated,
|
|
146
|
-
coverageStatus: CoverageStatus
|
|
147
|
+
coverageStatus: CoverageStatus,
|
|
148
|
+
coverageDepth: CoverageDepth,
|
|
149
|
+
coverage_depth: CoverageDepth,
|
|
150
|
+
directTests: CoverageEvidence.directTests,
|
|
151
|
+
scenarioTests: CoverageEvidence.scenarioTests,
|
|
152
|
+
testStatuses: CoverageEvidence.testStatuses,
|
|
153
|
+
verificationScopes: CoverageEvidence.verificationScopes
|
|
147
154
|
}.
|
|
148
155
|
|
|
149
156
|
symbol_coverage_row(Tags, Row) :-
|
|
@@ -232,6 +239,125 @@ requirement_test_count(Id, Count) :-
|
|
|
232
239
|
sort(Combined0, Combined),
|
|
233
240
|
length(Combined, Count).
|
|
234
241
|
|
|
242
|
+
requirement_coverage_depth(Id, ScenarioCount, CoverageDepth, Evidence) :-
|
|
243
|
+
requirement_direct_tests(Id, DirectTests),
|
|
244
|
+
requirement_scenario_tests(Id, ScenarioTests),
|
|
245
|
+
append(DirectTests, ScenarioTests, AllTests0),
|
|
246
|
+
sort(AllTests0, AllTests),
|
|
247
|
+
passing_tests(DirectTests, PassingDirectTests),
|
|
248
|
+
passing_tests(ScenarioTests, PassingScenarioTests),
|
|
249
|
+
passing_tests(AllTests, PassingTests),
|
|
250
|
+
test_statuses(AllTests, TestStatuses),
|
|
251
|
+
test_scopes(AllTests, VerificationScopes),
|
|
252
|
+
Evidence = _{
|
|
253
|
+
directTests: DirectTests,
|
|
254
|
+
scenarioTests: ScenarioTests,
|
|
255
|
+
testStatuses: TestStatuses,
|
|
256
|
+
verificationScopes: VerificationScopes
|
|
257
|
+
},
|
|
258
|
+
classify_coverage_depth(
|
|
259
|
+
ScenarioCount,
|
|
260
|
+
AllTests,
|
|
261
|
+
PassingTests,
|
|
262
|
+
PassingDirectTests,
|
|
263
|
+
PassingScenarioTests,
|
|
264
|
+
CoverageDepth
|
|
265
|
+
).
|
|
266
|
+
|
|
267
|
+
requirement_direct_tests(Id, Tests) :-
|
|
268
|
+
findall(TestId, direct_requirement_test(Id, TestId), Tests0),
|
|
269
|
+
sort(Tests0, Tests).
|
|
270
|
+
|
|
271
|
+
direct_requirement_test(Id, TestId) :-
|
|
272
|
+
kb_relationship(verified_by, Id, TestId),
|
|
273
|
+
kb_entity(TestId, test, _).
|
|
274
|
+
direct_requirement_test(Id, TestId) :-
|
|
275
|
+
kb_relationship(validates, TestId, Id),
|
|
276
|
+
kb_entity(TestId, test, _).
|
|
277
|
+
direct_requirement_test(Id, TestId) :-
|
|
278
|
+
kb_relationship(covered_by, Id, TestId),
|
|
279
|
+
kb_entity(TestId, test, _).
|
|
280
|
+
|
|
281
|
+
requirement_scenario_tests(Id, Tests) :-
|
|
282
|
+
findall(TestId, scenario_requirement_test(Id, TestId), Tests0),
|
|
283
|
+
sort(Tests0, Tests).
|
|
284
|
+
|
|
285
|
+
scenario_requirement_test(Id, TestId) :-
|
|
286
|
+
kb_relationship(specified_by, Id, ScenarioId),
|
|
287
|
+
scenario_test(ScenarioId, TestId).
|
|
288
|
+
|
|
289
|
+
scenario_test(ScenarioId, TestId) :-
|
|
290
|
+
kb_relationship(validates, TestId, ScenarioId),
|
|
291
|
+
kb_entity(TestId, test, _).
|
|
292
|
+
scenario_test(ScenarioId, TestId) :-
|
|
293
|
+
kb_relationship(verified_by, ScenarioId, TestId),
|
|
294
|
+
kb_entity(TestId, test, _).
|
|
295
|
+
|
|
296
|
+
passing_tests(Tests, PassingTests) :-
|
|
297
|
+
include(passing_test, Tests, PassingTests).
|
|
298
|
+
|
|
299
|
+
passing_test(TestId) :-
|
|
300
|
+
kb_entity(TestId, test, Props),
|
|
301
|
+
memberchk(status=StatusRaw, Props),
|
|
302
|
+
source_value_atom(StatusRaw, passing).
|
|
303
|
+
|
|
304
|
+
test_statuses(Tests, Statuses) :-
|
|
305
|
+
findall(Status, (member(TestId, Tests), test_status(TestId, Status)), Statuses0),
|
|
306
|
+
sort(Statuses0, Statuses).
|
|
307
|
+
|
|
308
|
+
test_status(TestId, Status) :-
|
|
309
|
+
kb_entity(TestId, test, Props),
|
|
310
|
+
memberchk(status=StatusRaw, Props),
|
|
311
|
+
source_value_atom(StatusRaw, Status).
|
|
312
|
+
|
|
313
|
+
test_scopes(Tests, Scopes) :-
|
|
314
|
+
findall(Scope, (member(TestId, Tests), test_scope(TestId, Scope)), Scopes0),
|
|
315
|
+
sort(Scopes0, Scopes).
|
|
316
|
+
|
|
317
|
+
test_scope(TestId, Scope) :-
|
|
318
|
+
kb_entity(TestId, test, Props),
|
|
319
|
+
( memberchk(verification_scope=ScopeRaw, Props)
|
|
320
|
+
-> source_value_atom(ScopeRaw, Scope)
|
|
321
|
+
; legacy_e2e_test(Props)
|
|
322
|
+
-> Scope = end_to_end
|
|
323
|
+
; Scope = unknown
|
|
324
|
+
).
|
|
325
|
+
|
|
326
|
+
legacy_e2e_test(Props) :-
|
|
327
|
+
legacy_e2e_tag(Props),
|
|
328
|
+
!.
|
|
329
|
+
legacy_e2e_test(Props) :-
|
|
330
|
+
memberchk(source=SourceRaw, Props),
|
|
331
|
+
source_value_atom(SourceRaw, Source),
|
|
332
|
+
downcase_atom(Source, LowercaseSource),
|
|
333
|
+
sub_atom(LowercaseSource, _, _, _, 'e2e').
|
|
334
|
+
|
|
335
|
+
legacy_e2e_tag(Props) :-
|
|
336
|
+
memberchk(tags=Tags, Props),
|
|
337
|
+
member(TagRaw, Tags),
|
|
338
|
+
source_value_atom(TagRaw, Tag),
|
|
339
|
+
downcase_atom(Tag, e2e).
|
|
340
|
+
|
|
341
|
+
classify_coverage_depth(_ScenarioCount, _AllTests, _PassingTests, PassingDirectTests, _PassingScenarioTests, direct_passing_e2e) :-
|
|
342
|
+
member(TestId, PassingDirectTests),
|
|
343
|
+
test_scope(TestId, end_to_end),
|
|
344
|
+
!.
|
|
345
|
+
classify_coverage_depth(_ScenarioCount, _AllTests, _PassingTests, _PassingDirectTests, PassingScenarioTests, scenario_passing_e2e) :-
|
|
346
|
+
member(TestId, PassingScenarioTests),
|
|
347
|
+
test_scope(TestId, end_to_end),
|
|
348
|
+
!.
|
|
349
|
+
classify_coverage_depth(_ScenarioCount, _AllTests, PassingTests, _PassingDirectTests, _PassingScenarioTests, unit_only) :-
|
|
350
|
+
PassingTests \= [],
|
|
351
|
+
forall(member(TestId, PassingTests), test_scope(TestId, unit)),
|
|
352
|
+
!.
|
|
353
|
+
classify_coverage_depth(_ScenarioCount, AllTests, _PassingTests, _PassingDirectTests, _PassingScenarioTests, open_or_nonpassing_tests_only) :-
|
|
354
|
+
AllTests \= [],
|
|
355
|
+
!.
|
|
356
|
+
classify_coverage_depth(ScenarioCount, _AllTests, _PassingTests, _PassingDirectTests, _PassingScenarioTests, scenario_only_no_test) :-
|
|
357
|
+
ScenarioCount > 0,
|
|
358
|
+
!.
|
|
359
|
+
classify_coverage_depth(_ScenarioCount, _AllTests, _PassingTests, _PassingDirectTests, _PassingScenarioTests, no_test_evidence).
|
|
360
|
+
|
|
235
361
|
count_direct_symbols(Id, Count) :-
|
|
236
362
|
findall(SymbolId, kb_relationship(implements, SymbolId, Id), Symbols0),
|
|
237
363
|
sort(Symbols0, Symbols),
|
package/src/status.pl
CHANGED
|
@@ -117,6 +117,7 @@ documentation_markdown_untracked(WorkspaceRoot) :-
|
|
|
117
117
|
documentation_markdown_file(Path, Path) :-
|
|
118
118
|
exists_file(Path),
|
|
119
119
|
file_name_extension(_, md, Path),
|
|
120
|
+
\+ ignored_documentation_file(Path),
|
|
120
121
|
!.
|
|
121
122
|
documentation_markdown_file(Path, FilePath) :-
|
|
122
123
|
exists_directory(Path),
|
|
@@ -138,6 +139,8 @@ known_source_path(RelativePath) :-
|
|
|
138
139
|
repo_relative_source(SourceAtom, RelativePath).
|
|
139
140
|
|
|
140
141
|
directory_tree_newer(Path, SnapshotTime) :-
|
|
142
|
+
exists_file(Path),
|
|
143
|
+
\+ ignored_documentation_file(Path),
|
|
141
144
|
time_file(Path, EntryTime),
|
|
142
145
|
EntryTime > SnapshotTime,
|
|
143
146
|
!.
|
|
@@ -212,5 +215,8 @@ source_value_atom(Val, Atom) :-
|
|
|
212
215
|
term_string(Val, Str),
|
|
213
216
|
atom_string(Atom, Str).
|
|
214
217
|
|
|
218
|
+
ignored_documentation_file(Path) :-
|
|
219
|
+
file_base_name(Path, 'README.md').
|
|
220
|
+
|
|
215
221
|
dict_json_string(Dict, JsonString) :-
|
|
216
222
|
with_output_to(string(JsonString), json_write_dict(current_output, Dict, [])).
|