kibi-cli 0.17.0 → 0.19.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.
Files changed (241) hide show
  1. package/dist/cli-json-command.d.ts +1 -2
  2. package/dist/cli-json-command.d.ts.map +1 -1
  3. package/dist/cli-json-command.js +88 -44
  4. package/dist/cli-operation-loader.d.ts +3 -0
  5. package/dist/cli-operation-loader.d.ts.map +1 -0
  6. package/dist/cli-operation-loader.js +58 -0
  7. package/dist/cli-operation-metadata.d.ts +82 -0
  8. package/dist/cli-operation-metadata.d.ts.map +1 -0
  9. package/dist/cli-operation-metadata.js +106 -0
  10. package/dist/cli-protocol.d.ts +1 -1
  11. package/dist/cli-protocol.d.ts.map +1 -1
  12. package/dist/cli-protocol.js +4 -3
  13. package/dist/cli-register-foundation.d.ts.map +1 -1
  14. package/dist/cli-register-foundation.js +14 -17
  15. package/dist/cli-register-json.d.ts +3 -0
  16. package/dist/cli-register-json.d.ts.map +1 -0
  17. package/dist/cli-register-json.js +35 -0
  18. package/dist/cli-register-maintenance.d.ts.map +1 -1
  19. package/dist/cli-register-maintenance.js +41 -8
  20. package/dist/cli-register-reporting.d.ts.map +1 -1
  21. package/dist/cli-register-reporting.js +18 -14
  22. package/dist/cli-register-skills.d.ts.map +1 -1
  23. package/dist/cli-register-skills.js +8 -6
  24. package/dist/cli.d.ts.map +1 -1
  25. package/dist/cli.js +3 -2
  26. package/dist/commands/aggregated-checks.d.ts.map +1 -1
  27. package/dist/commands/aggregated-checks.js +1 -0
  28. package/dist/commands/check.d.ts.map +1 -1
  29. package/dist/commands/check.js +30 -12
  30. package/dist/commands/coverage.d.ts +5 -0
  31. package/dist/commands/coverage.d.ts.map +1 -1
  32. package/dist/commands/coverage.js +6 -1
  33. package/dist/commands/discovery-shared.d.ts +2 -1
  34. package/dist/commands/discovery-shared.d.ts.map +1 -1
  35. package/dist/commands/discovery-shared.js +35 -13
  36. package/dist/commands/discovery-table.d.ts.map +1 -1
  37. package/dist/commands/discovery-table.js +96 -5
  38. package/dist/commands/engine.d.ts +8 -0
  39. package/dist/commands/engine.d.ts.map +1 -0
  40. package/dist/commands/engine.js +100 -0
  41. package/dist/commands/gaps.js +1 -1
  42. package/dist/commands/graph.js +1 -1
  43. package/dist/commands/query.d.ts.map +1 -1
  44. package/dist/commands/query.js +3 -1
  45. package/dist/commands/search.d.ts.map +1 -1
  46. package/dist/commands/search.js +3 -1
  47. package/dist/commands/status.d.ts.map +1 -1
  48. package/dist/commands/status.js +2 -1
  49. package/dist/commands/sync/cache.d.ts +12 -0
  50. package/dist/commands/sync/cache.d.ts.map +1 -1
  51. package/dist/commands/sync/cache.js +38 -1
  52. package/dist/commands/sync/persistence.d.ts +10 -1
  53. package/dist/commands/sync/persistence.d.ts.map +1 -1
  54. package/dist/commands/sync/persistence.js +276 -43
  55. package/dist/commands/sync/staging.d.ts +10 -1
  56. package/dist/commands/sync/staging.d.ts.map +1 -1
  57. package/dist/commands/sync/staging.js +84 -1
  58. package/dist/commands/sync.d.ts.map +1 -1
  59. package/dist/commands/sync.js +521 -27
  60. package/dist/commands/usage-metrics.d.ts +66 -2
  61. package/dist/commands/usage-metrics.d.ts.map +1 -1
  62. package/dist/commands/usage-metrics.js +34 -24
  63. package/dist/commands/usage-remediation.d.ts +9 -0
  64. package/dist/commands/usage-remediation.d.ts.map +1 -0
  65. package/dist/commands/usage-remediation.js +52 -0
  66. package/dist/engine-daemon.d.ts +3 -0
  67. package/dist/engine-daemon.d.ts.map +1 -0
  68. package/dist/engine-daemon.js +34 -0
  69. package/dist/engine.d.ts +63 -0
  70. package/dist/engine.d.ts.map +1 -0
  71. package/dist/engine.js +1256 -0
  72. package/dist/extractors/markdown.d.ts +8 -0
  73. package/dist/extractors/markdown.d.ts.map +1 -1
  74. package/dist/extractors/markdown.js +69 -2
  75. package/dist/extractors/relationships.d.ts +2 -0
  76. package/dist/extractors/relationships.d.ts.map +1 -1
  77. package/dist/extractors/relationships.js +15 -17
  78. package/dist/operations/modeling/logic-modeling.d.ts.map +1 -1
  79. package/dist/operations/modeling/logic-modeling.js +14 -5
  80. package/dist/operations/modeling/model-requirement.d.ts.map +1 -1
  81. package/dist/operations/modeling/model-requirement.js +27 -2
  82. package/dist/operations/modeling/predicate-applyplan.d.ts +1 -1
  83. package/dist/operations/modeling/predicate-applyplan.d.ts.map +1 -1
  84. package/dist/operations/modeling/predicate-applyplan.js +18 -6
  85. package/dist/operations/modeling/predicate-inference.d.ts.map +1 -1
  86. package/dist/operations/modeling/predicate-inference.js +5 -17
  87. package/dist/operations/modeling/predicate-loader.js +1 -1
  88. package/dist/operations/modeling/predicate-types.d.ts +9 -1
  89. package/dist/operations/modeling/predicate-types.d.ts.map +1 -1
  90. package/dist/operations/modeling/suggest-predicates.d.ts.map +1 -1
  91. package/dist/operations/modeling/suggest-predicates.js +35 -11
  92. package/dist/operations/mutation/audit.js +1 -1
  93. package/dist/operations/mutation/contradictions.d.ts +1 -1
  94. package/dist/operations/mutation/contradictions.d.ts.map +1 -1
  95. package/dist/operations/mutation/contradictions.js +23 -18
  96. package/dist/operations/mutation/serialization.d.ts.map +1 -1
  97. package/dist/operations/mutation/serialization.js +6 -3
  98. package/dist/operations/mutation/upsert.d.ts +3 -1
  99. package/dist/operations/mutation/upsert.d.ts.map +1 -1
  100. package/dist/operations/mutation/upsert.js +51 -12
  101. package/dist/operations/mutation/validate-upsert.d.ts.map +1 -1
  102. package/dist/operations/mutation/validate-upsert.js +18 -2
  103. package/dist/operations/mutation/validation.d.ts.map +1 -1
  104. package/dist/operations/mutation/validation.js +8 -0
  105. package/dist/operations/semantic-advisor/analysis-receipt.d.ts.map +1 -1
  106. package/dist/operations/semantic-advisor/analysis-receipt.js +7 -1
  107. package/dist/operations/semantic-advisor/analyze-prose.d.ts.map +1 -1
  108. package/dist/operations/semantic-advisor/analyze-prose.js +76 -61
  109. package/dist/operations/semantic-advisor/clauses.d.ts +1 -0
  110. package/dist/operations/semantic-advisor/clauses.d.ts.map +1 -1
  111. package/dist/operations/semantic-advisor/clauses.js +7 -7
  112. package/dist/operations/semantic-advisor/ingestion-boundary.d.ts +18 -0
  113. package/dist/operations/semantic-advisor/ingestion-boundary.d.ts.map +1 -0
  114. package/dist/operations/semantic-advisor/ingestion-boundary.js +167 -0
  115. package/dist/operations/semantic-advisor/shared.d.ts +8 -0
  116. package/dist/operations/semantic-advisor/shared.d.ts.map +1 -1
  117. package/dist/operations/semantic-advisor/shared.js +37 -3
  118. package/dist/operations/semantic-advisor/types.d.ts +5 -0
  119. package/dist/operations/semantic-advisor/types.d.ts.map +1 -1
  120. package/dist/prolog/codec.d.ts.map +1 -1
  121. package/dist/prolog/codec.js +9 -2
  122. package/dist/prolog.d.ts +14 -0
  123. package/dist/prolog.d.ts.map +1 -1
  124. package/dist/prolog.js +279 -69
  125. package/dist/public/diagnostic-usage.d.ts +17 -0
  126. package/dist/public/diagnostic-usage.d.ts.map +1 -0
  127. package/dist/public/diagnostic-usage.js +152 -0
  128. package/dist/public/distribution-parity.d.ts +90 -0
  129. package/dist/public/distribution-parity.d.ts.map +1 -0
  130. package/dist/public/distribution-parity.js +367 -0
  131. package/dist/public/impact/full-kb-quality.d.ts +2 -0
  132. package/dist/public/impact/full-kb-quality.d.ts.map +1 -1
  133. package/dist/public/impact/full-kb-quality.js +53 -1
  134. package/dist/public/impact-diagnostics.d.ts +2 -0
  135. package/dist/public/impact-diagnostics.d.ts.map +1 -1
  136. package/dist/public/impact-diagnostics.js +1 -0
  137. package/dist/public/operations/catalog.d.ts +151 -6
  138. package/dist/public/operations/catalog.d.ts.map +1 -1
  139. package/dist/public/operations/check-executor.d.ts.map +1 -1
  140. package/dist/public/operations/check-executor.js +4 -0
  141. package/dist/public/operations/discovery-entities.d.ts.map +1 -1
  142. package/dist/public/operations/discovery-entities.js +3 -2
  143. package/dist/public/operations/discovery-executors.d.ts +6 -0
  144. package/dist/public/operations/discovery-executors.d.ts.map +1 -1
  145. package/dist/public/operations/discovery-executors.js +58 -5
  146. package/dist/public/operations/index.d.ts +4 -0
  147. package/dist/public/operations/index.d.ts.map +1 -1
  148. package/dist/public/operations/index.js +4 -0
  149. package/dist/public/operations/legacy-migration-plan.d.ts +113 -0
  150. package/dist/public/operations/legacy-migration-plan.d.ts.map +1 -0
  151. package/dist/public/operations/legacy-migration-plan.js +409 -0
  152. package/dist/public/operations/node-ports.d.ts.map +1 -1
  153. package/dist/public/operations/node-ports.js +90 -0
  154. package/dist/public/operations/prolog-json.d.ts.map +1 -1
  155. package/dist/public/operations/prolog-json.js +14 -4
  156. package/dist/public/operations/repair-plan.d.ts +184 -0
  157. package/dist/public/operations/repair-plan.d.ts.map +1 -0
  158. package/dist/public/operations/repair-plan.js +397 -0
  159. package/dist/public/operations/runtime-types.d.ts +38 -0
  160. package/dist/public/operations/runtime-types.d.ts.map +1 -1
  161. package/dist/public/operations/specs/check.d.ts +2 -2
  162. package/dist/public/operations/specs/check.js +2 -2
  163. package/dist/public/operations/specs/discovery.d.ts +1 -1
  164. package/dist/public/operations/specs/discovery.js +1 -1
  165. package/dist/public/operations/specs/modeling.d.ts +19 -1
  166. package/dist/public/operations/specs/modeling.d.ts.map +1 -1
  167. package/dist/public/operations/specs/modeling.js +16 -1
  168. package/dist/public/operations/specs/mutation-schemas.d.ts +96 -1
  169. package/dist/public/operations/specs/mutation-schemas.d.ts.map +1 -1
  170. package/dist/public/operations/specs/mutation-schemas.js +40 -1
  171. package/dist/public/operations/specs/reporting.d.ts +42 -1
  172. package/dist/public/operations/specs/reporting.d.ts.map +1 -1
  173. package/dist/public/operations/specs/reporting.js +70 -6
  174. package/dist/public/operations/specs/semantic.js +1 -1
  175. package/dist/public/operations/specs/upsert.d.ts +96 -1
  176. package/dist/public/operations/specs/upsert.d.ts.map +1 -1
  177. package/dist/public/operations/workspace-snapshot.d.ts +10 -0
  178. package/dist/public/operations/workspace-snapshot.d.ts.map +1 -0
  179. package/dist/public/operations/workspace-snapshot.js +20 -0
  180. package/dist/public/schemas/entity.d.ts.map +1 -1
  181. package/dist/public/schemas/entity.js +43 -1
  182. package/dist/public/skills/init-kibi/SKILL.md +2 -2
  183. package/dist/public/skills/kibi-freshness/SKILL.md +2 -2
  184. package/dist/public/skills/kibi-traceability/SKILL.md +8 -2
  185. package/dist/public/skills/kibi-usage/SKILL.md +12 -6
  186. package/dist/public/skills/kibi-usage/resources/fact-lanes.md +1 -1
  187. package/dist/public/skills/kibi-usage/resources/logic-ir.md +1 -1
  188. package/dist/public/skills/kibi-usage/resources/operation-access.md +1 -1
  189. package/dist/public/skills/kibi-usage/resources/workflows.md +19 -2
  190. package/dist/public/telemetry-acceptance.d.ts +89 -0
  191. package/dist/public/telemetry-acceptance.d.ts.map +1 -0
  192. package/dist/public/telemetry-acceptance.js +597 -0
  193. package/dist/public/telemetry-remediation.d.ts +33 -0
  194. package/dist/public/telemetry-remediation.d.ts.map +1 -0
  195. package/dist/public/telemetry-remediation.js +257 -0
  196. package/dist/public/verification-receipt.d.ts +75 -0
  197. package/dist/public/verification-receipt.d.ts.map +1 -0
  198. package/dist/public/verification-receipt.js +129 -0
  199. package/dist/runtime/cli-runtime.d.ts.map +1 -1
  200. package/dist/runtime/cli-runtime.js +33 -37
  201. package/dist/schemas/entity.schema.json +96 -2
  202. package/dist/traceability/temp-kb.d.ts.map +1 -1
  203. package/dist/traceability/temp-kb.js +36 -3
  204. package/dist/types/entities.d.ts +1 -0
  205. package/dist/types/entities.d.ts.map +1 -1
  206. package/dist/utils/rule-registry.d.ts +2 -0
  207. package/dist/utils/rule-registry.d.ts.map +1 -1
  208. package/package.json +36 -4
  209. package/src/public/diagnostic-usage.ts +206 -0
  210. package/src/public/distribution-parity.ts +580 -0
  211. package/src/public/impact/full-kb-quality.ts +74 -1
  212. package/src/public/impact-diagnostics.ts +16 -0
  213. package/src/public/operations/check-executor.ts +4 -0
  214. package/src/public/operations/discovery-entities.ts +3 -2
  215. package/src/public/operations/discovery-executors.ts +68 -5
  216. package/src/public/operations/index.ts +4 -0
  217. package/src/public/operations/legacy-migration-plan.ts +688 -0
  218. package/src/public/operations/node-ports.ts +109 -0
  219. package/src/public/operations/prolog-json.ts +15 -3
  220. package/src/public/operations/repair-plan.ts +562 -0
  221. package/src/public/operations/runtime-types.ts +47 -0
  222. package/src/public/operations/specs/check.ts +2 -2
  223. package/src/public/operations/specs/discovery.ts +1 -1
  224. package/src/public/operations/specs/modeling.ts +19 -1
  225. package/src/public/operations/specs/mutation-schemas.ts +47 -1
  226. package/src/public/operations/specs/reporting.ts +91 -6
  227. package/src/public/operations/specs/semantic.ts +1 -1
  228. package/src/public/operations/workspace-snapshot.ts +28 -0
  229. package/src/public/schemas/entity.ts +44 -1
  230. package/src/public/skills/init-kibi/SKILL.md +2 -2
  231. package/src/public/skills/kibi-freshness/SKILL.md +2 -2
  232. package/src/public/skills/kibi-traceability/SKILL.md +8 -2
  233. package/src/public/skills/kibi-usage/SKILL.md +12 -6
  234. package/src/public/skills/kibi-usage/resources/fact-lanes.md +1 -1
  235. package/src/public/skills/kibi-usage/resources/logic-ir.md +1 -1
  236. package/src/public/skills/kibi-usage/resources/operation-access.md +1 -1
  237. package/src/public/skills/kibi-usage/resources/workflows.md +19 -2
  238. package/src/public/telemetry-acceptance.ts +859 -0
  239. package/src/public/telemetry-remediation.ts +462 -0
  240. package/src/public/verification-receipt.ts +170 -0
  241. package/src/schemas/entity.schema.json +96 -2
