kibi-core 0.17.3 → 0.18.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kibi-core",
3
- "version": "0.17.3",
3
+ "version": "0.18.0",
4
4
  "private": false,
5
5
  "description": "Core Prolog modules and RDF graph logic for Kibi",
6
6
  "type": "module",
package/src/discovery.pl CHANGED
@@ -32,18 +32,32 @@
32
32
  :- use_module('../schema/relationships.pl', [relationship_type/1]).
33
33
 
34
34
  find_gaps_json(TypeFilter, MissingRelationships, PresentRelationships, Tags, SourceFilter, Limit, Offset, JsonString) :-
35
+ % One pass over the entities: every candidate carries whether it is a
36
+ % provenance stub, so the rows and the stub count share the enumeration.
35
37
  findall(
36
- Row,
37
- matching_gap_row(TypeFilter, MissingRelationships, PresentRelationships, Tags, SourceFilter, Row),
38
- Rows0
38
+ Row-Stub,
39
+ matching_gap_candidate(TypeFilter, MissingRelationships, PresentRelationships, Tags, SourceFilter, Row, Stub),
40
+ Candidates
39
41
  ),
42
+ findall(Row, (member(Row-Stub, Candidates), gap_row_visible(Tags, Stub)), Rows0),
40
43
  sort_dict_rows(Rows0, SortedRows),
41
44
  paginate_rows(SortedRows, Offset, Limit, Rows),
42
45
  length(SortedRows, Count),
46
+ % Provenance stubs that match the same filters are counted apart
47
+ % (summary.provenanceStubs) whether or not the requested tags let them
48
+ % into the rows, so a caller can tell "no gaps" from "only stubs".
49
+ % implements REQ-bootstrap-provenance-stubs
50
+ aggregate_all(count, member(_-true, Candidates), StubCount),
51
+ Summary = _{total: Count, provenanceStubs: StubCount},
43
52
  status_meta_dict(Meta),
44
- Response = _{rows: Rows, count: Count, meta: Meta},
53
+ Response = _{rows: Rows, count: Count, summary: Summary, meta: Meta},
45
54
  dict_json_string(Response, JsonString).
46
55
 
56
+ % A stub enters the rows only when the caller asked for its tag.
57
+ gap_row_visible(_Tags, false).
58
+ gap_row_visible(Tags, true) :-
59
+ memberchk('bootstrap:provenance-stub', Tags).
60
+
47
61
  coverage_report_json(By, Tags, IncludePassing, IncludeTransitive, Limit, Offset, JsonString) :-
48
62
  coverage_report_json(By, Tags, IncludePassing, IncludeTransitive, Limit, Offset, unknown, '1970-01-01T00:00:00Z', 604800, JsonString).
49
63
 
