kibi-core 0.22.2 → 0.22.4

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kibi-core",
3
- "version": "0.22.2",
3
+ "version": "0.22.4",
4
4
  "private": false,
5
5
  "description": "Core Prolog modules and RDF graph logic for Kibi",
6
6
  "type": "module",
@@ -29,7 +29,8 @@ entity_property(_, priority, atom).
29
29
  entity_property(_, severity, atom).
30
30
  entity_property(_, links, list).
31
31
  entity_property(_, text_ref, uri).
32
- entity_property(_, sourceFile, uri).
32
+ % Workspace-relative source paths only (no absolute / .. escapes).
33
+ entity_property(_, sourceFile, workspace_relpath).
33
34
  % Provenance of any authored entity: a JSON object (kind, ref, approved_by,
34
35
  % recorded_at) naming who authored it and on what authority.
35
36
  entity_property(_, origin, list_or_json).
@@ -50,11 +50,26 @@ check_kind(datetime, V) :- string(V). % accept ISO strings for now
50
50
  check_kind(list, V) :- is_list(V).
51
51
  check_kind(list_or_json, V) :- is_list(V) ; string(V) ; atom(V).
52
52
  check_kind(uri, V) :- string(V).
53
+ % Non-empty workspace-relative path: reject absolute forms and `..` segments.
54
+ check_kind(workspace_relpath, V) :-
55
+ string(V),
56
+ string_length(V, Len),
57
+ Len > 0,
58
+ \+ workspace_relpath_absolute(V),
59
+ split_string(V, "/\\", "", Parts),
60
+ \+ member("..", Parts).
53
61
  check_kind(integer, V) :- integer(V).
54
62
  check_kind(number, V) :- number(V).
55
63
  check_kind(boolean, true).
56
64
  check_kind(boolean, false).
57
65
 
66
+ workspace_relpath_absolute(V) :- sub_string(V, 0, 1, _, "/").
67
+ workspace_relpath_absolute(V) :-
68
+ string_length(V, Len),
69
+ Len >= 2,
70
+ sub_string(V, 1, 1, _, ":").
71
+ workspace_relpath_absolute(V) :- sub_string(V, 0, 2, _, "\\\\").
72
+
58
73
  % Fallback false
59
74
  check_kind(_, _) :- fail.
60
75
 
package/src/checks.pl CHANGED
@@ -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
- "Production symbol lacks qualifying requirement coverage.",
218
- "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.",
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