kibi-cli 0.17.0 → 0.18.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.
Files changed (181) hide show
  1. package/dist/cli-json-command.d.ts.map +1 -1
  2. package/dist/cli-json-command.js +87 -11
  3. package/dist/cli-register-maintenance.d.ts.map +1 -1
  4. package/dist/cli-register-maintenance.js +8 -0
  5. package/dist/cli-register-reporting.d.ts.map +1 -1
  6. package/dist/cli-register-reporting.js +5 -0
  7. package/dist/cli.d.ts.map +1 -1
  8. package/dist/cli.js +2 -1
  9. package/dist/commands/aggregated-checks.d.ts.map +1 -1
  10. package/dist/commands/aggregated-checks.js +1 -0
  11. package/dist/commands/coverage.d.ts +5 -0
  12. package/dist/commands/coverage.d.ts.map +1 -1
  13. package/dist/commands/coverage.js +5 -0
  14. package/dist/commands/discovery-table.d.ts.map +1 -1
  15. package/dist/commands/discovery-table.js +96 -5
  16. package/dist/commands/sync/cache.d.ts +2 -0
  17. package/dist/commands/sync/cache.d.ts.map +1 -1
  18. package/dist/commands/sync/cache.js +8 -0
  19. package/dist/commands/sync/persistence.d.ts.map +1 -1
  20. package/dist/commands/sync/persistence.js +58 -1
  21. package/dist/commands/sync.d.ts.map +1 -1
  22. package/dist/commands/sync.js +72 -15
  23. package/dist/commands/usage-metrics.d.ts +66 -2
  24. package/dist/commands/usage-metrics.d.ts.map +1 -1
  25. package/dist/commands/usage-metrics.js +34 -24
  26. package/dist/commands/usage-remediation.d.ts +9 -0
  27. package/dist/commands/usage-remediation.d.ts.map +1 -0
  28. package/dist/commands/usage-remediation.js +52 -0
  29. package/dist/extractors/markdown.d.ts +8 -0
  30. package/dist/extractors/markdown.d.ts.map +1 -1
  31. package/dist/extractors/markdown.js +69 -2
  32. package/dist/operations/modeling/logic-modeling.d.ts.map +1 -1
  33. package/dist/operations/modeling/logic-modeling.js +14 -5
  34. package/dist/operations/modeling/model-requirement.d.ts.map +1 -1
  35. package/dist/operations/modeling/model-requirement.js +27 -2
  36. package/dist/operations/modeling/predicate-applyplan.d.ts +1 -1
  37. package/dist/operations/modeling/predicate-applyplan.d.ts.map +1 -1
  38. package/dist/operations/modeling/predicate-applyplan.js +18 -6
  39. package/dist/operations/modeling/predicate-inference.d.ts.map +1 -1
  40. package/dist/operations/modeling/predicate-inference.js +5 -17
  41. package/dist/operations/modeling/predicate-loader.js +1 -1
  42. package/dist/operations/modeling/predicate-types.d.ts +9 -1
  43. package/dist/operations/modeling/predicate-types.d.ts.map +1 -1
  44. package/dist/operations/modeling/suggest-predicates.d.ts.map +1 -1
  45. package/dist/operations/modeling/suggest-predicates.js +35 -11
  46. package/dist/operations/mutation/audit.js +1 -1
  47. package/dist/operations/mutation/contradictions.d.ts +1 -1
  48. package/dist/operations/mutation/contradictions.d.ts.map +1 -1
  49. package/dist/operations/mutation/contradictions.js +23 -18
  50. package/dist/operations/mutation/serialization.d.ts.map +1 -1
  51. package/dist/operations/mutation/serialization.js +6 -3
  52. package/dist/operations/mutation/upsert.d.ts +3 -1
  53. package/dist/operations/mutation/upsert.d.ts.map +1 -1
  54. package/dist/operations/mutation/upsert.js +51 -12
  55. package/dist/operations/mutation/validate-upsert.d.ts.map +1 -1
  56. package/dist/operations/mutation/validate-upsert.js +18 -2
  57. package/dist/operations/mutation/validation.d.ts.map +1 -1
  58. package/dist/operations/mutation/validation.js +8 -0
  59. package/dist/operations/semantic-advisor/analysis-receipt.d.ts.map +1 -1
  60. package/dist/operations/semantic-advisor/analysis-receipt.js +7 -1
  61. package/dist/operations/semantic-advisor/analyze-prose.d.ts.map +1 -1
  62. package/dist/operations/semantic-advisor/analyze-prose.js +76 -61
  63. package/dist/operations/semantic-advisor/clauses.d.ts +1 -0
  64. package/dist/operations/semantic-advisor/clauses.d.ts.map +1 -1
  65. package/dist/operations/semantic-advisor/clauses.js +7 -7
  66. package/dist/operations/semantic-advisor/ingestion-boundary.d.ts +18 -0
  67. package/dist/operations/semantic-advisor/ingestion-boundary.d.ts.map +1 -0
  68. package/dist/operations/semantic-advisor/ingestion-boundary.js +167 -0
  69. package/dist/operations/semantic-advisor/shared.d.ts +8 -0
  70. package/dist/operations/semantic-advisor/shared.d.ts.map +1 -1
  71. package/dist/operations/semantic-advisor/shared.js +37 -3
  72. package/dist/operations/semantic-advisor/types.d.ts +5 -0
  73. package/dist/operations/semantic-advisor/types.d.ts.map +1 -1
  74. package/dist/prolog/codec.d.ts.map +1 -1
  75. package/dist/prolog/codec.js +9 -2
  76. package/dist/prolog.d.ts +6 -0
  77. package/dist/prolog.d.ts.map +1 -1
  78. package/dist/prolog.js +240 -61
  79. package/dist/public/diagnostic-usage.d.ts +17 -0
  80. package/dist/public/diagnostic-usage.d.ts.map +1 -0
  81. package/dist/public/diagnostic-usage.js +152 -0
  82. package/dist/public/distribution-parity.d.ts +90 -0
  83. package/dist/public/distribution-parity.d.ts.map +1 -0
  84. package/dist/public/distribution-parity.js +367 -0
  85. package/dist/public/impact/full-kb-quality.d.ts +2 -0
  86. package/dist/public/impact/full-kb-quality.d.ts.map +1 -1
  87. package/dist/public/impact/full-kb-quality.js +53 -1
  88. package/dist/public/impact-diagnostics.d.ts +2 -0
  89. package/dist/public/impact-diagnostics.d.ts.map +1 -1
  90. package/dist/public/impact-diagnostics.js +1 -0
  91. package/dist/public/operations/catalog.d.ts +151 -6
  92. package/dist/public/operations/catalog.d.ts.map +1 -1
  93. package/dist/public/operations/check-executor.d.ts.map +1 -1
  94. package/dist/public/operations/check-executor.js +4 -0
  95. package/dist/public/operations/discovery-executors.d.ts +6 -0
  96. package/dist/public/operations/discovery-executors.d.ts.map +1 -1
  97. package/dist/public/operations/discovery-executors.js +18 -2
  98. package/dist/public/operations/index.d.ts +4 -0
  99. package/dist/public/operations/index.d.ts.map +1 -1
  100. package/dist/public/operations/index.js +4 -0
  101. package/dist/public/operations/legacy-migration-plan.d.ts +113 -0
  102. package/dist/public/operations/legacy-migration-plan.d.ts.map +1 -0
  103. package/dist/public/operations/legacy-migration-plan.js +409 -0
  104. package/dist/public/operations/node-ports.d.ts.map +1 -1
  105. package/dist/public/operations/node-ports.js +90 -0
  106. package/dist/public/operations/repair-plan.d.ts +184 -0
  107. package/dist/public/operations/repair-plan.d.ts.map +1 -0
  108. package/dist/public/operations/repair-plan.js +397 -0
  109. package/dist/public/operations/runtime-types.d.ts +7 -0
  110. package/dist/public/operations/runtime-types.d.ts.map +1 -1
  111. package/dist/public/operations/specs/check.d.ts +2 -2
  112. package/dist/public/operations/specs/check.js +2 -2
  113. package/dist/public/operations/specs/discovery.d.ts +1 -1
  114. package/dist/public/operations/specs/discovery.js +1 -1
  115. package/dist/public/operations/specs/modeling.d.ts +19 -1
  116. package/dist/public/operations/specs/modeling.d.ts.map +1 -1
  117. package/dist/public/operations/specs/modeling.js +16 -1
  118. package/dist/public/operations/specs/mutation-schemas.d.ts +96 -1
  119. package/dist/public/operations/specs/mutation-schemas.d.ts.map +1 -1
  120. package/dist/public/operations/specs/mutation-schemas.js +40 -1
  121. package/dist/public/operations/specs/reporting.d.ts +42 -1
  122. package/dist/public/operations/specs/reporting.d.ts.map +1 -1
  123. package/dist/public/operations/specs/reporting.js +70 -6
  124. package/dist/public/operations/specs/semantic.js +1 -1
  125. package/dist/public/operations/specs/upsert.d.ts +96 -1
  126. package/dist/public/operations/specs/upsert.d.ts.map +1 -1
  127. package/dist/public/operations/workspace-snapshot.d.ts +10 -0
  128. package/dist/public/operations/workspace-snapshot.d.ts.map +1 -0
  129. package/dist/public/operations/workspace-snapshot.js +20 -0
  130. package/dist/public/schemas/entity.d.ts.map +1 -1
  131. package/dist/public/schemas/entity.js +43 -1
  132. package/dist/public/skills/kibi-traceability/SKILL.md +7 -1
  133. package/dist/public/skills/kibi-usage/SKILL.md +10 -4
  134. package/dist/public/skills/kibi-usage/resources/fact-lanes.md +1 -1
  135. package/dist/public/skills/kibi-usage/resources/logic-ir.md +1 -1
  136. package/dist/public/skills/kibi-usage/resources/workflows.md +19 -2
  137. package/dist/public/telemetry-acceptance.d.ts +89 -0
  138. package/dist/public/telemetry-acceptance.d.ts.map +1 -0
  139. package/dist/public/telemetry-acceptance.js +597 -0
  140. package/dist/public/telemetry-remediation.d.ts +33 -0
  141. package/dist/public/telemetry-remediation.d.ts.map +1 -0
  142. package/dist/public/telemetry-remediation.js +257 -0
  143. package/dist/public/verification-receipt.d.ts +75 -0
  144. package/dist/public/verification-receipt.d.ts.map +1 -0
  145. package/dist/public/verification-receipt.js +129 -0
  146. package/dist/schemas/entity.schema.json +96 -2
  147. package/dist/traceability/temp-kb.d.ts.map +1 -1
  148. package/dist/traceability/temp-kb.js +36 -3
  149. package/dist/types/entities.d.ts +1 -0
  150. package/dist/types/entities.d.ts.map +1 -1
  151. package/dist/utils/rule-registry.d.ts +2 -0
  152. package/dist/utils/rule-registry.d.ts.map +1 -1
  153. package/package.json +30 -2
  154. package/src/public/diagnostic-usage.ts +206 -0
  155. package/src/public/distribution-parity.ts +580 -0
  156. package/src/public/impact/full-kb-quality.ts +74 -1
  157. package/src/public/impact-diagnostics.ts +16 -0
  158. package/src/public/operations/check-executor.ts +4 -0
  159. package/src/public/operations/discovery-executors.ts +24 -2
  160. package/src/public/operations/index.ts +4 -0
  161. package/src/public/operations/legacy-migration-plan.ts +688 -0
  162. package/src/public/operations/node-ports.ts +109 -0
  163. package/src/public/operations/repair-plan.ts +562 -0
  164. package/src/public/operations/runtime-types.ts +8 -0
  165. package/src/public/operations/specs/check.ts +2 -2
  166. package/src/public/operations/specs/discovery.ts +1 -1
  167. package/src/public/operations/specs/modeling.ts +19 -1
  168. package/src/public/operations/specs/mutation-schemas.ts +47 -1
  169. package/src/public/operations/specs/reporting.ts +91 -6
  170. package/src/public/operations/specs/semantic.ts +1 -1
  171. package/src/public/operations/workspace-snapshot.ts +28 -0
  172. package/src/public/schemas/entity.ts +44 -1
  173. package/src/public/skills/kibi-traceability/SKILL.md +7 -1
  174. package/src/public/skills/kibi-usage/SKILL.md +10 -4
  175. package/src/public/skills/kibi-usage/resources/fact-lanes.md +1 -1
  176. package/src/public/skills/kibi-usage/resources/logic-ir.md +1 -1
  177. package/src/public/skills/kibi-usage/resources/workflows.md +19 -2
  178. package/src/public/telemetry-acceptance.ts +859 -0
  179. package/src/public/telemetry-remediation.ts +462 -0
  180. package/src/public/verification-receipt.ts +170 -0
  181. package/src/schemas/entity.schema.json +96 -2