@@ -458,10 +472,15 @@ graph_expand_json(SeedIds, Relationships, Direction, Depth, EntityTypes, MaxNode
458
472
  dict_json_string(Response, JsonString).
459
473
 
460
474
  matching_gap_row(TypeFilter, MissingRelationships, PresentRelationships, Tags, SourceFilter, Row) :-
475
+ matching_gap_candidate(TypeFilter, MissingRelationships, PresentRelationships, Tags, SourceFilter, Row, Stub),
476
+ gap_row_visible(Tags, Stub).
477
+
478
+ % implements REQ-bootstrap-provenance-stubs
479
+ matching_gap_candidate(TypeFilter, MissingRelationships, PresentRelationships, Tags, SourceFilter, Row, Stub) :-
461
480
  kb_entity(Id, Type, Props),
462
481
  matches_type(TypeFilter, Type),
463
482
  matches_tags(Tags, Props),
464
- \+ hidden_provenance_stub(Tags, Type, Props),
483
+ (provenance_stub_props(Type, Props) -> Stub = true ; Stub = false),
465
484
  matches_source(SourceFilter, Props),
466
485
  relationships_missing(Id, MissingRelationships),
467
486
  relationships_present(Id, PresentRelationships),
package/src/kb.pl CHANGED
@@ -15,6 +15,7 @@
15
15
  kb_assert_entity_no_audit/2,
16
16
  kb_commit_upsert/5,
17
17
  kb_commit_upsert_batch/2,
18
+ kb_preview_upsert_contradiction/3,
18
19
  kb_log_entity_upsert/3,
19
20
  kb_retract_entity/1,
20
21
  kb_retract_entity/3,
@@ -1136,6 +1137,36 @@ kb_stage_upsert(Type, Props, Relationships, SkipContradiction, ChangeKind) :-
1136
1137
  kb_log_entity_upsert(ChangeKind, Type, Props),
1137
1138
  kb_commit_relationship_audits(Relationships).
1138
1139
 
1140
+ %% kb_preview_upsert_contradiction(+Type, +Properties, +Relationships)
1141
+ % implements REQ-kibi-operation-interface-parity
1142
+ % Stage one upsert in a transaction that is always rolled back and run the
1143
+ % commit-time requirement contradiction check against the staged KB, so a
1144
+ % kb_upsert dry run refuses exactly what the commit would refuse. Nothing is
1145
+ % written and nothing marks the branch dirty: the no-audit assertions only
1146
+ % touch the RDF store, which the transaction rolls back, and no audit entry,
1147
+ % snapshot save or journal flush follows. The entity index is not
1148
+ % transactional, so the staged id is re-indexed from the restored store.
1149
+ kb_preview_upsert_contradiction(Type, Props, Relationships) :-
1150
+ memberchk(id=Id, Props),
1151
+ with_kb_mutex(
1152
+ call_cleanup(
1153
+ catch(
1154
+ rdf_transaction((
1155
+ kb_assert_entity_no_audit(Type, Props),
1156
+ kb_commit_relationships_no_audit(Relationships),
1157
+ kb_maybe_check_req_contradiction(Type, Id, false),
1158
+ throw(kibi_preview_rolled_back)
1159
+ )),
1160
+ Caught,
1161
+ ( Caught == kibi_preview_rolled_back
1162
+ -> true
1163
+ ; throw(Caught)
1164
+ )
1165
+ ),
1166
+ kb_refresh_entity_index(Id)
1167
+ )
1168
+ ).
1169
+
1139
1170
  kb_stage_upserts([], []).
1140
1171
  kb_stage_upserts([
1141
1172
  upsert(Type, Props, Relationships, SkipContradiction)|Rest
@@ -705,13 +705,22 @@ unique_evidence_by_test(Evidence0, Evidence) :-
705
705
  sort(TestIds0, TestIds),
706
706
  maplist(first_evidence_for_test(Evidence0), TestIds, Evidence).
707
707
 
708
+ % A test shared by scenarios with different accepted scopes keeps its receipt
709
+ % state from any scenario that accepts its scope, independent of scenario order.
710
+ % implements REQ-ui-pattern-component-proof
711
+ first_evidence_for_test(Evidence, TestId, Item) :-
712
+ member(Item, Evidence),
713
+ Item.testId == TestId,
714
+ Item.state \== not_end_to_end,
715
+ !.
708
716
  first_evidence_for_test(Evidence, TestId, Item) :-
709
717
  member(Item, Evidence),
710
718
  Item.testId == TestId,
711
719
  !.
712
720
 
713
721
  scenario_passing_e2e_obligation(Context, TestObligation, Obligation) :-
714
- maplist(test_receipt_evidence(Context), TestObligation.tests, Evidence),
722
+ scenario_accepted_scopes(TestObligation.scenarioId, AcceptedScopes),
723
+ maplist(test_receipt_evidence(Context, AcceptedScopes), TestObligation.tests, Evidence),
715
724
  include(e2e_evidence, Evidence, E2eEvidence),
716
725
  include(non_e2e_evidence, Evidence, NonEndToEndEvidence),
717
726
  evidence_tests_with_state(E2eEvidence, passed, PassingE2eTests),
@@ -734,6 +743,7 @@ scenario_passing_e2e_obligation(Context, TestObligation, Obligation) :-
734
743
  scenarioTestTargets: TestObligation.scenarioTestTargets,
735
744
  invalidScenarioTestTargets: TestObligation.invalidScenarioTestTargets,
736
745
  tests: TestObligation.tests,
746
+ acceptedScopes: AcceptedScopes,
737
747
  e2eTests: E2eTests,
738
748
  nonEndToEndTests: NonEndToEndTests,
739
749
  evidence: Evidence,
@@ -747,6 +757,42 @@ scenario_passing_e2e_obligation(Context, TestObligation, Obligation) :-
747
757
  snapshotUnavailableTests: SnapshotUnavailableTests
748
758
  }.
749
759
 
760
+ %% scenario_accepted_scopes(+ScenarioId, -Scopes)
761
+ % End-to-end evidence proves every scenario. A scenario whose specifying
762
+ % requirements are all grounded only in component-scope UI pattern predicates
763
+ % may also be proven by a unit or integration test that renders the component
764
+ % in isolation, with the same receipt rules (valid, fresh, passing).
765
+ % implements REQ-ui-pattern-component-proof
766
+ scenario_accepted_scopes(ScenarioId, [unit, integration, end_to_end]) :-
767
+ component_scope_scenario(ScenarioId),
768
+ !.
769
+ scenario_accepted_scopes(_ScenarioId, [end_to_end]).
770
+
771
+ %% component_scope_predicate(?Name)
772
+ % Built-in UI pattern predicates a component rendered in isolation can prove:
773
+ % the pattern, its markers and variant parity are visible in the rendered
774
+ % markup. Layout claims (ui_container, visual_layout_rule) depend on real
775
+ % rendering and keep the end-to-end requirement.
776
+ component_scope_predicate(ui_pattern).
777
+ component_scope_predicate(same_pattern).
778
+ component_scope_predicate(pattern_marker).
779
+
780
+ component_scope_scenario(ScenarioId) :-
781
+ findall(ReqId, kb_relationship(specified_by, ReqId, ScenarioId), ReqIds0),
782
+ sort(ReqIds0, ReqIds),
783
+ ReqIds \= [],
784
+ forall(member(ReqId, ReqIds), component_scope_requirement(ReqId)).
785
+
786
+ component_scope_requirement(ReqId) :-
787
+ findall(FactId, kb_relationship(requires_predicate, ReqId, FactId), FactIds),
788
+ FactIds \= [],
789
+ forall(member(FactId, FactIds),
790
+ ( predicate_fact(FactId, _Namespace, Name, _Args, _Polarity),
791
+ component_scope_predicate(Name)
792
+ )),
793
+ \+ kb_relationship(requires_property, ReqId, _),
794
+ \+ kb_relationship(requires_rule, ReqId, _).
795
+
750
796
  e2e_evidence(Item) :- Item.state \= not_end_to_end.
751
797
  non_e2e_evidence(Item) :- Item.state == not_end_to_end.
752
798
 
@@ -799,9 +845,15 @@ evidence_tests_with_state(Evidence, State, TestIds) :-
799
845
  sort(TestIds0, TestIds).
800
846
 
801
847
  test_receipt_evidence(Context, TestId, Evidence) :-
848
+ test_receipt_evidence(Context, [end_to_end], TestId, Evidence).
849
+
850
+ % implements REQ-ui-pattern-component-proof
851
+ % AcceptedScopes lists the verification scopes whose receipts count for the
852
+ % scenario. Tests outside them keep the nonblocking not_end_to_end state.
853
+ test_receipt_evidence(Context, AcceptedScopes, TestId, Evidence) :-
802
854
  kb_entity(TestId, test, Props),
803
855
  test_scope(Props, Scope),
804
- ( Scope \= end_to_end
856
+ ( \+ memberchk(Scope, AcceptedScopes)
805
857
  -> Evidence = _{testId: TestId, state: not_end_to_end, scope: Scope}
806
858
  ; Context.proofSnapshot == unknown
807
859
  -> Evidence = _{testId: TestId, state: snapshot_unavailable, scope: Scope}
@@ -1216,7 +1268,7 @@ type_shape_symbol_with_structural_contract(SymbolId) :-
1216
1268
  memberchk(Status, [active, passing]).
1217
1269
 
1218
1270
  % Stage status plus a typed reason, from a single total clause so status and
1219
- % reason can never disagree (the Align stale-receipts case reported `blocked`
1271
+ % reason can never disagree (a test project's stale-receipts case reported `blocked`
1220
1272
  % with no indication that fresh receipts were the unblock). Clause order keeps
1221
1273
  % the historical precedence: no production symbols at all is `missing` even
1222
1274
  % when E2E evidence is also absent; only then does absent E2E evidence read
@@ -58,6 +58,8 @@ known_rule('adr-proposed').
58
58
  known_rule('predicate-schema-conformance').
59
59
  known_rule('entity-context-missing').
60
60
  known_rule('entity-context-acknowledged').
61
+ known_rule('policy-ownership').
62
+ known_rule('policy-markers').
61
63
 
62
64
  rule_enforcement_class('must-priority-coverage', canonical).
63
65
  rule_enforcement_class('symbol-coverage', canonical).
@@ -103,6 +105,8 @@ rule_enforcement_class('adr-proposed', advisory).
103
105
  rule_enforcement_class('predicate-schema-conformance', advisory).
104
106
  rule_enforcement_class('entity-context-missing', canonical).
105
107
  rule_enforcement_class('entity-context-acknowledged', advisory).
108
+ rule_enforcement_class('policy-ownership', canonical).
109
+ rule_enforcement_class('policy-markers', canonical).
106
110
 
107
111
  rule_implementation('must-priority-coverage', prolog).
108
112
  rule_implementation('symbol-coverage', prolog).
@@ -148,6 +152,8 @@ rule_implementation('adr-proposed', prolog).
148
152
  rule_implementation('predicate-schema-conformance', typescript).
149
153
  rule_implementation('entity-context-missing', typescript).
150
154
  rule_implementation('entity-context-acknowledged', typescript).
155
+ rule_implementation('policy-ownership', typescript).
156
+ rule_implementation('policy-markers', typescript).
151
157
 
152
158
  rule_predicate('must-priority-coverage', check_must_priority_coverage).
153
159
  rule_predicate('symbol-coverage', check_symbol_coverage).
@@ -263,3 +269,5 @@ rule_description('adr-proposed', 'Informational: an ADR that is still proposed a
263
269
  rule_description('predicate-schema-conformance', 'Predicate facts match a predicate_schema (project-local, or the built-in catalog in the default namespace) and use its declared argument constants').
264
270
  rule_description('entity-context-missing', 'A current requirement, scenario, test, ADR or observation/meta fact must carry body context: at least 12 words of prose (requirements: under a Context, Rationale, Why, Background, Source, Notes or Evidence heading) that do not restate the title or semantic_text; entities tagged review:context-missing are acknowledged legacy and exempt').
265
271
  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').
272
+ 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').
273
+ rule_description('policy-markers', 'When a plugin activated for kibi.check-policy.v1 declares marker rules, every file implementing a requirement grounded in the rule\'s pattern predicate (with its declared sibling files) must contain each marker the KB declares for that pattern').