kibi-core 0.13.0 → 0.14.1
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 +3 -0
- package/schema/relationships.pl +8 -0
- package/schema/validation.pl +2 -0
- package/src/checks.pl +69 -36
- package/src/discovery.pl +42 -7
- package/src/kb.pl +328 -18
- package/src/requirement_proof.pl +55 -5
- package/src/rule_registry.pl +40 -0
- package/src/semantic_quality.pl +750 -0
- package/src/status.pl +51 -16
- package/src/units.pl +146 -0
package/src/status.pl
CHANGED
|
@@ -19,7 +19,16 @@ status_meta_dict(StatusDict) :-
|
|
|
19
19
|
snapshot_id(SnapshotId),
|
|
20
20
|
synced_at(DataFile, SyncedAt),
|
|
21
21
|
freshness_state(DataFile, Dirty, SyncState),
|
|
22
|
-
|
|
22
|
+
( SyncState == fresh
|
|
23
|
+
-> % fresh means no indexed source is newer or missing and no knowledge
|
|
24
|
+
% lane or documentation file is newer than the snapshot: exactly the
|
|
25
|
+
% conditions stale_reasons enumerates. Skip its full entity scan,
|
|
26
|
+
% which dominates status latency on large KBs.
|
|
27
|
+
StaleReasons = [],
|
|
28
|
+
StaleReasonCount = 0,
|
|
29
|
+
StaleReasonsTruncated = false
|
|
30
|
+
; stale_reasons(StaleReasons, StaleReasonCount, StaleReasonsTruncated)
|
|
31
|
+
),
|
|
23
32
|
StatusDict = _{
|
|
24
33
|
branch: Branch,
|
|
25
34
|
snapshotId: SnapshotId,
|
|
@@ -90,7 +99,8 @@ synced_at(DataFile, SyncedAt) :-
|
|
|
90
99
|
exists_file(DataFile),
|
|
91
100
|
!,
|
|
92
101
|
time_file(DataFile, Timestamp),
|
|
93
|
-
|
|
102
|
+
stamp_date_time(Timestamp, TimestampUTC, 'UTC'),
|
|
103
|
+
format_time(atom(SyncedAt), '%FT%TZ', TimestampUTC).
|
|
94
104
|
% Before the first successful sync there is no kb.rdf, so the public JSON contract must expose syncedAt: null.
|
|
95
105
|
synced_at(_, null).
|
|
96
106
|
|
|
@@ -250,14 +260,11 @@ documentation_tree_changed(SnapshotTime) :-
|
|
|
250
260
|
!.
|
|
251
261
|
|
|
252
262
|
directory_tree_newer(Path, SnapshotTime) :-
|
|
253
|
-
|
|
254
|
-
entity_documentation_file(Path),
|
|
255
|
-
\+ ignored_documentation_file(Path),
|
|
256
|
-
time_file(Path, EntryTime),
|
|
257
|
-
EntryTime > SnapshotTime,
|
|
263
|
+
newer_entity_documentation_file(Path, SnapshotTime),
|
|
258
264
|
!.
|
|
259
265
|
|
|
260
266
|
directory_tree_newer(Path, SnapshotTime) :-
|
|
267
|
+
\+ ignored_documentation_file(Path),
|
|
261
268
|
exists_directory(Path),
|
|
262
269
|
directory_files(Path, Entries),
|
|
263
270
|
member(Entry, Entries),
|
|
@@ -268,12 +275,9 @@ directory_tree_newer(Path, SnapshotTime) :-
|
|
|
268
275
|
!.
|
|
269
276
|
|
|
270
277
|
directory_tree_newer_path(Path, SnapshotTime, Path) :-
|
|
271
|
-
|
|
272
|
-
entity_documentation_file(Path),
|
|
273
|
-
\+ ignored_documentation_file(Path),
|
|
274
|
-
time_file(Path, EntryTime),
|
|
275
|
-
EntryTime > SnapshotTime.
|
|
278
|
+
newer_entity_documentation_file(Path, SnapshotTime).
|
|
276
279
|
directory_tree_newer_path(Path, SnapshotTime, ChildPath) :-
|
|
280
|
+
\+ ignored_documentation_file(Path),
|
|
277
281
|
exists_directory(Path),
|
|
278
282
|
directory_files(Path, Entries),
|
|
279
283
|
member(Entry, Entries),
|
|
@@ -282,9 +286,22 @@ directory_tree_newer_path(Path, SnapshotTime, ChildPath) :-
|
|
|
282
286
|
directory_file_path(Path, Entry, Candidate),
|
|
283
287
|
directory_tree_newer_path(Candidate, SnapshotTime, ChildPath).
|
|
284
288
|
|
|
289
|
+
:- dynamic workspace_root_memo/2.
|
|
290
|
+
|
|
291
|
+
% Resolved once per attached KB path: status maps every entity source to a
|
|
292
|
+
% workspace-relative path, and re-deriving the root per entity dominated it.
|
|
285
293
|
attached_workspace_root(WorkspaceRoot) :-
|
|
286
294
|
kb:kb_attached(KbPath),
|
|
287
|
-
|
|
295
|
+
( workspace_root_memo(KbPath, Memo)
|
|
296
|
+
-> Memo = found(WorkspaceRoot)
|
|
297
|
+
; ( once(branch_workspace_from_kb_path(KbPath, _Branch, Root))
|
|
298
|
+
-> Memo = found(Root)
|
|
299
|
+
; Memo = none
|
|
300
|
+
),
|
|
301
|
+
retractall(workspace_root_memo(_, _)),
|
|
302
|
+
assertz(workspace_root_memo(KbPath, Memo)),
|
|
303
|
+
Memo = found(WorkspaceRoot)
|
|
304
|
+
).
|
|
288
305
|
|
|
289
306
|
branch_workspace_from_kb_path(KbPath, Branch, WorkspaceRoot) :-
|
|
290
307
|
branch_path_segments(KbPath, BranchesDir, Segments),
|
|
@@ -363,15 +380,33 @@ ignored_documentation_file(Path) :-
|
|
|
363
380
|
file_base_name(Path, 'README.md').
|
|
364
381
|
ignored_documentation_file(Path) :-
|
|
365
382
|
sub_atom(Path, _, _, _, '/tests/e2e/').
|
|
383
|
+
ignored_documentation_file(Path) :-
|
|
384
|
+
sub_atom(Path, _, _, 0, '/tests/e2e').
|
|
366
385
|
ignored_documentation_file(Path) :-
|
|
367
386
|
sub_atom(Path, _, _, _, '/tests/benchmarks/').
|
|
387
|
+
ignored_documentation_file(Path) :-
|
|
388
|
+
sub_atom(Path, _, _, 0, '/tests/benchmarks').
|
|
389
|
+
|
|
390
|
+
%% newer_entity_documentation_file(+Path, +SnapshotTime) is semidet.
|
|
391
|
+
% Cheap path and mtime tests run before the file is read, and the content
|
|
392
|
+
% test is deterministic. Previously each sub_string/5 left a choicepoint per
|
|
393
|
+
% occurrence, so a failing mtime test backtracked through every combination
|
|
394
|
+
% of id:/title:/status: positions (millions of redos on large trees).
|
|
395
|
+
newer_entity_documentation_file(Path, SnapshotTime) :-
|
|
396
|
+
exists_file(Path),
|
|
397
|
+
\+ ignored_documentation_file(Path),
|
|
398
|
+
time_file(Path, EntryTime),
|
|
399
|
+
EntryTime > SnapshotTime,
|
|
400
|
+
entity_documentation_file(Path).
|
|
368
401
|
|
|
369
402
|
entity_documentation_file(Path) :-
|
|
370
403
|
read_file_to_string(Path, Content, []),
|
|
371
404
|
sub_string(Content, 0, 3, _, "---"),
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
sub_string(Content, _, _, _, "
|
|
405
|
+
% These are existence checks, not generators. Repeated metadata examples
|
|
406
|
+
% must not multiply freshness reasons or cause Cartesian backtracking.
|
|
407
|
+
once(sub_string(Content, _, _, _, "id:")),
|
|
408
|
+
once(sub_string(Content, _, _, _, "title:")),
|
|
409
|
+
once(sub_string(Content, _, _, _, "status:")).
|
|
375
410
|
|
|
376
411
|
dict_json_string(Dict, JsonString) :-
|
|
377
412
|
with_output_to(string(JsonString), json_write_dict(current_output, Dict, [])).
|
package/src/units.pl
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
% Module: units
|
|
2
|
+
% Deterministic unit canonicalization for property_value comparison.
|
|
3
|
+
%
|
|
4
|
+
% Authored values are stored unchanged. Checks canonicalize a numeric value to
|
|
5
|
+
% the base unit of its family before computing logical ground signatures and
|
|
6
|
+
% before contradiction, redundancy, or implication comparison, so "30 min" and
|
|
7
|
+
% "1800 s" share one comparable term. Only exact, fixed-ratio conversions are
|
|
8
|
+
% listed: calendar units (month, year) have no fixed length and stay as-is.
|
|
9
|
+
% Unknown units are never equated with anything but themselves.
|
|
10
|
+
%
|
|
11
|
+
% implements REQ-kibi-unit-canonicalization
|
|
12
|
+
|
|
13
|
+
:- module(units, [
|
|
14
|
+
canonical_quantity/6, % +ValueType, +Value, +Unit, -CanonType, -CanonValue, -CanonUnit
|
|
15
|
+
unit_base/3 % ?Unit, -BaseUnit, -Factor
|
|
16
|
+
]).
|
|
17
|
+
|
|
18
|
+
% Aliases are grouped by unit family rather than by predicate.
|
|
19
|
+
:- discontiguous unit_symbol/3, unit_word/3.
|
|
20
|
+
|
|
21
|
+
%% unit_base(?Unit, -BaseUnit, -Factor)
|
|
22
|
+
% Factor converts one Unit into BaseUnit (Value * Factor). Symbols match
|
|
23
|
+
% exactly (case matters: MB is a megabyte, Mb is not listed); spelled-out words
|
|
24
|
+
% match case-insensitively. Ambiguous spellings (KB, Mb, bps, month, year) are
|
|
25
|
+
% deliberately absent so they are never silently equated.
|
|
26
|
+
unit_base(Unit, BaseUnit, Factor) :-
|
|
27
|
+
unit_symbol(Unit, BaseUnit, Factor).
|
|
28
|
+
unit_base(Unit, BaseUnit, Factor) :-
|
|
29
|
+
unit_word(Unit, BaseUnit, Factor).
|
|
30
|
+
|
|
31
|
+
% Duration family, base unit: s (second).
|
|
32
|
+
unit_symbol(ns, s, 1.0e-9).
|
|
33
|
+
unit_symbol(us, s, 1.0e-6).
|
|
34
|
+
unit_symbol(ms, s, 1.0e-3).
|
|
35
|
+
unit_symbol(s, s, 1).
|
|
36
|
+
unit_symbol(sec, s, 1).
|
|
37
|
+
unit_symbol(secs, s, 1).
|
|
38
|
+
unit_symbol(min, s, 60).
|
|
39
|
+
unit_symbol(mins, s, 60).
|
|
40
|
+
unit_symbol(h, s, 3600).
|
|
41
|
+
unit_symbol(hr, s, 3600).
|
|
42
|
+
unit_symbol(hrs, s, 3600).
|
|
43
|
+
unit_symbol(d, s, 86400).
|
|
44
|
+
unit_symbol(wk, s, 604800).
|
|
45
|
+
unit_word(nanosecond, s, 1.0e-9).
|
|
46
|
+
unit_word(nanoseconds, s, 1.0e-9).
|
|
47
|
+
unit_word(microsecond, s, 1.0e-6).
|
|
48
|
+
unit_word(microseconds, s, 1.0e-6).
|
|
49
|
+
unit_word(millisecond, s, 1.0e-3).
|
|
50
|
+
unit_word(milliseconds, s, 1.0e-3).
|
|
51
|
+
unit_word(second, s, 1).
|
|
52
|
+
unit_word(seconds, s, 1).
|
|
53
|
+
unit_word(minute, s, 60).
|
|
54
|
+
unit_word(minutes, s, 60).
|
|
55
|
+
unit_word(hour, s, 3600).
|
|
56
|
+
unit_word(hours, s, 3600).
|
|
57
|
+
unit_word(day, s, 86400).
|
|
58
|
+
unit_word(days, s, 86400).
|
|
59
|
+
unit_word(week, s, 604800).
|
|
60
|
+
unit_word(weeks, s, 604800).
|
|
61
|
+
|
|
62
|
+
% Data size family, base unit: byte. SI prefixes are decimal (kB = 1000 B);
|
|
63
|
+
% IEC prefixes are binary (KiB = 1024 B).
|
|
64
|
+
unit_symbol('B', byte, 1).
|
|
65
|
+
unit_symbol(kB, byte, 1000).
|
|
66
|
+
unit_symbol('MB', byte, 1000000).
|
|
67
|
+
unit_symbol('GB', byte, 1000000000).
|
|
68
|
+
unit_symbol('TB', byte, 1000000000000).
|
|
69
|
+
unit_symbol('KiB', byte, 1024).
|
|
70
|
+
unit_symbol('MiB', byte, 1048576).
|
|
71
|
+
unit_symbol('GiB', byte, 1073741824).
|
|
72
|
+
unit_symbol('TiB', byte, 1099511627776).
|
|
73
|
+
unit_word(byte, byte, 1).
|
|
74
|
+
unit_word(bytes, byte, 1).
|
|
75
|
+
unit_word(kilobyte, byte, 1000).
|
|
76
|
+
unit_word(kilobytes, byte, 1000).
|
|
77
|
+
unit_word(megabyte, byte, 1000000).
|
|
78
|
+
unit_word(megabytes, byte, 1000000).
|
|
79
|
+
unit_word(gigabyte, byte, 1000000000).
|
|
80
|
+
unit_word(gigabytes, byte, 1000000000).
|
|
81
|
+
unit_word(terabyte, byte, 1000000000000).
|
|
82
|
+
unit_word(terabytes, byte, 1000000000000).
|
|
83
|
+
unit_word(kibibyte, byte, 1024).
|
|
84
|
+
unit_word(kibibytes, byte, 1024).
|
|
85
|
+
unit_word(mebibyte, byte, 1048576).
|
|
86
|
+
unit_word(mebibytes, byte, 1048576).
|
|
87
|
+
unit_word(gibibyte, byte, 1073741824).
|
|
88
|
+
unit_word(gibibytes, byte, 1073741824).
|
|
89
|
+
unit_word(tebibyte, byte, 1099511627776).
|
|
90
|
+
unit_word(tebibytes, byte, 1099511627776).
|
|
91
|
+
|
|
92
|
+
% Percentage family, base unit: percent.
|
|
93
|
+
unit_symbol('%', percent, 1).
|
|
94
|
+
unit_word(percent, percent, 1).
|
|
95
|
+
unit_word(percentage, percent, 1).
|
|
96
|
+
unit_word(pct, percent, 1).
|
|
97
|
+
unit_word(basis_point, percent, 0.01).
|
|
98
|
+
unit_word(basis_points, percent, 0.01).
|
|
99
|
+
|
|
100
|
+
%% canonical_quantity(+ValueType, +Value, +Unit, -CanonType, -CanonValue, -CanonUnit)
|
|
101
|
+
% Numeric values with a known unit are converted to the family base unit.
|
|
102
|
+
% Integral numeric results are represented as int so int/number spellings of
|
|
103
|
+
% the same quantity compare equal. Non-numeric values and unknown units pass
|
|
104
|
+
% through unchanged.
|
|
105
|
+
canonical_quantity(ValueType, Value, Unit, CanonType, CanonValue, CanonUnit) :-
|
|
106
|
+
numeric_value_type(ValueType),
|
|
107
|
+
number(Value),
|
|
108
|
+
!,
|
|
109
|
+
( known_unit(Unit, BaseUnit, Factor)
|
|
110
|
+
-> Scaled is Value * Factor,
|
|
111
|
+
CanonUnit = BaseUnit
|
|
112
|
+
; Scaled = Value,
|
|
113
|
+
CanonUnit = Unit
|
|
114
|
+
),
|
|
115
|
+
canonical_number(Scaled, CanonType, CanonValue).
|
|
116
|
+
canonical_quantity(ValueType, Value, Unit, ValueType, Value, Unit).
|
|
117
|
+
|
|
118
|
+
numeric_value_type(int).
|
|
119
|
+
numeric_value_type(number).
|
|
120
|
+
|
|
121
|
+
known_unit(Unit, BaseUnit, Factor) :-
|
|
122
|
+
atom(Unit),
|
|
123
|
+
Unit \== '',
|
|
124
|
+
( unit_symbol(Unit, BaseUnit, Factor)
|
|
125
|
+
-> true
|
|
126
|
+
; downcase_atom(Unit, Lower),
|
|
127
|
+
unit_word(Lower, BaseUnit, Factor)
|
|
128
|
+
),
|
|
129
|
+
!.
|
|
130
|
+
|
|
131
|
+
% Integers (including big integers) stay exact. Floats have binary noise
|
|
132
|
+
% rounded away (0.1 h * 3600 = 360.00000000000006) before deciding whether the
|
|
133
|
+
% canonical value is integral; floats too large to round stay as they are.
|
|
134
|
+
canonical_number(Value, int, Value) :-
|
|
135
|
+
integer(Value),
|
|
136
|
+
!.
|
|
137
|
+
canonical_number(Value, Type, Canonical) :-
|
|
138
|
+
catch(Rounded is round(Value * 1.0e9) / 1.0e9, _, fail),
|
|
139
|
+
!,
|
|
140
|
+
( Rounded =:= round(Rounded)
|
|
141
|
+
-> Type = int,
|
|
142
|
+
Canonical is integer(round(Rounded))
|
|
143
|
+
; Type = number,
|
|
144
|
+
Canonical is float(Rounded)
|
|
145
|
+
).
|
|
146
|
+
canonical_number(Value, number, Value).
|