@@ -0,0 +1,580 @@
1
+ /*
2
+ Kibi — repo-local, per-branch, queryable long-term memory for software projects
3
+ Copyright (C) 2026 Piotr Franczyk
4
+
5
+ This program is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU Affero General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+ */
10
+
11
+ import { existsSync, readFileSync, realpathSync } from "node:fs";
12
+ import path from "node:path";
13
+
14
+ export const DISTRIBUTION_PARITY_VERSION =
15
+ "kibi.distribution-parity.v1" as const;
16
+
17
+ export const REQUIREMENT_COMPILER_CAPABILITIES = [
18
+ "semantic_inventory",
19
+ "contradiction_witnesses",
20
+ "conservative_proof",
21
+ "repair_plan",
22
+ "verification_receipts",
23
+ "telemetry_acceptance",
24
+ ] as const;
25
+
26
+ export type RequirementCompilerCapability =
27
+ (typeof REQUIREMENT_COMPILER_CAPABILITIES)[number];
28
+ export type DistributionRuntimeKind =
29
+ | "source_checkout"
30
+ | "packed_artifact"
31
+ | "project_resolved";
32
+ export type DistributionSurface = "cli" | "mcp";
33
+ export type CapabilityState = "supported" | "unsupported" | "failed";
34
+
35
+ export interface DistributionRuntimeProvenance {
36
+ readonly status: "resolved" | "unresolved";
37
+ readonly executable: string;
38
+ readonly entrypoint?: string;
39
+ readonly packageRoot?: string;
40
+ readonly version?: string;
41
+ readonly evidence: "executable_resolution" | "entrypoint_resolution";
42
+ readonly detail?: string;
43
+ }
44
+
45
+ export interface DistributionRuntime {
46
+ readonly id: string;
47
+ readonly kind: DistributionRuntimeKind;
48
+ readonly surface: DistributionSurface;
49
+ readonly project?: string;
50
+ readonly provenance: DistributionRuntimeProvenance;
51
+ readonly actions?: Partial<
52
+ Record<RequirementCompilerCapability, DistributionParityAction>
53
+ >;
54
+ }
55
+
56
+ export interface DistributionParityAction {
57
+ readonly kind: "upgrade" | "compatibility";
58
+ readonly detail: string;
59
+ }
60
+
61
+ export interface DistributionCapabilityResult {
62
+ readonly state: CapabilityState;
63
+ readonly outcome?: unknown;
64
+ readonly diagnosticIds?: readonly string[];
65
+ readonly detail?: string;
66
+ }
67
+
68
+ export interface DistributionRuntimeAdapter {
69
+ readonly runtime: DistributionRuntime;
70
+ readonly execute: (
71
+ capability: RequirementCompilerCapability,
72
+ ) => Promise<DistributionCapabilityResult>;
73
+ }
74
+
75
+ export interface DistributionParityObservation
76
+ extends DistributionCapabilityResult {
77
+ readonly runtimeId: string;
78
+ readonly capability: RequirementCompilerCapability;
79
+ }
80
+
81
+ export type DistributionComparison =
82
+ | "match"
83
+ | "diverged"
84
+ | "unsupported"
85
+ | "failed"
86
+ | "not_compared";
87
+
88
+ export interface DistributionParityRow {
89
+ readonly runtimeId: string;
90
+ readonly kind: DistributionRuntimeKind;
91
+ readonly surface: DistributionSurface;
92
+ readonly project?: string;
93
+ readonly capability: RequirementCompilerCapability;
94
+ readonly state: CapabilityState | "missing";
95
+ readonly comparison: DistributionComparison;
96
+ readonly referenceRuntimeId?: string;
97
+ readonly normalizedOutcome?: unknown;
98
+ readonly diagnosticIds: readonly string[];
99
+ readonly action?: DistributionParityAction;
100
+ readonly detail?: string;
101
+ }
102
+
103
+ export type DistributionParityIssueCode =
104
+ | "duplicate_runtime_id"
105
+ | "missing_current_runtime"
106
+ | "unresolved_runtime_provenance"
107
+ | "missing_capability_observation"
108
+ | "duplicate_capability_observation"
109
+ | "current_capability_unsupported"
110
+ | "capability_execution_failed"
111
+ | "source_packed_mismatch"
112
+ | "project_divergence_without_action";
113
+
114
+ export interface DistributionParityIssue {
115
+ readonly code: DistributionParityIssueCode;
116
+ readonly runtimeId?: string;
117
+ readonly surface?: DistributionSurface;
118
+ readonly capability?: RequirementCompilerCapability;
119
+ readonly detail: string;
120
+ }
121
+
122
+ export interface DistributionParityReport {
123
+ readonly version: typeof DISTRIBUTION_PARITY_VERSION;
124
+ readonly status: "passed" | "failed";
125
+ readonly capabilities: readonly RequirementCompilerCapability[];
126
+ readonly runtimes: readonly DistributionRuntime[];
127
+ readonly rows: readonly DistributionParityRow[];
128
+ readonly issues: readonly DistributionParityIssue[];
129
+ readonly summary: {
130
+ readonly runtimeCount: number;
131
+ readonly observationCount: number;
132
+ readonly matchCount: number;
133
+ readonly divergenceCount: number;
134
+ readonly unsupportedCount: number;
135
+ readonly failedCount: number;
136
+ readonly actionCount: number;
137
+ };
138
+ }
139
+
140
+ export interface DistributionParityOptions {
141
+ readonly workspaceRoots?: readonly string[];
142
+ readonly volatileKeys?: ReadonlySet<string>;
143
+ }
144
+
145
+ const DEFAULT_VOLATILE_KEYS = new Set([
146
+ "_diagnostic_telemetry",
147
+ "active_branch",
148
+ "artifact_digest",
149
+ "branch",
150
+ "branchName",
151
+ "code_snapshot",
152
+ "created_at",
153
+ "createdAt",
154
+ "elapsedMs",
155
+ "environment_hash",
156
+ "finished_at",
157
+ "finishedAt",
158
+ "pid",
159
+ "planId",
160
+ "prologPid",
161
+ "receipt_id",
162
+ "request_id",
163
+ "requestId",
164
+ "snapshot_id",
165
+ "snapshotId",
166
+ "started_at",
167
+ "startedAt",
168
+ "timestamp",
169
+ "updated_at",
170
+ "updatedAt",
171
+ "usageLogLineNumber",
172
+ "verificationSnapshot",
173
+ ]);
174
+
175
+ type PackageManifest = {
176
+ readonly name?: string;
177
+ readonly version?: string;
178
+ };
179
+
180
+ function nearestPackageInfo(entrypoint: string): {
181
+ readonly packageRoot?: string;
182
+ readonly version?: string;
183
+ } {
184
+ let current = path.dirname(entrypoint);
185
+ while (true) {
186
+ const manifestPath = path.join(current, "package.json");
187
+ if (existsSync(manifestPath)) {
188
+ try {
189
+ const manifest = JSON.parse(
190
+ readFileSync(manifestPath, "utf8"),
191
+ ) as PackageManifest;
192
+ return {
193
+ packageRoot: current,
194
+ ...(manifest.version === undefined
195
+ ? {}
196
+ : { version: manifest.version }),
197
+ };
198
+ } catch {
199
+ return { packageRoot: current };
200
+ }
201
+ }
202
+ const parent = path.dirname(current);
203
+ if (parent === current) return {};
204
+ current = parent;
205
+ }
206
+ }
207
+
208
+ function declaredEntrypoint(contents: string): string | undefined {
209
+ const shimTarget = contents.match(/^# cmd-shim-target=(.+)$/m)?.[1]?.trim();
210
+ if (shimTarget) return shimTarget;
211
+ const absoluteImport = contents.match(/\bfrom\s+["'](\/(?:[^"']+))["']/)?.[1];
212
+ return absoluteImport;
213
+ }
214
+
215
+ // implements REQ-kibi-distribution-parity-matrix
216
+ export function resolveDistributionRuntimeProvenance(
217
+ executable: string,
218
+ ): DistributionRuntimeProvenance {
219
+ const requested = path.resolve(executable);
220
+ if (!existsSync(requested)) {
221
+ return {
222
+ status: "unresolved",
223
+ executable: requested,
224
+ evidence: "executable_resolution",
225
+ detail: `Executable does not exist: ${requested}`,
226
+ };
227
+ }
228
+ try {
229
+ const resolvedExecutable = realpathSync(requested);
230
+ const contents = readFileSync(resolvedExecutable, "utf8");
231
+ const declared = declaredEntrypoint(contents);
232
+ const entrypoint = declared
233
+ ? realpathSync(path.resolve(path.dirname(resolvedExecutable), declared))
234
+ : resolvedExecutable;
235
+ const packageInfo = nearestPackageInfo(entrypoint);
236
+ return {
237
+ status: "resolved",
238
+ executable: resolvedExecutable,
239
+ entrypoint,
240
+ ...packageInfo,
241
+ evidence:
242
+ entrypoint === resolvedExecutable
243
+ ? "executable_resolution"
244
+ : "entrypoint_resolution",
245
+ };
246
+ } catch (error) {
247
+ return {
248
+ status: "unresolved",
249
+ executable: requested,
250
+ evidence: "executable_resolution",
251
+ detail: error instanceof Error ? error.message : String(error),
252
+ };
253
+ }
254
+ }
255
+
256
+ function normalizeString(value: string, roots: readonly string[]): string {
257
+ let normalized = value;
258
+ for (const root of [...roots].sort(
259
+ (left, right) => right.length - left.length,
260
+ )) {
261
+ normalized = normalized.replaceAll(root, "<workspace>");
262
+ }
263
+ return normalized
264
+ .replaceAll(
265
+ /[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}/gi,
266
+ "<uuid>",
267
+ )
268
+ .replaceAll(
269
+ /\b\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z\b/g,
270
+ "<timestamp>",
271
+ );
272
+ }
273
+
274
+ // implements REQ-kibi-distribution-parity-matrix
275
+ export function normalizeDistributionParityValue(
276
+ value: unknown,
277
+ options: DistributionParityOptions = {},
278
+ ): unknown {
279
+ if (typeof value === "string") {
280
+ return normalizeString(value, options.workspaceRoots ?? []);
281
+ }
282
+ if (Array.isArray(value)) {
283
+ return value.map((entry) =>
284
+ normalizeDistributionParityValue(entry, options),
285
+ );
286
+ }
287
+ if (value === null || typeof value !== "object") {
288
+ return value;
289
+ }
290
+
291
+ const volatileKeys = options.volatileKeys ?? DEFAULT_VOLATILE_KEYS;
292
+ const normalized: Record<string, unknown> = {};
293
+ for (const key of Object.keys(value).sort()) {
294
+ if (volatileKeys.has(key)) continue;
295
+ normalized[key] = normalizeDistributionParityValue(
296
+ (value as Record<string, unknown>)[key],
297
+ options,
298
+ );
299
+ }
300
+ return normalized;
301
+ }
302
+
303
+ function semanticFingerprint(value: unknown): string {
304
+ return JSON.stringify(value);
305
+ }
306
+
307
+ function sortedUnique(values: readonly string[] | undefined): string[] {
308
+ return [...new Set(values ?? [])].sort((left, right) =>
309
+ left.localeCompare(right),
310
+ );
311
+ }
312
+
313
+ function currentRuntime(
314
+ runtimes: readonly DistributionRuntime[],
315
+ kind: "source_checkout" | "packed_artifact",
316
+ surface: DistributionSurface,
317
+ ): DistributionRuntime | undefined {
318
+ return runtimes.find(
319
+ (runtime) => runtime.kind === kind && runtime.surface === surface,
320
+ );
321
+ }
322
+
323
+ function observationKey(
324
+ runtimeId: string,
325
+ capability: RequirementCompilerCapability,
326
+ ): string {
327
+ return `${runtimeId}\0${capability}`;
328
+ }
329
+
330
+ function compareObservation(
331
+ observation: DistributionParityObservation,
332
+ reference: DistributionParityObservation | undefined,
333
+ normalized: unknown,
334
+ normalizedReference: unknown,
335
+ ): DistributionComparison {
336
+ if (observation.state === "failed") return "failed";
337
+ if (observation.state === "unsupported") return "unsupported";
338
+ if (!reference || reference.state !== "supported") return "not_compared";
339
+ return semanticFingerprint(normalized) ===
340
+ semanticFingerprint(normalizedReference)
341
+ ? "match"
342
+ : "diverged";
343
+ }
344
+
345
+ // implements REQ-kibi-distribution-parity-matrix
346
+ export function buildDistributionParityReport(
347
+ runtimes: readonly DistributionRuntime[],
348
+ observations: readonly DistributionParityObservation[],
349
+ options: DistributionParityOptions = {},
350
+ ): DistributionParityReport {
351
+ const issues: DistributionParityIssue[] = [];
352
+ const runtimeIds = new Set<string>();
353
+ for (const runtime of runtimes) {
354
+ if (runtimeIds.has(runtime.id)) {
355
+ issues.push({
356
+ code: "duplicate_runtime_id",
357
+ runtimeId: runtime.id,
358
+ surface: runtime.surface,
359
+ detail: `Runtime id ${runtime.id} is declared more than once.`,
360
+ });
361
+ }
362
+ runtimeIds.add(runtime.id);
363
+ if (runtime.provenance.status === "unresolved") {
364
+ issues.push({
365
+ code: "unresolved_runtime_provenance",
366
+ runtimeId: runtime.id,
367
+ surface: runtime.surface,
368
+ detail:
369
+ runtime.provenance.detail ??
370
+ `Runtime ${runtime.id} has no executable resolution evidence.`,
371
+ });
372
+ }
373
+ }
374
+
375
+ for (const surface of ["cli", "mcp"] as const) {
376
+ for (const kind of ["source_checkout", "packed_artifact"] as const) {
377
+ if (!currentRuntime(runtimes, kind, surface)) {
378
+ issues.push({
379
+ code: "missing_current_runtime",
380
+ surface,
381
+ detail: `Missing ${kind} runtime for ${surface}.`,
382
+ });
383
+ }
384
+ }
385
+ }
386
+
387
+ const indexed = new Map<string, DistributionParityObservation>();
388
+ for (const observation of observations) {
389
+ const key = observationKey(observation.runtimeId, observation.capability);
390
+ if (indexed.has(key)) {
391
+ issues.push({
392
+ code: "duplicate_capability_observation",
393
+ runtimeId: observation.runtimeId,
394
+ capability: observation.capability,
395
+ detail: `Capability ${observation.capability} was observed more than once for ${observation.runtimeId}.`,
396
+ });
397
+ continue;
398
+ }
399
+ indexed.set(key, observation);
400
+ }
401
+
402
+ const rows: DistributionParityRow[] = [];
403
+ for (const runtime of [...runtimes].sort((left, right) =>
404
+ left.id.localeCompare(right.id),
405
+ )) {
406
+ const source = currentRuntime(runtimes, "source_checkout", runtime.surface);
407
+ for (const capability of REQUIREMENT_COMPILER_CAPABILITIES) {
408
+ const observation = indexed.get(observationKey(runtime.id, capability));
409
+ const reference = source
410
+ ? indexed.get(observationKey(source.id, capability))
411
+ : undefined;
412
+ const action = runtime.actions?.[capability];
413
+ if (!observation) {
414
+ issues.push({
415
+ code: "missing_capability_observation",
416
+ runtimeId: runtime.id,
417
+ surface: runtime.surface,
418
+ capability,
419
+ detail: `Runtime ${runtime.id} has no ${capability} observation.`,
420
+ });
421
+ rows.push({
422
+ runtimeId: runtime.id,
423
+ kind: runtime.kind,
424
+ surface: runtime.surface,
425
+ ...(runtime.project === undefined
426
+ ? {}
427
+ : { project: runtime.project }),
428
+ capability,
429
+ state: "missing",
430
+ comparison: "not_compared",
431
+ ...(source === undefined ? {} : { referenceRuntimeId: source.id }),
432
+ diagnosticIds: [],
433
+ ...(action === undefined ? {} : { action }),
434
+ });
435
+ continue;
436
+ }
437
+
438
+ const normalized = normalizeDistributionParityValue(
439
+ observation.outcome,
440
+ options,
441
+ );
442
+ const normalizedReference = normalizeDistributionParityValue(
443
+ reference?.outcome,
444
+ options,
445
+ );
446
+ const comparison =
447
+ runtime.kind === "source_checkout"
448
+ ? observation.state === "supported"
449
+ ? "match"
450
+ : observation.state
451
+ : compareObservation(
452
+ observation,
453
+ reference,
454
+ normalized,
455
+ normalizedReference,
456
+ );
457
+
458
+ if (
459
+ runtime.kind !== "project_resolved" &&
460
+ observation.state === "unsupported"
461
+ ) {
462
+ issues.push({
463
+ code: "current_capability_unsupported",
464
+ runtimeId: runtime.id,
465
+ surface: runtime.surface,
466
+ capability,
467
+ detail: `Current ${runtime.kind} runtime does not support ${capability}.`,
468
+ });
469
+ }
470
+ if (observation.state === "failed") {
471
+ issues.push({
472
+ code: "capability_execution_failed",
473
+ runtimeId: runtime.id,
474
+ surface: runtime.surface,
475
+ capability,
476
+ detail:
477
+ observation.detail ??
478
+ `Runtime ${runtime.id} failed while executing ${capability}.`,
479
+ });
480
+ }
481
+ if (runtime.kind === "packed_artifact" && comparison === "diverged") {
482
+ issues.push({
483
+ code: "source_packed_mismatch",
484
+ runtimeId: runtime.id,
485
+ surface: runtime.surface,
486
+ capability,
487
+ detail: `Packed ${runtime.surface} outcome diverges from source for ${capability}.`,
488
+ });
489
+ }
490
+ if (
491
+ runtime.kind === "project_resolved" &&
492
+ (comparison === "diverged" || comparison === "unsupported") &&
493
+ !action
494
+ ) {
495
+ issues.push({
496
+ code: "project_divergence_without_action",
497
+ runtimeId: runtime.id,
498
+ surface: runtime.surface,
499
+ capability,
500
+ detail: `Project runtime ${runtime.id} needs an upgrade or compatibility action for ${capability}.`,
501
+ });
502
+ }
503
+
504
+ rows.push({
505
+ runtimeId: runtime.id,
506
+ kind: runtime.kind,
507
+ surface: runtime.surface,
508
+ ...(runtime.project === undefined ? {} : { project: runtime.project }),
509
+ capability,
510
+ state: observation.state,
511
+ comparison,
512
+ ...(runtime.kind === "source_checkout" || source === undefined
513
+ ? {}
514
+ : { referenceRuntimeId: source.id }),
515
+ ...(observation.state === "supported"
516
+ ? { normalizedOutcome: normalized }
517
+ : {}),
518
+ diagnosticIds: sortedUnique(observation.diagnosticIds),
519
+ ...(action === undefined ? {} : { action }),
520
+ ...(observation.detail === undefined
521
+ ? {}
522
+ : { detail: observation.detail }),
523
+ });
524
+ }
525
+ }
526
+
527
+ return {
528
+ version: DISTRIBUTION_PARITY_VERSION,
529
+ status: issues.length === 0 ? "passed" : "failed",
530
+ capabilities: REQUIREMENT_COMPILER_CAPABILITIES,
531
+ runtimes: [...runtimes].sort((left, right) =>
532
+ left.id.localeCompare(right.id),
533
+ ),
534
+ rows,
535
+ issues,
536
+ summary: {
537
+ runtimeCount: runtimes.length,
538
+ observationCount: observations.length,
539
+ matchCount: rows.filter((row) => row.comparison === "match").length,
540
+ divergenceCount: rows.filter((row) => row.comparison === "diverged")
541
+ .length,
542
+ unsupportedCount: rows.filter((row) => row.state === "unsupported")
543
+ .length,
544
+ failedCount: rows.filter((row) => row.state === "failed").length,
545
+ actionCount: rows.filter((row) => row.action !== undefined).length,
546
+ },
547
+ };
548
+ }
549
+
550
+ // implements REQ-kibi-distribution-parity-matrix
551
+ export async function runDistributionParityMatrix(
552
+ adapters: readonly DistributionRuntimeAdapter[],
553
+ options: DistributionParityOptions = {},
554
+ ): Promise<DistributionParityReport> {
555
+ const observations: DistributionParityObservation[] = [];
556
+ for (const adapter of adapters) {
557
+ for (const capability of REQUIREMENT_COMPILER_CAPABILITIES) {
558
+ try {
559
+ const result = await adapter.execute(capability);
560
+ observations.push({
561
+ runtimeId: adapter.runtime.id,
562
+ capability,
563
+ ...result,
564
+ });
565
+ } catch (error) {
566
+ observations.push({
567
+ runtimeId: adapter.runtime.id,
568
+ capability,
569
+ state: "failed",
570
+ detail: error instanceof Error ? error.message : String(error),
571
+ });
572
+ }
573
+ }
574
+ }
575
+ return buildDistributionParityReport(
576
+ adapters.map((adapter) => adapter.runtime),
577
+ observations,
578
+ options,
579
+ );
580
+ }
@@ -1,3 +1,5 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import path from "node:path";
1
3
  import type {
2
4
  ExtractedEntity,
3
5
  ExtractionResult,
@@ -8,6 +10,11 @@ import {
8
10
  parseListOfLists,
9
11
  parseTriples,
10
12
  } from "../../prolog/codec.js";
13
+ import {
14
+ analyzeTelemetryAcceptance,
15
+ createTelemetryAcceptanceDiagnostics,
16
+ parseTelemetryUsageLog,
17
+ } from "../telemetry-acceptance.js";
11
18
  import { createCoverageDepthQualityDiagnostics } from "./coverage-depth-quality.js";
12
19
  import { createRequirementQualityDiagnostics } from "./requirement-quality.js";
13
20
  import { createSymbolQualityDiagnostics } from "./symbol-quality.js";
@@ -36,6 +43,8 @@ type FullKbQualityDiagnosticsOptions = {
36
43
  readonly prolog: Pick<PrologProcess, "query">;
37
44
  readonly hardViolationEntityIds?: ReadonlySet<string>;
38
45
  readonly maxDiagnostics?: number;
46
+ readonly workspaceRoot?: string;
47
+ readonly now?: Date;
39
48
  };
40
49
 
41
50
  function stringField(entity: Record<string, unknown>, key: string): string {
@@ -88,6 +97,8 @@ function toExtractedEntity(entity: Record<string, unknown>): ExtractedEntity {
88
97
  if (severity !== undefined) extracted.severity = severity;
89
98
  const textRef = optionalStringField(entity, "text_ref");
90
99
  if (textRef !== undefined) extracted.text_ref = textRef;
100
+ const semanticText = optionalStringField(entity, "semantic_text");
101
+ if (semanticText !== undefined) extracted.semantic_text = semanticText;
91
102
  const logicClaims = optionalStringArrayField(entity, "logic_claims");
92
103
  if (logicClaims !== undefined) extracted.logic_claims = logicClaims;
93
104
  const granularityReason = optionalStringField(entity, "granularity_reason");
@@ -206,13 +217,75 @@ function capDiagnostics(
206
217
  : diagnostics;
207
218
  }
208
219
 
220
+ function telemetryReadDiagnostic(message: string): QualityDiagnostic {
221
+ return {
222
+ id: "telemetry_evidence_unreadable",
223
+ severity: "review",
224
+ blocking: false,
225
+ category: "telemetry",
226
+ source: ".kb/usage.log",
227
+ message,
228
+ suggestion:
229
+ "Repair or regenerate .kb/usage.log through Kibi diagnostic mode, then rerun kibi usage-metrics --require-acceptance and an unfiltered kb_check.",
230
+ };
231
+ }
232
+
233
+ async function collectTelemetryDiagnostics(
234
+ workspaceRoot: string | undefined,
235
+ now: Date,
236
+ ): Promise<readonly QualityDiagnostic[]> {
237
+ if (workspaceRoot === undefined) return [];
238
+ const usageLogPath = path.join(workspaceRoot, ".kb", "usage.log");
239
+ let contents: string;
240
+ try {
241
+ contents = await readFile(usageLogPath, "utf8");
242
+ } catch (error) {
243
+ if (
244
+ typeof error === "object" &&
245
+ error !== null &&
246
+ "code" in error &&
247
+ error.code === "ENOENT"
248
+ ) {
249
+ return [];
250
+ }
251
+ const message = error instanceof Error ? error.message : String(error);
252
+ return [
253
+ telemetryReadDiagnostic(
254
+ `Telemetry acceptance evidence is unreadable: ${message}`,
255
+ ),
256
+ ];
257
+ }
258
+
259
+ try {
260
+ const report = analyzeTelemetryAcceptance(
261
+ parseTelemetryUsageLog(contents),
262
+ now,
263
+ );
264
+ return createTelemetryAcceptanceDiagnostics(report);
265
+ } catch (error) {
266
+ const message = error instanceof Error ? error.message : String(error);
267
+ return [
268
+ telemetryReadDiagnostic(
269
+ `Telemetry acceptance evidence is malformed: ${message}`,
270
+ ),
271
+ ];
272
+ }
273
+ }
274
+
209
275
  export async function collectFullKbQualityDiagnostics(
210
276
  options: FullKbQualityDiagnosticsOptions,
211
277
  ): Promise<readonly QualityDiagnostic[]> {
212
- const manifestResults = await loadKbExtractionResults(options.prolog);
278
+ const [manifestResults, telemetryDiagnostics] = await Promise.all([
279
+ loadKbExtractionResults(options.prolog),
280
+ collectTelemetryDiagnostics(
281
+ options.workspaceRoot,
282
+ options.now ?? new Date(),
283
+ ),
284
+ ]);
213
285
  const coverageDepthDiagnostics =
214
286
  createCoverageDepthQualityDiagnostics(manifestResults);
215
287
  const diagnostics = [
288
+ ...telemetryDiagnostics,
216
289
  ...createRequirementQualityDiagnostics({
217
290
  manifestResults,
218
291
  ...(options.hardViolationEntityIds !== undefined