kibi-core 0.18.0 → 0.18.2
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/discovery.pl +19 -2
- package/src/rule_registry.pl +4 -0
- package/src/semantic_quality.pl +49 -8
package/package.json
CHANGED
package/src/discovery.pl
CHANGED
|
@@ -513,17 +513,34 @@ hidden_provenance_stub(RequestedTags, Type, Props) :-
|
|
|
513
513
|
\+ memberchk('bootstrap:provenance-stub', RequestedTags),
|
|
514
514
|
provenance_stub_props(Type, Props).
|
|
515
515
|
|
|
516
|
+
% A requested missing/present relationship is satisfied by its documented
|
|
517
|
+
% inverse as well (docs/entity-schema.md: `validates` is the inverse edge of
|
|
518
|
+
% `verified_by` for req/scenario <-> test links), so a requirement whose test
|
|
519
|
+
% `validates` it is not a `verified_by` gap. relationshipCounts stay per name.
|
|
520
|
+
% implements REQ-mcp-search-discovery
|
|
516
521
|
relationships_missing(_Id, []).
|
|
517
522
|
relationships_missing(Id, [Relationship|Rest]) :-
|
|
518
|
-
|
|
523
|
+
linked_relationship_count(Id, Relationship, 0),
|
|
519
524
|
relationships_missing(Id, Rest).
|
|
520
525
|
|
|
526
|
+
% implements REQ-mcp-search-discovery
|
|
521
527
|
relationships_present(_Id, []).
|
|
522
528
|
relationships_present(Id, [Relationship|Rest]) :-
|
|
523
|
-
|
|
529
|
+
linked_relationship_count(Id, Relationship, Count),
|
|
524
530
|
Count > 0,
|
|
525
531
|
relationships_present(Id, Rest).
|
|
526
532
|
|
|
533
|
+
inverse_relationship(verified_by, validates).
|
|
534
|
+
inverse_relationship(validates, verified_by).
|
|
535
|
+
|
|
536
|
+
linked_relationship_count(Id, Relationship, Count) :-
|
|
537
|
+
relationship_count(Id, Relationship, Direct),
|
|
538
|
+
( inverse_relationship(Relationship, Inverse)
|
|
539
|
+
-> relationship_count(Id, Inverse, Reverse)
|
|
540
|
+
; Reverse = 0
|
|
541
|
+
),
|
|
542
|
+
Count is Direct + Reverse.
|
|
543
|
+
|
|
527
544
|
relationship_counts(Id, CountsDict) :-
|
|
528
545
|
findall(Relationship-Count,
|
|
529
546
|
(relationship_type(Relationship), relationship_count(Id, Relationship, Count)),
|
package/src/rule_registry.pl
CHANGED
|
@@ -19,6 +19,7 @@ known_rule('symbol-coverage').
|
|
|
19
19
|
known_rule('symbol-traceability').
|
|
20
20
|
known_rule('no-dangling-refs').
|
|
21
21
|
known_rule('source-relationship-parity').
|
|
22
|
+
known_rule('branch-store-not-compiled').
|
|
22
23
|
known_rule('source-path-dangling').
|
|
23
24
|
known_rule('no-cycles').
|
|
24
25
|
known_rule('required-fields').
|
|
@@ -66,6 +67,7 @@ rule_enforcement_class('symbol-coverage', canonical).
|
|
|
66
67
|
rule_enforcement_class('symbol-traceability', canonical).
|
|
67
68
|
rule_enforcement_class('no-dangling-refs', canonical).
|
|
68
69
|
rule_enforcement_class('source-relationship-parity', canonical).
|
|
70
|
+
rule_enforcement_class('branch-store-not-compiled', canonical).
|
|
69
71
|
rule_enforcement_class('source-path-dangling', canonical).
|
|
70
72
|
rule_enforcement_class('no-cycles', canonical).
|
|
71
73
|
rule_enforcement_class('required-fields', canonical).
|
|
@@ -113,6 +115,7 @@ rule_implementation('symbol-coverage', prolog).
|
|
|
113
115
|
rule_implementation('symbol-traceability', prolog).
|
|
114
116
|
rule_implementation('no-dangling-refs', prolog).
|
|
115
117
|
rule_implementation('source-relationship-parity', typescript).
|
|
118
|
+
rule_implementation('branch-store-not-compiled', typescript).
|
|
116
119
|
rule_implementation('source-path-dangling', typescript).
|
|
117
120
|
rule_implementation('no-cycles', prolog).
|
|
118
121
|
rule_implementation('required-fields', prolog).
|
|
@@ -230,6 +233,7 @@ rule_description('symbol-coverage', 'Production symbols need qualifying coverage
|
|
|
230
233
|
rule_description('symbol-traceability', 'Production symbols must directly implement requirements for ownership; covered_by is coverage only and executable_for is test identity only').
|
|
231
234
|
rule_description('no-dangling-refs', 'All relationship targets must exist in the KB').
|
|
232
235
|
rule_description('source-relationship-parity', 'Authored Markdown and relationship-shard edges must exactly match the compiled KB').
|
|
236
|
+
rule_description('branch-store-not-compiled', 'The current branch\'s KB store must be compiled from the authored .kb/ sources; a missing or empty store is reported once, naming kibi sync, instead of every store-backed rule reading an empty KB').
|
|
233
237
|
rule_description('source-path-dangling', 'An authored entity\'s source field must name an existing workspace path (a #anchor suffix is ignored), an existing entity id, or an http(s) URL; a missing source is allowed').
|
|
234
238
|
rule_description('no-cycles', 'No circular dependency chains in requirements').
|
|
235
239
|
rule_description('required-fields', 'All entities must have required fields').
|
package/src/semantic_quality.pl
CHANGED
|
@@ -363,7 +363,10 @@ implication_witness_violation(Witness, violation(
|
|
|
363
363
|
% was minted as several facts so requirements about it never meet: more than
|
|
364
364
|
% one active subject fact for the same subject_key, and more than one active
|
|
365
365
|
% property_value fact stating the same claim: the same subject_key,
|
|
366
|
-
% property_key, operator, typed value and unit.
|
|
366
|
+
% property_key, operator, polarity (absent = require), typed value and unit.
|
|
367
|
+
% A `forbid` fact states the opposite of its `require` twin, so the pair is
|
|
368
|
+
% never one claim. Facts linked only by superseded or deprecated requirements
|
|
369
|
+
% are history, not live duplicates. Facts that differ in operator
|
|
367
370
|
% (the two bounds of a range) or in value (two requirements bounding the same
|
|
368
371
|
% property differently) are distinct claims, which domain-contradictions and
|
|
369
372
|
% domain-implication compare. Each group is reported once, on its first fact
|
|
@@ -390,7 +393,8 @@ duplicate_subject_fact_violations(Violations) :-
|
|
|
390
393
|
findall(
|
|
391
394
|
SubjectKey-FactId,
|
|
392
395
|
( fact_subject_key_of_kind(FactId, subject, SubjectKey),
|
|
393
|
-
active_fact(FactId)
|
|
396
|
+
active_fact(FactId),
|
|
397
|
+
identity_live_fact(FactId)
|
|
394
398
|
),
|
|
395
399
|
Pairs0
|
|
396
400
|
),
|
|
@@ -423,15 +427,17 @@ duplicate_subject_fact_violations(Violations) :-
|
|
|
423
427
|
%% duplicate_property_fact_violations(-Violations)
|
|
424
428
|
duplicate_property_fact_violations(Violations) :-
|
|
425
429
|
findall(
|
|
426
|
-
key(SubjectKey, PropertyKey, Operator, Value)-FactId,
|
|
430
|
+
key(SubjectKey, PropertyKey, Operator, Polarity, Value)-FactId,
|
|
427
431
|
( fact_subject_key_of_kind(FactId, property_value, SubjectKey),
|
|
428
432
|
kb_entity(FactId, fact, Props),
|
|
429
433
|
memberchk(property_key=RawProperty, Props),
|
|
430
434
|
normalize_term_atom(RawProperty, PropertyKey),
|
|
431
435
|
memberchk(operator=RawOperator, Props),
|
|
432
436
|
normalize_term_atom(RawOperator, Operator),
|
|
437
|
+
property_value_polarity(Props, Polarity),
|
|
433
438
|
property_value_signature(Props, Value),
|
|
434
|
-
active_fact(FactId)
|
|
439
|
+
active_fact(FactId),
|
|
440
|
+
identity_live_fact(FactId)
|
|
435
441
|
),
|
|
436
442
|
Pairs0
|
|
437
443
|
),
|
|
@@ -444,20 +450,55 @@ duplicate_property_fact_violations(Violations) :-
|
|
|
444
450
|
Description,
|
|
445
451
|
"Link every requirement to one shared fact and remove the duplicates (kb_delete) once nothing links them",
|
|
446
452
|
Source,
|
|
447
|
-
_{subjectKey: SubjectKey, propertyKey: PropertyKey, operator: Operator, facts: FactIds}
|
|
453
|
+
_{subjectKey: SubjectKey, propertyKey: PropertyKey, operator: Operator, polarity: Polarity, facts: FactIds}
|
|
448
454
|
),
|
|
449
|
-
( member(key(SubjectKey, PropertyKey, Operator, _)-FactIds, Groups),
|
|
455
|
+
( member(key(SubjectKey, PropertyKey, Operator, Polarity, _)-FactIds, Groups),
|
|
450
456
|
FactIds = [FirstId, _|_],
|
|
451
457
|
length(FactIds, Count),
|
|
452
458
|
atomic_list_concat(FactIds, ', ', FactText),
|
|
453
459
|
format(string(Description),
|
|
454
|
-
"~w active property_value facts state the same claim on ~w ~w (operator ~w, same value) (~w), so one claim is minted as several facts",
|
|
455
|
-
[Count, SubjectKey, PropertyKey, Operator, FactText]),
|
|
460
|
+
"~w active property_value facts state the same claim on ~w ~w (operator ~w, polarity ~w, same value) (~w), so one claim is minted as several facts",
|
|
461
|
+
[Count, SubjectKey, PropertyKey, Operator, Polarity, FactText]),
|
|
456
462
|
entity_text(FirstId, fact, source, Source)
|
|
457
463
|
),
|
|
458
464
|
Violations
|
|
459
465
|
).
|
|
460
466
|
|
|
467
|
+
%% property_value_polarity(+Props, -Polarity)
|
|
468
|
+
% The polarity of a property_value fact; an absent polarity means require.
|
|
469
|
+
% `forbid` states the opposite claim of `require` on the same tuple, so the
|
|
470
|
+
% two never count as one claim minted twice.
|
|
471
|
+
% implements REQ-kibi-subject-vocabulary
|
|
472
|
+
property_value_polarity(Props, Polarity) :-
|
|
473
|
+
( memberchk(polarity=Raw, Props),
|
|
474
|
+
normalize_term_atom(Raw, Polarity0),
|
|
475
|
+
Polarity0 \== ''
|
|
476
|
+
-> Polarity = Polarity0
|
|
477
|
+
; Polarity = require
|
|
478
|
+
).
|
|
479
|
+
|
|
480
|
+
%% identity_live_fact(+FactId)
|
|
481
|
+
% A fact still carried by the current requirement set: no requirement links
|
|
482
|
+
% it (an orphan duplicate stays visible), or at least one current requirement
|
|
483
|
+
% links it. A fact linked only by superseded or deprecated requirements is
|
|
484
|
+
% history kept for the append-only supersedes chain, not a live duplicate.
|
|
485
|
+
% `closed` means done, not retired, so a closed requirement still counts.
|
|
486
|
+
% implements REQ-kibi-subject-vocabulary
|
|
487
|
+
identity_live_fact(FactId) :-
|
|
488
|
+
findall(
|
|
489
|
+
ReqId,
|
|
490
|
+
( member(LinkType, [constrains, requires_property, requires_predicate, requires_rule]),
|
|
491
|
+
kb_relationship(LinkType, ReqId, FactId)
|
|
492
|
+
),
|
|
493
|
+
Linkers
|
|
494
|
+
),
|
|
495
|
+
( Linkers == []
|
|
496
|
+
-> true
|
|
497
|
+
; member(ReqId, Linkers),
|
|
498
|
+
kb:current_req(ReqId)
|
|
499
|
+
-> true
|
|
500
|
+
).
|
|
501
|
+
|
|
461
502
|
%% property_value_signature(+Props, -Signature)
|
|
462
503
|
% The typed value and unit of a property_value fact, as Field-Value pairs in a
|
|
463
504
|
% fixed field order, so two facts compare equal only when they store the same
|