@@ -0,0 +1,462 @@
1
+ import { diagnosticMutationFingerprint } from "./diagnostic-usage.js";
2
+ import {
3
+ DEFAULT_TELEMETRY_ACCEPTANCE_POLICY,
4
+ type TelemetryAcceptancePolicy,
5
+ type TelemetryAcceptanceReport,
6
+ type TelemetryMetricId,
7
+ type TelemetryUsageEvent,
8
+ analyzeTelemetryAcceptance,
9
+ } from "./telemetry-acceptance.js";
10
+
11
+ export const TELEMETRY_REMEDIATION_VERSION =
12
+ "kibi.telemetry-remediation.v1" as const;
13
+
14
+ export interface TelemetryRemediationEventReference {
15
+ readonly logLine: number;
16
+ readonly requestId: string | null;
17
+ readonly timestamp: string | null;
18
+ readonly tool: string | null;
19
+ readonly sessionId: string | null;
20
+ readonly actorId: string | null;
21
+ }
22
+
23
+ export interface TelemetryRemediationItem {
24
+ readonly id: string;
25
+ readonly rank: number;
26
+ readonly metric: TelemetryMetricId | "evidence_freshness";
27
+ readonly scope: "event" | "report";
28
+ readonly target: string;
29
+ readonly reason: string;
30
+ readonly action: string;
31
+ readonly event: TelemetryRemediationEventReference | null;
32
+ }
33
+
34
+ export interface TelemetryRemediationReport {
35
+ readonly version: typeof TELEMETRY_REMEDIATION_VERSION;
36
+ readonly status: "clear" | "action_required";
37
+ readonly acceptance: TelemetryAcceptanceReport;
38
+ readonly summary: Readonly<{
39
+ total: number;
40
+ eventItems: number;
41
+ reportItems: number;
42
+ }>;
43
+ readonly items: readonly TelemetryRemediationItem[];
44
+ }
45
+
46
+ type IndexedEvent = {
47
+ readonly event: TelemetryUsageEvent;
48
+ readonly index: number;
49
+ readonly time: number | null;
50
+ };
51
+
52
+ const RANKS: Readonly<
53
+ Record<TelemetryMetricId | "evidence_freshness", number>
54
+ > = {
55
+ repeated_mutation_failures: 10,
56
+ validation_before_upsert: 20,
57
+ advisor_before_requirement_write: 30,
58
+ e2e_receipt_freshness: 40,
59
+ proof_gap_recovery: 50,
60
+ source_lookup_zero_result_rate: 60,
61
+ telemetry_completeness: 70,
62
+ evidence_freshness: 5,
63
+ };
64
+
65
+ function isRecord(value: unknown): value is Record<string, unknown> {
66
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
67
+ }
68
+
69
+ function args(event: TelemetryUsageEvent): Readonly<Record<string, unknown>> {
70
+ return event.business_args ?? event.args ?? {};
71
+ }
72
+
73
+ function succeeded(event: TelemetryUsageEvent): boolean {
74
+ return event.status === "success" || event.success === true;
75
+ }
76
+
77
+ function failed(event: TelemetryUsageEvent): boolean {
78
+ return event.status === "error" || event.success === false;
79
+ }
80
+
81
+ function telemetryProvided(event: TelemetryUsageEvent): boolean {
82
+ if (event.telemetry_status === "provided") return true;
83
+ if (event.telemetry_status === "missing") return false;
84
+ return event.telemetry !== null && event.telemetry !== undefined;
85
+ }
86
+
87
+ function correlationValue(
88
+ event: TelemetryUsageEvent,
89
+ key: "session_id" | "actor_id",
90
+ ): string | undefined {
91
+ if (typeof event[key] === "string" && event[key].length > 0) {
92
+ return event[key];
93
+ }
94
+ return isRecord(event.telemetry) && typeof event.telemetry[key] === "string"
95
+ ? event.telemetry[key]
96
+ : undefined;
97
+ }
98
+
99
+ function compatible(left: TelemetryUsageEvent, right: TelemetryUsageEvent) {
100
+ return (["session_id", "actor_id"] as const).every((key) => {
101
+ const leftValue = correlationValue(left, key);
102
+ const rightValue = correlationValue(right, key);
103
+ return (
104
+ leftValue === undefined ||
105
+ rightValue === undefined ||
106
+ leftValue === rightValue
107
+ );
108
+ });
109
+ }
110
+
111
+ function withinAge(
112
+ earlier: number | null,
113
+ later: number | null,
114
+ seconds: number,
115
+ ): boolean {
116
+ return (
117
+ earlier !== null &&
118
+ later !== null &&
119
+ earlier <= later &&
120
+ later - earlier <= seconds * 1_000
121
+ );
122
+ }
123
+
124
+ function fingerprint(event: TelemetryUsageEvent): string {
125
+ return (
126
+ event.mutation_fingerprint ?? diagnosticMutationFingerprint(args(event))
127
+ );
128
+ }
129
+
130
+ function target(event: TelemetryUsageEvent): string {
131
+ if (event.mutation_target) return event.mutation_target;
132
+ const eventArgs = args(event);
133
+ const type = typeof eventArgs.type === "string" ? eventArgs.type : "unknown";
134
+ const id =
135
+ typeof eventArgs.id === "string" ? eventArgs.id : fingerprint(event);
136
+ return `${type}:${id}`;
137
+ }
138
+
139
+ function reference(entry: IndexedEvent): TelemetryRemediationEventReference {
140
+ return {
141
+ logLine: entry.index + 1,
142
+ requestId: entry.event.request_id ?? null,
143
+ timestamp: entry.event.timestamp ?? null,
144
+ tool: entry.event.tool ?? null,
145
+ sessionId: correlationValue(entry.event, "session_id") ?? null,
146
+ actorId: correlationValue(entry.event, "actor_id") ?? null,
147
+ };
148
+ }
149
+
150
+ function eventItem(
151
+ metric: TelemetryMetricId,
152
+ entry: IndexedEvent,
153
+ itemTarget: string,
154
+ reason: string,
155
+ action: string,
156
+ ): TelemetryRemediationItem {
157
+ return {
158
+ id: `${metric}:line-${entry.index + 1}:${itemTarget}`,
159
+ rank: RANKS[metric],
160
+ metric,
161
+ scope: "event",
162
+ target: itemTarget,
163
+ reason,
164
+ action,
165
+ event: reference(entry),
166
+ };
167
+ }
168
+
169
+ function reportItem(
170
+ metric: TelemetryMetricId | "evidence_freshness",
171
+ itemTarget: string,
172
+ reason: string,
173
+ action: string,
174
+ ): TelemetryRemediationItem {
175
+ return {
176
+ id: `${metric}:report:${itemTarget}`,
177
+ rank: RANKS[metric],
178
+ metric,
179
+ scope: "report",
180
+ target: itemTarget,
181
+ reason,
182
+ action,
183
+ event: null,
184
+ };
185
+ }
186
+
187
+ function isActionable(
188
+ acceptance: TelemetryAcceptanceReport,
189
+ metric: TelemetryMetricId,
190
+ ): boolean {
191
+ const status = acceptance.metrics.find(
192
+ (candidate) => candidate.id === metric,
193
+ )?.status;
194
+ return status === "failed" || status === "insufficient_evidence";
195
+ }
196
+
197
+ export function buildTelemetryRemediationReport(
198
+ events: readonly TelemetryUsageEvent[],
199
+ now: Date = new Date(),
200
+ policy: TelemetryAcceptancePolicy = DEFAULT_TELEMETRY_ACCEPTANCE_POLICY,
201
+ ): TelemetryRemediationReport {
202
+ const acceptance = analyzeTelemetryAcceptance(events, now, policy);
203
+ const all: IndexedEvent[] = events.map((event, index) => ({
204
+ event,
205
+ index,
206
+ time:
207
+ event.timestamp === undefined ||
208
+ !Number.isFinite(Date.parse(event.timestamp))
209
+ ? null
210
+ : Date.parse(event.timestamp),
211
+ }));
212
+ const recentStart = Math.max(0, all.length - policy.eventLimit);
213
+ const recent = all.slice(recentStart);
214
+ const items: TelemetryRemediationItem[] = [];
215
+
216
+ if (!acceptance.scope.fresh) {
217
+ items.push(
218
+ reportItem(
219
+ "evidence_freshness",
220
+ ".kb/usage.log",
221
+ "The acceptance window has no current timestamped evidence.",
222
+ "Run the current workflow in diagnostic mode, then regenerate this report.",
223
+ ),
224
+ );
225
+ }
226
+
227
+ if (isActionable(acceptance, "telemetry_completeness")) {
228
+ for (const entry of recent.filter(
229
+ ({ event }) => !telemetryProvided(event),
230
+ )) {
231
+ items.push(
232
+ eventItem(
233
+ "telemetry_completeness",
234
+ entry,
235
+ entry.event.tool ?? "unknown tool",
236
+ "Diagnostic telemetry is missing for this event.",
237
+ "Repeat this operation in diagnostic mode with complete _diagnostic_telemetry.",
238
+ ),
239
+ );
240
+ }
241
+ if (recent.length < policy.minimumEvents) {
242
+ items.push(
243
+ reportItem(
244
+ "telemetry_completeness",
245
+ "acceptance window",
246
+ `Only ${recent.length}/${policy.minimumEvents} required events are available.`,
247
+ "Capture enough current diagnostic events to satisfy the minimum evidence window.",
248
+ ),
249
+ );
250
+ }
251
+ }
252
+
253
+ if (isActionable(acceptance, "validation_before_upsert")) {
254
+ const preflights = new Map<string, IndexedEvent[]>();
255
+ for (const entry of all) {
256
+ if (
257
+ entry.event.tool === "kb_validate_upsert" &&
258
+ succeeded(entry.event) &&
259
+ entry.event.validation_valid !== false
260
+ ) {
261
+ const matches = preflights.get(fingerprint(entry.event)) ?? [];
262
+ matches.push(entry);
263
+ preflights.set(fingerprint(entry.event), matches);
264
+ continue;
265
+ }
266
+ if (entry.index < recentStart || entry.event.tool !== "kb_upsert")
267
+ continue;
268
+ const match = (preflights.get(fingerprint(entry.event)) ?? []).findLast(
269
+ (candidate) =>
270
+ compatible(candidate.event, entry.event) &&
271
+ withinAge(candidate.time, entry.time, policy.preflightMaxAgeSeconds),
272
+ );
273
+ if (match === undefined) {
274
+ items.push(
275
+ eventItem(
276
+ "validation_before_upsert",
277
+ entry,
278
+ target(entry.event),
279
+ "No recent successful preflight matches this exact payload and correlation context.",
280
+ "Run kb_validate_upsert for this exact payload in the same session/actor context before retrying once.",
281
+ ),
282
+ );
283
+ }
284
+ }
285
+ }
286
+
287
+ if (isActionable(acceptance, "advisor_before_requirement_write")) {
288
+ const advisors = new Map<string, IndexedEvent[]>();
289
+ for (const entry of all) {
290
+ const eventArgs = args(entry.event);
291
+ const id =
292
+ typeof eventArgs.id === "string"
293
+ ? eventArgs.id
294
+ : typeof eventArgs.requirementId === "string"
295
+ ? eventArgs.requirementId
296
+ : undefined;
297
+ if (
298
+ entry.event.tool === "kb_semantic_advisor" &&
299
+ succeeded(entry.event) &&
300
+ id
301
+ ) {
302
+ const matches = advisors.get(id) ?? [];
303
+ matches.push(entry);
304
+ advisors.set(id, matches);
305
+ continue;
306
+ }
307
+ if (
308
+ entry.index < recentStart ||
309
+ entry.event.tool !== "kb_upsert" ||
310
+ eventArgs.type !== "req"
311
+ ) {
312
+ continue;
313
+ }
314
+ const requirementId = id ?? target(entry.event);
315
+ const match = (advisors.get(requirementId) ?? []).findLast(
316
+ (candidate) =>
317
+ compatible(candidate.event, entry.event) &&
318
+ (candidate.event.semantic_source_hash === undefined ||
319
+ entry.event.semantic_source_hash === undefined ||
320
+ candidate.event.semantic_source_hash ===
321
+ entry.event.semantic_source_hash) &&
322
+ withinAge(candidate.time, entry.time, policy.advisorMaxAgeSeconds),
323
+ );
324
+ if (match === undefined) {
325
+ items.push(
326
+ eventItem(
327
+ "advisor_before_requirement_write",
328
+ entry,
329
+ requirementId,
330
+ "No recent semantic-advisor event matches this requirement, source hash, and correlation context.",
331
+ "Run kb_semantic_advisor on the complete prose in the same session/actor context before the next upsert.",
332
+ ),
333
+ );
334
+ }
335
+ }
336
+ }
337
+
338
+ if (isActionable(acceptance, "source_lookup_zero_result_rate")) {
339
+ for (const entry of recent) {
340
+ const sourceFile = args(entry.event).sourceFile;
341
+ if (
342
+ (entry.event.tool === "kb_query" || entry.event.tool === "kb_search") &&
343
+ succeeded(entry.event) &&
344
+ typeof sourceFile === "string" &&
345
+ (entry.event.zero_results === true || entry.event.result_count === 0)
346
+ ) {
347
+ items.push(
348
+ eventItem(
349
+ "source_lookup_zero_result_rate",
350
+ entry,
351
+ sourceFile,
352
+ "This source-linked lookup returned zero results.",
353
+ "Inspect the source, refresh its symbol links if stale, then repeat search followed by exact query.",
354
+ ),
355
+ );
356
+ }
357
+ }
358
+ }
359
+
360
+ const completeCoverage = recent.filter(
361
+ ({ event }) =>
362
+ event.tool === "kb_coverage" &&
363
+ succeeded(event) &&
364
+ (event.coverage_by ?? "req") === "req" &&
365
+ event.coverage_scope_complete === true &&
366
+ typeof event.coverage_proof_gap_count === "number",
367
+ );
368
+ const latestCoverage = completeCoverage.at(-1);
369
+ if (isActionable(acceptance, "proof_gap_recovery")) {
370
+ items.push(
371
+ latestCoverage
372
+ ? eventItem(
373
+ "proof_gap_recovery",
374
+ latestCoverage,
375
+ "coverage:req",
376
+ `The latest complete report has ${latestCoverage.event.coverage_proof_gap_count} proof gaps without demonstrated recovery.`,
377
+ "Apply one reviewed repair batch and rerun complete requirement coverage.",
378
+ )
379
+ : reportItem(
380
+ "proof_gap_recovery",
381
+ "coverage:req",
382
+ "No complete requirement coverage event contains proof-gap evidence.",
383
+ "Run kb_coverage by req with complete scope and diagnostic telemetry.",
384
+ ),
385
+ );
386
+ }
387
+ if (isActionable(acceptance, "e2e_receipt_freshness")) {
388
+ items.push(
389
+ latestCoverage &&
390
+ latestCoverage.event.coverage_receipt_gap_count !== undefined
391
+ ? eventItem(
392
+ "e2e_receipt_freshness",
393
+ latestCoverage,
394
+ "coverage:req",
395
+ `The latest complete report has ${latestCoverage.event.coverage_receipt_gap_count} receipt gaps.`,
396
+ "Run the cited E2E tests, append fresh passing receipts, sync, and rerun coverage.",
397
+ )
398
+ : reportItem(
399
+ "e2e_receipt_freshness",
400
+ "coverage:req",
401
+ "No complete requirement coverage event contains receipt-freshness evidence.",
402
+ "Run complete requirement coverage after attaching fresh E2E verification receipts.",
403
+ ),
404
+ );
405
+ }
406
+
407
+ if (isActionable(acceptance, "repeated_mutation_failures")) {
408
+ const streaks = new Map<string, IndexedEvent[]>();
409
+ const offending: IndexedEvent[][] = [];
410
+ const flush = (itemTarget: string) => {
411
+ const streak = streaks.get(itemTarget) ?? [];
412
+ if (streak.length >= policy.repeatedMutationFailureThreshold) {
413
+ offending.push(streak);
414
+ }
415
+ streaks.set(itemTarget, []);
416
+ };
417
+ for (const entry of recent) {
418
+ if (entry.event.tool !== "kb_upsert") continue;
419
+ const itemTarget = target(entry.event);
420
+ if (failed(entry.event)) {
421
+ const streak = streaks.get(itemTarget) ?? [];
422
+ streak.push(entry);
423
+ streaks.set(itemTarget, streak);
424
+ } else if (succeeded(entry.event)) {
425
+ flush(itemTarget);
426
+ }
427
+ }
428
+ for (const itemTarget of streaks.keys()) flush(itemTarget);
429
+ for (const streak of offending) {
430
+ for (const entry of streak) {
431
+ items.push(
432
+ eventItem(
433
+ "repeated_mutation_failures",
434
+ entry,
435
+ target(entry.event),
436
+ "This event belongs to a mutation-failure streak at or above the retry threshold.",
437
+ "Stop retrying; inspect endpoints and the exact preflight result, then make one repaired attempt.",
438
+ ),
439
+ );
440
+ }
441
+ }
442
+ }
443
+
444
+ items.sort(
445
+ (left, right) =>
446
+ left.rank - right.rank ||
447
+ (left.event?.logLine ?? Number.MAX_SAFE_INTEGER) -
448
+ (right.event?.logLine ?? Number.MAX_SAFE_INTEGER) ||
449
+ left.id.localeCompare(right.id),
450
+ );
451
+ return {
452
+ version: TELEMETRY_REMEDIATION_VERSION,
453
+ status: items.length === 0 ? "clear" : "action_required",
454
+ acceptance,
455
+ summary: {
456
+ total: items.length,
457
+ eventItems: items.filter((item) => item.scope === "event").length,
458
+ reportItems: items.filter((item) => item.scope === "report").length,
459
+ },
460
+ items,
461
+ };
462
+ }
@@ -0,0 +1,170 @@
1
+ export const VERIFICATION_RECEIPT_VERSION =
2
+ "kibi.verification-receipt.v1" as const;
3
+
4
+ export const VERIFICATION_RECEIPT_MAX_AGE_SECONDS = 7 * 24 * 60 * 60;
5
+
6
+ export const VERIFICATION_RECEIPT_OUTCOMES = [
7
+ "passed",
8
+ "failed",
9
+ "errored",
10
+ "cancelled",
11
+ "skipped",
12
+ ] as const;
13
+
14
+ export const VERIFICATION_RECEIPT_SCHEMA = {
15
+ type: "object",
16
+ required: [
17
+ "version",
18
+ "receipt_id",
19
+ "test_id",
20
+ "runner",
21
+ "command",
22
+ "scope",
23
+ "outcome",
24
+ "code_snapshot",
25
+ "environment_hash",
26
+ "started_at",
27
+ "finished_at",
28
+ "artifact_digest",
29
+ ],
30
+ properties: {
31
+ version: { type: "string", const: VERIFICATION_RECEIPT_VERSION },
32
+ receipt_id: {
33
+ type: "string",
34
+ pattern: "^VR-[A-Za-z0-9][A-Za-z0-9._:-]{7,127}$",
35
+ },
36
+ test_id: { type: "string", minLength: 1 },
37
+ runner: { type: "string", minLength: 1 },
38
+ command: { type: "string", minLength: 1 },
39
+ scope: { type: "string", enum: ["unit", "integration", "end_to_end"] },
40
+ outcome: { type: "string", enum: [...VERIFICATION_RECEIPT_OUTCOMES] },
41
+ code_snapshot: { type: "string", pattern: "^[a-f0-9]{64}$" },
42
+ environment_hash: { type: "string", pattern: "^[a-f0-9]{64}$" },
43
+ started_at: { type: "string", minLength: 1 },
44
+ finished_at: { type: "string", minLength: 1 },
45
+ artifact_digest: { type: "string", pattern: "^[a-f0-9]{64}$" },
46
+ },
47
+ additionalProperties: false,
48
+ } as const;
49
+
50
+ export type VerificationReceipt = Readonly<{
51
+ version: typeof VERIFICATION_RECEIPT_VERSION;
52
+ receipt_id: string;
53
+ test_id: string;
54
+ runner: string;
55
+ command: string;
56
+ scope: "unit" | "integration" | "end_to_end";
57
+ outcome: (typeof VERIFICATION_RECEIPT_OUTCOMES)[number];
58
+ code_snapshot: string;
59
+ environment_hash: string;
60
+ started_at: string;
61
+ finished_at: string;
62
+ artifact_digest: string;
63
+ }>;
64
+
65
+ function timestamp(value: unknown): number | null {
66
+ if (typeof value !== "string") return null;
67
+ if (
68
+ !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/.test(
69
+ value,
70
+ )
71
+ ) {
72
+ return null;
73
+ }
74
+ const parsed = Date.parse(value);
75
+ return Number.isFinite(parsed) ? parsed : null;
76
+ }
77
+
78
+ export function verificationReceiptHistoryErrors(
79
+ testId: string,
80
+ verificationScope: unknown,
81
+ receipts: readonly Readonly<Record<string, unknown>>[],
82
+ ): readonly string[] {
83
+ const errors: string[] = [];
84
+ let previousFinishedAt: number | null = null;
85
+ if (receipts.length > 0 && typeof verificationScope !== "string") {
86
+ errors.push(
87
+ "verification_scope is required when verification_receipts are present",
88
+ );
89
+ }
90
+ const receiptIds = new Set<string>();
91
+ for (const [index, receipt] of receipts.entries()) {
92
+ const prefix = `verification_receipts[${index}]`;
93
+ const receiptId = receipt.receipt_id;
94
+ if (typeof receiptId === "string") {
95
+ if (receiptIds.has(receiptId)) {
96
+ errors.push(`${prefix}.receipt_id duplicates '${receiptId}'`);
97
+ }
98
+ receiptIds.add(receiptId);
99
+ }
100
+ if (receipt.test_id !== testId) {
101
+ errors.push(`${prefix}.test_id must equal '${testId}'`);
102
+ }
103
+ if (
104
+ typeof verificationScope === "string" &&
105
+ receipt.scope !== verificationScope
106
+ ) {
107
+ errors.push(
108
+ `${prefix}.scope must equal the test verification_scope '${verificationScope}'`,
109
+ );
110
+ }
111
+ const startedAt = timestamp(receipt.started_at);
112
+ const finishedAt = timestamp(receipt.finished_at);
113
+ if (startedAt === null)
114
+ errors.push(`${prefix}.started_at must be ISO-8601`);
115
+ if (finishedAt === null)
116
+ errors.push(`${prefix}.finished_at must be ISO-8601`);
117
+ if (startedAt !== null && finishedAt !== null && finishedAt < startedAt) {
118
+ errors.push(`${prefix}.finished_at must not precede started_at`);
119
+ }
120
+ if (
121
+ finishedAt !== null &&
122
+ previousFinishedAt !== null &&
123
+ finishedAt <= previousFinishedAt
124
+ ) {
125
+ errors.push(
126
+ `${prefix}.finished_at must be strictly later than the previous receipt`,
127
+ );
128
+ }
129
+ if (finishedAt !== null) previousFinishedAt = finishedAt;
130
+ }
131
+ return errors;
132
+ }
133
+
134
+ function canonicalJson(value: unknown): string {
135
+ if (Array.isArray(value)) {
136
+ return `[${value.map(canonicalJson).join(",")}]`;
137
+ }
138
+ if (value !== null && typeof value === "object") {
139
+ return `{${Object.entries(value)
140
+ .sort(([left], [right]) => left.localeCompare(right))
141
+ .map(([key, child]) => `${JSON.stringify(key)}:${canonicalJson(child)}`)
142
+ .join(",")}}`;
143
+ }
144
+ return JSON.stringify(value);
145
+ }
146
+
147
+ export function appendOnlyVerificationReceiptHistoryErrors(
148
+ previous: readonly Readonly<Record<string, unknown>>[],
149
+ next: readonly Readonly<Record<string, unknown>>[] | undefined,
150
+ ): readonly string[] {
151
+ if (previous.length === 0) return [];
152
+ if (next === undefined) {
153
+ return [
154
+ "verification_receipts is append-only and cannot be removed from an existing test",
155
+ ];
156
+ }
157
+ if (next.length < previous.length) {
158
+ return [
159
+ `verification_receipts is append-only: expected at least ${previous.length} historical receipt(s), received ${next.length}`,
160
+ ];
161
+ }
162
+ for (const [index, receipt] of previous.entries()) {
163
+ if (canonicalJson(next[index]) !== canonicalJson(receipt)) {
164
+ return [
165
+ `verification_receipts is append-only: historical receipt at index ${index} was changed, removed, or reordered`,
166
+ ];
167
+ }
168
+ }
169
+ return [];
170
+ }