kibi-core 0.6.1 → 0.6.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 +5 -2
- package/src/checks.pl +2 -2
- package/src/kb.pl +89 -19
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kibi-core",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.3",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Core Prolog modules and RDF graph logic for Kibi",
|
|
6
6
|
"type": "module",
|
|
@@ -9,7 +9,10 @@
|
|
|
9
9
|
"bun": ">=1.0"
|
|
10
10
|
},
|
|
11
11
|
"main": "./src/kb.pl",
|
|
12
|
-
"files": [
|
|
12
|
+
"files": [
|
|
13
|
+
"src/**/*.pl",
|
|
14
|
+
"schema/**/*.pl"
|
|
15
|
+
],
|
|
13
16
|
"license": "AGPL-3.0-or-later",
|
|
14
17
|
"author": "Piotr Franczyk",
|
|
15
18
|
"repository": {
|
package/src/checks.pl
CHANGED
|
@@ -107,7 +107,7 @@ symbol_coverage_violation(SymbolId, violation(
|
|
|
107
107
|
'symbol-coverage',
|
|
108
108
|
SymbolId,
|
|
109
109
|
"Production symbol lacks qualifying requirement coverage.",
|
|
110
|
-
"Add 'covered_by: TEST-xxx' for production coverage
|
|
110
|
+
"Add 'covered_by: TEST-xxx' 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.",
|
|
111
111
|
Source
|
|
112
112
|
)) :-
|
|
113
113
|
violation_source(SymbolId, symbol, Source).
|
|
@@ -718,7 +718,7 @@ strict_readiness_issue(
|
|
|
718
718
|
strict_readiness_issue(
|
|
719
719
|
ReqId,
|
|
720
720
|
"Strict readiness: not-ready (strict-ready). Requirement has strict subject and property facts but no contradiction-ready matched pair, so contradiction checks still skip it.",
|
|
721
|
-
"
|
|
721
|
+
"Ensure constrains and requires_property use the same subject_key, and keep the requirement current if it should participate in contradiction checks"
|
|
722
722
|
) :-
|
|
723
723
|
kb_entity(ReqId, req, _),
|
|
724
724
|
strict_readiness_level(ReqId, strict_ready).
|
package/src/kb.pl
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
kb_log_entity_upsert/3,
|
|
11
11
|
kb_retract_entity/1,
|
|
12
12
|
kb_retract_entity/3,
|
|
13
|
+
kb_retract_entity_relationships/1,
|
|
13
14
|
kb_entity/3,
|
|
14
15
|
kb_entities_by_source/2,
|
|
15
16
|
kb_assert_relationship/4,
|
|
@@ -279,9 +280,19 @@ kb_assert_entity_no_audit(Type, Props) :-
|
|
|
279
280
|
% Execute RDF operations with mutex protection
|
|
280
281
|
with_kb_mutex((
|
|
281
282
|
% Create entity URI using prefix notation for namespace expansion
|
|
282
|
-
|
|
283
|
-
% Upsert semantics: remove
|
|
284
|
-
|
|
283
|
+
entity_id_to_uri(Id, EntityURI),
|
|
284
|
+
% Upsert semantics: remove only property triples, preserving relationships.
|
|
285
|
+
% Relationship triples have entity URI objects (kb:entity/...);
|
|
286
|
+
% property triples have typed literal objects (_^^xsd:...).
|
|
287
|
+
forall(
|
|
288
|
+
( rdf(EntityURI, Prop, Obj, Graph),
|
|
289
|
+
( atom(Obj)
|
|
290
|
+
-> \+ entity_uri_to_id(Obj, _)
|
|
291
|
+
; true
|
|
292
|
+
)
|
|
293
|
+
),
|
|
294
|
+
rdf_retractall(EntityURI, Prop, Obj, Graph)
|
|
295
|
+
),
|
|
285
296
|
% Store type as string literal to prevent URI interpretation
|
|
286
297
|
atom_string(Type, TypeStr),
|
|
287
298
|
rdf_assert(EntityURI, kb:type, TypeStr^^'http://www.w3.org/2001/XMLSchema#string', Graph),
|
|
@@ -292,6 +303,22 @@ kb_assert_entity_no_audit(Type, Props) :-
|
|
|
292
303
|
)
|
|
293
304
|
)).
|
|
294
305
|
|
|
306
|
+
%% kb_retract_entity_relationships(+Id)
|
|
307
|
+
% Remove all relationship triples for an entity, preserving property triples.
|
|
308
|
+
% Used by projectStagedEntities to clear stale relationships before re-asserting.
|
|
309
|
+
kb_retract_entity_relationships(Id) :-
|
|
310
|
+
kb_graph(Graph),
|
|
311
|
+
with_kb_mutex((
|
|
312
|
+
entity_id_to_uri(Id, EntityURI),
|
|
313
|
+
forall(
|
|
314
|
+
( rdf(EntityURI, RelURI, TargetURI, Graph),
|
|
315
|
+
atom(TargetURI),
|
|
316
|
+
entity_uri_to_id(TargetURI, _)
|
|
317
|
+
),
|
|
318
|
+
rdf_retractall(EntityURI, RelURI, TargetURI, Graph)
|
|
319
|
+
)
|
|
320
|
+
)).
|
|
321
|
+
|
|
295
322
|
%% kb_log_entity_upsert(+ChangeKind, +Type, +Properties)
|
|
296
323
|
% Append the audit entry for a successfully committed entity upsert.
|
|
297
324
|
kb_log_entity_upsert(ChangeKind, Type, Props) :-
|
|
@@ -319,7 +346,7 @@ kb_retract_entity(Id, Type, AuditProps) :-
|
|
|
319
346
|
kb_graph(Graph),
|
|
320
347
|
with_kb_mutex((
|
|
321
348
|
% Create entity URI
|
|
322
|
-
|
|
349
|
+
entity_id_to_uri(Id, EntityURI),
|
|
323
350
|
% Remove all triples for this entity
|
|
324
351
|
rdf_retractall(EntityURI, _, _, Graph),
|
|
325
352
|
% Log to audit
|
|
@@ -357,8 +384,8 @@ kb_entity(Id, Type, Props) :-
|
|
|
357
384
|
% Find entity by pattern - use unquoted namespace term kb:type
|
|
358
385
|
( var(Id)
|
|
359
386
|
-> rdf(EntityURI, kb:type, TypeLiteral, Graph),
|
|
360
|
-
|
|
361
|
-
;
|
|
387
|
+
entity_uri_to_id(EntityURI, Id)
|
|
388
|
+
; entity_id_to_uri(Id, EntityURI),
|
|
362
389
|
rdf(EntityURI, kb:type, TypeLiteral, Graph)
|
|
363
390
|
),
|
|
364
391
|
% Extract type - convert string literal to atom
|
|
@@ -430,12 +457,24 @@ kb_assert_relationship(RelType, FromId, ToId, Metadata) :-
|
|
|
430
457
|
% leave partial audit residue.
|
|
431
458
|
kb_assert_relationship_no_audit(RelType, FromId, ToId, _Metadata) :-
|
|
432
459
|
kb_graph(Graph),
|
|
433
|
-
% Validate
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
460
|
+
% Validate source entity exists
|
|
461
|
+
( once(kb_entity(FromId, FromType, _))
|
|
462
|
+
-> true
|
|
463
|
+
; throw(error(existence_error(entity, FromId),
|
|
464
|
+
context(kb_assert_relationship, 'Source entity does not exist')))
|
|
465
|
+
),
|
|
466
|
+
% Validate target entity exists
|
|
467
|
+
( once(kb_entity(ToId, ToType, _))
|
|
468
|
+
-> true
|
|
469
|
+
; throw(error(existence_error(entity, ToId),
|
|
470
|
+
context(kb_assert_relationship, 'Target entity does not exist')))
|
|
471
|
+
),
|
|
472
|
+
% Validate relationship type and direction
|
|
473
|
+
( validate_relationship(RelType, FromType, ToType)
|
|
474
|
+
-> true
|
|
475
|
+
; throw(error(type_error(relationship, RelType),
|
|
476
|
+
context(kb_assert_relationship, 'Invalid relationship: ~w from ~w to ~w'-[RelType, FromType, ToType])))
|
|
477
|
+
),
|
|
439
478
|
validate_symbol_role_compatibility(RelType, FromId, ToId),
|
|
440
479
|
% NOTE: Strict-lane fact_kind pairing is validated at the MCP layer
|
|
441
480
|
% via validateStrictLanePairing() before the transaction begins.
|
|
@@ -444,8 +483,8 @@ kb_assert_relationship_no_audit(RelType, FromId, ToId, _Metadata) :-
|
|
|
444
483
|
% Execute RDF operations with mutex protection
|
|
445
484
|
with_kb_mutex((
|
|
446
485
|
% Create entity URIs
|
|
447
|
-
|
|
448
|
-
|
|
486
|
+
entity_id_to_uri(FromId, FromURI),
|
|
487
|
+
entity_id_to_uri(ToId, ToURI),
|
|
449
488
|
% Create relationship property URI (full URI to match saved/loaded RDF)
|
|
450
489
|
kb_uri(BaseURI),
|
|
451
490
|
atom_concat(BaseURI, RelType, RelURI),
|
|
@@ -528,14 +567,45 @@ kb_relationship(RelType, FromId, ToId) :-
|
|
|
528
567
|
% Create relationship property URI (full URI to match loaded RDF)
|
|
529
568
|
kb_uri(BaseURI),
|
|
530
569
|
atom_concat(BaseURI, RelType, RelURI),
|
|
531
|
-
|
|
570
|
+
kb_relationship_with_reluri(RelURI, Graph, FromId, ToId).
|
|
571
|
+
|
|
572
|
+
kb_relationship_with_reluri(RelURI, Graph, FromId, ToId) :-
|
|
573
|
+
nonvar(FromId), nonvar(ToId), !,
|
|
574
|
+
entity_id_to_uri(FromId, FromURI),
|
|
575
|
+
entity_id_to_uri(ToId, ToURI),
|
|
576
|
+
rdf(FromURI, RelURI, ToURI, Graph).
|
|
577
|
+
kb_relationship_with_reluri(RelURI, Graph, FromId, ToId) :-
|
|
578
|
+
nonvar(FromId), var(ToId), !,
|
|
579
|
+
entity_id_to_uri(FromId, FromURI),
|
|
532
580
|
rdf(FromURI, RelURI, ToURI, Graph),
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
581
|
+
entity_uri_to_id(ToURI, ToId).
|
|
582
|
+
kb_relationship_with_reluri(RelURI, Graph, FromId, ToId) :-
|
|
583
|
+
var(FromId), nonvar(ToId), !,
|
|
584
|
+
entity_id_to_uri(ToId, ToURI),
|
|
585
|
+
rdf(FromURI, RelURI, ToURI, Graph),
|
|
586
|
+
entity_uri_to_id(FromURI, FromId).
|
|
587
|
+
kb_relationship_with_reluri(RelURI, Graph, FromId, ToId) :-
|
|
588
|
+
rdf(FromURI, RelURI, ToURI, Graph),
|
|
589
|
+
entity_uri_to_id(FromURI, FromId),
|
|
590
|
+
entity_uri_to_id(ToURI, ToId).
|
|
536
591
|
|
|
537
592
|
% Helper predicates
|
|
538
593
|
|
|
594
|
+
%% entity_id_to_uri(+Id, -URI)
|
|
595
|
+
% Build the canonical entity URI used for RDF read/write.
|
|
596
|
+
entity_id_to_uri(Id, URI) :-
|
|
597
|
+
format(atom(URI), 'kb:entity/~w', [Id]).
|
|
598
|
+
|
|
599
|
+
%% entity_uri_to_id(+URI, -Id)
|
|
600
|
+
% Extract entity ID from canonical or legacy entity URIs.
|
|
601
|
+
entity_uri_to_id(URI, Id) :-
|
|
602
|
+
( kb_uri(BaseURI),
|
|
603
|
+
atom_concat(BaseURI, 'entity/', Prefix),
|
|
604
|
+
atom_concat(Prefix, Id, URI)
|
|
605
|
+
-> true
|
|
606
|
+
; atom_concat('kb:entity/', Id, URI)
|
|
607
|
+
).
|
|
608
|
+
|
|
539
609
|
%% store_property(+EntityURI, +Key, +Value, +Graph)
|
|
540
610
|
% Store a property as an RDF triple with appropriate datatype.
|
|
541
611
|
% All values are stored as typed string literals to avoid URI interpretation issues.
|
|
@@ -1212,8 +1282,8 @@ symbol_has_req_coverage(Symbol, Req) :-
|
|
|
1212
1282
|
production_symbol_covered_for_requirement(Symbol, Req).
|
|
1213
1283
|
|
|
1214
1284
|
test_covers_requirement(Test, Req) :-
|
|
1215
|
-
requirement_test_fallback_allowed(Req),
|
|
1216
1285
|
requirement_verified_by_test(Req, Test),
|
|
1286
|
+
requirement_test_fallback_allowed(Req),
|
|
1217
1287
|
test_satisfies_requirement_semantics(Test, Req).
|
|
1218
1288
|
test_covers_requirement(Test, Req) :-
|
|
1219
1289
|
kb_relationship(specified_by, Req, Scenario),
|